Файли
android_kernel_samsung_sm86…/driver/vidc/src/msm_vidc.c
Govindaraj Rajagopal 58a111c212 video: driver: fix invalid ptr dereference issue with struct v4l2_buffer
qbuf call for actual bitstream and yuv buffer 'struct v4l2_buffer'
length field indicates numplanes and for metadata buffers, length field
indicates capacity. So always check length field before accessing/
dereferencing b->m.planes or b->m.fd.

Change-Id: I409b28e0a66bd5c031b2c98c6d7614da99164a6c
Signed-off-by: Govindaraj Rajagopal <quic_grajagop@quicinc.com>
2022-01-13 17:29:22 +05:30

971 рядки
24 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/types.h>
#include <linux/hash.h>
#include "msm_vidc_core.h"
#include "msm_vidc_inst.h"
#include "msm_vdec.h"
#include "msm_venc.h"
#include "msm_vidc_internal.h"
#include "msm_vidc_driver.h"
#include "msm_vidc_vb2.h"
#include "msm_vidc_v4l2.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_control.h"
#include "msm_vidc_power.h"
#include "msm_vidc_memory.h"
#include "venus_hfi_response.h"
#include "msm_vidc.h"
#define MSM_VIDC_DRV_NAME "msm_vidc_driver"
#define MSM_VIDC_BUS_NAME "platform:msm_vidc_bus"
/* kernel/msm-4.19 */
#define MSM_VIDC_VERSION ((5 << 16) + (10 << 8) + 0)
#define MAX_EVENTS 30
static inline bool valid_v4l2_buffer(struct v4l2_buffer *b,
struct msm_vidc_inst *inst)
{
if (b->type == INPUT_MPLANE || b->type == OUTPUT_MPLANE)
return b->length > 0;
else if (b->type == INPUT_META_PLANE || b->type == OUTPUT_META_PLANE)
return true;
return false;
}
static int get_poll_flags(struct msm_vidc_inst *inst, u32 port)
{
int poll = 0;
struct vb2_queue *q = NULL;
struct vb2_buffer *vb = NULL;
unsigned long flags = 0;
if (!inst || port >= MAX_PORT) {
d_vpr_e("%s: invalid params, inst %pK, port %d\n",
__func__, inst, port);
return poll;
}
q = &inst->vb2q[port];
spin_lock_irqsave(&q->done_lock, flags);
if (!list_empty(&q->done_list))
vb = list_first_entry(&q->done_list, struct vb2_buffer,
done_entry);
if (vb && (vb->state == VB2_BUF_STATE_DONE ||
vb->state == VB2_BUF_STATE_ERROR)) {
if (port == OUTPUT_PORT || port == OUTPUT_META_PORT)
poll |= POLLIN | POLLRDNORM;
else if (port == INPUT_PORT || port == INPUT_META_PORT)
poll |= POLLOUT | POLLWRNORM;
}
spin_unlock_irqrestore(&q->done_lock, flags);
return poll;
}
int msm_vidc_poll(void *instance, struct file *filp,
struct poll_table_struct *wait)
{
int poll = 0;
struct msm_vidc_inst *inst = instance;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return POLLERR;
}
if (is_session_error(inst)) {
i_vpr_e(inst, "%s: inst in error state\n", __func__);
return POLLERR;
}
poll_wait(filp, &inst->event_handler.wait, wait);
poll_wait(filp, &inst->vb2q[INPUT_META_PORT].done_wq, wait);
poll_wait(filp, &inst->vb2q[OUTPUT_META_PORT].done_wq, wait);
poll_wait(filp, &inst->vb2q[INPUT_PORT].done_wq, wait);
poll_wait(filp, &inst->vb2q[OUTPUT_PORT].done_wq, wait);
if (v4l2_event_pending(&inst->event_handler))
poll |= POLLPRI;
poll |= get_poll_flags(inst, INPUT_META_PORT);
poll |= get_poll_flags(inst, OUTPUT_META_PORT);
poll |= get_poll_flags(inst, INPUT_PORT);
poll |= get_poll_flags(inst, OUTPUT_PORT);
return poll;
}
EXPORT_SYMBOL(msm_vidc_poll);
int msm_vidc_querycap(void *instance, struct v4l2_capability *cap)
{
struct msm_vidc_inst *inst = instance;
if (!inst || !cap) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver));
strlcpy(cap->bus_info, MSM_VIDC_BUS_NAME, sizeof(cap->bus_info));
cap->version = MSM_VIDC_VERSION;
memset(cap->reserved, 0, sizeof(cap->reserved));
if (inst->domain == MSM_VIDC_DECODER)
strlcpy(cap->card, "msm_vidc_decoder", sizeof(cap->card));
else if (inst->domain == MSM_VIDC_ENCODER)
strlcpy(cap->card, "msm_vidc_encoder", sizeof(cap->card));
else
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(msm_vidc_querycap);
int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
{
struct msm_vidc_inst *inst = instance;
if (!inst || !f) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (inst->domain == MSM_VIDC_DECODER)
return msm_vdec_enum_fmt(inst, f);
if (inst->domain == MSM_VIDC_ENCODER)
return msm_venc_enum_fmt(inst, f);
return -EINVAL;
}
EXPORT_SYMBOL(msm_vidc_enum_fmt);
int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *q_ctrl)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
struct v4l2_ctrl *ctrl;
if (!inst || !q_ctrl) {
d_vpr_e("%s: invalid params %pK %pK\n",
__func__, inst, q_ctrl);
return -EINVAL;
}
ctrl = v4l2_ctrl_find(&inst->ctrl_handler, q_ctrl->id);
if (!ctrl) {
i_vpr_e(inst, "%s: get_ctrl failed for id %d\n",
__func__, q_ctrl->id);
return -EINVAL;
}
q_ctrl->minimum = ctrl->minimum;
q_ctrl->maximum = ctrl->maximum;
q_ctrl->default_value = ctrl->default_value;
q_ctrl->flags = 0;
q_ctrl->step = ctrl->step;
i_vpr_h(inst,
"query ctrl: %s: min %d, max %d, default %d step %d flags %#x\n",
ctrl->name, q_ctrl->minimum, q_ctrl->maximum,
q_ctrl->default_value, q_ctrl->step, q_ctrl->flags);
return rc;
}
EXPORT_SYMBOL(msm_vidc_query_ctrl);
int msm_vidc_query_menu(void *instance, struct v4l2_querymenu *qmenu)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
struct v4l2_ctrl *ctrl;
if (!inst || !qmenu) {
d_vpr_e("%s: invalid params %pK %pK\n",
__func__, inst, qmenu);
return -EINVAL;
}
ctrl = v4l2_ctrl_find(&inst->ctrl_handler, qmenu->id);
if (!ctrl) {
i_vpr_e(inst, "%s: get_ctrl failed for id %d\n",
__func__, qmenu->id);
return -EINVAL;
}
if (ctrl->type != V4L2_CTRL_TYPE_MENU) {
i_vpr_e(inst, "%s: ctrl: %s: type (%d) is not MENU type\n",
__func__, ctrl->name, ctrl->type);
return -EINVAL;
}
if (qmenu->index < ctrl->minimum || qmenu->index > ctrl->maximum)
return -EINVAL;
if (ctrl->menu_skip_mask & (1 << qmenu->index))
rc = -EINVAL;
i_vpr_h(inst,
"%s: ctrl: %s: min %d, max %d, menu_skip_mask %#x, qmenu: id %d, index %d, %s\n",
__func__, ctrl->name, ctrl->minimum, ctrl->maximum,
ctrl->menu_skip_mask, qmenu->id, qmenu->index,
rc ? "not supported" : "supported");
return rc;
}
EXPORT_SYMBOL(msm_vidc_query_menu);
int msm_vidc_try_fmt(void *instance, struct v4l2_format *f)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
if (!inst || !f) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (!msm_vidc_allow_s_fmt(inst, f->type))
return -EBUSY;
if (inst->domain == MSM_VIDC_DECODER)
rc = msm_vdec_try_fmt(inst, f);
if (inst->domain == MSM_VIDC_ENCODER)
rc = msm_venc_try_fmt(inst, f);
if (rc)
i_vpr_e(inst, "%s: try_fmt(%d) failed %d\n",
__func__, f->type, rc);
return rc;
}
EXPORT_SYMBOL(msm_vidc_try_fmt);
int msm_vidc_s_fmt(void *instance, struct v4l2_format *f)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
if (!inst || !f) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (!msm_vidc_allow_s_fmt(inst, f->type))
return -EBUSY;
if (inst->domain == MSM_VIDC_DECODER)
rc = msm_vdec_s_fmt(inst, f);
if (inst->domain == MSM_VIDC_ENCODER)
rc = msm_venc_s_fmt(inst, f);
if (rc)
i_vpr_e(inst, "%s: s_fmt(%d) failed %d\n",
__func__, f->type, rc);
return rc;
}
EXPORT_SYMBOL(msm_vidc_s_fmt);
int msm_vidc_g_fmt(void *instance, struct v4l2_format *f)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
if (!inst || !f) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (is_decode_session(inst))
rc = msm_vdec_g_fmt(inst, f);
if (is_encode_session(inst))
rc = msm_venc_g_fmt(inst, f);
if (rc)
return rc;
if (f->type == INPUT_MPLANE || f->type == OUTPUT_MPLANE)
i_vpr_h(inst, "%s: type %s format %s width %d height %d size %d\n",
__func__, v4l2_type_name(f->type),
v4l2_pixelfmt_name(f->fmt.pix_mp.pixelformat),
f->fmt.pix_mp.width, f->fmt.pix_mp.height,
f->fmt.pix_mp.plane_fmt[0].sizeimage);
else if (f->type == INPUT_META_PLANE || f->type == OUTPUT_META_PLANE)
i_vpr_h(inst, "%s: type %s size %d\n",
__func__, v4l2_type_name(f->type), f->fmt.meta.buffersize);
return 0;
}
EXPORT_SYMBOL(msm_vidc_g_fmt);
int msm_vidc_s_selection(void *instance, struct v4l2_selection *s)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
if (!inst || !s) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (is_decode_session(inst))
rc = msm_vdec_s_selection(inst, s);
if (is_encode_session(inst))
rc = msm_venc_s_selection(inst, s);
return rc;
}
EXPORT_SYMBOL(msm_vidc_s_selection);
int msm_vidc_g_selection(void *instance, struct v4l2_selection *s)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
if (!inst || !s) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (is_decode_session(inst))
rc = msm_vdec_g_selection(inst, s);
if (is_encode_session(inst))
rc = msm_venc_g_selection(inst, s);
return rc;
}
EXPORT_SYMBOL(msm_vidc_g_selection);
int msm_vidc_s_param(void *instance, struct v4l2_streamparm *param)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
if (!inst || !param) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return -EINVAL;
if (is_decode_session(inst))
rc = msm_vdec_s_param(instance, param);
else if (is_encode_session(inst))
rc = msm_venc_s_param(instance, param);
return rc;
}
EXPORT_SYMBOL(msm_vidc_s_param);
int msm_vidc_g_param(void *instance, struct v4l2_streamparm *param)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
if (!inst || !param) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return -EINVAL;
if (is_decode_session(inst))
rc = msm_vdec_g_param(instance, param);
else if (is_encode_session(inst))
rc = msm_venc_g_param(instance, param);
return rc;
}
EXPORT_SYMBOL(msm_vidc_g_param);
int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
int port;
if (!inst || !b) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (!msm_vidc_allow_reqbufs(inst, b->type)) {
rc = -EBUSY;
goto exit;
}
port = v4l2_type_to_driver_port(inst, b->type, __func__);
if (port < 0) {
rc = -EINVAL;
goto exit;
}
rc = vb2_reqbufs(&inst->vb2q[port], b);
if (rc) {
i_vpr_e(inst, "%s: vb2_reqbufs(%d) failed, %d\n",
__func__, b->type, rc);
goto exit;
}
exit:
return rc;
}
EXPORT_SYMBOL(msm_vidc_reqbufs);
int msm_vidc_qbuf(void *instance, struct media_device *mdev,
struct v4l2_buffer *b)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
struct vb2_queue *q;
u64 timestamp_us = 0;
if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst)) {
d_vpr_e("%s: invalid params %pK %pK\n", __func__, inst, b);
return -EINVAL;
}
/* Expecting non-zero filledlen on INPUT port */
if (b->type == INPUT_MPLANE && !b->m.planes[0].bytesused) {
i_vpr_e(inst,
"%s: zero bytesused input buffer not supported\n", __func__);
return -EINVAL;
}
q = msm_vidc_get_vb2q(inst, b->type, __func__);
if (!q) {
rc = -EINVAL;
goto exit;
}
if (is_encode_session(inst) && b->type == INPUT_MPLANE) {
timestamp_us = (u64)((b->timestamp.tv_sec * USEC_PER_SEC) +
b->timestamp.tv_usec);
msm_vidc_set_auto_framerate(inst, timestamp_us);
}
inst->last_qbuf_time_ns = ktime_get_ns();
rc = vb2_qbuf(q, mdev, b);
if (rc)
i_vpr_e(inst, "%s: failed with %d\n", __func__, rc);
exit:
return rc;
}
EXPORT_SYMBOL(msm_vidc_qbuf);
int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
struct vb2_queue *q;
if (!inst || !b || !valid_v4l2_buffer(b, inst)) {
d_vpr_e("%s: invalid params %pK %pK\n", __func__, inst, b);
return -EINVAL;
}
q = msm_vidc_get_vb2q(inst, b->type, __func__);
if (!q) {
rc = -EINVAL;
goto exit;
}
rc = vb2_dqbuf(q, b, true);
if (rc == -EAGAIN) {
goto exit;
} else if (rc) {
i_vpr_l(inst, "%s: failed with %d\n", __func__, rc);
goto exit;
}
exit:
return rc;
}
EXPORT_SYMBOL(msm_vidc_dqbuf);
int msm_vidc_streamon(void *instance, enum v4l2_buf_type type)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
int port;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (!msm_vidc_allow_streamon(inst, type)) {
rc = -EBUSY;
goto exit;
}
rc = msm_vidc_state_change_streamon(inst, type);
if (rc)
goto exit;
port = v4l2_type_to_driver_port(inst, type, __func__);
if (port < 0) {
rc = -EINVAL;
goto exit;
}
rc = vb2_streamon(&inst->vb2q[port], type);
if (rc) {
i_vpr_e(inst, "%s: vb2_streamon(%d) failed, %d\n",
__func__, type, rc);
msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR, __func__);
goto exit;
}
exit:
return rc;
}
EXPORT_SYMBOL(msm_vidc_streamon);
int msm_vidc_streamoff(void *instance, enum v4l2_buf_type type)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
int port;
enum msm_vidc_allow allow;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
allow = msm_vidc_allow_streamoff(inst, type);
if (allow == MSM_VIDC_DISALLOW) {
rc = -EBUSY;
goto exit;
} else if (allow == MSM_VIDC_IGNORE) {
goto exit;
} else if (allow != MSM_VIDC_ALLOW) {
rc = -EINVAL;
goto exit;
}
rc = msm_vidc_state_change_streamoff(inst, type);
if (rc)
goto exit;
port = v4l2_type_to_driver_port(inst, type, __func__);
if (port < 0) {
rc = -EINVAL;
goto exit;
}
rc = vb2_streamoff(&inst->vb2q[port], type);
if (rc) {
i_vpr_e(inst, "%s: vb2_streamoff(%d) failed, %d\n",
__func__, type, rc);
msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR, __func__);
goto exit;
}
exit:
return rc;
}
EXPORT_SYMBOL(msm_vidc_streamoff);
int msm_vidc_try_cmd(void *instance, union msm_v4l2_cmd *cmd)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
struct v4l2_decoder_cmd *dec = NULL;
struct v4l2_encoder_cmd *enc = NULL;
if (is_decode_session(inst)) {
dec = (struct v4l2_decoder_cmd *)cmd;
i_vpr_h(inst, "%s: cmd %d\n", __func__, dec->cmd);
if (dec->cmd != V4L2_DEC_CMD_STOP && dec->cmd != V4L2_DEC_CMD_START)
return -EINVAL;
dec->flags = 0;
if (dec->cmd == V4L2_DEC_CMD_STOP) {
dec->stop.pts = 0;
} else if (dec->cmd == V4L2_DEC_CMD_START) {
dec->start.speed = 0;
dec->start.format = V4L2_DEC_START_FMT_NONE;
}
} else if (is_encode_session(inst)) {
enc = (struct v4l2_encoder_cmd *)cmd;
i_vpr_h(inst, "%s: cmd %d\n", __func__, enc->cmd);
if (enc->cmd != V4L2_ENC_CMD_STOP && enc->cmd != V4L2_ENC_CMD_START)
return -EINVAL;
enc->flags = 0;
}
return rc;
}
EXPORT_SYMBOL(msm_vidc_try_cmd);
int msm_vidc_cmd(void *instance, union msm_v4l2_cmd *cmd)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
struct v4l2_decoder_cmd *dec = NULL;
struct v4l2_encoder_cmd *enc = NULL;
if (is_decode_session(inst)) {
dec = (struct v4l2_decoder_cmd *)cmd;
rc = msm_vdec_process_cmd(inst, dec->cmd);
} else if (is_encode_session(inst)) {
enc = (struct v4l2_encoder_cmd *)cmd;
rc = msm_venc_process_cmd(inst, enc->cmd);
}
if (rc)
return rc;
return 0;
}
EXPORT_SYMBOL(msm_vidc_cmd);
int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize)
{
struct msm_vidc_inst *inst = instance;
struct msm_vidc_inst_capability *capability;
enum msm_vidc_colorformat_type colorfmt;
enum msm_vidc_codec_type codec;
if (!inst || !fsize) {
d_vpr_e("%s: invalid params: %pK %pK\n",
__func__, inst, fsize);
return -EINVAL;
}
if (!inst->capabilities) {
i_vpr_e(inst, "capabilities not available\n", __func__);
return -EINVAL;
}
capability = inst->capabilities;
/* only index 0 allowed as per v4l2 spec */
if (fsize->index)
return -EINVAL;
/* validate pixel format */
codec = v4l2_codec_to_driver(fsize->pixel_format, __func__);
if (!codec) {
colorfmt = v4l2_colorformat_to_driver(fsize->pixel_format, __func__);
if (colorfmt == MSM_VIDC_FMT_NONE) {
i_vpr_e(inst, "%s: unsupported pix fmt %#x\n", __func__, fsize->pixel_format);
return -EINVAL;
}
}
fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
fsize->stepwise.min_width = capability->cap[FRAME_WIDTH].min;
fsize->stepwise.max_width = capability->cap[FRAME_WIDTH].max;
fsize->stepwise.step_width =
capability->cap[FRAME_WIDTH].step_or_mask;
fsize->stepwise.min_height = capability->cap[FRAME_HEIGHT].min;
fsize->stepwise.max_height = capability->cap[FRAME_HEIGHT].max;
fsize->stepwise.step_height =
capability->cap[FRAME_HEIGHT].step_or_mask;
return 0;
}
EXPORT_SYMBOL(msm_vidc_enum_framesizes);
int msm_vidc_enum_frameintervals(void *instance, struct v4l2_frmivalenum *fival)
{
struct msm_vidc_inst *inst = instance;
struct msm_vidc_core *core;
struct msm_vidc_inst_capability *capability;
enum msm_vidc_colorformat_type colorfmt;
u32 fps, mbpf;
if (!inst || !fival) {
d_vpr_e("%s: invalid params: %pK %pK\n",
__func__, inst, fival);
return -EINVAL;
}
if (is_decode_session(inst)) {
i_vpr_e(inst, "%s: not supported by decoder\n", __func__);
return -ENOTTY;
}
core = inst->core;
if (!inst->capabilities || !core->capabilities) {
i_vpr_e(inst, "capabilities not available\n", __func__);
return -EINVAL;
}
capability = inst->capabilities;
/* only index 0 allowed as per v4l2 spec */
if (fival->index)
return -EINVAL;
/* validate pixel format */
colorfmt = v4l2_colorformat_to_driver(fival->pixel_format, __func__);
if (colorfmt == MSM_VIDC_FMT_NONE) {
i_vpr_e(inst, "%s: unsupported pix fmt %#x\n", __func__, fival->pixel_format);
return -EINVAL;
}
/* validate resolution */
if (fival->width > capability->cap[FRAME_WIDTH].max ||
fival->width < capability->cap[FRAME_WIDTH].min ||
fival->height > capability->cap[FRAME_HEIGHT].max ||
fival->height < capability->cap[FRAME_HEIGHT].min) {
i_vpr_e(inst, "%s: unsupported resolution %u x %u\n", __func__,
fival->width, fival->height);
return -EINVAL;
}
/* calculate max supported fps for a given resolution */
mbpf = NUM_MBS_PER_FRAME(fival->height, fival->width);
fps = core->capabilities[MAX_MBPS].value / mbpf;
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
fival->stepwise.min.numerator = 1;
fival->stepwise.min.denominator =
min_t(u32, fps, capability->cap[FRAME_RATE].max);
fival->stepwise.max.numerator = 1;
fival->stepwise.max.denominator = 1;
fival->stepwise.step.numerator = 1;
fival->stepwise.step.denominator = capability->cap[FRAME_RATE].max;
return 0;
}
EXPORT_SYMBOL(msm_vidc_enum_frameintervals);
int msm_vidc_subscribe_event(void *instance,
const struct v4l2_event_subscription *sub)
{
int rc = 0;
struct msm_vidc_inst *inst = (struct msm_vidc_inst *)instance;
if (!inst || !sub) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
i_vpr_h(inst, "%s: type %d id %d\n", __func__, sub->type, sub->id);
rc = v4l2_event_subscribe(&inst->event_handler,
sub, MAX_EVENTS, NULL);
if (rc)
i_vpr_e(inst, "%s: fialed, type %d id %d\n",
__func__, sub->type, sub->id);
return rc;
}
EXPORT_SYMBOL(msm_vidc_subscribe_event);
int msm_vidc_unsubscribe_event(void *instance,
const struct v4l2_event_subscription *sub)
{
int rc = 0;
struct msm_vidc_inst *inst = (struct msm_vidc_inst *)instance;
if (!inst || !sub) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
i_vpr_h(inst, "%s: type %d id %d\n", __func__, sub->type, sub->id);
rc = v4l2_event_unsubscribe(&inst->event_handler, sub);
if (rc)
i_vpr_e(inst, "%s: fialed, type %d id %d\n",
__func__, sub->type, sub->id);
return rc;
}
EXPORT_SYMBOL(msm_vidc_unsubscribe_event);
int msm_vidc_dqevent(void *instance, struct v4l2_event *event)
{
int rc = 0;
struct msm_vidc_inst *inst = (struct msm_vidc_inst *)instance;
if (!inst || !event) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
rc = v4l2_event_dequeue(&inst->event_handler, event, false);
if (rc)
i_vpr_e(inst, "%s: fialed\n", __func__);
return rc;
}
EXPORT_SYMBOL(msm_vidc_dqevent);
void *msm_vidc_open(void *vidc_core, u32 session_type)
{
int rc = 0;
struct msm_vidc_inst *inst;
struct msm_vidc_core *core;
int i = 0;
d_vpr_h("%s()\n", __func__);
core = vidc_core;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return NULL;
}
if (session_type != MSM_VIDC_DECODER &&
session_type != MSM_VIDC_ENCODER) {
d_vpr_e("%s: invalid session_type %d\n",
__func__, session_type);
return NULL;
}
rc = msm_vidc_core_init(core);
if (rc)
return NULL;
inst = kzalloc(sizeof(*inst), GFP_KERNEL);
if (!inst) {
d_vpr_e("%s: failed to allocate inst memory\n", __func__);
return NULL;
}
inst->core = core;
inst->domain = session_type;
inst->session_id = hash32_ptr(inst);
inst->state = MSM_VIDC_OPEN;
inst->active = true;
inst->request = false;
inst->ipsc_properties_set = false;
inst->opsc_properties_set = false;
inst->has_bframe = false;
inst->auto_framerate = DEFAULT_FPS << 16;
kref_init(&inst->kref);
mutex_init(&inst->lock);
msm_vidc_update_debug_str(inst);
i_vpr_h(inst, "Opening video instance: %d\n", session_type);
rc = msm_memory_pools_init(inst);
if (rc) {
i_vpr_e(inst, "%s: failed to init pool buffers\n", __func__);
kfree(inst);
return NULL;
}
INIT_LIST_HEAD(&inst->response_works);
INIT_LIST_HEAD(&inst->timestamps.list);
INIT_LIST_HEAD(&inst->buffers.input.list);
INIT_LIST_HEAD(&inst->buffers.input_meta.list);
INIT_LIST_HEAD(&inst->buffers.output.list);
INIT_LIST_HEAD(&inst->buffers.output_meta.list);
INIT_LIST_HEAD(&inst->buffers.read_only.list);
INIT_LIST_HEAD(&inst->buffers.release.list);
INIT_LIST_HEAD(&inst->buffers.bin.list);
INIT_LIST_HEAD(&inst->buffers.arp.list);
INIT_LIST_HEAD(&inst->buffers.comv.list);
INIT_LIST_HEAD(&inst->buffers.non_comv.list);
INIT_LIST_HEAD(&inst->buffers.line.list);
INIT_LIST_HEAD(&inst->buffers.dpb.list);
INIT_LIST_HEAD(&inst->buffers.persist.list);
INIT_LIST_HEAD(&inst->buffers.vpss.list);
INIT_LIST_HEAD(&inst->allocations.bin.list);
INIT_LIST_HEAD(&inst->allocations.arp.list);
INIT_LIST_HEAD(&inst->allocations.comv.list);
INIT_LIST_HEAD(&inst->allocations.non_comv.list);
INIT_LIST_HEAD(&inst->allocations.line.list);
INIT_LIST_HEAD(&inst->allocations.dpb.list);
INIT_LIST_HEAD(&inst->allocations.persist.list);
INIT_LIST_HEAD(&inst->allocations.vpss.list);
INIT_LIST_HEAD(&inst->mappings.input.list);
INIT_LIST_HEAD(&inst->mappings.input_meta.list);
INIT_LIST_HEAD(&inst->mappings.output.list);
INIT_LIST_HEAD(&inst->mappings.output_meta.list);
INIT_LIST_HEAD(&inst->mappings.bin.list);
INIT_LIST_HEAD(&inst->mappings.arp.list);
INIT_LIST_HEAD(&inst->mappings.comv.list);
INIT_LIST_HEAD(&inst->mappings.non_comv.list);
INIT_LIST_HEAD(&inst->mappings.line.list);
INIT_LIST_HEAD(&inst->mappings.dpb.list);
INIT_LIST_HEAD(&inst->mappings.persist.list);
INIT_LIST_HEAD(&inst->mappings.vpss.list);
INIT_LIST_HEAD(&inst->children.list);
INIT_LIST_HEAD(&inst->firmware.list);
INIT_LIST_HEAD(&inst->enc_input_crs);
INIT_LIST_HEAD(&inst->dmabuf_tracker);
for (i = 0; i < MAX_SIGNAL; i++)
init_completion(&inst->completions[i]);
inst->response_workq = create_singlethread_workqueue("response_workq");
if (!inst->response_workq) {
i_vpr_e(inst, "%s: create input_psc_workq failed\n", __func__);
goto error;
}
INIT_DELAYED_WORK(&inst->response_work, handle_session_response_work_handler);
INIT_DELAYED_WORK(&inst->stats_work, msm_vidc_stats_handler);
inst->capabilities = kzalloc(sizeof(struct msm_vidc_inst_capability), GFP_KERNEL);
if (!inst->capabilities) {
i_vpr_e(inst,
"%s: inst capability allocation failed\n", __func__);
goto error;
}
if (is_decode_session(inst))
rc = msm_vdec_inst_init(inst);
else if (is_encode_session(inst))
rc = msm_venc_inst_init(inst);
if (rc)
goto error;
rc = msm_vidc_vb2_queue_init(inst);
if (rc)
goto error;
rc = msm_vidc_event_queue_init(inst);
if (rc)
goto error;
rc = msm_vidc_add_session(inst);
if (rc) {
i_vpr_e(inst, "%s: failed to get session id\n", __func__);
goto error;
}
msm_vidc_scale_power(inst, true);
rc = msm_vidc_session_open(inst);
if (rc) {
msm_vidc_core_deinit(core, true);
goto error;
}
inst->debugfs_root =
msm_vidc_debugfs_init_inst(inst, core->debugfs_root);
if (!inst->debugfs_root)
i_vpr_h(inst, "%s: debugfs not available\n", __func__);
return inst;
error:
msm_vidc_close(inst);
return NULL;
}
EXPORT_SYMBOL(msm_vidc_open);
int msm_vidc_close(void *instance)
{
int rc = 0;
struct msm_vidc_inst *inst = instance;
struct msm_vidc_core *core;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
core = inst->core;
i_vpr_h(inst, "%s()\n", __func__);
inst_lock(inst, __func__);
cancel_response_work(inst);
/* print final stats */
msm_vidc_print_stats(inst);
msm_vidc_session_close(inst);
msm_vidc_remove_session(inst);
msm_vidc_destroy_buffers(inst);
inst_unlock(inst, __func__);
cancel_stats_work_sync(inst);
msm_vidc_show_stats(inst);
put_inst(inst);
msm_vidc_schedule_core_deinit(core);
return rc;
}
EXPORT_SYMBOL(msm_vidc_close);