blob: 071f6989a2b7e102e53c3628eaebde6ce1aa6a89 [file] [log] [blame]
/* Copyright (c) 2012-2014, The 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/jiffies.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <asm/div64.h>
#include <mach/subsystem_restart.h>
#include "msm_vidc_common.h"
#include "vidc_hfi_api.h"
#include "msm_smem.h"
#include "msm_vidc_debug.h"
#define IS_ALREADY_IN_STATE(__p, __d) ({\
int __rc = (__p >= __d);\
__rc; \
})
#define V4L2_EVENT_SEQ_CHANGED_SUFFICIENT \
V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT
#define V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT \
V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT
#define V4L2_EVENT_RELEASE_BUFFER_REFERENCE \
V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE
#define NUM_MBS_PER_SEC(__height, __width, __fps) ({\
(__height >> 4) * (__width >> 4) * __fps; \
})
#define VIDC_BUS_LOAD(__height, __width, __fps, __br) ({\
__height * __width * __fps; \
})
#define GET_NUM_MBS(__h, __w) ({\
u32 __mbs = (__h >> 4) * (__w >> 4);\
__mbs;\
})
static bool is_turbo_requested(struct msm_vidc_core *core,
enum session_type type)
{
struct msm_vidc_inst *inst = NULL;
bool wants_turbo = false;
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
mutex_lock(&inst->lock);
if (inst->session_type == type &&
inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_STOP_DONE) {
wants_turbo = inst->flags & VIDC_TURBO;
}
mutex_unlock(&inst->lock);
if (wants_turbo)
break;
}
mutex_unlock(&core->lock);
return wants_turbo;
}
static bool is_thumbnail_session(struct msm_vidc_inst *inst)
{
if (inst->session_type == MSM_VIDC_DECODER) {
int rc = 0;
struct v4l2_control ctrl = {
.id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE
};
rc = v4l2_g_ctrl(&inst->ctrl_handler, &ctrl);
if (!rc && ctrl.value)
return true;
}
return false;
}
enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst)
{
if (inst->session_type == MSM_VIDC_DECODER) {
int rc = 0;
struct v4l2_control ctrl = {
.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE
};
rc = v4l2_g_ctrl(&inst->ctrl_handler, &ctrl);
if (!rc && ctrl.value)
return HAL_VIDEO_DECODER_SECONDARY;
}
return HAL_VIDEO_DECODER_PRIMARY;
}
static int msm_comm_get_mbs_per_sec(struct msm_vidc_inst *inst)
{
int height, width;
height = max(inst->prop.height[CAPTURE_PORT],
inst->prop.height[OUTPUT_PORT]);
width = max(inst->prop.width[CAPTURE_PORT],
inst->prop.width[OUTPUT_PORT]);
return NUM_MBS_PER_SEC(height, width, inst->prop.fps);
}
static int msm_comm_get_load(struct msm_vidc_core *core,
enum session_type type)
{
struct msm_vidc_inst *inst = NULL;
int num_mbs_per_sec = 0;
if (!core) {
dprintk(VIDC_ERR, "Invalid args: %p\n", core);
return -EINVAL;
}
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
mutex_lock(&inst->lock);
if (inst->session_type == type &&
inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_STOP_DONE) {
if (!is_thumbnail_session(inst))
num_mbs_per_sec +=
msm_comm_get_mbs_per_sec(inst);
}
mutex_unlock(&inst->lock);
}
mutex_unlock(&core->lock);
return num_mbs_per_sec;
}
static int msm_comm_scale_bus(struct msm_vidc_core *core,
enum session_type type, enum mem_type mtype)
{
int load;
int rc = 0;
struct hfi_device *hdev;
if (!core || type >= MSM_VIDC_MAX_DEVICES) {
dprintk(VIDC_ERR, "Invalid args: %p, %d\n", core, type);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device handle %p\n", hdev);
return -EINVAL;
}
if (is_turbo_requested(core, type))
load = core->resources.max_load;
else
load = msm_comm_get_load(core, type);
rc = call_hfi_op(hdev, scale_bus, hdev->hfi_device_data,
load, type, mtype);
if (rc)
dprintk(VIDC_ERR, "Failed to scale bus: %d\n", rc);
return rc;
}
static void msm_comm_unvote_buses(struct msm_vidc_core *core,
enum mem_type mtype)
{
int i;
struct hfi_device *hdev;
if (!core || !core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return;
}
hdev = core->device;
for (i = 0; i < MSM_VIDC_MAX_DEVICES; i++) {
if ((mtype & DDR_MEM) &&
call_hfi_op(hdev, unvote_bus, hdev->hfi_device_data,
i, DDR_MEM))
dprintk(VIDC_WARN,
"Failed to unvote for DDR accesses\n");
if ((mtype & OCMEM_MEM) &&
call_hfi_op(hdev, unvote_bus, hdev->hfi_device_data,
i, OCMEM_MEM))
dprintk(VIDC_WARN,
"Failed to unvote for OCMEM accesses\n");
}
}
struct msm_vidc_core *get_vidc_core(int core_id)
{
struct msm_vidc_core *core;
int found = 0;
if (core_id > MSM_VIDC_CORES_MAX) {
dprintk(VIDC_ERR, "Core id = %d is greater than max = %d\n",
core_id, MSM_VIDC_CORES_MAX);
return NULL;
}
mutex_lock(&vidc_driver->lock);
list_for_each_entry(core, &vidc_driver->cores, list) {
if (core && core->id == core_id) {
found = 1;
break;
}
}
mutex_unlock(&vidc_driver->lock);
if (found)
return core;
return NULL;
}
const struct msm_vidc_format *msm_comm_get_pixel_fmt_index(
const struct msm_vidc_format fmt[], int size, int index, int fmt_type)
{
int i, k = 0;
if (!fmt || index < 0) {
dprintk(VIDC_ERR, "Invalid inputs, fmt = %p, index = %d\n",
fmt, index);
return NULL;
}
for (i = 0; i < size; i++) {
if (fmt[i].type != fmt_type)
continue;
if (k == index)
break;
k++;
}
if (i == size) {
dprintk(VIDC_INFO, "Format not found\n");
return NULL;
}
return &fmt[i];
}
struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc(
struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type)
{
int i;
if (!fmt) {
dprintk(VIDC_ERR, "Invalid inputs, fmt = %p\n", fmt);
return NULL;
}
for (i = 0; i < size; i++) {
if (fmt[i].fourcc == fourcc)
break;
}
if (i == size) {
dprintk(VIDC_INFO, "Format not found\n");
return NULL;
}
return &fmt[i];
}
struct buf_queue *msm_comm_get_vb2q(
struct msm_vidc_inst *inst, enum v4l2_buf_type type)
{
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return &inst->bufq[CAPTURE_PORT];
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return &inst->bufq[OUTPUT_PORT];
return NULL;
}
static void handle_sys_init_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_core *core;
struct vidc_hal_sys_init_done *sys_init_msg;
int index = SYS_MSG_INDEX(cmd);
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for sys init\n");
return;
}
core = get_vidc_core(response->device_id);
if (!core) {
dprintk(VIDC_ERR, "Wrong device_id received\n");
return;
}
sys_init_msg = response->data;
if (!sys_init_msg) {
dprintk(VIDC_ERR, "sys_init_done message not proper\n");
return;
}
core->enc_codec_supported = sys_init_msg->enc_codec_supported;
core->dec_codec_supported = sys_init_msg->dec_codec_supported;
dprintk(VIDC_DBG, "supported_codecs: enc = 0x%x, dec = 0x%x\n",
core->enc_codec_supported, core->dec_codec_supported);
dprintk(VIDC_DBG, "ptr[%d] = %p\n", index, &(core->completions[index]));
complete(&(core->completions[index]));
}
static void handle_session_release_buf_done(enum command_response cmd,
void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
struct internal_buf *buf;
struct list_head *ptr, *next;
struct hal_buffer_info *buffer;
u32 address, buf_found = false;
if (!response || !response->data) {
dprintk(VIDC_ERR, "Invalid release_buf_done response\n");
return;
}
inst = (struct msm_vidc_inst *)response->session_id;
buffer = (struct hal_buffer_info *) response->data;
address = (u32) buffer->buffer_addr;
list_for_each_safe(ptr, next, &inst->internalbufs) {
buf = list_entry(ptr, struct internal_buf, list);
if (address == buf->handle->device_addr) {
dprintk(VIDC_DBG, "releasing scratch: 0x%x",
(u32) buf->handle->device_addr);
buf_found = true;
}
}
list_for_each_safe(ptr, next, &inst->persistbufs) {
buf = list_entry(ptr, struct internal_buf, list);
if (address == (u32) buf->handle->device_addr) {
dprintk(VIDC_DBG, "releasing persist: 0x%x",
(u32) buf->handle->device_addr);
buf_found = true;
}
}
if (!buf_found)
dprintk(VIDC_ERR, "invalid buffer received from firmware");
complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
}
static void handle_sys_release_res_done(
enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_core *core;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for sys init\n");
return;
}
core = get_vidc_core(response->device_id);
if (!core) {
dprintk(VIDC_ERR, "Wrong device_id received\n");
return;
}
complete(&core->completions[SYS_MSG_INDEX(cmd)]);
}
static void change_inst_state(struct msm_vidc_inst *inst,
enum instance_state state)
{
if (!inst) {
dprintk(VIDC_ERR, "Invalid parameter %s", __func__);
return;
}
mutex_lock(&inst->lock);
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_DBG,
"Inst: %p is in bad state can't change state",
inst);
goto exit;
}
dprintk(VIDC_DBG, "Moved inst: %p from state: %d to state: %d\n",
inst, inst->state, state);
inst->state = state;
exit:
mutex_unlock(&inst->lock);
}
static int signal_session_msg_receipt(enum command_response cmd,
struct msm_vidc_inst *inst)
{
if (!inst) {
dprintk(VIDC_ERR, "Invalid(%p) instance id\n", inst);
return -EINVAL;
}
complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
return 0;
}
static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
enum command_response cmd)
{
int rc = 0;
rc = wait_for_completion_timeout(
&inst->completions[SESSION_MSG_INDEX(cmd)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR,
"%s: Wait interrupted or timeout[%u]: %d\n",
__func__, (u32)inst->session, SESSION_MSG_INDEX(cmd));
msm_comm_recover_from_session_error(inst);
rc = -EIO;
} else {
rc = 0;
}
return rc;
}
static int wait_for_state(struct msm_vidc_inst *inst,
enum instance_state flipped_state,
enum instance_state desired_state,
enum command_response hal_cmd)
{
int rc = 0;
if (IS_ALREADY_IN_STATE(flipped_state, desired_state)) {
dprintk(VIDC_INFO, "inst: %p is already in state: %d\n",
inst, inst->state);
goto err_same_state;
}
dprintk(VIDC_DBG, "Waiting for hal_cmd: %d\n", hal_cmd);
rc = wait_for_sess_signal_receipt(inst, hal_cmd);
if (!rc)
change_inst_state(inst, desired_state);
err_same_state:
return rc;
}
void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type)
{
struct v4l2_event event = {.id = 0, .type = event_type};
v4l2_event_queue_fh(&inst->event_handler, &event);
wake_up(&inst->kernel_event_queue);
}
static void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
{
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
return;
}
mutex_lock(&inst->lock);
inst->session = NULL;
inst->state = MSM_VIDC_CORE_INVALID;
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
mutex_unlock(&inst->lock);
}
static void msm_comm_generate_max_clients_error(struct msm_vidc_inst *inst)
{
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return;
}
mutex_lock(&inst->sync_lock);
inst->session = NULL;
inst->state = MSM_VIDC_CORE_INVALID;
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_MAX_CLIENTS);
dprintk(VIDC_WARN,
"%s: Too many clients\n", __func__);
mutex_unlock(&inst->sync_lock);
}
static void handle_session_init_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst = NULL;
struct hfi_device *hdev;
if (response) {
struct vidc_hal_session_init_done *session_init_done =
(struct vidc_hal_session_init_done *)
response->data;
inst = (struct msm_vidc_inst *)response->session_id;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return;
}
hdev = inst->core->device;
if (!response->status && session_init_done) {
inst->capability.width = session_init_done->width;
inst->capability.height = session_init_done->height;
inst->capability.frame_rate =
session_init_done->frame_rate;
inst->capability.scale_x = session_init_done->scale_x;
inst->capability.scale_y = session_init_done->scale_y;
inst->capability.ltr_count =
session_init_done->ltr_count;
inst->capability.hier_p = session_init_done->hier_p;
inst->capability.pixelprocess_capabilities =
call_hfi_op(hdev, get_core_capabilities);
inst->capability.capability_set = true;
inst->capability.buffer_mode[CAPTURE_PORT] =
session_init_done->alloc_mode_out;
} else {
dprintk(VIDC_ERR,
"Session init response from FW : 0x%x",
response->status);
if (response->status == VIDC_ERR_MAX_CLIENTS)
msm_comm_generate_max_clients_error(inst);
else
msm_comm_generate_session_error(inst);
}
signal_session_msg_receipt(cmd, inst);
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for session init\n");
}
}
static void handle_event_change(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
struct v4l2_control control = {0};
struct msm_vidc_cb_event *event_notify;
int event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
int rc = 0;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
event_notify = (struct msm_vidc_cb_event *) response->data;
switch (event_notify->hal_event_type) {
case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES:
event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
control.id =
V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER;
rc = v4l2_g_ctrl(&inst->ctrl_handler, &control);
if (rc)
dprintk(VIDC_WARN,
"Failed to get Smooth streamng flag\n");
if (!rc && control.value == true)
event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
break;
case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
break;
case HAL_EVENT_RELEASE_BUFFER_REFERENCE:
{
struct v4l2_event buf_event = {0};
struct buffer_info *binfo = NULL;
u32 *ptr = NULL;
dprintk(VIDC_DBG,
"%s - inst: %p buffer: %p extra: %p\n",
__func__, inst,
event_notify->packet_buffer,
event_notify->exra_data_buffer);
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state ==
VIDC_CORE_INVALID) {
dprintk(VIDC_DBG,
"Event release buf ref received in invalid state - discard\n");
return;
}
/*
* Get the buffer_info entry for the
* device address.
*/
binfo = device_to_uvaddr(inst,
&inst->registered_bufs,
(u32)event_notify->packet_buffer);
if (!binfo) {
dprintk(VIDC_ERR,
"%s buffer not found in registered list\n",
__func__);
return;
}
/* Fill event data to be sent to client*/
buf_event.type =
V4L2_EVENT_RELEASE_BUFFER_REFERENCE;
ptr = (u32 *)buf_event.u.data;
ptr[0] = binfo->fd[0];
ptr[1] = binfo->buff_off[0];
dprintk(VIDC_DBG,
"RELEASE REFERENCE EVENT FROM F/W - fd = %d offset = %d\n",
ptr[0], ptr[1]);
mutex_lock(&inst->sync_lock);
/* Decrement buffer reference count*/
buf_ref_put(inst, binfo);
/*
* Release buffer and remove from list
* if reference goes to zero.
*/
if (unmap_and_deregister_buf(inst, binfo))
dprintk(VIDC_ERR,
"%s: buffer unmap failed\n", __func__);
mutex_unlock(&inst->sync_lock);
/*send event to client*/
v4l2_event_queue_fh(&inst->event_handler,
&buf_event);
wake_up(&inst->kernel_event_queue);
return;
}
default:
break;
}
if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) {
dprintk(VIDC_DBG,
"V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT\n");
inst->reconfig_height = event_notify->height;
inst->reconfig_width = event_notify->width;
inst->in_reconfig = true;
} else {
dprintk(VIDC_DBG,
"V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n");
if (msm_comm_get_stream_output_mode(inst) !=
HAL_VIDEO_DECODER_SECONDARY) {
dprintk(VIDC_DBG,
"event_notify->height = %d event_notify->width = %d\n",
event_notify->height,
event_notify->width);
inst->prop.height[CAPTURE_PORT] =
event_notify->height;
inst->prop.width[CAPTURE_PORT] =
event_notify->width;
inst->prop.height[OUTPUT_PORT] =
event_notify->height;
inst->prop.width[OUTPUT_PORT] =
event_notify->width;
}
}
rc = msm_vidc_check_session_supported(inst);
if (!rc) {
msm_vidc_queue_v4l2_event(inst, event);
}
return;
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for event_change\n");
}
}
static void handle_session_prop_info(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
int i;
if (!response || !response->data) {
dprintk(VIDC_ERR,
"Failed to get valid response for prop info\n");
return;
}
inst = (struct msm_vidc_inst *)response->session_id;
mutex_lock(&inst->lock);
memcpy(&inst->buff_req, response->data,
sizeof(struct buffer_requirements));
mutex_unlock(&inst->lock);
for (i = 0; i < HAL_BUFFER_MAX; i++) {
dprintk(VIDC_DBG,
"buffer type: %d, count : %d, size: %d\n",
inst->buff_req.buffer[i].buffer_type,
inst->buff_req.buffer[i].buffer_count_actual,
inst->buff_req.buffer[i].buffer_size);
}
dprintk(VIDC_PROF, "Input buffers: %d, Output buffers: %d\n",
inst->buff_req.buffer[0].buffer_count_actual,
inst->buff_req.buffer[1].buffer_count_actual);
signal_session_msg_receipt(cmd, inst);
}
static void handle_load_resource_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
if (response->status) {
dprintk(VIDC_ERR,
"Load resource response from FW : 0x%x",
response->status);
msm_comm_generate_session_error(inst);
}
}
else
dprintk(VIDC_ERR,
"Failed to get valid response for load resource\n");
}
static void handle_start_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for start\n");
}
}
static void handle_stop_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for stop\n");
}
}
static void handle_release_res_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for release resource\n");
}
}
void validate_output_buffers(struct msm_vidc_inst *inst)
{
struct internal_buf *binfo;
u32 buffers_owned_by_driver = 0;
struct hal_buffer_requirements *output_buf;
output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
if (!output_buf) {
dprintk(VIDC_DBG,
"This output buffer not required, buffer_type: %x\n",
HAL_BUFFER_OUTPUT);
return;
}
list_for_each_entry(binfo, &inst->outputbufs, list) {
if (!binfo) {
dprintk(VIDC_ERR, "Invalid parameter\n");
return;
}
if (binfo->buffer_ownership != DRIVER) {
dprintk(VIDC_DBG,
"This buffer is with FW 0x%lx\n",
binfo->handle->device_addr);
return;
}
buffers_owned_by_driver++;
}
if (buffers_owned_by_driver != output_buf->buffer_count_actual)
dprintk(VIDC_ERR,
"OUTPUT Buffer count mismatch %d of %d",
buffers_owned_by_driver,
output_buf->buffer_count_actual);
return;
}
int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst)
{
struct internal_buf *binfo;
struct hfi_device *hdev;
struct msm_smem *handle;
struct vidc_frame_data frame_data = {0};
struct hal_buffer_requirements *output_buf;
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
if (!output_buf) {
dprintk(VIDC_DBG,
"This output buffer not required, buffer_type: %x\n",
HAL_BUFFER_OUTPUT);
return 0;
}
dprintk(VIDC_DBG,
"output: num = %d, size = %d\n",
output_buf->buffer_count_actual,
output_buf->buffer_size);
list_for_each_entry(binfo, &inst->outputbufs, list) {
if (binfo->buffer_ownership != DRIVER)
continue;
handle = binfo->handle;
frame_data.alloc_len = output_buf->buffer_size;
frame_data.filled_len = 0;
frame_data.offset = 0;
frame_data.device_addr = handle->device_addr;
frame_data.flags = 0;
frame_data.extradata_addr = handle->device_addr +
output_buf->buffer_size;
frame_data.buffer_type = HAL_BUFFER_OUTPUT;
rc = call_hfi_op(hdev, session_ftb,
(void *) inst->session, &frame_data);
binfo->buffer_ownership = FIRMWARE;
}
return 0;
}
static void handle_session_flush(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
int rc;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY) {
mutex_lock(&inst->lock);
validate_output_buffers(inst);
if (!inst->in_reconfig) {
rc = msm_comm_queue_output_buffers(inst);
if (rc) {
dprintk(VIDC_ERR,
"Failed to queue output buffers: %d\n",
rc);
}
}
mutex_unlock(&inst->lock);
}
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
} else {
dprintk(VIDC_ERR, "Failed to get valid response for flush\n");
}
}
static void handle_session_error(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst = NULL;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
if (inst) {
dprintk(VIDC_WARN,
"Session error receivd for session %p\n", inst);
mutex_lock(&inst->sync_lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->sync_lock);
msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_SYS_ERROR);
}
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for session error\n");
}
}
struct sys_err_handler_data {
struct msm_vidc_core *core;
struct delayed_work work;
};
void hw_sys_error_handler(struct work_struct *work)
{
struct msm_vidc_core *core = NULL;
struct hfi_device *hdev = NULL;
struct sys_err_handler_data *handler = NULL;
int rc = 0;
handler = container_of(work, struct sys_err_handler_data, work.work);
if (!handler || !handler->core || !handler->core->device) {
dprintk(VIDC_ERR, "%s - invalid work or core handle\n",
__func__);
goto exit;
}
core = handler->core;
hdev = core->device;
mutex_lock(&core->lock);
/*
* Restart the firmware to bring out of bad state.
*/
if ((core->state == VIDC_CORE_INVALID) &&
hdev->resurrect_fw) {
rc = call_hfi_op(hdev, resurrect_fw,
hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR,
"%s - resurrect_fw failed: %d\n",
__func__, rc);
}
core->state = VIDC_CORE_LOADED;
} else {
dprintk(VIDC_DBG,
"fw unloaded after sys error, no need to resurrect\n");
}
mutex_unlock(&core->lock);
exit:
/* free sys error handler, allocated in handle_sys_err */
kfree(handler);
}
static void handle_sys_error(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_core *core = NULL;
struct sys_err_handler_data *handler = NULL;
struct hfi_device *hdev = NULL;
struct msm_vidc_inst *inst = NULL;
int rc = 0;
subsystem_crashed("venus");
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for sys error\n");
return;
}
core = get_vidc_core(response->device_id);
if (!core) {
dprintk(VIDC_ERR,
"Got SYS_ERR but unable to identify core\n");
return;
}
dprintk(VIDC_WARN, "SYS_ERROR %d received for core %p\n", cmd, core);
mutex_lock(&core->lock);
core->state = VIDC_CORE_INVALID;
/*
* 1. Delete each instance session from hfi list
* 2. Notify all clients about hardware error.
*/
list_for_each_entry(inst, &core->instances,
list) {
mutex_lock(&inst->lock);
inst->state = MSM_VIDC_CORE_INVALID;
if (inst->core)
hdev = inst->core->device;
if (hdev && inst->session) {
dprintk(VIDC_DBG,
"cleaning up inst: 0x%p\n", inst);
rc = call_hfi_op(hdev, session_clean,
(void *) inst->session);
if (rc)
dprintk(VIDC_ERR,
"Sess clean failed :%p\n",
inst);
}
inst->session = NULL;
mutex_unlock(&inst->lock);
msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_SYS_ERROR);
}
mutex_unlock(&core->lock);
handler = kzalloc(sizeof(*handler), GFP_KERNEL);
if (!handler) {
dprintk(VIDC_ERR,
"%s - failed to allocate sys error handler\n",
__func__);
return;
}
handler->core = core;
INIT_DELAYED_WORK(&handler->work, hw_sys_error_handler);
/*
* Sleep for 5 sec to ensure venus has completed any
* pending cache operations. Without this sleep, we see
* device reset when firmware is unloaded after a sys
* error.
*/
schedule_delayed_work(&handler->work, msecs_to_jiffies(5000));
}
static void handle_session_close(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
struct hfi_device *hdev = NULL;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return;
}
hdev = inst->core->device;
mutex_lock(&inst->lock);
if (inst->session) {
dprintk(VIDC_DBG, "cleaning up inst: 0x%p", inst);
call_hfi_op(hdev, session_clean,
(void *) inst->session);
}
inst->session = NULL;
mutex_unlock(&inst->lock);
signal_session_msg_receipt(cmd, inst);
show_stats(inst);
} else {
dprintk(VIDC_ERR,
"Failed to get valid response for session close\n");
}
}
static struct vb2_buffer *get_vb_from_device_addr(struct buf_queue *bufq,
u32 dev_addr)
{
struct vb2_buffer *vb = NULL;
struct vb2_queue *q = NULL;
int found = 0;
if (!bufq) {
dprintk(VIDC_ERR, "Invalid parameter\n");
return NULL;
}
q = &bufq->vb2_bufq;
mutex_lock(&bufq->lock);
list_for_each_entry(vb, &q->queued_list, queued_entry) {
if (vb->v4l2_planes[0].m.userptr == dev_addr) {
found = 1;
break;
}
}
mutex_unlock(&bufq->lock);
if (!found) {
dprintk(VIDC_ERR,
"Failed to find the buffer in queued list: 0x%x, %d\n",
dev_addr, q->type);
vb = NULL;
}
return vb;
}
static void handle_ebd(enum command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
struct vb2_buffer *vb;
struct msm_vidc_inst *inst;
struct vidc_hal_ebd *empty_buf_done;
if (!response) {
dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
return;
}
vb = response->clnt_data;
inst = (struct msm_vidc_inst *)response->session_id;
if (!inst) {
dprintk(VIDC_ERR, "%s Invalid response from vidc_hal\n",
__func__);
return;
}
if (vb) {
vb->v4l2_planes[0].bytesused = response->input_done.filled_len;
vb->v4l2_planes[0].data_offset = response->input_done.offset;
if (vb->v4l2_planes[0].data_offset > vb->v4l2_planes[0].length)
dprintk(VIDC_INFO, "data_offset overflow length\n");
if (vb->v4l2_planes[0].bytesused > vb->v4l2_planes[0].length)
dprintk(VIDC_INFO, "bytesused overflow length\n");
if ((u8 *)vb->v4l2_planes[0].m.userptr !=
response->input_done.packet_buffer)
dprintk(VIDC_INFO, "Unexpected buffer address\n");
vb->v4l2_buf.flags = 0;
empty_buf_done = (struct vidc_hal_ebd *)&response->input_done;
if (empty_buf_done) {
if (empty_buf_done->status == VIDC_ERR_NOT_SUPPORTED) {
dprintk(VIDC_INFO,
"Failed : Unsupported input stream\n");
vb->v4l2_buf.flags |=
V4L2_QCOM_BUF_INPUT_UNSUPPORTED;
}
if (empty_buf_done->status == VIDC_ERR_BITSTREAM_ERR) {
dprintk(VIDC_INFO,
"Failed : Corrupted input stream\n");
vb->v4l2_buf.flags |=
V4L2_QCOM_BUF_DATA_CORRUPT;
}
}
dprintk(VIDC_DBG,
"Got ebd from hal: device_addr: 0x%x, alloc: %d, status: 0x%x, pic_type: 0x%x, flags: 0x%x\n",
(u32)empty_buf_done->packet_buffer,
empty_buf_done->alloc_len, empty_buf_done->status,
empty_buf_done->picture_type, empty_buf_done->flags);
mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
wake_up(&inst->kernel_event_queue);
msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
}
}
int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo)
{
int cnt = 0;
if (!inst || !binfo)
return -EINVAL;
mutex_lock(&inst->lock);
atomic_inc(&binfo->ref_count);
cnt = atomic_read(&binfo->ref_count);
if (cnt > 2) {
dprintk(VIDC_DBG, "%s: invalid ref_cnt: %d\n", __func__, cnt);
cnt = -EINVAL;
}
dprintk(VIDC_DBG, "REF_GET[%d] fd[0] = %d\n", cnt, binfo->fd[0]);
mutex_unlock(&inst->lock);
return cnt;
}
int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo)
{
int rc = 0;
int cnt;
bool release_buf = false;
bool qbuf_again = false;
if (!inst || !binfo)
return -EINVAL;
mutex_lock(&inst->lock);
atomic_dec(&binfo->ref_count);
cnt = atomic_read(&binfo->ref_count);
dprintk(VIDC_DBG, "REF_PUT[%d] fd[0] = %d\n", cnt, binfo->fd[0]);
if (cnt == 0)
release_buf = true;
else if (cnt == 1)
qbuf_again = true;
else {
dprintk(VIDC_DBG, "%s: invalid ref_cnt: %d\n", __func__, cnt);
cnt = -EINVAL;
}
mutex_unlock(&inst->lock);
if (cnt < 0)
return cnt;
rc = output_buffer_cache_invalidate(inst, binfo);
if (rc)
return rc;
if (release_buf) {
/*
* We can not delete binfo here as we need to set the user
* virtual address saved in binfo->uvaddr to the dequeued v4l2
* buffer.
*
* We will set the pending_deletion flag to true here and delete
* binfo from registered list in dqbuf after setting the uvaddr.
*/
dprintk(VIDC_DBG, "fd[0] = %d -> pending_deletion = true\n",
binfo->fd[0]);
binfo->pending_deletion = true;
} else if (qbuf_again) {
rc = qbuf_dynamic_buf(inst, binfo);
if (!rc)
return rc;
}
return cnt;
}
static void handle_dynamic_buffer(struct msm_vidc_inst *inst,
u32 device_addr, u32 flags)
{
struct buffer_info *binfo = NULL;
/*
* Update reference count and release OR queue back the buffer,
* only when firmware is not holding a reference.
*/
if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) {
binfo = device_to_uvaddr(inst, &inst->registered_bufs,
device_addr);
if (!binfo) {
dprintk(VIDC_ERR,
"%s buffer not found in registered list\n",
__func__);
return;
}
if (flags & HAL_BUFFERFLAG_READONLY) {
dprintk(VIDC_DBG,
"_F_B_D_ fd[0] = %d -> Reference with f/w, addr: 0x%x",
binfo->fd[0], device_addr);
} else {
dprintk(VIDC_DBG,
"_F_B_D_ fd[0] = %d -> FBD_ref_released, addr: 0x%x\n",
binfo->fd[0], device_addr);
buf_ref_put(inst, binfo);
}
}
}
static int handle_multi_stream_buffers(struct msm_vidc_inst *inst,
u32 dev_addr)
{
struct internal_buf *binfo;
struct msm_smem *handle;
bool found = false;
mutex_lock(&inst->lock);
list_for_each_entry(binfo, &inst->outputbufs, list) {
if (!binfo) {
dprintk(VIDC_ERR, "Invalid parameter\n");
break;
}
handle = binfo->handle;
if (handle && dev_addr == handle->device_addr) {
if (binfo->buffer_ownership == DRIVER) {
dprintk(VIDC_ERR,
"FW returned same buffer : 0x%x\n",
dev_addr);
break;
}
binfo->buffer_ownership = DRIVER;
found = true;
break;
}
}
if (!found)
dprintk(VIDC_ERR,
"Failed to find output buffer in queued list: 0x%x\n",
dev_addr);
mutex_unlock(&inst->lock);
return 0;
}
enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst)
{
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY)
return HAL_BUFFER_OUTPUT2;
else
return HAL_BUFFER_OUTPUT;
}
static void handle_fbd(enum command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
struct msm_vidc_inst *inst;
struct vb2_buffer *vb = NULL;
struct vidc_hal_fbd *fill_buf_done;
enum hal_buffer buffer_type;
int64_t time_usec = 0;
int extra_idx = 0;
if (!response) {
dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
return;
}
inst = (struct msm_vidc_inst *)response->session_id;
fill_buf_done = (struct vidc_hal_fbd *)&response->output_done;
buffer_type = msm_comm_get_hal_output_buffer(inst);
if (fill_buf_done->buffer_type == buffer_type)
vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT],
(u32)fill_buf_done->packet_buffer1);
else {
if (handle_multi_stream_buffers(inst,
(u32)fill_buf_done->packet_buffer1))
dprintk(VIDC_ERR,
"Failed : Output buffer not found 0x%x\n",
(u32)fill_buf_done->packet_buffer1);
return;
}
if (vb) {
vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
vb->v4l2_planes[0].data_offset = fill_buf_done->offset1;
vb->v4l2_planes[0].reserved[2] = fill_buf_done->start_x_coord;
vb->v4l2_planes[0].reserved[3] = fill_buf_done->start_y_coord;
vb->v4l2_planes[0].reserved[4] = fill_buf_done->frame_width;
vb->v4l2_planes[0].reserved[5] = fill_buf_done->frame_height;
if (vb->v4l2_planes[0].data_offset > vb->v4l2_planes[0].length)
dprintk(VIDC_INFO,
"fbd:Overflow data_offset = %d; length = %d\n",
vb->v4l2_planes[0].data_offset,
vb->v4l2_planes[0].length);
if (vb->v4l2_planes[0].bytesused > vb->v4l2_planes[0].length)
dprintk(VIDC_INFO,
"fbd:Overflow bytesused = %d; length = %d\n",
vb->v4l2_planes[0].bytesused,
vb->v4l2_planes[0].length);
if (!(fill_buf_done->flags1 &
HAL_BUFFERFLAG_TIMESTAMPINVALID) &&
fill_buf_done->filled_len1) {
time_usec = fill_buf_done->timestamp_hi;
time_usec = (time_usec << 32) |
fill_buf_done->timestamp_lo;
vb->v4l2_buf.timestamp =
ns_to_timeval(time_usec * NSEC_PER_USEC);
}
vb->v4l2_buf.flags = 0;
extra_idx =
EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
vb->v4l2_planes[extra_idx].m.userptr =
(unsigned long)fill_buf_done->extra_data_buffer;
vb->v4l2_planes[extra_idx].bytesused =
vb->v4l2_planes[extra_idx].length;
vb->v4l2_planes[extra_idx].data_offset = 0;
}
handle_dynamic_buffer(inst, (u32)fill_buf_done->packet_buffer1,
fill_buf_done->flags1);
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_READONLY)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_READONLY;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS)
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG)
vb->v4l2_buf.flags &= ~V4L2_QCOM_BUF_FLAG_CODECCONFIG;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOSEQ)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_EOSEQ;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DECODEONLY)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_DECODEONLY;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DATA_CORRUPT;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DROP_FRAME)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DROP_FRAME;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_MBAFF)
vb->v4l2_buf.flags |= V4L2_MSM_BUF_FLAG_MBAFF;
switch (fill_buf_done->picture_type) {
case HAL_PICTURE_IDR:
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
break;
case HAL_PICTURE_I:
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
break;
case HAL_PICTURE_P:
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
break;
case HAL_PICTURE_B:
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME;
break;
case HAL_FRAME_NOTCODED:
case HAL_UNUSED_PICT:
/* Do we need to care about these? */
case HAL_FRAME_YUV:
break;
default:
break;
}
inst->count.fbd++;
if (fill_buf_done->filled_len1)
msm_vidc_debugfs_update(inst,
MSM_VIDC_DEBUGFS_EVENT_FBD);
dprintk(VIDC_DBG,
"Got fbd from hal: device_addr: 0x%x, alloc: %d, filled: %d, offset: %d, ts: %lld, flags: 0x%x, crop: %d %d %d %d, pic_type: 0x%x\n",
(u32)fill_buf_done->packet_buffer1, fill_buf_done->alloc_len1,
fill_buf_done->filled_len1, fill_buf_done->offset1, time_usec,
fill_buf_done->flags1, fill_buf_done->start_x_coord,
fill_buf_done->start_y_coord, fill_buf_done->frame_width,
fill_buf_done->frame_height, fill_buf_done->picture_type);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
dprintk(VIDC_DBG,
"extradata: userptr = %p; bytesused = %d; length = %d\n",
(u8 *)vb->v4l2_planes[extra_idx].m.userptr,
vb->v4l2_planes[extra_idx].bytesused,
vb->v4l2_planes[extra_idx].length);
}
mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
wake_up(&inst->kernel_event_queue);
} else {
/*
* FIXME:
* Special handling for EOS case: if we sent a 0 length input
* buf with EOS set, Venus doesn't return a valid output buffer.
* So pick up a random buffer that's with us, and send it to
* v4l2 client with EOS flag set.
*
* This would normally be OK unless client decides to send
* frames even after EOS.
*
* This should be fixed in upcoming versions of firmware
*/
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS
&& fill_buf_done->filled_len1 == 0) {
struct buf_queue *q = &inst->bufq[CAPTURE_PORT];
if (!list_empty(&q->vb2_bufq.queued_list)) {
vb = list_first_entry(&q->vb2_bufq.queued_list,
struct vb2_buffer, queued_entry);
vb->v4l2_planes[0].bytesused = 0;
vb->v4l2_planes[0].data_offset = 0;
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
mutex_lock(&q->lock);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&q->lock);
}
}
}
}
static void handle_seq_hdr_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
struct msm_vidc_inst *inst;
struct vb2_buffer *vb;
struct vidc_hal_fbd *fill_buf_done;
if (!response) {
dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
return;
}
inst = (struct msm_vidc_inst *)response->session_id;
fill_buf_done = (struct vidc_hal_fbd *)&response->output_done;
vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT],
(u32)fill_buf_done->packet_buffer1);
if (!vb) {
dprintk(VIDC_ERR,
"Failed to find video buffer for seq_hdr_done");
return;
}
vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
vb->v4l2_planes[0].data_offset = fill_buf_done->offset1;
vb->v4l2_buf.flags = V4L2_QCOM_BUF_FLAG_CODECCONFIG;
vb->v4l2_buf.timestamp = ns_to_timeval(0);
dprintk(VIDC_DBG, "Filled length = %d; offset = %d; flags %x\n",
vb->v4l2_planes[0].bytesused,
vb->v4l2_planes[0].data_offset,
vb->v4l2_buf.flags);
mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
}
void handle_cmd_response(enum command_response cmd, void *data)
{
dprintk(VIDC_DBG, "Command response = %d\n", cmd);
switch (cmd) {
case SYS_INIT_DONE:
handle_sys_init_done(cmd, data);
break;
case RELEASE_RESOURCE_DONE:
handle_sys_release_res_done(cmd, data);
break;
case SESSION_INIT_DONE:
handle_session_init_done(cmd, data);
break;
case SESSION_PROPERTY_INFO:
handle_session_prop_info(cmd, data);
break;
case SESSION_LOAD_RESOURCE_DONE:
handle_load_resource_done(cmd, data);
break;
case SESSION_START_DONE:
handle_start_done(cmd, data);
break;
case SESSION_ETB_DONE:
handle_ebd(cmd, data);
break;
case SESSION_FTB_DONE:
handle_fbd(cmd, data);
break;
case SESSION_STOP_DONE:
handle_stop_done(cmd, data);
break;
case SESSION_RELEASE_RESOURCE_DONE:
handle_release_res_done(cmd, data);
break;
case SESSION_END_DONE:
case SESSION_ABORT_DONE:
handle_session_close(cmd, data);
break;
case VIDC_EVENT_CHANGE:
handle_event_change(cmd, data);
break;
case SESSION_FLUSH_DONE:
handle_session_flush(cmd, data);
break;
case SESSION_GET_SEQ_HDR_DONE:
handle_seq_hdr_done(cmd, data);
break;
case SYS_WATCHDOG_TIMEOUT:
handle_sys_error(cmd, data);
break;
case SYS_ERROR:
handle_sys_error(cmd, data);
break;
case SESSION_ERROR:
handle_session_error(cmd, data);
break;
case SESSION_RELEASE_BUFFER_DONE:
handle_session_release_buf_done(cmd, data);
break;
default:
dprintk(VIDC_ERR, "response unhandled\n");
break;
}
}
static int msm_comm_scale_clocks(struct msm_vidc_core *core)
{
int num_mbs_per_sec;
int rc = 0;
struct hfi_device *hdev;
if (!core) {
dprintk(VIDC_ERR, "%s Invalid args: %p\n", __func__, core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "%s Invalid device handle: %p\n",
__func__, hdev);
return -EINVAL;
}
if (is_turbo_requested(core, MSM_VIDC_ENCODER) ||
is_turbo_requested(core, MSM_VIDC_DECODER)) {
num_mbs_per_sec = core->resources.max_load;
} else {
num_mbs_per_sec = msm_comm_get_load(core, MSM_VIDC_ENCODER);
num_mbs_per_sec += msm_comm_get_load(core, MSM_VIDC_DECODER);
}
dprintk(VIDC_INFO, "num_mbs_per_sec = %d\n", num_mbs_per_sec);
rc = call_hfi_op(hdev, scale_clocks,
hdev->hfi_device_data, num_mbs_per_sec);
if (rc)
dprintk(VIDC_ERR, "Failed to set clock rate: %d\n", rc);
return rc;
}
void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
return;
}
core = inst->core;
hdev = core->device;
if (msm_comm_scale_clocks(core)) {
dprintk(VIDC_WARN,
"Failed to scale clocks. Performance might be impacted\n");
}
if (msm_comm_scale_bus(core, inst->session_type, DDR_MEM)) {
dprintk(VIDC_WARN,
"Failed to scale DDR bus. Performance might be impacted\n");
}
if (core->resources.has_ocmem) {
if (msm_comm_scale_bus(core, inst->session_type,
OCMEM_MEM))
dprintk(VIDC_WARN,
"Failed to scale OCMEM bus. Performance might be impacted\n");
}
}
static inline unsigned long get_ocmem_requirement(u32 height, u32 width)
{
int num_mbs = 0;
num_mbs = GET_NUM_MBS(height, width);
/*TODO: This should be changes once the numbers are
* available from firmware*/
return 512 * 1024;
}
static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
{
int rc = 0;
struct hfi_device *hdev;
if (!core || !core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
hdev = core->device;
if (core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core is in bad state. Cannot unset ocmem\n");
return -EIO;
}
init_completion(
&core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)]);
rc = call_hfi_op(hdev, unset_ocmem, hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_INFO, "Failed to unset OCMEM on driver\n");
goto release_ocmem_failed;
}
rc = wait_for_completion_timeout(
&core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
__func__, SYS_MSG_INDEX(RELEASE_RESOURCE_DONE));
rc = -EIO;
}
release_ocmem_failed:
return rc;
}
static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core = inst->core;
int rc = 0;
mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT_DONE) {
dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto core_already_inited;
}
dprintk(VIDC_DBG, "Waiting for SYS_INIT_DONE\n");
rc = wait_for_completion_timeout(
&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
__func__, SYS_MSG_INDEX(SYS_INIT_DONE));
rc = -EIO;
goto exit;
} else {
core->state = VIDC_CORE_INIT_DONE;
}
dprintk(VIDC_DBG, "SYS_INIT_DONE!!!\n");
core_already_inited:
change_inst_state(inst, MSM_VIDC_CORE_INIT_DONE);
rc = 0;
exit:
mutex_unlock(&core->lock);
return rc;
}
static int msm_comm_init_core(struct msm_vidc_inst *inst)
{
int rc = 0;
struct msm_vidc_core *core = inst->core;
struct hfi_device *hdev;
if (!core || !core->device)
return -EINVAL;
hdev = core->device;
mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT) {
dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto core_already_inited;
}
mutex_unlock(&core->lock);
rc = msm_comm_scale_bus(core, inst->session_type, DDR_MEM);
if (rc) {
dprintk(VIDC_ERR, "Failed to scale DDR bus: %d\n", rc);
goto fail_scale_bus;
}
mutex_lock(&core->lock);
if (core->state < VIDC_CORE_LOADED) {
rc = call_hfi_op(hdev, load_fw, hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to load video firmware\n");
goto fail_load_fw;
}
core->state = VIDC_CORE_LOADED;
}
mutex_unlock(&core->lock);
rc = msm_comm_scale_clocks(core);
if (rc) {
dprintk(VIDC_ERR, "Failed to scale clocks: %d\n", rc);
goto fail_core_init;
}
mutex_lock(&core->lock);
if (core->state == VIDC_CORE_LOADED) {
init_completion(&core->completions
[SYS_MSG_INDEX(SYS_INIT_DONE)]);
rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to init core, id = %d\n",
core->id);
goto fail_core_init;
}
core->state = VIDC_CORE_INIT;
}
core_already_inited:
change_inst_state(inst, MSM_VIDC_CORE_INIT);
mutex_unlock(&core->lock);
return rc;
fail_core_init:
call_hfi_op(hdev, unload_fw, hdev->hfi_device_data);
fail_load_fw:
msm_comm_unvote_buses(core, DDR_MEM);
fail_scale_bus:
mutex_unlock(&core->lock);
return rc;
}
static int msm_vidc_deinit_core(struct msm_vidc_inst *inst)
{
int rc = 0;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
core = inst->core;
hdev = core->device;
mutex_lock(&core->lock);
if (core->state == VIDC_CORE_UNINIT) {
dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto core_already_uninited;
}
mutex_unlock(&core->lock);
msm_comm_scale_clocks_and_bus(inst);
mutex_lock(&core->lock);
if (list_empty(&core->instances)) {
if (core->state > VIDC_CORE_INIT) {
if (core->resources.has_ocmem) {
if (inst->state != MSM_VIDC_CORE_INVALID)
msm_comm_unset_ocmem(core);
call_hfi_op(hdev, free_ocmem,
hdev->hfi_device_data);
}
dprintk(VIDC_DBG, "Calling vidc_hal_core_release\n");
rc = call_hfi_op(hdev, core_release,
hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR,
"Failed to release core, id = %d\n",
core->id);
goto exit;
}
}
core->state = VIDC_CORE_UNINIT;
call_hfi_op(hdev, unload_fw, hdev->hfi_device_data);
if (core->resources.has_ocmem)
msm_comm_unvote_buses(core, DDR_MEM|OCMEM_MEM);
else
msm_comm_unvote_buses(core, DDR_MEM);
}
core_already_uninited:
change_inst_state(inst, MSM_VIDC_CORE_UNINIT);
exit:
mutex_unlock(&core->lock);
return rc;
}
int msm_comm_force_cleanup(struct msm_vidc_inst *inst)
{
return msm_vidc_deinit_core(inst);
}
static enum hal_domain get_hal_domain(int session_type)
{
enum hal_domain domain;
switch (session_type) {
case MSM_VIDC_ENCODER:
domain = HAL_VIDEO_DOMAIN_ENCODER;
break;
case MSM_VIDC_DECODER:
domain = HAL_VIDEO_DOMAIN_DECODER;
break;
default:
dprintk(VIDC_ERR, "Wrong domain\n");
domain = HAL_UNUSED_DOMAIN;
break;
}
return domain;
}
enum hal_video_codec get_hal_codec_type(int fourcc)
{
enum hal_video_codec codec;
dprintk(VIDC_DBG, "codec is 0x%x", fourcc);
switch (fourcc) {
case V4L2_PIX_FMT_H264:
case V4L2_PIX_FMT_H264_NO_SC:
codec = HAL_VIDEO_CODEC_H264;
break;
case V4L2_PIX_FMT_H263:
codec = HAL_VIDEO_CODEC_H263;
break;
case V4L2_PIX_FMT_MPEG1:
codec = HAL_VIDEO_CODEC_MPEG1;
break;
case V4L2_PIX_FMT_MPEG2:
codec = HAL_VIDEO_CODEC_MPEG2;
break;
case V4L2_PIX_FMT_MPEG4:
codec = HAL_VIDEO_CODEC_MPEG4;
break;
case V4L2_PIX_FMT_VC1_ANNEX_G:
case V4L2_PIX_FMT_VC1_ANNEX_L:
codec = HAL_VIDEO_CODEC_VC1;
break;
case V4L2_PIX_FMT_VP8:
codec = HAL_VIDEO_CODEC_VP8;
break;
case V4L2_PIX_FMT_DIVX_311:
codec = HAL_VIDEO_CODEC_DIVX_311;
break;
case V4L2_PIX_FMT_DIVX:
codec = HAL_VIDEO_CODEC_DIVX;
break;
/*HAL_VIDEO_CODEC_MVC
HAL_VIDEO_CODEC_SPARK
HAL_VIDEO_CODEC_VP6
HAL_VIDEO_CODEC_VP7*/
case V4L2_PIX_FMT_HEVC:
codec = HAL_VIDEO_CODEC_HEVC;
break;
case V4L2_PIX_FMT_HEVC_HYBRID:
codec = HAL_VIDEO_CODEC_HEVC_HYBRID;
break;
default:
dprintk(VIDC_ERR, "Wrong codec: %d\n", fourcc);
codec = HAL_UNUSED_CODEC;
}
return codec;
}
static int msm_comm_session_init(int flipped_state,
struct msm_vidc_inst *inst)
{
int rc = 0;
int fourcc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_OPEN)) {
dprintk(VIDC_INFO, "inst: %p is already in state: %d\n",
inst, inst->state);
goto exit;
}
if (inst->session_type == MSM_VIDC_DECODER) {
fourcc = inst->fmts[OUTPUT_PORT]->fourcc;
} else if (inst->session_type == MSM_VIDC_ENCODER) {
fourcc = inst->fmts[CAPTURE_PORT]->fourcc;
} else {
dprintk(VIDC_ERR, "Invalid session\n");
return -EINVAL;
}
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_INIT_DONE)]);
mutex_lock(&inst->lock);
inst->session = call_hfi_op(hdev, session_init, hdev->hfi_device_data,
(u32) inst, get_hal_domain(inst->session_type),
get_hal_codec_type(fourcc));
mutex_unlock(&inst->lock);
if (!inst->session) {
dprintk(VIDC_ERR,
"Failed to call session init for: %d, %d, %d, %d\n",
(int)inst->core->device, (int)inst,
inst->session_type, fourcc);
goto exit;
}
inst->ftb_count = 0;
change_inst_state(inst, MSM_VIDC_OPEN);
exit:
return rc;
}
static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
{
struct msm_vidc_inst *temp;
dprintk(VIDC_ERR, "Running instances:\n");
dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
mutex_lock(&core->lock);
list_for_each_entry(temp, &core->instances, list) {
mutex_lock(&temp->lock);
if (temp->state >= MSM_VIDC_OPEN_DONE &&
temp->state < MSM_VIDC_STOP_DONE) {
dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d\n",
temp->session_type,
temp->prop.width[CAPTURE_PORT],
temp->prop.height[CAPTURE_PORT],
temp->prop.fps);
}
mutex_unlock(&temp->lock);
}
mutex_unlock(&core->lock);
}
static int msm_vidc_load_resources(int flipped_state,
struct msm_vidc_inst *inst)
{
int rc = 0;
u32 ocmem_sz = 0;
struct hfi_device *hdev;
int num_mbs_per_sec = 0;
int height, width;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
if (inst->core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core is in bad state can't do load res\n");
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Instance is in invalid state can't do load res\n");
return -EINVAL;
}
num_mbs_per_sec = msm_comm_get_load(inst->core, MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core, MSM_VIDC_ENCODER);
if (num_mbs_per_sec > inst->core->resources.max_load) {
dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n",
num_mbs_per_sec, inst->core->resources.max_load);
msm_vidc_print_running_insts(inst->core);
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
return -EBUSY;
}
hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) {
dprintk(VIDC_INFO, "inst: %p is already in state: %d\n",
inst, inst->state);
goto exit;
}
if (inst->core->resources.has_ocmem) {
height = max(inst->prop.height[CAPTURE_PORT],
inst->prop.height[OUTPUT_PORT]);
width = max(inst->prop.width[CAPTURE_PORT],
inst->prop.width[OUTPUT_PORT]);
ocmem_sz = get_ocmem_requirement(
height, width);
rc = msm_comm_scale_bus(inst->core, inst->session_type,
OCMEM_MEM);
if (!rc) {
mutex_lock(&inst->core->lock);
rc = call_hfi_op(hdev, alloc_ocmem,
hdev->hfi_device_data,
ocmem_sz);
mutex_unlock(&inst->core->lock);
if (rc) {
dprintk(VIDC_WARN,
"Failed to allocate OCMEM. Performance will be impacted\n");
msm_comm_unvote_buses(inst->core, OCMEM_MEM);
}
} else {
dprintk(VIDC_WARN,
"Failed to vote for OCMEM BW. Performance will be impacted\n");
}
}
rc = call_hfi_op(hdev, session_load_res, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send load resources\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_LOAD_RESOURCES);
exit:
return rc;
}
static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core is in bad state can't do start");
return -EINVAL;
}
hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_START)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
inst, inst->state);
goto exit;
}
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_START_DONE)]);
rc = call_hfi_op(hdev, session_start, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send start\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_START);
exit:
return rc;
}
static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_STOP)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
inst, inst->state);
goto exit;
}
dprintk(VIDC_DBG, "Send Stop to hal\n");
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_STOP_DONE)]);
rc = call_hfi_op(hdev, session_stop, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "Failed to send stop\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_STOP);
exit:
return rc;
}
static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_RELEASE_RESOURCES)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
inst, inst->state);
goto exit;
}
dprintk(VIDC_DBG,
"Send release res to hal\n");
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_RELEASE_RESOURCE_DONE)]);
rc = call_hfi_op(hdev, session_release_res, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send release resources\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES);
exit:
return rc;
}
static int msm_comm_session_close(int flipped_state,
struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid params", __func__);
return -EINVAL;
}
hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_CLOSE)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
inst, inst->state);
goto exit;
}
dprintk(VIDC_DBG,
"Send session close to hal\n");
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_END_DONE)]);
rc = call_hfi_op(hdev, session_end, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send close\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_CLOSE);
exit:
return rc;
}
static int get_flipped_state(int present_state,
int desired_state)
{
int flipped_state = present_state;
if (flipped_state < MSM_VIDC_STOP
&& desired_state > MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
} else if (flipped_state > MSM_VIDC_STOP
&& desired_state < MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP -
(flipped_state - MSM_VIDC_STOP + 1);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
}
return flipped_state;
}
struct hal_buffer_requirements *get_buff_req_buffer(
struct msm_vidc_inst *inst, enum hal_buffer buffer_type)
{
int i;
for (i = 0; i < HAL_BUFFER_MAX; i++) {
if (inst->buff_req.buffer[i].buffer_type == buffer_type)
return &inst->buff_req.buffer[i];
}
return NULL;
}
static int set_output_buffers(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type)
{
int rc = 0;
struct msm_smem *handle;
struct internal_buf *binfo;
struct vidc_buffer_addr_info buffer_info = {0};
u32 smem_flags = 0, buffer_size;
struct hal_buffer_requirements *output_buf, *extradata_buf;
int i;
struct hfi_device *hdev;
hdev = inst->core->device;
output_buf = get_buff_req_buffer(inst, buffer_type);
if (!output_buf) {
dprintk(VIDC_DBG,
"This output buffer not required, buffer_type: %x\n",
buffer_type);
return 0;
}
dprintk(VIDC_DBG,
"output: num = %d, size = %d\n",
output_buf->buffer_count_actual,
output_buf->buffer_size);
buffer_size = output_buf->buffer_size;
extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT);
if (extradata_buf) {
dprintk(VIDC_DBG,
"extradata: num = %d, size = %d\n",
extradata_buf->buffer_count_actual,
extradata_buf->buffer_size);
buffer_size += extradata_buf->buffer_size;
} else {
dprintk(VIDC_DBG,
"This extradata buffer not required, buffer_type: %x\n",
buffer_type);
}
if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
if (output_buf->buffer_size) {
for (i = 0; i < output_buf->buffer_count_actual;
i++) {
handle = msm_comm_smem_alloc(inst,
buffer_size, 1, smem_flags,
buffer_type, 0);
if (!handle) {
dprintk(VIDC_ERR,
"Failed to allocate output memory\n");
rc = -ENOMEM;
goto err_no_mem;
}
rc = msm_comm_smem_cache_operations(inst,
handle, SMEM_CACHE_CLEAN);
if (rc) {
dprintk(VIDC_WARN,
"Failed to clean cache may cause undefined behavior\n");
}
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "Out of memory\n");
rc = -ENOMEM;
goto fail_kzalloc;
}
mutex_lock(&inst->lock);
binfo->handle = handle;
buffer_info.buffer_size = output_buf->buffer_size;
buffer_info.buffer_type = buffer_type;
binfo->buffer_type = buffer_type;
buffer_info.num_buffers = 1;
binfo->buffer_ownership = DRIVER;
buffer_info.align_device_addr = handle->device_addr;
buffer_info.extradata_addr = handle->device_addr +
output_buf->buffer_size;
if (extradata_buf) {
buffer_info.extradata_size =
extradata_buf->buffer_size;
}
dprintk(VIDC_DBG, "Output buffer address: %x",
buffer_info.align_device_addr);
dprintk(VIDC_DBG, "Output extradata address: %x",
buffer_info.extradata_addr);
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
mutex_unlock(&inst->lock);
if (rc) {
dprintk(VIDC_ERR,
"%s : session_set_buffers failed",
__func__);
goto fail_set_buffers;
}
mutex_lock(&inst->lock);
list_add_tail(&binfo->list, &inst->outputbufs);
mutex_unlock(&inst->lock);
}
}
return rc;
fail_set_buffers:
kfree(binfo);
fail_kzalloc:
msm_comm_smem_free(inst, handle);
err_no_mem:
return rc;
}
static int set_scratch_buffers(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type)
{
int rc = 0;
struct msm_smem *handle;
struct internal_buf *binfo;
struct vidc_buffer_addr_info buffer_info;
u32 smem_flags = 0;
struct hal_buffer_requirements *scratch_buf;
int i;
struct hfi_device *hdev;
hdev = inst->core->device;
scratch_buf = get_buff_req_buffer(inst, buffer_type);
if (!scratch_buf) {
dprintk(VIDC_DBG,
"This scratch buffer not required, buffer_type: %x\n",
buffer_type);
return 0;
}
dprintk(VIDC_DBG,
"scratch: num = %d, size = %d\n",
scratch_buf->buffer_count_actual,
scratch_buf->buffer_size);
if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
if (scratch_buf->buffer_size) {
for (i = 0; i < scratch_buf->buffer_count_actual;
i++) {
handle = msm_comm_smem_alloc(inst,
scratch_buf->buffer_size, 1, smem_flags,
buffer_type, 0);
if (!handle) {
dprintk(VIDC_ERR,
"Failed to allocate scratch memory\n");
rc = -ENOMEM;
goto err_no_mem;
}
rc = msm_comm_smem_cache_operations(inst,
handle, SMEM_CACHE_CLEAN);
if (rc) {
dprintk(VIDC_WARN,
"Failed to clean cache may cause undefined behavior\n");
}
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "Out of memory\n");
rc = -ENOMEM;
goto fail_kzalloc;
}
binfo->handle = handle;
buffer_info.buffer_size = scratch_buf->buffer_size;
buffer_info.buffer_type = buffer_type;
binfo->buffer_type = buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
dprintk(VIDC_DBG, "Scratch buffer address: %x",
buffer_info.align_device_addr);
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
"vidc_hal_session_set_buffers failed");
goto fail_set_buffers;
}
mutex_lock(&inst->lock);
list_add_tail(&binfo->list, &inst->internalbufs);
mutex_unlock(&inst->lock);
}
}
return rc;
fail_set_buffers:
kfree(binfo);
fail_kzalloc:
msm_comm_smem_free(inst, handle);
err_no_mem:
return rc;
}
static int set_persist_buffers(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type)
{
int rc = 0;
struct msm_smem *handle;
struct internal_buf *binfo;
struct vidc_buffer_addr_info buffer_info;
u32 smem_flags = 0;
struct hal_buffer_requirements *persist_buf;
int i;
struct hfi_device *hdev;
hdev = inst->core->device;
persist_buf = get_buff_req_buffer(inst, buffer_type);
if (!persist_buf) {
dprintk(VIDC_DBG,
"This persist buffer not required, buffer_type: %x\n",
buffer_type);
return 0;
}
dprintk(VIDC_DBG,
"persist: num = %d, size = %d\n",
persist_buf->buffer_count_actual,
persist_buf->buffer_size);
if (!list_empty(&inst->persistbufs)) {
dprintk(VIDC_ERR,
"Persist buffers already allocated\n");
return rc;
}
if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
if (persist_buf->buffer_size) {
for (i = 0; i < persist_buf->buffer_count_actual; i++) {
handle = msm_comm_smem_alloc(inst,
persist_buf->buffer_size, 1, smem_flags,
buffer_type, 0);
if (!handle) {
dprintk(VIDC_ERR,
"Failed to allocate persist memory\n");
rc = -ENOMEM;
goto err_no_mem;
}
rc = msm_comm_smem_cache_operations(inst,
handle, SMEM_CACHE_CLEAN);
if (rc) {
dprintk(VIDC_WARN,
"Failed to clean cache may cause undefined behavior\n");
}
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "Out of memory\n");
rc = -ENOMEM;
goto fail_kzalloc;
}
binfo->handle = handle;
buffer_info.buffer_size = persist_buf->buffer_size;
buffer_info.buffer_type = buffer_type;
binfo->buffer_type = buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
dprintk(VIDC_DBG, "Persist buffer address: %x",
buffer_info.align_device_addr);
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
"vidc_hal_session_set_buffers failed");
goto fail_set_buffers;
}
mutex_lock(&inst->lock);
list_add_tail(&binfo->list, &inst->persistbufs);
mutex_unlock(&inst->lock);
}
}
return rc;
fail_set_buffers:
kfree(binfo);
fail_kzalloc:
msm_comm_smem_free(inst, handle);
err_no_mem:
return rc;
}
int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
{
int rc = 0;
int flipped_state;
struct msm_vidc_core *core;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
return -EINVAL;
}
dprintk(VIDC_DBG,
"Trying to move inst: %p from: 0x%x to 0x%x\n",
inst, inst->state, state);
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %p\n", inst);
return -EINVAL;
}
mutex_lock(&inst->sync_lock);
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core is in bad state can't change the state");
rc = -EINVAL;
goto exit;
}
flipped_state = get_flipped_state(inst->state, state);
dprintk(VIDC_DBG,
"flipped_state = 0x%x\n", flipped_state);
switch (flipped_state) {
case MSM_VIDC_CORE_UNINIT_DONE:
case MSM_VIDC_CORE_INIT:
rc = msm_comm_init_core(inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_CORE_INIT_DONE:
rc = msm_comm_init_core_done(inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_OPEN:
rc = msm_comm_session_init(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_OPEN_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
SESSION_INIT_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_LOAD_RESOURCES:
rc = msm_vidc_load_resources(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_LOAD_RESOURCES_DONE:
case MSM_VIDC_START:
rc = msm_vidc_start(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_START_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_START_DONE,
SESSION_START_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_STOP:
rc = msm_vidc_stop(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_STOP_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_STOP_DONE,
SESSION_STOP_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
dprintk(VIDC_DBG, "Moving to Stop Done state\n");
case MSM_VIDC_RELEASE_RESOURCES:
rc = msm_vidc_release_res(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_RELEASE_RESOURCES_DONE:
rc = wait_for_state(inst, flipped_state,
MSM_VIDC_RELEASE_RESOURCES_DONE,
SESSION_RELEASE_RESOURCE_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
dprintk(VIDC_DBG,
"Moving to release resources done state\n");
case MSM_VIDC_CLOSE:
rc = msm_comm_session_close(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_CLOSE_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
SESSION_END_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_CORE_UNINIT:
dprintk(VIDC_DBG, "Sending core uninit\n");
rc = msm_vidc_deinit_core(inst);
if (rc || state == get_flipped_state(inst->state, state))
break;
default:
dprintk(VIDC_ERR, "State not recognized\n");
rc = -EINVAL;
break;
}
exit:
mutex_unlock(&inst->sync_lock);
if (rc)
dprintk(VIDC_ERR,
"Failed to move from state: %d to %d\n",
inst->state, state);
return rc;
}
int msm_comm_qbuf(struct vb2_buffer *vb)
{
int rc = 0;
struct vb2_queue *q;
struct msm_vidc_inst *inst;
struct vb2_buf_entry *entry;
struct vidc_frame_data frame_data;
struct msm_vidc_core *core;
struct hfi_device *hdev;
q = vb->vb2_queue;
inst = q->drv_priv;
if (!inst || !vb) {
dprintk(VIDC_ERR, "Invalid input: %p, %p\n", inst, vb);
return -EINVAL;
}
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid input: %p, %p, %p\n", inst, core, vb);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid input: %p", hdev);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR, "Core is in bad state. Can't Queue\n");
return -EINVAL;
}
if (inst->state != MSM_VIDC_START_DONE) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
dprintk(VIDC_ERR, "Out of memory\n");
goto err_no_mem;
}
entry->vb = vb;
mutex_lock(&inst->sync_lock);
list_add_tail(&entry->list, &inst->pendingq);
mutex_unlock(&inst->sync_lock);
} else {
int64_t time_usec = timeval_to_ns(&vb->v4l2_buf.timestamp);
do_div(time_usec, NSEC_PER_USEC);
memset(&frame_data, 0 , sizeof(struct vidc_frame_data));
frame_data.alloc_len = vb->v4l2_planes[0].length;
frame_data.filled_len = vb->v4l2_planes[0].bytesused;
frame_data.offset = vb->v4l2_planes[0].data_offset;
frame_data.device_addr = vb->v4l2_planes[0].m.userptr;
frame_data.timestamp = time_usec;
frame_data.flags = 0;
frame_data.clnt_data = (u32)vb;
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
(frame_data.filled_len > frame_data.alloc_len ||
frame_data.offset > frame_data.alloc_len)) {
dprintk(VIDC_ERR,
"Buffer will overflow, not queueing it\n");
rc = -EINVAL;
goto err_bad_input;
}
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
frame_data.buffer_type = HAL_BUFFER_INPUT;
if (vb->v4l2_buf.flags & V4L2_BUF_FLAG_EOS) {
frame_data.flags |= HAL_BUFFERFLAG_EOS;
dprintk(VIDC_DBG,
"Received EOS on output capability\n");
}
if (vb->v4l2_buf.flags &
V4L2_QCOM_BUF_FLAG_CODECCONFIG) {
frame_data.flags |= HAL_BUFFERFLAG_CODECCONFIG;
dprintk(VIDC_DBG,
"Received CODECCONFIG on output cap\n");
}
if (vb->v4l2_buf.flags &
V4L2_QCOM_BUF_FLAG_DECODEONLY) {
frame_data.flags |= HAL_BUFFERFLAG_DECODEONLY;
dprintk(VIDC_DBG,
"Received DECODEONLY on output cap\n");
}
if (vb->v4l2_buf.flags &
V4L2_QCOM_BUF_TIMESTAMP_INVALID)
frame_data.timestamp = LLONG_MAX;
dprintk(VIDC_DBG,
"Sending etb to hal: device_addr: 0x%x, alloc: %d, filled: %d, offset: %d, ts: %lld, flags = 0x%x\n",
frame_data.device_addr, frame_data.alloc_len,
frame_data.filled_len, frame_data.offset,
frame_data.timestamp, frame_data.flags);
rc = call_hfi_op(hdev, session_etb, (void *)
inst->session, &frame_data);
if (!rc)
msm_vidc_debugfs_update(inst,
MSM_VIDC_DEBUGFS_EVENT_ETB);
dprintk(VIDC_DBG, "Sent etb to HAL\n");
} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct vidc_seq_hdr seq_hdr;
int extra_idx = 0;
frame_data.filled_len = 0;
frame_data.offset = 0;
frame_data.alloc_len = vb->v4l2_planes[0].length;
frame_data.buffer_type =
msm_comm_get_hal_output_buffer(inst);
extra_idx =
EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES) &&
vb->v4l2_planes[extra_idx].m.userptr) {
frame_data.extradata_addr =
vb->v4l2_planes[extra_idx].m.userptr;
frame_data.extradata_size =
vb->v4l2_planes[extra_idx].length;
}
dprintk(VIDC_DBG,
"Sending ftb to hal: device_addr: 0x%x, alloc: %d, buffer_type: %d, ts: %lld, flags = 0x%x\n",
frame_data.device_addr, frame_data.alloc_len,
frame_data.buffer_type, frame_data.timestamp,
frame_data.flags);
if (!inst->ftb_count &&
inst->session_type == MSM_VIDC_ENCODER) {
seq_hdr.seq_hdr = (u8 *) vb->v4l2_planes[0].
m.userptr;
seq_hdr.seq_hdr_len = vb->v4l2_planes[0].length;
rc = call_hfi_op(hdev, session_get_seq_hdr,
(void *) inst->session, &seq_hdr);
if (!rc) {
inst->vb2_seq_hdr = vb;
dprintk(VIDC_DBG, "Seq_hdr: %p\n",
inst->vb2_seq_hdr);
}
} else {
rc = call_hfi_op(hdev, session_ftb,
(void *) inst->session, &frame_data);
if (!rc)
msm_vidc_debugfs_update(inst,
MSM_VIDC_DEBUGFS_EVENT_FTB);
}
inst->ftb_count++;
} else {
dprintk(VIDC_ERR,
"This capability is not supported: %d\n",
q->type);
rc = -EINVAL;
}
}
err_bad_input:
if (rc)
dprintk(VIDC_ERR, "Failed to queue buffer\n");
err_no_mem:
return rc;
}
int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core is in bad state can't query get_bufreqs()");
return -EINVAL;
}
hdev = inst->core->device;
mutex_lock(&inst->sync_lock);
if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
dprintk(VIDC_ERR,
"Not in proper state to query buffer requirements\n");
rc = -EAGAIN;
goto exit;
}
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)]);
rc = call_hfi_op(hdev, session_get_buf_req, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "Failed to get property\n");
goto exit;
}
rc = wait_for_completion_timeout(
&inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR,
"%s: Wait interrupted or timeout[%u]: %d\n",
__func__, (u32)inst->session,
SESSION_MSG_INDEX(SESSION_PROPERTY_INFO));
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
rc = -EIO;
goto exit;
}
rc = 0;
exit:
mutex_unlock(&inst->sync_lock);
return rc;
}
int msm_comm_release_output_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
struct list_head *ptr, *next;
struct internal_buf *buf;
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
return -EINVAL;
}
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %p\n", core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev);
return -EINVAL;
}
mutex_lock(&inst->lock);
if (!list_empty(&inst->outputbufs)) {
list_for_each_safe(ptr, next, &inst->outputbufs) {
buf = list_entry(ptr, struct internal_buf,
list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
buffer_info.response_required = false;
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_WARN,
"Rel output buf fail:0x%x, %d",
buffer_info.align_device_addr,
buffer_info.buffer_size);
}
list_del(&buf->list);
mutex_unlock(&inst->lock);
msm_comm_smem_free(inst, buf->handle);
kfree(buf);
mutex_lock(&inst->lock);
}
}
mutex_unlock(&inst->lock);
return rc;
}
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
struct list_head *ptr, *next;
struct internal_buf *buf;
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
return -EINVAL;
}
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %p\n", core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev);
return -EINVAL;
}
mutex_lock(&inst->lock);
if (!list_empty(&inst->internalbufs)) {
list_for_each_safe(ptr, next, &inst->internalbufs) {
buf = list_entry(ptr, struct internal_buf,
list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
buffer_info.response_required = true;
init_completion(
&inst->completions[SESSION_MSG_INDEX
(SESSION_RELEASE_BUFFER_DONE)]);
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_WARN,
"Rel scrtch buf fail:0x%x, %d",
buffer_info.align_device_addr,
buffer_info.buffer_size);
mutex_unlock(&inst->lock);
rc = wait_for_sess_signal_receipt(inst,
SESSION_RELEASE_BUFFER_DONE);
if (rc) {
mutex_lock(&inst->sync_lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->sync_lock);
msm_comm_recover_from_session_error(
inst);
}
mutex_lock(&inst->lock);
}
list_del(&buf->list);
mutex_unlock(&inst->lock);
msm_comm_smem_free(inst, buf->handle);
kfree(buf);
mutex_lock(&inst->lock);
}
}
mutex_unlock(&inst->lock);
return rc;
}
int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
struct list_head *ptr, *next;
struct internal_buf *buf;
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
return -EINVAL;
}
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %p\n", core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev);
return -EINVAL;
}
mutex_lock(&inst->lock);
if (!list_empty(&inst->persistbufs)) {
list_for_each_safe(ptr, next, &inst->persistbufs) {
buf = list_entry(ptr, struct internal_buf,
list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
buffer_info.response_required = true;
init_completion(
&inst->completions[SESSION_MSG_INDEX
(SESSION_RELEASE_BUFFER_DONE)]);
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_WARN,
"Rel prst buf fail:0x%x, %d",
buffer_info.align_device_addr,
buffer_info.buffer_size);
mutex_unlock(&inst->lock);
rc = wait_for_sess_signal_receipt(inst,
SESSION_RELEASE_BUFFER_DONE);
if (rc) {
mutex_lock(&inst->sync_lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->sync_lock);
msm_comm_recover_from_session_error(
inst);
}
mutex_lock(&inst->lock);
}
list_del(&buf->list);
mutex_unlock(&inst->lock);
msm_comm_smem_free(inst, buf->handle);
kfree(buf);
mutex_lock(&inst->lock);
}
}
mutex_unlock(&inst->lock);
return rc;
}
int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
enum hal_property ptype, void *pdata)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR, "Invalid input: %p\n", inst);
return -EINVAL;
}
if (!inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
hdev = inst->core->device;
mutex_lock(&inst->sync_lock);
if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
dprintk(VIDC_ERR, "Not in proper state to set property\n");
rc = -EAGAIN;
goto exit;
}
rc = call_hfi_op(hdev, session_set_property, (void *)inst->session,
ptype, pdata);
if (rc)
dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
exit:
mutex_unlock(&inst->sync_lock);
return rc;
}
int msm_comm_set_output_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
if (msm_comm_release_output_buffers(inst))
dprintk(VIDC_WARN, "Failed to release output buffers\n");
rc = set_output_buffers(inst, HAL_BUFFER_OUTPUT);
if (rc)
goto error;
return rc;
error:
msm_comm_release_output_buffers(inst);
return rc;
}
int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
if (msm_comm_release_scratch_buffers(inst))
dprintk(VIDC_WARN, "Failed to release scratch buffers\n");
rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH);
if (rc)
goto error;
rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_1);
if (rc)
goto error;
rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_2);
if (rc)
goto error;
return rc;
error:
msm_comm_release_scratch_buffers(inst);
return rc;
}
int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
rc = set_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST);
if (rc)
goto error;
rc = set_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST_1);
if (rc)
goto error;
return rc;
error:
msm_comm_release_persist_buffers(inst);
return rc;
}
static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
{
struct list_head *ptr, *next;
struct vb2_buffer *vb;
if (!list_empty(&inst->bufq[CAPTURE_PORT].
vb2_bufq.queued_list)) {
list_for_each_safe(ptr, next,
&inst->bufq[CAPTURE_PORT].
vb2_bufq.queued_list) {
vb = container_of(ptr,
struct vb2_buffer,
queued_entry);
if (vb) {
vb->v4l2_planes[0].bytesused = 0;
vb->v4l2_planes[0].data_offset = 0;
mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
vb2_buffer_done(vb,
VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
}
}
}
if (!list_empty(&inst->bufq[OUTPUT_PORT].
vb2_bufq.queued_list)) {
list_for_each_safe(ptr, next,
&inst->bufq[OUTPUT_PORT].
vb2_bufq.queued_list) {
vb = container_of(ptr,
struct vb2_buffer,
queued_entry);
if (vb) {
vb->v4l2_planes[0].bytesused = 0;
vb->v4l2_planes[0].data_offset = 0;
mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
vb2_buffer_done(vb,
VB2_BUF_STATE_DONE);
mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
}
}
}
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
return;
}
void msm_comm_flush_dynamic_buffers(struct msm_vidc_inst *inst)
{
struct buffer_info *binfo = NULL, *dummy = NULL;
struct list_head *list = NULL;
if (inst->buffer_mode_set[CAPTURE_PORT] != HAL_BUFFER_MODE_DYNAMIC)
return;
/*
* dynamic buffer mode:- if flush is called during seek
* driver should not queue any new buffer it has been holding.
*
* Each dynamic o/p buffer can have one of following ref_count:
* ref_count : 0 - f/w has released reference and sent fbd back.
* The buffer has been returned back to client.
*
* ref_count : 1 - f/w is holding reference. f/w may have released
* fbd as read_only OR fbd is pending. f/w will
* release reference before sending flush_done.
*
* ref_count : 2 - f/w is holding reference, f/w has released fbd as
* read_only, which client has queued back to driver.
* driver holds this buffer and will queue back
* only when f/w releases the reference. During
* flush_done, f/w will release the reference but driver
* should not queue back the buffer to f/w.
* Flush all buffers with ref_count 2.
*/
mutex_lock(&inst->lock);
if (!list_empty(&inst->registered_bufs)) {
struct v4l2_event buf_event = {0};
u32 *ptr = NULL;
list = &inst->registered_bufs;
list_for_each_entry_safe(binfo, dummy, list, list) {
if (binfo &&
(binfo->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
(atomic_read(&binfo->ref_count) == 2)) {
atomic_dec(&binfo->ref_count);
buf_event.type =
V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER;
ptr = (u32 *)buf_event.u.data;
ptr[0] = binfo->fd[0];
ptr[1] = binfo->buff_off[0];
ptr[2] = binfo->uvaddr[0];
ptr[3] = (u32) binfo->timestamp.tv_sec;
ptr[4] = (u32) binfo->timestamp.tv_usec;
ptr[5] = binfo->v4l2_index;
dprintk(VIDC_DBG,
"released buffer held in driver before issuing flush: 0x%x fd[0]: %d\n",
binfo->device_addr[0], binfo->fd[0]);
/*send event to client*/
v4l2_event_queue_fh(&inst->event_handler,
&buf_event);
wake_up(&inst->kernel_event_queue);
}
}
}
mutex_unlock(&inst->lock);
}
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
{
int rc = 0;
bool ip_flush = false;
bool op_flush = false;
struct list_head *ptr, *next;
struct vb2_buf_entry *temp;
struct mutex *lock;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
return -EINVAL;
}
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %p\n", core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device pointer = %p", hdev);
return -EINVAL;
}
ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
if (ip_flush && !op_flush) {
dprintk(VIDC_INFO, "Input only flush not supported\n");
return 0;
}
mutex_lock(&inst->sync_lock);
msm_comm_flush_dynamic_buffers(inst);
mutex_unlock(&inst->sync_lock);
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core %p and inst %p are in bad state\n",
core, inst);
msm_comm_flush_in_invalid_state(inst);
return 0;
}
mutex_lock(&inst->sync_lock);
if (inst->in_reconfig && !ip_flush && op_flush) {
if (!list_empty(&inst->pendingq)) {
/*Execution can never reach here since port reconfig
* wont happen unless pendingq is emptied out
* (both pendingq and flush being secured with same
* lock). Printing a message here incase this breaks.*/
dprintk(VIDC_WARN,
"FLUSH BUG: Pending q not empty! It should be empty\n");
}
rc = call_hfi_op(hdev, session_flush, inst->session,
HAL_FLUSH_OUTPUT);
if (!rc && (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY))
rc = call_hfi_op(hdev, session_flush, inst->session,
HAL_FLUSH_OUTPUT2);
} else {
if (!list_empty(&inst->pendingq)) {
/*If flush is called after queueing buffers but before
* streamon driver should flush the pending queue*/
list_for_each_safe(ptr, next, &inst->pendingq) {
temp =
list_entry(ptr, struct vb2_buf_entry, list);
if (temp->vb->v4l2_buf.type ==
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
lock = &inst->bufq[CAPTURE_PORT].lock;
else
lock = &inst->bufq[OUTPUT_PORT].lock;
temp->vb->v4l2_planes[0].bytesused = 0;
mutex_lock(lock);
vb2_buffer_done(temp->vb, VB2_BUF_STATE_DONE);
mutex_unlock(lock);
list_del(&temp->list);
kfree(temp);
}
}
/*Do not send flush in case of session_error */
if (!(inst->state == MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID))
rc = call_hfi_op(hdev, session_flush, inst->session,
HAL_FLUSH_ALL);
}
mutex_unlock(&inst->sync_lock);
return rc;
}
enum hal_extradata_id msm_comm_get_hal_extradata_index(
enum v4l2_mpeg_vidc_extradata index)
{
int ret = 0;
switch (index) {
case V4L2_MPEG_VIDC_EXTRADATA_NONE:
ret = HAL_EXTRADATA_NONE;
break;
case V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION:
ret = HAL_EXTRADATA_MB_QUANTIZATION;
break;
case V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO:
ret = HAL_EXTRADATA_INTERLACE_VIDEO;
break;
case V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP:
ret = HAL_EXTRADATA_VC1_FRAMEDISP;
break;
case V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP:
ret = HAL_EXTRADATA_VC1_SEQDISP;
break;
case V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP:
ret = HAL_EXTRADATA_TIMESTAMP;
break;
case V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING:
ret = HAL_EXTRADATA_S3D_FRAME_PACKING;
break;
case V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE:
ret = HAL_EXTRADATA_FRAME_RATE;
break;
case V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW:
ret = HAL_EXTRADATA_PANSCAN_WINDOW;
break;
case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI:
ret = HAL_EXTRADATA_RECOVERY_POINT_SEI;
break;
case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
ret = HAL_EXTRADATA_MULTISLICE_INFO;
break;
case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB:
ret = HAL_EXTRADATA_NUM_CONCEALED_MB;
break;
case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER:
ret = HAL_EXTRADATA_METADATA_FILLER;
break;
case V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO:
ret = HAL_EXTRADATA_ASPECT_RATIO;
break;
case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP:
ret = HAL_EXTRADATA_MPEG2_SEQDISP;
break;
case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP:
ret = HAL_EXTRADATA_FRAME_QP;
break;
case V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO:
ret = HAL_EXTRADATA_FRAME_BITS_INFO;
break;
case V4L2_MPEG_VIDC_EXTRADATA_LTR:
ret = HAL_EXTRADATA_LTR_INFO;
break;
case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
ret = HAL_EXTRADATA_METADATA_MBI;
break;
case V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA:
ret = HAL_EXTRADATA_STREAM_USERDATA;
break;
default:
dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
break;
}
return ret;
};
int msm_comm_get_domain_partition(struct msm_vidc_inst *inst, u32 flags,
enum v4l2_buf_type buf_type, int *domain, int *partition)
{
struct hfi_device *hdev;
u32 hal_buffer_type = 0;
if (!inst || !inst->core || !inst->core->device)
return -EINVAL;
hdev = inst->core->device;
/*
* TODO: Due to the way in which the underlying smem mechanism
* maps buffer types to corresponding IOMMU domains, we need to
* pass in HAL_BUFFER_OUTPUT for input buffers (and vice versa)
* so that buffers are mapped into the correct domains. In the
* future, we should try to remove this workaround.
*/
switch (buf_type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
hal_buffer_type = (inst->session_type == MSM_VIDC_ENCODER) ?
HAL_BUFFER_INPUT : HAL_BUFFER_OUTPUT;
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
hal_buffer_type = (inst->session_type == MSM_VIDC_ENCODER) ?
HAL_BUFFER_OUTPUT : HAL_BUFFER_INPUT;
break;
default:
dprintk(VIDC_ERR, "v4l2 buf type not found %d\n", buf_type);
return -ENOTSUPP;
}
return call_hfi_op(hdev, iommu_get_domain_partition,
hdev->hfi_device_data, flags, hal_buffer_type, domain,
partition);
};
int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
enum hal_ssr_trigger_type type)
{
int rc = 0;
struct hfi_device *hdev;
if (!core || !core->device) {
dprintk(VIDC_WARN, "Invalid parameters: %p\n", core);
return -EINVAL;
}
hdev = core->device;
if (core->state == VIDC_CORE_INIT_DONE)
rc = call_hfi_op(hdev, core_trigger_ssr,
hdev->hfi_device_data, type);
return rc;
}
static int msm_vidc_load_supported(struct msm_vidc_inst *inst)
{
int num_mbs_per_sec = 0;
if (inst->state == MSM_VIDC_OPEN_DONE) {
num_mbs_per_sec = msm_comm_get_load(inst->core,
MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core,
MSM_VIDC_ENCODER);
if (num_mbs_per_sec > inst->core->resources.max_load) {
dprintk(VIDC_ERR,
"H/w is overloaded. needed: %d max: %d\n",
num_mbs_per_sec,
inst->core->resources.max_load);
msm_vidc_print_running_insts(inst->core);
return -EINVAL;
}
}
return 0;
}
int msm_comm_check_scaling_supported(struct msm_vidc_inst *inst)
{
u32 x_min, x_max, y_min, y_max;
u32 input_height, input_width, output_height, output_width;
if (!inst->capability.scale_x.min || !inst->capability.scale_x.max ||
!inst->capability.scale_y.min ||
!inst->capability.scale_y.max) {
dprintk(VIDC_ERR, "%s : Invalid scaling ratios",
__func__);
return -ENOTSUPP;
}
x_min = (1 << 16) / inst->capability.scale_x.min;
y_min = (1 << 16) / inst->capability.scale_y.min;
x_max = inst->capability.scale_x.max >> 16;
y_max = inst->capability.scale_y.max >> 16;
input_height = inst->prop.height[OUTPUT_PORT];
input_width = inst->prop.width[OUTPUT_PORT];
output_height = inst->prop.height[CAPTURE_PORT];
output_width = inst->prop.width[CAPTURE_PORT];
if (!input_height || !input_width || !output_height || !output_width) {
dprintk(VIDC_ERR,
"Invalid : Input Height = %d Width = %d"
" Output Height = %d Width = %d",
input_height, input_width, output_height,
output_width);
return -ENOTSUPP;
}
if (input_height > output_height) {
if (input_height / output_height > x_min) {
dprintk(VIDC_ERR,
"Unsupported Height Downscale ratio %d Vs %d",
input_height/output_height, x_min);
return -ENOTSUPP;
}
} else {
if (input_height / output_height > x_max) {
dprintk(VIDC_ERR,
"Unsupported Height Upscale ratio %d Vs %d",
input_height/output_height, x_max);
return -ENOTSUPP;
}
}
if (input_width > output_width) {
if (input_width / output_width > y_min) {
dprintk(VIDC_ERR,
"Unsupported Width Downscale ratio %d Vs %d",
input_width/output_width, y_min);
return -ENOTSUPP;
}
} else {
if (input_width / output_width > y_max) {
dprintk(VIDC_ERR,
"Unsupported Width Upscale ratio %d Vs %d",
input_width/output_width, y_max);
return -ENOTSUPP;
}
}
return 0;
}
int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
{
struct msm_vidc_core_capability *capability;
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__);
return -EINVAL;
}
capability = &inst->capability;
hdev = inst->core->device;
rc = msm_vidc_load_supported(inst);
if (!rc && inst->capability.capability_set) {
if (inst->prop.width[CAPTURE_PORT] < capability->width.min ||
inst->prop.height[CAPTURE_PORT] <
capability->height.min) {
dprintk(VIDC_ERR,
"Unsupported WxH = (%u)x(%u), min supported is - (%u)x(%u)\n",
inst->prop.width[CAPTURE_PORT],
inst->prop.height[CAPTURE_PORT],
capability->width.min,
capability->height.min);
rc = -ENOTSUPP;
}
if (!rc) {
rc = call_hfi_op(hdev, capability_check,
inst->fmts[OUTPUT_PORT]->fourcc,
inst->prop.width[CAPTURE_PORT],
&capability->width.max,
&capability->height.max);
}
if (!rc && (inst->prop.height[CAPTURE_PORT]
* inst->prop.width[CAPTURE_PORT] >
capability->width.max * capability->height.max)) {
dprintk(VIDC_ERR,
"Unsupported WxH = (%u)x(%u), Max supported is - (%u)x(%u)\n",
inst->prop.width[CAPTURE_PORT],
inst->prop.height[CAPTURE_PORT],
capability->width.max, capability->height.max);
rc = -ENOTSUPP;
}
}
if (rc) {
mutex_lock(&inst->sync_lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->sync_lock);
msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_HW_OVERLOAD);
dprintk(VIDC_WARN,
"%s: Hardware is overloaded\n", __func__);
wake_up(&inst->kernel_event_queue);
}
return rc;
}
static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core;
enum command_response cmd = SYS_ERROR;
struct msm_vidc_cb_cmd_done response = {0};
if (!inst || !inst->core) {
dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
return;
}
core = inst->core;
response.device_id = (u32) core->id;
handle_sys_error(cmd, (void *) &response);
}
int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst)
{
struct hfi_device *hdev;
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
return -EINVAL;
}
if (!inst->session || inst->state < MSM_VIDC_OPEN_DONE) {
dprintk(VIDC_WARN,
"No corresponding FW session. No need to send Abort");
return rc;
}
hdev = inst->core->device;
init_completion(&inst->completions[SESSION_MSG_INDEX
(SESSION_ABORT_DONE)]);
/* We have received session_error. Send session_abort to firmware
* to clean up and release the session
*/
rc = call_hfi_op(hdev, session_abort, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "session_abort failed rc: %d\n", rc);
return rc;
}
rc = wait_for_completion_timeout(
&inst->completions[SESSION_MSG_INDEX(SESSION_ABORT_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "%s: Wait interrupted or timeout[%u]: %d\n",
__func__, (u32)inst->session,
SESSION_MSG_INDEX(SESSION_ABORT_DONE));
msm_comm_generate_sys_error(inst);
} else
change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
return rc;
}
static inline int power_on_for_smem(struct msm_vidc_inst *inst)
{
struct hfi_device *hdev = NULL;
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s: invalid inst handle\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
rc = call_hfi_op(hdev, power_enable, hdev->hfi_device_data);
if (rc)
dprintk(VIDC_ERR, "%s: failed to power on fw\n", __func__);
return rc;
}
struct msm_smem *msm_comm_smem_alloc(struct msm_vidc_inst *inst,
size_t size, u32 align, u32 flags,
enum hal_buffer buffer_type, int map_kernel)
{
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid inst: %p\n", __func__, inst);
return NULL;
}
if (power_on_for_smem(inst))
return NULL;
return msm_smem_alloc(inst->mem_client, size, align,
flags, buffer_type, map_kernel);
}
void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem)
{
if (!inst || !mem) {
dprintk(VIDC_ERR,
"%s: invalid params: %p %p\n", __func__, inst, mem);
return;
}
if (power_on_for_smem(inst))
return;
return msm_smem_free(inst->mem_client, mem);
}
int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst,
struct msm_smem *mem, enum smem_cache_ops cache_ops)
{
if (!inst || !mem) {
dprintk(VIDC_ERR,
"%s: invalid params: %p %p\n", __func__, inst, mem);
return -EINVAL;
}
return msm_smem_cache_operations(inst->mem_client, mem, cache_ops);
}
struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst,
int fd, u32 offset, enum hal_buffer buffer_type)
{
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid inst: %p\n", __func__, inst);
return NULL;
}
if (power_on_for_smem(inst))
return NULL;
return msm_smem_user_to_kernel(inst->mem_client,
fd, offset, buffer_type);
}
int msm_comm_smem_get_domain_partition(struct msm_vidc_inst *inst,
u32 flags, enum hal_buffer buffer_type,
int *domain_num, int *partition_num)
{
if (!inst || !domain_num || !partition_num) {
dprintk(VIDC_ERR, "%s: invalid params: %p %p %p\n",
__func__, inst, domain_num, partition_num);
return -EINVAL;
}
return msm_smem_get_domain_partition(inst->mem_client, flags,
buffer_type, domain_num, partition_num);
}