summaryrefslogtreecommitdiff
path: root/drivers/media/video/wmt/wmt-codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/wmt/wmt-codec.c')
-rw-r--r--drivers/media/video/wmt/wmt-codec.c857
1 files changed, 857 insertions, 0 deletions
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 */