This reverts commit b739bce629.
Conflicts:
	arch/arm/configs/htcleo_defconfig
	arch/arm/mach-msm/Makefile
	arch/arm/mach-msm/board-htcleo.c
		
	
		
			
				
	
	
		
			990 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			990 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are met:
 | |
|  *     * Redistributions of source code must retain the above copyright
 | |
|  *       notice, this list of conditions and the following disclaimer.
 | |
|  *     * Redistributions in binary form must reproduce the above copyright
 | |
|  *       notice, this list of conditions and the following disclaimer in the
 | |
|  *       documentation and/or other materials provided with the distribution.
 | |
|  *     * Neither the name of Code Aurora Forum nor
 | |
|  *       the names of its contributors may be used to endorse or promote
 | |
|  *       products derived from this software without specific prior written
 | |
|  *       permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | |
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | |
|  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | |
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | |
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | |
|  * POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /*
 | |
| #define DEBUG_TRACE_VDEC
 | |
| #define DEBUG
 | |
| */
 | |
| 
 | |
| #include <linux/cdev.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/file.h>
 | |
| #include <linux/fs.h>
 | |
| #include <linux/list.h>
 | |
| #include <linux/miscdevice.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/mutex.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/spinlock.h>
 | |
| #include <linux/uaccess.h>
 | |
| #include <linux/wakelock.h>
 | |
| 
 | |
| #include <linux/android_pmem.h>
 | |
| #include <linux/msm_q6vdec.h>
 | |
| 
 | |
| #include "dal.h"
 | |
| 
 | |
| #define DALDEVICEID_VDEC_DEVICE		0x02000026
 | |
| #define DALDEVICEID_VDEC_PORTNAME	"DSP_DAL_AQ_VID"
 | |
| 
 | |
| #define VDEC_INTERFACE_VERSION		0x00020000
 | |
| 
 | |
| #define MAJOR_MASK			0xFFFF0000
 | |
| #define MINOR_MASK			0x0000FFFF
 | |
| 
 | |
| #define VDEC_GET_MAJOR_VERSION(version)	(((version)&MAJOR_MASK)>>16)
 | |
| 
 | |
| #define VDEC_GET_MINOR_VERSION(version)	((version)&MINOR_MASK)
 | |
| 
 | |
| #ifdef DEBUG_TRACE_VDEC
 | |
| #define TRACE(fmt,x...)			\
 | |
| 	do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0)
 | |
| #else
 | |
| #define TRACE(fmt, x...)		do { } while (0)
 | |
| #endif
 | |
| 
 | |
| #define MAX_SUPPORTED_INSTANCES 2
 | |
| 
 | |
| enum {
 | |
| 	VDEC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API,
 | |
| 	VDEC_DALRPC_SETBUFFERS,
 | |
| 	VDEC_DALRPC_FREEBUFFERS,
 | |
| 	VDEC_DALRPC_QUEUE,
 | |
| 	VDEC_DALRPC_SIGEOFSTREAM,
 | |
| 	VDEC_DALRPC_FLUSH,
 | |
| 	VDEC_DALRPC_REUSEFRAMEBUFFER,
 | |
| 	VDEC_DALRPC_GETDECATTRIBUTES,
 | |
| };
 | |
| 
 | |
| enum {
 | |
| 	VDEC_ASYNCMSG_DECODE_DONE = 0xdec0de00,
 | |
| 	VDEC_ASYNCMSG_REUSE_FRAME,
 | |
| };
 | |
| 
 | |
| struct vdec_init_cfg {
 | |
| 	u32			decode_done_evt;
 | |
| 	u32			reuse_frame_evt;
 | |
| 	struct vdec_config	cfg;
 | |
| };
 | |
| 
 | |
| struct vdec_buffer_status {
 | |
| 	u32			data;
 | |
| 	u32			status;
 | |
| };
 | |
| 
 | |
| #define VDEC_MSG_MAX		128
 | |
| 
 | |
| struct vdec_msg_list {
 | |
| 	struct list_head	list;
 | |
| 	struct vdec_msg		vdec_msg;
 | |
| };
 | |
| 
 | |
| struct vdec_mem_info {
 | |
| 	u32			buf_type;
 | |
| 	u32			id;
 | |
| 	unsigned long		phys_addr;
 | |
| 	unsigned long		len;
 | |
| 	struct file		*file;
 | |
| };
 | |
| 
 | |
| struct vdec_mem_list {
 | |
| 	struct list_head	list;
 | |
| 	struct vdec_mem_info	mem;
 | |
| };
 | |
| 
 | |
| struct vdec_data {
 | |
| 	struct dal_client	*vdec_handle;
 | |
| 	struct list_head	vdec_msg_list_head;
 | |
| 	struct list_head	vdec_msg_list_free;
 | |
| 	wait_queue_head_t	vdec_msg_evt;
 | |
| 	spinlock_t		vdec_list_lock;
 | |
| 	struct list_head	vdec_mem_list_head;
 | |
| 	spinlock_t		vdec_mem_list_lock;
 | |
| 	int			mem_initialized;
 | |
| 	int			running;
 | |
| 	int			close_decode;
 | |
| };
 | |
