/**************************************************************************** * * The MIT License (MIT) * * Copyright (c) 2014 - 2021 VERISILICON * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * ***************************************************************************** * * The GPL License (GPL) * * Copyright (C) 2014 - 2021 VERISILICON * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************** * * Note: This software is released under dual MIT and GPL licenses. A * recipient may use this file under the terms of either the MIT license or * GPL License. If you wish to use only one license not the other, you can * indicate your decision by deleting one of the above license notices in your * version of this file. * *****************************************************************************/ #include #include /* needed for __init,__exit directives */ #include /* needed for remap_page_range SetPageReserved ClearPageReserved */ #include /* obviously, for kmalloc */ #include /* for struct file_operations, register_chrdev() */ #include /* standard error codes */ #include #include /* request_irq(), free_irq() */ #include #include #include #include /* needed for virt_to_phys() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* our own stuff */ #include "vc8000_driver.h" unsigned long gBaseDDRHw = 0; unsigned int pcie = 0; /* used in hantro_mmu.c*/ //#define MULTI_THR_TEST #ifdef MULTI_THR_TEST #define WAIT_NODE_NUM 32 struct wait_list_node { u32 node_id; //index of the node u32 used_flag; //1:the node is insert to the wait queue list. u32 sem_used; //1:the source is released and the semphone is uped. struct semaphore wait_sem; //the unique semphone for per reserve_encoder thread. u32 wait_cond; //the condition for wait. Equal to the "core_info". struct list_head wait_list; //list node. }; static struct list_head reserve_header; static struct wait_list_node res_wait_node[WAIT_NODE_NUM]; static void wait_delay(unsigned int delay) { if(delay > 0) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ktime_t dl = ktime_set((delay / MSEC_PER_SEC), (delay % MSEC_PER_SEC) * NSEC_PER_MSEC); __set_current_state(TASK_UNINTERRUPTIBLE); schedule_hrtimeout(&dl, HRTIMER_MODE_REL); #else msleep(delay); #endif } } static u32 request_wait_node(struct wait_list_node **node,u32 start_id) { u32 i; struct wait_list_node *temp_node; while(1) { for(i=start_id;iused_flag==0) { temp_node->used_flag=1; *node = temp_node; return i; } } wait_delay(10); } } static void request_wait_sema(struct wait_list_node **node) { u32 i; struct wait_list_node *temp_node; while(1) { for(i=0;iused_flag==0)&&(temp_node->sem_used==0)) { temp_node->sem_used =1; *node = temp_node; return ; } } wait_delay(10); } } static void init_wait_node(struct wait_list_node *node,u32 cond, u32 sem_flag) { node->used_flag = 0; node->wait_cond = cond; sema_init(&node->wait_sem, sem_flag); INIT_LIST_HEAD(&node->wait_list); if(sem_flag>0) { node->sem_used =1; } } static void init_reserve_wait(u32 dev_num) { u32 i; u32 cond = 0x80000001; u32 sem_flag =0; struct wait_list_node *node; // printk("%s,%d, dev_num %d\n",__FUNCTION__,__LINE__,dev_num); INIT_LIST_HEAD(&reserve_header); for(i=0;inode_id = i; init_wait_node(node,cond,sem_flag); } } void release_reserve_wait(u32 dev_num) { } #endif /********variables declaration related with race condition**********/ struct semaphore enc_core_sem; DECLARE_WAIT_QUEUE_HEAD(hw_queue); DEFINE_SPINLOCK(owner_lock); DECLARE_WAIT_QUEUE_HEAD(enc_wait_queue); /*------------------------------------------------------------------------ *****************************PORTING LAYER******************************** -------------------------------------------------------------------------*/ #define RESOURCE_SHARED_INTER_SUBSYS 0 /*0:no resource sharing inter subsystems 1: existing resource sharing*/ #define SUBSYS_0_IO_ADDR 0x90000 /*customer specify according to own platform*/ #define SUBSYS_0_IO_SIZE (1024 * 4) /* bytes */ #define SUBSYS_1_IO_ADDR 0xA0000 /*customer specify according to own platform*/ #define SUBSYS_1_IO_SIZE (20000 * 4) /* bytes */ #define INT_PIN_SUBSYS_0_VC8000E -1 #define INT_PIN_SUBSYS_0_CUTREE -1 #define INT_PIN_SUBSYS_0_DEC400 -1 #define INT_PIN_SUBSYS_0_L2CACHE -1 #define INT_PIN_SUBSYS_1_VC8000E -1 #define INT_PIN_SUBSYS_1_CUTREE -1 #define INT_PIN_SUBSYS_1_DEC400 -1 /*for all subsystem, the subsys info should be listed here for subsequent use*/ /*base_addr, iosize, resource_shared*/ SUBSYS_CONFIG subsys_array[]= { {SUBSYS_0_IO_ADDR, SUBSYS_0_IO_SIZE, RESOURCE_SHARED_INTER_SUBSYS}, //subsys_0 //{SUBSYS_1_IO_ADDR, SUBSYS_1_IO_SIZE, RESOURCE_SHARED_INTER_SUBSYS}, //subsys_1 }; /*here config every core in all subsystem*/ /*NOTE: no matter what format(HEVC/H264/JPEG/AV1/...) is supported in VC8000E, just use [CORE_VC8000E] to indicate it's a VC8000E core*/ /* CUTREE can work standalone, so it can be a subsytem or just one core of a subsytem.*/ /*subsys_idx, core_type, offset, reg_size, irq*/ CORE_CONFIG core_array[]= { {0, CORE_VC8000E, 0x1000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_VC8000E //{0, CORE_MMU, 0x2000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_VC8000E //{0, CORE_AXIFE, 0x3000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_AXIFE //{0, CORE_MMU_1, 0x4000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_VC8000E //{0, CORE_AXIFE_1, 0x5000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_AXIFE_1 //{0, CORE_DEC400, 0x6000, 1600 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_DEC400 //{0, CORE_L2CACHE, 0xc000, 500 * 4, INT_PIN_SUBSYS_0_L2CACHE}, //subsys_0_l2cache //{0, CORE_CUTREE, 0xd000, 500 * 4, INT_PIN_SUBSYS_0_L2CACHE}, //subsys_0_CUTREE //{1, CORE_CUTREE, 0x1000, 500 * 4, INT_PIN_SUBSYS_0_CUTREE}, //CUTREE //{1, CORE_MMU, 0x2000, 500 * 4, INT_PIN_SUBSYS_0_CUTREE}, //subsys_1_MMU //{1, CORE_AXIFE, 0x3000, 500 * 4, INT_PIN_SUBSYS_0_CUTREE}, //subsys_1_AXIFE }; /*------------------------------END-------------------------------------*/ /***************************TYPE AND FUNCTION DECLARATION****************/ /* here's all the must remember stuff */ typedef struct { SUBSYS_DATA subsys_data; //config of each core,such as base addr, iosize,etc u32 hw_id; //VC8000E/VC8000EJ hw id to indicate project u32 subsys_id; //subsys id for driver and sw internal use u32 is_valid; //indicate this subsys is hantro's core or not int pid[CORE_MAX]; //indicate which process is occupying the subsys u32 is_reserved[CORE_MAX]; //indicate this subsys is occupied by user or not u32 irq_received[CORE_MAX]; //indicate which core receives irq u32 irq_status[CORE_MAX]; //IRQ status of each core u32 job_id[CORE_MAX]; char *buffer; unsigned int buffsize; volatile u8 *hwregs; struct fasync_struct *async_queue; } hantroenc_t; static int ReserveIO(void); static void ReleaseIO(void); //static void ResetAsic(hantroenc_t * dev); #ifdef hantroenc_DEBUG static void dump_regs(unsigned long data); #endif /* IRQ handler */ #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) static irqreturn_t hantroenc_isr(int irq, void *dev_id, struct pt_regs *regs); #else static irqreturn_t hantroenc_isr(int irq, void *dev_id); #endif /*********************local variable declaration*****************/ unsigned long sram_base = 0; unsigned int sram_size = 0; /* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/ static int hantroenc_major = 0; static int total_subsys_num = 0; static int total_core_num = 0; volatile unsigned int asic_status = 0; /* dynamic allocation*/ static hantroenc_t* hantroenc_data = NULL; #ifdef IRQ_SIMULATION struct timer_list timer0; struct timer_list timer1; #endif /******************************************************************************/ static int CheckEncIrq(hantroenc_t *dev,u32 *core_info,u32 *irq_status, u32 *job_id) { unsigned long flags; int rdy = 0; u8 core_type = 0; u8 subsys_idx = 0; core_type = (u8)(*core_info & 0x0F); subsys_idx = (u8)(*core_info >> 4); if (subsys_idx > total_subsys_num-1) { *core_info = -1; *irq_status = 0; return 1; } spin_lock_irqsave(&owner_lock, flags); if(dev[subsys_idx].irq_received[core_type]) { /* reset the wait condition(s) */ PDEBUG("check subsys[%d][%d] irq ready\n", subsys_idx, core_type); //dev[subsys_idx].irq_received[core_type] = 0; rdy = 1; *core_info = subsys_idx; *irq_status = dev[subsys_idx].irq_status[core_type]; if(job_id != NULL) *job_id = dev[subsys_idx].job_id[core_type]; } spin_unlock_irqrestore(&owner_lock, flags); return rdy; } static unsigned int WaitEncReady(hantroenc_t *dev,u32 *core_info,u32 *irq_status) { PDEBUG("WaitEncReady\n"); if(wait_event_interruptible(enc_wait_queue, CheckEncIrq(dev,core_info,irq_status, NULL))) { PDEBUG("ENC wait_event_interruptible interrupted\n"); return -ERESTARTSYS; } return 0; } static int CheckEncIrqbyPolling(hantroenc_t *dev,u32 *core_info,u32 *irq_status,u32 *job_id) { unsigned long flags; int rdy = 0; u8 core_type = 0; u8 subsys_idx = 0; u32 irq, hwId, majorId, wClr; unsigned long reg_offset = 0; u32 loop = 30; u32 interval = 100; u32 enable_status = 0; core_type = (u8)(*core_info & 0x0F); subsys_idx = (u8)(*core_info >> 4); if (subsys_idx > total_subsys_num-1) { *core_info = -1; *irq_status = 0; return 1; } do { spin_lock_irqsave(&owner_lock, flags); if(dev[subsys_idx].is_reserved[core_type] == 0) { //printk(KERN_DEBUG"subsys[%d][%d] is not reserved\n", subsys_idx, core_type); goto end_1; } else if(dev[subsys_idx].irq_received[core_type] && (dev[subsys_idx].irq_status[core_type] & (ASIC_STATUS_FUSE_ERROR |ASIC_STATUS_HW_TIMEOUT|ASIC_STATUS_BUFF_FULL| ASIC_STATUS_HW_RESET|ASIC_STATUS_ERROR|ASIC_STATUS_FRAME_READY )) ) { rdy = 1; *core_info = subsys_idx; *irq_status = dev[subsys_idx].irq_status[core_type]; *job_id = dev[subsys_idx].job_id[core_type]; goto end_1; } reg_offset = dev[subsys_idx].subsys_data.core_info.offset[core_type]; irq = (u32)ioread32((void *)(dev[subsys_idx].hwregs + reg_offset + 0x04)); enable_status = (u32)ioread32((void *)(dev[subsys_idx].hwregs + reg_offset + 20)); if(irq & ASIC_STATUS_ALL) { PDEBUG("check subsys[%d][%d] irq ready\n", subsys_idx, core_type); if(irq & 0x20) iowrite32(0, (void *)(dev[subsys_idx].hwregs + reg_offset + 0x14)); /* clear all IRQ bits. (hwId >= 0x80006100) means IRQ is cleared by writting 1 */ hwId = ioread32((void *)dev[subsys_idx].hwregs + reg_offset); majorId = (hwId & 0x0000FF00) >> 8; wClr = (majorId >= 0x61) ? irq: (irq & (~0x1FD)); iowrite32(wClr, (void *)(dev[subsys_idx].hwregs + reg_offset + 0x04)); rdy = 1; *core_info = subsys_idx; *irq_status = irq; dev[subsys_idx].irq_received[core_type] = 1; dev[subsys_idx].irq_status[core_type] = irq; *job_id = dev[subsys_idx].job_id[core_type]; goto end_1; } spin_unlock_irqrestore(&owner_lock, flags); mdelay(interval); }while(loop--); goto end_2; end_1: spin_unlock_irqrestore(&owner_lock, flags); end_2: return rdy; } static int CheckEncAnyIrq(hantroenc_t *dev, CORE_WAIT_OUT *out) { u32 i; int rdy = 1; u32 core_info,irq_status, job_id; u32 core_type = CORE_VC8000E; for(i = 0; i < total_subsys_num; i++) { if(!(dev[i].subsys_data.core_info.type_info & (1<job_id[out->irq_num] = job_id; out->irq_status[out->irq_num] = irq_status; //printk(KERN_DEBUG "irq_status of subsys %d job_id %d is:%x\n",i,job_id,irq_status); out->irq_num++; rdy = 1; } } return rdy; } static unsigned int WaitEncAnyReady(hantroenc_t *dev,CORE_WAIT_OUT *out) { if(wait_event_interruptible(enc_wait_queue, CheckEncAnyIrq(dev,out))) { PDEBUG("ENC wait_event_interruptible interrupted\n"); return -ERESTARTSYS; } return 0; } static int CheckCoreOccupation(hantroenc_t *dev, u8 core_type) { int ret = 0; unsigned long flags; core_type = (core_type == CORE_VC8000EJ ? CORE_VC8000E : core_type); spin_lock_irqsave(&owner_lock, flags); if(!dev->is_reserved[core_type]) { dev->is_reserved[core_type] = 1; #ifndef MULTI_THR_TEST dev->pid[core_type] = current->pid; #endif ret = 1; PDEBUG("CheckCoreOccupation pid=%d\n",dev->pid[core_type]); } spin_unlock_irqrestore(&owner_lock, flags); return ret; } static int GetWorkableCore(hantroenc_t *dev,u32 *core_info,u32 *core_info_tmp) { int ret = 0; u32 i = 0; u32 cores; u8 core_type = 0; u32 required_num = 0; static u32 reserved_job_id = 0; unsigned long flags; /*input core_info[32 bit]: mode[1bit](1:all 0:specified)+amount[3bit](the needing amount -1)+reserved+core_type[8bit] output core_info[32 bit]: the reserved core info to user space and defined as below. mode[1bit](1:all 0:specified)+amount[3bit](reserved total core num)+reserved+subsys_mapping[8bit] */ cores = *core_info; required_num = ((cores >> CORE_INFO_AMOUNT_OFFSET)& 0x7)+1; core_type = (u8)(cores&0xFF); if (*core_info_tmp == 0) *core_info_tmp = required_num << CORE_INFO_AMOUNT_OFFSET; else required_num = (*core_info_tmp >> CORE_INFO_AMOUNT_OFFSET); PDEBUG("GetWorkableCore:required_num=%d,core_info=%x\n",required_num,*core_info); if(required_num) { /* a valid free Core with specified core type */ for (i = 0; i < total_subsys_num; i++) { if (dev[i].subsys_data.core_info.type_info & (1 << core_type)) { core_type = (core_type == CORE_VC8000EJ ? CORE_VC8000E : core_type); if(dev[i].is_valid && CheckCoreOccupation(&dev[i], core_type)) { *core_info_tmp = ((((*core_info_tmp >> CORE_INFO_AMOUNT_OFFSET)-1)<> CORE_INFO_AMOUNT_OFFSET)==0) { ret = 1; spin_lock_irqsave(&owner_lock, flags); *core_info = (reserved_job_id << 16)|(*core_info_tmp & 0xFF); dev[i].job_id[core_type] = reserved_job_id; reserved_job_id++; spin_unlock_irqrestore(&owner_lock, flags); *core_info_tmp = 0; required_num = 0; break; } } } } } else ret = 1; PDEBUG("*core_info = %x\n",*core_info); return ret; } static long ReserveEncoder(hantroenc_t *dev,u32 *core_info) { u32 core_info_tmp = 0; #ifdef MULTI_THR_TEST struct wait_list_node *wait_node; u32 start_id=0; #endif /*If HW resources are shared inter cores, just make sure only one is using the HW*/ if (dev[0].subsys_data.cfg.resouce_shared) { if (down_interruptible(&enc_core_sem)) return -ERESTARTSYS; } #ifdef MULTI_THR_TEST while(1) { start_id=request_wait_node(&wait_node,start_id); if(wait_node->sem_used==1) { if(GetWorkableCore(dev,core_info,&core_info_tmp)) { down_interruptible(&wait_node->wait_sem); wait_node->sem_used=0; wait_node->used_flag=0; break; } else { start_id++; } } else { wait_node->wait_cond = *core_info; list_add_tail(&wait_node->wait_list,&reserve_header); down_interruptible(&wait_node->wait_sem); *core_info = wait_node->wait_cond; list_del(&wait_node->wait_list); wait_node->sem_used=0; wait_node->used_flag=0; break; } } #else /* lock a core that has specified core id*/ if(wait_event_interruptible(hw_queue, GetWorkableCore(dev,core_info,&core_info_tmp) != 0 )) return -ERESTARTSYS; #endif return 0; } static void ReleaseEncoder(hantroenc_t * dev,u32 *core_info) { unsigned long flags; u8 core_type = 0, subsys_idx = 0, unCheckPid = 0; unCheckPid = (u8)((*core_info) >> 31); #ifdef MULTI_THR_TEST u32 release_ok=0; struct list_head *node; struct wait_list_node *wait_node; u32 core_info_tmp = 0; #endif subsys_idx = (u8)((*core_info&0xF0) >> 4); core_type = (u8)(*core_info&0x0F); PDEBUG("ReleaseEncoder:subsys_idx=%d,core_type=%x\n",subsys_idx,core_type); /* release specified subsys and core type */ if (dev[subsys_idx].subsys_data.core_info.type_info & (1 << core_type)) { core_type = (core_type == CORE_VC8000EJ ? CORE_VC8000E : core_type); spin_lock_irqsave(&owner_lock, flags); PDEBUG("subsys[%d].pid[%d]=%d,current->pid=%d\n",subsys_idx, core_type, dev[subsys_idx].pid[core_type],current->pid); #ifdef MULTI_THR_TEST if(dev[subsys_idx].is_reserved[core_type]) #else if(dev[subsys_idx].is_reserved[core_type] && (dev[subsys_idx].pid[core_type] == current->pid || unCheckPid == 1)) #endif { dev[subsys_idx].pid[core_type] = -1; dev[subsys_idx].is_reserved[core_type] = 0; dev[subsys_idx].irq_received[core_type] = 0; dev[subsys_idx].irq_status[core_type] = 0; dev[subsys_idx].job_id[core_type] = 0; spin_unlock_irqrestore(&owner_lock, flags); #ifdef MULTI_THR_TEST release_ok=0; if(list_empty(&reserve_header)) { request_wait_sema(&wait_node); up(&wait_node->wait_sem); } else { list_for_each(node,&reserve_header) { wait_node = container_of(node,struct wait_list_node,wait_list); if((GetWorkableCore(dev,&wait_node->wait_cond,&core_info_tmp))&&(wait_node->sem_used==0)) { release_ok =1; wait_node->sem_used = 1; up(&wait_node->wait_sem); break; } } if(release_ok==0) { request_wait_sema(&wait_node); up(&wait_node->wait_sem); } } #endif } else { if (dev[subsys_idx].pid[core_type] != current->pid && unCheckPid == 0) printk(KERN_ERR "WARNING:pid(%d) is trying to release core reserved by pid(%d)\n",current->pid,dev[subsys_idx].pid[core_type]); spin_unlock_irqrestore(&owner_lock, flags); } //wake_up_interruptible_all(&hw_queue); } #ifndef MULTI_THR_TEST wake_up_interruptible_all(&hw_queue); #endif if(dev->subsys_data.cfg.resouce_shared) up(&enc_core_sem); return; } #ifdef IRQ_SIMULATION static void get_random_bytes(void *buf, int nbytes); static void hantroenc_trigger_irq_0(unsigned long value) { PDEBUG("trigger core 0 irq\n"); del_timer(&timer0); hantroenc_isr(0,(void *)&hantroenc_data[0]); } static void hantroenc_trigger_irq_1(unsigned long value) { PDEBUG("trigger core 1 irq\n"); del_timer(&timer1); hantroenc_isr(0,(void *)&hantroenc_data[1]); } #endif static long hantroenc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; unsigned int tmp; #ifdef HANTROMMU_SUPPORT u32 i = 0; volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; #endif PDEBUG("ioctl cmd 0x%08ux\n", cmd); /* * extract the type and number bitfields, and don't encode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if(_IOC_TYPE(cmd) != HANTRO_IOC_MAGIC #ifdef HANTROMMU_SUPPORT &&_IOC_TYPE(cmd) != HANTRO_IOC_MMU #endif ) return -ENOTTY; if((_IOC_TYPE(cmd) == HANTRO_IOC_MAGIC && _IOC_NR(cmd) > HANTRO_IOC_MAXNR) #ifdef HANTROMMU_SUPPORT ||(_IOC_TYPE(cmd) == HANTRO_IOC_MMU && _IOC_NR(cmd) > HANTRO_IOC_MMU_MAXNR) #endif ) return -ENOTTY; /* * the direction is a bitmask, and VERIFY_WRITE catches R/W * transfers. `Type' is user-oriented, while * access_ok is kernel-oriented, so the concept of "read" and * "write" is reversed */ if(_IOC_DIR(cmd) & _IOC_READ) #if KERNEL_VERSION(5,0,0) <= LINUX_VERSION_CODE err = !access_ok((void *) arg, _IOC_SIZE(cmd)); #else err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); #endif else if(_IOC_DIR(cmd) & _IOC_WRITE) #if KERNEL_VERSION(5,0,0) <= LINUX_VERSION_CODE err = !access_ok((void *) arg, _IOC_SIZE(cmd)); #else err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); #endif if(err) return -EFAULT; switch (cmd) { case HANTRO_IOCH_GET_VCMD_ENABLE: { __put_user(0, (unsigned long *) arg); break; } case HANTRO_IOCG_HWOFFSET: { u32 id; __get_user(id, (u32*)arg); if(id >= total_subsys_num) { return -EFAULT; } __put_user(hantroenc_data[id].subsys_data.cfg.base_addr, (unsigned long *) arg); break; } case HANTRO_IOCG_HWIOSIZE: { u32 id; u32 io_size; __get_user(id, (u32*)arg); if(id >= total_subsys_num) { return -EFAULT; } io_size = hantroenc_data[id].subsys_data.cfg.iosize; __put_user(io_size, (u32 *) arg); return 0; } case HANTRO_IOCG_SRAMOFFSET: __put_user(sram_base, (unsigned long *) arg); break; case HANTRO_IOCG_SRAMEIOSIZE: __put_user(sram_size, (unsigned int *) arg); break; case HANTRO_IOCG_CORE_NUM: __put_user(total_subsys_num, (unsigned int *) arg); break; case HANTRO_IOCG_CORE_INFO: { u32 idx; SUBSYS_CORE_INFO in_data; copy_from_user(&in_data, (void*)arg, sizeof(SUBSYS_CORE_INFO)); idx = in_data.type_info; if (idx > total_subsys_num - 1) return -1; copy_to_user((void*)arg, &hantroenc_data[idx].subsys_data.core_info, sizeof(SUBSYS_CORE_INFO)); break; } case HANTRO_IOCH_ENC_RESERVE: { u32 core_info; int ret; PDEBUG("Reserve ENC Cores\n"); __get_user(core_info, (u32*)arg); ret = ReserveEncoder(hantroenc_data,&core_info); if (ret == 0) __put_user(core_info, (u32 *) arg); return ret; } case HANTRO_IOCH_ENC_RELEASE: { u32 core_info; __get_user(core_info, (u32*)arg); PDEBUG("Release ENC Core\n"); ReleaseEncoder(hantroenc_data,&core_info); break; } case HANTRO_IOCG_CORE_WAIT: { u32 core_info; u32 irq_status; __get_user(core_info, (u32*)arg); #ifdef IRQ_SIMULATION u32 random_num; get_random_bytes(&random_num, sizeof(u32)); random_num = random_num%10+80; PDEBUG("random_num=%d\n",random_num); /*init a timer to trigger irq*/ if (core_info==1) { init_timer(&timer0); timer0.function = &hantroenc_trigger_irq_0; timer0.expires = jiffies + random_num*HZ/10; //the expires time is 1s add_timer(&timer0); } if (core_info==2) { init_timer(&timer1); timer1.function = &hantroenc_trigger_irq_1; timer1.expires = jiffies + random_num*HZ/10; //the expires time is 1s add_timer(&timer1); } #endif tmp = WaitEncReady(hantroenc_data,&core_info,&irq_status); if (tmp==0) { __put_user(irq_status, (unsigned int *)arg); return core_info;//return core_id } else { return -1; } break; } case HANTRO_IOCG_ANYCORE_WAIT: { CORE_WAIT_OUT out; memset(&out, 0, sizeof(CORE_WAIT_OUT)); #ifdef IRQ_SIMULATION u32 random_num; get_random_bytes(&random_num, sizeof(u32)); random_num = random_num%10+80; PDEBUG("random_num=%d\n",random_num); /*init a timer to trigger irq*/ if (core_info==1) { init_timer(&timer0); timer0.function = &hantroenc_trigger_irq_0; timer0.expires = jiffies + random_num*HZ/10; //the expires time is 1s add_timer(&timer0); } if (core_info==2) { init_timer(&timer1); timer1.function = &hantroenc_trigger_irq_1; timer1.expires = jiffies + random_num*HZ/10; //the expires time is 1s add_timer(&timer1); } #endif tmp = WaitEncAnyReady(hantroenc_data,&out); if (tmp==0) { copy_to_user((void*)arg, &out, sizeof(CORE_WAIT_OUT)); return 0; } else { return -1; } break; } default: { #ifdef HANTROMMU_SUPPORT if(_IOC_TYPE(cmd) == HANTRO_IOC_MMU) { memset (mmu_hwregs, 0, MAX_SUBSYS_NUM*2*sizeof(u8*)); for (i = 0; i < total_subsys_num; i++ ) { if(hantroenc_data[i].subsys_data.core_info.type_info & (1<private_data = (void *) dev; PDEBUG("dev opened\n"); return result; } static int hantroenc_release(struct inode *inode, struct file *filp) { hantroenc_t *dev = (hantroenc_t *) filp->private_data; u32 core_id = 0, i = 0; #ifdef hantroenc_DEBUG dump_regs((unsigned long) dev); /* dump the regs */ #endif unsigned long flags; PDEBUG("dev closed\n"); for (i = 0;i < total_subsys_num; i++) { for (core_id = 0; core_id < CORE_MAX; core_id++) { spin_lock_irqsave(&owner_lock, flags); if (dev[i].is_reserved[core_id] == 1 && dev[i].pid[core_id] == current->pid) { dev[i].pid[core_id] = -1; dev[i].is_reserved[core_id] = 0; dev[i].irq_received[core_id] = 0; dev[i].irq_status[core_id] = 0; PDEBUG("release reserved core\n"); } spin_unlock_irqrestore(&owner_lock, flags); } } #ifdef HANTROMMU_SUPPORT for(i = 0; i < total_subsys_num; i++) { if(!(hantroenc_data[i].subsys_data.core_info.type_info & (1<subsys_data.cfg.resouce_shared) up(&enc_core_sem); return 0; } /* VFS methods */ static struct file_operations hantroenc_fops = { .owner= THIS_MODULE, .open = hantroenc_open, .release = hantroenc_release, .unlocked_ioctl = hantroenc_ioctl, .fasync = NULL, }; /*----------------------------------------- platform register -----------------------------------------*/ static const struct of_device_id isp_of_match[] = { { .compatible = "thead,light-vc8000e", }, { /* sentinel */ }, }; static int encoder_hantrodec_probe(struct platform_device *pdev) { int result; int i, j; struct resource *mem; mem = platform_get_resource(pdev,IORESOURCE_MEM,0); if(mem->start) subsys_array[0].base_addr = mem->start; core_array[0].irq = platform_get_irq(pdev,0); total_subsys_num = sizeof(subsys_array)/sizeof(SUBSYS_CONFIG); for (i = 0; i< total_subsys_num; i++) { printk(KERN_INFO "hantroenc: module init - subsys[%d] addr =%p\n",i, (void *)subsys_array[i].base_addr); } hantroenc_data = (hantroenc_t *)vmalloc(sizeof(hantroenc_t)*total_subsys_num); if (hantroenc_data == NULL) goto err1; memset(hantroenc_data,0,sizeof(hantroenc_t)*total_subsys_num); for(i = 0; i < total_subsys_num; i++) { hantroenc_data[i].subsys_data.cfg = subsys_array[i]; hantroenc_data[i].async_queue = NULL; hantroenc_data[i].hwregs = NULL; hantroenc_data[i].subsys_id = i; for(j = 0; j < CORE_MAX; j++) hantroenc_data[i].subsys_data.core_info.irq[j] = -1; } total_core_num = sizeof(core_array)/sizeof(CORE_CONFIG); for (i = 0; i < total_core_num; i++) { hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.type_info |= (1<<(core_array[i].core_type)); hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.offset[core_array[i].core_type] = core_array[i].offset; hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.regSize[core_array[i].core_type] = core_array[i].reg_size; hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.irq[core_array[i].core_type] = core_array[i].irq; } result = register_chrdev(hantroenc_major, "vc8000", &hantroenc_fops); if(result < 0) { printk(KERN_INFO "hantroenc: unable to get major <%d>\n", hantroenc_major); goto err1; } else if(result != 0) /* this is for dynamic major */ { hantroenc_major = result; } result = ReserveIO(); if(result < 0) { goto err; } //ResetAsic(hantroenc_data); /* reset hardware */ sema_init(&enc_core_sem, 1); #ifdef HANTROMMU_SUPPORT /* MMU only initial once No matter how many MMU we have */ for(i = 0; i < total_subsys_num; i++) { if(!(hantroenc_data[i].subsys_data.core_info.type_info & (1< busy, change your config\n", hantroenc_data[i].subsys_data.core_info.irq[j]); ReleaseIO(); goto err; } } else { printk(KERN_INFO "hantroenc: IRQ not in use!\n"); } } } #ifdef MULTI_THR_TEST init_reserve_wait(total_subsys_num); #endif printk(KERN_INFO "hantroenc: module inserted. Major <%d>\n", hantroenc_major); return 0; err: unregister_chrdev(hantroenc_major, "vc8000"); err1: if (hantroenc_data != NULL) vfree(hantroenc_data); printk(KERN_INFO "hantroenc: module not inserted\n"); return result; } static int encoder_hantrodec_remove(struct platform_device *pdev) { int i=0, j = 0; #ifdef HANTROMMU_SUPPORT volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; #endif for(i=0;i> 16) & 0xFFFF) != ((ENC_HW_ID1 >> 16) & 0xFFFF))) && ((((hwid >> 16) & 0xFFFF) != ((ENC_HW_ID2 >> 16) & 0xFFFF)))) { printk(KERN_INFO "hantroenc: HW not found at %p\n", (void *)hantroenc_data[i].subsys_data.cfg.base_addr); #ifdef hantroenc_DEBUG dump_regs((unsigned long) &hantroenc_data); #endif hantroenc_data[i].is_valid = 0; ReleaseIO(); continue; } hantroenc_data[i].hw_id = hwid; hantroenc_data[i].is_valid = 1; found_hw = 1; hw_cfg = (u32)ioread32((void *)hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[VC8000E_core_idx] + 320); hantroenc_data[i].subsys_data.core_info.type_info &= 0xFFFFFFFC; if(hw_cfg & 0x88000000) hantroenc_data[i].subsys_data.core_info.type_info |= (1< with ID <0x%08x>\n", (void *)hantroenc_data[i].subsys_data.cfg.base_addr, hwid); } if (found_hw == 0) { printk(KERN_ERR "hantroenc: NO ANY HW found!!\n"); return -1; } return 0; } static void ReleaseIO(void) { u32 i; for (i=0;i<=total_subsys_num;i++) { if (hantroenc_data[i].is_valid == 0) continue; if(hantroenc_data[i].hwregs) iounmap((void *) hantroenc_data[i].hwregs); release_mem_region(hantroenc_data[i].subsys_data.cfg.base_addr, hantroenc_data[i].subsys_data.cfg.iosize); } } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) irqreturn_t hantroenc_isr(int irq, void *dev_id, struct pt_regs *regs) #else irqreturn_t hantroenc_isr(int irq, void *dev_id) #endif { unsigned int handled = 0; hantroenc_t *dev = (hantroenc_t *) dev_id; u32 irq_status; unsigned long flags; u32 core_type = 0, i = 0; unsigned long reg_offset = 0; u32 hwId, majorId, wClr; /*get core id by irq from subsys config*/ for (i = 0; i < CORE_MAX; i++) { if (dev->subsys_data.core_info.irq[i] == irq) { core_type = i; reg_offset = dev->subsys_data.core_info.offset[i]; break; } } /*If core is not reserved by any user, but irq is received, just clean it*/ spin_lock_irqsave(&owner_lock, flags); if (!dev->is_reserved[core_type]) { printk(KERN_DEBUG "hantroenc_isr:received IRQ but core is not reserved!\n"); irq_status = (u32)ioread32((void *)(dev->hwregs + reg_offset + 0x04)); if(irq_status & 0x01) { /* Disable HW when buffer over-flow happen * HW behavior changed in over-flow * in-pass, HW cleanup HWIF_ENC_E auto * new version: ask SW cleanup HWIF_ENC_E when buffer over-flow */ if(irq_status & 0x20) iowrite32(0, (void *)(dev->hwregs + reg_offset + 0x14)); /* clear all IRQ bits. (hwId >= 0x80006100) means IRQ is cleared by writting 1 */ hwId = ioread32((void *)dev->hwregs + reg_offset); majorId = (hwId & 0x0000FF00) >> 8; wClr = (majorId >= 0x61) ? irq_status: (irq_status & (~0x1FD)); iowrite32(wClr, (void *)(dev->hwregs + reg_offset + 0x04)); } spin_unlock_irqrestore(&owner_lock, flags); return IRQ_HANDLED; } spin_unlock_irqrestore(&owner_lock, flags); printk(KERN_DEBUG "hantroenc_isr:received IRQ!\n"); irq_status = (u32)ioread32((void *)(dev->hwregs + reg_offset + 0x04)); printk(KERN_DEBUG "irq_status of subsys %d core %d is:%x\n",dev->subsys_id,core_type,irq_status); if(irq_status & 0x01) { /* Disable HW when buffer over-flow happen * HW behavior changed in over-flow * in-pass, HW cleanup HWIF_ENC_E auto * new version: ask SW cleanup HWIF_ENC_E when buffer over-flow */ if(irq_status & 0x20) iowrite32(0, (void *)(dev->hwregs + reg_offset + 0x14)); /* clear all IRQ bits. (hwId >= 0x80006100) means IRQ is cleared by writting 1 */ hwId = ioread32((void *)dev->hwregs + reg_offset); majorId = (hwId & 0x0000FF00) >> 8; wClr = (majorId >= 0x61) ? irq_status: (irq_status & (~0x1FD)); iowrite32(wClr, (void *)(dev->hwregs + reg_offset + 0x04)); spin_lock_irqsave(&owner_lock, flags); dev->irq_received[core_type] = 1; dev->irq_status[core_type] = irq_status & (~0x01); spin_unlock_irqrestore(&owner_lock, flags); wake_up_interruptible_all(&enc_wait_queue); handled++; } if(!handled) { PDEBUG("IRQ received, but not hantro's!\n"); } return IRQ_HANDLED; } #ifdef hantroenc_DEBUG static void ResetAsic(hantroenc_t * dev) { int i,n; for (n=0;niosize; i += 4) { PDEBUG("\toffset %02X = %08X\n", i, ioread32(dev->hwregs + i)); } PDEBUG("Reg Dump End\n"); } #endif