summaryrefslogtreecommitdiff
path: root/drivers/media/video/wmt/wmt-lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/wmt/wmt-lock.c')
-rw-r--r--drivers/media/video/wmt/wmt-lock.c512
1 files changed, 512 insertions, 0 deletions
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");
+