| 
 | |
| static struct class *driver_class;
 | |
| static dev_t vdec_device_no;
 | |
| static struct cdev vdec_cdev;
 | |
| static int ref_cnt;
 | |
| static DEFINE_MUTEX(vdec_ref_lock);
 | |
| 
 | |
| static DEFINE_MUTEX(idlecount_lock);
 | |
| static int idlecount;
 | |
| static struct wake_lock wakelock;
 | |
| static struct wake_lock idlelock;
 | |
| 
 | |
| static void prevent_sleep(void)
 | |
| {
 | |
| 	mutex_lock(&idlecount_lock);
 | |
| 	if (++idlecount == 1) {
 | |
| 		wake_lock(&idlelock);
 | |
| 		wake_lock(&wakelock);
 | |
| 	}
 | |
| 	mutex_unlock(&idlecount_lock);
 | |
| }
 | |
| 
 | |
| static void allow_sleep(void)
 | |
| {
 | |
| 	mutex_lock(&idlecount_lock);
 | |
| 	if (--idlecount == 0) {
 | |
| 		wake_unlock(&idlelock);
 | |
| 		wake_unlock(&wakelock);
 | |
| 	}
 | |
| 	mutex_unlock(&idlecount_lock);
 | |
| }
 | |
| 
 | |
| static inline int vdec_check_version(u32 client, u32 server)
 | |
