summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/wmt/com-lock.h58
-rw-r--r--drivers/media/video/wmt/com-vd.h73
-rw-r--r--drivers/media/video/wmt/wmt-codec.c857
-rw-r--r--drivers/media/video/wmt/wmt-codec.h72
-rw-r--r--drivers/media/video/wmt/wmt-lock.c512
-rw-r--r--drivers/media/video/wmt/wmt-lock.h45
-rw-r--r--drivers/media/video/wmt/wmt-vd.c541
-rw-r--r--drivers/media/video/wmt/wmt-vd.h106
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
+