diff options
Diffstat (limited to 'drivers/media/video/wmt')
-rw-r--r-- | drivers/media/video/wmt/com-lock.h | 58 | ||||
-rw-r--r-- | drivers/media/video/wmt/com-vd.h | 73 | ||||
-rw-r--r-- | drivers/media/video/wmt/wmt-codec.c | 857 | ||||
-rw-r--r-- | drivers/media/video/wmt/wmt-codec.h | 72 | ||||
-rw-r--r-- | drivers/media/video/wmt/wmt-lock.c | 512 | ||||
-rw-r--r-- | drivers/media/video/wmt/wmt-lock.h | 45 | ||||
-rw-r--r-- | drivers/media/video/wmt/wmt-vd.c | 541 | ||||
-rw-r--r-- | drivers/media/video/wmt/wmt-vd.h | 106 |
8 files changed, 2264 insertions, 0 deletions
diff --git a/drivers/media/video/wmt/com-lock.h b/drivers/media/video/wmt/com-lock.h new file mode 100644 index 00000000..477ddca2 --- /dev/null +++ b/drivers/media/video/wmt/com-lock.h @@ -0,0 +1,58 @@ +/*++ + * WonderMedia Codec Lock driver + * + * Copyright (c) 2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ +#ifndef COM_LOCK_H +/* To assert that only one occurrence is included */ +#define COM_LOCK_H + +/*-------------------- MODULE DEPENDENCY ---------------------------------*/ + +/*-------------------- EXPORTED PRIVATE TYPES-----------------------------*/ + + +/*Define wmt lock types*/ +enum { + lock_jpeg = 0, /* Lock for JPEG decoder */ + lock_video, /* Lock for MSVD decoders */ + lock_max +}; /* wmt_lock_type */ + +struct wmt_lock { + long lock_type; /* TYPE_JPEG or TYPE_VIDEO */ + long arg2; /* for IO_WMT_UNLOCK, the timeout value */ + int is_wait; /* is someone wait for this lock? */ +}; +#define wmt_lock_ioctl_arg struct wmt_lock + +/*--------------------- EXPORTED PRIVATE FUNCTIONS ----------------------*/ + +/*-------------------------------------------------------------------------- + Macros below are used for driver in IOCTL +--------------------------------------------------------------------------*/ +#define LOCK_IOC_MAGIC 'L' +#define IO_WMT_LOCK _IOWR(LOCK_IOC_MAGIC, 0, unsigned long) +#define IO_WMT_UNLOCK _IOWR(LOCK_IOC_MAGIC, 1, unsigned long) +#define IO_WMT_CHECK_WAIT _IOR(LOCK_IOC_MAGIC, 2, unsigned long) + + +#endif /* ifndef COM_LOCK_H */ + +/*=== END com-lock.h =====================================================*/ diff --git a/drivers/media/video/wmt/com-vd.h b/drivers/media/video/wmt/com-vd.h new file mode 100644 index 00000000..9ad58b17 --- /dev/null +++ b/drivers/media/video/wmt/com-vd.h @@ -0,0 +1,73 @@ +/*++ + * Copyright (c) 2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ + +#ifndef COM_VD_H +/* To assert that only one occurrence is included */ +#define COM_VD_H + +/*-------------------- MODULE DEPENDENCY ---------------------------------*/ +#ifdef __KERNEL__ +#include <mach/com-video.h> +#else +#include "com-video.h" +#endif + +/*-------------------- EXPORTED PRIVATE CONSTANTS ------------------------*/ + +#define VD_JPEG 0x0001 +#define VD_MAX 0x0002 + +/*-------------------------------------------------------------------------- +Following macros define hardware decoder type for vd_ioctl_cmd +--------------------------------------------------------------------------*/ +#define IOCTL_CMD_INIT(cmd, _struct_type_, vd_type, version) + +#define VD_IOCTL_CMD_M \ + unsigned int identity; /* decoder type */\ + unsigned int size /* size of full structure */ +/* End of VD_IOCTL_CMD_M */ + +/*------------------------------------------------------------------------- + Definitions of Struct +--------------------------------------------------------------------------*/ +struct vd_ioctl_cmd_s { + VD_IOCTL_CMD_M; +}; +#define vd_ioctl_cmd struct vd_ioctl_cmd_s + +struct vd_handle_s { + int vd_fd; + int mb_fd; +}; +#define vd_handle_t struct vd_handle_s + +/*-------------------------------------------------------------------------- + Macros below are used for driver in IOCTL +--------------------------------------------------------------------------*/ +#define VD_IOC_MAGIC 'k' +#define VD_IOC_MAXNR 1 + +/* VDIOSET_DECODE_LOOP: application send decode data to driver + (blocking mode in driver) */ +#define VDIOSET_DECODE_LOOP _IOWR(VD_IOC_MAGIC, 0, vd_ioctl_cmd) + +#endif /* ifndef COM_VD_H */ + +/*=== END com-vd.h ======================================================*/ diff --git a/drivers/media/video/wmt/wmt-codec.c b/drivers/media/video/wmt/wmt-codec.c new file mode 100644 index 00000000..393281b1 --- /dev/null +++ b/drivers/media/video/wmt/wmt-codec.c @@ -0,0 +1,857 @@ +/*++ + * Common interface for WonderMedia SoC hardware encoder and decoder drivers + * + * Copyright (c) 2008-2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ +#ifndef CODEC_C +#define CODEC_C + +#include <linux/device.h> /* For EXPORT_SYMBOL() */ +#include <linux/spinlock.h> +#include <mach/hardware.h> +#include <linux/module.h> +#include <linux/slab.h> /* for kmalloc and kfree */ +#include <linux/semaphore.h> /* for semaphore */ + +#include "wmt-codec.h" + +#define CFG_CODEC_PERFORM_EN /* Flag for codec performance analysis */ + + +/*#define CODEC_DEBUG*/ +#undef CODEC_DEBUG +#ifdef CODEC_DEBUG +#define DBG_MSG(fmt, args...) \ + do {\ + printk(KERN_INFO "{%s} " fmt, __func__, ## args);\ + } while (0) +#else +#define DBG_MSG(fmt, args...) +#endif + +#undef DBG_ERR +#define DBG_ERR(fmt, args...) \ + do {\ + printk(KERN_ERR "*E* {%s} " fmt, __func__, ## args);\ + } while (0) + +#undef PRINTK +#define PRINTK(fmt, args...) printk(KERN_INFO "{%s} " fmt, __func__, ## args) + +/*#define CODEC_REG_TRACE*/ +#ifdef CODEC_REG_TRACE +#define REG_WRITE(addr, val) \ + do {\ + PRINTK("REG_SET:0x%x -> 0x%0x\n", addr, val);\ + REG32_VAL(addr) = (val);\ + } while (0) +#else +#define REG_WRITE(addr, val) (REG32_VAL(addr) = (val)) +#endif + + +struct codec_lock { + struct semaphore sem; + unsigned int is_init; + unsigned int is_locked; +}; + +static struct codec_lock codec_lock[CODEC_MAX]; +static int msvd_clk_enable_cnt; +DEFINE_MUTEX(wmt_clk_mutex); + + +#ifdef CFG_CODEC_PERFORM_EN +static int wmt_codec_debug; + +struct wmt_tm { + unsigned long seq_id; /* seqence number */ + char name[16]; /* name */ + unsigned int initial; /* initial flag */ + unsigned int total_tm; /* total time */ + unsigned int interval_tm; /* interval time */ + unsigned int reset; /* reset counter */ + unsigned int total_cnt; /* total counter */ + unsigned int count; /* interval counter */ + unsigned int max; /* max time */ + unsigned int min; /* min time */ + unsigned int threshold; + struct timeval start; /* start time */ + struct timeval end; /* end time */ +} ; +#endif + +/*!************************************************************************* +* wmt_power_en +* +* Private Function +* +* \retval 0 if success +*/ +static int wmt_power_en(int enable) +{ + static DEFINE_SPINLOCK(wmt_power_lock); + unsigned long flags = 0; + unsigned int value; + int ret = 0; + static int wmt_open_cnt=0; + + if(enable){ + spin_lock_irqsave(&wmt_power_lock, flags); + wmt_open_cnt++; + spin_unlock_irqrestore(&wmt_power_lock, flags); + if(wmt_open_cnt == 1){ + value = REG32_VAL(PM_CTRL_BASE_ADDR + 0x0604); + *(volatile unsigned int *)(PM_CTRL_BASE_ADDR + 0x0604) = (value | 0x1); + while((REG32_VAL(PM_CTRL_BASE_ADDR + 0x0604) & 0xf5) != 0xf1){}; + } + }else { + spin_lock_irqsave(&wmt_power_lock, flags); + if( wmt_open_cnt >= 1) { + wmt_open_cnt--; + }else { + DBG_ERR("Unexpected WMT Codec power off, ignore it!\n"); + ret = -1; + } + spin_unlock_irqrestore(&wmt_power_lock, flags); + if(wmt_open_cnt == 0){ + value = REG32_VAL(PM_CTRL_BASE_ADDR + 0x0604); + *(volatile unsigned char *)(PM_CTRL_BASE_ADDR + 0x0604) = (value & ~(0x1)); + while((REG32_VAL(PM_CTRL_BASE_ADDR + 0x0604) & 0xf5) != 0x00){}; + } + } + return ret; +} /* End of wmt_power_en() */ + +/*!************************************************************************* +* msvd_power_en +* +* Private Function +* +* \retval 0 if success +*/ +static int msvd_power_en(int enable) +{ + static DEFINE_SPINLOCK(msvd_power_lock); + unsigned long flags = 0; + unsigned int value; + int ret = 0; + static int msvd_open_cnt = 0; + + if (enable) { + spin_lock_irqsave(&msvd_power_lock, flags); + msvd_open_cnt++; + spin_unlock_irqrestore(&msvd_power_lock, flags); + if(msvd_open_cnt == 1){ + value = REG32_VAL(PM_CTRL_BASE_ADDR + 0x062C); + *(volatile unsigned int *)(PM_CTRL_BASE_ADDR + 0x062C) = (value | 0x1); + while((REG32_VAL(PM_CTRL_BASE_ADDR + 0x062C) & 0xf5) != 0xf1){}; + } + } else { + spin_lock_irqsave(&msvd_power_lock, flags); + if( msvd_open_cnt >= 1) { + msvd_open_cnt--; + }else { + DBG_ERR("Unexpected MSVD Codec power off, ignore it!\n"); + ret = -1; + } + spin_unlock_irqrestore(&msvd_power_lock, flags); + if(msvd_open_cnt == 0){ + value = REG32_VAL(PM_CTRL_BASE_ADDR + 0x062C); + *(volatile unsigned int *)(PM_CTRL_BASE_ADDR + 0x062C) = (value & ~(0x1)); + while((REG32_VAL(PM_CTRL_BASE_ADDR + 0x062C) & 0xf5) != 0x00){}; + } + } + return 0; +} /* End of msvd_power_en() */ + +/*!************************************************************************* +* wmt_clock_en +* +* Public Function +*/ +/*! +* \brief +* Set source PRD table +* +* \retval 0 if success +*/ +int wmt_clock_en(int codec_type, int enable) +{ + static DEFINE_SPINLOCK(clk_lock); + unsigned long flags = 0; + + if ((codec_type < 0 ) || (codec_type >= CODEC_MAX)) { + DBG_ERR("Unsupported codec ID %d\n", codec_type); + return -1; + } + + if (codec_type == CODEC_VD_JPEG) { + spin_lock_irqsave(&clk_lock, flags); + if (enable) { + auto_pll_divisor(DEV_JDEC, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_MSVD, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_WMTVDU, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_WMTNA, CLK_ENABLE, 0, 0); + } else { + auto_pll_divisor(DEV_WMTNA, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_WMTVDU, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_MSVD, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_JDEC, CLK_DISABLE, 0, 0); + } + spin_unlock_irqrestore(&clk_lock, flags); + } else if (codec_type == CODEC_VD_MSVD) { + mutex_lock(&wmt_clk_mutex); + if (enable) { + auto_pll_divisor(DEV_WMTNA, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_CNMNA, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_MSVD, CLK_ENABLE, 0, 0); + } else { + auto_pll_divisor(DEV_MSVD, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_CNMNA, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_WMTNA, CLK_DISABLE, 0, 0); + } + mutex_unlock(&wmt_clk_mutex); + } else if (codec_type == CODEC_VE_H264) { + spin_lock_irqsave(&clk_lock, flags); + if (enable) { + auto_pll_divisor(DEV_WMTNA, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_MSVD, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_WMTVDU, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_H264, CLK_ENABLE, 0, 0); + } else { + auto_pll_divisor(DEV_H264, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_MSVD, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_WMTNA, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_WMTVDU, CLK_DISABLE, 0, 0); + } + spin_unlock_irqrestore(&clk_lock, flags); + } else if (codec_type == CODEC_VE_JPEG) { + spin_lock_irqsave(&clk_lock, flags); + if (enable) { + auto_pll_divisor(DEV_JENC, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_MSVD, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_WMTVDU, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_WMTNA, CLK_ENABLE, 0, 0); + } else { + auto_pll_divisor(DEV_WMTNA, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_WMTVDU, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_MSVD, CLK_DISABLE, 0, 0); + auto_pll_divisor(DEV_JENC, CLK_DISABLE, 0, 0); + } + spin_unlock_irqrestore(&clk_lock, flags); + } + return 0; +} /* End of wmt_clock_en() */ + +/*!************************************************************************* +* wmt_get_codec_clock_count +* +* Private Function +* +* \retval 0 if success +*/ +int wmt_get_codec_clock_count(void) +{ + return msvd_clk_enable_cnt; +} +EXPORT_SYMBOL(wmt_get_codec_clock_count); + +/*!************************************************************************* +* wmt_reset_codec_clock_count +* +* Private Function +* +* \retval 0 if success +*/ +void wmt_reset_codec_clock_count(void) +{ + msvd_clk_enable_cnt = 0; +} +EXPORT_SYMBOL(wmt_reset_codec_clock_count); + +/*!************************************************************************* +* wmt_codec_clock_en +* +* Public Function +*/ +/*! +* \brief +* +* \retval 0 if success +*/ +int wmt_codec_clock_en(int codec_type, int enable) +{ + static DEFINE_SPINLOCK(clk_lock); + unsigned long flags = 0; + int ret = 0; + + if ((codec_type < 0 ) || (codec_type >= CODEC_MAX)) { + DBG_ERR("Unsupported codec ID %d\n", codec_type); + return -1; + } + if (codec_type == CODEC_VD_JPEG) { + spin_lock_irqsave(&clk_lock, flags); + if (enable) { + REG_WRITE(MSVD_BASE_ADDR + 0x020, 0x0F); + } else { + REG_WRITE(MSVD_BASE_ADDR + 0x020, 0x00); + } + spin_unlock_irqrestore(&clk_lock, flags); + } else if (codec_type == CODEC_VD_MSVD) { + mutex_lock(&wmt_clk_mutex); + if(enable) { + msvd_clk_enable_cnt++; + if(msvd_clk_enable_cnt == 1){ + auto_pll_divisor(DEV_CNMVDU, CLK_ENABLE, 0, 0); + REG_WRITE(MSVD_BASE_ADDR + 0x000, 0x07); + } + }else { + if( msvd_clk_enable_cnt >= 1) { + msvd_clk_enable_cnt--; + if(msvd_clk_enable_cnt == 0){ + REG_WRITE(MSVD_BASE_ADDR + 0x000, 0x00); + auto_pll_divisor(DEV_CNMVDU, CLK_DISABLE, 0, 0); + } + }else { + DBG_ERR("Unexpected MSVD Codec power off, ignore it!\n"); + ret = -1; + } + } + mutex_unlock(&wmt_clk_mutex); + } else if (codec_type == CODEC_VE_H264 ) { + spin_lock_irqsave(&clk_lock, flags); + if(enable) { + REG_WRITE(MSVD_BASE_ADDR + 0x040, 0x0F); + }else { + REG_WRITE(MSVD_BASE_ADDR + 0x040, 0x00); + } + spin_unlock_irqrestore(&clk_lock, flags); + } else if (codec_type == CODEC_VE_JPEG) { + spin_lock_irqsave(&clk_lock, flags); + if (enable) { + REG_WRITE(MSVD_BASE_ADDR + 0x060, 0x0F); + } else { + REG_WRITE(MSVD_BASE_ADDR + 0x060, 0x00); + } + spin_unlock_irqrestore(&clk_lock, flags); + } + return ret; +} /* End of wmt_codec_clock_en() */ +EXPORT_SYMBOL(wmt_codec_clock_en); + +/*!************************************************************************* +* wmt_codec_pmc_ctl +* +* Public Function +*/ +/*! +* \brief +* +* \retval 0 if success +*/ +int wmt_codec_pmc_ctl(int codec_type, int enable) +{ + + if ((codec_type < 0 ) || (codec_type >= CODEC_MAX)) { + DBG_ERR("Unsupported codec ID %d\n", codec_type); + return -1; + } + if (enable){ + wmt_clock_en(codec_type, 1); /* WMT clock on */ + wmt_power_en(1); /* WMT Codec power cotrol *//*PMC.0x604 */ + if (codec_type == CODEC_VD_MSVD) + msvd_power_en(1); /* MSVD Codec power control */ /*PMC.0x62c */ + }else{ + if (codec_type == CODEC_VD_MSVD) + msvd_power_en(0); /* MSVD Codec power control */ /*PMC.0x62c */ + wmt_power_en(0); /* WMT Codec power cotrol *//*PMC.0x604 */ + + wmt_clock_en(codec_type, 0); /* WMT clock on */ + } + return 0; +} /* End of wmt_codec_pmc_ctl() */ +EXPORT_SYMBOL(wmt_codec_pmc_ctl); + +/*!************************************************************************* +* wmt_codec_msvd_reset +* +* Public Function +*/ +/*! +* \brief +* +* \retval 0 if success +*/ +int wmt_codec_msvd_reset(int codec_type) +{ + if (codec_type == CODEC_VD_MSVD) { + /* Do MSVD SW reset */ + REG_WRITE(MSVD_BASE_ADDR + 0x008, 0x07); + REG_WRITE(MSVD_BASE_ADDR + 0x008, 0x00); + + /* automatic disable clock , BIT0 AHB, BIT1 core, BIT2 NA bus*/ + REG_WRITE(MSVD_BASE_ADDR + 0x004, 0x00); + + /* Disable SRAM Power Down */ + REG_WRITE(MSVD_BASE_ADDR + 0x00C, 0x00); + } else if (codec_type == CODEC_VD_JPEG) { + REG_WRITE(MSVD_BASE_ADDR + 0x028, 0xF); /* Enable MSVD SW reset */ + REG_WRITE(MSVD_BASE_ADDR + 0x028, 0x0); /* Disable MSVD SW reset */ + REG_WRITE(MSVD_BASE_ADDR + 0x02C, 0x0); /* Disable MSVD SW reset */ + } else if (codec_type == CODEC_VE_H264) { + REG_WRITE(MSVD_BASE_ADDR + 0x048, 0xF); /* Enable MSVD SW reset */ + REG_WRITE(MSVD_BASE_ADDR + 0x048, 0x0); /* Disable MSVD SW reset */ + REG_WRITE(MSVD_BASE_ADDR + 0x044, 0xC); /* Enable Auto disable clock */ + REG_WRITE(MSVD_BASE_ADDR + 0x04C, 0x0); /* Disable SRAM Power down */ + } else if (codec_type == CODEC_VE_JPEG) { + REG_WRITE(MSVD_BASE_ADDR + 0x068, 0x0F); + REG_WRITE(MSVD_BASE_ADDR + 0x068, 0x00); + REG_WRITE(MSVD_BASE_ADDR + 0x06C, 0x00); + } + return 0; +} /* End of wmt_codec_msvd_reset() */ +EXPORT_SYMBOL(wmt_codec_msvd_reset); + +/*!************************************************************************* +* wmt_codec_lock +* +* Public Function +*/ +/*! +* \brief +* +* \retval 0 if success +*/ +int wmt_codec_lock(int codec_type, int timeout) +{ + struct semaphore *sem; + int ret = ret; + + if ((codec_type != CODEC_VD_JPEG) && (codec_type != CODEC_VE_JPEG)) + return -1; + + if (codec_lock[codec_type].is_init != 1) { + sema_init(&codec_lock[codec_type].sem, 1); + + codec_lock[codec_type].is_init = 1; + } + sem = &codec_lock[codec_type].sem; + + if (timeout == 0) { + ret = down_trylock(sem); + if (ret) + ret = -ETIME; /* reasonable if lock holded by other */ + } else if (timeout == -1) { + ret = down_interruptible(sem); + if (ret) + DBG_ERR("<%d> down_interruptible fail (ret: %d)\n", + codec_type, ret); + } else { + ret = down_timeout(sem, msecs_to_jiffies(timeout)); + if (ret) + DBG_MSG("<%d> down_timeout(%d ms) fail (ret: %d)\n", + codec_type, timeout, ret); + } + if (ret == 0) + codec_lock[codec_type].is_locked = 1; + return ret; +} /* End of wmt_codec_lock() */ +EXPORT_SYMBOL(wmt_codec_lock); + +/*!************************************************************************* +* wmt_codec_unlock +* +* Public Function +*/ +/*! +* \brief +* +* \retval 0 if success +*/ +int wmt_codec_unlock(int codec_type) +{ + if ((codec_type != CODEC_VD_JPEG) && (codec_type != CODEC_VE_JPEG)) + return -1; + + if (codec_lock[codec_type].is_locked == 1) { + up(&codec_lock[codec_type].sem); + codec_lock[codec_type].is_locked = 0; + } else { + DBG_ERR("Try to unlock non-locked sem (%s)", + (codec_type == CODEC_VD_JPEG) ? "jdec" : "jenc"); + return -1; + } + return 0; +} /* End of wmt_codec_unlock() */ +EXPORT_SYMBOL(wmt_codec_unlock); + +/*!************************************************************************* +* wmt_codec_write_prd +* +* Public Function +*/ +/*! +* \brief +* Transfer the buffer address as PRD foramt +* +* \parameter +* prd_addr [IN] Phyical address +* +* \retval 0 if success +*/ +int wmt_codec_write_prd( + unsigned int src_buf, + unsigned int src_size, + unsigned int prd_addr_in, + unsigned int prd_buf_size) +{ + #define PRD_MAX_SIZE (60*1024) + + unsigned char *buf_addr = (unsigned char *)src_buf; + unsigned int buf_size = src_size; + unsigned int *prd_addr = (unsigned int *)prd_addr_in; + unsigned int items; + unsigned int count = 0; + /*---------------------------------------------------------------------- + Transfer the input address as PRD foramt + ----------------------------------------------------------------------*/ + DBG_MSG("src_buf: 0x%x, src_size: %d, prd_addr_in: 0x%x\n", + src_buf, src_size, prd_addr_in); + items = prd_buf_size/8; + while (buf_size > 0) { + if (buf_size > PRD_MAX_SIZE) { + *prd_addr++ = (unsigned int)buf_addr; + *prd_addr++ = PRD_MAX_SIZE; + buf_size -= PRD_MAX_SIZE; + buf_addr += PRD_MAX_SIZE; + } else { + *prd_addr++ = (unsigned int)buf_addr; + *prd_addr++ = (0x80000000 | buf_size); + buf_size = 0; + } + count++; + if (count > items) + return -1; + } + return 0; +} /* End of wmt_codec_write_prd() */ +EXPORT_SYMBOL(wmt_codec_write_prd); + +/*!************************************************************************* +* wmt_codec_dump_prd +* +* Public Function +*/ +/*! +* \brief +* Dump data in PRD foramt for buffer +* +* \parameter +* prd_addr [IN] Phyical address +* +* \retval 0 if success +*/ +int wmt_codec_dump_prd(unsigned int prd_virt_in, int dump_data) +{ + unsigned int prd_addr_in, prd_data_size = 0; + unsigned int addr, len; + int i, j; + + prd_addr_in = prd_virt_in; + for (i = 0; ; i += 2) { + addr = *(unsigned int *)(prd_addr_in + i * 4); + len = *(unsigned int *)(prd_addr_in + (i + 1) * 4); + prd_data_size += (len & 0xFFFF); + + PRINTK("[%02d]Addr: 0x%08x\n", i, addr); + PRINTK(" Len: 0x%08x (%d)\n", len, (len & 0xFFFF)); + + if (dump_data) { + unsigned char *ptr; + + ptr = (unsigned char *)phys_to_virt(addr); + for (j = 0; j < (len & 0xFFFF); j++) { + if ((j%16) == 15) + PRINTK("0x%02x\n", *ptr); + else + PRINTK("0x%02x ", *ptr); + ptr++; + } + } + if (len & 0x80000000) + break; + } /* for(i=0; ; i+=2) */ + PRINTK("Data size in PRD table: %d\n", prd_data_size); + + return 0; +} /* End of wmt_codec_dump_prd() */ +EXPORT_SYMBOL(wmt_codec_dump_prd); + +/*!************************************************************************* +* check_debug_option +* +* Private Function +*/ +/*! +* \brief +* Initial VD timer +* +* \retval 0 if success +*/ +#ifdef CFG_CODEC_PERFORM_EN +static void check_debug_option(void) +{ + extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); + + static int s_first_run; + + if (s_first_run == 0) { + char buf[80] = {0}; + int varlen = 80; + + /* Read u-boot parameter to decide value of wmt_codec_debug */ + if (wmt_getsyspara("wmt.codec.debug", buf, &varlen) == 0) + wmt_codec_debug = simple_strtol(buf, NULL, 10); + + s_first_run = 1; + } +} /* End of check_debug_option() */ +#endif /* #ifdef CFG_CODEC_PERFORM_EN */ + +/*!************************************************************************* +* wmt_codec_timer_init +* +* API Function +*/ +/*! +* \brief +* Initial VD timer +* +* \retval 0 if success +*/ +int wmt_codec_timer_init( + void **pphandle, + const char *name, + unsigned int count, + int threshold_ms) +{ +#ifdef CFG_CODEC_PERFORM_EN + static atomic_t s_Seq_id = ATOMIC_INIT(0); + struct wmt_tm *pcodec; + + check_debug_option(); + + if (wmt_codec_debug == 0) + return 0; + + pcodec = kmalloc(sizeof(struct wmt_tm), GFP_KERNEL); + if (pcodec == 0) + DBG_ERR("Allocate %d bytes fail\n", sizeof(struct wmt_tm)); + + memset(pcodec, 0, sizeof(struct wmt_tm)); + + pcodec->reset = count; + pcodec->threshold = threshold_ms * 1000; /* us */ + pcodec->max = 0; + pcodec->min = 0xFFFFFFF; + pcodec->initial = 1; + strcpy(pcodec->name, name); + + pcodec->seq_id = (unsigned long)(atomic_add_return(1, &s_Seq_id)); + + *pphandle = pcodec; +#endif /* #ifdef CFG_CODEC_PERFORM_EN */ + return 0; +} /* End of wmt_codec_timer_init() */ +EXPORT_SYMBOL(wmt_codec_timer_init); + +/*!************************************************************************* +* wmt_codec_timer_reset_count +* +* API Function +*/ +/*! +* \brief +* Release VD timer +* +* \retval 0 if success +*/ +int wmt_codec_timer_reset(void *phandle, unsigned int count, int threshold_ms) +{ +#ifdef CFG_CODEC_PERFORM_EN + struct wmt_tm *pcodec = (struct wmt_tm *)phandle; + + if (pcodec) { + pcodec->reset = count; + pcodec->threshold = threshold_ms * 1000; /* us */ + } +#endif /* #ifdef CFG_CODEC_PERFORM_EN */ + return 0; +} /* End of wmt_codec_timer_reset_count() */ +EXPORT_SYMBOL(wmt_codec_timer_reset); + +/*!************************************************************************* +* wmt_codec_timer_start +* +* API Function +*/ +/*! +* \brief +* Start a VD timer +* +* \retval 0 if success +*/ +int wmt_codec_timer_start(void *phandle) +{ +#ifdef CFG_CODEC_PERFORM_EN + struct wmt_tm *pcodec = (struct wmt_tm *)phandle; + + if (wmt_codec_debug == 0) + return 0; + + if (pcodec->initial != 1) { + DBG_ERR("Timer was not initialized!\n"); + return -1; + } + do_gettimeofday(&pcodec->start); +#endif + return 0; +} /* End of wmt_codec_timer_start()*/ +EXPORT_SYMBOL(wmt_codec_timer_start); + +/*!************************************************************************* +* wmt_codec_timer_stop +* +* API Function +*/ +/*! +* \brief +* Stop a VD timer +* +* \retval 0 if success +*/ +int wmt_codec_timer_stop(void *phandle) +{ +#ifdef CFG_CODEC_PERFORM_EN + struct wmt_tm *pcodec = (struct wmt_tm *)phandle; + int this_time; + + if (wmt_codec_debug == 0) + return 0; + + if (pcodec->initial != 1) { + DBG_ERR("timer was not initialized!\n"); + return -1; + } + do_gettimeofday(&pcodec->end); + + /* unit in us */ + if (pcodec->start.tv_sec == pcodec->end.tv_sec) { + this_time = pcodec->end.tv_usec - pcodec->start.tv_usec; + } else { + this_time = (pcodec->end.tv_sec - pcodec->start.tv_sec)*1000000 + + (pcodec->end.tv_usec - pcodec->start.tv_usec); + } + if (this_time < 0) { + PRINTK("Start sec: %ld, usec: %ld\n", + pcodec->start.tv_sec, pcodec->start.tv_usec); + PRINTK("End sec: %ld, usec: %ld\n", + pcodec->end.tv_sec, pcodec->end.tv_usec); + } + + pcodec->total_tm += this_time; + pcodec->interval_tm += this_time; + pcodec->total_cnt++; + + if (this_time >= pcodec->max) + pcodec->max = this_time; + if (this_time <= pcodec->min) + pcodec->min = this_time; + + if (pcodec->threshold && (this_time > pcodec->threshold)) { + PRINTK("[%s] (%d) Decode time(%d) over %d (usec)\n", + pcodec->name, pcodec->total_cnt, + this_time, pcodec->threshold); + } + pcodec->count++; + if ((pcodec->reset != 0) && (pcodec->count >= pcodec->reset)) { + PRINTK("=================================================\n"); + PRINTK("[%s] Avg. time = %d (usec)\n", + pcodec->name, pcodec->interval_tm/pcodec->count); + PRINTK("(~ %d) Decode Time Range[%d ~ %d](usec)\n", + pcodec->total_cnt, pcodec->min, pcodec->max); + pcodec->interval_tm = 0; + pcodec->count = 0; + } +#endif /* #ifdef CFG_CODEC_PERFORM_EN */ + return 0; +} /* End of wmt_codec_timer_stop()*/ +EXPORT_SYMBOL(wmt_codec_timer_stop); + +/*!************************************************************************* +* wmt_codec_timer_exit +* +* API Function +*/ +/*! +* \brief +* Release VD timer +* +* \retval 0 if success +*/ +int wmt_codec_timer_exit(void *phandle) +{ +#ifdef CFG_CODEC_PERFORM_EN + struct wmt_tm *pcodec = (struct wmt_tm *)phandle; + + if (wmt_codec_debug == 0) + return 0; + + if (pcodec == 0) + DBG_ERR("Illegal NULL handle!\n"); + + if (pcodec->initial != 1) { + DBG_ERR("Codec(%s) timer was not initialized!\n", pcodec->name); + return -1; + } + if (pcodec->total_cnt) { + unsigned int avg_tm = pcodec->total_tm/pcodec->total_cnt; + PRINTK("=== [seq_id: %ld %s] Timer status:\n", + pcodec->seq_id, pcodec->name); + PRINTK("Total count = %d\n", pcodec->total_cnt); + PRINTK("Total time = %d (usec)\n", pcodec->total_tm); + PRINTK("Avg. time = %d (usec)\n", avg_tm); + PRINTK("Max time = %d (usec)\n", pcodec->max); + PRINTK("Min time = %d (usec)\n", pcodec->min); + PRINTK("==========================================\n"); + } + /* reset all */ + memset(pcodec, 0, sizeof(struct wmt_tm)); + kfree(pcodec); +#endif /* #ifdef CFG_CODEC_PERFORM_EN */ + return 0; +} /* End of wmt_codec_timer_exit()*/ +EXPORT_SYMBOL(wmt_codec_timer_exit); + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_LICENSE("GPL"); + +/*--------------------End of Function Body -----------------------------------*/ +#endif /* ifndef CODEC_C */ diff --git a/drivers/media/video/wmt/wmt-codec.h b/drivers/media/video/wmt/wmt-codec.h new file mode 100644 index 00000000..86e62f20 --- /dev/null +++ b/drivers/media/video/wmt/wmt-codec.h @@ -0,0 +1,72 @@ +/*++ + * Common interface for WonderMedia SoC hardware encoder and decoder drivers + * + * Copyright (c) 2008-2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ +#ifndef CODEC_H +#define CODEC_H + +/*-------------------- MODULE DEPENDENCY ---------------------------------*/ + +enum { + CODEC_VD_JPEG, + CODEC_VD_MSVD, + CODEC_VE_H264, + CODEC_VE_JPEG, + CODEC_MAX +}; /* codec_type */ + +/*--------------------- EXPORTED PRIVATE FUNCTIONS ----------------------*/ + +int wmt_get_codec_clock_count(void); +void wmt_reset_codec_clock_count(void); + +int wmt_codec_pmc_ctl(int codec_type, int enable); +int wmt_codec_clock_en(int codec_type, int enable); +int wmt_codec_msvd_reset(int codec_type); + +int wmt_codec_lock(int codec_type, int timeout); +int wmt_codec_unlock(int codec_type); + +int wmt_codec_write_prd( + unsigned int src_buf, + unsigned int src_size, + unsigned int prd_addr_in, + unsigned int prd_buf_size); + +int wmt_codec_dump_prd(unsigned int prd_virt_in, int dump_data); + +int wmt_codec_timer_init( + void **ppcodec, + const char *name, + unsigned int count, + int threshold_ms); + +int wmt_codec_timer_reset( + void *phandle, + unsigned int count, + int threshold_ms); + +int wmt_codec_timer_start(void *pcodec); +int wmt_codec_timer_stop(void *pcodec); +int wmt_codec_timer_exit(void *pcodec); + +/*=== END wmt-codec.h ====================================================*/ +#endif /* #ifndef CODEC_H */ + diff --git a/drivers/media/video/wmt/wmt-lock.c b/drivers/media/video/wmt/wmt-lock.c new file mode 100644 index 00000000..7bf72ce9 --- /dev/null +++ b/drivers/media/video/wmt/wmt-lock.c @@ -0,0 +1,512 @@ +/*++ + * WonderMedia Codec Lock driver + * + * Copyright (c) 2008-2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ + +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/cdev.h> +#include <linux/pci.h> +#include <asm/uaccess.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <linux/major.h> + +#include "com-lock.h" + +#define WMT_LOCK_NAME "wmt-lock" +#define WMT_LOCK_MAJOR MB_MAJOR /* same as memblock driver */ +#define WMT_LOCK_MINOR 1 /* 0 is /dev/mbdev, 1 is /dev/wmt-lock */ + +#define MAX_LOCK_JDEC 1 /* Max lock number for JPEG decoder */ + +/* Even for multi-decoding, this value should <= 4 */ +#define MAX_LOCK_VDEC 4 + +#define MAX_LOCK_TYPE 3 /* JPEG, MSVD & encoders */ + +#define MAX_LOCK_OWNER (MAX_LOCK_JDEC + MAX_LOCK_VDEC) + + +static struct class *wmt_lock_class; + + +#undef WMT_LOCK_DEBUG +/*#define WMT_LOCK_DEBUG*/ + +#ifdef WMT_LOCK_DEBUG +#define P_DEBUG(fmt, args...) printk(KERN_INFO "["WMT_LOCK_NAME"] " fmt , ## args) +#else +#define P_DEBUG(fmt, args...) ((void)(0)) +#endif + +#define P_INFO(fmt, args...) printk(KERN_INFO "[wmt-lock] " fmt , ## args) +#define P_WARN(fmt, args...) printk(KERN_WARNING "[wmt-lock] *W* " fmt, ## args) +#define P_ERROR(fmt, args...) printk(KERN_ERR "[wmt-lock] *E* " fmt , ## args) + + +static struct cdev wmt_lock_cdev; + +struct lock_owner { + pid_t pid; + void *private_data; + char comm[TASK_COMM_LEN]; + const char *type; +}; + +struct semaphore lock_sem_jpeg; +struct semaphore lock_sem_video; +struct semaphore lock_sem_encoder; + +struct lock_owner_s { + const char *type; + struct semaphore *sem; + unsigned int max_lock_num; + struct lock_owner owners[MAX_LOCK_OWNER]; +}; + +struct lock_owner_s gLockers[MAX_LOCK_TYPE]; + +static spinlock_t gSpinlock; + + +/*!************************************************************************* +* get_sema +* +* Private Function +* +* \brief +* +* \retval ponters to specified semaphore +*/ +static struct semaphore *get_sema(long lock_type) +{ + return gLockers[lock_type].sem; +} + +/*!************************************************************************* +* set_owner +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static struct lock_owner *set_owner(long lock_type, struct file *filp) +{ + struct lock_owner *o; + int i; + + for (i = 0; i < gLockers[lock_type].max_lock_num; i++) { + if (gLockers[lock_type].owners[i].pid == 0) { + o = &gLockers[lock_type].owners[i]; + o->private_data = filp->private_data; + o->pid = current->pid; + memcpy(o->comm, current->comm, TASK_COMM_LEN); + return o; + } + } + P_ERROR("Set owner fail because of %s lock full\n", + gLockers[lock_type].type); + return 0; +} + +/*!************************************************************************* +* get_owner +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static struct lock_owner *get_owner(long lock_type) +{ + struct lock_owner *o; + int i; + + for (i = 0; i < gLockers[lock_type].max_lock_num; i++) { + o = &gLockers[lock_type].owners[i]; + if (o->pid == current->pid) + return o; + } + return NULL; +} + +/*!************************************************************************* +* check_busy +* +* Private Function +* +* \brief +* +* \retval 0 if not busy, otherwise return 1 +*/ +static int check_busy(long lock_type) +{ + struct lock_owner *o; + int i; + + for (i = 0; i < gLockers[lock_type].max_lock_num; i++) { + o = &gLockers[lock_type].owners[i]; + if (o->pid == current->pid) + return 1; /* Busy */ + } + return 0; +} + +/*!************************************************************************* +* lock_read_proc +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static int lock_read_proc( + char *page, char **start, + off_t offset, + int len, + int *eof, + void *data) +{ + char *p = page; + struct lock_owner *o; + int i, j; + + for (i = 0; i < MAX_LOCK_TYPE; i++) { + p += sprintf(p, "------ %s lock ------\n", gLockers[i].type); + for (j = 0; j < gLockers[i].max_lock_num; j++) { + o = &gLockers[i].owners[j]; + if (o->pid != 0) + p += sprintf(p, "%s lock : occupied by [%s,%d]\n", + gLockers[i].type, o->comm, o->pid); + else + p += sprintf(p, "%s lock : Free\n", gLockers[i].type); + } + } + return p - page; +} + +/*!************************************************************************* +* wmt_lock_ioctl +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static long wmt_lock_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct lock_owner *o; + struct semaphore *sem; + struct wmt_lock *lock_arg; + long timeout; + unsigned long flags = 0; + + /* check ioctl type and number, if fail return EINVAL */ + if (_IOC_TYPE(cmd) != LOCK_IOC_MAGIC) { + P_WARN("ioctl unknown cmd %X, type %X by [%s,%d]\n", + cmd, _IOC_TYPE(cmd), current->comm, current->pid); + return -EINVAL; + } + + if (!access_ok(VERIFY_READ, (void __user *)arg, sizeof(struct wmt_lock))) { + P_WARN("ioctl access_ok failed, cmd %X, type %X by [%s,%d]\n", + cmd, _IOC_TYPE(cmd), current->comm, current->pid); + return -EFAULT; + } + + lock_arg = (struct wmt_lock *)arg; + + if (lock_arg->lock_type >= MAX_LOCK_TYPE || lock_arg->lock_type < 0) { + P_WARN("invalid lock type %ld by [%s,%d]\n", + lock_arg->lock_type, current->comm, current->pid); + return -E2BIG; + } + + spin_lock_irqsave(&gSpinlock, flags); + sem = get_sema(lock_arg->lock_type); + + switch (cmd) { + case IO_WMT_LOCK: + timeout = lock_arg->arg2; + /* check if the current thread already get the lock */ + if (check_busy(lock_arg->lock_type)) { + P_WARN("Recursive %s lock by [%s,%d]\n", + gLockers[lock_arg->lock_type].type, + current->comm, current->pid); + return -EBUSY; + } + + if (timeout == 0) { + ret = down_trylock(sem); + if (ret) + ret = -ETIME; /* reasonable if lock holded by other */ + } else if (timeout == -1) { + ret = down_interruptible(sem); + if (ret) + P_INFO("Require %s lock error %d by [%s,%d]\n", + gLockers[lock_arg->lock_type].type, ret, + current->comm, current->pid); + } else { + /* require lock with a timeout value, please beware + this function can't exit when interrupt */ + spin_unlock_irqrestore(&gSpinlock, flags); + ret = down_timeout(sem, msecs_to_jiffies(timeout)); + spin_lock_irqsave(&gSpinlock, flags); + } + if (ret == 0) { + o = set_owner(lock_arg->lock_type, filp); + if (o) + P_DEBUG("%s is locked by [%s,%d], seq %d\n", + o->type, o->comm, o->pid, (int)filp->private_data); + } + break; + + case IO_WMT_UNLOCK: + o = get_owner(lock_arg->lock_type); + if (o == NULL) { + P_WARN("Unnecessary %s unlock from [%s,%d] when lock is free\n", + gLockers[lock_arg->lock_type].type, + current->comm, current->pid); + ret = -EACCES; + } else if (filp->private_data == o->private_data) { + P_DEBUG("%s is unlocked by [%s,%d], seq %d\n", + o->type, o->comm, o->pid, (int)filp->private_data); + o->pid = 0; + o->private_data = 0; + up(sem); + ret = 0; + } else{ + P_WARN("Unexpected %s unlock from [%s,%d], hold by [%s,%d] now\n", + o->type, current->comm, current->pid, o->comm, o->pid); + ret = -EACCES; + } + break; + + case IO_WMT_CHECK_WAIT: + if (sem->count > 0) + lock_arg->is_wait = 0; + else { + if ((sem->wait_list.prev == 0) && (sem->wait_list.next == 0)) + lock_arg->is_wait = 0; + else + lock_arg->is_wait = 1; + } + P_INFO("sem->count: %d\n", sem->count); + P_INFO("lock_arg->is_wait: %d\n", lock_arg->is_wait); + break; + + default: + P_WARN("ioctl unknown cmd 0x%X by [%s,%d]\n", + cmd, current->comm, current->pid); + ret = -EINVAL; + } + spin_unlock_irqrestore(&gSpinlock, flags); + + return ret; +} + +/*!************************************************************************* +* wmt_lock_open +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static int wmt_lock_open(struct inode *inode, struct file *filp) +{ + static atomic_t lock_seq_id = ATOMIC_INIT(0); + + /* use a sequence number as the file open id */ + filp->private_data = (void *)(atomic_add_return(1, &lock_seq_id)); + P_DEBUG("open by [%s,%d], seq %d\n", + current->comm, current->pid, (int)filp->private_data); + return 0; +} + +/*!************************************************************************* +* wmt_lock_release +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static int wmt_lock_release(struct inode *inode, struct file *filp) +{ + int i, j; + + for (i = 0; i < MAX_LOCK_TYPE; i++) { + for (j = 0; j < gLockers[i].max_lock_num; j++) { + struct lock_owner *o = &gLockers[i].owners[j]; + if (o->pid != 0 && filp->private_data == o->private_data) { + P_WARN("Auto free %s lock hold by [%s,%d]\n", + o->type, o->comm, o->pid); + o->pid = 0; + o->private_data = 0; + up(get_sema(i)); + } + } + } + P_DEBUG("Release by [%s,%d], seq %d\n", + current->comm, current->pid, (int)filp->private_data); + return 0; +} + +static const struct file_operations wmt_lock_fops = { + .owner = THIS_MODULE, + .open = wmt_lock_open, + .unlocked_ioctl = wmt_lock_ioctl, + .release = wmt_lock_release, +}; + +static void check_multi_vd_count(int *max_vd_count) +{ + extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); + + char buf[80] = {0}; + int varlen = 80; + int max_count = 1; + + /* Read u-boot parameter to decide value of wmt_codec_debug */ + /*---------------------------------------------------------------------- + Check wmt.codec.debug + ----------------------------------------------------------------------*/ + if (wmt_getsyspara("wmt.multi.vd.max", buf, &varlen) == 0) + max_count = simple_strtol(buf, NULL, 10); + + if (max_count < 1) + max_count = 1; + if (max_count > MAX_LOCK_VDEC) + max_count = MAX_LOCK_VDEC; + + *max_vd_count = max_count; + + return; +} /* End of check_debug_option() */ + +/*!************************************************************************* +* wmt_lock_init +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static int __init wmt_lock_init(void) +{ + dev_t dev_id; + int max_vd_count; + int ret, i, j; + + dev_id = MKDEV(WMT_LOCK_MAJOR, WMT_LOCK_MINOR); + ret = register_chrdev_region(dev_id, 1, WMT_LOCK_NAME); + if (ret < 0) { + P_ERROR("can't register %s device %d:%d, ret %d\n", + WMT_LOCK_NAME, WMT_LOCK_MAJOR, WMT_LOCK_MINOR, ret); + return ret; + } + + cdev_init(&wmt_lock_cdev, &wmt_lock_fops); + ret = cdev_add(&wmt_lock_cdev, dev_id, 1); + if (ret) { + P_ERROR("cdev add error(%d).\n", ret); + unregister_chrdev_region(dev_id, 1); + return ret; + } + + /* let udev to handle /dev/wmt-lock */ + wmt_lock_class = class_create(THIS_MODULE, WMT_LOCK_NAME); + device_create(wmt_lock_class, NULL, dev_id, NULL, "%s", WMT_LOCK_NAME); + + create_proc_read_entry(WMT_LOCK_NAME, 0, NULL, lock_read_proc, NULL); + P_INFO("init ok, major=%d, minor=%d\n", WMT_LOCK_MAJOR, WMT_LOCK_MINOR); + + spin_lock_init(&gSpinlock); + + check_multi_vd_count(&max_vd_count); + + /* Init sema for JPEG decoder */ + sema_init(&lock_sem_jpeg, MAX_LOCK_JDEC); + gLockers[lock_jpeg].type = "jdec"; + gLockers[lock_jpeg].sem = &lock_sem_jpeg; + gLockers[lock_jpeg].max_lock_num = MAX_LOCK_JDEC; + + /* Init sema for MSVD decoders */ + sema_init(&lock_sem_video, max_vd_count); + gLockers[lock_video].type = "vdec"; + gLockers[lock_video].sem = &lock_sem_video; + gLockers[lock_video].max_lock_num = max_vd_count; + + for (i = 0; i < MAX_LOCK_TYPE; i++) { + for (j = 0; j < gLockers[i].max_lock_num; j++) { + struct lock_owner *o = &gLockers[i].owners[j]; + o->type = gLockers[i].type; + } + } + return ret; +} + +/*!************************************************************************* +* wmt_lock_cleanup +* +* Private Function +* +* \brief +* +* \retval 0 if success, error code if fail +*/ +static void __exit wmt_lock_cleanup(void) +{ + dev_t dev_id = MKDEV(WMT_LOCK_MAJOR, WMT_LOCK_MINOR); + + cdev_del(&wmt_lock_cdev); + + /* let udev to handle /dev/wmt-lock */ + device_destroy(wmt_lock_class, dev_id); + class_destroy(wmt_lock_class); + + unregister_chrdev_region(dev_id, 1); + remove_proc_entry(WMT_LOCK_NAME, NULL); + + P_INFO("cleanup done\n"); +} + +module_init(wmt_lock_init); +module_exit(wmt_lock_cleanup); + + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT Codec Lock device driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/video/wmt/wmt-lock.h b/drivers/media/video/wmt/wmt-lock.h new file mode 100644 index 00000000..cf15aee3 --- /dev/null +++ b/drivers/media/video/wmt/wmt-lock.h @@ -0,0 +1,45 @@ +/*++ + * Common interface for WonderMedia SoC hardware decoder drivers + * + * Copyright (c) 2008-2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ +#ifndef WMTLOCK_H +#define WMTLOCK_H + +/*-------------------- MODULE DEPENDENCY -------------------------------------*/ + +/* Even for multi-decoding, this value should <= 4 */ +#define MAX_LOCK_NUM_VDEC 4 /* Max lock number for MSVD decoders */ + + + +/*-------------------- EXPORTED PRIVATE VARIABLES -----------------------------*/ +#ifdef WMTLOCK_C + #define EXTERN +#else + #define EXTERN extern +#endif /* ifdef WMTLOCK_C */ + +#undef EXTERN + +/*--------------------- EXPORTED PRIVATE FUNCTIONS ---------------------------*/ + +/*=== END wmt-lock.h ==========================================================*/ +#endif /* #ifndef WMTLOCK_H */ + diff --git a/drivers/media/video/wmt/wmt-vd.c b/drivers/media/video/wmt/wmt-vd.c new file mode 100644 index 00000000..435c375c --- /dev/null +++ b/drivers/media/video/wmt/wmt-vd.c @@ -0,0 +1,541 @@ +/*++ + * Common interface for WonderMedia SoC hardware decoder drivers + * + * Copyright (c) 2008-2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ + +#include "wmt-vd.h" + +#define THE_MB_USER "WMT-VD" + +#define DRIVER_NAME "wmt-vd" + +#define VD_DEV_NAME "wmt-vd" + +/*#define VD_DEBUG_DEF*/ +/*#define VD_WORDY_DEF*/ + + +#define VD_INFO(fmt, args...) \ + do {\ + printk(KERN_INFO "[wmt-vd] " fmt , ## args);\ + } while (0) + +#define VD_WARN(fmt, args...) \ + do {\ + printk(KERN_WARNING "[wmt-vd] " fmt , ## args);\ + } while (0) + +#define VD_ERROR(fmt, args...) \ + do {\ + printk(KERN_ERR "[wmt-vd] " fmt , ## args);\ + } while (0) + +#ifdef VD_DEBUG_DEF +#define VD_DBG(fmt, args...) \ + do {\ + printk(KERN_DEBUG "[wmt-vd] %s: " fmt, __func__ , ## args);\ + } while (0) +#else +#define VD_DBG(fmt, args...) +#undef VD_WORDY_DEF +#endif + +#ifdef VD_WORDY_DEF +#define VD_WDBG(fmt, args...) VD_DBG(fmt, args...) +#else +#define VD_WDBG(fmt, args...) +#endif + + +static struct class *vd_class; +static int videodecoder_minor; +static int videodecoder_dev_nr = 1; +static struct cdev *videodecoder_cdev; + +static struct videodecoder *decoders[VD_MAX] = {0}; + +static struct vd_resource vd_res = { + .prdt = { NULL, 0x0, MAX_INPUT_BUF_SIZE}, +}; + +static DEFINE_SEMAPHORE(vd_sem); + +static int wmt_vd_open(struct inode *inode, struct file *filp) +{ + int ret = -EINVAL; + unsigned int idx = VD_MAX; + + down(&vd_sem); + idx = iminor(inode); + + if (idx < VD_MAX && decoders[idx]) { + struct videodecoder *vd = decoders[idx]; + + if (vd && vd->fops.open) { + if (idx == VD_JPEG) + filp->private_data = (void *)&(vd_res.prdt); + ret = vd->fops.open(inode, filp); + } + VD_DBG("decoder %s opened.\n", vd->name); + } + up(&vd_sem); + + return ret; +} /* End of wmt_vd_open() */ + +static int wmt_vd_release(struct inode *inode, struct file *filp) +{ + int ret = -EINVAL; + unsigned int idx = VD_MAX; + + down(&vd_sem); + idx = iminor(inode); + + if (idx < VD_MAX && decoders[idx]) { + struct videodecoder *vd = decoders[idx]; + if (vd && vd->fops.release) + ret = vd->fops.release(inode, filp); + VD_DBG("decoder %s closed.\n", vd->name); + } + up(&vd_sem); + + return ret; +} /* End of wmt_vd_release() */ + +static long wmt_vd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = -EINVAL; + unsigned int idx = VD_MAX; + struct inode *inode = filp->f_mapping->host; + + /* check type and number, if fail return ENOTTY */ + if ((_IOC_TYPE(cmd) != VD_IOC_MAGIC) || (_IOC_NR(cmd) > VD_IOC_MAXNR)) + return -ENOTTY; + + /* check argument area */ + if (_IOC_DIR(cmd) & _IOC_READ) + ret = !access_ok(VERIFY_WRITE, (void __user *)arg, + _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + ret = !access_ok(VERIFY_READ, (void __user *)arg, + _IOC_SIZE(cmd)); + + if (ret) + return -EFAULT; + + down(&vd_sem); + idx = iminor(inode); + if (idx < VD_MAX && decoders[idx]) { + struct videodecoder *vd = decoders[idx]; + + if (vd && vd->fops.unlocked_ioctl) + ret = vd->fops.unlocked_ioctl(filp, cmd, arg); + } + up(&vd_sem); + + return ret; +} /* End of wmt_vd_ioctl() */ + +static int wmt_vd_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret = -EINVAL; + unsigned int idx = VD_MAX; + + down(&vd_sem); + if (filp && filp->f_dentry && filp->f_dentry->d_inode) + idx = iminor(filp->f_dentry->d_inode); + + if (idx < VD_MAX && decoders[idx]) { + struct videodecoder *vd = decoders[idx]; + if (vd && vd->fops.mmap) + ret = vd->fops.mmap(filp, vma); + } + up(&vd_sem); + + return ret; +} + +const struct file_operations videodecoder_fops = { + .owner = THIS_MODULE, + .open = wmt_vd_open, + .release = wmt_vd_release, + .unlocked_ioctl = wmt_vd_ioctl, + .mmap = wmt_vd_mmap, +}; + +static int wmt_vd_probe(struct platform_device *dev) +{ + dev_t dev_no; + int ret; + + dev_no = MKDEV(VD_MAJOR, videodecoder_minor); + + /* register char device */ + videodecoder_cdev = cdev_alloc(); + if (!videodecoder_cdev) { + VD_ERROR("alloc dev error.\n"); + return -ENOMEM; + } + + cdev_init(videodecoder_cdev, &videodecoder_fops); + ret = cdev_add(videodecoder_cdev, dev_no, 1); + + if (ret) { + VD_ERROR("reg char dev error(%d).\n", ret); + cdev_del(videodecoder_cdev); + return ret; + } + + vd_res.prdt.virt = dma_alloc_coherent(NULL, vd_res.prdt.size, + &vd_res.prdt.phys, GFP_KERNEL | GFP_DMA); + if (!vd_res.prdt.virt) { + VD_ERROR("allocate video PRDT buffer error.\n"); + cdev_del(videodecoder_cdev); + vd_res.prdt.virt = NULL; + vd_res.prdt.phys = 0; + return -ENOMEM; + } + + VD_DBG("prob /dev/%s major %d, minor %d, Resource:\n", + DRIVER_NAME, VD_MAJOR, videodecoder_minor); + + VD_DBG("PRDT %p/%x, size %d KB\n", + vd_res.prdt.virt, vd_res.prdt.phys, vd_res.prdt.size/1024); + + return ret; +} + +static int wmt_vd_remove(struct platform_device *dev) +{ + unsigned int idx = 0; + + down(&vd_sem); + while (idx < VD_MAX) { + if (decoders[idx] && decoders[idx]->remove) { + decoders[idx]->remove(); + decoders[idx] = NULL; + } + idx++; + } + up(&vd_sem); + + if (vd_res.prdt.virt) + dma_free_coherent(NULL, MAX_INPUT_BUF_SIZE, + vd_res.prdt.virt, vd_res.prdt.phys); + + return 0; +} + +static int wmt_vd_suspend(struct platform_device *dev, pm_message_t state) +{ + int ret; + unsigned int idx = 0; + + down(&vd_sem); + while (idx < VD_MAX) { + if (decoders[idx] && decoders[idx]->suspend) { + ret = decoders[idx]->suspend(state); + if (ret < 0) + VD_WARN("vdec %s suspend fail. ret:%d\n", + decoders[idx]->name, ret); + } + idx++; + } + up(&vd_sem); + + return 0; +} + +static int wmt_vd_resume(struct platform_device *dev) +{ + int ret; + unsigned int idx = 0; + + down(&vd_sem); + while (idx < VD_MAX) { + if (decoders[idx] && decoders[idx]->resume) { + ret = decoders[idx]->resume(); + if (ret < 0) { + VD_WARN("vdec %s resume fail. ret:%d\n", + decoders[idx]->name, ret); + } + } + idx++; + } + up(&vd_sem); + + return 0; +} + +static void wmt_vd_elease(struct device *device) +{ + unsigned int idx = 0; + + down(&vd_sem); + while (idx < VD_MAX) { + if (decoders[idx] && decoders[idx]->remove) { + decoders[idx]->remove(); + decoders[idx] = NULL; + } + idx++; + } + up(&vd_sem); + + if (vd_res.prdt.virt) + dma_free_coherent(NULL, MAX_INPUT_BUF_SIZE, + vd_res.prdt.virt, vd_res.prdt.phys); + + return; +} + +static struct platform_driver videodecoder_driver = { + .driver = { + .name = "wmt-vd", + .bus = &platform_bus_type, + }, + .probe = wmt_vd_probe, + .remove = wmt_vd_remove, + .suspend = wmt_vd_suspend, + .resume = wmt_vd_resume +}; + +static struct platform_device videodecoder_device = { + .name = "wmt-vd", + .id = 0, + .dev = { + .release = wmt_vd_elease, + }, + .num_resources = 0, /* ARRAY_SIZE(spi_resources), */ + .resource = NULL, /* spi_resources, */ +}; + +#ifdef CONFIG_PROC_FS +static int wmt_vd_read_proc( + char *page, + char **start, + off_t off, + int count, + int *eof, + void *data) +{ + int size; + unsigned int idx = 0; + char *p; + + down(&vd_sem); + p = page; + p += sprintf(p, "***** video decoder information *****\n"); + + while (idx < VD_MAX) { + if (decoders[idx] && decoders[idx]->get_info) { + size = decoders[idx]->get_info(p, start, off, count); + count -= size; + if (count <= 40) + break; + p += size; + } + idx++; + } + p += sprintf(p, "**************** end ****************\n"); + up(&vd_sem); + + *eof = 1; + + return p - page; +} +#endif + +int wmt_vd_register(struct videodecoder *dec) +{ + int ret = 0; + dev_t dev_no; + + if (!dec || dec->id >= VD_MAX) { + VD_WARN("register invalid video decoder\n"); + return -1; + } + + if (decoders[dec->id]) { + VD_WARN("video decoder (ID=%d) exist.(E:%32s, N:%32s)\n", + dec->id, decoders[dec->id]->name, dec->name); + return -1; + } + + dev_no = MKDEV(VD_MAJOR, dec->id); + ret = register_chrdev_region(dev_no, videodecoder_dev_nr, dec->name); + if (ret < 0) { + VD_ERROR("can't get %s device minor %d\n", dec->name, dec->id); + return ret; + } + + dec->device = cdev_alloc(); + if (!dec->device) { + unregister_chrdev_region(dev_no, videodecoder_dev_nr); + VD_ERROR("alloc dev error\n"); + return -ENOMEM; + } + + cdev_init(dec->device, &videodecoder_fops); + ret = cdev_add(dec->device, dev_no, 1); + if (ret) { + VD_ERROR("reg char dev error(%d)\n", ret); + unregister_chrdev_region(dev_no, videodecoder_dev_nr); + cdev_del(dec->device); + dec->device = NULL; + return ret; + } + + if (dec->irq_num && dec->isr) { + ret = request_irq(dec->irq_num, dec->isr, + IRQF_SHARED, dec->name, dec->isr_data); + VD_INFO("%s Request IRQ %d %s.\n", + dec->name, dec->irq_num, (ret < 0) ? "Fail " : "Ok"); + if (ret) { + VD_ERROR("isr: 0x%p, isr_data: 0x%p (ret: %d)\n", + dec->isr, dec->isr_data, ret); + goto EXIT_wmt_vd_register; + } + } + + if (!ret) { + if (dec->setup) + ret = dec->setup(); + } + + if (ret >= 0) { + down(&vd_sem); + decoders[dec->id] = dec; + up(&vd_sem); + VD_INFO("%s registered major %d minor %d\n", + dec->name, VD_MAJOR, dec->id); + + /* let udev to handle /dev/ node */ + dec->vd_class = class_create(dec->fops.owner, dec->name); + device_create(dec->vd_class, 0, dev_no, 0, "%s", dec->name); + } else { + VD_ERROR("%s register major %d minor %d fail\n", + dec->name, VD_MAJOR, dec->id); + free_irq(dec->irq_num, (void *)dec->isr_data); + unregister_chrdev_region(dev_no, videodecoder_dev_nr); + cdev_del(dec->device); + dec->device = NULL; + } +EXIT_wmt_vd_register: + return ret; +} +EXPORT_SYMBOL(wmt_vd_register); + +int wmt_vd_unregister(struct videodecoder *dec) +{ + int ret = 0; + dev_t dev_no; + + if (!dec || dec->id >= VD_MAX || !dec->device) { + VD_WARN("unregister invalid video decoder\n"); + return -1; + } + + if (decoders[dec->id] != dec) { + VD_WARN("unregiseter wrong video decoder. (E:%32s, R:%32s)\n", + decoders[dec->id]->name, dec->name); + return -1; + } + + down(&vd_sem); + decoders[dec->id] = NULL; + up(&vd_sem); + + if (dec->remove) + ret = dec->remove(); + + dev_no = MKDEV(VD_MAJOR, dec->id); + free_irq(dec->irq_num, (void *)dec->isr_data); + + /* let udev to handle /dev/ node */ + device_destroy(dec->vd_class, dev_no); + class_destroy(dec->vd_class); + + unregister_chrdev_region(dev_no, videodecoder_dev_nr); + cdev_del(dec->device); + dec->device = NULL; + + return ret; +} +EXPORT_SYMBOL(wmt_vd_unregister); + +static int __init videodecoder_init(void) +{ + int ret; + dev_t dev_no; + + dev_no = MKDEV(VD_MAJOR, videodecoder_minor); + ret = register_chrdev_region(dev_no, videodecoder_dev_nr, "wmt-vd"); + if (ret < 0) { + VD_ERROR("can't get %s major %d\n", DRIVER_NAME, VD_MAJOR); + return ret; + } + +#ifdef CONFIG_PROC_FS + create_proc_read_entry("wmt-vd", 0, NULL, wmt_vd_read_proc, NULL); + VD_DBG("create video decoder proc\n"); +#endif + + ret = platform_driver_register(&videodecoder_driver); + if (!ret) { + ret = platform_device_register(&videodecoder_device); + if (ret) + platform_driver_unregister(&videodecoder_driver); + } + + /* let udev to handle /dev/wmt-vd */ + vd_class = class_create(THIS_MODULE, VD_DEV_NAME); + device_create(vd_class, NULL, dev_no, NULL, "%s", VD_DEV_NAME); + + VD_INFO("WonderMedia HW decoder driver inited\n"); + + return ret; +} + +void __exit videodecoder_exit(void) +{ + dev_t dev_no; + + platform_driver_unregister(&videodecoder_driver); + platform_device_unregister(&videodecoder_device); + dev_no = MKDEV(VD_MAJOR, videodecoder_minor); + + /* let udev to handle /dev/wmt-vd */ + device_destroy(vd_class, dev_no); + class_destroy(vd_class); + + unregister_chrdev_region(dev_no, videodecoder_dev_nr); + + VD_INFO("WonderMedia HW decoder driver exit\n"); + + return; +} + +fs_initcall(videodecoder_init); +module_exit(videodecoder_exit); + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("Video Codec device driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/video/wmt/wmt-vd.h b/drivers/media/video/wmt/wmt-vd.h new file mode 100644 index 00000000..a981650b --- /dev/null +++ b/drivers/media/video/wmt/wmt-vd.h @@ -0,0 +1,106 @@ +/*++ + * Common interface for WonderMedia SoC hardware decoder drivers + * + * Copyright (c) 2008-2013 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ +#ifndef WMT_VD_H +#define WMT_VD_H + +/*-------------------- MODULE DEPENDENCY -------------------------------------*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <asm/uaccess.h> +#include <linux/kdev_t.h> +#include <linux/cdev.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <asm/page.h> + + +#include <linux/dma-mapping.h> +#include <linux/irqreturn.h> + +#include <mach/irqs.h> +#include <mach/hardware.h> +#include <linux/sched.h> /* for 2.6.32 wait_event_interruptible() */ + +#include <linux/wmt-mb.h> + +#ifdef CONFIG_PROC_FS +#include <linux/proc_fs.h> + #include <linux/uaccess.h> +#endif + +#include "com-vd.h" + + +/*-------------------- EXPORTED PRIVATE CONSTANTS ------------------------*/ + +/*-------------------------------------------------------------------------- + Since the page size in kernel is 4 KB, so we may assume the max buffer + size as input buffer size = (prd_size/8)*4KB + In short, 1 KB PRD size could store about 0.5 MB data. + If we support maximun input buffer size is 50 MB, we must set prd_size + as 100 KB. +---------------------------------------------------------------------------*/ +#define MAX_INPUT_BUF_SIZE (100*1024) /* 100 KB */ + +struct vd_mem_set { + void *virt; + dma_addr_t phys; + unsigned int size; +}; + +struct vd_resource { + struct vd_mem_set prdt; + struct vd_mem_set cnm_fw_real; + struct vd_mem_set cnm_fw_align; +}; + +struct videodecoder { + char name[32]; + int id; + int (*setup)(void); + int (*remove)(void); + int (*suspend)(pm_message_t state); + int (*resume)(void); + const struct file_operations fops; + int (*get_info)(char *, char **, off_t, int); + struct cdev *device; + int irq_num; + irqreturn_t (*isr)(int irq, void *isr_data); + void *isr_data; + + struct class *vd_class; /* for udev */ +}; + +int wmt_vd_register(struct videodecoder *); +int wmt_vd_unregister(struct videodecoder *); + +#endif + |