| {
 | |
| 	int ret = -EINVAL;
 | |
| 	if ((VDEC_GET_MAJOR_VERSION(client) == VDEC_GET_MAJOR_VERSION(server))
 | |
| 	    && (VDEC_GET_MINOR_VERSION(client) <=
 | |
| 		VDEC_GET_MINOR_VERSION(server)))
 | |
| 		ret = 0;
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_get_msg(struct vdec_data *vd, void *msg)
 | |
| {
 | |
| 	struct vdec_msg_list *l;
 | |
| 	unsigned long flags;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (!vd->running)
 | |
| 		return -EPERM;
 | |
| 
 | |
| 	spin_lock_irqsave(&vd->vdec_list_lock, flags);
 | |
| 	list_for_each_entry_reverse(l, &vd->vdec_msg_list_head, list) {
 | |
| 		if (copy_to_user(msg, &l->vdec_msg, sizeof(struct vdec_msg)))
 | |
| 			pr_err("vdec_get_msg failed to copy_to_user!\n");
 | |
| 		if (l->vdec_msg.id == VDEC_MSG_REUSEINPUTBUFFER)
 | |
| 			TRACE("reuse_input_buffer %d\n", l->vdec_msg.buf_id);
 | |
| 		else if (l->vdec_msg.id == VDEC_MSG_FRAMEDONE)
 | |
| 			TRACE("frame_done (stat=%d)\n",
 | |
| 			      l->vdec_msg.vfr_info.status);
 | |
| 		else
 | |
| 			TRACE("unknown msg (msgid=%d)\n", l->vdec_msg.id);
 | |
| 		list_del(&l->list);
 | |
| 		list_add(&l->list, &vd->vdec_msg_list_free);
 | |
| 		ret = 1;
 | |
| 		break;
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
 | |
| 
 | |
| 	if (vd->close_decode)
 | |
| 		ret = 1;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void vdec_put_msg(struct vdec_data *vd, struct vdec_msg *msg)
 | |
| {
 | |
| 	struct vdec_msg_list *l;
 | |
| 	unsigned long flags;
 | |
| 	int found = 0;
 | |
| 
 | |
| 	spin_lock_irqsave(&vd->vdec_list_lock, flags);
 | |
| 	list_for_each_entry(l, &vd->vdec_msg_list_free, list) {
 | |
| 		memcpy(&l->vdec_msg, msg, sizeof(struct vdec_msg));
 | |
| 		list_del(&l->list);
 | |
| 		list_add(&l->list, &vd->vdec_msg_list_head);
 | |
| 		found = 1;
 | |
| 		break;
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
 | |
| 
 | |
| 	if (found)
 | |
| 		wake_up(&vd->vdec_msg_evt);
 | |
| 	else
 | |
| 		pr_err("vdec_put_msg can't find free list!\n");
 | |
| }
 | |
| 
 | |
| static struct vdec_mem_list *vdec_get_mem_from_list(struct vdec_data *vd,
 | |
| 						    u32 pmem_id, u32 buf_type)
 | |
| {
 | |
| 	struct vdec_mem_list *l;
 | |
| 	unsigned long flags;
 | |
| 	int found = 0;
 | |
| 
 | |
| 	spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
 | |
| 	list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
 | |
| 		if (l->mem.buf_type == buf_type && l->mem.id == pmem_id) {
 | |
| 			found = 1;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
 | |
| 
 | |
| 	if (found)
 | |
| 		return l;
 | |
| 	else
 | |
| 		return NULL;
 | |
| 
 | |
| }
 | |
| 
 | |
| static int vdec_initialize(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	struct vdec_config_sps vdec_cfg_sps;
 | |
| 	struct vdec_init_cfg vi_cfg;
 | |
| 	struct vdec_buf_req vdec_buf_req;
 | |
| 	struct u8 *header;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ret = copy_from_user(&vdec_cfg_sps,
 | |
| 			     &((struct vdec_init *)argp)->sps_cfg,
 | |
| 			     sizeof(vdec_cfg_sps));
 | |
| 
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	vi_cfg.decode_done_evt = VDEC_ASYNCMSG_DECODE_DONE;
 | |
| 	vi_cfg.reuse_frame_evt = VDEC_ASYNCMSG_REUSE_FRAME;
 | |
| 	memcpy(&vi_cfg.cfg, &vdec_cfg_sps.cfg, sizeof(struct vdec_config));
 | |
| 
 | |
| 	header = kmalloc(vdec_cfg_sps.seq.len, GFP_KERNEL);
 | |
| 	if (!header) {
 | |
| 		pr_err("%s: kmalloc failed\n", __func__);
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	ret = copy_from_user(header,
 | |
| 			     ((struct vdec_init *)argp)->sps_cfg.seq.header,
 | |
| 			     vdec_cfg_sps.seq.len);
 | |
| 
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		kfree(header);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	TRACE("vi_cfg: handle=%p fourcc=0x%x w=%d h=%d order=%d notify_en=%d "
 | |
| 	      "vc1_rb=%d h264_sd=%d h264_nls=%d pp_flag=%d fruc_en=%d\n",
 | |
| 	      vd->vdec_handle, vi_cfg.cfg.fourcc, vi_cfg.cfg.width,
 | |
| 	      vi_cfg.cfg.height, vi_cfg.cfg.order, vi_cfg.cfg.notify_enable,
 | |
| 	      vi_cfg.cfg.vc1_rowbase, vi_cfg.cfg.h264_startcode_detect,
 | |
| 	      vi_cfg.cfg.h264_nal_len_size, vi_cfg.cfg.postproc_flag,
 | |
| 	      vi_cfg.cfg.fruc_enable);
 | |
| 	ret = dal_call_f13(vd->vdec_handle, VDEC_DALRPC_INITIALIZE,
 | |
| 			   &vi_cfg, sizeof(vi_cfg),
 | |
| 			   header, vdec_cfg_sps.seq.len,
 | |
| 			   &vdec_buf_req, sizeof(vdec_buf_req));
 | |
| 
 | |
| 	kfree(header);
 | |
| 
 | |
| 	if (ret)
 | |
| 		pr_err("%s: remote function failed (%d)\n", __func__, ret);
 | |
| 	else
 | |
| 		ret = copy_to_user(((struct vdec_init *)argp)->buf_req,
 | |
| 				   &vdec_buf_req, sizeof(vdec_buf_req));
 | |
| 
 | |
| 	vd->close_decode = 0;
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_setbuffers(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	struct vdec_buffer vmem;
 | |
| 	struct vdec_mem_list *l;
 | |
| 	unsigned long vstart;
 | |
| 	unsigned long flags;
 | |
| 	struct {
 | |
| 		uint32_t size;
 | |
| 		struct vdec_buf_info buf;
 | |
| 	} rpc;
 | |
| 	uint32_t res;
 | |
| 
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	vd->mem_initialized = 0;
 | |
| 
 | |
| 	ret = copy_from_user(&vmem, argp, sizeof(vmem));
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	l = kzalloc(sizeof(struct vdec_mem_list), GFP_KERNEL);
 | |
| 	if (!l) {
 | |
| 		pr_err("%s: kzalloc failed!\n", __func__);
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	l->mem.id = vmem.pmem_id;
 | |
| 	l->mem.buf_type = vmem.buf.buf_type;
 | |
| 
 | |
| 	ret = get_pmem_file(l->mem.id, &l->mem.phys_addr, &vstart,
 | |
| 			    &l->mem.len, &l->mem.file);
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: get_pmem_fd failed\n", __func__);
 | |
| 		goto err_get_pmem_file;
 | |
| 	}
 | |
| 
 | |
| 	TRACE("pmem_id=%d (phys=0x%08lx len=0x%lx) buftype=%d num_buf=%d "
 | |
| 	      "islast=%d src_id=%d offset=0x%08x size=0x%x\n",
 | |
| 	      vmem.pmem_id, l->mem.phys_addr, l->mem.len,
 | |
| 	      vmem.buf.buf_type, vmem.buf.num_buf, vmem.buf.islast,
 | |
| 	      vmem.buf.region.src_id, vmem.buf.region.offset,
 | |
| 	      vmem.buf.region.size);
 | |
| 
 | |
| 	/* input buffers */
 | |
| 	if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
 | |
| 		pr_err("%s: invalid input buffer offset!\n", __func__);
 | |
| 		ret = -EINVAL;
 | |
| 		goto err_bad_offset;
 | |
| 
 | |
| 	}
 | |
| 	vmem.buf.region.offset += l->mem.phys_addr;
 | |
| 
 | |
| 	rpc.size = sizeof(vmem.buf);
 | |
| 	memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
 | |
| 
 | |
| 
 | |
| 	ret = dal_call(vd->vdec_handle, VDEC_DALRPC_SETBUFFERS, 5,
 | |
| 		       &rpc, sizeof(rpc), &res, sizeof(res));
 | |
| 
 | |
| 	if (ret < 4) {
 | |
| 		pr_err("%s: remote function failed (%d)\n", __func__, ret);
 | |
| 		ret = -EIO;
 | |
| 		goto err_dal_call;
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
 | |
| 	list_add(&l->list, &vd->vdec_mem_list_head);
 | |
| 	spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
 | |
| 
 | |
| 	vd->mem_initialized = 1;
 | |
| 	return ret;
 | |
| 
 | |
| err_dal_call:
 | |
| err_bad_offset:
 | |
| 	put_pmem_file(l->mem.file);
 | |
| err_get_pmem_file:
 | |
| 	kfree(l);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_queue(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	struct {
 | |
| 		uint32_t size;
 | |
| 		struct vdec_input_buf_info buf_info;
 | |
| 		uint32_t osize;
 | |
| 	} rpc;
 | |
| 	struct vdec_mem_list *l;
 | |
| 	struct {
 | |
| 		uint32_t result;
 | |
| 		uint32_t size;
 | |
| 		struct vdec_queue_status status;
 | |
| 	} rpc_res;
 | |
| 
 | |
| 	u32 pmem_id;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (!vd->mem_initialized) {
 | |
| 		pr_err("%s: memory is not being initialized!\n", __func__);
 | |
| 		return -EPERM;
 | |
| 	}
 | |
| 
 | |
| 	ret = copy_from_user(&rpc.buf_info,
 | |
| 			     &((struct vdec_input_buf *)argp)->buffer,
 | |
| 			     sizeof(rpc.buf_info));
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = copy_from_user(&pmem_id,
 | |
| 			     &((struct vdec_input_buf *)argp)->pmem_id,
 | |
| 			     sizeof(u32));
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	l = vdec_get_mem_from_list(vd, pmem_id, VDEC_BUFFER_TYPE_INPUT);
 | |
| 
 | |
| 	if (NULL == l) {
 | |
| 		pr_err("%s: not able to find the buffer from list\n", __func__);
 | |
| 		return -EPERM;
 | |
| 	}
 | |
| 
 | |
| 	if ((rpc.buf_info.size + rpc.buf_info.offset) >= l->mem.len) {
 | |
| 		pr_err("%s: invalid queue buffer offset!\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	rpc.buf_info.offset += l->mem.phys_addr;
 | |
| 	rpc.size = sizeof(struct vdec_input_buf_info);
 | |
| 	rpc.osize = sizeof(struct vdec_queue_status);
 | |
| 
 | |
| 	/* complete the writes to the buffer */
 | |
| 	wmb();
 | |
| 	ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8,
 | |
| 		       &rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res));
 | |
| 	if (ret < 4) {
 | |
| 		pr_err("%s: remote function failed (%d)\n", __func__, ret);
 | |
| 		ret = -EIO;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_reuse_framebuffer(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	u32 buf_id;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ret = copy_from_user(&buf_id, argp, sizeof(buf_id));
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_REUSEFRAMEBUFFER,
 | |
| 			  buf_id);
 | |
| 	if (ret)
 | |
| 		pr_err("%s: remote function failed (%d)\n", __func__, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_flush(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	u32 flush_type;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ret = copy_from_user(&flush_type, argp, sizeof(flush_type));
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	TRACE("flush_type=%d\n", flush_type);
 | |
| 	ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_FLUSH, flush_type);
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: remote function failed (%d)\n", __func__, ret);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_close(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	struct vdec_mem_list *l;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	pr_info("q6vdec_close()\n");
 | |
| 	vd->close_decode = 1;
 | |
| 	wake_up(&vd->vdec_msg_evt);
 | |
| 	ret = dal_call_f0(vd->vdec_handle, DAL_OP_CLOSE, 0);
 | |
| 	if (ret)
 | |
| 		pr_err("%s: failed to close daldevice (%d)\n", __func__, ret);
 | |
| 
 | |
| 	if (vd->mem_initialized) {
 | |
| 		list_for_each_entry(l, &vd->vdec_mem_list_head, list)
 | |
| 		    put_pmem_file(l->mem.file);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| static int vdec_getdecattributes(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	struct {
 | |
| 		uint32_t status;
 | |
| 		uint32_t size;
 | |
| 		struct vdec_dec_attributes dec_attr;
 | |
| 	} rpc;
 | |
| 	uint32_t inp;
 | |
| 	int ret = 0;
 | |
| 	inp = sizeof(struct vdec_dec_attributes);
 | |
| 
 | |
| 	ret = dal_call(vd->vdec_handle, VDEC_DALRPC_GETDECATTRIBUTES, 9,
 | |
| 		       &inp, sizeof(inp), &rpc, sizeof(rpc));
 | |
| 	if (ret < 4 || rpc.size != sizeof(struct vdec_dec_attributes)) {
 | |
| 		pr_err("%s: remote function failed (%d)\n", __func__, ret);
 | |
| 		ret = -EIO;
 | |
| 	} else
 | |
| 		ret =
 | |
| 		    copy_to_user(((struct vdec_dec_attributes *)argp),
 | |
| 				 &rpc.dec_attr, sizeof(rpc.dec_attr));
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_freebuffers(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	struct vdec_buffer vmem;
 | |
| 	struct vdec_mem_list *l;
 | |
| 	struct {
 | |
| 		uint32_t size;
 | |
| 		struct vdec_buf_info buf;
 | |
| 	} rpc;
 | |
| 	uint32_t res;
 | |
| 
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (!vd->mem_initialized) {
 | |
| 		pr_err("%s: memory is not being initialized!\n", __func__);
 | |
| 		return -EPERM;
 | |
| 	}
 | |
| 
 | |
| 	ret = copy_from_user(&vmem, argp, sizeof(vmem));
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: copy_from_user failed\n", __func__);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	l = vdec_get_mem_from_list(vd, vmem.pmem_id, vmem.buf.buf_type);
 | |
| 
 | |
| 	if (NULL == l) {
 | |
| 		pr_err("%s: not able to find the buffer from list\n", __func__);
 | |
| 		return -EPERM;
 | |
| 	}
 | |
| 
 | |
| 	/* input buffers */
 | |
| 	if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
 | |
| 		pr_err("%s: invalid input buffer offset!\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	}
 | |
| 	vmem.buf.region.offset += l->mem.phys_addr;
 | |
| 
 | |
| 	rpc.size = sizeof(vmem.buf);
 | |
| 	memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
 | |
| 
 | |
| 	ret = dal_call(vd->vdec_handle, VDEC_DALRPC_FREEBUFFERS, 5,
 | |
| 		       &rpc, sizeof(rpc), &res, sizeof(res));
 | |
| 	if (ret < 4) {
 | |
| 		pr_err("%s: remote function failed (%d)\n", __func__, ret);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_getversion(struct vdec_data *vd, void *argp)
 | |
| {
 | |
| 	struct vdec_version ver_info;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ver_info.major = VDEC_GET_MAJOR_VERSION(VDEC_INTERFACE_VERSION);
 | |
| 	ver_info.minor = VDEC_GET_MINOR_VERSION(VDEC_INTERFACE_VERSION);
 | |
| 
 | |
| 	ret = copy_to_user(((struct vdec_version *)argp),
 | |
| 				&ver_info, sizeof(ver_info));
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 | |
| {
 | |
| 	struct vdec_data *vd = file->private_data;
 | |
| 	void __user *argp = (void __user *)arg;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (!vd->running)
 | |
| 		return -EPERM;
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 	case VDEC_IOCTL_INITIALIZE:
 | |
| 		ret = vdec_initialize(vd, argp);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_SETBUFFERS:
 | |
| 		ret = vdec_setbuffers(vd, argp);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_QUEUE:
 | |
| 		TRACE("VDEC_IOCTL_QUEUE (pid=%d tid=%d)\n",
 | |
| 		      current->group_leader->pid, current->pid);
 | |
| 		ret = vdec_queue(vd, argp);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_REUSEFRAMEBUFFER:
 | |
| 		TRACE("VDEC_IOCTL_REUSEFRAMEBUFFER (pid=%d tid=%d)\n",
 | |
| 		      current->group_leader->pid, current->pid);
 | |
| 		ret = vdec_reuse_framebuffer(vd, argp);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_FLUSH:
 | |
| 		ret = vdec_flush(vd, argp);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_EOS:
 | |
| 		TRACE("VDEC_IOCTL_EOS (pid=%d tid=%d)\n",
 | |
| 		      current->group_leader->pid, current->pid);
 | |
| 		ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_SIGEOFSTREAM, 0);
 | |
| 		if (ret)
 | |
| 			pr_err("%s: remote function failed (%d)\n",
 | |
| 			       __func__, ret);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_GETMSG:
 | |
| 		TRACE("VDEC_IOCTL_GETMSG (pid=%d tid=%d)\n",
 | |
| 		      current->group_leader->pid, current->pid);
 | |
| 		wait_event_interruptible(vd->vdec_msg_evt,
 | |
| 					 vdec_get_msg(vd, argp));
 | |
| 
 | |
| 		if (vd->close_decode)
 | |
| 			ret = -EINTR;
 | |
| 		else
 | |
| 			/* order the reads from the buffer */
 | |
| 			rmb();
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_CLOSE:
 | |
| 		ret = vdec_close(vd, argp);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_GETDECATTRIBUTES:
 | |
| 		TRACE("VDEC_IOCTL_GETDECATTRIBUTES (pid=%d tid=%d)\n",
 | |
| 		      current->group_leader->pid, current->pid);
 | |
| 		ret = vdec_getdecattributes(vd, argp);
 | |
| 
 | |
| 		if (ret)
 | |
| 			pr_err("%s: remote function failed (%d)\n",
 | |
| 			       __func__, ret);
 | |
| 		break;
 | |
| 
 | |
| 	case VDEC_IOCTL_FREEBUFFERS:
 | |
| 		TRACE("VDEC_IOCTL_FREEBUFFERS (pid=%d tid=%d)\n",
 | |
| 		      current->group_leader->pid, current->pid);
 | |
| 		ret = vdec_freebuffers(vd, argp);
 | |
| 
 | |
| 		if (ret)
 | |
| 			pr_err("%s: remote function failed (%d)\n",
 | |
| 			       __func__, ret);
 | |
| 		break;
 | |
| 	case VDEC_IOCTL_GETVERSION:
 | |
| 		TRACE("VDEC_IOCTL_GETVERSION (pid=%d tid=%d)\n",
 | |
| 			current->group_leader->pid, current->pid);
 | |
| 		ret = vdec_getversion(vd, argp);
 | |
| 
 | |
| 		if (ret)
 | |
| 			pr_err("%s: remote function failed (%d)\n",
 | |
| 				__func__, ret);
 | |
| 		break;
 | |
| 	default:
 | |
| 		pr_err("%s: invalid ioctl!\n", __func__);
 | |
| 		ret = -EINVAL;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	TRACE("ioctl done (pid=%d tid=%d)\n",
 | |
| 	      current->group_leader->pid, current->pid);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void vdec_dcdone_handler(struct vdec_data *vd, void *frame,
 | |
| 				uint32_t frame_size)
 | |
| {
 | |
| 	struct vdec_msg msg;
 | |
| 	struct vdec_mem_list *l;
 | |
| 	unsigned long flags;
 | |
| 	int found = 0;
 | |
| 
 | |
| /*if (frame_size != sizeof(struct vdec_frame_info)) {*/
 | |
| 	if (frame_size < sizeof(struct vdec_frame_info)) {
 | |
| 		pr_warning("%s: msg size mismatch %d != %d\n", __func__,
 | |
| 			   frame_size, sizeof(struct vdec_frame_info));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	memcpy(&msg.vfr_info, (struct vdec_frame_info *)frame,
 | |
| 	       sizeof(struct vdec_frame_info));
 | |
| 
 | |
| 	if (msg.vfr_info.status == VDEC_FRAME_DECODE_OK) {
 | |
| 		spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
 | |
| 		list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
 | |
| 			if ((l->mem.buf_type == VDEC_BUFFER_TYPE_OUTPUT) &&
 | |
| 			    (msg.vfr_info.offset >= l->mem.phys_addr) &&
 | |
| 			    (msg.vfr_info.offset <
 | |
| 			     (l->mem.phys_addr + l->mem.len))) {
 | |
| 				found = 1;
 | |
| 				msg.vfr_info.offset -= l->mem.phys_addr;
 | |
| 				msg.vfr_info.data2 = l->mem.id;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
 | |
| 	}
 | |
| 
 | |
| 	if (found || (msg.vfr_info.status != VDEC_FRAME_DECODE_OK)) {
 | |
| 		msg.id = VDEC_MSG_FRAMEDONE;
 | |
| 		vdec_put_msg(vd, &msg);
 | |
| 	} else {
 | |
| 		pr_err("%s: invalid phys addr = 0x%x\n",
 | |
| 		       __func__, msg.vfr_info.offset);
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| static void vdec_reuseibuf_handler(struct vdec_data *vd, void *bufstat,
 | |
| 				   uint32_t bufstat_size)
 | |
| {
 | |
| 	struct vdec_buffer_status *vdec_bufstat;
 | |
| 	struct vdec_msg msg;
 | |
| 
 | |
| 	/* TODO: how do we signal the client? If they are waiting on a
 | |
| 	 * message in an ioctl, they may block forever */
 | |
| 	if (bufstat_size != sizeof(struct vdec_buffer_status)) {
 | |
| 		pr_warning("%s: msg size mismatch %d != %d\n", __func__,
 | |
| 			   bufstat_size, sizeof(struct vdec_buffer_status));
 | |
| 		return;
 | |
| 	}
 | |
| 	vdec_bufstat = (struct vdec_buffer_status *)bufstat;
 | |
| 	msg.id = VDEC_MSG_REUSEINPUTBUFFER;
 | |
| 	msg.buf_id = vdec_bufstat->data;
 | |
| 	vdec_put_msg(vd, &msg);
 | |
| }
 | |
| 
 | |
| static void callback(void *data, int len, void *cookie)
 | |
| {
 | |
| 	struct vdec_data *vd = (struct vdec_data *)cookie;
 | |
| 	uint32_t *tmp = (uint32_t *) data;
 | |
| 
 | |
| 	if (!vd->mem_initialized) {
 | |
| 		pr_err("%s:memory not initialize but callback called!\n",
 | |
| 		       __func__);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	TRACE("vdec_async: tmp=0x%08x 0x%08x 0x%08x\n", tmp[0], tmp[1], tmp[2]);
 | |
| 	switch (tmp[0]) {
 | |
| 	case VDEC_ASYNCMSG_DECODE_DONE:
 | |
| 		vdec_dcdone_handler(vd, &tmp[3], tmp[2]);
 | |
| 		break;
 | |
| 	case VDEC_ASYNCMSG_REUSE_FRAME:
 | |
| 		vdec_reuseibuf_handler(vd, &tmp[3], tmp[2]);
 | |
| 		break;
 | |
| 	default:
 | |
| 		pr_err("%s: Unknown async message from DSP id=0x%08x sz=%u\n",
 | |
| 		       __func__, tmp[0], tmp[2]);
 | |
| 	}
 | |
| }
 | |
| static int vdec_open(struct inode *inode, struct file *file)
 | |
| {
 | |
| 	int ret;
 | |
| 	int i;
 | |
| 	struct vdec_msg_list *l;
 | |
| 	struct vdec_data *vd;
 | |
| 	struct dal_info version_info;
 | |
| 
 | |
| 	pr_info("q6vdec_open()\n");
 | |
| 	mutex_lock(&vdec_ref_lock);
 | |
| 	if (ref_cnt >= MAX_SUPPORTED_INSTANCES) {
 | |
| 		pr_err("%s: Max allowed instances exceeded \n", __func__);
 | |
| 		mutex_unlock(&vdec_ref_lock);
 | |
| 		return -EBUSY;
 | |
| 	}
 | |
| 	ref_cnt++;
 | |
| 	mutex_unlock(&vdec_ref_lock);
 | |
| 	vd = kmalloc(sizeof(struct vdec_data), GFP_KERNEL);
 | |
| 	if (!vd) {
 | |
| 		pr_err("%s: kmalloc failed\n", __func__);
 | |
| 		ret = -ENOMEM;
 | |
| 		goto vdec_open_err_handle_vd;
 | |
| 	}
 | |
| 	file->private_data = vd;
 | |
| 
 | |
| 	vd->mem_initialized = 0;
 | |
| 	INIT_LIST_HEAD(&vd->vdec_msg_list_head);
 | |
| 	INIT_LIST_HEAD(&vd->vdec_msg_list_free);
 | |
| 	INIT_LIST_HEAD(&vd->vdec_mem_list_head);
 | |
| 	init_waitqueue_head(&vd->vdec_msg_evt);
 | |
| 
 | |
| 	spin_lock_init(&vd->vdec_list_lock);
 | |
| 	spin_lock_init(&vd->vdec_mem_list_lock);
 | |
| 	for (i = 0; i < VDEC_MSG_MAX; i++) {
 | |
| 		l = kzalloc(sizeof(struct vdec_msg_list), GFP_KERNEL);
 | |
| 		if (!l) {
 | |
| 			pr_err("%s: kzalloc failed!\n", __func__);
 | |
| 			ret = -ENOMEM;
 | |
| 			goto vdec_open_err_handle_list;
 | |
| 		}
 | |
| 		list_add(&l->list, &vd->vdec_msg_list_free);
 | |
| 	}
 | |
| 
 | |
| 	vd->vdec_handle = dal_attach(DALDEVICEID_VDEC_DEVICE,
 | |
| 				     DALDEVICEID_VDEC_PORTNAME,
 | |
| 				     callback, vd);
 | |
| 
 | |
| 	if (!vd->vdec_handle) {
 | |
| 		pr_err("%s: failed to attach \n", __func__);
 | |
| 		ret = -EIO;
 | |
| 		goto vdec_open_err_handle_list;
 | |
| 	}
 | |
| 	ret = dal_call_f9(vd->vdec_handle, DAL_OP_INFO,
 | |
| 				&version_info, sizeof(struct dal_info));
 | |
| 
 | |
| 	if (ret) {
 | |
| 		pr_err("%s: failed to get version \n", __func__);
 | |
| 		goto vdec_open_err_handle_version;
 | |
| 	}
 | |
| 
 | |
| 	TRACE("q6vdec_open() interface version 0x%x\n", version_info.version);
 | |
| 	if (vdec_check_version(VDEC_INTERFACE_VERSION,
 | |
| 			version_info.version)) {
 | |
| 		pr_err("%s: driver version mismatch !\n", __func__);
 | |
| 		goto vdec_open_err_handle_version;
 | |
| 	}
 | |
| 
 | |
| 	vd->running = 1;
 | |
| 	prevent_sleep();
 | |
| 	return 0;
 | |
| vdec_open_err_handle_version:
 | |
| 	dal_detach(vd->vdec_handle);
 | |
| vdec_open_err_handle_list:
 | |
| 	{
 | |
| 		struct vdec_msg_list *l, *n;
 | |
| 		list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
 | |
| 			list_del(&l->list);
 | |
| 			kfree(l);
 | |
| 		}
 | |
| 	}
 | |
| vdec_open_err_handle_vd:
 | |
| 	mutex_lock(&vdec_ref_lock);
 | |
| 	ref_cnt--;
 | |
| 	mutex_unlock(&vdec_ref_lock);
 | |
| 	kfree(vd);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int vdec_release(struct inode *inode, struct file *file)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct vdec_msg_list *l, *n;
 | |
| 	struct vdec_mem_list *m, *k;
 | |
| 	struct vdec_data *vd = file->private_data;
 | |
| 
 | |
| 	vd->running = 0;
 | |
| 	wake_up_all(&vd->vdec_msg_evt);
 | |
| 
 | |
| 	if (!vd->close_decode)
 | |
| 		vdec_close(vd, NULL);
 | |
| 
 | |
| 	ret = dal_detach(vd->vdec_handle);
 | |
| 	if (ret)
 | |
| 		printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret);
 | |
| 
 | |
| 	list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
 | |
| 		list_del(&l->list);
 | |
| 		kfree(l);
 | |
| 	}
 | |
| 
 | |
| 	list_for_each_entry_safe(l, n, &vd->vdec_msg_list_head, list) {
 | |
| 		list_del(&l->list);
 | |
| 		kfree(l);
 | |
| 	}
 | |
| 
 | |
| 	list_for_each_entry_safe(m, k, &vd->vdec_mem_list_head, list) {
 | |
| 		list_del(&m->list);
 | |
| 		kfree(m);
 | |
| 	}
 | |
| 	mutex_lock(&vdec_ref_lock);
 | |
| 	BUG_ON(ref_cnt <= 0);
 | |
| 	ref_cnt--;
 | |
| 	mutex_unlock(&vdec_ref_lock);
 | |
| 	kfree(vd);
 | |
| 	allow_sleep();
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct file_operations vdec_fops = {
 | |
| 	.owner = THIS_MODULE,
 | |
| 	.open = vdec_open,
 | |
| 	.release = vdec_release,
 | |
| 	.unlocked_ioctl = vdec_ioctl,
 | |
| };
 | |
| 
 | |
| static int __init vdec_init(void)
 | |
| {
 | |
| 	struct device *class_dev;
 | |
| 	int rc = 0;
 | |
| 
 | |
| 	wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "vdec_idle");
 | |
| 	wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "vdec_suspend");
 | |
| 
 | |
| 	rc = alloc_chrdev_region(&vdec_device_no, 0, 1, "vdec");
 | |
| 	if (rc < 0) {
 | |
| 		pr_err("%s: alloc_chrdev_region failed %d\n", __func__, rc);
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	driver_class = class_create(THIS_MODULE, "vdec");
 | |
| 	if (IS_ERR(driver_class)) {
 | |
| 		rc = -ENOMEM;
 | |
| 		pr_err("%s: class_create failed %d\n", __func__, rc);
 | |
| 		goto vdec_init_err_unregister_chrdev_region;
 | |
| 	}
 | |
| 	class_dev = device_create(driver_class, NULL,
 | |
| 				  vdec_device_no, NULL, "vdec");
 | |
| 	if (!class_dev) {
 | |
| 		pr_err("%s: class_device_create failed %d\n", __func__, rc);
 | |
| 		rc = -ENOMEM;
 | |
| 		goto vdec_init_err_class_destroy;
 | |
| 	}
 | |
| 
 | |
| 	cdev_init(&vdec_cdev, &vdec_fops);
 | |
| 	vdec_cdev.owner = THIS_MODULE;
 | |
| 	rc = cdev_add(&vdec_cdev, MKDEV(MAJOR(vdec_device_no), 0), 1);
 | |
| 
 | |
| 	if (rc < 0) {
 | |
| 		pr_err("%s: cdev_add failed %d\n", __func__, rc);
 | |
| 		goto vdec_init_err_class_device_destroy;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| vdec_init_err_class_device_destroy:
 | |
| 	device_destroy(driver_class, vdec_device_no);
 | |
| vdec_init_err_class_destroy:
 | |
| 	class_destroy(driver_class);
 | |
| vdec_init_err_unregister_chrdev_region:
 | |
| 	unregister_chrdev_region(vdec_device_no, 1);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static void __exit vdec_exit(void)
 | |
| {
 | |
| 	device_destroy(driver_class, vdec_device_no);
 | |
| 	class_destroy(driver_class);
 | |
| 	unregister_chrdev_region(vdec_device_no, 1);
 | |
| }
 | |
| 
 | |
| MODULE_DESCRIPTION("video decoder driver for QSD platform");
 | |
| MODULE_VERSION("2.00");
 | |
| 
 | |
| module_init(vdec_init);
 | |
| module_exit(vdec_exit);
 |