diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/misc/sprd_vpp/sprd_deint | |
Diffstat (limited to 'drivers/misc/sprd_vpp/sprd_deint')
| -rw-r--r-- | drivers/misc/sprd_vpp/sprd_deint/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/misc/sprd_vpp/sprd_deint/Makefile | 3 | ||||
| -rw-r--r-- | drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.c | 798 | ||||
| -rw-r--r-- | drivers/misc/sprd_vpp/sprd_deint/vpp_deint_drv.h | 95 |
4 files changed, 901 insertions, 0 deletions
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 |
