From 8e4bff4cab0ddac6060645b0715210484d02ff40 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Wed, 30 Aug 2017 08:25:59 +0000 Subject: Initial file dump from open source release --- drivers/misc/sprd_coda7l/vpu.c | 1597 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1597 insertions(+) create mode 100644 drivers/misc/sprd_coda7l/vpu.c (limited to 'drivers/misc/sprd_coda7l/vpu.c') diff --git a/drivers/misc/sprd_coda7l/vpu.c b/drivers/misc/sprd_coda7l/vpu.c new file mode 100644 index 00000000..a1df00f0 --- /dev/null +++ b/drivers/misc/sprd_coda7l/vpu.c @@ -0,0 +1,1597 @@ +/* + * vpu.c + * + * linux device driver for VPU. + * + * Copyright (C) 2006 - 2013 CHIPS&MEDIA INC. + * + * This library 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 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "vpuconfig.h" +#include "vpu.h" +#include "TShark2_CODEC_AHB_Control_Register.h" + +#define LOG_TAG "CNM_VPU_DRV" + + +/* definitions to be changed as customer configuration */ +/* if you want to have clock gating scheme frame by frame */ +/* #define VPU_SUPPORT_CLOCK_CONTROL */ + +/* if the driver want to use interrupt service from kernel ISR */ +#define VPU_SUPPORT_ISR +#ifdef VPU_SUPPORT_ISR +/* if the driver want to disable and enable IRQ whenever interrupt asserted. */ +//#define VPU_IRQ_CONTROL +#endif + +/* if the platform driver knows the name of this driver */ +/* VPU_PLATFORM_DEVICE_NAME */ + +#define VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + +/* if this driver knows the dedicated video memory address */ +//#define VPU_SUPPORT_RESERVED_VIDEO_MEMORY + +#define VPU_PLATFORM_DEVICE_NAME "vdec" +#define VPU_CLK_NAME "vcodec" +#define VPU_DEV_NAME "vpu" + +#define __SPRD_MM_TIMEOUT (1000) + +/* if the platform driver knows this driver */ +/* the definition of VPU_REG_BASE_ADDR and VPU_REG_SIZE are not meaningful */ + +//Set register 0x3000_0110(phy_addr) +#define VPU_AXI_CLK_ADDR (0x62000004) +#define VPU_AXI_CLK_ENABLE 0x0A04060A +#define VPU_AXI_CLK_DISABLE 0x0A04040A + +#define VPU_REG_BASE_ADDR 0x62100000 +#define VPU_REG_SIZE (0x4000*MAX_NUM_VPU_CORE) + +#ifdef VPU_SUPPORT_ISR +#define VPU_IRQ_NUM (23+32) +#endif + +/* this definition is only for chipsnmedia FPGA board env */ +/* so for SOC env of customers can be ignored */ + + +#ifndef VM_RESERVED /*for kernel up to 3.7.0 version*/ +# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) +#endif + +#define VPU_MINOR MISC_DYNAMIC_MINOR + + +typedef struct vpu_drv_context_t { + struct fasync_struct *async_queue; + u32 open_count; /*!<< device reference count. Not instance count */ + + unsigned int freq_div; + + struct semaphore deint_mutex; + + struct clk *clk_coda7_axi; + struct clk *clk_coda7_cc; + struct clk *clk_coda7_apb; + + struct clk *clk_parent_axi; + struct clk *clk_parent_cc; + struct clk *clk_parent_apb; + + struct clk *clk_parent; + struct clk *clk_mm_i; + + unsigned int irq; + unsigned int version; + + //struct deint_fh *deint_fp; + struct device_node *dev_np; +} vpu_drv_context_t; + +/* To track the allocated memory buffer */ +typedef struct vpudrv_buffer_pool_t { + struct list_head list; + struct vpudrv_buffer_t vb; + struct file *filp; +} vpudrv_buffer_pool_t; + +/* To track the instance index and buffer in instance pool */ +typedef struct vpudrv_instanace_list_t { + struct list_head list; + unsigned long inst_idx; + unsigned long core_idx; + struct file *filp; +} vpudrv_instanace_list_t; + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + +#define VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE (62*1024*1024) +#define VPU_DRAM_PHYSICAL_BASE 0x86C00000 + +#include "vmm.h" +static video_mm_t s_vmem; +static vpudrv_buffer_t s_video_memory = {0}; +#else +#endif /*VPU_SUPPORT_RESERVED_VIDEO_MEMORY*/ + +typedef struct vpudrv_instance_pool_t { + unsigned char codecInstPool[MAX_NUM_INSTANCE][MAX_INST_HANDLE_SIZE]; + int vpu_instance_num; +} vpudrv_instance_pool_t; + +static int vpu_hw_reset(void); + +static void vpu_clk_disable(struct clk *clk); +static int vpu_clk_enable(struct clk *clk); +static struct clk *vpu_clk_get(struct device *dev); +static void vpu_clk_put(struct clk *clk); + + +/* end customer definition */ +static vpudrv_buffer_t s_instance_pool = {0}; +static vpudrv_buffer_t s_common_memory = {0}; +static vpu_drv_context_t s_vpu_drv_context; + +static struct clk *s_vpu_clk; + +static int s_vpu_open_ref_count; + +#ifdef VPU_SUPPORT_ISR +static int s_vpu_irq = VPU_IRQ_NUM; +#endif + +static unsigned long s_vpu_reg_phy_addr = 0; +static unsigned int s_vpu_power_status_mask = 0; + +static void __iomem *s_vpu_reg_virt_addr = NULL; +static void __iomem *s_vpu_power_reg_vir_addr = NULL; +static void __iomem *s_vpu_power_status_vir_addr = NULL; + +static int s_interrupt_flag; +static wait_queue_head_t s_interrupt_wait_q; + +static spinlock_t s_vpu_lock = __SPIN_LOCK_UNLOCKED(s_vpu_lock); +static DEFINE_SEMAPHORE(s_vpu_sem); +static struct list_head s_vbp_head = LIST_HEAD_INIT(s_vbp_head); +static struct list_head s_inst_list_head = LIST_HEAD_INIT(s_inst_list_head); + + +static vpu_bit_firmware_info_t s_bit_firmware_info[MAX_NUM_VPU_CORE]; +static struct vpu_dev vpu_hw_dev; + + +#define BIT_BASE 0x0000 +#define BIT_CODE_RUN (BIT_BASE + 0x000) +#define BIT_CODE_DOWN (BIT_BASE + 0x004) +#define BIT_INT_CLEAR (BIT_BASE + 0x00C) +#define BIT_INT_STS (BIT_BASE + 0x010) +#define BIT_CODE_RESET (BIT_BASE + 0x014) +#define BIT_BUSY_FLAG (BIT_BASE + 0x160) +#define BIT_RUN_COMMAND (BIT_BASE + 0x164) +#define BIT_RUN_INDEX (BIT_BASE + 0x168) +#define BIT_RUN_COD_STD (BIT_BASE + 0x16C) +#define BIT_CUR_PC (BIT_BASE + 0x018) +#define BIT_INT_REASON (BIT_BASE + 0x174) +#define VPU_PRODUCT_CODE_REGISTER (BIT_BASE + 0x1044) + +#ifdef CONFIG_PM +/* implement to power management functions */ +static u32 s_vpu_reg_store[MAX_NUM_VPU_CORE][64]; +static u32 s_run_index; +static u32 s_run_codstd; +#endif + +static int vpu_resume(struct platform_device *pdev); +static int vpu_suspend(struct platform_device *pdev, pm_message_t state); +static int vpu_set_mm_clk(void); +static int vpu_set_clk_by_register(void); +static int vpu_clk_free(vpu_drv_context_t* vpu_context); +static int vpu_power_on(); +static int vpu_power_shutdown(); + +#define ReadVpuRegister(addr) *(volatile unsigned int *)(s_vpu_reg_virt_addr + s_bit_firmware_info[core].reg_base_offset + addr) +#define WriteVpuRegister(addr, val) *(volatile unsigned int *)(s_vpu_reg_virt_addr + s_bit_firmware_info[core].reg_base_offset + addr) = (unsigned int)val +#define WriteVpu(addr, val) *(volatile unsigned int *)(addr) = (unsigned int)val + +#define DEFAULT_FREQ_DIV 0x0 + +struct clock_name_map_t { + unsigned long freq; + char *name; +}; + +static struct clock_name_map_t clock_coda7l_axi_map[] = { + {192000000,"clk_192m"}, + {153600000,"clk_153m6"}, + {128000000,"clk_128m"}, + {76800000,"clk_76m8"} +}; + +static struct clock_name_map_t clock_coda7l_cc_map[] = { + {192000000,"clk_192m"}, + {153600000,"clk_153m6"}, + {128000000,"clk_128m"}, + {76800000,"clk_76m8"} +}; + +static struct clock_name_map_t clock_coda7l_apb_map[] = { + {128000000,"clk_128m"}, + {96000000,"clk_96m"}, + {76800000,"clk_76m8"}, + {26000000,"ext_26m"} +}; + +static int max_freq_level = ARRAY_SIZE(clock_coda7l_axi_map); + +static char *vpu_get_clk_src_name(unsigned int freq_level, struct clock_name_map_t clk_map[]) +{ + if (freq_level >= max_freq_level ) { + printk(KERN_INFO "set freq_level to 0"); + freq_level = 0; + } + + return clk_map[freq_level].name; +} + +static int find_vpu_freq_level(unsigned long freq, struct clock_name_map_t clk_map[]) +{ + int level = 0; + int i; + for (i = 0; i < max_freq_level; i++) { + if (clk_map[i].freq == freq) { + level = i; + break; + } + } + return level; +} + +static int vpu_power_on() +{ + unsigned int power_state1, power_state2, power_state3; + unsigned long timeout = jiffies + msecs_to_jiffies(__SPRD_MM_TIMEOUT); + unsigned int count = 0; + + __raw_writel(BIT(24) | __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + __raw_writel((~BIT(25)) & __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + + do { + power_state1 = (__raw_readl(s_vpu_power_status_vir_addr) & s_vpu_power_status_mask); + power_state2 = (__raw_readl(s_vpu_power_status_vir_addr) & s_vpu_power_status_mask); + power_state3 = (__raw_readl(s_vpu_power_status_vir_addr) & s_vpu_power_status_mask); + count++; + + if(time_after(jiffies, timeout)) { + return -1; + } + } while(power_state1 != power_state2 || power_state2 != power_state3 || power_state1 != 0); + + return 0; +} + +static int vpu_power_shutdown() +{ + __raw_writel(BIT(25) | __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + __raw_writel((~BIT(24)) & __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + + return 0; +} + +static int vpu_alloc_dma_buffer(vpudrv_buffer_t *vb) +{ + if (!vb) + return -1; + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + vb->phys_addr = (unsigned long)vmem_alloc(&s_vmem, vb->size, 0); + if ((unsigned long)vb->phys_addr == (unsigned long)-1) { + vpu_loge("reserved Physical memory allocation error size=%d, base_addr=0x%x, mem_size=%d\n", vb->size, (int)s_vmem.base_addr, (int)s_vmem.mem_size); + return -1; + } + + vb->base = (unsigned long)(s_video_memory.base + (vb->phys_addr - s_video_memory.phys_addr)); +#else + 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) { + vpu_loge("dynamic Physical memory allocation error size=%d\n", vb->size); + return -1; + } +#endif + + + return 0; +} + +static void vpu_free_dma_buffer(vpudrv_buffer_t *vb) +{ + if (!vb) + return; + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (vb->base) + vmem_free(&s_vmem, vb->phys_addr, 0); +#else + if (vb->base) + dma_free_coherent(0, PAGE_ALIGN(vb->size), (void *)vb->base, vb->phys_addr); +#endif + +} + +static int vpu_free_instances(struct file *filp) +{ + vpudrv_instanace_list_t *vil, *n; + vpudrv_instance_pool_t *vip; + unsigned char *vip_base; + int instance_pool_size_per_core; + unsigned char *vdi_mutexes_base; + const int PTHREAD_MUTEX_T_DESTROY_VALUE = 0xdead10cc; + + vpu_logd("vpu_free_instances inter. sizeof(vpudrv_instance_pool_t)=%d \n", sizeof(vpudrv_instance_pool_t)); + + instance_pool_size_per_core = (s_instance_pool.size/MAX_NUM_VPU_CORE); /* s_instance_pool.size assigned to the size of all core once call VDI_IOCTL_GET_INSTANCE_POOL by user. */ + + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->filp == filp) { + s_vpu_open_ref_count--; + vip_base = (unsigned char *)(s_instance_pool.base + (instance_pool_size_per_core*vil->core_idx)); + vpu_logd("vpu_free_instances detect instance crash\n"); + vpu_logd("instIdx=%d, coreIdx=%d, vip_base=%p, instance_pool_size_per_core=%d\n", (int)vil->inst_idx, (int)vil->core_idx, vip_base, (int)instance_pool_size_per_core); + vip = (vpudrv_instance_pool_t *)vip_base; + if (vip) { + memset(&vip->codecInstPool[vil->inst_idx], 0x00, 4); /* only first 4 byte is key point to free the corresponding instance. */ + vip->vpu_instance_num = s_vpu_open_ref_count; +#define PTHREAD_MUTEX_T_HANDLE_SIZE 4 + vdi_mutexes_base = (unsigned char *)(vip_base + (instance_pool_size_per_core - PTHREAD_MUTEX_T_HANDLE_SIZE*4)); + vpu_logd("vpu_free_instances : force to destroy vdi_mutexes_base=%p in userspace, vip->vpu_instance_num=%d\n", + vdi_mutexes_base, vip->vpu_instance_num); + if (vdi_mutexes_base) { + int i; + for (i=0; i < 4; i++) { + memcpy(vdi_mutexes_base, &PTHREAD_MUTEX_T_DESTROY_VALUE, PTHREAD_MUTEX_T_HANDLE_SIZE); + vdi_mutexes_base += PTHREAD_MUTEX_T_HANDLE_SIZE; + } + } + } + list_del(&vil->list); + kfree(vil); + } + } + + return 1; +} + +static int vpu_free_buffers(struct file *filp) +{ + vpudrv_buffer_pool_t *pool, *n; + vpudrv_buffer_t vb; + + vpu_logd("vpu_free_buffers\n"); + + list_for_each_entry_safe(pool, n, &s_vbp_head, list) + { + if (pool->filp == filp) { + vb = pool->vb; + if (vb.base) { + vpu_free_dma_buffer(&vb); + list_del(&pool->list); + kfree(pool); + } + } + + } + + return 0; +} + +static irqreturn_t vpu_irq_handler(int irq, void *dev_id) +{ + vpu_drv_context_t *dev = (vpu_drv_context_t *)dev_id; + + /* this can be removed. it also work in VPU_WaitInterrupt of API function */ + int core; + + if (s_vpu_drv_context.open_count <= 0) + { + //printk(KERN_ERR "This interrupt signal is not for VPU\n"); + return IRQ_NONE; + } + +#ifdef VPU_IRQ_CONTROL + disable_irq_nosync(s_vpu_irq); +#endif + + for (core = 0; core < MAX_NUM_VPU_CORE; core++) { + /*it means that we didn't get an information the current core from API layer. No core activated.*/ + if (s_bit_firmware_info[core].size == 0) + continue; + + if (ReadVpuRegister(BIT_INT_STS)) + { + WriteVpuRegister(BIT_INT_CLEAR, 0x1); + } + else + { + return IRQ_NONE; + } + } + + if (dev->async_queue) + kill_fasync(&dev->async_queue, SIGIO, POLL_IN); /* notify the interrupt to user space */ + + s_interrupt_flag = 1; + + wake_up_interruptible(&s_interrupt_wait_q); + return IRQ_HANDLED; +} + +static int vpu_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + + if(vpu_power_on()) { + return -EINVAL; + } + + ret = vpu_set_mm_clk(); + +#if defined(CONFIG_SPRD_IOMMU) + sprd_iommu_module_enable(IOMMU_MM); +#endif + + spin_lock(&s_vpu_lock); + s_vpu_drv_context.open_count++; + filp->private_data = (void *)(&s_vpu_drv_context); + spin_unlock(&s_vpu_lock); + + vpu_logi("[VPUDRV] vpu_open done, open_count = %d\n", s_vpu_drv_context.open_count); + + return ret; +} + +/*static int vpu_ioctl(struct inode *inode, struct file *filp, u_int cmd, u_long arg) // for kernel 2.6.9 of C&M*/ +static long vpu_ioctl(struct file *filp, u_int cmd, u_long arg) +{ + int ret = 0; + switch (cmd) { + case VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY: + { + vpudrv_buffer_pool_t *vbp; + + down(&s_vpu_sem); + + vbp = kzalloc(sizeof(*vbp), GFP_KERNEL); + if (!vbp) { + up(&s_vpu_sem); + return -ENOMEM; + } + + ret = copy_from_user(&(vbp->vb), (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + up(&s_vpu_sem); + return -EFAULT; + } + + ret = vpu_alloc_dma_buffer(&(vbp->vb)); + if (ret == -1) { + ret = -ENOMEM; + kfree(vbp); + up(&s_vpu_sem); + break; + } + ret = copy_to_user((void __user *)arg, &(vbp->vb), sizeof(vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + ret = -EFAULT; + up(&s_vpu_sem); + break; + } + + vbp->filp = filp; + spin_lock(&s_vpu_lock); + list_add(&vbp->list, &s_vbp_head); + spin_unlock(&s_vpu_lock); + + up(&s_vpu_sem); + } + break; + case VDI_IOCTL_FREE_PHYSICALMEMORY: + { + vpudrv_buffer_pool_t *vbp, *n; + vpudrv_buffer_t vb; + + down(&s_vpu_sem); + + ret = copy_from_user(&vb, (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret) { + up(&s_vpu_sem); + return -EACCES; + } + + if (vb.base) + vpu_free_dma_buffer(&vb); + + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vbp, n, &s_vbp_head, list) + { + if (vbp->vb.base == vb.base) { + list_del(&vbp->list); + kfree(vbp); + break; + } + } + spin_unlock(&s_vpu_lock); + + up(&s_vpu_sem); + + } + break; + case VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO: + { +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (s_video_memory.base != 0) { + ret = copy_to_user((void __user *)arg, &s_video_memory, sizeof(vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = -EFAULT; + } +#endif + } + break; + + case VDI_IOCTL_WAIT_INTERRUPT: + { + u32 timeout = (u32) arg; + + ret = wait_event_interruptible_timeout(s_interrupt_wait_q, s_interrupt_flag != 0, msecs_to_jiffies(timeout)); + if (ret == 0) { + ret = -ETIME; + break; + } + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + ret = 0; + s_interrupt_flag = 0; +#ifdef VPU_IRQ_CONTROL + enable_irq(s_vpu_irq); +#endif + } + break; + + case VDI_IOCTL_SET_CLOCK_GATE: + { + u32 clkgate; + + if (get_user(clkgate, (u32 __user *) arg)) + return -EFAULT; +#ifdef VPU_SUPPORT_CLOCK_CONTROL + if (clkgate) + vpu_clk_enable(s_vpu_clk); + else + vpu_clk_disable(s_vpu_clk); +#endif + + } + break; + case VDI_IOCTL_GET_INSTANCE_POOL: + { + down(&s_vpu_sem); + + vpu_logi("[VPUDRV] VDI_IOCTL_GET_INSTANCE_POOL\n"); + + if (s_instance_pool.base != 0) { + ret = copy_to_user((void __user *)arg, &s_instance_pool, sizeof(vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = copy_from_user(&s_instance_pool, (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + if (vpu_alloc_dma_buffer(&s_instance_pool) != -1) + { + vpu_logi("[VPUDRV] vpu_alloc_dma_buffer sucessfully\n"); + memset((void *)s_instance_pool.base, 0x0, s_instance_pool.size); /*clearing memory*/ + ret = copy_to_user((void __user *)arg, &s_instance_pool, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + /* success to get memory for instance pool */ + up(&s_vpu_sem); + break; + } + } + + } + ret = -EFAULT; + } + + up(&s_vpu_sem); + } + break; + case VDI_IOCTL_GET_COMMON_MEMORY: + { + if (s_common_memory.base != 0) { + ret = copy_to_user((void __user *)arg, &s_common_memory, sizeof(vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = copy_from_user(&s_common_memory, (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + if (vpu_alloc_dma_buffer(&s_common_memory) != -1) { + ret = copy_to_user((void __user *)arg, &s_common_memory, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + /* success to get memory for common memory */ + break; + } + } + } + + ret = -EFAULT; + } + } + break; + case VDI_IOCTL_OPEN_INSTANCE: + { + vpudrv_inst_info_t inst_info; + vpudrv_instanace_list_t *vil, *n; + + vil = kzalloc(sizeof(*vil), GFP_KERNEL); + if (!vil) + return -ENOMEM; + + if (copy_from_user(&inst_info, (vpudrv_inst_info_t *)arg, sizeof(vpudrv_inst_info_t))) { + kfree(vil); + return -EFAULT; + } + + vil->inst_idx = inst_info.inst_idx; + vil->core_idx = inst_info.core_idx; + vil->filp = filp; + spin_lock(&s_vpu_lock); + list_add(&vil->list, &s_inst_list_head); + + inst_info.inst_open_count = 0; /* counting the current open instance number */ + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + spin_unlock(&s_vpu_lock); + s_vpu_open_ref_count++; /* flag just for that vpu is in opened or closed */ + + if (copy_to_user((void __user *)arg, &inst_info, sizeof(vpudrv_inst_info_t))) { + kfree(vil); + return -EFAULT; + } + + vpu_logd("[VPUDRV] VDI_IOCTL_OPEN_INSTANCE core_idx=%d, inst_idx=%d, s_vpu_open_ref_count=%d, inst_open_count=%d\n", + (int)inst_info.core_idx, (int)inst_info.inst_idx, s_vpu_open_ref_count, inst_info.inst_open_count); + } + break; + case VDI_IOCTL_CLOSE_INSTANCE: + { + vpudrv_inst_info_t inst_info; + vpudrv_instanace_list_t *vil, *n; + + if (copy_from_user(&inst_info, (vpudrv_inst_info_t *)arg, sizeof(vpudrv_inst_info_t))) + return -EFAULT; + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->inst_idx == inst_info.inst_idx && vil->core_idx == inst_info.core_idx) { + list_del(&vil->list); + kfree(vil); + break; + } + } + + inst_info.inst_open_count = 0; /* counting the current open instance number */ + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + spin_unlock(&s_vpu_lock); + s_vpu_open_ref_count--; /* flag just for that vpu is in opened or closed */ + + if (copy_to_user((void __user *)arg, &inst_info, sizeof(vpudrv_inst_info_t))) + return -EFAULT; + + vpu_logd("[VPUDRV] VDI_IOCTL_CLOSE_INSTANCE core_idx=%d, inst_idx=%d, s_vpu_open_ref_count=%d, inst_open_count=%d\n", + (int)inst_info.core_idx, (int)inst_info.inst_idx, s_vpu_open_ref_count, inst_info.inst_open_count); + } + break; + case VDI_IOCTL_GET_INSTANCE_NUM: + { + vpudrv_inst_info_t inst_info; + vpudrv_instanace_list_t *vil, *n; + + ret = copy_from_user(&inst_info, (vpudrv_inst_info_t *)arg, sizeof(vpudrv_inst_info_t)); + if (ret != 0) + break; + + inst_info.inst_open_count = 0; + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + spin_unlock(&s_vpu_lock); + ret = copy_to_user((void __user *)arg, &inst_info, sizeof(vpudrv_inst_info_t)); + + vpu_logd("[VPUDRV] VDI_IOCTL_GET_INSTANCE_NUM core_idx=%d, inst_idx=%d, open_count=%d\n", (int)inst_info.core_idx, (int)inst_info.inst_idx, inst_info.inst_open_count); + + } + break; + case VDI_IOCTL_RESET: + { + vpu_hw_reset(); + } + break; + default: + { + printk(KERN_ERR "[VPUDRV] No such IOCTL, cmd is %d\n", cmd); + } + break; + } + return ret; +} + + +static ssize_t vpu_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) +{ + + return -1; +} + +static ssize_t vpu_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) +{ + if (!buf) { + vpu_loge("vpu_write buf = NULL error \n"); + return -EFAULT; + } + + if (len == sizeof(vpu_bit_firmware_info_t)) { + vpu_bit_firmware_info_t *bit_firmware_info; + + bit_firmware_info = kzalloc(sizeof(vpu_bit_firmware_info_t), GFP_KERNEL); + if (!bit_firmware_info) { + vpu_loge("vpu_write bit_firmware_info allocation error \n"); + return -EFAULT; + } + + if (copy_from_user(bit_firmware_info, buf, len)) { + vpu_loge("vpu_write copy_from_user error for bit_firmware_info\n"); + kfree(bit_firmware_info); + return -EFAULT; + } + + if (bit_firmware_info->size == sizeof(vpu_bit_firmware_info_t)) { + vpu_logd("vpu_write set bit_firmware_info coreIdx=0x%x, reg_base_offset=0x%x size=0x%x, bit_code[0]=0x%x\n", + bit_firmware_info->core_idx, (int)bit_firmware_info->reg_base_offset, bit_firmware_info->size, bit_firmware_info->bit_code[0]); + + if (bit_firmware_info->core_idx > MAX_NUM_VPU_CORE) { + vpu_loge("vpu_write coreIdx[%d] is exceeded than MAX_NUM_VPU_CORE[%d]\n", bit_firmware_info->core_idx, MAX_NUM_VPU_CORE); + kfree(bit_firmware_info); + return -ENODEV; + } + + memcpy((void *)&s_bit_firmware_info[bit_firmware_info->core_idx], bit_firmware_info, sizeof(vpu_bit_firmware_info_t)); + kfree(bit_firmware_info); + return len; + } + + kfree(bit_firmware_info); + } + + + + + return -1; +} + +static int vpu_release(struct inode *inode, struct file *filp) +{ + int reg_addr; + + spin_lock(&s_vpu_lock); + + vpu_logi("[VPUDRV] vpu_release, open_count= %d\n", s_vpu_drv_context.open_count); + + /* found and free the not handled buffer by user applications */ + vpu_free_buffers(filp); + + /* found and free the not closed instance by user applications */ + vpu_free_instances(filp); + s_vpu_drv_context.open_count--; + if (s_vpu_drv_context.open_count == 0) { + if (s_instance_pool.base) { + vpu_logi("[VPUDRV] free instance pool\n"); + vpu_free_dma_buffer(&s_instance_pool); + s_instance_pool.base = 0; + } + + if (s_common_memory.base) { + vpu_logi("[VPUDRV] free common memory\n"); + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + } + spin_unlock(&s_vpu_lock); + + vpu_clk_free(&s_vpu_drv_context); + + if(s_vpu_drv_context.open_count == 0) { + vpu_power_shutdown(); + } + +#if defined(CONFIG_SPRD_IOMMU) + sprd_iommu_module_disable(IOMMU_MM); +#endif + + return 0; +} + + +static int vpu_fasync(int fd, struct file *filp, int mode) +{ + struct vpu_drv_context_t *dev = (struct vpu_drv_context_t *)filp->private_data; + return fasync_helper(fd, filp, mode, &dev->async_queue); +} + + +static int vpu_map_to_register(struct file *fp, struct vm_area_struct *vm) +{ + unsigned long pfn; + + vm->vm_flags |= VM_IO | VM_RESERVED; + vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot); + pfn = s_vpu_reg_phy_addr >> 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 vpu_map_to_physical_memory(struct file *fp, struct vm_area_struct *vm) +{ + 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 vpu_map_to_instance_pool_memory(struct file *fp, struct vm_area_struct *vm) +{ + return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff, vm->vm_end-vm->vm_start, vm->vm_page_prot) ? -EAGAIN : 0; +} + +/*! + * @brief memory map interface for vpu file operation + * @return 0 on success or negative error code on error + */ +static int vpu_mmap(struct file *fp, struct vm_area_struct *vm) +{ + printk("[%s], vm_pgoff = %ld \n", __FUNCTION__, vm->vm_pgoff); + + if (vm->vm_pgoff) { + if (vm->vm_pgoff == (s_instance_pool.phys_addr>>PAGE_SHIFT)) + return vpu_map_to_instance_pool_memory(fp, vm); + + return vpu_map_to_physical_memory(fp, vm); + } else { + return vpu_map_to_register(fp, vm); + } +} + +struct file_operations vpu_fops = { + .owner = THIS_MODULE, + .open = vpu_open, + .read = vpu_read, + .write = vpu_write, + .unlocked_ioctl = vpu_ioctl, + .release = vpu_release, + .fasync = vpu_fasync, + .mmap = vpu_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl = vpu_ioctl, +#endif +}; + +static struct miscdevice vpu_dev = { + .minor = VPU_MINOR, + .name = "sprd_coda7l", + .fops = &vpu_fops, +}; + + +#ifdef CONFIG_OF +static const struct of_device_id of_match_table_coda7l[] = { + { .compatible = "sprd,sprd_coda7l", }, + { }, +}; + +static int vpu_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct resource res; + u32 clock_parent_info[2]; + u32 power_regs_info[4]; + 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 "vpu: failed to parse_dt!\n"); + return -EINVAL; + } + + s_vpu_reg_phy_addr = res.start; + s_vpu_reg_virt_addr = ioremap_nocache(res.start, resource_size(&res)); + if(!s_vpu_reg_virt_addr) + BUG(); + + s_vpu_drv_context.irq = irq_of_parse_and_map(np, 0); + s_vpu_drv_context.dev_np = np; + + printk(KERN_INFO "vpu_parse_dt , irq = %d, SPRD_VPP_PHYS = %p, SPRD_VPP_BASE = %p\n", + s_vpu_drv_context.irq, (void*)s_vpu_reg_phy_addr, (void*)s_vpu_reg_virt_addr); + + ret = of_property_read_u32_array(np, "clock-parent-info", clock_parent_info, 2); + if(0 != ret) { + printk(KERN_ERR "vpu: 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 "vpu: 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_coda7l_axi_map[i].name = name_parent; + clock_coda7l_axi_map[i].freq = frequency; + + clock_coda7l_cc_map[i].name = name_parent; + clock_coda7l_cc_map[i].freq = frequency; + + name_parent = of_clk_get_parent_name(np, i+clock_parent_info[0]+clock_parent_info[1]); + clk_parent = clk_get(NULL, name_parent); + frequency = clk_get_rate(clk_parent); + + clock_coda7l_apb_map[i].name = name_parent; + clock_coda7l_apb_map[i].freq = frequency; + } + + ret = of_property_read_u32_array(np, "power-regs-info", power_regs_info, 4); + if(0 != ret) { + printk(KERN_ERR "vpu: read power_regs_info fail (%d)\n", ret); + return -EINVAL; + } + + s_vpu_power_status_mask = power_regs_info[3]; + s_vpu_power_reg_vir_addr = ioremap_nocache(power_regs_info[0], 4); + s_vpu_power_status_vir_addr = ioremap_nocache(power_regs_info[2], 4); + if(!(s_vpu_power_reg_vir_addr && s_vpu_power_status_vir_addr)) { + printk(KERN_ERR "vpu power regs remap errorl \n"); + return -EINVAL; + } + + return 0; +} +#else +static int vpu_parse_dt( + struct device *dev) +{ + //vsp_hw_dev.irq = IRQ_VSP_INT; + return 0; +} +#endif + + +static int vpu_probe(struct platform_device *pdev) +{ + int err = 0; + int ret; + int reg_addr; + struct resource *res = NULL; + + vpu_logi("[VPUDRV] vpu_probe\n"); + +#ifdef CONFIG_OF + if (pdev->dev.of_node) { + ret = vpu_parse_dt(&pdev->dev); + } +#else + ret = vpu_parse_dt(&pdev->dev); +#endif + + s_vpu_drv_context.freq_div = DEFAULT_FREQ_DIV; + s_vpu_drv_context.clk_mm_i= NULL; + s_vpu_drv_context.clk_parent_axi= NULL; + s_vpu_drv_context.clk_parent_cc= NULL; + s_vpu_drv_context.clk_parent_apb= NULL; + s_vpu_drv_context.clk_coda7_apb= NULL; + s_vpu_drv_context.clk_coda7_axi= NULL; + s_vpu_drv_context.clk_coda7_cc= NULL; + + ret = misc_register(&vpu_dev); + if (ret) { + printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n", + VPU_MINOR, ret); + goto ERROR_PROVE_DEVICE; + } + +#ifdef VPU_SUPPORT_ISR + /*if (pdev) + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) {// if platform driver is implemented + s_vpu_irq = res->start; + vpu_logi("[VPUDRV] : vpu irq number get from platform driver irq=0x%x\n", s_vpu_irq); + } else { + vpu_logi("[VPUDRV] : vpu irq number get from defined value irq=0x%x\n", s_vpu_irq); + }*/ + s_vpu_irq = s_vpu_drv_context.irq; + + err = request_irq(s_vpu_irq, vpu_irq_handler, IRQF_SHARED, "VPU_CODEC_IRQ", (void *)(&s_vpu_drv_context)); + if (err) { + printk(KERN_ERR "[VPUDRV] : fail to register interrupt handler\n"); + goto ERROR_PROVE_DEVICE; + } +#endif + + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + s_video_memory.size = VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE; + s_video_memory.phys_addr = VPU_DRAM_PHYSICAL_BASE; + s_video_memory.base = (unsigned long)ioremap(s_video_memory.phys_addr, PAGE_ALIGN(s_video_memory.size)); + if (!s_video_memory.base) { + printk(KERN_ERR "[VPUDRV] : fail to remap video memory physical phys_addr=0x%x, base=0x%x, size=%d\n", + (int)s_video_memory.phys_addr, (int)s_video_memory.base, (int)s_video_memory.size); + goto ERROR_PROVE_DEVICE; + } + + if (vmem_init(&s_vmem, s_video_memory.phys_addr, s_video_memory.size) < 0) { + printk(KERN_ERR "[VPUDRV] : fail to init vmem system\n"); + goto ERROR_PROVE_DEVICE; + } + vpu_logi("[VPUDRV] success to probe vpu device with reserved video memory phys_addr=0x%x, base = 0x%x\n", + (int) s_video_memory.phys_addr, (int)s_video_memory.base); +#else + vpu_logi("[VPUDRV] success to probe vpu device with non reserved video memory\n"); +#endif + + return 0; + + +ERROR_PROVE_DEVICE: + + misc_deregister(&vpu_dev); + + if (s_vpu_reg_virt_addr) + iounmap(s_vpu_reg_virt_addr); + + return err; +} + +static int vpu_remove(struct platform_device *pdev) +{ + vpu_logd("vpu_remove\n"); +#ifdef VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + + misc_deregister(&vpu_dev); + + if (s_instance_pool.base) { + vpu_free_dma_buffer(&s_instance_pool); + s_instance_pool.base = 0; + } + + if (s_common_memory.base) { + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (s_video_memory.base) { + iounmap((void *)s_video_memory.base); + s_video_memory.base = 0; + + vmem_exit(&s_vmem); + } +#endif + +#ifdef VPU_SUPPORT_ISR + if (s_vpu_drv_context.irq) + free_irq(s_vpu_drv_context.irq, &s_vpu_drv_context); +#endif + + vpu_clk_put(s_vpu_clk); + + + +#endif /*VPU_SUPPORT_PLATFORM_DRIVER_REGISTER*/ + + + return 0; +} + +#ifdef CONFIG_PM +static int vpu_suspend(struct platform_device *pdev, pm_message_t state) +{ + vpu_logd("vpu_suspend\n"); + return 0; +} +static int vpu_resume(struct platform_device *pdev) +{ + vpu_logd("vpu_resume\n"); + return 0; +} +#else +#define vpu_suspend NULL +#define vpu_resume NULL +#endif /* !CONFIG_PM */ + + +static struct platform_driver vpu_driver = { + .probe = vpu_probe, + .remove = vpu_remove, + .suspend = vpu_suspend, + .resume = vpu_resume, + .driver = { + .owner = THIS_MODULE, + .name = "sprd_coda7l", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(of_match_table_coda7l) , +#endif + }, +}; + +static int __init vpu_init(void) +{ + int res = 0; + + vpu_logd("vpu_init, REG_AON_APB_BOND_OPT0 = 0x%x\n", __raw_readl(REG_AON_APB_BOND_OPT0)); + + if(__raw_readl(REG_AON_APB_BOND_OPT0) & (1<<12)) { + return 0; + } + + init_waitqueue_head(&s_interrupt_wait_q); + s_common_memory.base = 0; + s_instance_pool.base = 0; +#ifdef VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + res = platform_driver_register(&vpu_driver); +#else + res = platform_driver_register(&vpu_driver); + res = vpu_probe(NULL); +#endif + vpu_power_shutdown(); + vpu_logd("end vpu_init result=0x%x\n", res); + + return res; +} + +static void __exit vpu_exit(void) +{ +#ifdef VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + vpu_logd("vpu_exit\n"); + + if(__raw_readl(REG_AON_APB_BOND_OPT0) & (1<<12)) { + return ; + } + + platform_driver_unregister(&vpu_driver); + +#else + + vpu_clk_put(s_vpu_clk); + + if (s_instance_pool.base) { + vpu_free_dma_buffer(&s_instance_pool); + s_instance_pool.base = 0; + } + + if (s_common_memory.base) { + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (s_video_memory.base) { + iounmap((void *)s_video_memory.base); + s_video_memory.base = 0; + + vmem_exit(&s_vmem); + } +#endif + + if (s_vpu_major > 0) { + cdev_del(&s_vpu_cdev); + unregister_chrdev_region(s_vpu_major, 1); + s_vpu_major = 0; + } + +#ifdef VPU_SUPPORT_ISR + if (s_vpu_irq) + free_irq(s_vpu_irq, &s_vpu_drv_context); +#endif + +#endif + + return; +} + + + +MODULE_AUTHOR("A customer using C&M VPU, Inc."); +MODULE_DESCRIPTION("VPU linux driver"); +MODULE_LICENSE("GPL"); + +module_init(vpu_init); +module_exit(vpu_exit); + +int vpu_hw_reset(void) +{ + vpu_logd("request vpu reset from application. \n"); + return 0; +} + +static int vpu_clk_free(vpu_drv_context_t* vpu_context) +{ + if (vpu_context->clk_coda7_apb) { + clk_disable(vpu_context->clk_coda7_apb); + } + + if (vpu_context->clk_coda7_axi) { + clk_disable(vpu_context->clk_coda7_axi); + } + + if (vpu_context->clk_coda7_cc) { + clk_disable(vpu_context->clk_coda7_cc); + } + + if (vpu_context->clk_coda7_apb) { + clk_unprepare(vpu_context->clk_coda7_apb); + } + + if (vpu_context->clk_coda7_axi) { + clk_unprepare(vpu_context->clk_coda7_axi); + } + + if (vpu_context->clk_coda7_cc) { + clk_unprepare(vpu_context->clk_coda7_cc); + } + + if (vpu_context->clk_mm_i) { + clk_disable_unprepare(vpu_context->clk_mm_i); + } + + return 0; +} + +static int vpu_set_parent_for_coda7l_clk() +{ + struct clk *clk_parent; + char *name_parent; + int ret =0; + + name_parent = vpu_get_clk_src_name(3, clock_coda7l_axi_map); + clk_parent = clk_get(NULL, name_parent); + printk(KERN_ERR "clock[%s]: get parent in probe[%s] by clk_get()!\n", "clk_coda7_axi", 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_coda7_axi", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_axi= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_axi, s_vpu_drv_context.clk_parent_axi); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_axi"); + ret = -EINVAL; + } + + name_parent = vpu_get_clk_src_name(s_vpu_drv_context.freq_div, clock_coda7l_axi_map); + clk_parent = clk_get(NULL, name_parent); + printk(KERN_ERR "clock[%s]: get parent in probe[%s] by clk_get()!\n", "clk_coda7_axi", 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_coda7_axi", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_axi= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_axi, s_vpu_drv_context.clk_parent_axi); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_axi"); + ret = -EINVAL; + } + + printk(KERN_ERR "vpu parent clock name %s, freq: %dHz\n", name_parent, (int)clk_get_rate(s_vpu_drv_context.clk_coda7_axi)); + + name_parent = vpu_get_clk_src_name(3, clock_coda7l_cc_map); + 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_coda7_cc", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_cc= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_cc, s_vpu_drv_context.clk_parent_cc); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_cc"); + ret = -EINVAL; + } + + name_parent = vpu_get_clk_src_name(s_vpu_drv_context.freq_div, clock_coda7l_cc_map); + 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_coda7_cc", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_cc= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_cc, s_vpu_drv_context.clk_parent_cc); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_cc"); + ret = -EINVAL; + } + + printk(KERN_ERR "vpu parent clock name %s, freq: %dHz\n", name_parent, (int)clk_get_rate(s_vpu_drv_context.clk_coda7_cc)); + + name_parent = vpu_get_clk_src_name(3, clock_coda7l_apb_map); + 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_coda7_apb", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_apb= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_apb, s_vpu_drv_context.clk_parent_apb); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_apb"); + ret = -EINVAL; + } + + name_parent = vpu_get_clk_src_name(s_vpu_drv_context.freq_div, clock_coda7l_apb_map); + 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_coda7_apb", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_apb= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_apb, s_vpu_drv_context.clk_parent_apb); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_apb"); + ret = -EINVAL; + } + + printk(KERN_ERR "vpu parent clock name %s, freq: %dHz\n", name_parent, (int)clk_get_rate(s_vpu_drv_context.clk_coda7_apb)); + + return ret; +} +static int vpu_set_mm_clk(void) +{ + int ret =0; + struct clk *clk_mm_i; + struct clk *clk_coda7_axi; + struct clk *clk_coda7_cc; + struct clk *clk_coda7_apb; + +#if defined(CONFIG_ARCH_SCX35) + +//Config for clk_mm_i +#ifdef CONFIG_OF + clk_mm_i = of_clk_get_by_name(s_vpu_drv_context.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 { + s_vpu_drv_context.clk_mm_i= clk_mm_i; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_mm_i); + if (ret) { + printk(KERN_ERR "###:s_vpu_drv_context.clk_mm_i: clk_enable() failed!\n"); + return ret; + } +#endif + +//Config for clk_coda7_axi +#ifdef CONFIG_OF + clk_coda7_axi = of_clk_get_by_name(s_vpu_drv_context.dev_np, "clk_coda7_axi"); +#else + clk_coda7_axi = clk_get(NULL, "clk_coda7_axi"); +#endif + + if (IS_ERR(clk_coda7_axi) || (!clk_coda7_axi)) { + printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n", "clk_coda7_axi"); + printk(KERN_ERR "###: clk_coda7_axi = %p\n", clk_coda7_axi); + ret = -EINVAL; + goto errout; + } else { + s_vpu_drv_context.clk_coda7_axi = clk_coda7_axi; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_coda7_axi); + if (ret) { + printk(KERN_ERR "###: clk_coda7_axi: clk_enable() failed!\n"); + return ret; + } + +//Config for clk_coda7_cc +#ifdef CONFIG_OF + clk_coda7_cc = of_clk_get_by_name(s_vpu_drv_context.dev_np, "clk_coda7_cc"); +#else + clk_coda7_cc = clk_get(NULL, "clk_coda7_cc"); +#endif + + if (IS_ERR(clk_coda7_cc) || (!clk_coda7_cc)) { + printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n", "clk_coda7_cc"); + printk(KERN_ERR "###: clk_coda7_cc = %p\n", clk_coda7_cc); + ret = -EINVAL; + goto errout; + } else { + s_vpu_drv_context.clk_coda7_cc = clk_coda7_cc; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_coda7_cc); + if (ret) { + printk(KERN_ERR "###: clk_coda7_cc: clk_enable() failed!\n"); + return ret; + } + +//Config for clk_coda7_apb +#ifdef CONFIG_OF + clk_coda7_apb = of_clk_get_by_name(s_vpu_drv_context.dev_np, "clk_coda7_apb"); +#else + clk_coda7_apb = clk_get(NULL, "clk_coda7_apb"); +#endif + + if (IS_ERR(clk_coda7_apb) || (!clk_coda7_apb)) { + printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n", "clk_coda7_cc"); + printk(KERN_ERR "###: clk_coda7_cc = %p\n", clk_coda7_apb); + ret = -EINVAL; + goto errout; + } else { + s_vpu_drv_context.clk_coda7_apb = clk_coda7_apb; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_coda7_apb); + if (ret) { + printk(KERN_ERR "###: clk_coda7_apb: clk_enable() failed!\n"); + return ret; + } + + ret = vpu_set_parent_for_coda7l_clk(); + if(ret) { + printk(KERN_ERR "###:vpu set parent failed!\n"); + return ret; + } + + return 0; + +errout: + + vpu_clk_free(&s_vpu_drv_context); + + return ret; +} + +struct clk *vpu_clk_get(struct device *dev) +{ + return clk_get(dev, VPU_CLK_NAME); +} +void vpu_clk_put(struct clk *clk) +{ + if (!(clk == NULL || IS_ERR(clk))) + clk_put(clk); +} +int vpu_clk_enable(struct clk *clk) +{ + +#if 0 + if (!(clk == NULL || IS_ERR(clk))) { + /* the bellow is for C&M EVB.*/ + { + struct clk *s_vpuext_clk = NULL; + s_vpuext_clk = clk_get(NULL, "vcore"); + if (s_vpuext_clk) + { + vpu_logi("[VPUDRV] vcore clk=%p\n", s_vpuext_clk); + clk_enable(s_vpuext_clk); + } + + vpu_logi("[VPUDRV] vbus clk=%p\n", s_vpuext_clk); + if (s_vpuext_clk) + { + s_vpuext_clk = clk_get(NULL, "vbus"); + clk_enable(s_vpuext_clk); + } + } + + /* for C&M EVB. */ + return clk_enable(clk); + } +#endif + + return 0; +} + +void vpu_clk_disable(struct clk *clk) +{ +#if 0 + if (!(clk == NULL || IS_ERR(clk))) { + vpu_logd("[VPUDRV] vpu_clk_disable\n"); + clk_disable(clk); + } +#endif +} + + -- cgit v1.3.1