summaryrefslogtreecommitdiff
path: root/drivers/misc/sprd_vpp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/sprd_vpp')
-rw-r--r--drivers/misc/sprd_vpp/Kconfig2
-rw-r--r--drivers/misc/sprd_vpp/Makefile4
-rw-r--r--drivers/misc/sprd_vpp/sprd_deint/Kconfig5
-rw-r--r--drivers/misc/sprd_vpp/sprd_deint/Makefile3
-rw-r--r--drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.c798
-rw-r--r--drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.h95
-rw-r--r--drivers/misc/sprd_vpp/sprd_dither/Kconfig3
-rw-r--r--drivers/misc/sprd_vpp/sprd_dither/Makefile10
-rw-r--r--drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.c500
-rw-r--r--drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.h75
-rw-r--r--drivers/misc/sprd_vpp/sprd_dither/vpp_dither_drv.c460
-rw-r--r--drivers/misc/sprd_vpp/sprd_dither/vpp_dither_reg.h50
-rw-r--r--drivers/misc/sprd_vpp/sprd_dither/vpp_dither_types.h74
13 files changed, 2079 insertions, 0 deletions
diff --git a/drivers/misc/sprd_vpp/Kconfig b/drivers/misc/sprd_vpp/Kconfig
new file mode 100644
index 00000000..d4e1bf5a
--- /dev/null
+++ b/drivers/misc/sprd_vpp/Kconfig
@@ -0,0 +1,2 @@
+
+source "drivers/misc/sprd_vpp/sprd_deint/Kconfig"
diff --git a/drivers/misc/sprd_vpp/Makefile b/drivers/misc/sprd_vpp/Makefile
new file mode 100644
index 00000000..cc0a92cc
--- /dev/null
+++ b/drivers/misc/sprd_vpp/Makefile
@@ -0,0 +1,4 @@
+
+#obj-$(CONFIG_SPRD_VPP) += deint_drv.o
+
+obj-y += sprd_deint/
diff --git a/drivers/misc/sprd_vpp/sprd_deint/Kconfig b/drivers/misc/sprd_vpp/sprd_deint/Kconfig
new file mode 100644
index 00000000..bcf9f05b
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_deint/Kconfig
@@ -0,0 +1,5 @@
+# to add sprd vsp configs in the future
+config SPRD_VPP
+ bool "SPRD VPP driver"
+ help
+ "enable vpp device driver"
diff --git a/drivers/misc/sprd_vpp/sprd_deint/Makefile b/drivers/misc/sprd_vpp/sprd_deint/Makefile
new file mode 100644
index 00000000..9668e91a
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_deint/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_SPRD_VPP) += vpp_deint_drv.o
+
diff --git a/drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.c b/drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.c
new file mode 100644
index 00000000..2aeb229e
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.c
@@ -0,0 +1,798 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications 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/platform_device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/cdev.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/clk.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk-provider.h>
+#include <linux/sprd_iommu.h>
+
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+#include "vpp_deint_drv.h"
+
+#ifndef VM_RESERVED /*for kernel up to 3.7.0 version*/
+# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
+#endif
+
+#define SPRD_MMAHB_BASE_DT SPRD_MMAHB_BASE
+
+#define VPP_MINOR MISC_DYNAMIC_MINOR
+#define VPP_AQUIRE_TIMEOUT_MS 500
+#define VPP_INIT_TIMEOUT_MS 200
+
+#define DEFAULT_FREQ_DIV 0x0
+
+static DEFINE_SEMAPHORE(s_vpp_sem);
+
+struct deint_fh {
+ int is_deint_aquired;
+ int is_clock_enabled;
+
+ wait_queue_head_t wait_queue_work;
+ int condition_work;
+ int deint_int_status;
+};
+
+struct deint_dev {
+ unsigned int freq_div;
+
+ struct semaphore deint_mutex;
+
+ struct clk *vpp_clk;
+ struct clk *vpp_parent_clk;
+ struct clk *mm_clk;
+
+ unsigned int irq;
+
+ struct deint_fh *deint_fp;
+ struct device_node *dev_np;
+};
+
+static struct deint_dev deint_hw_dev;
+//static struct wake_lock deint_wakelock;
+static atomic_t deint_instance_cnt = ATOMIC_INIT(0);
+
+static unsigned long SPRD_VPP_PHYS = 0;
+static unsigned long SPRD_VPP_BASE = 0;
+
+struct clock_name_map_t {
+ unsigned long freq;
+ char *name;
+};
+
+#if defined(CONFIG_ARCH_SCX35LT8)
+static struct clock_name_map_t clock_name_map[] = {
+ {256000000,"clk_256m"},
+ {153600000,"clk_153m6"},
+ {128000000,"clk_128m"},
+ {96000000,"clk_96m"}
+};
+#else
+static struct clock_name_map_t clock_name_map[] = {
+ {256000000,"clk_256m"},
+ {192000000,"clk_192m"},
+ {128000000,"clk_128m"},
+ {76800000,"clk_76m8"}
+};
+#endif
+
+static int max_freq_level = ARRAY_SIZE(clock_name_map);
+
+static char *deint_get_clk_src_name(unsigned int freq_level)
+{
+ if (freq_level >= max_freq_level ) {
+ printk(KERN_ERR "set freq_level to 0");
+ freq_level = 0;
+ }
+
+ return clock_name_map[freq_level].name;
+}
+
+static int find_deint_freq_level(unsigned long freq)
+{
+ int level = 0;
+ int i;
+ for (i = 0; i < max_freq_level; i++) {
+ if (clock_name_map[i].freq == freq) {
+ level = i;
+ break;
+ }
+ }
+ return level;
+}
+
+static int deint_alloc_dma_buffer(VPP_DRV_BUFFER_T *vb)
+{
+ if (!vb)
+ return -1;
+
+ //printk(KERN_ERR "deint_alloc_dma_buffer size=%d\n", vb->size);
+
+ vb->base = (unsigned long)dma_alloc_coherent(NULL, PAGE_ALIGN(vb->size), (dma_addr_t *) (&vb->phys_addr), GFP_DMA | GFP_KERNEL);
+ if ((void *)(vb->base) == NULL) {
+ printk(KERN_ERR "dynamic Physical memory allocation error size=%d\n", vb->size);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void deint_free_dma_buffer(VPP_DRV_BUFFER_T *vb)
+{
+ if (!vb)
+ return;
+
+ if (vb->base)
+ dma_free_coherent(0, PAGE_ALIGN(vb->size), (void *)vb->base, vb->phys_addr);
+
+ return;
+}
+static long deint_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int ret;
+ struct clk *clk_parent;
+ char *name_parent;
+ unsigned long frequency;
+ struct deint_fh *deint_fp = filp->private_data;
+
+ if (deint_fp == NULL) {
+ printk(KERN_ERR "deint_ioctl error occured, deint_fp == NULL\n");
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case SPRD_VPP_ALLOCATE_PHYSICAL_MEMORY:
+ {
+ VPP_DRV_BUFFER_T *vb = NULL;
+
+ down(&s_vpp_sem);
+
+ vb = kzalloc(sizeof(VPP_DRV_BUFFER_T), GFP_KERNEL);
+ if (!vb) {
+ up(&s_vpp_sem);
+ return -ENOMEM;
+ }
+
+ ret = copy_from_user(vb, (VPP_DRV_BUFFER_T *)arg, sizeof(VPP_DRV_BUFFER_T));
+ if (ret) {
+ kfree(vb);
+ up(&s_vpp_sem);
+ return -EFAULT;
+ }
+
+ ret = deint_alloc_dma_buffer(vb);
+ if (ret == -1) {
+ ret = -ENOMEM;
+ kfree(vb);
+ up(&s_vpp_sem);
+ break;
+ }
+ ret = copy_to_user((void __user *)arg, vb, sizeof(VPP_DRV_BUFFER_T));
+ if (ret) {
+ kfree(vb);
+ ret = -EFAULT;
+ up(&s_vpp_sem);
+ break;
+ }
+
+ up(&s_vpp_sem);
+ break;
+ }
+
+ case SPRD_VPP_FREE_PHYSICAL_MEMORY:
+ {
+ VPP_DRV_BUFFER_T vb;
+
+ down(&s_vpp_sem);
+
+ ret = copy_from_user(&vb, (VPP_DRV_BUFFER_T *)arg, sizeof(VPP_DRV_BUFFER_T));
+ if (ret) {
+ up(&s_vpp_sem);
+ return -EACCES;
+ }
+
+ printk(KERN_ERR "SPRD_VPP_FREE_PHYSICAL_MEMORY base: 0x%x, phys_addr: 0x%x, size: 0x%x, virt_addr: 0x%x\n",
+ vb.base, vb.phys_addr, vb.size, vb.virt_addr);
+
+ if (vb.base)
+ deint_free_dma_buffer(&vb);
+
+ up(&s_vpp_sem);
+ break;
+ }
+
+ case SPRD_VPP_DEINT_COMPLETE:
+ {
+ ret = wait_event_interruptible_timeout(
+ deint_fp->wait_queue_work,
+ deint_fp->condition_work,
+ msecs_to_jiffies(VPP_INIT_TIMEOUT_MS));
+
+ if (ret == -ERESTARTSYS) {
+ printk(KERN_INFO "deint complete -ERESTARTSYS\n");
+ put_user(ret, (int __user *)arg);
+ ret = -EINVAL;
+ }
+ else {
+ if (ret == 0) {
+ pr_debug("vpp complete timeout\n");
+ ret = -ETIMEDOUT;
+
+ /*clear vpp int*/
+ __raw_writel((1<<1), SPRD_VPP_BASE + VPP_INT_CLR);
+ }
+
+ put_user(ret, (int __user *)arg);
+ deint_fp->condition_work = 0;
+ }
+
+ break;
+ }
+
+ case SPRD_VPP_DEINT_ACQUIRE:
+ {
+ pr_debug("deint ioctl SPRD_VPP_DEINT_ACQUIRE begin\n");
+ ret = down_timeout(&deint_hw_dev.deint_mutex, msecs_to_jiffies(VPP_AQUIRE_TIMEOUT_MS));
+ if (ret) {
+ printk(KERN_ERR "deint error timeout\n");
+ //up(&deint_hw_dev.deint_mutex);
+ return ret;
+ }
+
+ deint_fp->is_deint_aquired = 1;
+ deint_hw_dev.deint_fp= deint_fp;
+
+#ifdef SEQUENCE_RUNNING
+ ret = clk_prepare_enable(deint_hw_dev.vpp_clk);
+ if (ret) {
+ printk(KERN_ERR "###: vpp_clk: clk_prepare_enable() failed!\n");
+ return ret;
+ }
+#endif
+
+ break;
+ }
+
+ case SPRD_VPP_DEINT_RELEASE:
+ {
+ pr_debug("SPRD_VPP_DEINT_RELEASE\n");
+ deint_fp->is_deint_aquired = 0;
+ deint_hw_dev.deint_fp = NULL;
+
+#ifdef SEQUENCE_RUNNING
+ if (deint_hw_dev.vpp_clk) {
+ clk_disable_unprepare(deint_hw_dev.vpp_clk);
+ clk_put(deint_hw_dev.vpp_clk);
+ }
+#endif
+
+ up(&deint_hw_dev.deint_mutex);
+
+ break;
+ }
+
+ case VPP_RESET:
+ pr_debug("vsp ioctl VSP_RESET\n");
+ sci_glb_set(SPRD_MMAHB_BASE_DT+0x04, BIT(15));
+ sci_glb_clr(SPRD_MMAHB_BASE_DT+0x04, BIT(15));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static irqreturn_t deint_isr(int irq, void *data)
+{
+ int ret = 0xff; // 0xff : invalid
+ struct deint_fh *deint_fp = ((struct deint_dev *)data)->deint_fp;
+
+ if(NULL == deint_fp) {
+ //printk(KERN_ERR "This interrupt signal is not for Deint\n");
+ return IRQ_NONE;
+ }
+
+ ret = __raw_readl(SPRD_VPP_BASE + VPP_INT_STS);
+ if((ret >> 1) & 0x1)
+ {
+ __raw_writel((1<<1), SPRD_VPP_BASE + VPP_INT_CLR);
+ }
+ else
+ {
+ return IRQ_NONE;
+ }
+
+ deint_fp->condition_work = 1;
+ wake_up_interruptible(&deint_fp->wait_queue_work);
+
+ return IRQ_HANDLED;
+}
+
+static int vpp_set_mm_clk(void)
+{
+ int ret =0;
+ struct clk *clk_mm_i;
+ struct clk *clk_vpp;
+ struct clk *clk_parent;
+ char *name_parent;
+ int instance_cnt = atomic_read(&deint_instance_cnt);
+
+ pr_debug(KERN_INFO "vsp_set_mm_clk: deint_instance_cnt %d\n", instance_cnt);
+
+#if defined(CONFIG_ARCH_SCX35)
+
+#ifdef CONFIG_OF
+ clk_mm_i = of_clk_get_by_name(deint_hw_dev.dev_np, "clk_mm_i");
+#else
+ clk_mm_i = clk_get(NULL, "clk_mm_i");
+#endif
+
+ if (IS_ERR(clk_mm_i) || (!clk_mm_i)) {
+ printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n",
+ "clk_mm_i");
+ printk(KERN_ERR "###: clk_mm_i = %p\n", clk_mm_i);
+ ret = -EINVAL;
+ goto errout;
+ } else {
+ deint_hw_dev.mm_clk= clk_mm_i;
+ }
+#endif
+
+ pr_debug(KERN_INFO "deint mmi_clk open");
+ ret = clk_prepare_enable(deint_hw_dev.mm_clk);
+ if (ret) {
+ printk(KERN_ERR "###:deint_hw_dev.mm_clk: clk_enable() failed!\n");
+ return ret;
+ }
+
+#ifdef CONFIG_OF
+ clk_vpp = of_clk_get_by_name(deint_hw_dev.dev_np, "clk_vpp");
+#else
+ clk_vpp = clk_get(NULL, "clk_vpp");
+#endif
+
+ if (IS_ERR(clk_vpp) || (!clk_vpp)) {
+ printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n",
+ "clk_vpp");
+ printk(KERN_ERR "###: vpp_clk = %p\n", clk_vpp);
+ ret = -EINVAL;
+ goto errout;
+ } else {
+ deint_hw_dev.vpp_clk = clk_vpp;
+ }
+
+#ifndef SEQUENCE_RUNNING
+ ret = clk_prepare_enable(deint_hw_dev.vpp_clk);
+ if (ret) {
+ printk(KERN_ERR "###: vpp_clk: clk_prepare_enable() failed!\n");
+ return ret;
+ }
+#endif
+
+#ifdef SET_CLK_FOR_VPP_TWICE
+ name_parent = deint_get_clk_src_name(3);
+ clk_parent = clk_get(NULL, name_parent);
+ if ((!clk_parent )|| IS_ERR(clk_parent) ) {
+ printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_vpp", name_parent);
+ ret = -EINVAL;
+ goto errout;
+ } else {
+ deint_hw_dev.vpp_parent_clk = clk_parent;
+ }
+
+ ret = clk_set_parent(deint_hw_dev.vpp_clk, deint_hw_dev.vpp_parent_clk);
+ if (ret) {
+ printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_vpp");
+ ret = -EINVAL;
+ goto errout;
+ }
+#endif
+
+ name_parent = deint_get_clk_src_name(deint_hw_dev.freq_div);
+ clk_parent = clk_get(NULL, name_parent);
+ if ((!clk_parent )|| IS_ERR(clk_parent) ) {
+ printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_vpp", name_parent);
+ ret = -EINVAL;
+ goto errout;
+ } else {
+ deint_hw_dev.vpp_parent_clk = clk_parent;
+ }
+
+ ret = clk_set_parent(deint_hw_dev.vpp_clk, deint_hw_dev.vpp_parent_clk);
+ if (ret) {
+ printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_vpp");
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ printk("vpp parent clock name %s\n", name_parent);
+ printk("vpp_freq %d Hz",
+ (int)clk_get_rate(deint_hw_dev.vpp_clk));
+
+#if defined(CONFIG_SPRD_IOMMU)
+ sprd_iommu_module_enable(IOMMU_MM);
+#endif
+
+ return 0;
+
+errout:
+#if defined(CONFIG_ARCH_SCX35)
+ if (deint_hw_dev.mm_clk) {
+ clk_put(deint_hw_dev.mm_clk);
+ }
+#endif
+
+ if (deint_hw_dev.vpp_clk) {
+ clk_put(deint_hw_dev.vpp_clk);
+ }
+
+ if (deint_hw_dev.vpp_parent_clk) {
+ clk_put(deint_hw_dev.vpp_parent_clk);
+ }
+ return ret;
+}
+
+static int deint_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ struct deint_fh *deint_fp = kmalloc(sizeof(struct deint_fh), GFP_KERNEL);
+
+ printk("deint_open called %p\n", deint_fp);
+
+ if (deint_fp == NULL) {
+ printk(KERN_ERR "deint open error occured\n");
+ return -EINVAL;
+ }
+ filp->private_data = deint_fp;
+ deint_fp->is_clock_enabled = 0;
+ deint_fp->is_deint_aquired = 0;
+ deint_fp->condition_work = 0;
+ //deint_hw_dev.deint_fp = deint_fp;
+
+ init_waitqueue_head(&deint_fp->wait_queue_work);
+
+ ret = vpp_set_mm_clk();
+
+ atomic_inc_return(&deint_instance_cnt);
+
+ printk("deint_open: ret %d, deint_fp = %p\n", ret, deint_hw_dev.deint_fp);
+
+ return ret;
+
+
+}
+
+static int deint_release (struct inode *inode, struct file *filp)
+{
+ struct deint_fh *deint_fp = filp->private_data;
+ int instance_cnt = atomic_read(&deint_instance_cnt);
+
+ if (deint_fp == NULL) {
+ printk(KERN_ERR "deint_release error occured, deint_fp == NULL\n");
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "deint_release: instance_cnt = %d\n", instance_cnt);
+
+ if (deint_fp->is_deint_aquired) {
+ printk(KERN_ERR "error occured and up deint_mutex \n");
+ up(&deint_hw_dev.deint_mutex);
+ }
+
+ kfree(filp->private_data);
+ filp->private_data=NULL;
+
+#ifndef SEQUENCE_RUNNING
+ if (deint_hw_dev.vpp_clk) {
+ clk_disable_unprepare(deint_hw_dev.vpp_clk);
+ clk_put(deint_hw_dev.vpp_clk);
+ }
+#endif
+
+ if (deint_hw_dev.mm_clk) {
+ clk_disable_unprepare(deint_hw_dev.mm_clk);
+ clk_put(deint_hw_dev.mm_clk);
+ }
+
+ atomic_dec_return(&deint_instance_cnt);
+
+#if defined(CONFIG_SPRD_IOMMU)
+ sprd_iommu_module_disable(IOMMU_MM);
+#endif
+
+ return 0;
+}
+
+static int deint_map_to_register(struct file *fp, struct vm_area_struct *vm)
+{
+ unsigned long pfn;
+
+ printk("@deint[%s] \n", __FUNCTION__);
+
+ vm->vm_flags |= VM_IO | VM_RESERVED;
+ vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot);
+ pfn = SPRD_VPP_PHYS >> PAGE_SHIFT;
+
+ return remap_pfn_range(vm, vm->vm_start, pfn, vm->vm_end-vm->vm_start, vm->vm_page_prot) ? -EAGAIN : 0;
+}
+
+static int deint_map_to_physical_memory(struct file *fp, struct vm_area_struct *vm)
+{
+ printk("@deint[%s] \n", __FUNCTION__);
+
+ vm->vm_flags |= VM_IO | VM_RESERVED;
+ vm->vm_page_prot = pgprot_writecombine(vm->vm_page_prot);
+
+ return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff, vm->vm_end-vm->vm_start, vm->vm_page_prot) ? -EAGAIN : 0;
+}
+
+static int deint_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int ret = 0;
+ printk("@deint[%s], vm_pgoff = %ld\n", __FUNCTION__, vma->vm_pgoff);
+
+ if (vma->vm_pgoff)
+ {
+ ret = deint_map_to_physical_memory(filp, vma);
+ }
+ else
+ {
+ ret = deint_map_to_register(filp, vma);
+ }
+
+ printk("@deint mmap start: %p,size: %ld\n",
+ (void*)vma->vm_start,
+ (unsigned long)(vma->vm_end - vma->vm_start));
+
+ return 0;
+}
+
+static const struct file_operations deint_fops =
+{
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = deint_ioctl,
+ .mmap = deint_mmap,
+ .open = deint_open,
+ .release = deint_release,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = deint_ioctl,
+#endif
+};
+
+static struct miscdevice deint_dev = {
+ .minor = VPP_MINOR,
+ .name = "sprd_vpp",
+ .fops = &deint_fops,
+};
+
+static int deint_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret=-1;
+ int cnt;
+ int instance_cnt = atomic_read(&deint_instance_cnt);
+
+ for (cnt = 0; cnt < instance_cnt; cnt++) {
+#if defined(CONFIG_SPRD_IOMMU)
+ sprd_iommu_module_disable(IOMMU_MM);
+#endif
+
+ if (deint_hw_dev.mm_clk) {
+ clk_disable_unprepare(deint_hw_dev.mm_clk);
+ clk_put(deint_hw_dev.mm_clk);
+ }
+
+ printk(KERN_INFO "deint_suspend, cnt: %d\n", cnt);
+ }
+
+
+ return 0;
+}
+
+static int deint_resume(struct platform_device *pdev)
+{
+
+ int ret = 0;
+ int cnt;
+ int instance_cnt = atomic_read(&deint_instance_cnt);
+
+ for (cnt = 0; cnt < instance_cnt; cnt++) {
+ ret = vpp_set_mm_clk();
+ printk(KERN_INFO "deint_resume, cnt: %d\n", cnt);
+ }
+
+ return ret;
+}
+static int deint_remove(struct platform_device *pdev)
+{
+
+ misc_deregister(&deint_dev);
+
+ free_irq(deint_hw_dev.irq, &deint_hw_dev);
+
+ if (deint_hw_dev.vpp_clk) {
+ clk_put(deint_hw_dev.vpp_clk);
+ }
+
+ if (deint_hw_dev.vpp_parent_clk) {
+ clk_put(deint_hw_dev.vpp_parent_clk);
+ }
+
+ printk(KERN_INFO "deint_remove Success !\n");
+ return 0;
+}
+
+static int deint_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct resource res;
+ u32 clock_parent_info[2];
+ int i, ret;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if(ret < 0) {
+ dev_err(dev, "no reg of property specified\n");
+ printk(KERN_ERR "deint: failed to parse_dt!\n");
+ return -EINVAL;
+ }
+
+ SPRD_VPP_PHYS = res.start;
+ SPRD_VPP_BASE = (unsigned long)ioremap_nocache(res.start, resource_size(&res));
+ if(!SPRD_VPP_BASE)
+ BUG();
+
+ deint_hw_dev.irq = irq_of_parse_and_map(np, 0);
+ deint_hw_dev.dev_np = np;
+
+ printk(KERN_INFO "deint_parse_dt , SPRD_VPP_PHYS = %p, SPRD_VPP_BASE = %p, irq = 0x%x\n",
+ (void*)SPRD_VPP_PHYS, (void*)SPRD_VPP_BASE, deint_hw_dev.irq);
+
+ ret = of_property_read_u32_array(np, "clock-parent-info", clock_parent_info, 2);
+ if(0 != ret) {
+ printk(KERN_ERR "deint: read clock-parent-info fail (%d)\n", ret);
+ return -EINVAL;
+ }
+
+ max_freq_level = clock_parent_info[1];
+ if (max_freq_level > 4) {
+ printk(KERN_ERR "deint: max_freq_level is invalid\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < max_freq_level; i++) {
+ struct clk *clk_parent;
+ char *name_parent;
+ unsigned long frequency;
+
+ name_parent = of_clk_get_parent_name(np, i+clock_parent_info[0]);
+ clk_parent = clk_get(NULL, name_parent);
+ frequency = clk_get_rate(clk_parent);
+
+ clock_name_map[i].name = name_parent;
+ clock_name_map[i].freq = frequency;
+ }
+
+ return 0;
+}
+
+static int deint_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *res = NULL;
+
+ printk(KERN_INFO "deint_probe called !\n");
+
+#ifdef CONFIG_OF
+ if (pdev->dev.of_node) {
+ ret = deint_parse_dt(&pdev->dev);
+ }
+#else
+ ret = deint_parse_dt(&pdev->dev);
+#endif
+
+ sema_init(&deint_hw_dev.deint_mutex, 1);
+
+ deint_hw_dev.freq_div = DEFAULT_FREQ_DIV;
+ deint_hw_dev.vpp_clk = NULL;
+ deint_hw_dev.vpp_parent_clk = NULL;
+ deint_hw_dev.mm_clk= NULL;
+ deint_hw_dev.deint_fp = NULL;
+
+ ret = misc_register(&deint_dev);
+ if (ret) {
+ printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n",
+ VPP_MINOR, ret);
+ goto errout;
+ }
+
+ /* register isr */
+ ret = request_irq(deint_hw_dev.irq, deint_isr, IRQF_DISABLED|IRQF_SHARED, "deinterlace", &deint_hw_dev);
+ if (ret) {
+ printk(KERN_ERR "vpp: failed to request deint irq!\n");
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ return 0;
+
+errout:
+ misc_deregister(&deint_dev);
+
+ return ret;
+}
+
+static const struct of_device_id of_match_table_vpp[] = {
+ { .compatible = "sprd,sprd_vpp", },
+ { },
+};
+
+static struct platform_driver deint_driver = {
+ .probe = deint_probe,
+ .remove = deint_remove,
+ .suspend = deint_suspend,
+ .resume = deint_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sprd_vpp",
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(of_match_table_vpp) ,
+#endif
+ },
+};
+
+static int __init deint_init(void)
+{
+ printk(KERN_INFO "deint_init called !\n");
+ if (platform_driver_register(&deint_driver) != 0) {
+ printk(KERN_ERR "platform device deint drv register Failed \n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __exit deint_exit(void)
+{
+ printk(KERN_INFO "deint_exit called !\n");
+ platform_driver_unregister(&deint_driver);
+}
+
+module_init(deint_init);
+module_exit(deint_exit);
+
+MODULE_DESCRIPTION("SPRD VPP Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.h b/drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.h
new file mode 100644
index 00000000..ed09eb21
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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 _SPRD_VPP_H
+#define _SPRD_VPP_H
+
+#include <linux/ioctl.h>
+
+/*----------------------------------------------------------------------------*
+** Macro Definitions *
+**---------------------------------------------------------------------------*/
+/*
+ Bit define, for video
+*/
+#define V_BIT_0 0x00000001
+#define V_BIT_1 0x00000002
+#define V_BIT_2 0x00000004
+#define V_BIT_3 0x00000008
+#define V_BIT_4 0x00000010
+#define V_BIT_5 0x00000020
+#define V_BIT_6 0x00000040
+#define V_BIT_7 0x00000080
+#define V_BIT_8 0x00000100
+#define V_BIT_9 0x00000200
+#define V_BIT_10 0x00000400
+#define V_BIT_11 0x00000800
+#define V_BIT_12 0x00001000
+#define V_BIT_13 0x00002000
+#define V_BIT_14 0x00004000
+#define V_BIT_15 0x00008000
+#define V_BIT_16 0x00010000
+#define V_BIT_17 0x00020000
+#define V_BIT_18 0x00040000
+#define V_BIT_19 0x00080000
+#define V_BIT_20 0x00100000
+#define V_BIT_21 0x00200000
+#define V_BIT_22 0x00400000
+#define V_BIT_23 0x00800000
+#define V_BIT_24 0x01000000
+#define V_BIT_25 0x02000000
+#define V_BIT_26 0x04000000
+#define V_BIT_27 0x08000000
+#define V_BIT_28 0x10000000
+#define V_BIT_29 0x20000000
+#define V_BIT_30 0x40000000
+#define V_BIT_31 0x80000000
+
+#define USE_INTERRUPT_DEINT
+//#define SEQUENCE_RUNNING
+#define SET_CLK_FOR_VPP_TWICE
+#define SPRD_VPP_MAP_SIZE 0x4000
+
+#define SPRD_VPP_IOCTL_MAGIC 'd'
+#define SPRD_VPP_ALLOCATE_PHYSICAL_MEMORY _IO(SPRD_VPP_IOCTL_MAGIC, 1)
+#define SPRD_VPP_FREE_PHYSICAL_MEMORY _IO(SPRD_VPP_IOCTL_MAGIC, 2)
+#define SPRD_VPP_DEINT_COMPLETE _IO(SPRD_VPP_IOCTL_MAGIC, 3)
+#define SPRD_VPP_DEINT_ACQUIRE _IO(SPRD_VPP_IOCTL_MAGIC, 4)
+#define SPRD_VPP_DEINT_RELEASE _IO(SPRD_VPP_IOCTL_MAGIC, 5)
+#define VPP_RESET _IO(SPRD_VPP_IOCTL_MAGIC, 6)
+
+
+#define VPP_CFG (0x0000)
+#define VPP_INT_STS (0x0010)
+#define VPP_INT_MSK (0x0014)
+#define VPP_INT_CLR (0x0018)
+#define VPP_INT_RAW (0x001C)
+#define DEINT_PATH_CFG (0x2000)
+#define DEINT_PATH_START (0x2004)
+#define DEINT_IMG_SIZE (0x2008)
+
+typedef struct vpp_drv_buffer_t {
+ unsigned int size;
+ unsigned int phys_addr;
+ unsigned long base; /*kernel logical address in use kernel*/
+ unsigned long virt_addr; /* virtual user space address */
+} VPP_DRV_BUFFER_T;
+
+enum sprd_vpp_frequency_e {
+ VPP_FREQENCY_LEVEL_0 = 0,
+ VPP_FREQENCY_LEVEL_1 = 1,
+ VPP_FREQENCY_LEVEL_2 = 2,
+ VPP_FREQENCY_LEVEL_3 = 3
+};
+
+#endif
diff --git a/drivers/misc/sprd_vpp/sprd_dither/Kconfig b/drivers/misc/sprd_vpp/sprd_dither/Kconfig
new file mode 100644
index 00000000..49faa7b7
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_dither/Kconfig
@@ -0,0 +1,3 @@
+config VIDEO_VPP_DITHER_SPRD
+ tristate "SPRD VPP_DITHER driver"
+ default n
diff --git a/drivers/misc/sprd_vpp/sprd_dither/Makefile b/drivers/misc/sprd_vpp/sprd_dither/Makefile
new file mode 100644
index 00000000..2a2134df
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_dither/Makefile
@@ -0,0 +1,10 @@
+
+
+
+ifeq ($(CONFIG_VIDEO_VPP_DITHER_SPRD),y)
+sprd_vpp_dither-objs := vpp_dither_config.o vpp_dither_drv.o
+obj-y += sprd_vpp_dither.o
+
+endif
+
+
diff --git a/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.c b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.c
new file mode 100644
index 00000000..62d0e5ea
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <linux/math64.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/pid.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/device.h>
+#endif
+
+#include <mach/hardware.h>
+#include <mach/arch_misc.h>
+#include <mach/sci.h>
+#include <video/ion_sprd.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND_GSP
+#include <linux/earlysuspend.h>
+#endif
+
+#include <mach/hardware.h>
+#include "vpp_dither_types.h"
+#include "vpp_dither_config.h"
+#include "vpp_dither_reg.h"
+
+
+extern struct vpp_dither_device *vpp_dither_ctx;
+
+void VPP_Dither_Early_Init()
+{
+ const unsigned char dither_path_table[DITHER_PATH_SIZE] =
+ {
+ 0x00,0x01,0x11,0x10,0x20,0x30,0x31,0x21,0x22,0x32,0x33,0x23,0x13,0x12,0x02,0x03,0x04,0x14,0x15,0x05,0x06,0x07,0x17,0x16,0x26,0x27,0x37,0x36,0x35,0x25,0x24,0x34,
+ 0x44,0x54,0x55,0x45,0x46,0x47,0x57,0x56,0x66,0x67,0x77,0x76,0x75,0x65,0x64,0x74,0x73,0x72,0x62,0x63,0x53,0x43,0x42,0x52,0x51,0x41,0x40,0x50,0x60,0x61,0x71,0x70,
+ 0x80,0x90,0x91,0x81,0x82,0x83,0x93,0x92,0xa2,0xa3,0xb3,0xb2,0xb1,0xa1,0xa0,0xb0,0xc0,0xc1,0xd1,0xd0,0xe0,0xf0,0xf1,0xe1,0xe2,0xf2,0xf3,0xe3,0xd3,0xd2,0xc2,0xc3,
+ 0xc4,0xc5,0xd5,0xd4,0xe4,0xf4,0xf5,0xe5,0xe6,0xf6,0xf7,0xe7,0xd7,0xd6,0xc6,0xc7,0xb7,0xa7,0xa6,0xb6,0xb5,0xb4,0xa4,0xa5,0x95,0x94,0x84,0x85,0x86,0x96,0x97,0x87,
+ 0x88,0x98,0x99,0x89,0x8a,0x8b,0x9b,0x9a,0xaa,0xab,0xbb,0xba,0xb9,0xa9,0xa8,0xb8,0xc8,0xc9,0xd9,0xd8,0xe8,0xf8,0xf9,0xe9,0xea,0xfa,0xfb,0xeb,0xdb,0xda,0xca,0xcb,
+ 0xcc,0xcd,0xdd,0xdc,0xec,0xfc,0xfd,0xed,0xee,0xfe,0xff,0xef,0xdf,0xde,0xce,0xcf,0xbf,0xaf,0xae,0xbe,0xbd,0xbc,0xac,0xad,0x9d,0x9c,0x8c,0x8d,0x8e,0x9e,0x9f,0x8f,
+ 0x7f,0x7e,0x6e,0x6f,0x5f,0x4f,0x4e,0x5e,0x5d,0x4d,0x4c,0x5c,0x6c,0x6d,0x7d,0x7c,0x7b,0x6d,0x6a,0x7a,0x79,0x78,0x68,0x69,0x59,0x58,0x48,0x49,0x4a,0x5a,0x5b,0x4b,
+ 0x3b,0x2b,0x2a,0x3a,0x39,0x38,0x28,0x29,0x19,0x18,0x08,0x09,0x0a,0x1a,0x1b,0x0b,0x0c,0x0d,0x1d,0x1c,0x2c,0x3c,0x3d,0x2d,0x2e,0x3e,0x3f,0x2f,0x1f,0x1e,0x0e,0x0f,
+ 0xff,0xef,0xee,0xfe,0xfd,0xfc,0xec,0xed,0xdd,0xdc,0xcc,0xcd,0xce,0xde,0xdf,0xcf,0xbf,0xbe,0xae,0xaf,0x9f,0x8f,0x8e,0x9e,0x9d,0x8d,0x8c,0x9c,0xac,0xad,0xbd,0xbc,
+ 0xbb,0xba,0xaa,0xab,0x9b,0x8b,0x8a,0x9a,0x99,0x89,0x88,0x98,0xa8,0xa9,0xb9,0xb8,0xc8,0xd8,0xd9,0xc9,0xca,0xcb,0xdb,0xda,0xea,0xeb,0xfb,0xfa,0xf9,0xe9,0xe8,0xf8,
+ 0xf7,0xf6,0xe6,0xe7,0xd7,0xc7,0xc6,0xd6,0xd5,0xc5,0xc4,0xd4,0xe4,0xe5,0xf5,0xf4,0xf3,0xe3,0xe2,0xf2,0xf1,0xf0,0xe0,0xe1,0xd1,0xd0,0xc0,0xc1,0xc2,0xd2,0xd3,0xc3,
+ 0xb3,0xa3,0xa2,0xb2,0xb1,0xb0,0xa0,0xa1,0x91,0x90,0x80,0x81,0x82,0x92,0x93,0x83,0x84,0x85,0x95,0x94,0xa4,0xb4,0xb5,0xa5,0xa6,0xb6,0xb7,0xa7,0x97,0x96,0x86,0x87,
+ 0x77,0x76,0x66,0x67,0x57,0x47,0x46,0x56,0x55,0x45,0x44,0x54,0x64,0x65,0x75,0x74,0x73,0x63,0x62,0x72,0x71,0x70,0x60,0x61,0x51,0x50,0x40,0x41,0x42,0x52,0x53,0x43,
+ 0x33,0x23,0x22,0x32,0x31,0x30,0x20,0x21,0x11,0x10,0x00,0x01,0x02,0x12,0x13,0x03,0x04,0x05,0x15,0x14,0x24,0x34,0x35,0x25,0x26,0x36,0x37,0x27,0x17,0x16,0x06,0x07,
+ 0x08,0x18,0x19,0x09,0x0a,0x0b,0x1b,0x1a,0x2a,0x2b,0x3b,0x3a,0x39,0x29,0x28,0x38,0x48,0x49,0x59,0x58,0x68,0x78,0x79,0x69,0x6a,0x7a,0x7b,0x6b,0x5b,0x5a,0x4a,0x4b,
+ 0x4c,0x4d,0x5d,0x5c,0x6c,0x7c,0x7d,0x6d,0x6e,0x7e,0x7f,0x6f,0x5f,0x5e,0x4e,0x4f,0x3f,0x2f,0x2e,0x3e,0x3d,0x3c,0x2c,0x2d,0x1d,0x1c,0x0c,0x0d,0x0e,0x1e,0x1f,0x0f,
+ 0xff,0xfe,0xee,0xef,0xdf,0xcf,0xce,0xde,0xdd,0xcd,0xcc,0xdc,0xec,0xed,0xfd,0xfc,0xfb,0xeb,0xea,0xfa,0xf9,0xf8,0xe8,0xe9,0xd9,0xd8,0xc8,0xc9,0xca,0xda,0xdb,0xcb,
+ 0xbb,0xab,0xaa,0xba,0xb9,0xb8,0xa8,0xa9,0x99,0x98,0x88,0x89,0x8a,0x9a,0x9b,0x8b,0x8c,0x8d,0x9d,0x9c,0xac,0xbc,0xbd,0xad,0xae,0xbe,0xbf,0xaf,0x9f,0x9e,0x8e,0x8f,
+ 0x7f,0x6f,0x6e,0x7e,0x7d,0x7c,0x6c,0x6d,0x5d,0x5c,0x4c,0x4d,0x4e,0x5e,0x5f,0x4f,0x3f,0x3e,0x2e,0x2f,0x1f,0x0f,0x0e,0x1e,0x1d,0x0d,0x0c,0x1c,0x2c,0x2d,0x3d,0x3c,
+ 0x3b,0x3a,0x2a,0x2b,0x1b,0x0b,0x0a,0x1a,0x19,0x09,0x08,0x18,0x28,0x29,0x39,0x38,0x48,0x58,0x59,0x49,0x4a,0x4b,0x5b,0x5a,0x6a,0x6b,0x7b,0x7a,0x79,0x69,0x68,0x78,
+ 0x77,0x67,0x66,0x76,0x75,0x74,0x64,0x65,0x55,0x54,0x44,0x45,0x46,0x56,0x57,0x47,0x37,0x36,0x26,0x27,0x17,0x07,0x06,0x16,0x15,0x05,0x04,0x14,0x24,0x25,0x35,0x34,
+ 0x33,0x32,0x22,0x23,0x13,0x03,0x02,0x12,0x11,0x01,0x00,0x10,0x20,0x21,0x31,0x30,0x40,0x50,0x51,0x41,0x42,0x43,0x53,0x52,0x62,0x63,0x73,0x72,0x71,0x61,0x60,0x70,
+ 0x80,0x81,0x91,0x90,0xa0,0xb0,0xb1,0xa1,0xa2,0xb2,0xb3,0xa3,0x93,0x92,0x82,0x83,0x84,0x94,0x95,0x85,0x86,0x87,0x97,0x96,0xa6,0xa7,0xb7,0xb6,0xb5,0xa5,0xa4,0xb4,
+ 0xc4,0xd4,0xd5,0xc5,0xc6,0xc7,0xd7,0xd6,0xe6,0xe7,0xf7,0xf6,0xf5,0xe5,0xe4,0xf4,0xf3,0xf2,0xe2,0xe3,0xd3,0xc3,0xc2,0xd2,0xd1,0xc1,0xc0,0xd0,0xe0,0xe1,0xf1,0xf0,
+ 0x00,0x10,0x11,0x01,0x02,0x03,0x13,0x12,0x22,0x23,0x33,0x32,0x31,0x21,0x20,0x30,0x40,0x41,0x51,0x50,0x60,0x70,0x71,0x61,0x62,0x72,0x73,0x63,0x53,0x52,0x42,0x43,
+ 0x44,0x45,0x55,0x54,0x64,0x74,0x75,0x65,0x66,0x76,0x77,0x67,0x57,0x56,0x46,0x47,0x37,0x27,0x26,0x36,0x35,0x34,0x24,0x25,0x15,0x14,0x04,0x05,0x06,0x16,0x17,0x07,
+ 0x08,0x09,0x19,0x18,0x28,0x38,0x39,0x29,0x2a,0x3a,0x3b,0x2b,0x1b,0x1a,0x0a,0x0b,0x0c,0x1c,0x1d,0x0d,0x0e,0x0f,0x1f,0x1e,0x2e,0x2f,0x3f,0x3e,0x3d,0x2d,0x2c,0x3c,
+ 0x4c,0x5c,0x5d,0x4d,0x4e,0x4f,0x5f,0x5e,0x6e,0x6f,0x7f,0x7e,0x7d,0x6d,0x6c,0x7c,0x7b,0x7a,0x6a,0x6b,0x5b,0x4b,0x4a,0x5a,0x59,0x49,0x48,0x58,0x68,0x69,0x79,0x78,
+ 0x88,0x89,0x99,0x98,0xa8,0xb8,0xb9,0xa9,0xaa,0xba,0xbb,0xab,0x9b,0x9a,0x8a,0x8b,0x8c,0x9c,0x9d,0x8d,0x8e,0x8f,0x9f,0x9e,0xae,0xaf,0xbf,0xbe,0xbd,0xad,0xac,0xbc,
+ 0xcc,0xdc,0xdd,0xcd,0xce,0xcf,0xdf,0xde,0xee,0xef,0xff,0xfe,0xfd,0xed,0xec,0xfc,0xfb,0xfa,0xea,0xeb,0xdb,0xcb,0xca,0xda,0xd9,0xc9,0xc8,0xd8,0xe8,0xe9,0xf9,0xf8,
+ 0xf7,0xe7,0xe6,0xf6,0xf5,0xf4,0xe4,0xe5,0xd5,0xd4,0xc4,0xc5,0xc6,0xd6,0xd7,0xc7,0xb7,0xb6,0xa6,0xa7,0x97,0x87,0x86,0x96,0x95,0x85,0x84,0x94,0xa4,0xa5,0xb5,0xb4,
+ 0xb3,0xb2,0xa2,0xa3,0x93,0x83,0x82,0x92,0x91,0x81,0x80,0x90,0xa0,0xa1,0xb1,0xb0,0xc0,0xd0,0xd1,0xc1,0xc2,0xc3,0xd3,0xd2,0xe2,0xe3,0xf3,0xf2,0xf1,0xe1,0xe0,0xf0
+ };
+
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT(25)); //enable MM module
+ sci_glb_set(REG_MM_AHB_AHB_EB, BIT(8)); //enable VPP module
+ sci_glb_set(REG_MM_AHB_GEN_CKG_CFG, BIT(9)); //enable VPP AXI
+
+ TB_REG_SET(DITH_MEM_SW, 0x00);
+ memcpy(DITH_PATH_TABLE, &dither_path_table, DITHER_PATH_SIZE);
+
+ TB_REG_SET(RB_COEF_CFG0, 0x0);
+ TB_REG_SET(RB_COEF_CFG1, 0x0);
+ TB_REG_SET(RB_COEF_CFG2, 0xC0C0B0B0);
+ TB_REG_SET(RB_COEF_CFG3, 0xE0D0D0C0);
+ TB_REG_SET(G_COEF_CFG0, 0x0);
+ TB_REG_SET(G_COEF_CFG1, 0x0);
+ TB_REG_SET(G_COEF_CFG2, 0xC0C0B0B0);
+ TB_REG_SET(G_COEF_CFG3, 0xE0D0D0C0);
+ TB_REG_SET(DITH_MEM_SW, 0x01);
+
+ VPP_Dither_IRQENABLE_SET(0);
+ VPP_Dither_IRQSTATUS_Clear();
+ VPP_Dither_IRQENABLE_SET(1);
+}
+#if 0
+int VPP_Dither_Clock_Init(struct vpp_dither_device *dev)
+{
+ int ret = 0;
+ struct clk *clk_mm_i;
+ struct clk *clk_vpp_i;
+ struct clk *clk_vpp_parent_i;
+
+#ifdef CONFIG_OF
+ clk_mm_i = of_clk_get_by_name(dev->of_dev->of_node, "clk_mm_i");
+#else
+ clk_mm_i = clk_get(NULL, "clk_mm_i");
+#endif
+ if(IS_ERR(clk_mm_i))
+ {
+ printk(KERN_ERR "[vpp_dither] can't get clock [%s]\n", "clk_mm_i");
+ ret = -EINVAL;
+ goto out;
+ }
+ else
+ {
+ dev->clk_mm = clk_mm_i;
+ printk(KERN_INFO "[vpp_dither] get clk_mm ok!\n");
+ }
+
+ ret = clk_enable(dev->clk_mm);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] mm_clk:clk_enable failed!\n");
+ return ret;
+ }
+
+#ifdef CONFIG_OF
+ clk_vpp_i = of_clk_get_by_name(dev->of_dev->of_node, "clk_vpp");
+#else
+ clk_vpp_i = clk_get(NULL, "clk_vpp");
+#endif
+ if(IS_ERR(clk_vpp_i))
+ {
+ printk(KERN_ERR "[vpp_dither] can't get clock [%s]\n", "clk_vpp");
+ ret = -EINVAL;
+ goto out;
+ }
+ else
+ {
+ dev->clk_vpp = clk_vpp_i;
+ printk(KERN_INFO "[vpp_dither] get clk_vpp ok!\n");
+ }
+
+ clk_vpp_parent_i = clk_get(NULL, "clk_256m");
+ if(IS_ERR(clk_vpp_parent_i))
+ {
+ printk(KERN_ERR "[vpp_dither] can't get clock [%s]\n", "clk_vpp_parent");
+ ret = -EINVAL;
+ goto out;
+ }
+ else
+ {
+ dev->clk_vpp_parent = clk_vpp_parent_i;
+ printk(KERN_INFO "[vpp_dither] get clk_vpp_parent ok!\n");
+ }
+
+ ret = clk_set_parent(dev->clk_vpp, dev->clk_vpp_parent);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] clk_set_parent failed!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ if(dev->clk_mm)
+ clk_put(dev->clk_mm);
+
+ if(dev->clk_vpp)
+ clk_put(dev->clk_vpp);
+
+ if(dev->clk_vpp_parent)
+ clk_put(dev->clk_vpp_parent);
+
+ return ret;
+}
+
+void VPP_Dither_Clock_Enable(struct vpp_dither_device *dev)
+{
+ int ret = 0;
+
+ if(dev->clk_vpp == NULL)
+ {
+ printk(KERN_ERR "[vpp_dither] clk not init yet!\n");
+ return;
+ }
+
+ ret = clk_prepare_enable(dev->clk_vpp);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] enable clock fail!\n");
+ return;
+ }
+}
+
+void VPP_Dither_Clock_Disable(struct vpp_dither_device *dev)
+{
+ if(dev->clk_vpp == NULL)
+ {
+ printk(KERN_ERR "[vpp_dither] clk not init yet!\n");
+ return;
+ }
+
+ clk_disable_unprepare(dev->clk_vpp);
+}
+#endif
+void VPP_Dither_Module_Enable()
+{
+ unsigned int reg_value = 0;
+
+ reg_value = TB_REG_GET(VPP_CFG);
+ reg_value |= 0x01;
+ TB_REG_SET(VPP_CFG, reg_value);
+}
+
+void VPP_Dither_Module_Disable()
+{
+ unsigned int reg_value = 0;
+
+ reg_value = TB_REG_GET(VPP_CFG);
+ reg_value &= 0xFFFFFFFE;
+ TB_REG_SET(VPP_CFG, reg_value);
+}
+
+void VPP_Dither_IRQSTATUS_Clear()
+{
+ TB_REG_SET(VPP_INT_CLR, 0x01);
+}
+
+void VPP_Dither_IRQENABLE_SET(unsigned char irq_flag)
+{
+ unsigned int reg_value = 0;
+
+ reg_value = TB_REG_GET(VPP_INT_MSK);
+
+ if(irq_flag == 1)
+ reg_value |= 0x01;
+ else
+ reg_value &= 0xFFFFFFFE;
+
+ TB_REG_SET(VPP_INT_MSK, reg_value);
+}
+
+void VPP_Dither_Init(struct vpp_dither_device *dev)
+{
+ //clock enable
+// VPP_Dither_Clock_Enable(dev);
+
+ //module enable
+ VPP_Dither_Module_Enable();
+
+ //irq enable
+// VPP_Dither_IRQENABLE_SET(0);
+// VPP_Dither_IRQSTATUS_Clear();
+// VPP_Dither_IRQENABLE_SET(1);
+}
+
+void VPP_Dither_Deinit(struct vpp_dither_device *dev)
+{
+ //module disable
+ VPP_Dither_Module_Disable();
+
+ //irq disable
+// VPP_Dither_IRQENABLE_SET(0);
+// VPP_Dither_IRQSTATUS_Clear();
+
+ //clock disable
+// VPP_Dither_Clock_Disable(dev);
+}
+
+void VPP_Dither_Module_Reset()
+{
+ sci_glb_set(REG_MM_AHB_AHB_RST, BIT(15));
+ udelay(10);
+ sci_glb_clr(REG_MM_AHB_AHB_RST, BIT(15));
+}
+
+void VPP_Dither_Info_Config(struct fb_to_vpp_info *fb_info, struct vpp_dither_device *dev)
+{
+ dev->reg_info.output_format = fb_info->output_format; //RGB666
+ dev->reg_info.pixel_type = fb_info->pixel_type; //one pixel one word
+ dev->reg_info.input_format = fb_info->input_format; //ARGB888
+ dev->reg_info.block_mod = 0;
+ dev->reg_info.img_width = fb_info->img_width;
+ dev->reg_info.img_height = fb_info->img_height;
+ dev->reg_info.src_addr = fb_info->buf_addr;
+ dev->reg_info.des_addr = fb_info->buf_addr;
+}
+
+void VPP_Dither_CFG_Config(struct vpp_dither_device *dev)
+{
+ unsigned int reg_value = 0;
+
+ if(NULL == dev)
+ {
+ printk(KERN_ERR "[vpp_dither] %s, dev is null!\n",__func__);
+ return 0;
+ }
+
+ if(dev->reg_info.output_format == 1) //RGB666
+ dev->reg_info.pixel_type = 1; //one pixel one word
+
+ reg_value |= dev->reg_info.output_format << 0;
+ reg_value |= dev->reg_info.pixel_type << 1;
+ reg_value |= dev->reg_info.input_format << 2;
+ reg_value |= dev->reg_info.block_mod << 3;
+ TB_REG_SET(DITH_PATH_CFG, reg_value);
+
+ reg_value = 0;
+ reg_value |= dev->reg_info.img_width << 0;
+ reg_value |= dev->reg_info.img_height << 16;
+ TB_REG_SET(DITH_IMG_SIZE, reg_value);
+
+ reg_value = dev->reg_info.src_addr >> 3;
+ TB_REG_SET(DITH_SRC_ADDR, reg_value);
+
+ reg_value = dev->reg_info.des_addr >> 3;
+ TB_REG_SET(DITH_DES_ADDR, reg_value);
+}
+
+int VPP_Dither_Map(struct vpp_dither_device *dev)
+{
+ return 0;
+}
+
+int VPP_Dither_Unmap(struct vpp_dither_device *dev)
+{
+ return 0;
+}
+
+int irq_status_get()
+{
+ unsigned int irq_value = 0;
+
+ irq_value = TB_REG_GET(VPP_INT_STS);
+ if(irq_value & 0x01)
+ return 1;
+ else
+ return 0;
+}
+
+irqreturn_t VPP_Dither_IRQ_Handler(int irq, void *dev_id)
+{
+ struct vpp_dither_device *dev = dev_id;
+
+ if(1 == irq_status_get())
+ {
+ printk(KERN_INFO "[vpp_dither] %s enter!\n",__func__);
+
+ VPP_Dither_IRQENABLE_SET(0);
+ VPP_Dither_IRQSTATUS_Clear();
+ up(&dev->wait_interrupt_sem);
+
+ VPP_Dither_IRQENABLE_SET(1);
+ return IRQ_HANDLED;
+ }
+ else
+ return IRQ_NONE;
+}
+
+void VPP_Dither_Trigger()
+{
+ TB_REG_SET(DITH_PATH_START, 0x01);
+}
+
+unsigned int Dither_WORKSTATUS_Get()
+{
+ return TB_REG_GET(AXIM_STS);
+}
+
+void VPP_Dither_Wait_Finish()
+{
+ volatile unsigned int i = 0;
+ while(1)
+ {
+ if(Dither_WORKSTATUS_Get() == 0)
+ return;
+ else
+ {
+ msleep(1);
+ i++;
+ if(i >= 30)
+ {
+ printk(KERN_ERR "[vpp_dither] device is hane up!\n");
+ return;
+ }
+ }
+ }
+}
+
+/*
+ FB Interface
+*/
+int Do_Dither_Work(struct fb_to_vpp_info *fb_info)
+{
+ int ret = 0;
+ struct vpp_dither_device *dev = vpp_dither_ctx;
+
+ printk(KERN_INFO "[vpp_dither] %s enter!\n",__func__);
+
+ if(0 == dev->is_device_free) //device busy
+ {
+ printk(KERN_ERR "[vpp_dither] %s, device is busy!\n",__func__);
+ return ret;
+ }
+
+ /*VPP_DITHER_SET_PARAM*/
+
+ ret = down_interruptible(&dev->hw_resource_sem);
+ dev->is_device_free = 0;
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] %s,wait hw sema interrupt by signal,return",__func__);
+ ret = -ERESTARTSYS;
+ goto exit;
+ }
+
+ VPP_Dither_Init(dev);
+ ret = VPP_Dither_Map(dev);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] %s, mmu map fail!\n",__func__);
+ ret = -EFAULT;
+ goto exit2;
+ }
+ VPP_Dither_Info_Config(fb_info, dev);
+ VPP_Dither_CFG_Config(dev);
+
+ /*VPP_DITHER_TRIGGER_RUN*/
+ VPP_Dither_Trigger();
+
+ /*VPP_DITHER_WAIT_FINISH*/
+ ret = down_timeout(&dev->wait_interrupt_sem, msecs_to_jiffies(30));
+ if(ret == 0)
+ {
+ printk(KERN_INFO "[vpp_dither] %s,wait done sema success!\n",__func__);
+ }
+ else if(ret == -ETIME)//timeout
+ {
+ printk(KERN_ERR "[vpp_dither] %s,wait done sema timeout!\n",__func__);
+ VPP_Dither_Module_Reset();
+ VPP_Dither_Early_Init();
+ ret = -EFAULT;
+ goto exit2;
+ }
+ else if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] %s,wait done sema interrupted by a signal!\n",__func__);
+ VPP_Dither_Module_Reset();
+ VPP_Dither_Early_Init();
+ ret = -EFAULT;
+ goto exit2;
+ }
+
+// VPP_Dither_Wait_Finish();
+ VPP_Dither_Unmap(dev);
+ VPP_Dither_Deinit(dev);
+
+ up(&dev->hw_resource_sem);
+ dev->is_device_free = 1;
+
+ return ret;
+
+exit2:
+ VPP_Dither_Unmap(dev);
+ VPP_Dither_Deinit(dev);
+ sema_init(&dev->wait_interrupt_sem, 0);
+exit1:
+ up(&dev->hw_resource_sem);
+
+exit:
+ return ret;
+
+}
+
+
+
+
diff --git a/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.h b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.h
new file mode 100644
index 00000000..24617b6a
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_config.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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 _VPP_DITHER_CONFIG_H_
+#define _VPP_DITHER_CONFIG_H_
+
+#include <video/gsp_types_shark.h>
+#include <mach/sci_glb_regs.h>
+#include <mach/globalregs.h> //define IRQ_GSP_INT
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#define DITHER_PATH_SIZE 1024
+
+#define VPP_CLOCK_PARENT ("clk_vpp_parent")
+#define VPP_CLOCK_NAME ("clk_vpp")
+
+
+void VPP_Dither_Early_Init();
+
+int VPP_Dither_Clock_Init(struct vpp_dither_device *dev);
+
+void VPP_Dither_Clock_Enable(struct vpp_dither_device *dev);
+
+void VPP_Dither_Clock_Disable(struct vpp_dither_device *dev);
+
+void VPP_Dither_Module_Enable();
+
+void VPP_Dither_Module_Disable();
+
+void VPP_Dither_IRQSTATUS_Clear();
+
+void VPP_Dither_IRQENABLE_SET(unsigned char irq_flag);
+
+void VPP_Dither_Init(struct vpp_dither_device *dev);
+
+void VPP_Dither_Deinit(struct vpp_dither_device *dev);
+
+void VPP_Dither_Module_Reset();
+
+void VPP_Dither_Info_Config(struct fb_to_vpp_info *fb_info, struct vpp_dither_device *dev);
+
+void VPP_Dither_CFG_Config(struct vpp_dither_device *dev);
+
+int VPP_Dither_Map(struct vpp_dither_device *dev);
+
+int VPP_Dither_Unmap(struct vpp_dither_device *dev);
+
+int irq_status_get();
+
+irqreturn_t VPP_Dither_IRQ_Handler(int irq, void *dev_id);
+
+void VPP_Dither_Trigger();
+
+unsigned int Dither_WORKSTATUS_Get();
+
+void VPP_Dither_Wait_Finish();
+
+int Do_Dither_Work(struct fb_to_vpp_info *fb_info);
+
+
+
+#endif
+
+
diff --git a/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_drv.c b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_drv.c
new file mode 100644
index 00000000..8ec6ddc2
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_drv.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <linux/math64.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/pid.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/device.h>
+#endif
+
+#include <mach/hardware.h>
+#include <mach/arch_misc.h>
+#include <video/ion_sprd.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include "vpp_dither_types.h"
+#include "vpp_dither_config.h"
+#define DO_DITHER_WORK 0x01
+
+
+struct vpp_dither_device *vpp_dither_ctx = NULL;
+
+static int vpp_dither_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ struct vpp_dither_device *dev = vpp_dither_ctx;
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+
+ if(NULL == dev)
+ {
+ printk(KERN_ERR "[vpp_dither] dev is null!\n");
+ ret = -ENODEV;
+ goto exit;
+ }
+ filp->private_data = dev;
+
+exit:
+ return ret;
+}
+
+static int vpp_dither_release(struct inode *inode, struct file *filp)
+{
+ struct vpp_dither_device *dev = (vpp_dither_device *)filp->private_data;
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+ if(NULL == dev)
+ {
+ printk(KERN_ERR "[vpp_dither] vpp_dither_release error!--dev is null!\n");
+ return -ENODEV;
+ }
+ filp->private_data = NULL;
+ return 0;
+}
+
+static ssize_t vpp_dither_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
+{
+ struct vpp_dither_device *dev = (vpp_dither_device *)filp->private_data;
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+ if(NULL == dev)
+ {
+ printk(KERN_ERR "[vpp_dither] %s:error--dev is null!\n",__func__);
+ return -ENODEV;
+ }
+ return 1;
+}
+
+static ssize_t vpp_dither_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
+{
+ struct vpp_dither_device *dev = (vpp_dither_device *)filp->private_data;
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+ if(NULL == dev)
+ {
+ printk(KERN_ERR "[vpp_dither] %s:error--dev is null!\n",__func__);
+ return -ENODEV;
+ }
+ return 1;
+}
+
+static long vpp_dither_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+
+ int ret = 0;
+
+ unsigned int param_size = _IOC_SIZE(cmd);
+ struct vpp_dither_device *dev = (vpp_dither_device *)filp->private_data;
+
+// printk(KERN_INFO "[vpp_dither] %s:,cmd:0x%08x\n",__func__, cmd);
+
+ if(dev->s_suspend_resume_flag == 1)
+ {
+ printk(KERN_ERR "[vpp_dither] %s,in suspend, ioctl just return",__func__);
+ return ret;
+ }
+ switch(cmd)
+ {
+ case DO_DITHER_WORK:
+ /*VPP_DITHER_SET_PARAM*/
+ if(param_size > sizeof(struct vpp_dither_reg_info) || param_size == 0)
+ {
+ printk(KERN_ERR "[vpp_dither] %s, param_size error!\n");
+ return -EFAULT;
+ }
+ else
+ {
+ ret = down_interruptible(&dev->hw_resource_sem);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] %s,wait gsp-hw sema interrupt by signal,return",__func__);
+ ret = -ERESTARTSYS;
+ goto exit;
+ }
+
+ ret = copy_from_user((void*)&dev->reg_info, (void*)arg, param_size);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] %s,copy_params_from_user failed!\n",__func__);
+ ret = -EFAULT;
+ goto exit1;
+ }
+ else
+ {
+ printk(KERN_INFO "[vpp_dither] %s,copy_params_from_user success!\n",__func__);
+ VPP_Dither_Init(dev);
+ ret = VPP_Dither_Map(dev);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] %s, mmu map fail!\n",__func__);
+ ret = -EFAULT;
+ goto exit2;
+ }
+
+ VPP_Dither_CFG_Config(dev);
+ }
+ }
+
+ /*VPP_DITHER_TRIGGER_RUN*/
+// printk(KERN_INFO "[vpp_dither] %s,trigger to run!\n",__func__);
+ VPP_Dither_Trigger();
+
+ /*VPP_DITHER_WAIT_FINISH*/
+// printk(KERN_INFO "[vpp_dither] %s,wait finish!\n",__func__);
+ ret = down_timeout(&dev->wait_interrupt_sem, msecs_to_jiffies(30));
+ if(ret == 0)
+ {
+ printk(KERN_INFO "[vpp_dither] %s,wait done sema success!\n",__func__);
+ }
+ else if(ret == -ETIME)//timeout
+ {
+ printk(KERN_ERR "[vpp_dither] %s,wait done sema timeout!\n",__func__);
+ VPP_Dither_Module_Reset();
+ VPP_Dither_Early_Init();
+ ret = -EFAULT;
+
+ goto exit2;
+ }
+ else if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] %s,wait done sema interrupted by a signal!\n",__func__);
+ VPP_Dither_Module_Reset();
+ VPP_Dither_Early_Init();
+ ret = -EFAULT;
+
+ goto exit2;
+ }
+
+// VPP_Dither_Wait_Finish();
+ VPP_Dither_Unmap(dev);
+ VPP_Dither_Deinit(dev);
+ up(&dev->hw_resource_sem);
+
+ break;
+
+ default:
+ ret = -EINVAL;
+
+ }
+ return ret;
+
+exit2:
+ VPP_Dither_Unmap(dev);
+ VPP_Dither_Deinit(dev);
+ sema_init(&dev->wait_interrupt_sem, 0);
+exit1:
+ up(&dev->hw_resource_sem);
+exit:
+
+ return ret;
+}
+
+static struct file_operations vpp_dither_fops =
+{
+ .owner = THIS_MODULE,
+ .open = vpp_dither_open,
+ .release = vpp_dither_release,
+ .write = vpp_dither_write,
+ .read = vpp_dither_read,
+ .unlocked_ioctl = vpp_dither_ioctl,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void vpp_dither_early_suspend(struct early_suspend *es)
+{
+ struct vpp_dither_device *dev = container_of(es, struct vpp_dither_device, early_suspend);
+
+ down(&dev->hw_resource_sem);
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+
+ VPP_Dither_Module_Disable(dev);
+// VPP_Dither_Clock_Disable(dev);
+ dev->s_suspend_resume_flag = 1;
+
+ up(&dev->hw_resource_sem);
+}
+
+static void vpp_dither_late_resume(struct early_suspend *es)
+{
+ struct vpp_dither_device *dev = container_of(es, struct vpp_dither_device, early_suspend);
+
+ down(&dev->hw_resource_sem);
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+
+// VPP_Dither_Clock_Enable(dev);
+ VPP_Dither_Module_Enable(dev);
+ VPP_Dither_Early_Init();
+ dev->s_suspend_resume_flag = 0;
+
+ up(&dev->hw_resource_sem);
+}
+
+#else
+
+static int vpp_dither_suspend(struct platform *pdev, pm_message_t state)
+{
+ struct vpp_dither_device *dev = platform_get_drvdata(pdev);
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+
+ down(&dev->hw_resource_sem);
+
+ VPP_Dither_Module_Disable(dev);
+// VPP_Dither_Clock_Disable(dev);
+ dev->s_suspend_resume_flag = 1;
+
+ up(&dev->hw_resource_sem);
+ return 0;
+}
+
+static int vpp_dither_resume(struct platform_device *pdev)
+{
+ struct vpp_dither_device *dev = platform_get_drvdata(pdev);
+
+ printk(KERN_INFO "[vpp_dither] %s\n",__func__);
+
+ down(&dev->hw_resource_sem);
+
+// VPP_Dither_Clock_Enable(dev);
+ VPP_Dither_Module_Enable(dev);
+ VPP_Dither_Early_Init();
+ dev->s_suspend_resume_flag = 0;
+
+ up(&dev->hw_resource_sem);
+ return 0;
+}
+#endif
+
+static int vpp_dither_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct vpp_dither_device *dev = NULL;
+
+#ifdef CONFIG_OF
+ struct resource r;
+#endif
+
+ printk(KERN_INFO "[vpp_dither] vpp_dither_probe enter!\n");
+
+ dev = kmalloc(sizeof(struct vpp_dither_device), GFP_KERNEL);
+ if(!dev)
+ {
+ printk(KERN_ERR "[vpp_dither] %s,fail to alloc memory!\n",__func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+ memset(dev, 0, sizeof(struct vpp_dither_device));
+
+#ifdef CONFIG_OF
+ dev->of_dev = &(pdev->dev);
+ dev->irq_num = irq_of_parse_and_map(dev->of_dev->of_node, 0);
+
+ if(0 != of_address_to_resource(dev->of_dev->of_node, 0, &r))
+ {
+ printk(KERN_ERR "[vpp_dither] probe fail! (can't get register base address)\n");
+ goto out;
+ }
+ dev->reg_base_addr = r.start;
+ printk(KERN_INFO "[vpp_dither] irq_num = %d, reg_base_addr = 0x%x\n", dev->irq_num, dev->reg_base_addr);
+#else
+ dev->irq_num = VPP_DITHER_IRQ_NUM;
+ dev->reg_base_addr = VPP_DITHER_BASE_ADDR;
+#endif
+/*
+ ret = VPP_Dither_Clock_Init(dev);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] vpp clock init fail!\n");
+ goto out;
+ }
+ VPP_Dither_Clock_Enable(dev);*/
+
+ vpp_dither_ctx = dev;
+
+ VPP_Dither_Early_Init();
+
+// VPP_Dither_Clock_Disable(dev);
+
+ dev->misc_dev.minor = MISC_DYNAMIC_MINOR,
+ dev->misc_dev.name = "sprd_vpp_dither",
+ dev->misc_dev.fops = &vpp_dither_fops,
+
+ ret = misc_register(&dev->misc_dev);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] can't register miscdev!\n");
+ goto out;
+ }
+
+ ret = request_irq(dev->irq_num, VPP_Dither_IRQ_Handler, IRQF_SHARED, "vpp_dither", dev);
+ if(ret)
+ {
+ printk(KERN_ERR "[vpp_dither] can't request irq %d\n", dev->irq_num);
+ goto out1;
+ }
+
+ dev->is_device_free = 1; //device free
+ sema_init(&dev->hw_resource_sem, 1);
+ sema_init(&dev->wait_interrupt_sem, 0);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dev->early_suspend.suspend = vpp_dither_early_suspend;
+ dev->early_suspend.resume = vpp_dither_late_resume;
+ dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
+ register_early_suspend(&dev->early_suspend);
+#endif
+
+ platform_set_drvdata(pdev, dev);
+
+ printk(KERN_INFO "[vpp_dither] vpp_dither_probe out!\n");
+
+ return ret;
+
+out1:
+ misc_deregister(&dev->misc_dev);
+out:
+ return ret;
+}
+
+static int vpp_dither_remove(struct platform_device *pdev)
+{
+ struct vpp_dither_device *dev = platform_get_drvdata(pdev);
+ printk(KERN_INFO "[vpp_dither] vpp_dither_remove called!\n");
+
+ free_irq(dev->irq_num, dev);
+
+ misc_deregister(&dev->misc_dev);
+ kfree(dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id vpp_dither_dt_ids[] =
+{
+ {.compatible = "sprd,sprd_vpp",},
+ {}
+};
+#endif
+
+static struct platform_driver vpp_dither_driver =
+{
+ .probe = vpp_dither_probe,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = vpp_dither_suspend,
+ .resume = vpp_dither_resume,
+#endif
+ .remove = vpp_dither_remove,
+ .driver =
+ {
+ .owner = THIS_MODULE,
+ .name = "sprd_vpp_dither",
+
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(vpp_dither_dt_ids),
+#endif
+
+ }
+};
+
+static int __init vpp_dither_init(void)
+{
+ printk(KERN_INFO "[vpp_dither] vpp_dither_init enter!\n");
+
+ if(platform_driver_register(&vpp_dither_driver))
+ {
+ printk(KERN_ERR "[vpp_dither] platform driver register fail!\n");
+ return -1;
+ }
+ else
+ {
+ printk(KERN_INFO "[vpp_dither] platform driver register successful!\n");
+ }
+ return 0;
+}
+
+static void __exit vpp_dither_exit(void)
+{
+ platform_driver_unregister(&vpp_dither_driver);
+ printk(KERN_INFO "[vpp_dither] platform driver unregistered!\n");
+}
+
+module_init(vpp_dither_init);
+module_exit(vpp_dither_exit);
+
+MODULE_DESCRIPTION("VPP_Dither Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_reg.h b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_reg.h
new file mode 100644
index 00000000..94991a73
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_reg.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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 _VPP_DITHER_REG_H_
+#define _VPP_DITHER_REG_H_
+
+extern struct vpp_dither_device *vpp_dither_ctx;
+
+#define VPP_REG_BASE (vpp_dither_ctx->reg_base_addr)
+
+#define VPP_CFG (VPP_REG_BASE + 0x0000)
+#define VPP_INT_STS (VPP_REG_BASE + 0x0010)
+#define VPP_INT_MSK (VPP_REG_BASE + 0x0014)
+#define VPP_INT_CLR (VPP_REG_BASE + 0x0018)
+#define VPP_INT_RAW (VPP_REG_BASE + 0x001C)
+
+#define AXIM_STS (VPP_REG_BASE + 0x002C)
+
+#define DITH_PATH_CFG (VPP_REG_BASE + 0x1000)
+#define DITH_PATH_START (VPP_REG_BASE + 0x1004)
+#define DITH_IMG_SIZE (VPP_REG_BASE + 0x1008)
+#define DITH_MEM_SW (VPP_REG_BASE + 0x100C)
+#define DITH_SRC_ADDR (VPP_REG_BASE + 0x1010)
+#define DITH_DES_ADDR (VPP_REG_BASE + 0x1014)
+#define RB_COEF_CFG0 (VPP_REG_BASE + 0x1018)
+#define RB_COEF_CFG1 (VPP_REG_BASE + 0x101C)
+#define RB_COEF_CFG2 (VPP_REG_BASE + 0x1020)
+#define RB_COEF_CFG3 (VPP_REG_BASE + 0x1024)
+#define G_COEF_CFG0 (VPP_REG_BASE + 0x1028)
+#define G_COEF_CFG1 (VPP_REG_BASE + 0x102C)
+#define G_COEF_CFG2 (VPP_REG_BASE + 0x1030)
+#define G_COEF_CFG3 (VPP_REG_BASE + 0x1034)
+
+#define DITH_PATH_TABLE (VPP_REG_BASE + 0x1400)
+
+#define MM_ENABLE 0x402E0000
+#define VPP_ENABLE 0x60D00000
+#define AXI_ENABLE 0x60D00008
+
+#endif
+
diff --git a/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_types.h b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_types.h
new file mode 100644
index 00000000..5c6fcfee
--- /dev/null
+++ b/drivers/misc/sprd_vpp/sprd_dither/vpp_dither_types.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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 _VPP_DITHER_TYPES_H_
+#define _VPP_DITHER_TYPES_H_
+
+#include <linux/types.h>
+#include <linux/earlysuspend.h>
+
+#define VPP_DITHER_IRQ_NUM 40
+#define VPP_DITHER_BASE_ADDR 0X61000000
+
+
+struct fb_to_vpp_info //FB Interface
+{
+ unsigned int input_format; //0:ARGB888 1:RGBA888
+ unsigned int output_format; //0:RGB565 1:RGB666
+ unsigned int pixel_type; //0:one pixel half word 1:one pixel one word
+ unsigned int img_width;
+ unsigned int img_height;
+ unsigned int buf_addr; //buf address
+};
+
+struct vpp_dither_reg_info
+{
+ unsigned int input_format; //ARGB888/RGBA888
+ unsigned int output_format; //RGB565/RGB666
+ unsigned int pixel_type; //half_word / one_word
+ unsigned int block_mod;
+ unsigned int img_width;
+ unsigned int img_height;
+ unsigned int src_addr;
+ unsigned int des_addr;
+};
+
+
+typedef struct vpp_dither_device
+{
+ unsigned int is_device_free;
+ unsigned int irq_num;
+ unsigned int s_suspend_resume_flag;
+ unsigned int reg_base_addr;
+ struct clk *clk_vpp;
+ struct clk *clk_mm;
+ struct clk *clk_vpp_parent;
+ struct semaphore hw_resource_sem;
+ struct semaphore wait_interrupt_sem;
+ struct vpp_dither_reg_info reg_info;
+ struct miscdevice misc_dev;
+#ifdef CONFIG_OF
+ struct device *of_dev;
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+}vpp_dither_device;
+
+#define TB_REG_GET(reg_addr) (*(volatile unsigned int *)(reg_addr))
+#define TB_REG_SET(reg_addr, value) *(volatile unsigned int *)(reg_addr) = (value)
+
+#endif
+
+