/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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" 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 (!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; void __user *arg = (void __user *)u_arg; 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);