mirror of
https://github.com/thead-yocto-mirror/vpu-vc8000e-kernel
synced 2026-06-21 17:12:35 +02:00
1459 lines
44 KiB
C
1459 lines
44 KiB
C
/****************************************************************************
|
|
*
|
|
* 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 <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
/* needed for __init,__exit directives */
|
|
#include <linux/init.h>
|
|
/* needed for remap_page_range
|
|
SetPageReserved
|
|
ClearPageReserved
|
|
*/
|
|
#include <linux/mm.h>
|
|
/* obviously, for kmalloc */
|
|
#include <linux/slab.h>
|
|
/* for struct file_operations, register_chrdev() */
|
|
#include <linux/fs.h>
|
|
/* standard error codes */
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
/* request_irq(), free_irq() */
|
|
#include <linux/interrupt.h>
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/semaphore.h>
|
|
#include <linux/spinlock.h>
|
|
/* needed for virt_to_phys() */
|
|
#include <asm/io.h>
|
|
#include <linux/pci.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/ioport.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <linux/version.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of.h>
|
|
|
|
|
|
/* 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;i<WAIT_NODE_NUM;i++)
|
|
{
|
|
temp_node = &res_wait_node[i];
|
|
if(temp_node->used_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;i<WAIT_NODE_NUM;i++)
|
|
{
|
|
temp_node = &res_wait_node[i];
|
|
if((temp_node->used_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;i<WAIT_NODE_NUM;i++)
|
|
{
|
|
if(i<dev_num)
|
|
sem_flag =1;
|
|
else
|
|
sem_flag =0;
|
|
node = &res_wait_node[i];
|
|
node->node_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<<core_type)))
|
|
continue;
|
|
|
|
core_info = ((i << 4) | core_type);
|
|
if((1 == CheckEncIrqbyPolling(dev, &core_info, &irq_status, &job_id)) && (core_info == i))
|
|
{
|
|
out->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)|(*core_info_tmp & 0x0FF));
|
|
*core_info_tmp = (*core_info_tmp | (1 << i));
|
|
if ((*core_info_tmp >> 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<<CORE_MMU))
|
|
mmu_hwregs[i][0] = hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[CORE_MMU];
|
|
if(hantroenc_data[i].subsys_data.core_info.type_info & (1<<CORE_MMU_1))
|
|
mmu_hwregs[i][1] = hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[CORE_MMU_1];
|
|
}
|
|
return (MMUIoctl(cmd, filp, arg, mmu_hwregs));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int hantroenc_open(struct inode *inode, struct file *filp)
|
|
{
|
|
int result = 0;
|
|
hantroenc_t *dev = hantroenc_data;
|
|
|
|
filp->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<<CORE_MMU)))
|
|
continue;
|
|
|
|
MMURelease(filp,hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[CORE_MMU]);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
wake_up_interruptible_all(&hw_queue);
|
|
|
|
if(dev->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<<CORE_MMU)))
|
|
continue;
|
|
|
|
result = MMUInit(hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[CORE_MMU]);
|
|
if(result == MMU_STATUS_NOT_FOUND)
|
|
printk(KERN_INFO "MMU does not exist!\n");
|
|
else if(result != MMU_STATUS_OK)
|
|
{
|
|
ReleaseIO();
|
|
goto err;
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* get the IRQ line */
|
|
for (i=0;i<total_subsys_num;i++)
|
|
{
|
|
if (hantroenc_data[i].is_valid==0)
|
|
continue;
|
|
|
|
for (j = 0; j < CORE_MAX; j++)
|
|
{
|
|
if(hantroenc_data[i].subsys_data.core_info.irq[j]!= -1)
|
|
{
|
|
result = request_irq(hantroenc_data[i].subsys_data.core_info.irq[j], hantroenc_isr,
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
|
|
SA_INTERRUPT | SA_SHIRQ,
|
|
#else
|
|
IRQF_SHARED,
|
|
#endif
|
|
"vc8000", (void *) &hantroenc_data[i]);
|
|
if(result == -EINVAL)
|
|
{
|
|
printk(KERN_ERR "hantroenc: Bad irq number or handler\n");
|
|
ReleaseIO();
|
|
goto err;
|
|
}
|
|
else if(result == -EBUSY)
|
|
{
|
|
printk(KERN_ERR "hantroenc: IRQ <%d> 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<total_subsys_num;i++)
|
|
{
|
|
if (hantroenc_data[i].is_valid==0)
|
|
continue;
|
|
//writel(0, hantroenc_data[i].hwregs + 0x14); /* disable HW */
|
|
//writel(0, hantroenc_data[i].hwregs + 0x04); /* clear enc IRQ */
|
|
|
|
/* free the core IRQ */
|
|
for (j = 0; j < total_core_num; j++)
|
|
{
|
|
if(hantroenc_data[i].subsys_data.core_info.irq[j] != -1)
|
|
{
|
|
free_irq(hantroenc_data[i].subsys_data.core_info.irq[j], (void *)&hantroenc_data[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HANTROMMU_SUPPORT
|
|
memset (mmu_hwregs, 0, MAX_SUBSYS_NUM*2);
|
|
|
|
for (i = 0; i < total_subsys_num; i++ ) {
|
|
if(hantroenc_data[i].subsys_data.core_info.type_info & (1<<CORE_MMU))
|
|
mmu_hwregs[i][0] = hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[CORE_MMU];
|
|
|
|
if(hantroenc_data[i].subsys_data.core_info.type_info & (1<<CORE_MMU_1))
|
|
mmu_hwregs[i][1] = hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[CORE_MMU_1];
|
|
}
|
|
MMUCleanup(mmu_hwregs);
|
|
#endif
|
|
|
|
ReleaseIO();
|
|
vfree(hantroenc_data);
|
|
|
|
unregister_chrdev(hantroenc_major, "vc8000");
|
|
|
|
printk(KERN_INFO "hantroenc: module removed\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct platform_driver encoder_hantrodec_driver = {
|
|
.probe = encoder_hantrodec_probe,
|
|
.remove = encoder_hantrodec_remove,
|
|
.driver = {
|
|
.name = "encoder_hantrodec",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(isp_of_match),
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int __init hantroenc_normal_init(void)
|
|
{
|
|
int ret = 0;
|
|
printk("enter %s\n",__func__);
|
|
ret = platform_driver_register(&encoder_hantrodec_driver);
|
|
if(ret)
|
|
{
|
|
pr_err("register platform driver failed!\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void __exit hantroenc_normal_cleanup(void)
|
|
{
|
|
printk("enter %s\n",__func__);
|
|
platform_driver_unregister(&encoder_hantrodec_driver);
|
|
return;
|
|
}
|
|
|
|
static int ReserveIO(void)
|
|
{
|
|
u32 hwid;
|
|
int i;
|
|
u32 found_hw = 0, hw_cfg;
|
|
u32 VC8000E_core_idx;
|
|
|
|
for (i=0;i<total_subsys_num;i++)
|
|
{
|
|
if(!request_mem_region
|
|
(hantroenc_data[i].subsys_data.cfg.base_addr, hantroenc_data[i].subsys_data.cfg.iosize, "vc8000"))
|
|
{
|
|
printk(KERN_INFO "hantroenc: failed to reserve HW regs\n");
|
|
continue;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0))
|
|
hantroenc_data[i].hwregs =
|
|
(volatile u8 *) ioremap_nocache(hantroenc_data[i].subsys_data.cfg.base_addr,
|
|
hantroenc_data[i].subsys_data.cfg.iosize);
|
|
#else
|
|
hantroenc_data[i].hwregs =
|
|
(volatile u8 *) ioremap(hantroenc_data[i].subsys_data.cfg.base_addr,
|
|
hantroenc_data[i].subsys_data.cfg.iosize);
|
|
#endif
|
|
|
|
if(hantroenc_data[i].hwregs == NULL)
|
|
{
|
|
printk(KERN_INFO "hantroenc: failed to ioremap HW regs\n");
|
|
ReleaseIO();
|
|
continue;
|
|
}
|
|
|
|
/*read hwid and check validness and store it*/
|
|
VC8000E_core_idx = GET_ENCODER_IDX(hantroenc_data[0].subsys_data.core_info.type_info);
|
|
if(!(hantroenc_data[i].subsys_data.core_info.type_info & (1<<CORE_VC8000E)))
|
|
VC8000E_core_idx = CORE_CUTREE;
|
|
hwid = (u32)ioread32((void *)hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[VC8000E_core_idx]);
|
|
printk(KERN_INFO"hwid=0x%08x\n", hwid);
|
|
|
|
/* check for encoder HW ID */
|
|
if( ((((hwid >> 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<<CORE_VC8000E);
|
|
if(hw_cfg & 0x00008000)
|
|
hantroenc_data[i].subsys_data.core_info.type_info |= (1<<CORE_VC8000EJ);
|
|
|
|
printk(KERN_INFO
|
|
"hantroenc: HW at base <%p> 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;n<total_subsys_num;n++)
|
|
{
|
|
if (dev[n].is_valid==0)
|
|
continue;
|
|
iowrite32(0, (void *)(dev[n].hwregs + 0x14));
|
|
for(i = 4; i < dev[n].subsys_data.cfg.iosize; i += 4)
|
|
{
|
|
iowrite32(0, (void *)(dev[n].hwregs + i));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dump_regs(unsigned long data)
|
|
{
|
|
hantroenc_t *dev = (hantroenc_t *) data;
|
|
int i;
|
|
|
|
PDEBUG("Reg Dump Start\n");
|
|
for(i = 0; i < dev->iosize; i += 4)
|
|
{
|
|
PDEBUG("\toffset %02X = %08X\n", i, ioread32(dev->hwregs + i));
|
|
}
|
|
PDEBUG("Reg Dump End\n");
|
|
}
|
|
#endif
|
|
|
|
|