From: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx> This implements vidc wrapper functions for all v4l2 IOCTLs. Signed-off-by: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx> Signed-off-by: Vikash Garodia <quic_vgarodia@xxxxxxxxxxx> --- .../media/platform/qcom/iris/vidc/inc/msm_vidc.h | 60 ++ .../media/platform/qcom/iris/vidc/src/msm_vidc.c | 841 +++++++++++++++++++++ 2 files changed, 901 insertions(+) create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h new file mode 100644 index 0000000..6cd5fad --- /dev/null +++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _MSM_VIDC_H_ +#define _MSM_VIDC_H_ + +#include <linux/videodev2.h> +#include <media/media-device.h> + +struct msm_vidc_core; +struct msm_vidc_inst; + +union msm_v4l2_cmd { + struct v4l2_decoder_cmd dec; + struct v4l2_encoder_cmd enc; +}; + +void *msm_vidc_open(struct msm_vidc_core *core, u32 session_type); +int msm_vidc_close(struct msm_vidc_inst *inst); +int msm_vidc_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap); +int msm_vidc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f); +int msm_vidc_try_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f); +int msm_vidc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f); +int msm_vidc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f); +int msm_vidc_s_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s); +int msm_vidc_g_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s); +int msm_vidc_s_param(struct msm_vidc_inst *inst, struct v4l2_streamparm *sp); +int msm_vidc_g_param(struct msm_vidc_inst *inst, struct v4l2_streamparm *sp); +int msm_vidc_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b); +int msm_vidc_querybuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_vidc_create_bufs(struct msm_vidc_inst *inst, struct v4l2_create_buffers *b); +int msm_vidc_prepare_buf(struct msm_vidc_inst *inst, struct media_device *mdev, + struct v4l2_buffer *b); +int msm_vidc_release_buffer(struct msm_vidc_inst *inst, int buffer_type, + unsigned int buffer_index); +int msm_vidc_qbuf(struct msm_vidc_inst *inst, struct media_device *mdev, + struct v4l2_buffer *b); +int msm_vidc_dqbuf(struct msm_vidc_inst *inste, struct v4l2_buffer *b); +int msm_vidc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i); +int msm_vidc_query_ctrl(struct msm_vidc_inst *inst, struct v4l2_queryctrl *ctrl); +int msm_vidc_query_menu(struct msm_vidc_inst *inst, struct v4l2_querymenu *qmenu); +int msm_vidc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i); +int msm_vidc_try_cmd(struct msm_vidc_inst *inst, union msm_v4l2_cmd *cmd); +int msm_vidc_start_cmd(struct msm_vidc_inst *inst); +int msm_vidc_stop_cmd(struct msm_vidc_inst *inst); +int msm_vidc_poll(struct msm_vidc_inst *inst, struct file *filp, + struct poll_table_struct *pt); +int msm_vidc_subscribe_event(struct msm_vidc_inst *inst, + const struct v4l2_event_subscription *sub); +int msm_vidc_unsubscribe_event(struct msm_vidc_inst *inst, + const struct v4l2_event_subscription *sub); +int msm_vidc_dqevent(struct msm_vidc_inst *inst, struct v4l2_event *event); +int msm_vidc_g_crop(struct msm_vidc_inst *inst, struct v4l2_crop *a); +int msm_vidc_enum_framesizes(struct msm_vidc_inst *inst, struct v4l2_frmsizeenum *fsize); +int msm_vidc_enum_frameintervals(struct msm_vidc_inst *inst, struct v4l2_frmivalenum *fival); + +#endif diff --git a/drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c new file mode 100644 index 0000000..c9848c7 --- /dev/null +++ b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c @@ -0,0 +1,841 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/hash.h> +#include <linux/types.h> + +#include "msm_vdec.h" +#include "msm_venc.h" +#include "msm_vidc.h" +#include "msm_vidc_control.h" +#include "msm_vidc_core.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_driver.h" +#include "msm_vidc_inst.h" +#include "msm_vidc_internal.h" +#include "msm_vidc_memory.h" +#include "msm_vidc_power.h" +#include "msm_vidc_v4l2.h" +#include "msm_vidc_vb2.h" +#include "venus_hfi_response.h" + +#define MSM_VIDC_DRV_NAME "msm_vidc_driver" +#define MSM_VIDC_BUS_NAME "platform:msm_vidc_bus" + +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; + + 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 (port >= MAX_PORT) { + d_vpr_e("%s: invalid params, inst %pK, port %d\n", + __func__, inst, port); + return poll; + } + q = inst->bufq[port].vb2q; + + 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) + poll |= POLLIN | POLLRDNORM; + else if (port == INPUT_PORT) + poll |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&q->done_lock, flags); + + return poll; +} + +int msm_vidc_poll(struct msm_vidc_inst *inst, struct file *filp, + struct poll_table_struct *wait) +{ + int poll = 0; + + poll_wait(filp, &inst->fh.wait, wait); + poll_wait(filp, &inst->bufq[INPUT_PORT].vb2q->done_wq, wait); + poll_wait(filp, &inst->bufq[OUTPUT_PORT].vb2q->done_wq, wait); + + if (v4l2_event_pending(&inst->fh)) + poll |= POLLPRI; + + poll |= get_poll_flags(inst, INPUT_PORT); + poll |= get_poll_flags(inst, OUTPUT_PORT); + + return poll; +} + +int msm_vidc_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap) +{ + strscpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->bus_info, MSM_VIDC_BUS_NAME, sizeof(cap->bus_info)); + + memset(cap->reserved, 0, sizeof(cap->reserved)); + + if (is_decode_session(inst)) + strscpy(cap->card, "msm_vidc_decoder", sizeof(cap->card)); + else if (is_encode_session(inst)) + strscpy(cap->card, "msm_vidc_encoder", sizeof(cap->card)); + else + return -EINVAL; + + return 0; +} + +int msm_vidc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f) +{ + if (is_decode_session(inst)) + return msm_vdec_enum_fmt(inst, f); + if (is_encode_session(inst)) + return msm_venc_enum_fmt(inst, f); + + return -EINVAL; +} + +int msm_vidc_query_ctrl(struct msm_vidc_inst *inst, struct v4l2_queryctrl *q_ctrl) +{ + int rc = 0; + struct v4l2_ctrl *ctrl; + + 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; +} + +int msm_vidc_query_menu(struct msm_vidc_inst *inst, struct v4l2_querymenu *qmenu) +{ + int rc = 0; + struct v4l2_ctrl *ctrl; + + 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 %lld, max %lld, menu_skip_mask %lld, qmenu: id %u, 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; +} + +int msm_vidc_try_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + int rc = 0; + + if (is_decode_session(inst)) + rc = msm_vdec_try_fmt(inst, f); + if (is_encode_session(inst)) + 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; +} + +int msm_vidc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + int rc = 0; + + if (is_decode_session(inst)) + rc = msm_vdec_s_fmt(inst, f); + if (is_encode_session(inst)) + 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; +} + +int msm_vidc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + int rc = 0; + + 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; + + 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(inst, f->fmt.pix_mp.pixelformat), + f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.plane_fmt[0].sizeimage); + + return 0; +} + +int msm_vidc_s_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s) +{ + int rc = 0; + + 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; +} + +int msm_vidc_g_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s) +{ + int rc = 0; + + 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; +} + +int msm_vidc_s_param(struct msm_vidc_inst *inst, struct v4l2_streamparm *param) +{ + int rc = 0; + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + if (is_encode_session(inst)) { + rc = msm_venc_s_param(inst, param); + } else { + i_vpr_e(inst, "%s: invalid domain %#x\n", + __func__, inst->domain); + return -EINVAL; + } + + return rc; +} + +int msm_vidc_g_param(struct msm_vidc_inst *inst, struct v4l2_streamparm *param) +{ + int rc = 0; + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + param->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + if (is_encode_session(inst)) { + rc = msm_venc_g_param(inst, param); + } else { + i_vpr_e(inst, "%s: invalid domain %#x\n", + __func__, inst->domain); + return -EINVAL; + } + + return rc; +} + +int msm_vidc_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b) +{ + int rc = 0; + int port; + + port = v4l2_type_to_driver_port(inst, b->type, __func__); + if (port < 0) { + rc = -EINVAL; + goto exit; + } + + rc = vb2_reqbufs(inst->bufq[port].vb2q, b); + if (rc) { + i_vpr_e(inst, "%s: vb2_reqbufs(%d) failed, %d\n", + __func__, b->type, rc); + goto exit; + } + +exit: + return rc; +} + +int msm_vidc_querybuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + int rc = 0; + int port; + + port = v4l2_type_to_driver_port(inst, b->type, __func__); + if (port < 0) { + rc = -EINVAL; + goto exit; + } + + rc = vb2_querybuf(inst->bufq[port].vb2q, b); + if (rc) { + i_vpr_e(inst, "%s: vb2_querybuf(%d) failed, %d\n", + __func__, b->type, rc); + goto exit; + } + +exit: + return rc; +} + +int msm_vidc_create_bufs(struct msm_vidc_inst *inst, struct v4l2_create_buffers *b) +{ + int rc = 0; + int port; + struct v4l2_format *f; + + f = &b->format; + port = v4l2_type_to_driver_port(inst, f->type, __func__); + if (port < 0) { + rc = -EINVAL; + goto exit; + } + + rc = vb2_create_bufs(inst->bufq[port].vb2q, b); + if (rc) { + i_vpr_e(inst, "%s: vb2_create_bufs(%d) failed, %d\n", + __func__, f->type, rc); + goto exit; + } + +exit: + return rc; +} + +int msm_vidc_prepare_buf(struct msm_vidc_inst *inst, struct media_device *mdev, + struct v4l2_buffer *b) +{ + int rc = 0; + struct vb2_queue *q; + + if (!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_prepare_buf(q, mdev, b); + if (rc) { + i_vpr_e(inst, "%s: failed with %d\n", __func__, rc); + goto exit; + } + +exit: + return rc; +} + +int msm_vidc_qbuf(struct msm_vidc_inst *inst, struct media_device *mdev, + struct v4l2_buffer *b) +{ + int rc = 0; + struct vb2_queue *q; + + if (!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_qbuf(q, mdev, b); + if (rc) + i_vpr_e(inst, "%s: failed with %d\n", __func__, rc); + +exit: + return rc; +} + +int msm_vidc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + int rc = 0; + struct vb2_queue *q; + + if (!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; +} + +int msm_vidc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type type) +{ + int rc = 0; + int port; + + port = v4l2_type_to_driver_port(inst, type, __func__); + if (port < 0) { + rc = -EINVAL; + goto exit; + } + + rc = vb2_streamon(inst->bufq[port].vb2q, type); + if (rc) { + i_vpr_e(inst, "%s: vb2_streamon(%d) failed, %d\n", + __func__, type, rc); + goto exit; + } + +exit: + return rc; +} +EXPORT_SYMBOL(msm_vidc_streamon); + +int msm_vidc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type type) +{ + int rc = 0; + int port; + + port = v4l2_type_to_driver_port(inst, type, __func__); + if (port < 0) { + rc = -EINVAL; + goto exit; + } + + rc = vb2_streamoff(inst->bufq[port].vb2q, type); + if (rc) { + i_vpr_e(inst, "%s: vb2_streamoff(%d) failed, %d\n", + __func__, type, rc); + goto exit; + } + +exit: + return rc; +} + +int msm_vidc_try_cmd(struct msm_vidc_inst *inst, union msm_v4l2_cmd *cmd) +{ + int rc = 0; + 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; +} + +int msm_vidc_start_cmd(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!is_decode_session(inst) && !is_encode_session(inst)) { + i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain); + return -EINVAL; + } + + if (is_decode_session(inst)) { + rc = msm_vdec_start_cmd(inst); + if (rc) + return rc; + } else if (is_encode_session(inst)) { + rc = msm_venc_start_cmd(inst); + if (rc) + return rc; + } + + return rc; +} + +int msm_vidc_stop_cmd(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!is_decode_session(inst) && !is_encode_session(inst)) { + i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain); + return -EINVAL; + } + + if (is_decode_session(inst)) { + rc = msm_vdec_stop_cmd(inst); + if (rc) + return rc; + } else if (is_encode_session(inst)) { + rc = msm_venc_stop_cmd(inst); + if (rc) + return rc; + } + + return rc; +} + +int msm_vidc_enum_framesizes(struct msm_vidc_inst *inst, struct v4l2_frmsizeenum *fsize) +{ + enum msm_vidc_colorformat_type colorfmt; + enum msm_vidc_codec_type codec; + + /* only index 0 allowed as per v4l2 spec */ + if (fsize->index) + return -EINVAL; + + /* validate pixel format */ + codec = v4l2_codec_to_driver(inst, fsize->pixel_format, __func__); + if (!codec) { + colorfmt = v4l2_colorformat_to_driver(inst, 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 = inst->capabilities[FRAME_WIDTH].min; + fsize->stepwise.max_width = inst->capabilities[FRAME_WIDTH].max; + fsize->stepwise.step_width = + inst->capabilities[FRAME_WIDTH].step_or_mask; + fsize->stepwise.min_height = inst->capabilities[FRAME_HEIGHT].min; + fsize->stepwise.max_height = inst->capabilities[FRAME_HEIGHT].max; + fsize->stepwise.step_height = + inst->capabilities[FRAME_HEIGHT].step_or_mask; + + return 0; +} + +int msm_vidc_enum_frameintervals(struct msm_vidc_inst *inst, struct v4l2_frmivalenum *fival) +{ + struct msm_vidc_core *core; + enum msm_vidc_colorformat_type colorfmt; + u32 fps, mbpf; + + if (is_decode_session(inst)) { + i_vpr_e(inst, "%s: not supported by decoder\n", __func__); + return -ENOTTY; + } + + core = inst->core; + + /* only index 0 allowed as per v4l2 spec */ + if (fival->index) + return -EINVAL; + + /* validate pixel format */ + colorfmt = v4l2_colorformat_to_driver(inst, 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 > inst->capabilities[FRAME_WIDTH].max || + fival->width < inst->capabilities[FRAME_WIDTH].min || + fival->height > inst->capabilities[FRAME_HEIGHT].max || + fival->height < inst->capabilities[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, inst->capabilities[FRAME_RATE].max); + fival->stepwise.max.numerator = 1; + fival->stepwise.max.denominator = 1; + fival->stepwise.step.numerator = 1; + fival->stepwise.step.denominator = inst->capabilities[FRAME_RATE].max; + + return 0; +} + +int msm_vidc_subscribe_event(struct msm_vidc_inst *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + + i_vpr_h(inst, "%s: type %d id %d\n", __func__, sub->type, sub->id); + + if (is_decode_session(inst)) + rc = msm_vdec_subscribe_event(inst, sub); + if (is_encode_session(inst)) + rc = msm_venc_subscribe_event(inst, sub); + + return rc; +} + +int msm_vidc_unsubscribe_event(struct msm_vidc_inst *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + + i_vpr_h(inst, "%s: type %d id %d\n", __func__, sub->type, sub->id); + rc = v4l2_event_unsubscribe(&inst->fh, sub); + if (rc) + i_vpr_e(inst, "%s: failed, type %d id %d\n", + __func__, sub->type, sub->id); + return rc; +} + +int msm_vidc_dqevent(struct msm_vidc_inst *inst, struct v4l2_event *event) +{ + int rc = 0; + + rc = v4l2_event_dequeue(&inst->fh, event, false); + if (rc) + i_vpr_e(inst, "%s: fialed\n", __func__); + return rc; +} + +void *msm_vidc_open(struct msm_vidc_core *core, u32 session_type) +{ + int rc = 0; + struct msm_vidc_inst *inst = NULL; + int i = 0; + + d_vpr_h("%s: %s\n", __func__, video_banner); + + 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; + + rc = msm_vidc_core_init_wait(core); + if (rc) + return NULL; + + inst = vzalloc(sizeof(*inst)); + if (!inst) { + d_vpr_e("%s: allocation failed\n", __func__); + return NULL; + } + + inst->core = core; + inst->domain = session_type; + inst->session_id = hash32_ptr(inst); + msm_vidc_update_state(inst, MSM_VIDC_OPEN, __func__); + inst->sub_state = MSM_VIDC_SUB_STATE_NONE; + strscpy(inst->sub_state_name, "SUB_STATE_NONE", sizeof(inst->sub_state_name)); + inst->active = true; + inst->ipsc_properties_set = false; + inst->opsc_properties_set = false; + inst->caps_list_prepared = false; + inst->has_bframe = false; + inst->iframe = false; + inst->initial_time_us = ktime_get_ns() / 1000; + kref_init(&inst->kref); + mutex_init(&inst->lock); + mutex_init(&inst->ctx_q_lock); + mutex_init(&inst->client_lock); + msm_vidc_update_debug_str(inst); + i_vpr_h(inst, "Opening video instance: %d\n", session_type); + + rc = msm_vidc_add_session(inst); + if (rc) { + i_vpr_e(inst, "%s: failed to add session\n", __func__); + goto fail_add_session; + } + + rc = msm_vidc_pools_init(inst); + if (rc) { + i_vpr_e(inst, "%s: failed to init pool buffers\n", __func__); + goto fail_pools_init; + } + INIT_LIST_HEAD(&inst->caps_list); + INIT_LIST_HEAD(&inst->timestamps.list); + INIT_LIST_HEAD(&inst->buffers.input.list); + INIT_LIST_HEAD(&inst->buffers.output.list); + INIT_LIST_HEAD(&inst->buffers.read_only.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->mem_info.bin.list); + INIT_LIST_HEAD(&inst->mem_info.arp.list); + INIT_LIST_HEAD(&inst->mem_info.comv.list); + INIT_LIST_HEAD(&inst->mem_info.non_comv.list); + INIT_LIST_HEAD(&inst->mem_info.line.list); + INIT_LIST_HEAD(&inst->mem_info.dpb.list); + INIT_LIST_HEAD(&inst->mem_info.persist.list); + INIT_LIST_HEAD(&inst->mem_info.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); + INIT_LIST_HEAD(&inst->input_timer_list); + INIT_LIST_HEAD(&inst->buffer_stats_list); + for (i = 0; i < MAX_SIGNAL; i++) + init_completion(&inst->completions[i]); + + inst->workq = create_singlethread_workqueue("workq"); + if (!inst->workq) { + i_vpr_e(inst, "%s: create workq failed\n", __func__); + goto fail_create_workq; + } + + INIT_DELAYED_WORK(&inst->stats_work, msm_vidc_stats_handler); + + rc = msm_vidc_v4l2_fh_init(inst); + if (rc) + goto fail_eventq_init; + + rc = msm_vidc_vb2_queue_init(inst); + if (rc) + goto fail_vb2q_init; + + 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 fail_inst_init; + + msm_vidc_scale_power(inst, true); + + rc = msm_vidc_session_open(inst); + if (rc) { + msm_vidc_core_deinit(core, true); + goto fail_session_open; + } + + 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; + +fail_session_open: + if (is_decode_session(inst)) + msm_vdec_inst_deinit(inst); + else if (is_encode_session(inst)) + msm_venc_inst_deinit(inst); +fail_inst_init: + msm_vidc_vb2_queue_deinit(inst); +fail_vb2q_init: + msm_vidc_v4l2_fh_deinit(inst); +fail_eventq_init: + destroy_workqueue(inst->workq); +fail_create_workq: + msm_vidc_pools_deinit(inst); +fail_pools_init: + msm_vidc_remove_session(inst); + msm_vidc_remove_dangling_session(inst); +fail_add_session: + mutex_destroy(&inst->client_lock); + mutex_destroy(&inst->ctx_q_lock); + mutex_destroy(&inst->lock); + vfree(inst); + return NULL; +} + +int msm_vidc_close(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + + core = inst->core; + + client_lock(inst, __func__); + inst_lock(inst, __func__); + /* print final stats */ + msm_vidc_print_stats(inst); + /* print internal buffer memory usage stats */ + msm_vidc_print_memory_stats(inst); + msm_vidc_session_close(inst); + msm_vidc_change_state(inst, MSM_VIDC_CLOSE, __func__); + inst->sub_state = MSM_VIDC_SUB_STATE_NONE; + strscpy(inst->sub_state_name, "SUB_STATE_NONE", sizeof(inst->sub_state_name)); + inst_unlock(inst, __func__); + client_unlock(inst, __func__); + cancel_stats_work_sync(inst); + put_inst(inst); + msm_vidc_schedule_core_deinit(core); + + return 0; +} -- 2.7.4