blob: 175947f7ea14621b90c21658c55e41a08f563ef6 [file] [log] [blame]
/* Copyright (c) 2010-2013, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*
*/
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/clk.h>
#include <linux/timer.h>
#include <media/msm/vidc_type.h>
#include <media/msm/vcd_api.h>
#include <media/msm/vidc_init.h>
#include <mach/iommu_domains.h>
#include "vcd_res_tracker_api.h"
#include "vdec_internal.h"
#define DBG(x...) pr_debug(x)
#define INFO(x...) pr_info(x)
#define ERR(x...) pr_err(x)
#define VID_DEC_NAME "msm_vidc_dec"
#ifdef KW_TAINT_ANALYSIS 51
extern void * get_tainted_stuff();
#endif
static char *node_name[2] = {"", "_sec"};
static struct vid_dec_dev *vid_dec_device_p;
static dev_t vid_dec_dev_num;
static struct class *vid_dec_class;
static s32 vid_dec_get_empty_client_index(void)
{
u32 i, found = false;
for (i = 0; i < VIDC_MAX_NUM_CLIENTS; i++) {
if (!vid_dec_device_p->vdec_clients[i].vcd_handle) {
found = true;
break;
}
}
if (!found) {
ERR("%s():ERROR No space for new client\n", __func__);
return -ENOMEM;
} else {
DBG("%s(): available client index = %u\n", __func__, i);
return i;
}
}
u32 vid_dec_get_status(u32 status)
{
u32 vdec_status;
switch (status) {
case VCD_ERR_SEQHDR_PARSE_FAIL:
case VCD_ERR_BITSTREAM_ERR:
vdec_status = VDEC_S_INPUT_BITSTREAM_ERR;
break;
case VCD_S_SUCCESS:
vdec_status = VDEC_S_SUCCESS;
break;
case VCD_ERR_FAIL:
vdec_status = VDEC_S_EFAIL;
break;
case VCD_ERR_ALLOC_FAIL:
vdec_status = VDEC_S_ENOSWRES;
break;
case VCD_ERR_ILLEGAL_OP:
vdec_status = VDEC_S_EINVALCMD;
break;
case VCD_ERR_ILLEGAL_PARM:
vdec_status = VDEC_S_EBADPARAM;
break;
case VCD_ERR_BAD_POINTER:
case VCD_ERR_BAD_HANDLE:
vdec_status = VDEC_S_EFATAL;
break;
case VCD_ERR_NOT_SUPPORTED:
vdec_status = VDEC_S_ENOTSUPP;
break;
case VCD_ERR_BAD_STATE:
vdec_status = VDEC_S_EINVALSTATE;
break;
case VCD_ERR_BUSY:
vdec_status = VDEC_S_BUSY;
break;
case VCD_ERR_MAX_CLIENT:
vdec_status = VDEC_S_ENOHWRES;
break;
default:
vdec_status = VDEC_S_EFAIL;
break;
}
return vdec_status;
}
static void vid_dec_notify_client(struct video_client_ctx *client_ctx)
{
if (client_ctx)
complete(&client_ctx->event);
}
void vid_dec_vcd_open_done(struct video_client_ctx *client_ctx,
struct vcd_handle_container *handle_container)
{
DBG("vid_dec_vcd_open_done\n");
if (client_ctx) {
if (handle_container)
client_ctx->vcd_handle = handle_container->handle;
else
ERR("%s(): ERROR. handle_container is NULL\n",
__func__);
vid_dec_notify_client(client_ctx);
} else
ERR("%s(): ERROR. client_ctx is NULL\n", __func__);
}
static void vid_dec_handle_field_drop(struct video_client_ctx *client_ctx,
u32 event, u32 status, int64_t time_stamp)
{
struct vid_dec_msg *vdec_msg;
if (!client_ctx) {
ERR("%s() NULL pointer\n", __func__);
return;
}
vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
if (!vdec_msg) {
ERR("%s(): cannot allocate vid_dec_msg "
" buffer\n", __func__);
return;
}
vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
if (event == VCD_EVT_IND_INFO_FIELD_DROPPED) {
vdec_msg->vdec_msg_info.msgcode =
VDEC_MSG_EVT_INFO_FIELD_DROPPED;
vdec_msg->vdec_msg_info.msgdata.output_frame.time_stamp
= time_stamp;
DBG("Send FIELD_DROPPED message to client = %p\n", client_ctx);
} else {
ERR("vid_dec_input_frame_done(): invalid event type: "
"%d\n", event);
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_INVALID;
}
vdec_msg->vdec_msg_info.msgdatasize =
sizeof(struct vdec_output_frameinfo);
mutex_lock(&client_ctx->msg_queue_lock);
list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
mutex_unlock(&client_ctx->msg_queue_lock);
wake_up(&client_ctx->msg_wait);
}
static void vid_dec_input_frame_done(struct video_client_ctx *client_ctx,
u32 event, u32 status,
struct vcd_frame_data *vcd_frame_data)
{
struct vid_dec_msg *vdec_msg;
if (!client_ctx || !vcd_frame_data) {
ERR("vid_dec_input_frame_done() NULL pointer\n");
return;
}
kfree(vcd_frame_data->desc_buf);
vcd_frame_data->desc_buf = NULL;
vcd_frame_data->desc_size = 0;
vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
if (!vdec_msg) {
ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg "
" buffer\n");
return;
}
vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
if (event == VCD_EVT_RESP_INPUT_DONE) {
vdec_msg->vdec_msg_info.msgcode =
VDEC_MSG_RESP_INPUT_BUFFER_DONE;
DBG("Send INPUT_DON message to client = %p\n", client_ctx);
} else if (event == VCD_EVT_RESP_INPUT_FLUSHED) {
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_INPUT_FLUSHED;
DBG("Send INPUT_FLUSHED message to client = %p\n", client_ctx);
} else {
ERR("vid_dec_input_frame_done(): invalid event type: "
"%d\n", event);
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_INVALID;
}
vdec_msg->vdec_msg_info.msgdata.input_frame_clientdata =
(void *)vcd_frame_data->frm_clnt_data;
vdec_msg->vdec_msg_info.msgdatasize = sizeof(void *);
mutex_lock(&client_ctx->msg_queue_lock);
list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
mutex_unlock(&client_ctx->msg_queue_lock);
wake_up(&client_ctx->msg_wait);
}
static void vid_dec_output_frame_done(struct video_client_ctx *client_ctx,
u32 event, u32 status,
struct vcd_frame_data *vcd_frame_data)
{
struct vid_dec_msg *vdec_msg;
unsigned long kernel_vaddr = 0, phy_addr = 0, user_vaddr = 0;
int pmem_fd;
struct file *file;
s32 buffer_index = -1;
enum vdec_picture pic_type;
u32 ion_flag = 0;
struct ion_handle *buff_handle = NULL;
struct vdec_output_frameinfo *output_frame;
if (!client_ctx || !vcd_frame_data) {
ERR("vid_dec_input_frame_done() NULL pointer\n");
return;
}
vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
if (!vdec_msg) {
ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg "
" buffer\n");
return;
}
vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
if (event == VCD_EVT_RESP_OUTPUT_DONE)
vdec_msg->vdec_msg_info.msgcode =
VDEC_MSG_RESP_OUTPUT_BUFFER_DONE;
else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED)
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_OUTPUT_FLUSHED;
else {
ERR("QVD: vid_dec_output_frame_done invalid cmd type: "
"%d\n", event);
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_INVALID;
}
kernel_vaddr = (unsigned long)vcd_frame_data->virtual;
if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
false, &user_vaddr, &kernel_vaddr,
&phy_addr, &pmem_fd, &file,
&buffer_index) ||
(vcd_frame_data->flags & VCD_FRAME_FLAG_EOS)) {
if (res_trk_check_for_sec_session() &&
event == VCD_EVT_RESP_OUTPUT_DONE) {
DBG("Buffer Index = %d", buffer_index);
if (buffer_index != -1) {
if (client_ctx->meta_addr_table[buffer_index].
kernel_vir_addr_iommu &&
client_ctx->
meta_addr_table[buffer_index].
kernel_vir_addr) {
memcpy(client_ctx->
meta_addr_table[buffer_index].
kernel_vir_addr_iommu,
client_ctx->
meta_addr_table[buffer_index].
kernel_vir_addr,
client_ctx->meta_buf_size);
DBG("Copying Meta Buffer from "\
"secure memory"
"kernel_virt_iommu = %p "
"kernel_virt = %p",
client_ctx->
meta_addr_table[buffer_index].
kernel_vir_addr_iommu,
client_ctx->
meta_addr_table[buffer_index].
kernel_vir_addr);
}
}
}
/* Buffer address in user space */
vdec_msg->vdec_msg_info.msgdata.output_frame.bufferaddr =
(u8 *) user_vaddr;
/* Data length */
vdec_msg->vdec_msg_info.msgdata.output_frame.len =
vcd_frame_data->data_len;
vdec_msg->vdec_msg_info.msgdata.output_frame.flags =
vcd_frame_data->flags;
/* Timestamp pass-through from input frame */
vdec_msg->vdec_msg_info.msgdata.output_frame.time_stamp =
vcd_frame_data->time_stamp;
/* Output frame client data */
vdec_msg->vdec_msg_info.msgdata.output_frame.client_data =
(void *)vcd_frame_data->frm_clnt_data;
/* Associated input frame client data */
vdec_msg->vdec_msg_info.msgdata.output_frame.
input_frame_clientdata =
(void *)vcd_frame_data->ip_frm_tag;
/* Decoded picture width and height */
vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.
bottom =
vcd_frame_data->dec_op_prop.disp_frm.bottom;
vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.left =
vcd_frame_data->dec_op_prop.disp_frm.left;
vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.right =
vcd_frame_data->dec_op_prop.disp_frm.right;
vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.top =
vcd_frame_data->dec_op_prop.disp_frm.top;
if (vcd_frame_data->interlaced) {
vdec_msg->vdec_msg_info.msgdata.
output_frame.interlaced_format =
VDEC_InterlaceInterleaveFrameTopFieldFirst;
} else {
vdec_msg->vdec_msg_info.msgdata.
output_frame.interlaced_format =
VDEC_InterlaceFrameProgressive;
}
/* Decoded picture type */
switch (vcd_frame_data->frame) {
case VCD_FRAME_I:
pic_type = PICTURE_TYPE_I;
break;
case VCD_FRAME_P:
pic_type = PICTURE_TYPE_P;
break;
case VCD_FRAME_B:
pic_type = PICTURE_TYPE_B;
break;
case VCD_FRAME_NOTCODED:
pic_type = PICTURE_TYPE_SKIP;
break;
case VCD_FRAME_IDR:
pic_type = PICTURE_TYPE_IDR;
break;
default:
pic_type = PICTURE_TYPE_UNKNOWN;
}
vdec_msg->vdec_msg_info.msgdata.output_frame.pic_type =
pic_type;
output_frame = &vdec_msg->vdec_msg_info.msgdata.output_frame;
output_frame->aspect_ratio_info.aspect_ratio =
vcd_frame_data->aspect_ratio_info.aspect_ratio;
output_frame->aspect_ratio_info.par_width =
vcd_frame_data->aspect_ratio_info.par_width;
output_frame->aspect_ratio_info.par_height =
vcd_frame_data->aspect_ratio_info.par_height;
vdec_msg->vdec_msg_info.msgdatasize =
sizeof(struct vdec_output_frameinfo);
} else {
ERR("vid_dec_output_frame_done UVA can not be found\n");
vdec_msg->vdec_msg_info.status_code = VDEC_S_EFATAL;
}
if (vcd_frame_data->data_len > 0) {
ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
pmem_fd, kernel_vaddr, buffer_index,
&buff_handle);
if (ion_flag == ION_FLAG_CACHED && buff_handle) {
DBG("%s: Cache invalidate: vaddr (%p), "\
"size %u\n", __func__,
(void *)kernel_vaddr,
vcd_frame_data->alloc_len);
msm_ion_do_cache_op(client_ctx->user_ion_client,
buff_handle,
(unsigned long *) kernel_vaddr,
(unsigned long)vcd_frame_data->\
alloc_len,
ION_IOC_INV_CACHES);
}
}
mutex_lock(&client_ctx->msg_queue_lock);
list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
mutex_unlock(&client_ctx->msg_queue_lock);
wake_up(&client_ctx->msg_wait);
}
static void vid_dec_lean_event(struct video_client_ctx *client_ctx,
u32 event, u32 status)
{
struct vid_dec_msg *vdec_msg;
if (!client_ctx) {
ERR("%s(): !client_ctx pointer\n", __func__);
return;
}
vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
if (!vdec_msg) {
ERR("%s(): cannot allocate vid_dec_msg buffer\n", __func__);
return;
}
vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
switch (event) {
case VCD_EVT_IND_OUTPUT_RECONFIG:
DBG("msm_vidc_dec: Sending VDEC_MSG_EVT_CONFIG_CHANGED"
" to client");
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_CONFIG_CHANGED;
break;
case VCD_EVT_IND_RESOURCES_LOST:
DBG("msm_vidc_dec: Sending VDEC_EVT_RESOURCES_LOST"
" to client");
vdec_msg->vdec_msg_info.msgcode = VDEC_EVT_RESOURCES_LOST;
break;
case VCD_EVT_RESP_FLUSH_INPUT_DONE:
DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_INPUT_DONE"
" to client");
vdec_msg->vdec_msg_info.msgcode =
VDEC_MSG_RESP_FLUSH_INPUT_DONE;
break;
case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_OUTPUT_DONE"
" to client");
vdec_msg->vdec_msg_info.msgcode =
VDEC_MSG_RESP_FLUSH_OUTPUT_DONE;
break;
case VCD_EVT_IND_HWERRFATAL:
DBG("msm_vidc_dec: Sending VDEC_MSG_EVT_HW_ERROR"
" to client");
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_HW_ERROR;
break;
case VCD_EVT_RESP_START:
DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_START_DONE"
" to client");
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_START_DONE;
break;
case VCD_EVT_RESP_STOP:
DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_STOP_DONE"
" to client");
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_STOP_DONE;
break;
case VCD_EVT_RESP_PAUSE:
DBG("msm_vidc_dec: Sending VDEC_MSG_RESP_PAUSE_DONE"
" to client");
vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_PAUSE_DONE;
break;
case VCD_EVT_IND_INFO_OUTPUT_RECONFIG:
DBG("msm_vidc_dec: Sending VDEC_MSG_EVT_INFO_CONFIG_CHANGED"
" to client");
vdec_msg->vdec_msg_info.msgcode =
VDEC_MSG_EVT_INFO_CONFIG_CHANGED;
break;
default:
ERR("%s() : unknown event type\n", __func__);
break;
}
vdec_msg->vdec_msg_info.msgdatasize = 0;
if (client_ctx->stop_sync_cb &&
(event == VCD_EVT_RESP_STOP || event == VCD_EVT_IND_HWERRFATAL)) {
client_ctx->stop_sync_cb = false;
complete(&client_ctx->event);
kfree(vdec_msg);
return;
}
mutex_lock(&client_ctx->msg_queue_lock);
list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
mutex_unlock(&client_ctx->msg_queue_lock);
wake_up(&client_ctx->msg_wait);
}
void vid_dec_vcd_cb(u32 event, u32 status,
void *info, size_t sz, void *handle, void *const client_data)
{
struct video_client_ctx *client_ctx =
(struct video_client_ctx *)client_data;
DBG("Entering %s()\n", __func__);
if (!client_ctx) {
ERR("%s(): client_ctx is NULL\n", __func__);
return;
}
client_ctx->event_status = status;
switch (event) {
case VCD_EVT_RESP_OPEN:
vid_dec_vcd_open_done(client_ctx,
(struct vcd_handle_container *)
info);
break;
case VCD_EVT_RESP_INPUT_DONE:
case VCD_EVT_RESP_INPUT_FLUSHED:
vid_dec_input_frame_done(client_ctx, event, status,
(struct vcd_frame_data *)info);
break;
case VCD_EVT_IND_INFO_FIELD_DROPPED:
if (info)
vid_dec_handle_field_drop(client_ctx, event,
status, *((int64_t *)info));
else
pr_err("Wrong Payload for Field dropped\n");
break;
case VCD_EVT_RESP_OUTPUT_DONE:
case VCD_EVT_RESP_OUTPUT_FLUSHED:
vid_dec_output_frame_done(client_ctx, event, status,
(struct vcd_frame_data *)info);
break;
case VCD_EVT_RESP_PAUSE:
case VCD_EVT_RESP_STOP:
case VCD_EVT_RESP_FLUSH_INPUT_DONE:
case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
case VCD_EVT_IND_OUTPUT_RECONFIG:
case VCD_EVT_IND_HWERRFATAL:
case VCD_EVT_IND_RESOURCES_LOST:
case VCD_EVT_IND_INFO_OUTPUT_RECONFIG:
vid_dec_lean_event(client_ctx, event, status);
break;
case VCD_EVT_RESP_START:
if (!client_ctx->seq_header_set)
vid_dec_lean_event(client_ctx, event, status);
else
vid_dec_notify_client(client_ctx);
break;
default:
ERR("%s() : Error - Invalid event type =%u\n", __func__,
event);
break;
}
}
static u32 vid_dec_set_codec(struct video_client_ctx *client_ctx,
enum vdec_codec *vdec_codec)
{
u32 result = true;
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_codec codec;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx || !vdec_codec)
return false;
vcd_property_hdr.prop_id = VCD_I_CODEC;
vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
switch (*vdec_codec) {
case VDEC_CODECTYPE_MPEG4:
codec.codec = VCD_CODEC_MPEG4;
break;
case VDEC_CODECTYPE_H264:
codec.codec = VCD_CODEC_H264;
break;
case VDEC_CODECTYPE_DIVX_3:
codec.codec = VCD_CODEC_DIVX_3;
break;
case VDEC_CODECTYPE_DIVX_4:
codec.codec = VCD_CODEC_DIVX_4;
break;
case VDEC_CODECTYPE_DIVX_5:
codec.codec = VCD_CODEC_DIVX_5;
break;
case VDEC_CODECTYPE_DIVX_6:
codec.codec = VCD_CODEC_DIVX_6;
break;
case VDEC_CODECTYPE_XVID:
codec.codec = VCD_CODEC_XVID;
break;
case VDEC_CODECTYPE_H263:
codec.codec = VCD_CODEC_H263;
break;
case VDEC_CODECTYPE_MPEG2:
codec.codec = VCD_CODEC_MPEG2;
break;
case VDEC_CODECTYPE_VC1:
codec.codec = VCD_CODEC_VC1;
break;
case VDEC_CODECTYPE_VC1_RCV:
codec.codec = VCD_CODEC_VC1_RCV;
break;
default:
result = false;
break;
}
if (result) {
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &codec);
if (vcd_status)
result = false;
}
return result;
}
static u32 vid_dec_set_output_format(struct video_client_ctx *client_ctx,
enum vdec_output_fromat *output_format)
{
u32 result = true;
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_buffer_format vcd_prop_buffer_format;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx || !output_format)
return false;
vcd_property_hdr.prop_id = VCD_I_BUFFER_FORMAT;;
vcd_property_hdr.sz =
sizeof(struct vcd_property_buffer_format);
switch (*output_format) {
case VDEC_YUV_FORMAT_NV12:
vcd_prop_buffer_format.buffer_format = VCD_BUFFER_FORMAT_NV12;
break;
case VDEC_YUV_FORMAT_TILE_4x2:
vcd_prop_buffer_format.buffer_format =
VCD_BUFFER_FORMAT_TILE_4x2;
break;
default:
result = false;
break;
}
if (result)
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr,
&vcd_prop_buffer_format);
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_set_frame_resolution(struct video_client_ctx *client_ctx,
struct vdec_picsize *video_resoultion)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_frame_size frame_resolution;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx || !video_resoultion)
return false;
vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size);
frame_resolution.width = video_resoultion->frame_width;
frame_resolution.height = video_resoultion->frame_height;
frame_resolution.stride = video_resoultion->stride;
frame_resolution.scan_lines = video_resoultion->scan_lines;
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &frame_resolution);
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_set_turbo_clk(struct video_client_ctx *client_ctx)
{
struct vcd_property_hdr vcd_property_hdr;
u32 vcd_status = VCD_ERR_FAIL;
u32 dummy = 0;
if (!client_ctx)
return false;
vcd_property_hdr.prop_id = VCD_I_SET_TURBO_CLK;
vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &dummy);
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_get_frame_resolution(struct video_client_ctx *client_ctx,
struct vdec_picsize *video_resoultion)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_frame_size frame_resolution;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx || !video_resoultion)
return false;
vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE;
vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size);
vcd_status = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
&frame_resolution);
video_resoultion->frame_width = frame_resolution.width;
video_resoultion->frame_height = frame_resolution.height;
video_resoultion->scan_lines = frame_resolution.scan_lines;
video_resoultion->stride = frame_resolution.stride;
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_get_progressive_only(struct video_client_ctx *client_ctx,
u32 *progressive_only)
{
struct vcd_property_hdr vcd_property_hdr;
if (!client_ctx || !progressive_only)
return false;
vcd_property_hdr.prop_id = VCD_I_PROGRESSIVE_ONLY;
vcd_property_hdr.sz = sizeof(u32);
if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
progressive_only))
return false;
else
return true;
}
static u32 vid_dec_get_disable_dmx_support(struct video_client_ctx *client_ctx,
u32 *disable_dmx)
{
struct vcd_property_hdr vcd_property_hdr;
if (!client_ctx || !disable_dmx)
return false;
vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX_SUPPORT;
vcd_property_hdr.sz = sizeof(u32);
if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
disable_dmx))
return false;
else
return true;
}
static u32 vid_dec_get_disable_dmx(struct video_client_ctx *client_ctx,
u32 *disable_dmx)
{
struct vcd_property_hdr vcd_property_hdr;
if (!client_ctx || !disable_dmx)
return false;
vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX;
vcd_property_hdr.sz = sizeof(u32);
if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
disable_dmx))
return false;
else
return true;
}
static u32 vid_dec_set_disable_dmx(struct video_client_ctx *client_ctx)
{
struct vcd_property_hdr vcd_property_hdr;
u32 vcd_disable_dmx;
if (!client_ctx)
return false;
vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX;
vcd_property_hdr.sz = sizeof(u32);
vcd_disable_dmx = true;
DBG("%s() : Setting Disable DMX: %d\n",
__func__, vcd_disable_dmx);
if (vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
&vcd_disable_dmx))
return false;
else
return true;
}
static u32 vid_dec_set_picture_order(struct video_client_ctx *client_ctx,
u32 *picture_order)
{
struct vcd_property_hdr vcd_property_hdr;
u32 vcd_status = VCD_ERR_FAIL, vcd_picture_order, ret = true;
if (!client_ctx || !picture_order)
return false;
vcd_property_hdr.prop_id = VCD_I_OUTPUT_ORDER;
vcd_property_hdr.sz = sizeof(u32);
if (*picture_order == VDEC_ORDER_DISPLAY)
vcd_picture_order = VCD_DEC_ORDER_DISPLAY;
else if (*picture_order == VDEC_ORDER_DECODE)
vcd_picture_order = VCD_DEC_ORDER_DECODE;
else
ret = false;
if (ret) {
DBG("%s() : Setting output picture order: %d\n",
__func__, vcd_picture_order);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &vcd_picture_order);
if (vcd_status != VCD_S_SUCCESS)
ret = false;
}
return ret;
}
static u32 vid_dec_set_frame_rate(struct video_client_ctx *client_ctx,
struct vdec_framerate *frame_rate)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_frame_rate vcd_frame_rate;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx || !frame_rate)
return false;
vcd_property_hdr.prop_id = VCD_I_FRAME_RATE;
vcd_property_hdr.sz = sizeof(struct vcd_property_frame_rate);
vcd_frame_rate.fps_numerator = frame_rate->fps_numerator;
vcd_frame_rate.fps_denominator = frame_rate->fps_denominator;
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &vcd_frame_rate);
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_set_extradata(struct video_client_ctx *client_ctx,
u32 *extradata_flag)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_meta_data_enable vcd_meta_data;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx || !extradata_flag)
return false;
vcd_property_hdr.prop_id = VCD_I_METADATA_ENABLE;
vcd_property_hdr.sz = sizeof(struct vcd_property_meta_data_enable);
vcd_meta_data.meta_data_enable_flag = *extradata_flag;
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &vcd_meta_data);
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_set_idr_only_decoding(struct video_client_ctx *client_ctx)
{
struct vcd_property_hdr vcd_property_hdr;
u32 vcd_status = VCD_ERR_FAIL;
u32 enable = true;
if (!client_ctx)
return false;
vcd_property_hdr.prop_id = VCD_I_DEC_PICTYPE;
vcd_property_hdr.sz = sizeof(u32);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &enable);
if (vcd_status)
return false;
return true;
}
static u32 vid_dec_set_meta_buffers(struct video_client_ctx *client_ctx,
struct vdec_meta_buffers *meta_buffers)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_meta_buffer *vcd_meta_buffer = NULL;
u32 vcd_status = VCD_ERR_FAIL;
u32 len = 0, len_iommu = 0, buf_size = 0;
int rc = 0;
unsigned long ionflag = 0, ionflag_iommu = 0;
unsigned long buffer_size = 0, buffer_size_iommu = 0;
unsigned long iova = 0, iova_iommu = 0;
int index = -1, num_buffers = 0;
u8 *ker_vir_addr = NULL, *ker_vir_addr_iommu = NULL;
if (!client_ctx || !meta_buffers)
return false;
vcd_property_hdr.prop_id = VCD_I_SET_EXT_METABUFFER;
vcd_property_hdr.sz = sizeof(struct vcd_property_meta_buffer);
vcd_meta_buffer = &client_ctx->vcd_meta_buffer;
memset(&client_ctx->vcd_meta_buffer, 0,
sizeof(struct vcd_property_meta_buffer));
vcd_meta_buffer->size = meta_buffers->size;
vcd_meta_buffer->count = meta_buffers->count;
vcd_meta_buffer->pmem_fd = meta_buffers->pmem_fd;
vcd_meta_buffer->offset = meta_buffers->offset;
vcd_meta_buffer->pmem_fd_iommu = meta_buffers->pmem_fd_iommu;
if (meta_buffers->count > MAX_META_BUFFERS) {
ERR("meta buffers maximum count reached, count = %d",
meta_buffers->count);
return false;
}
if (!vcd_get_ion_status()) {
pr_err("PMEM Not available\n");
return false;
} else {
client_ctx->meta_buffer_ion_handle = ion_import_dma_buf(
client_ctx->user_ion_client,
vcd_meta_buffer->pmem_fd);
if (IS_ERR_OR_NULL(client_ctx->meta_buffer_ion_handle)) {
ERR("%s(): get_ION_handle failed\n", __func__);
goto import_ion_error;
}
rc = ion_handle_get_flags(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle,
&ionflag);
if (rc) {
ERR("%s():get_ION_flags fail\n",
__func__);
goto import_ion_error;
}
vcd_meta_buffer->kernel_virtual_addr =
(u8 *) ion_map_kernel(
client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle);
if (!vcd_meta_buffer->kernel_virtual_addr) {
ERR("%s(): get_ION_kernel virtual addr failed\n",
__func__);
goto import_ion_error;
}
if (res_trk_check_for_sec_session() ||
(res_trk_get_core_type() == (u32)VCD_CORE_720P)) {
rc = ion_phys(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle,
(unsigned long *) (&(vcd_meta_buffer->
physical_addr)), &len);
if (rc) {
ERR("%s():get_ION_kernel physical addr fail\n",
__func__);
goto ion_map_error;
}
vcd_meta_buffer->client_data = NULL;
vcd_meta_buffer->dev_addr = (u8 *)
vcd_meta_buffer->physical_addr;
} else {
rc = ion_map_iommu(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle,
VIDEO_DOMAIN, VIDEO_MAIN_POOL,
SZ_4K, 0, (unsigned long *)&iova,
(unsigned long *)&buffer_size,
0, 0);
if (rc || !iova) {
ERR("%s():get_ION_kernel physical addr fail,"\
" rc = %d iova = 0x%lx\n",
__func__, rc, iova);
goto ion_map_error;
}
vcd_meta_buffer->physical_addr = (u8 *) iova;
vcd_meta_buffer->client_data = NULL;
vcd_meta_buffer->dev_addr = (u8 *) iova;
}
client_ctx->meta_buffer_iommu_ion_handle = ion_import_dma_buf(
client_ctx->user_ion_client,
vcd_meta_buffer->pmem_fd_iommu);
if (IS_ERR_OR_NULL(client_ctx->meta_buffer_iommu_ion_handle)) {
ERR("%s(): get_ION_handle failed\n", __func__);
goto import_ion_error;
}
rc = ion_handle_get_flags(client_ctx->user_ion_client,
client_ctx->
meta_buffer_iommu_ion_handle,
&ionflag_iommu);
if (rc) {
ERR("%s():get_ION_flags fail\n",
__func__);
goto import_ion_error;
}
vcd_meta_buffer->kernel_virt_addr_iommu =
(u8 *) ion_map_kernel(
client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle);
if (!vcd_meta_buffer->kernel_virt_addr_iommu) {
ERR("%s(): get_ION_kernel virtual addr failed\n",
__func__);
goto import_ion_error;
}
if (res_trk_get_core_type() == (u32)VCD_CORE_720P) {
rc = ion_phys(client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle,
(unsigned long *) (&(vcd_meta_buffer->
physical_addr_iommu)), &len_iommu);
if (rc) {
ERR("%s():get_ION_kernel physical addr fail\n",
__func__);
goto ion_map_error_iommu;
}
vcd_meta_buffer->client_data_iommu = NULL;
vcd_meta_buffer->dev_addr_iommu = (u8 *)
vcd_meta_buffer->physical_addr_iommu;
} else {
rc = ion_map_iommu(client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle,
VIDEO_DOMAIN, VIDEO_MAIN_POOL,
SZ_4K, 0, (unsigned long *)&iova_iommu,
(unsigned long *)&buffer_size_iommu,
0, 0);
if (rc || !iova_iommu) {
ERR("%s():get_ION_kernel physical addr fail, "\
"rc = %d iova = 0x%lx\n",
__func__, rc, iova);
goto ion_map_error_iommu;
}
vcd_meta_buffer->physical_addr_iommu =
(u8 *) iova_iommu;
vcd_meta_buffer->client_data_iommu = NULL;
vcd_meta_buffer->dev_addr_iommu = (u8 *) iova_iommu;
}
}
/*fill the meta addr table*/
num_buffers = vcd_meta_buffer->count;
buf_size = vcd_meta_buffer->size/num_buffers;
ker_vir_addr = vcd_meta_buffer->kernel_virtual_addr;
ker_vir_addr_iommu = vcd_meta_buffer->kernel_virt_addr_iommu;
client_ctx->meta_buf_size = buf_size;
for (index = 0; index < num_buffers; index++) {
client_ctx->meta_addr_table[index].kernel_vir_addr =
ker_vir_addr;
client_ctx->meta_addr_table[index].kernel_vir_addr_iommu =
ker_vir_addr_iommu;
DBG("[%d] kernel_virtual = %p kernel_vir_iommu = %p",
index, ker_vir_addr, ker_vir_addr_iommu);
ker_vir_addr += buf_size;
ker_vir_addr_iommu += buf_size;
}
DBG("Meta Buffer: Virt: %p, Phys %p, fd: %d",
vcd_meta_buffer->kernel_virtual_addr,
vcd_meta_buffer->physical_addr,
vcd_meta_buffer->pmem_fd);
DBG("IOMMU Meta Buffer: Virt: %p, Phys %p, fd: %d",
vcd_meta_buffer->kernel_virt_addr_iommu,
vcd_meta_buffer->physical_addr_iommu,
vcd_meta_buffer->pmem_fd_iommu);
DBG("Meta_buffer: Dev addr %p", vcd_meta_buffer->dev_addr);
DBG("IOMMU Meta_buffer: Dev addr %p",
vcd_meta_buffer->dev_addr_iommu);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr,
vcd_meta_buffer);
if (vcd_status)
return false;
else
return true;
ion_map_error_iommu:
if (vcd_meta_buffer->kernel_virt_addr_iommu) {
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle);
vcd_meta_buffer->kernel_virt_addr_iommu = NULL;
}
if (!IS_ERR_OR_NULL(client_ctx->meta_buffer_iommu_ion_handle)) {
ion_free(client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle);
client_ctx->meta_buffer_iommu_ion_handle = NULL;
}
ion_map_error:
if (vcd_meta_buffer->kernel_virtual_addr) {
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle);
vcd_meta_buffer->kernel_virtual_addr = NULL;
}
if (!IS_ERR_OR_NULL(client_ctx->meta_buffer_ion_handle)) {
ion_free(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle);
client_ctx->meta_buffer_ion_handle = NULL;
}
import_ion_error:
return false;
}
static u32 vid_dec_set_h264_mv_buffers(struct video_client_ctx *client_ctx,
struct vdec_h264_mv *mv_data)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_h264_mv_buffer *vcd_h264_mv_buffer = NULL;
u32 vcd_status = VCD_ERR_FAIL;
u32 len = 0;
int rc = 0;
unsigned long ionflag = 0;
unsigned long buffer_size = 0;
unsigned long iova = 0;
if (!client_ctx || !mv_data)
return false;
vcd_property_hdr.prop_id = VCD_I_H264_MV_BUFFER;
vcd_property_hdr.sz = sizeof(struct vcd_property_h264_mv_buffer);
vcd_h264_mv_buffer = &client_ctx->vcd_h264_mv_buffer;
memset(&client_ctx->vcd_h264_mv_buffer, 0,
sizeof(struct vcd_property_h264_mv_buffer));
vcd_h264_mv_buffer->size = mv_data->size;
vcd_h264_mv_buffer->count = mv_data->count;
vcd_h264_mv_buffer->pmem_fd = mv_data->pmem_fd;
vcd_h264_mv_buffer->offset = mv_data->offset;
if (!vcd_get_ion_status()) {
pr_err("PMEM not available\n");
return false;
} else {
client_ctx->h264_mv_ion_handle = ion_import_dma_buf(
client_ctx->user_ion_client,
vcd_h264_mv_buffer->pmem_fd);
if (IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) {
ERR("%s(): get_ION_handle failed\n", __func__);
goto import_ion_error;
}
rc = ion_handle_get_flags(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle,
&ionflag);
if (rc) {
ERR("%s():get_ION_flags fail\n",
__func__);
goto import_ion_error;
}
vcd_h264_mv_buffer->kernel_virtual_addr = (u8 *) ion_map_kernel(
client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle);
if (!vcd_h264_mv_buffer->kernel_virtual_addr) {
ERR("%s(): get_ION_kernel virtual addr failed\n",
__func__);
goto import_ion_error;
}
if (res_trk_check_for_sec_session() ||
(res_trk_get_core_type() == (u32)VCD_CORE_720P)) {
rc = ion_phys(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle,
(unsigned long *) (&(vcd_h264_mv_buffer->
physical_addr)), &len);
if (rc) {
ERR("%s():get_ION_kernel physical addr fail\n",
__func__);
goto ion_map_error;
}
vcd_h264_mv_buffer->client_data = NULL;
vcd_h264_mv_buffer->dev_addr = (u8 *)
vcd_h264_mv_buffer->physical_addr;
} else {
rc = ion_map_iommu(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle,
VIDEO_DOMAIN, VIDEO_MAIN_POOL,
SZ_4K, 0, (unsigned long *)&iova,
(unsigned long *)&buffer_size,
0, 0);
if (rc || !iova) {
ERR(
"%s():get_ION_kernel physical addr fail, rc = %d iova = 0x%lx\n",
__func__, rc, iova);
goto ion_map_error;
}
vcd_h264_mv_buffer->physical_addr = (u8 *) iova;
vcd_h264_mv_buffer->client_data = NULL;
vcd_h264_mv_buffer->dev_addr = (u8 *) iova;
}
}
DBG("Virt: %p, Phys %p, fd: %d", vcd_h264_mv_buffer->
kernel_virtual_addr, vcd_h264_mv_buffer->physical_addr,
vcd_h264_mv_buffer->pmem_fd);
DBG("Dev addr %p", vcd_h264_mv_buffer->dev_addr);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, vcd_h264_mv_buffer);
if (vcd_status)
return false;
else
return true;
ion_map_error:
if (vcd_h264_mv_buffer->kernel_virtual_addr) {
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle);
vcd_h264_mv_buffer->kernel_virtual_addr = NULL;
}
if (!IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) {
ion_free(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle);
client_ctx->h264_mv_ion_handle = NULL;
}
import_ion_error:
return false;
}
static u32 vid_dec_set_cont_on_reconfig(struct video_client_ctx *client_ctx)
{
struct vcd_property_hdr vcd_property_hdr;
u32 vcd_status = VCD_ERR_FAIL;
u32 enable = true;
if (!client_ctx)
return false;
vcd_property_hdr.prop_id = VCD_I_CONT_ON_RECONFIG;
vcd_property_hdr.sz = sizeof(u32);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &enable);
if (vcd_status)
return false;
return true;
}
static u32 vid_dec_get_h264_mv_buffer_size(struct video_client_ctx *client_ctx,
struct vdec_mv_buff_size *mv_buff)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_buffer_size h264_mv_buffer_size;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx || !mv_buff)
return false;
vcd_property_hdr.prop_id = VCD_I_GET_H264_MV_SIZE;
vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
h264_mv_buffer_size.width = mv_buff->width;
h264_mv_buffer_size.height = mv_buff->height;
vcd_status = vcd_get_property(client_ctx->vcd_handle,
&vcd_property_hdr, &h264_mv_buffer_size);
mv_buff->width = h264_mv_buffer_size.width;
mv_buff->height = h264_mv_buffer_size.height;
mv_buff->size = h264_mv_buffer_size.size;
mv_buff->alignment = h264_mv_buffer_size.alignment;
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_free_meta_buffers(struct video_client_ctx *client_ctx)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_buffer_size meta_buffer_size;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx)
return false;
vcd_property_hdr.prop_id = VCD_I_FREE_EXT_METABUFFER;
vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &meta_buffer_size);
if (!IS_ERR_OR_NULL(client_ctx->meta_buffer_ion_handle)) {
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle);
if (!res_trk_check_for_sec_session() &&
(res_trk_get_core_type() != (u32)VCD_CORE_720P)) {
ion_unmap_iommu(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle,
VIDEO_DOMAIN,
VIDEO_MAIN_POOL);
}
ion_free(client_ctx->user_ion_client,
client_ctx->meta_buffer_ion_handle);
client_ctx->meta_buffer_ion_handle = NULL;
}
if (!IS_ERR_OR_NULL(client_ctx->meta_buffer_iommu_ion_handle)) {
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle);
if (res_trk_check_for_sec_session() &&
(res_trk_get_core_type() != (u32)VCD_CORE_720P)) {
ion_unmap_iommu(client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle,
VIDEO_DOMAIN,
VIDEO_MAIN_POOL);
}
ion_free(client_ctx->user_ion_client,
client_ctx->meta_buffer_iommu_ion_handle);
client_ctx->meta_buffer_iommu_ion_handle = NULL;
}
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_free_h264_mv_buffers(struct video_client_ctx *client_ctx)
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_buffer_size h264_mv_buffer_size;
u32 vcd_status = VCD_ERR_FAIL;
if (!client_ctx)
return false;
vcd_property_hdr.prop_id = VCD_I_FREE_H264_MV_BUFFER;
vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_size);
vcd_status = vcd_set_property(client_ctx->vcd_handle,
&vcd_property_hdr, &h264_mv_buffer_size);
if (!IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) {
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle);
if (!res_trk_check_for_sec_session() &&
(res_trk_get_core_type() != (u32)VCD_CORE_720P)) {
ion_unmap_iommu(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle,
VIDEO_DOMAIN,
VIDEO_MAIN_POOL);
}
ion_free(client_ctx->user_ion_client,
client_ctx->h264_mv_ion_handle);
client_ctx->h264_mv_ion_handle = NULL;
}
if (vcd_status)
return false;
else
return true;
}
static u32 vid_dec_get_buffer_req(struct video_client_ctx *client_ctx,
struct vdec_allocatorproperty *vdec_buf_req)
{
u32 vcd_status = VCD_ERR_FAIL;
struct vcd_buffer_requirement vcd_buf_req;
if (!client_ctx || !vdec_buf_req)
return false;
if (vdec_buf_req->buffer_type == VDEC_BUFFER_TYPE_INPUT) {
vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
VCD_BUFFER_INPUT,
&vcd_buf_req);
} else {
vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
VCD_BUFFER_OUTPUT,
&vcd_buf_req);
}
if (vcd_status) {
return false;
} else {
vdec_buf_req->mincount = vcd_buf_req.min_count;
vdec_buf_req->maxcount = vcd_buf_req.max_count;
vdec_buf_req->actualcount = vcd_buf_req.actual_count;
vdec_buf_req->buffer_size = vcd_buf_req.sz;
vdec_buf_req->alignment = vcd_buf_req.align;
vdec_buf_req->buf_poolid = vcd_buf_req.buf_pool_id;
vdec_buf_req->meta_buffer_size = vcd_buf_req.meta_buffer_size;
return true;
}
}
static u32 vid_dec_set_buffer(struct video_client_ctx *client_ctx,
struct vdec_setbuffer_cmd *buffer_info)
{
enum vcd_buffer_type buffer = VCD_BUFFER_INPUT;
enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
u32 vcd_status = VCD_ERR_FAIL;
unsigned long kernel_vaddr, buf_adr_offset = 0, length;
if (!client_ctx || !buffer_info)
return false;
if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) {
dir_buffer = BUFFER_TYPE_OUTPUT;
buffer = VCD_BUFFER_OUTPUT;
buf_adr_offset = (unsigned long)buffer_info->buffer.offset;
}
length = buffer_info->buffer.buffer_len;
/*If buffer cannot be set, ignore */
if (!vidc_insert_addr_table(client_ctx, dir_buffer,
(unsigned long)buffer_info->buffer.bufferaddr,
&kernel_vaddr, buffer_info->buffer.pmem_fd,
buf_adr_offset, MAX_VIDEO_NUM_OF_BUFF, length)) {
DBG("%s() : user_virt_addr = %p cannot be set.",
__func__, buffer_info->buffer.bufferaddr);
return false;
}
vcd_status = vcd_set_buffer(client_ctx->vcd_handle,
buffer, (u8 *) kernel_vaddr,
buffer_info->buffer.buffer_len);
if (!vcd_status)
return true;
else
return false;
}
static u32 vid_dec_free_buffer(struct video_client_ctx *client_ctx,
struct vdec_setbuffer_cmd *buffer_info)
{
enum vcd_buffer_type buffer = VCD_BUFFER_INPUT;
enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
u32 vcd_status = VCD_ERR_FAIL;
unsigned long kernel_vaddr;
if (!client_ctx || !buffer_info)
return false;
if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) {
dir_buffer = BUFFER_TYPE_OUTPUT;
buffer = VCD_BUFFER_OUTPUT;
}
/*If buffer NOT set, ignore */
if (!vidc_delete_addr_table(client_ctx, dir_buffer,
(unsigned long)buffer_info->buffer.bufferaddr,
&kernel_vaddr)) {
DBG("%s() : user_virt_addr = %p has not been set.",
__func__, buffer_info->buffer.bufferaddr);
return true;
}
vcd_status = vcd_free_buffer(client_ctx->vcd_handle, buffer,
(u8 *)kernel_vaddr);
if (!vcd_status)
return true;
else
return false;
}
static u32 vid_dec_pause_resume(struct video_client_ctx *client_ctx, u32 pause)
{
u32 vcd_status;
if (!client_ctx) {
ERR("\n %s(): Invalid client_ctx", __func__);
return false;
}
if (pause) {
DBG("msm_vidc_dec: PAUSE command from client = %p\n",
client_ctx);
vcd_status = vcd_pause(client_ctx->vcd_handle);
} else{
DBG("msm_vidc_dec: RESUME command from client = %p\n",
client_ctx);
vcd_status = vcd_resume(client_ctx->vcd_handle);
}
if (vcd_status)
return false;
return true;
}
static u32 vid_dec_start_stop(struct video_client_ctx *client_ctx, u32 start)
{
struct vid_dec_msg *vdec_msg = NULL;
u32 vcd_status;
DBG("msm_vidc_dec: Inside %s()", __func__);
if (!client_ctx) {
ERR("\n Invalid client_ctx");
return false;
}
if (start) {
if (client_ctx->seq_header_set) {
DBG("%s(): Seq Hdr set: Send START_DONE to client",
__func__);
vdec_msg = kzalloc(sizeof(*vdec_msg), GFP_KERNEL);
if (!vdec_msg) {
ERR("vid_dec_start_stop: cannot allocate"
"buffer\n");
return false;
}
vdec_msg->vdec_msg_info.msgcode =
VDEC_MSG_RESP_START_DONE;
vdec_msg->vdec_msg_info.status_code = VDEC_S_SUCCESS;
vdec_msg->vdec_msg_info.msgdatasize = 0;
mutex_lock(&client_ctx->msg_queue_lock);
list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
mutex_unlock(&client_ctx->msg_queue_lock);
wake_up(&client_ctx->msg_wait);
DBG("Send START_DONE message to client = %p\n",
client_ctx);
} else {
DBG("%s(): Calling decode_start()", __func__);
vcd_status =
vcd_decode_start(client_ctx->vcd_handle, NULL);
if (vcd_status) {
ERR("%s(): vcd_decode_start failed."
" vcd_status = %u\n", __func__, vcd_status);
return false;
}
}
} else {
DBG("%s(): Calling vcd_stop()", __func__);
mutex_lock(&vid_dec_device_p->lock);
vcd_status = VCD_ERR_FAIL;
if (!client_ctx->stop_called) {
client_ctx->stop_called = true;
vcd_status = vcd_stop(client_ctx->vcd_handle);
}
if (vcd_status) {
ERR("%s(): vcd_stop failed. vcd_status = %u\n",
__func__, vcd_status);
mutex_unlock(&vid_dec_device_p->lock);
return false;
}
DBG("Send STOP_DONE message to client = %p\n", client_ctx);
mutex_unlock(&vid_dec_device_p->lock);
}
return true;
}
static u32 vid_dec_decode_frame(struct video_client_ctx *client_ctx,
struct vdec_input_frameinfo *input_frame_info,
u8 *desc_buf, u32 desc_size)
{
struct vcd_frame_data vcd_input_buffer;
unsigned long kernel_vaddr, phy_addr, user_vaddr;
int pmem_fd;
struct file *file;
s32 buffer_index = -1;
u32 vcd_status = VCD_ERR_FAIL;
u32 ion_flag = 0;
struct ion_handle *buff_handle = NULL;
if (!client_ctx || !input_frame_info)
return false;
user_vaddr = (unsigned long)input_frame_info->bufferaddr;
if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT,
true, &user_vaddr, &kernel_vaddr,
&phy_addr, &pmem_fd, &file,
&buffer_index)) {
/* kernel_vaddr is found. send the frame to VCD */
memset((void *)&vcd_input_buffer, 0,
sizeof(struct vcd_frame_data));
vcd_input_buffer.virtual =
(u8 *) (kernel_vaddr + input_frame_info->pmem_offset);
vcd_input_buffer.offset = input_frame_info->offset;
vcd_input_buffer.frm_clnt_data =
(u32) input_frame_info->client_data;
vcd_input_buffer.ip_frm_tag =
(u32) input_frame_info->client_data;
vcd_input_buffer.data_len = input_frame_info->datalen;
vcd_input_buffer.time_stamp = input_frame_info->timestamp;
/* Rely on VCD using the same flags as OMX */
vcd_input_buffer.flags = input_frame_info->flags;
vcd_input_buffer.desc_buf = desc_buf;
vcd_input_buffer.desc_size = desc_size;
if (vcd_input_buffer.data_len > 0) {
ion_flag = vidc_get_fd_info(client_ctx,
BUFFER_TYPE_INPUT,
pmem_fd,
kernel_vaddr,
buffer_index,
&buff_handle);
if (ion_flag == ION_FLAG_CACHED && buff_handle) {
msm_ion_do_cache_op(client_ctx->user_ion_client,
buff_handle,
(unsigned long *)kernel_vaddr,
(unsigned long) vcd_input_buffer.data_len,
ION_IOC_CLEAN_CACHES);
}
}
vcd_status = vcd_decode_frame(client_ctx->vcd_handle,
&vcd_input_buffer);
if (!vcd_status)
return true;
else {
ERR("%s(): vcd_decode_frame failed = %u\n", __func__,
vcd_status);
return false;
}
} else {
ERR("%s(): kernel_vaddr not found\n", __func__);
return false;
}
}
static u32 vid_dec_fill_output_buffer(struct video_client_ctx *client_ctx,
struct vdec_fillbuffer_cmd *fill_buffer_cmd)
{
unsigned long kernel_vaddr, phy_addr, user_vaddr;
int pmem_fd;
struct file *file;
s32 buffer_index = -1;
u32 vcd_status = VCD_ERR_FAIL;
struct ion_handle *buff_handle = NULL;
struct vcd_frame_data vcd_frame;
if (!client_ctx || !fill_buffer_cmd)
return false;
user_vaddr = (unsigned long)fill_buffer_cmd->buffer.bufferaddr;
if (vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
true, &user_vaddr, &kernel_vaddr,
&phy_addr, &pmem_fd, &file,
&buffer_index)) {
memset((void *)&vcd_frame, 0,
sizeof(struct vcd_frame_data));
vcd_frame.virtual = (u8 *) kernel_vaddr;
vcd_frame.frm_clnt_data = (u32) fill_buffer_cmd->client_data;
vcd_frame.alloc_len = fill_buffer_cmd->buffer.buffer_len;
vcd_frame.ion_flag = vidc_get_fd_info(client_ctx,
BUFFER_TYPE_OUTPUT,
pmem_fd, kernel_vaddr,
buffer_index,
&buff_handle);
vcd_frame.buff_ion_handle = buff_handle;
vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle,
&vcd_frame);
if (!vcd_status)
return true;
else {
ERR("%s(): vcd_fill_output_buffer failed = %u\n",
__func__, vcd_status);
return false;
}
} else {
ERR("%s(): kernel_vaddr not found\n", __func__);
return false;
}
}
static u32 vid_dec_flush(struct video_client_ctx *client_ctx,
enum vdec_bufferflush flush_dir)
{
u32 vcd_status = VCD_ERR_FAIL;
DBG("msm_vidc_dec: %s() called with dir = %u", __func__,
flush_dir);
if (!client_ctx) {
ERR("\n Invalid client_ctx");
return false;
}
switch (flush_dir) {
case VDEC_FLUSH_TYPE_INPUT:
vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_INPUT);
break;
case VDEC_FLUSH_TYPE_OUTPUT:
vcd_status = vcd_flush(client_ctx->vcd_handle,
VCD_FLUSH_OUTPUT);
break;
case VDEC_FLUSH_TYPE_ALL:
vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_ALL);
break;
default:
ERR("%s(): Inavlid flush cmd. flush_dir = %u\n", __func__,
flush_dir);
return false;
break;
}
if (!vcd_status)
return true;
else {
ERR("%s(): vcd_flush failed. vcd_status = %u "
" flush_dir = %u\n", __func__, vcd_status, flush_dir);
return false;
}
}
static u32 vid_dec_msg_pending(struct video_client_ctx *client_ctx)
{
u32 islist_empty = 0;
mutex_lock(&client_ctx->msg_queue_lock);
islist_empty = list_empty(&client_ctx->msg_queue);
mutex_unlock(&client_ctx->msg_queue_lock);
if (islist_empty) {
DBG("%s(): vid_dec msg queue empty\n", __func__);
if (client_ctx->stop_msg) {
DBG("%s(): List empty and Stop Msg set\n",
__func__);
return client_ctx->stop_msg;
}
} else
DBG("%s(): vid_dec msg queue Not empty\n", __func__);
return !islist_empty;
}
static int vid_dec_get_next_msg(struct video_client_ctx *client_ctx,
struct vdec_msginfo *vdec_msg_info)
{
int rc;
struct vid_dec_msg *vid_dec_msg = NULL;
if (!client_ctx)
return false;
rc = wait_event_interruptible(client_ctx->msg_wait,
vid_dec_msg_pending(client_ctx));
if (rc < 0) {
DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg);
return rc;
} else if (client_ctx->stop_msg) {
DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg);
return -EIO;
}
mutex_lock(&client_ctx->msg_queue_lock);
if (!list_empty(&client_ctx->msg_queue)) {
DBG("%s(): After Wait\n", __func__);
vid_dec_msg = list_first_entry(&client_ctx->msg_queue,
struct vid_dec_msg, list);
list_del(&vid_dec_msg->list);
memcpy(vdec_msg_info, &vid_dec_msg->vdec_msg_info,
sizeof(struct vdec_msginfo));
kfree(vid_dec_msg);
}
mutex_unlock(&client_ctx->msg_queue_lock);
return 0;
}
static long vid_dec_ioctl(struct file *file,
unsigned cmd, unsigned long u_arg)
{
struct video_client_ctx *client_ctx = NULL;
struct vdec_ioctl_msg vdec_msg;
u32 vcd_status;
unsigned long kernel_vaddr, phy_addr, len;
unsigned long ker_vaddr;
u32 result = true;
#ifdef KW_TAINT_ANALYSIS
void __user *arg = (void __user *) get_tainted_stuff();
#else
void __user *arg = (void __user *)u_arg;
#endif
int rc = 0;
size_t ion_len;
DBG("%s\n", __func__);
if (_IOC_TYPE(cmd) != VDEC_IOCTL_MAGIC)
return -ENOTTY;
client_ctx = (struct video_client_ctx *)file->private_data;
if (!client_ctx) {
ERR("!client_ctx. Cannot attach to device handle\n");
return -ENODEV;
}
switch (cmd) {
case VDEC_IOCTL_SET_CODEC:
{
enum vdec_codec vdec_codec;
DBG("VDEC_IOCTL_SET_CODEC\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&vdec_codec, vdec_msg.in,
sizeof(vdec_codec)))
return -EFAULT;
DBG("setting code type = %u\n", vdec_codec);
result = vid_dec_set_codec(client_ctx, &vdec_codec);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_SET_OUTPUT_FORMAT:
{
enum vdec_output_fromat output_format;
DBG("VDEC_IOCTL_SET_OUTPUT_FORMAT\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&output_format, vdec_msg.in,
sizeof(output_format)))
return -EFAULT;
result = vid_dec_set_output_format(client_ctx, &output_format);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_SET_PICRES:
{
struct vdec_picsize video_resoultion;
DBG("VDEC_IOCTL_SET_PICRES\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&video_resoultion, vdec_msg.in,
sizeof(video_resoultion)))
return -EFAULT;
result =
vid_dec_set_frame_resolution(client_ctx, &video_resoultion);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_GET_PICRES:
{
struct vdec_picsize video_resoultion;
DBG("VDEC_IOCTL_GET_PICRES\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&video_resoultion, vdec_msg.out,
sizeof(video_resoultion)))
return -EFAULT;
result = vid_dec_get_frame_resolution(client_ctx,
&video_resoultion);
if (result) {
if (copy_to_user(vdec_msg.out, &video_resoultion,
sizeof(video_resoultion)))
return -EFAULT;
} else
return -EIO;
break;
}
case VDEC_IOCTL_SET_BUFFER_REQ:
{
struct vdec_allocatorproperty vdec_buf_req;
struct vcd_buffer_requirement buffer_req;
DBG("VDEC_IOCTL_SET_BUFFER_REQ\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&vdec_buf_req, vdec_msg.in,
sizeof(vdec_buf_req)))
return -EFAULT;
buffer_req.actual_count = vdec_buf_req.actualcount;
buffer_req.align = vdec_buf_req.alignment;
buffer_req.max_count = vdec_buf_req.maxcount;
buffer_req.min_count = vdec_buf_req.mincount;
buffer_req.sz = vdec_buf_req.buffer_size;
switch (vdec_buf_req.buffer_type) {
case VDEC_BUFFER_TYPE_INPUT:
vcd_status =
vcd_set_buffer_requirements(client_ctx->vcd_handle,
VCD_BUFFER_INPUT, &buffer_req);
break;
case VDEC_BUFFER_TYPE_OUTPUT:
vcd_status =
vcd_set_buffer_requirements(client_ctx->vcd_handle,
VCD_BUFFER_OUTPUT, &buffer_req);
break;
default:
vcd_status = VCD_ERR_BAD_POINTER;
break;
}
if (vcd_status)
return -EFAULT;
break;
}
case VDEC_IOCTL_GET_BUFFER_REQ:
{
struct vdec_allocatorproperty vdec_buf_req;
DBG("VDEC_IOCTL_GET_BUFFER_REQ\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&vdec_buf_req, vdec_msg.out,
sizeof(vdec_buf_req)))
return -EFAULT;
result = vid_dec_get_buffer_req(client_ctx, &vdec_buf_req);
if (result) {
if (copy_to_user(vdec_msg.out, &vdec_buf_req,
sizeof(vdec_buf_req)))
return -EFAULT;
} else
return -EIO;
break;
}
case VDEC_IOCTL_SET_BUFFER:
{
struct vdec_setbuffer_cmd setbuffer;
DBG("VDEC_IOCTL_SET_BUFFER\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&setbuffer, vdec_msg.in,
sizeof(setbuffer)))
return -EFAULT;
result = vid_dec_set_buffer(client_ctx, &setbuffer);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_FREE_BUFFER:
{
struct vdec_setbuffer_cmd setbuffer;
DBG("VDEC_IOCTL_FREE_BUFFER\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&setbuffer, vdec_msg.in,
sizeof(setbuffer)))
return -EFAULT;
result = vid_dec_free_buffer(client_ctx, &setbuffer);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_CMD_START:
{
DBG(" VDEC_IOCTL_CMD_START\n");
result = vid_dec_start_stop(client_ctx, true);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_CMD_STOP:
{
DBG("VDEC_IOCTL_CMD_STOP\n");
result = vid_dec_start_stop(client_ctx, false);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_CMD_PAUSE:
{
result = vid_dec_pause_resume(client_ctx, true);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_CMD_RESUME:
{
DBG("VDEC_IOCTL_CMD_PAUSE\n");
result = vid_dec_pause_resume(client_ctx, false);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_DECODE_FRAME:
{
struct vdec_input_frameinfo input_frame_info;
u8 *desc_buf = NULL;
u32 desc_size = 0;
DBG("VDEC_IOCTL_DECODE_FRAME\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&input_frame_info, vdec_msg.in,
sizeof(input_frame_info)))
return -EFAULT;
if (client_ctx->dmx_disable) {
if (input_frame_info.desc_addr) {
desc_size = input_frame_info.desc_size;
desc_buf = kzalloc(desc_size, GFP_KERNEL);
if (desc_buf) {
if (copy_from_user(desc_buf,
input_frame_info.desc_addr,
desc_size)) {
kfree(desc_buf);
desc_buf = NULL;
return -EFAULT;
}
}
} else
return -EINVAL;
}
result = vid_dec_decode_frame(client_ctx, &input_frame_info,
desc_buf, desc_size);
if (!result) {
kfree(desc_buf);
desc_buf = NULL;
return -EIO;
}
break;
}
case VDEC_IOCTL_SET_PERF_CLK:
{
vid_dec_set_turbo_clk(client_ctx);
break;
}
case VDEC_IOCTL_FILL_OUTPUT_BUFFER:
{
struct vdec_fillbuffer_cmd fill_buffer_cmd;
DBG("VDEC_IOCTL_FILL_OUTPUT_BUFFER\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&fill_buffer_cmd, vdec_msg.in,
sizeof(fill_buffer_cmd)))
return -EFAULT;
result = vid_dec_fill_output_buffer(client_ctx,
&fill_buffer_cmd);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_CMD_FLUSH:
{
enum vdec_bufferflush flush_dir;
DBG("VDEC_IOCTL_CMD_FLUSH\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&flush_dir, vdec_msg.in,
sizeof(flush_dir)))
return -EFAULT;
result = vid_dec_flush(client_ctx, flush_dir);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_GET_NEXT_MSG:
{
struct vdec_msginfo vdec_msg_info;
DBG("VDEC_IOCTL_GET_NEXT_MSG\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
result = vid_dec_get_next_msg(client_ctx, &vdec_msg_info);
if (result)
return result;
if (copy_to_user(vdec_msg.out, &vdec_msg_info,
sizeof(vdec_msg_info)))
return -EFAULT;
break;
}
case VDEC_IOCTL_STOP_NEXT_MSG:
{
DBG("VDEC_IOCTL_STOP_NEXT_MSG\n");
client_ctx->stop_msg = 1;
wake_up(&client_ctx->msg_wait);
break;
}
case VDEC_IOCTL_SET_SEQUENCE_HEADER:
{
struct vdec_seqheader seq_header;
struct vcd_sequence_hdr vcd_seq_hdr;
unsigned long ionflag;
DBG("VDEC_IOCTL_SET_SEQUENCE_HEADER\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg))) {
ERR("Copy from user vdec_msg failed\n");
return -EFAULT;
}
if (copy_from_user(&seq_header, vdec_msg.in,
sizeof(seq_header))) {
ERR("Copy from user seq_header failed\n");
return -EFAULT;
}
if (!seq_header.seq_header_len) {
ERR("Seq Len is Zero\n");
return -EFAULT;
}
if (!vcd_get_ion_status()) {
pr_err("PMEM Not available\n");
return -EINVAL;
} else {
client_ctx->seq_hdr_ion_handle = ion_import_dma_buf(
client_ctx->user_ion_client,
seq_header.pmem_fd);
if (!client_ctx->seq_hdr_ion_handle) {
ERR("%s(): get_ION_handle failed\n", __func__);
return false;
}
rc = ion_handle_get_flags(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle,
&ionflag);
if (rc) {
ERR("%s():get_ION_flags fail\n",
__func__);
ion_free(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle);
return false;
}
ker_vaddr = (unsigned long) ion_map_kernel(
client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle);
if (!ker_vaddr) {
ERR("%s():get_ION_kernel virtual addr fail\n",
__func__);
ion_free(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle);
return false;
}
kernel_vaddr = ker_vaddr;
rc = ion_phys(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle,
&phy_addr, &ion_len);
if (rc) {
ERR("%s():get_ION_kernel physical addr fail\n",
__func__);
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle);
ion_free(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle);
return false;
}
len = ion_len;
}
vcd_seq_hdr.sequence_header_len = seq_header.seq_header_len;
kernel_vaddr += (unsigned long)seq_header.pmem_offset;
vcd_seq_hdr.sequence_header = (u8 *)kernel_vaddr;
if (!vcd_seq_hdr.sequence_header) {
ERR("Sequence Header pointer failed\n");
return -EFAULT;
}
client_ctx->seq_header_set = true;
if (vcd_decode_start(client_ctx->vcd_handle, &vcd_seq_hdr)) {
ERR("Decode start Failed\n");
client_ctx->seq_header_set = false;
return -EFAULT;
}
DBG("Wait Client completion Sequence Header\n");
wait_for_completion(&client_ctx->event);
vcd_seq_hdr.sequence_header = NULL;
if (client_ctx->event_status) {
ERR("Set Seq Header status is failed");
return -EFAULT;
}
if (vcd_get_ion_status()) {
if (client_ctx->seq_hdr_ion_handle) {
ion_unmap_kernel(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle);
ion_free(client_ctx->user_ion_client,
client_ctx->seq_hdr_ion_handle);
}
}
break;
}
case VDEC_IOCTL_GET_NUMBER_INSTANCES:
{
DBG("VDEC_IOCTL_GET_NUMBER_INSTANCES\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_to_user(vdec_msg.out,
&vid_dec_device_p->num_clients, sizeof(u32)))
return -EFAULT;
break;
}
case VDEC_IOCTL_GET_INTERLACE_FORMAT:
{
u32 progressive_only, interlace_format;
DBG("VDEC_IOCTL_GET_INTERLACE_FORMAT\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
result = vid_dec_get_progressive_only(client_ctx,
&progressive_only);
if (result) {
interlace_format = progressive_only ?
VDEC_InterlaceFrameProgressive :
VDEC_InterlaceInterleaveFrameTopFieldFirst;
if (copy_to_user(vdec_msg.out, &interlace_format,
sizeof(u32)))
return -EFAULT;
} else
return -EIO;
break;
}
case VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT:
{
u32 disable_dmx;
DBG("VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
result = vid_dec_get_disable_dmx_support(client_ctx,
&disable_dmx);
if (result) {
if (copy_to_user(vdec_msg.out, &disable_dmx,
sizeof(u32)))
return -EFAULT;
} else
return -EIO;
break;
}
case VDEC_IOCTL_GET_DISABLE_DMX:
{
u32 disable_dmx;
DBG("VDEC_IOCTL_GET_DISABLE_DMX\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
result = vid_dec_get_disable_dmx(client_ctx,
&disable_dmx);
if (result) {
if (copy_to_user(vdec_msg.out, &disable_dmx,
sizeof(u32)))
return -EFAULT;
} else
return -EIO;
break;
}
case VDEC_IOCTL_SET_DISABLE_DMX:
{
DBG("VDEC_IOCTL_SET_DISABLE_DMX\n");
result = vid_dec_set_disable_dmx(client_ctx);
if (!result)
return -EIO;
client_ctx->dmx_disable = 1;
break;
}
case VDEC_IOCTL_SET_PICTURE_ORDER:
{
u32 picture_order;
DBG("VDEC_IOCTL_SET_PICTURE_ORDER\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&picture_order, vdec_msg.in,
sizeof(u32)))
return -EFAULT;
result = vid_dec_set_picture_order(client_ctx, &picture_order);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_SET_FRAME_RATE:
{
struct vdec_framerate frame_rate;
DBG("VDEC_IOCTL_SET_FRAME_RATE\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&frame_rate, vdec_msg.in,
sizeof(frame_rate)))
return -EFAULT;
result = vid_dec_set_frame_rate(client_ctx, &frame_rate);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_SET_EXTRADATA:
{
u32 extradata_flag;
DBG("VDEC_IOCTL_SET_EXTRADATA\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&extradata_flag, vdec_msg.in,
sizeof(u32)))
return -EFAULT;
result = vid_dec_set_extradata(client_ctx, &extradata_flag);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_SET_META_BUFFERS:
{
struct vdec_meta_buffers meta_buffers;
DBG("VDEC_IOCTL_SET_META_BUFFERS\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&meta_buffers, vdec_msg.in,
sizeof(meta_buffers)))
return -EFAULT;
result = vid_dec_set_meta_buffers(client_ctx, &meta_buffers);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_FREE_META_BUFFERS:
{
DBG("VDEC_IOCTL_FREE_META_BUFFERS\n");
result = vid_dec_free_meta_buffers(client_ctx);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_SET_H264_MV_BUFFER:
{
struct vdec_h264_mv mv_data;
DBG("VDEC_IOCTL_SET_H264_MV_BUFFER\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&mv_data, vdec_msg.in,
sizeof(mv_data)))
return -EFAULT;
result = vid_dec_set_h264_mv_buffers(client_ctx, &mv_data);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_FREE_H264_MV_BUFFER:
{
DBG("VDEC_IOCTL_FREE_H264_MV_BUFFER\n");
result = vid_dec_free_h264_mv_buffers(client_ctx);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_GET_MV_BUFFER_SIZE:
{
struct vdec_mv_buff_size mv_buff;
DBG("VDEC_IOCTL_GET_MV_BUFFER_SIZE\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&mv_buff, vdec_msg.out,
sizeof(mv_buff)))
return -EFAULT;
result = vid_dec_get_h264_mv_buffer_size(client_ctx, &mv_buff);
if (result) {
DBG(" Returning W: %d, H: %d, S: %d, A: %d",
mv_buff.width, mv_buff.height,
mv_buff.size, mv_buff.alignment);
if (copy_to_user(vdec_msg.out, &mv_buff,
sizeof(mv_buff)))
return -EFAULT;
} else
return -EIO;
break;
}
case VDEC_IOCTL_SET_IDR_ONLY_DECODING:
{
result = vid_dec_set_idr_only_decoding(client_ctx);
if (!result)
return -EIO;
break;
}
case VDEC_IOCTL_SET_CONT_ON_RECONFIG:
{
result = vid_dec_set_cont_on_reconfig(client_ctx);
if (!result)
return -EIO;
break;
}
default:
ERR("%s(): Unsupported ioctl\n", __func__);
return -ENOTTY;
break;
}
return 0;
}
static u32 vid_dec_close_client(struct video_client_ctx *client_ctx)
{
struct vid_dec_msg *vdec_msg;
u32 vcd_status;
DBG("msm_vidc_dec: Inside %s()", __func__);
if (!client_ctx || (!client_ctx->vcd_handle)) {
ERR("\n Invalid client_ctx");
return false;
}
mutex_lock(&vid_dec_device_p->lock);
if (!client_ctx->stop_called) {
client_ctx->stop_called = true;
client_ctx->stop_sync_cb = true;
vcd_status = vcd_stop(client_ctx->vcd_handle);
DBG("\n Stuck at the stop call");
if (!vcd_status)
wait_for_completion(&client_ctx->event);
DBG("\n Came out of wait event");
}
mutex_lock(&client_ctx->msg_queue_lock);
while (!list_empty(&client_ctx->msg_queue)) {
DBG("%s(): Delete remaining entries\n", __func__);
vdec_msg = list_first_entry(&client_ctx->msg_queue,
struct vid_dec_msg, list);
if (vdec_msg) {
list_del(&vdec_msg->list);
kfree(vdec_msg);
}
}
mutex_unlock(&client_ctx->msg_queue_lock);
vcd_status = vcd_close(client_ctx->vcd_handle);
client_ctx->user_ion_client = NULL;
memset((void *)client_ctx, 0, sizeof(struct video_client_ctx));
vid_dec_device_p->num_clients--;
mutex_unlock(&vid_dec_device_p->lock);
return true;
}
int vid_dec_open_client(struct video_client_ctx **vid_clnt_ctx, int flags)
{
int rc = 0;
s32 client_index;
struct video_client_ctx *client_ctx = NULL;
u8 client_count;
if (!vid_clnt_ctx) {
ERR("Invalid input\n");
return -EINVAL;
}
*vid_clnt_ctx = NULL;
client_count = vcd_get_num_of_clients();
if (client_count == VIDC_MAX_NUM_CLIENTS) {
ERR("ERROR : vid_dec_open() max number of clients"
"limit reached\n");
rc = -ENOMEM;
goto client_failure;
}
DBG(" Virtual Address of ioremap is %p\n", vid_dec_device_p->virt_base);
if (!vid_dec_device_p->num_clients) {
if (!vidc_load_firmware()) {
rc = -ENOMEM;
goto client_failure;
}
}
client_index = vid_dec_get_empty_client_index();
if (client_index < 0) {
ERR("%s() : No free clients client_index == -1\n", __func__);
rc = -ENOMEM;
goto client_failure;
}
client_ctx = &vid_dec_device_p->vdec_clients[client_index];
vid_dec_device_p->num_clients++;
init_completion(&client_ctx->event);
mutex_init(&client_ctx->msg_queue_lock);
mutex_init(&client_ctx->enrty_queue_lock);
INIT_LIST_HEAD(&client_ctx->msg_queue);
init_waitqueue_head(&client_ctx->msg_wait);
client_ctx->stop_msg = 0;
client_ctx->stop_called = false;
client_ctx->stop_sync_cb = false;
client_ctx->dmx_disable = 0;
if (vcd_get_ion_status()) {
client_ctx->user_ion_client = vcd_get_ion_client();
if (!client_ctx->user_ion_client) {
ERR("vcd_open ion client get failed");
rc = -ENOMEM;
goto client_failure;
}
}
rc = vcd_open(vid_dec_device_p->device_handle, true,
vid_dec_vcd_cb, client_ctx, flags);
if (!rc) {
wait_for_completion(&client_ctx->event);
if (client_ctx->event_status) {
ERR("callback for vcd_open returned error: %u",
client_ctx->event_status);
rc = -ENODEV;
goto client_failure;
}
} else {
ERR("vcd_open returned error: %u", rc);
goto client_failure;
}
client_ctx->seq_header_set = false;
*vid_clnt_ctx = client_ctx;
client_failure:
return rc;
}
static int vid_dec_open_secure(struct inode *inode, struct file *file)
{
int rc = 0;
struct video_client_ctx *client_ctx;
mutex_lock(&vid_dec_device_p->lock);
rc = vid_dec_open_client(&client_ctx, VCD_CP_SESSION);
if (rc)
goto error;
if (!client_ctx) {
rc = -ENOMEM;
goto error;
}
file->private_data = client_ctx;
if (res_trk_open_secure_session()) {
ERR("Secure session operation failure\n");
rc = -EACCES;
goto error;
}
mutex_unlock(&vid_dec_device_p->lock);
return 0;
error:
mutex_unlock(&vid_dec_device_p->lock);
return rc;
}
static int vid_dec_open(struct inode *inode, struct file *file)
{
int rc = 0;
struct video_client_ctx *client_ctx;
INFO("msm_vidc_dec: Inside %s()", __func__);
mutex_lock(&vid_dec_device_p->lock);
rc = vid_dec_open_client(&client_ctx, 0);
if (rc) {
mutex_unlock(&vid_dec_device_p->lock);
return rc;
}
if (!client_ctx) {
mutex_unlock(&vid_dec_device_p->lock);
return -ENOMEM;
}
file->private_data = client_ctx;
mutex_unlock(&vid_dec_device_p->lock);
return rc;
}
static int vid_dec_release_secure(struct inode *inode, struct file *file)
{
struct video_client_ctx *client_ctx = file->private_data;
INFO("msm_vidc_dec: Inside %s()", __func__);
vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT);
vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_INPUT);
vid_dec_close_client(client_ctx);
vidc_release_firmware();
#ifndef USE_RES_TRACKER
vidc_disable_clk();
#endif
INFO("msm_vidc_dec: Return from %s()", __func__);
return 0;
}
static int vid_dec_release(struct inode *inode, struct file *file)
{
struct video_client_ctx *client_ctx = file->private_data;
INFO("msm_vidc_dec: Inside %s()", __func__);
vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT);
vidc_cleanup_addr_table(client_ctx, BUFFER_TYPE_INPUT);
vid_dec_close_client(client_ctx);
vidc_release_firmware();
#ifndef USE_RES_TRACKER
vidc_disable_clk();
#endif
INFO("msm_vidc_dec: Return from %s()", __func__);
return 0;
}
static const struct file_operations vid_dec_fops[2] = {
{
.owner = THIS_MODULE,
.open = vid_dec_open,
.release = vid_dec_release,
.unlocked_ioctl = vid_dec_ioctl,
},
{
.owner = THIS_MODULE,
.open = vid_dec_open_secure,
.release = vid_dec_release_secure,
.unlocked_ioctl = vid_dec_ioctl,
},
};
void vid_dec_interrupt_deregister(void)
{
}
void vid_dec_interrupt_register(void *device_name)
{
}
void vid_dec_interrupt_clear(void)
{
}
void *vid_dec_map_dev_base_addr(void *device_name)
{
return vid_dec_device_p->virt_base;
}
static int vid_dec_vcd_init(void)
{
int rc;
struct vcd_init_config vcd_init_config;
u32 i;
/* init_timer(&hw_timer); */
DBG("msm_vidc_dec: Inside %s()", __func__);
vid_dec_device_p->num_clients = 0;
for (i = 0; i < VIDC_MAX_NUM_CLIENTS; i++) {
memset((void *)&vid_dec_device_p->vdec_clients[i], 0,
sizeof(vid_dec_device_p->vdec_clients[i]));
}
mutex_init(&vid_dec_device_p->lock);
vid_dec_device_p->virt_base = vidc_get_ioaddr();
DBG("%s() : base address for VIDC core %u\n", __func__, \
(int)vid_dec_device_p->virt_base);
if (!vid_dec_device_p->virt_base) {
ERR("%s() : ioremap failed\n", __func__);
return -ENOMEM;
}
vcd_init_config.device_name = "VIDC";
vcd_init_config.map_dev_base_addr = vid_dec_map_dev_base_addr;
vcd_init_config.interrupt_clr = vid_dec_interrupt_clear;
vcd_init_config.register_isr = vid_dec_interrupt_register;
vcd_init_config.deregister_isr = vid_dec_interrupt_deregister;
vcd_init_config.timer_create = vidc_timer_create;
vcd_init_config.timer_release = vidc_timer_release;
vcd_init_config.timer_start = vidc_timer_start;
vcd_init_config.timer_stop = vidc_timer_stop;
rc = vcd_init(&vcd_init_config, &vid_dec_device_p->device_handle);
if (rc) {
ERR("%s() : vcd_init failed\n", __func__);
return -ENODEV;
}
return 0;
}
static int __init vid_dec_init(void)
{
int rc = 0, i = 0, j = 0;
struct device *class_devp;
DBG("msm_vidc_dec: Inside %s()", __func__);
vid_dec_device_p = kzalloc(sizeof(struct vid_dec_dev), GFP_KERNEL);
if (!vid_dec_device_p) {
ERR("%s Unable to allocate memory for vid_dec_dev\n",
__func__);
return -ENOMEM;
}
rc = alloc_chrdev_region(&vid_dec_dev_num, 0, NUM_OF_DRIVER_NODES,
VID_DEC_NAME);
if (rc < 0) {
ERR("%s: alloc_chrdev_region Failed rc = %d\n",
__func__, rc);
goto error_vid_dec_alloc_chrdev_region;
}
vid_dec_class = class_create(THIS_MODULE, VID_DEC_NAME);
if (IS_ERR(vid_dec_class)) {
rc = PTR_ERR(vid_dec_class);
ERR("%s: couldn't create vid_dec_class rc = %d\n",
__func__, rc);
goto error_vid_dec_class_create;
}
for (i = 0; i < NUM_OF_DRIVER_NODES; i++) {
class_devp = device_create(vid_dec_class, NULL,
(vid_dec_dev_num + i),
NULL, VID_DEC_NAME "%s",
node_name[i]);
if (IS_ERR(class_devp)) {
rc = PTR_ERR(class_devp);
ERR("%s: class device_create failed %d\n",
__func__, rc);
if (!i)
goto error_vid_dec_class_device_create;
else
goto error_vid_dec_cdev_add;
}
vid_dec_device_p->device[i] = class_devp;
cdev_init(&vid_dec_device_p->cdev[i], &vid_dec_fops[i]);
vid_dec_device_p->cdev[i].owner = THIS_MODULE;
rc = cdev_add(&(vid_dec_device_p->cdev[i]),
(vid_dec_dev_num+i), 1);
if (rc < 0) {
ERR("%s: cdev_add failed %d\n", __func__, rc);
goto error_vid_dec_cdev_add;
}
}
vid_dec_vcd_init();
return 0;
error_vid_dec_cdev_add:
for (j = i-1; j >= 0; j--)
cdev_del(&(vid_dec_device_p->cdev[j]));
device_destroy(vid_dec_class, vid_dec_dev_num);
error_vid_dec_class_device_create:
class_destroy(vid_dec_class);
error_vid_dec_class_create:
unregister_chrdev_region(vid_dec_dev_num, NUM_OF_DRIVER_NODES);
error_vid_dec_alloc_chrdev_region:
kfree(vid_dec_device_p);
return rc;
}
static void __exit vid_dec_exit(void)
{
int i = 0;
INFO("msm_vidc_dec: Inside %s()", __func__);
for (i = 0; i < NUM_OF_DRIVER_NODES; i++)
cdev_del(&(vid_dec_device_p->cdev[i]));
device_destroy(vid_dec_class, vid_dec_dev_num);
class_destroy(vid_dec_class);
unregister_chrdev_region(vid_dec_dev_num, NUM_OF_DRIVER_NODES);
kfree(vid_dec_device_p);
DBG("msm_vidc_dec: Return from %s()", __func__);
}
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Video decoder driver");
MODULE_VERSION("1.0");
module_init(vid_dec_init);
module_exit(vid_dec_exit);