[05/33] iris: vidc: add vb2 ops
Commit Message
From: Dikshita Agarwal <quic_dikshita@quicinc.com>
This implements vb2 ops for streaming modes for
alloc, free, map and unmap buffers.
Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
Signed-off-by: Vikash Garodia <quic_vgarodia@quicinc.com>
---
.../platform/qcom/iris/vidc/inc/msm_vidc_vb2.h | 39 ++
.../platform/qcom/iris/vidc/src/msm_vidc_vb2.c | 605 +++++++++++++++++++++
2 files changed, 644 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
Comments
Le vendredi 28 juillet 2023 à 18:53 +0530, Vikash Garodia a écrit :
> From: Dikshita Agarwal <quic_dikshita@quicinc.com>
>
> This implements vb2 ops for streaming modes for
> alloc, free, map and unmap buffers.
>
> Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
> Signed-off-by: Vikash Garodia <quic_vgarodia@quicinc.com>
> ---
> .../platform/qcom/iris/vidc/inc/msm_vidc_vb2.h | 39 ++
> .../platform/qcom/iris/vidc/src/msm_vidc_vb2.c | 605 +++++++++++++++++++++
> 2 files changed, 644 insertions(+)
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
>
> diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
> new file mode 100644
> index 0000000..12378ce
> --- /dev/null
> +++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
> @@ -0,0 +1,39 @@
> +/* 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_VB2_H_
> +#define _MSM_VIDC_VB2_H_
> +
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-v4l2.h>
> +
> +#include "msm_vidc_inst.h"
> +
> +struct vb2_queue *msm_vidc_get_vb2q(struct msm_vidc_inst *inst,
> + u32 type, const char *func);
> +
> +/* vb2_mem_ops */
> +void *msm_vb2_alloc(struct vb2_buffer *vb, struct device *dev,
> + unsigned long size);
> +void *msm_vb2_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
> + struct dma_buf *dbuf, unsigned long size);
> +
> +void msm_vb2_put(void *buf_priv);
> +int msm_vb2_mmap(void *buf_priv, struct vm_area_struct *vma);
> +void msm_vb2_detach_dmabuf(void *buf_priv);
> +int msm_vb2_map_dmabuf(void *buf_priv);
> +void msm_vb2_unmap_dmabuf(void *buf_priv);
> +
> +/* vb2_ops */
> +int msm_vb2_queue_setup(struct vb2_queue *q,
> + unsigned int *num_buffers, unsigned int *num_planes,
> + unsigned int sizes[], struct device *alloc_devs[]);
> +int msm_vidc_start_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q);
> +int msm_vidc_stop_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q);
> +int msm_vb2_start_streaming(struct vb2_queue *q, unsigned int count);
> +void msm_vb2_stop_streaming(struct vb2_queue *q);
> +void msm_vb2_buf_queue(struct vb2_buffer *vb2);
> +#endif // _MSM_VIDC_VB2_H_
> diff --git a/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
> new file mode 100644
> index 0000000..c936d95
> --- /dev/null
> +++ b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
> @@ -0,0 +1,605 @@
> +// 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 "msm_vdec.h"
> +#include "msm_venc.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_platform.h"
> +#include "msm_vidc_power.h"
> +#include "msm_vidc_vb2.h"
> +
> +struct vb2_queue *msm_vidc_get_vb2q(struct msm_vidc_inst *inst,
> + u32 type, const char *func)
> +{
> + struct vb2_queue *q = NULL;
> +
> + if (type == INPUT_MPLANE) {
> + q = inst->bufq[INPUT_PORT].vb2q;
> + } else if (type == OUTPUT_MPLANE) {
> + q = inst->bufq[OUTPUT_PORT].vb2q;
> + } else {
> + i_vpr_e(inst, "%s: invalid buffer type %d\n",
> + __func__, type);
> + }
> + return q;
> +}
> +
> +void *msm_vb2_alloc(struct vb2_buffer *vb, struct device *dev,
> + unsigned long size)
> +{
> + return (void *)0xdeadbeef;
> +}
> +
> +void *msm_vb2_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
> + struct dma_buf *dbuf, unsigned long size)
> +{
> + struct msm_vidc_inst *inst;
> + struct msm_vidc_core *core;
> + struct msm_vidc_buffer *buf = NULL;
> + struct msm_vidc_buffer *ro_buf, *dummy;
> +
> + if (!vb || !dev || !dbuf || !vb->vb2_queue) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return NULL;
> + }
> + inst = vb->vb2_queue->drv_priv;
> + inst = get_inst_ref(g_core, inst);
> + if (!inst || !inst->core) {
> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
> + return NULL;
> + }
> + core = inst->core;
> +
> + buf = msm_vidc_fetch_buffer(inst, vb);
> + if (!buf) {
> + i_vpr_e(inst, "%s: failed to fetch buffer\n", __func__);
> + buf = NULL;
> + goto exit;
> + }
> + buf->inst = inst;
> + buf->dmabuf = dbuf;
> +
> + if (is_decode_session(inst) && is_output_buffer(buf->type)) {
> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
> + if (ro_buf->dmabuf != buf->dmabuf)
> + continue;
> + print_vidc_buffer(VIDC_LOW, "low ", "attach: found ro buf", inst, ro_buf);
> + buf->attach = ro_buf->attach;
> + ro_buf->attach = NULL;
> + goto exit;
> + }
> + }
> +
> + buf->attach = call_mem_op(core, dma_buf_attach, core, dbuf, dev);
> + if (!buf->attach) {
> + buf->attach = NULL;
> + buf = NULL;
> + goto exit;
> + }
> + print_vidc_buffer(VIDC_LOW, "low ", "attach", inst, buf);
> +
> +exit:
> + if (!buf)
> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
> + put_inst(inst);
> + return buf;
> +}
> +
> +void msm_vb2_put(void *buf_priv)
> +{
> +}
> +
> +int msm_vb2_mmap(void *buf_priv, struct vm_area_struct *vma)
> +{
> + return 0;
> +}
> +
> +void msm_vb2_detach_dmabuf(void *buf_priv)
> +{
> + struct msm_vidc_buffer *vbuf = buf_priv;
> + struct msm_vidc_buffer *ro_buf, *dummy;
> + struct msm_vidc_core *core;
> + struct msm_vidc_inst *inst;
> +
> + if (!vbuf || !vbuf->inst) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return;
> + }
> + inst = vbuf->inst;
> + if (!inst || !inst->core) {
> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
> + return;
> + }
> + core = inst->core;
> +
> + if (is_decode_session(inst) && is_output_buffer(vbuf->type)) {
> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
> + if (ro_buf->dmabuf != vbuf->dmabuf)
> + continue;
> + print_vidc_buffer(VIDC_LOW, "low ", "detach: found ro buf", inst, ro_buf);
> + ro_buf->attach = vbuf->attach;
> + vbuf->attach = NULL;
> + goto exit;
> + }
> + }
> +
> + print_vidc_buffer(VIDC_LOW, "low ", "detach", inst, vbuf);
> + if (vbuf->attach && vbuf->dmabuf) {
> + call_mem_op(core, dma_buf_detach, core, vbuf->dmabuf, vbuf->attach);
> + vbuf->attach = NULL;
> + }
> +
> +exit:
> + vbuf->dmabuf = NULL;
> + vbuf->inst = NULL;
> +}
> +
> +int msm_vb2_map_dmabuf(void *buf_priv)
> +{
> + int rc = 0;
> + struct msm_vidc_buffer *buf = buf_priv;
> + struct msm_vidc_core *core;
> + struct msm_vidc_inst *inst;
> + struct msm_vidc_buffer *ro_buf, *dummy;
> +
> + if (!buf || !buf->inst) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return -EINVAL;
> + }
> + inst = buf->inst;
> + inst = get_inst_ref(g_core, inst);
> + if (!inst || !inst->core) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return -EINVAL;
> + }
> + core = inst->core;
> +
> + if (is_decode_session(inst) && is_output_buffer(buf->type)) {
> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
> + if (ro_buf->dmabuf != buf->dmabuf)
> + continue;
> + print_vidc_buffer(VIDC_LOW, "low ", "map: found ro buf", inst, ro_buf);
> + buf->sg_table = ro_buf->sg_table;
> + buf->device_addr = ro_buf->device_addr;
> + ro_buf->sg_table = NULL;
> + goto exit;
> + }
> + }
> +
> + buf->sg_table = call_mem_op(core, dma_buf_map_attachment, core, buf->attach);
> + if (!buf->sg_table || !buf->sg_table->sgl) {
> + buf->sg_table = NULL;
> + rc = -ENOMEM;
> + goto exit;
> + }
> + buf->device_addr = sg_dma_address(buf->sg_table->sgl);
> + print_vidc_buffer(VIDC_HIGH, "high", "map", inst, buf);
> +
> +exit:
> + if (rc)
> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
> + put_inst(inst);
> + return rc;
> +}
> +
> +void msm_vb2_unmap_dmabuf(void *buf_priv)
> +{
> + struct msm_vidc_buffer *vbuf = buf_priv;
> + struct msm_vidc_buffer *ro_buf, *dummy;
> + struct msm_vidc_core *core;
> + struct msm_vidc_inst *inst;
> +
> + if (!vbuf || !vbuf->inst) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return;
> + }
> + inst = vbuf->inst;
> + if (!inst || !inst->core) {
> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
> + return;
> + }
> + core = inst->core;
> +
> + if (is_decode_session(inst) && is_output_buffer(vbuf->type)) {
> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
> + if (ro_buf->dmabuf != vbuf->dmabuf)
> + continue;
> + print_vidc_buffer(VIDC_LOW, "low ", "unmap: found ro buf", inst, ro_buf);
> + ro_buf->sg_table = vbuf->sg_table;
> + vbuf->sg_table = NULL;
> + vbuf->device_addr = 0x0;
> + goto exit;
> + }
> + }
> +
> + print_vidc_buffer(VIDC_HIGH, "high", "unmap", inst, vbuf);
> + if (vbuf->attach && vbuf->sg_table) {
> + call_mem_op(core, dma_buf_unmap_attachment, core, vbuf->attach, vbuf->sg_table);
> + vbuf->sg_table = NULL;
> + vbuf->device_addr = 0x0;
> + }
> +
> +exit:
> + return;
> +}
> +
> +int msm_vb2_queue_setup(struct vb2_queue *q,
> + unsigned int *num_buffers, unsigned int *num_planes,
> + unsigned int sizes[], struct device *alloc_devs[])
> +{
> + int rc = 0;
> + struct msm_vidc_inst *inst;
> + struct msm_vidc_core *core;
> + int port;
> + struct v4l2_format *f;
> + enum msm_vidc_buffer_type buffer_type = 0;
> + enum msm_vidc_buffer_region region = MSM_VIDC_REGION_NONE;
> + struct context_bank_info *cb = NULL;
> + struct msm_vidc_buffers *buffers;
> +
> + if (!q || !num_buffers || !num_planes ||
> + !sizes || !q->drv_priv) {
> + d_vpr_e("%s: invalid params, q = %pK, %pK, %pK\n",
> + __func__, q, num_buffers, num_planes);
> + return -EINVAL;
> + }
> + inst = q->drv_priv;
> + if (!inst || !inst->core) {
> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
> + return -EINVAL;
> + }
> + core = inst->core;
> +
> + if (is_state(inst, MSM_VIDC_STREAMING)) {
> + i_vpr_e(inst, "%s: invalid state %d\n", __func__, inst->state);
> + return -EINVAL;
> + }
> +
> + port = v4l2_type_to_driver_port(inst, q->type, __func__);
> + if (port < 0)
> + return -EINVAL;
> +
> + /* prepare dependency list once per session */
> + if (!inst->caps_list_prepared) {
> + rc = msm_vidc_prepare_dependency_list(inst);
> + if (rc)
> + return rc;
> + inst->caps_list_prepared = true;
> + }
> +
> + /* adjust v4l2 properties for master port */
> + if ((is_encode_session(inst) && port == OUTPUT_PORT) ||
> + (is_decode_session(inst) && port == INPUT_PORT)) {
> + rc = msm_vidc_adjust_v4l2_properties(inst);
> + if (rc) {
> + i_vpr_e(inst, "%s: failed to adjust properties\n", __func__);
> + return rc;
> + }
> + }
> +
> + if (*num_planes && (port == INPUT_PORT || port == OUTPUT_PORT)) {
> + f = &inst->fmts[port];
> + if (*num_planes != f->fmt.pix_mp.num_planes) {
> + i_vpr_e(inst, "%s: requested num_planes %d not supported %d\n",
> + __func__, *num_planes, f->fmt.pix_mp.num_planes);
> + return -EINVAL;
> + }
> + if (sizes[0] < inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage) {
> + i_vpr_e(inst, "%s: requested size %d not acceptable\n",
> + __func__, sizes[0]);
> + return -EINVAL;
> + }
> + }
> +
> + buffer_type = v4l2_type_to_driver(q->type, __func__);
> + if (!buffer_type)
> + return -EINVAL;
> +
> + rc = msm_vidc_free_buffers(inst, buffer_type);
> + if (rc) {
> + i_vpr_e(inst, "%s: failed to free buffers, type %s\n",
> + __func__, v4l2_type_name(q->type));
> + return rc;
> + }
> +
> + buffers = msm_vidc_get_buffers(inst, buffer_type, __func__);
> + if (!buffers)
> + return -EINVAL;
> +
> + buffers->min_count = call_session_op(core, min_count, inst, buffer_type);
> + buffers->extra_count = call_session_op(core, extra_count, inst, buffer_type);
> + if (*num_buffers < buffers->min_count + buffers->extra_count)
> + *num_buffers = buffers->min_count + buffers->extra_count;
> + buffers->actual_count = *num_buffers;
> + *num_planes = 1;
> +
> + buffers->size = call_session_op(core, buffer_size, inst, buffer_type);
> +
> + inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage = buffers->size;
> + sizes[0] = inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage;
> +
> + rc = msm_vidc_allocate_buffers(inst, buffer_type, *num_buffers);
> + if (rc) {
> + i_vpr_e(inst, "%s: failed to allocate buffers, type %s\n",
> + __func__, v4l2_type_name(q->type));
> + return rc;
> + }
> +
> + region = call_mem_op(core, buffer_region, inst, buffer_type);
> + cb = msm_vidc_get_context_bank_for_region(core, region);
> + if (!cb) {
> + d_vpr_e("%s: Failed to get context bank device\n",
> + __func__);
> + return -EIO;
> + }
> + q->dev = cb->dev;
> +
> + i_vpr_h(inst,
> + "queue_setup: type %s num_buffers %d sizes[0] %d cb %s\n",
> + v4l2_type_name(q->type), *num_buffers, sizes[0], cb->name);
> + return rc;
> +}
> +
> +int msm_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> + int rc = 0;
> + struct msm_vidc_inst *inst;
> +
> + if (!q || !q->drv_priv) {
> + d_vpr_e("%s: invalid input, q = %pK\n", __func__, q);
> + return -EINVAL;
> + }
> + inst = q->drv_priv;
> + if (!inst || !inst->core) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return -EINVAL;
> + }
> +
> + rc = inst->event_handle(inst, MSM_VIDC_STREAMON, q);
> + if (rc) {
> + i_vpr_e(inst, "Streamon: %s failed\n", v4l2_type_name(q->type));
> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
> + goto exit;
> + }
> +
> +exit:
> + return rc;
> +}
> +
> +int msm_vidc_start_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q)
> +{
> + enum msm_vidc_buffer_type buf_type;
> + int rc = 0;
> +
> + if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
> + i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
> + return -EINVAL;
> + }
> + if (!is_decode_session(inst) && !is_encode_session(inst)) {
> + i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain);
> + return -EINVAL;
> + }
> + i_vpr_h(inst, "Streamon: %s\n", v4l2_type_name(q->type));
> +
> + if (!inst->once_per_session_set) {
This seems miss-placed, I think you should be able to move this into your driver
open() call and drop the inst->once_per_session_set boolean.
> + inst->once_per_session_set = true;
> + rc = msm_vidc_session_set_codec(inst);
> + if (rc)
> + return rc;
> +
> + if (is_encode_session(inst)) {
> + rc = msm_vidc_alloc_and_queue_session_int_bufs(inst,
> + MSM_VIDC_BUF_ARP);
> + if (rc)
> + return rc;
> + } else if (is_decode_session(inst)) {
> + rc = msm_vidc_session_set_default_header(inst);
> + if (rc)
> + return rc;
> +
> + rc = msm_vidc_alloc_and_queue_session_int_bufs(inst,
> + MSM_VIDC_BUF_PERSIST);
> + if (rc)
> + return rc;
> + }
> + }
> +
> + if (is_decode_session(inst))
> + inst->decode_batch.enable = msm_vidc_allow_decode_batch(inst);
> +
> + msm_vidc_allow_dcvs(inst);
> + msm_vidc_power_data_reset(inst);
> +
> + if (q->type == INPUT_MPLANE) {
> + if (is_decode_session(inst))
> + rc = msm_vdec_streamon_input(inst);
> + else if (is_encode_session(inst))
> + rc = msm_venc_streamon_input(inst);
> + } else if (q->type == OUTPUT_MPLANE) {
> + if (is_decode_session(inst))
> + rc = msm_vdec_streamon_output(inst);
> + else if (is_encode_session(inst))
> + rc = msm_venc_streamon_output(inst);
> + }
> + if (rc)
> + return rc;
> +
> + /* print final buffer counts & size details */
> + msm_vidc_print_buffer_info(inst);
> +
> + /* print internal buffer memory usage stats */
> + msm_vidc_print_memory_stats(inst);
> +
> + buf_type = v4l2_type_to_driver(q->type, __func__);
> + if (!buf_type)
> + return -EINVAL;
> +
> + /* queue pending buffers */
> + rc = msm_vidc_queue_deferred_buffers(inst, buf_type);
> + if (rc)
> + return rc;
> +
> + /* initialize statistics timer(one time) */
> + if (!inst->stats.time_ms)
> + inst->stats.time_ms = ktime_get_ns() / 1000 / 1000;
> +
> + /* schedule to print buffer statistics */
> + rc = schedule_stats_work(inst);
> + if (rc)
> + return rc;
> +
> + if ((q->type == INPUT_MPLANE && inst->bufq[OUTPUT_PORT].vb2q->streaming) ||
> + (q->type == OUTPUT_MPLANE && inst->bufq[INPUT_PORT].vb2q->streaming)) {
> + rc = msm_vidc_get_properties(inst);
> + if (rc)
> + return rc;
> + }
> +
> + i_vpr_h(inst, "Streamon: %s successful\n", v4l2_type_name(q->type));
> + return rc;
> +}
> +
> +int msm_vidc_stop_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q)
> +{
> + int rc = 0;
> +
> + if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
> + i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
> + return -EINVAL;
> + }
> + if (!is_decode_session(inst) && !is_encode_session(inst)) {
> + i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain);
> + return -EINVAL;
> + }
> + i_vpr_h(inst, "Streamoff: %s\n", v4l2_type_name(q->type));
> +
> + if (q->type == INPUT_MPLANE) {
> + if (is_decode_session(inst))
> + rc = msm_vdec_streamoff_input(inst);
> + else if (is_encode_session(inst))
> + rc = msm_venc_streamoff_input(inst);
> + } else if (q->type == OUTPUT_MPLANE) {
> + if (is_decode_session(inst))
> + rc = msm_vdec_streamoff_output(inst);
> + else if (is_encode_session(inst))
> + rc = msm_venc_streamoff_output(inst);
> + }
> + if (rc)
> + return rc;
> +
> + /* Input port streamoff */
> + if (q->type == INPUT_MPLANE) {
> + /* flush timestamps list */
> + msm_vidc_flush_ts(inst);
> + }
> +
> + /* print internal buffer memory usage stats */
> + msm_vidc_print_memory_stats(inst);
> +
> + i_vpr_h(inst, "Streamoff: %s successful\n", v4l2_type_name(q->type));
> + return rc;
> +}
> +
> +void msm_vb2_stop_streaming(struct vb2_queue *q)
> +{
> + struct msm_vidc_inst *inst;
> + int rc = 0;
> +
> + if (!q || !q->drv_priv) {
> + d_vpr_e("%s: invalid input, q = %pK\n", __func__, q);
> + return;
> + }
> + inst = q->drv_priv;
> + if (!inst) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return;
> + }
> +
> + rc = inst->event_handle(inst, MSM_VIDC_STREAMOFF, q);
> + if (rc) {
> + i_vpr_e(inst, "Streamoff: %s failed\n", v4l2_type_name(q->type));
> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
> + }
> +}
> +
> +void msm_vb2_buf_queue(struct vb2_buffer *vb2)
> +{
> + int rc = 0;
> + struct msm_vidc_inst *inst;
> + struct dma_buf *dbuf = NULL;
> + struct msm_vidc_core *core;
> + u64 ktime_ns = ktime_get_ns();
> +
> + if (!vb2) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return;
> + }
> +
> + inst = vb2_get_drv_priv(vb2->vb2_queue);
> + if (!inst || !inst->core) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + return;
> + }
> + core = inst->core;
> +
> + if (!vb2->planes[0].bytesused) {
> + if (vb2->type == INPUT_MPLANE) {
> + /* Expecting non-zero filledlen on INPUT port */
> + i_vpr_e(inst,
> + "%s: zero bytesused input buffer not supported\n", __func__);
> + rc = -EINVAL;
> + goto exit;
> + }
> + }
> +
> + inst->last_qbuf_time_ns = ktime_ns;
> +
> + if (vb2->type == INPUT_MPLANE) {
> + rc = msm_vidc_update_input_rate(inst, div_u64(ktime_ns, 1000));
> + if (rc)
> + goto exit;
> + }
> +
> + /*
> + * Userspace may close fd(from other thread), before driver attempts to call
> + * dma_buf_get() in qbuf(FTB) sequence(for decoder output buffer) which may
> + * lead to different kind of security issues. Add check to compare if dma_buf
> + * address is matching with driver dma_buf_get returned address for that fd.
> + */
> +
> + dbuf = call_mem_op(core, dma_buf_get, inst, vb2->planes[0].m.fd);
> + if (dbuf != vb2->planes[0].dbuf) {
> + i_vpr_e(inst, "%s: invalid dmabuf address 0x%p expected 0x%p\n",
> + __func__, dbuf, vb2->planes[0].dbuf);
> + rc = -EINVAL;
> + goto exit;
> + }
> +
> + if (is_decode_session(inst))
> + rc = msm_vdec_qbuf(inst, vb2);
> + else if (is_encode_session(inst))
> + rc = msm_venc_qbuf(inst, vb2);
> + else
> + rc = -EINVAL;
> + if (rc) {
> + print_vb2_buffer("failed vb2-qbuf", inst, vb2);
> + goto exit;
> + }
> +
> +exit:
> + if (dbuf)
> + call_mem_op(core, dma_buf_put, inst, dbuf);
> +
> + if (rc) {
> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
> + vb2_buffer_done(vb2, VB2_BUF_STATE_ERROR);
> + }
> +}
On 7/28/2023 11:33 PM, Nicolas Dufresne wrote:
> Le vendredi 28 juillet 2023 à 18:53 +0530, Vikash Garodia a écrit :
>> From: Dikshita Agarwal <quic_dikshita@quicinc.com>
>>
>> This implements vb2 ops for streaming modes for
>> alloc, free, map and unmap buffers.
>>
>> Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
>> Signed-off-by: Vikash Garodia <quic_vgarodia@quicinc.com>
>> ---
>> .../platform/qcom/iris/vidc/inc/msm_vidc_vb2.h | 39 ++
>> .../platform/qcom/iris/vidc/src/msm_vidc_vb2.c | 605 +++++++++++++++++++++
>> 2 files changed, 644 insertions(+)
>> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
>> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
>>
>> diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
>> new file mode 100644
>> index 0000000..12378ce
>> --- /dev/null
>> +++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
>> @@ -0,0 +1,39 @@
>> +/* 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_VB2_H_
>> +#define _MSM_VIDC_VB2_H_
>> +
>> +#include <media/videobuf2-core.h>
>> +#include <media/videobuf2-v4l2.h>
>> +
>> +#include "msm_vidc_inst.h"
>> +
>> +struct vb2_queue *msm_vidc_get_vb2q(struct msm_vidc_inst *inst,
>> + u32 type, const char *func);
>> +
>> +/* vb2_mem_ops */
>> +void *msm_vb2_alloc(struct vb2_buffer *vb, struct device *dev,
>> + unsigned long size);
>> +void *msm_vb2_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
>> + struct dma_buf *dbuf, unsigned long size);
>> +
>> +void msm_vb2_put(void *buf_priv);
>> +int msm_vb2_mmap(void *buf_priv, struct vm_area_struct *vma);
>> +void msm_vb2_detach_dmabuf(void *buf_priv);
>> +int msm_vb2_map_dmabuf(void *buf_priv);
>> +void msm_vb2_unmap_dmabuf(void *buf_priv);
>> +
>> +/* vb2_ops */
>> +int msm_vb2_queue_setup(struct vb2_queue *q,
>> + unsigned int *num_buffers, unsigned int *num_planes,
>> + unsigned int sizes[], struct device *alloc_devs[]);
>> +int msm_vidc_start_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q);
>> +int msm_vidc_stop_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q);
>> +int msm_vb2_start_streaming(struct vb2_queue *q, unsigned int count);
>> +void msm_vb2_stop_streaming(struct vb2_queue *q);
>> +void msm_vb2_buf_queue(struct vb2_buffer *vb2);
>> +#endif // _MSM_VIDC_VB2_H_
>> diff --git a/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
>> new file mode 100644
>> index 0000000..c936d95
>> --- /dev/null
>> +++ b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
>> @@ -0,0 +1,605 @@
>> +// 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 "msm_vdec.h"
>> +#include "msm_venc.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_platform.h"
>> +#include "msm_vidc_power.h"
>> +#include "msm_vidc_vb2.h"
>> +
>> +struct vb2_queue *msm_vidc_get_vb2q(struct msm_vidc_inst *inst,
>> + u32 type, const char *func)
>> +{
>> + struct vb2_queue *q = NULL;
>> +
>> + if (type == INPUT_MPLANE) {
>> + q = inst->bufq[INPUT_PORT].vb2q;
>> + } else if (type == OUTPUT_MPLANE) {
>> + q = inst->bufq[OUTPUT_PORT].vb2q;
>> + } else {
>> + i_vpr_e(inst, "%s: invalid buffer type %d\n",
>> + __func__, type);
>> + }
>> + return q;
>> +}
>> +
>> +void *msm_vb2_alloc(struct vb2_buffer *vb, struct device *dev,
>> + unsigned long size)
>> +{
>> + return (void *)0xdeadbeef;
>> +}
>> +
>> +void *msm_vb2_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
>> + struct dma_buf *dbuf, unsigned long size)
>> +{
>> + struct msm_vidc_inst *inst;
>> + struct msm_vidc_core *core;
>> + struct msm_vidc_buffer *buf = NULL;
>> + struct msm_vidc_buffer *ro_buf, *dummy;
>> +
>> + if (!vb || !dev || !dbuf || !vb->vb2_queue) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return NULL;
>> + }
>> + inst = vb->vb2_queue->drv_priv;
>> + inst = get_inst_ref(g_core, inst);
>> + if (!inst || !inst->core) {
>> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
>> + return NULL;
>> + }
>> + core = inst->core;
>> +
>> + buf = msm_vidc_fetch_buffer(inst, vb);
>> + if (!buf) {
>> + i_vpr_e(inst, "%s: failed to fetch buffer\n", __func__);
>> + buf = NULL;
>> + goto exit;
>> + }
>> + buf->inst = inst;
>> + buf->dmabuf = dbuf;
>> +
>> + if (is_decode_session(inst) && is_output_buffer(buf->type)) {
>> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
>> + if (ro_buf->dmabuf != buf->dmabuf)
>> + continue;
>> + print_vidc_buffer(VIDC_LOW, "low ", "attach: found ro buf", inst, ro_buf);
>> + buf->attach = ro_buf->attach;
>> + ro_buf->attach = NULL;
>> + goto exit;
>> + }
>> + }
>> +
>> + buf->attach = call_mem_op(core, dma_buf_attach, core, dbuf, dev);
>> + if (!buf->attach) {
>> + buf->attach = NULL;
>> + buf = NULL;
>> + goto exit;
>> + }
>> + print_vidc_buffer(VIDC_LOW, "low ", "attach", inst, buf);
>> +
>> +exit:
>> + if (!buf)
>> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
>> + put_inst(inst);
>> + return buf;
>> +}
>> +
>> +void msm_vb2_put(void *buf_priv)
>> +{
>> +}
>> +
>> +int msm_vb2_mmap(void *buf_priv, struct vm_area_struct *vma)
>> +{
>> + return 0;
>> +}
>> +
>> +void msm_vb2_detach_dmabuf(void *buf_priv)
>> +{
>> + struct msm_vidc_buffer *vbuf = buf_priv;
>> + struct msm_vidc_buffer *ro_buf, *dummy;
>> + struct msm_vidc_core *core;
>> + struct msm_vidc_inst *inst;
>> +
>> + if (!vbuf || !vbuf->inst) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return;
>> + }
>> + inst = vbuf->inst;
>> + if (!inst || !inst->core) {
>> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
>> + return;
>> + }
>> + core = inst->core;
>> +
>> + if (is_decode_session(inst) && is_output_buffer(vbuf->type)) {
>> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
>> + if (ro_buf->dmabuf != vbuf->dmabuf)
>> + continue;
>> + print_vidc_buffer(VIDC_LOW, "low ", "detach: found ro buf", inst, ro_buf);
>> + ro_buf->attach = vbuf->attach;
>> + vbuf->attach = NULL;
>> + goto exit;
>> + }
>> + }
>> +
>> + print_vidc_buffer(VIDC_LOW, "low ", "detach", inst, vbuf);
>> + if (vbuf->attach && vbuf->dmabuf) {
>> + call_mem_op(core, dma_buf_detach, core, vbuf->dmabuf, vbuf->attach);
>> + vbuf->attach = NULL;
>> + }
>> +
>> +exit:
>> + vbuf->dmabuf = NULL;
>> + vbuf->inst = NULL;
>> +}
>> +
>> +int msm_vb2_map_dmabuf(void *buf_priv)
>> +{
>> + int rc = 0;
>> + struct msm_vidc_buffer *buf = buf_priv;
>> + struct msm_vidc_core *core;
>> + struct msm_vidc_inst *inst;
>> + struct msm_vidc_buffer *ro_buf, *dummy;
>> +
>> + if (!buf || !buf->inst) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return -EINVAL;
>> + }
>> + inst = buf->inst;
>> + inst = get_inst_ref(g_core, inst);
>> + if (!inst || !inst->core) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return -EINVAL;
>> + }
>> + core = inst->core;
>> +
>> + if (is_decode_session(inst) && is_output_buffer(buf->type)) {
>> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
>> + if (ro_buf->dmabuf != buf->dmabuf)
>> + continue;
>> + print_vidc_buffer(VIDC_LOW, "low ", "map: found ro buf", inst, ro_buf);
>> + buf->sg_table = ro_buf->sg_table;
>> + buf->device_addr = ro_buf->device_addr;
>> + ro_buf->sg_table = NULL;
>> + goto exit;
>> + }
>> + }
>> +
>> + buf->sg_table = call_mem_op(core, dma_buf_map_attachment, core, buf->attach);
>> + if (!buf->sg_table || !buf->sg_table->sgl) {
>> + buf->sg_table = NULL;
>> + rc = -ENOMEM;
>> + goto exit;
>> + }
>> + buf->device_addr = sg_dma_address(buf->sg_table->sgl);
>> + print_vidc_buffer(VIDC_HIGH, "high", "map", inst, buf);
>> +
>> +exit:
>> + if (rc)
>> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
>> + put_inst(inst);
>> + return rc;
>> +}
>> +
>> +void msm_vb2_unmap_dmabuf(void *buf_priv)
>> +{
>> + struct msm_vidc_buffer *vbuf = buf_priv;
>> + struct msm_vidc_buffer *ro_buf, *dummy;
>> + struct msm_vidc_core *core;
>> + struct msm_vidc_inst *inst;
>> +
>> + if (!vbuf || !vbuf->inst) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return;
>> + }
>> + inst = vbuf->inst;
>> + if (!inst || !inst->core) {
>> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
>> + return;
>> + }
>> + core = inst->core;
>> +
>> + if (is_decode_session(inst) && is_output_buffer(vbuf->type)) {
>> + list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
>> + if (ro_buf->dmabuf != vbuf->dmabuf)
>> + continue;
>> + print_vidc_buffer(VIDC_LOW, "low ", "unmap: found ro buf", inst, ro_buf);
>> + ro_buf->sg_table = vbuf->sg_table;
>> + vbuf->sg_table = NULL;
>> + vbuf->device_addr = 0x0;
>> + goto exit;
>> + }
>> + }
>> +
>> + print_vidc_buffer(VIDC_HIGH, "high", "unmap", inst, vbuf);
>> + if (vbuf->attach && vbuf->sg_table) {
>> + call_mem_op(core, dma_buf_unmap_attachment, core, vbuf->attach, vbuf->sg_table);
>> + vbuf->sg_table = NULL;
>> + vbuf->device_addr = 0x0;
>> + }
>> +
>> +exit:
>> + return;
>> +}
>> +
>> +int msm_vb2_queue_setup(struct vb2_queue *q,
>> + unsigned int *num_buffers, unsigned int *num_planes,
>> + unsigned int sizes[], struct device *alloc_devs[])
>> +{
>> + int rc = 0;
>> + struct msm_vidc_inst *inst;
>> + struct msm_vidc_core *core;
>> + int port;
>> + struct v4l2_format *f;
>> + enum msm_vidc_buffer_type buffer_type = 0;
>> + enum msm_vidc_buffer_region region = MSM_VIDC_REGION_NONE;
>> + struct context_bank_info *cb = NULL;
>> + struct msm_vidc_buffers *buffers;
>> +
>> + if (!q || !num_buffers || !num_planes ||
>> + !sizes || !q->drv_priv) {
>> + d_vpr_e("%s: invalid params, q = %pK, %pK, %pK\n",
>> + __func__, q, num_buffers, num_planes);
>> + return -EINVAL;
>> + }
>> + inst = q->drv_priv;
>> + if (!inst || !inst->core) {
>> + d_vpr_e("%s: invalid params %pK\n", __func__, inst);
>> + return -EINVAL;
>> + }
>> + core = inst->core;
>> +
>> + if (is_state(inst, MSM_VIDC_STREAMING)) {
>> + i_vpr_e(inst, "%s: invalid state %d\n", __func__, inst->state);
>> + return -EINVAL;
>> + }
>> +
>> + port = v4l2_type_to_driver_port(inst, q->type, __func__);
>> + if (port < 0)
>> + return -EINVAL;
>> +
>> + /* prepare dependency list once per session */
>> + if (!inst->caps_list_prepared) {
>> + rc = msm_vidc_prepare_dependency_list(inst);
>> + if (rc)
>> + return rc;
>> + inst->caps_list_prepared = true;
>> + }
>> +
>> + /* adjust v4l2 properties for master port */
>> + if ((is_encode_session(inst) && port == OUTPUT_PORT) ||
>> + (is_decode_session(inst) && port == INPUT_PORT)) {
>> + rc = msm_vidc_adjust_v4l2_properties(inst);
>> + if (rc) {
>> + i_vpr_e(inst, "%s: failed to adjust properties\n", __func__);
>> + return rc;
>> + }
>> + }
>> +
>> + if (*num_planes && (port == INPUT_PORT || port == OUTPUT_PORT)) {
>> + f = &inst->fmts[port];
>> + if (*num_planes != f->fmt.pix_mp.num_planes) {
>> + i_vpr_e(inst, "%s: requested num_planes %d not supported %d\n",
>> + __func__, *num_planes, f->fmt.pix_mp.num_planes);
>> + return -EINVAL;
>> + }
>> + if (sizes[0] < inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage) {
>> + i_vpr_e(inst, "%s: requested size %d not acceptable\n",
>> + __func__, sizes[0]);
>> + return -EINVAL;
>> + }
>> + }
>> +
>> + buffer_type = v4l2_type_to_driver(q->type, __func__);
>> + if (!buffer_type)
>> + return -EINVAL;
>> +
>> + rc = msm_vidc_free_buffers(inst, buffer_type);
>> + if (rc) {
>> + i_vpr_e(inst, "%s: failed to free buffers, type %s\n",
>> + __func__, v4l2_type_name(q->type));
>> + return rc;
>> + }
>> +
>> + buffers = msm_vidc_get_buffers(inst, buffer_type, __func__);
>> + if (!buffers)
>> + return -EINVAL;
>> +
>> + buffers->min_count = call_session_op(core, min_count, inst, buffer_type);
>> + buffers->extra_count = call_session_op(core, extra_count, inst, buffer_type);
>> + if (*num_buffers < buffers->min_count + buffers->extra_count)
>> + *num_buffers = buffers->min_count + buffers->extra_count;
>> + buffers->actual_count = *num_buffers;
>> + *num_planes = 1;
>> +
>> + buffers->size = call_session_op(core, buffer_size, inst, buffer_type);
>> +
>> + inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage = buffers->size;
>> + sizes[0] = inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage;
>> +
>> + rc = msm_vidc_allocate_buffers(inst, buffer_type, *num_buffers);
>> + if (rc) {
>> + i_vpr_e(inst, "%s: failed to allocate buffers, type %s\n",
>> + __func__, v4l2_type_name(q->type));
>> + return rc;
>> + }
>> +
>> + region = call_mem_op(core, buffer_region, inst, buffer_type);
>> + cb = msm_vidc_get_context_bank_for_region(core, region);
>> + if (!cb) {
>> + d_vpr_e("%s: Failed to get context bank device\n",
>> + __func__);
>> + return -EIO;
>> + }
>> + q->dev = cb->dev;
>> +
>> + i_vpr_h(inst,
>> + "queue_setup: type %s num_buffers %d sizes[0] %d cb %s\n",
>> + v4l2_type_name(q->type), *num_buffers, sizes[0], cb->name);
>> + return rc;
>> +}
>> +
>> +int msm_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
>> +{
>> + int rc = 0;
>> + struct msm_vidc_inst *inst;
>> +
>> + if (!q || !q->drv_priv) {
>> + d_vpr_e("%s: invalid input, q = %pK\n", __func__, q);
>> + return -EINVAL;
>> + }
>> + inst = q->drv_priv;
>> + if (!inst || !inst->core) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return -EINVAL;
>> + }
>> +
>> + rc = inst->event_handle(inst, MSM_VIDC_STREAMON, q);
>> + if (rc) {
>> + i_vpr_e(inst, "Streamon: %s failed\n", v4l2_type_name(q->type));
>> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
>> + goto exit;
>> + }
>> +
>> +exit:
>> + return rc;
>> +}
>> +
>> +int msm_vidc_start_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q)
>> +{
>> + enum msm_vidc_buffer_type buf_type;
>> + int rc = 0;
>> +
>> + if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
>> + i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
>> + return -EINVAL;
>> + }
>> + if (!is_decode_session(inst) && !is_encode_session(inst)) {
>> + i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain);
>> + return -EINVAL;
>> + }
>> + i_vpr_h(inst, "Streamon: %s\n", v4l2_type_name(q->type));
>> +
>> + if (!inst->once_per_session_set) {
>
> This seems miss-placed, I think you should be able to move this into your driver
> open() call and drop the inst->once_per_session_set boolean>
The APIs being invoked under this condition can't be moved to open.
msm_vidc_session_set_codec is called to set the codec which is not known at
open, and only known at s_fmt.
buffer sizes for different internal buffer will be known only after s_fmt.
hence these setting are being done in start streaming.
Thanks,
Dikshita
>> + inst->once_per_session_set = true;
>> + rc = msm_vidc_session_set_codec(inst);
>> + if (rc)
>> + return rc;
>> +
>> + if (is_encode_session(inst)) {
>> + rc = msm_vidc_alloc_and_queue_session_int_bufs(inst,
>> + MSM_VIDC_BUF_ARP);
>> + if (rc)
>> + return rc;
>> + } else if (is_decode_session(inst)) {
>> + rc = msm_vidc_session_set_default_header(inst);
>> + if (rc)
>> + return rc;
>> +
>> + rc = msm_vidc_alloc_and_queue_session_int_bufs(inst,
>> + MSM_VIDC_BUF_PERSIST);
>> + if (rc)
>> + return rc;
>> + }
>> + }
>> +
>> + if (is_decode_session(inst))
>> + inst->decode_batch.enable = msm_vidc_allow_decode_batch(inst);
>> +
>> + msm_vidc_allow_dcvs(inst);
>> + msm_vidc_power_data_reset(inst);
>> +
>> + if (q->type == INPUT_MPLANE) {
>> + if (is_decode_session(inst))
>> + rc = msm_vdec_streamon_input(inst);
>> + else if (is_encode_session(inst))
>> + rc = msm_venc_streamon_input(inst);
>> + } else if (q->type == OUTPUT_MPLANE) {
>> + if (is_decode_session(inst))
>> + rc = msm_vdec_streamon_output(inst);
>> + else if (is_encode_session(inst))
>> + rc = msm_venc_streamon_output(inst);
>> + }
>> + if (rc)
>> + return rc;
>> +
>> + /* print final buffer counts & size details */
>> + msm_vidc_print_buffer_info(inst);
>> +
>> + /* print internal buffer memory usage stats */
>> + msm_vidc_print_memory_stats(inst);
>> +
>> + buf_type = v4l2_type_to_driver(q->type, __func__);
>> + if (!buf_type)
>> + return -EINVAL;
>> +
>> + /* queue pending buffers */
>> + rc = msm_vidc_queue_deferred_buffers(inst, buf_type);
>> + if (rc)
>> + return rc;
>> +
>> + /* initialize statistics timer(one time) */
>> + if (!inst->stats.time_ms)
>> + inst->stats.time_ms = ktime_get_ns() / 1000 / 1000;
>> +
>> + /* schedule to print buffer statistics */
>> + rc = schedule_stats_work(inst);
>> + if (rc)
>> + return rc;
>> +
>> + if ((q->type == INPUT_MPLANE && inst->bufq[OUTPUT_PORT].vb2q->streaming) ||
>> + (q->type == OUTPUT_MPLANE && inst->bufq[INPUT_PORT].vb2q->streaming)) {
>> + rc = msm_vidc_get_properties(inst);
>> + if (rc)
>> + return rc;
>> + }
>> +
>> + i_vpr_h(inst, "Streamon: %s successful\n", v4l2_type_name(q->type));
>> + return rc;
>> +}
>> +
>> +int msm_vidc_stop_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q)
>> +{
>> + int rc = 0;
>> +
>> + if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
>> + i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
>> + return -EINVAL;
>> + }
>> + if (!is_decode_session(inst) && !is_encode_session(inst)) {
>> + i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain);
>> + return -EINVAL;
>> + }
>> + i_vpr_h(inst, "Streamoff: %s\n", v4l2_type_name(q->type));
>> +
>> + if (q->type == INPUT_MPLANE) {
>> + if (is_decode_session(inst))
>> + rc = msm_vdec_streamoff_input(inst);
>> + else if (is_encode_session(inst))
>> + rc = msm_venc_streamoff_input(inst);
>> + } else if (q->type == OUTPUT_MPLANE) {
>> + if (is_decode_session(inst))
>> + rc = msm_vdec_streamoff_output(inst);
>> + else if (is_encode_session(inst))
>> + rc = msm_venc_streamoff_output(inst);
>> + }
>> + if (rc)
>> + return rc;
>> +
>> + /* Input port streamoff */
>> + if (q->type == INPUT_MPLANE) {
>> + /* flush timestamps list */
>> + msm_vidc_flush_ts(inst);
>> + }
>> +
>> + /* print internal buffer memory usage stats */
>> + msm_vidc_print_memory_stats(inst);
>> +
>> + i_vpr_h(inst, "Streamoff: %s successful\n", v4l2_type_name(q->type));
>> + return rc;
>> +}
>> +
>> +void msm_vb2_stop_streaming(struct vb2_queue *q)
>> +{
>> + struct msm_vidc_inst *inst;
>> + int rc = 0;
>> +
>> + if (!q || !q->drv_priv) {
>> + d_vpr_e("%s: invalid input, q = %pK\n", __func__, q);
>> + return;
>> + }
>> + inst = q->drv_priv;
>> + if (!inst) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return;
>> + }
>> +
>> + rc = inst->event_handle(inst, MSM_VIDC_STREAMOFF, q);
>> + if (rc) {
>> + i_vpr_e(inst, "Streamoff: %s failed\n", v4l2_type_name(q->type));
>> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
>> + }
>> +}
>> +
>> +void msm_vb2_buf_queue(struct vb2_buffer *vb2)
>> +{
>> + int rc = 0;
>> + struct msm_vidc_inst *inst;
>> + struct dma_buf *dbuf = NULL;
>> + struct msm_vidc_core *core;
>> + u64 ktime_ns = ktime_get_ns();
>> +
>> + if (!vb2) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return;
>> + }
>> +
>> + inst = vb2_get_drv_priv(vb2->vb2_queue);
>> + if (!inst || !inst->core) {
>> + d_vpr_e("%s: invalid params\n", __func__);
>> + return;
>> + }
>> + core = inst->core;
>> +
>> + if (!vb2->planes[0].bytesused) {
>> + if (vb2->type == INPUT_MPLANE) {
>> + /* Expecting non-zero filledlen on INPUT port */
>> + i_vpr_e(inst,
>> + "%s: zero bytesused input buffer not supported\n", __func__);
>> + rc = -EINVAL;
>> + goto exit;
>> + }
>> + }
>> +
>> + inst->last_qbuf_time_ns = ktime_ns;
>> +
>> + if (vb2->type == INPUT_MPLANE) {
>> + rc = msm_vidc_update_input_rate(inst, div_u64(ktime_ns, 1000));
>> + if (rc)
>> + goto exit;
>> + }
>> +
>> + /*
>> + * Userspace may close fd(from other thread), before driver attempts to call
>> + * dma_buf_get() in qbuf(FTB) sequence(for decoder output buffer) which may
>> + * lead to different kind of security issues. Add check to compare if dma_buf
>> + * address is matching with driver dma_buf_get returned address for that fd.
>> + */
>> +
>> + dbuf = call_mem_op(core, dma_buf_get, inst, vb2->planes[0].m.fd);
>> + if (dbuf != vb2->planes[0].dbuf) {
>> + i_vpr_e(inst, "%s: invalid dmabuf address 0x%p expected 0x%p\n",
>> + __func__, dbuf, vb2->planes[0].dbuf);
>> + rc = -EINVAL;
>> + goto exit;
>> + }
>> +
>> + if (is_decode_session(inst))
>> + rc = msm_vdec_qbuf(inst, vb2);
>> + else if (is_encode_session(inst))
>> + rc = msm_venc_qbuf(inst, vb2);
>> + else
>> + rc = -EINVAL;
>> + if (rc) {
>> + print_vb2_buffer("failed vb2-qbuf", inst, vb2);
>> + goto exit;
>> + }
>> +
>> +exit:
>> + if (dbuf)
>> + call_mem_op(core, dma_buf_put, inst, dbuf);
>> +
>> + if (rc) {
>> + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
>> + vb2_buffer_done(vb2, VB2_BUF_STATE_ERROR);
>> + }
>> +}
>
new file mode 100644
@@ -0,0 +1,39 @@
+/* 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_VB2_H_
+#define _MSM_VIDC_VB2_H_
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "msm_vidc_inst.h"
+
+struct vb2_queue *msm_vidc_get_vb2q(struct msm_vidc_inst *inst,
+ u32 type, const char *func);
+
+/* vb2_mem_ops */
+void *msm_vb2_alloc(struct vb2_buffer *vb, struct device *dev,
+ unsigned long size);
+void *msm_vb2_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
+ struct dma_buf *dbuf, unsigned long size);
+
+void msm_vb2_put(void *buf_priv);
+int msm_vb2_mmap(void *buf_priv, struct vm_area_struct *vma);
+void msm_vb2_detach_dmabuf(void *buf_priv);
+int msm_vb2_map_dmabuf(void *buf_priv);
+void msm_vb2_unmap_dmabuf(void *buf_priv);
+
+/* vb2_ops */
+int msm_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[]);
+int msm_vidc_start_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q);
+int msm_vidc_stop_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q);
+int msm_vb2_start_streaming(struct vb2_queue *q, unsigned int count);
+void msm_vb2_stop_streaming(struct vb2_queue *q);
+void msm_vb2_buf_queue(struct vb2_buffer *vb2);
+#endif // _MSM_VIDC_VB2_H_
new file mode 100644
@@ -0,0 +1,605 @@
+// 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 "msm_vdec.h"
+#include "msm_venc.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_platform.h"
+#include "msm_vidc_power.h"
+#include "msm_vidc_vb2.h"
+
+struct vb2_queue *msm_vidc_get_vb2q(struct msm_vidc_inst *inst,
+ u32 type, const char *func)
+{
+ struct vb2_queue *q = NULL;
+
+ if (type == INPUT_MPLANE) {
+ q = inst->bufq[INPUT_PORT].vb2q;
+ } else if (type == OUTPUT_MPLANE) {
+ q = inst->bufq[OUTPUT_PORT].vb2q;
+ } else {
+ i_vpr_e(inst, "%s: invalid buffer type %d\n",
+ __func__, type);
+ }
+ return q;
+}
+
+void *msm_vb2_alloc(struct vb2_buffer *vb, struct device *dev,
+ unsigned long size)
+{
+ return (void *)0xdeadbeef;
+}
+
+void *msm_vb2_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
+ struct dma_buf *dbuf, unsigned long size)
+{
+ struct msm_vidc_inst *inst;
+ struct msm_vidc_core *core;
+ struct msm_vidc_buffer *buf = NULL;
+ struct msm_vidc_buffer *ro_buf, *dummy;
+
+ if (!vb || !dev || !dbuf || !vb->vb2_queue) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return NULL;
+ }
+ inst = vb->vb2_queue->drv_priv;
+ inst = get_inst_ref(g_core, inst);
+ if (!inst || !inst->core) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, inst);
+ return NULL;
+ }
+ core = inst->core;
+
+ buf = msm_vidc_fetch_buffer(inst, vb);
+ if (!buf) {
+ i_vpr_e(inst, "%s: failed to fetch buffer\n", __func__);
+ buf = NULL;
+ goto exit;
+ }
+ buf->inst = inst;
+ buf->dmabuf = dbuf;
+
+ if (is_decode_session(inst) && is_output_buffer(buf->type)) {
+ list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
+ if (ro_buf->dmabuf != buf->dmabuf)
+ continue;
+ print_vidc_buffer(VIDC_LOW, "low ", "attach: found ro buf", inst, ro_buf);
+ buf->attach = ro_buf->attach;
+ ro_buf->attach = NULL;
+ goto exit;
+ }
+ }
+
+ buf->attach = call_mem_op(core, dma_buf_attach, core, dbuf, dev);
+ if (!buf->attach) {
+ buf->attach = NULL;
+ buf = NULL;
+ goto exit;
+ }
+ print_vidc_buffer(VIDC_LOW, "low ", "attach", inst, buf);
+
+exit:
+ if (!buf)
+ msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
+ put_inst(inst);
+ return buf;
+}
+
+void msm_vb2_put(void *buf_priv)
+{
+}
+
+int msm_vb2_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ return 0;
+}
+
+void msm_vb2_detach_dmabuf(void *buf_priv)
+{
+ struct msm_vidc_buffer *vbuf = buf_priv;
+ struct msm_vidc_buffer *ro_buf, *dummy;
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst;
+
+ if (!vbuf || !vbuf->inst) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return;
+ }
+ inst = vbuf->inst;
+ if (!inst || !inst->core) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, inst);
+ return;
+ }
+ core = inst->core;
+
+ if (is_decode_session(inst) && is_output_buffer(vbuf->type)) {
+ list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
+ if (ro_buf->dmabuf != vbuf->dmabuf)
+ continue;
+ print_vidc_buffer(VIDC_LOW, "low ", "detach: found ro buf", inst, ro_buf);
+ ro_buf->attach = vbuf->attach;
+ vbuf->attach = NULL;
+ goto exit;
+ }
+ }
+
+ print_vidc_buffer(VIDC_LOW, "low ", "detach", inst, vbuf);
+ if (vbuf->attach && vbuf->dmabuf) {
+ call_mem_op(core, dma_buf_detach, core, vbuf->dmabuf, vbuf->attach);
+ vbuf->attach = NULL;
+ }
+
+exit:
+ vbuf->dmabuf = NULL;
+ vbuf->inst = NULL;
+}
+
+int msm_vb2_map_dmabuf(void *buf_priv)
+{
+ int rc = 0;
+ struct msm_vidc_buffer *buf = buf_priv;
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst;
+ struct msm_vidc_buffer *ro_buf, *dummy;
+
+ if (!buf || !buf->inst) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ inst = buf->inst;
+ inst = get_inst_ref(g_core, inst);
+ if (!inst || !inst->core) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+
+ if (is_decode_session(inst) && is_output_buffer(buf->type)) {
+ list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
+ if (ro_buf->dmabuf != buf->dmabuf)
+ continue;
+ print_vidc_buffer(VIDC_LOW, "low ", "map: found ro buf", inst, ro_buf);
+ buf->sg_table = ro_buf->sg_table;
+ buf->device_addr = ro_buf->device_addr;
+ ro_buf->sg_table = NULL;
+ goto exit;
+ }
+ }
+
+ buf->sg_table = call_mem_op(core, dma_buf_map_attachment, core, buf->attach);
+ if (!buf->sg_table || !buf->sg_table->sgl) {
+ buf->sg_table = NULL;
+ rc = -ENOMEM;
+ goto exit;
+ }
+ buf->device_addr = sg_dma_address(buf->sg_table->sgl);
+ print_vidc_buffer(VIDC_HIGH, "high", "map", inst, buf);
+
+exit:
+ if (rc)
+ msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
+ put_inst(inst);
+ return rc;
+}
+
+void msm_vb2_unmap_dmabuf(void *buf_priv)
+{
+ struct msm_vidc_buffer *vbuf = buf_priv;
+ struct msm_vidc_buffer *ro_buf, *dummy;
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst;
+
+ if (!vbuf || !vbuf->inst) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return;
+ }
+ inst = vbuf->inst;
+ if (!inst || !inst->core) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, inst);
+ return;
+ }
+ core = inst->core;
+
+ if (is_decode_session(inst) && is_output_buffer(vbuf->type)) {
+ list_for_each_entry_safe(ro_buf, dummy, &inst->buffers.read_only.list, list) {
+ if (ro_buf->dmabuf != vbuf->dmabuf)
+ continue;
+ print_vidc_buffer(VIDC_LOW, "low ", "unmap: found ro buf", inst, ro_buf);
+ ro_buf->sg_table = vbuf->sg_table;
+ vbuf->sg_table = NULL;
+ vbuf->device_addr = 0x0;
+ goto exit;
+ }
+ }
+
+ print_vidc_buffer(VIDC_HIGH, "high", "unmap", inst, vbuf);
+ if (vbuf->attach && vbuf->sg_table) {
+ call_mem_op(core, dma_buf_unmap_attachment, core, vbuf->attach, vbuf->sg_table);
+ vbuf->sg_table = NULL;
+ vbuf->device_addr = 0x0;
+ }
+
+exit:
+ return;
+}
+
+int msm_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ int rc = 0;
+ struct msm_vidc_inst *inst;
+ struct msm_vidc_core *core;
+ int port;
+ struct v4l2_format *f;
+ enum msm_vidc_buffer_type buffer_type = 0;
+ enum msm_vidc_buffer_region region = MSM_VIDC_REGION_NONE;
+ struct context_bank_info *cb = NULL;
+ struct msm_vidc_buffers *buffers;
+
+ if (!q || !num_buffers || !num_planes ||
+ !sizes || !q->drv_priv) {
+ d_vpr_e("%s: invalid params, q = %pK, %pK, %pK\n",
+ __func__, q, num_buffers, num_planes);
+ return -EINVAL;
+ }
+ inst = q->drv_priv;
+ if (!inst || !inst->core) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, inst);
+ return -EINVAL;
+ }
+ core = inst->core;
+
+ if (is_state(inst, MSM_VIDC_STREAMING)) {
+ i_vpr_e(inst, "%s: invalid state %d\n", __func__, inst->state);
+ return -EINVAL;
+ }
+
+ port = v4l2_type_to_driver_port(inst, q->type, __func__);
+ if (port < 0)
+ return -EINVAL;
+
+ /* prepare dependency list once per session */
+ if (!inst->caps_list_prepared) {
+ rc = msm_vidc_prepare_dependency_list(inst);
+ if (rc)
+ return rc;
+ inst->caps_list_prepared = true;
+ }
+
+ /* adjust v4l2 properties for master port */
+ if ((is_encode_session(inst) && port == OUTPUT_PORT) ||
+ (is_decode_session(inst) && port == INPUT_PORT)) {
+ rc = msm_vidc_adjust_v4l2_properties(inst);
+ if (rc) {
+ i_vpr_e(inst, "%s: failed to adjust properties\n", __func__);
+ return rc;
+ }
+ }
+
+ if (*num_planes && (port == INPUT_PORT || port == OUTPUT_PORT)) {
+ f = &inst->fmts[port];
+ if (*num_planes != f->fmt.pix_mp.num_planes) {
+ i_vpr_e(inst, "%s: requested num_planes %d not supported %d\n",
+ __func__, *num_planes, f->fmt.pix_mp.num_planes);
+ return -EINVAL;
+ }
+ if (sizes[0] < inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage) {
+ i_vpr_e(inst, "%s: requested size %d not acceptable\n",
+ __func__, sizes[0]);
+ return -EINVAL;
+ }
+ }
+
+ buffer_type = v4l2_type_to_driver(q->type, __func__);
+ if (!buffer_type)
+ return -EINVAL;
+
+ rc = msm_vidc_free_buffers(inst, buffer_type);
+ if (rc) {
+ i_vpr_e(inst, "%s: failed to free buffers, type %s\n",
+ __func__, v4l2_type_name(q->type));
+ return rc;
+ }
+
+ buffers = msm_vidc_get_buffers(inst, buffer_type, __func__);
+ if (!buffers)
+ return -EINVAL;
+
+ buffers->min_count = call_session_op(core, min_count, inst, buffer_type);
+ buffers->extra_count = call_session_op(core, extra_count, inst, buffer_type);
+ if (*num_buffers < buffers->min_count + buffers->extra_count)
+ *num_buffers = buffers->min_count + buffers->extra_count;
+ buffers->actual_count = *num_buffers;
+ *num_planes = 1;
+
+ buffers->size = call_session_op(core, buffer_size, inst, buffer_type);
+
+ inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage = buffers->size;
+ sizes[0] = inst->fmts[port].fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ rc = msm_vidc_allocate_buffers(inst, buffer_type, *num_buffers);
+ if (rc) {
+ i_vpr_e(inst, "%s: failed to allocate buffers, type %s\n",
+ __func__, v4l2_type_name(q->type));
+ return rc;
+ }
+
+ region = call_mem_op(core, buffer_region, inst, buffer_type);
+ cb = msm_vidc_get_context_bank_for_region(core, region);
+ if (!cb) {
+ d_vpr_e("%s: Failed to get context bank device\n",
+ __func__);
+ return -EIO;
+ }
+ q->dev = cb->dev;
+
+ i_vpr_h(inst,
+ "queue_setup: type %s num_buffers %d sizes[0] %d cb %s\n",
+ v4l2_type_name(q->type), *num_buffers, sizes[0], cb->name);
+ return rc;
+}
+
+int msm_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ int rc = 0;
+ struct msm_vidc_inst *inst;
+
+ if (!q || !q->drv_priv) {
+ d_vpr_e("%s: invalid input, q = %pK\n", __func__, q);
+ return -EINVAL;
+ }
+ inst = q->drv_priv;
+ if (!inst || !inst->core) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = inst->event_handle(inst, MSM_VIDC_STREAMON, q);
+ if (rc) {
+ i_vpr_e(inst, "Streamon: %s failed\n", v4l2_type_name(q->type));
+ msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
+ goto exit;
+ }
+
+exit:
+ return rc;
+}
+
+int msm_vidc_start_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q)
+{
+ enum msm_vidc_buffer_type buf_type;
+ int rc = 0;
+
+ if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
+ return -EINVAL;
+ }
+ if (!is_decode_session(inst) && !is_encode_session(inst)) {
+ i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain);
+ return -EINVAL;
+ }
+ i_vpr_h(inst, "Streamon: %s\n", v4l2_type_name(q->type));
+
+ if (!inst->once_per_session_set) {
+ inst->once_per_session_set = true;
+ rc = msm_vidc_session_set_codec(inst);
+ if (rc)
+ return rc;
+
+ if (is_encode_session(inst)) {
+ rc = msm_vidc_alloc_and_queue_session_int_bufs(inst,
+ MSM_VIDC_BUF_ARP);
+ if (rc)
+ return rc;
+ } else if (is_decode_session(inst)) {
+ rc = msm_vidc_session_set_default_header(inst);
+ if (rc)
+ return rc;
+
+ rc = msm_vidc_alloc_and_queue_session_int_bufs(inst,
+ MSM_VIDC_BUF_PERSIST);
+ if (rc)
+ return rc;
+ }
+ }
+
+ if (is_decode_session(inst))
+ inst->decode_batch.enable = msm_vidc_allow_decode_batch(inst);
+
+ msm_vidc_allow_dcvs(inst);
+ msm_vidc_power_data_reset(inst);
+
+ if (q->type == INPUT_MPLANE) {
+ if (is_decode_session(inst))
+ rc = msm_vdec_streamon_input(inst);
+ else if (is_encode_session(inst))
+ rc = msm_venc_streamon_input(inst);
+ } else if (q->type == OUTPUT_MPLANE) {
+ if (is_decode_session(inst))
+ rc = msm_vdec_streamon_output(inst);
+ else if (is_encode_session(inst))
+ rc = msm_venc_streamon_output(inst);
+ }
+ if (rc)
+ return rc;
+
+ /* print final buffer counts & size details */
+ msm_vidc_print_buffer_info(inst);
+
+ /* print internal buffer memory usage stats */
+ msm_vidc_print_memory_stats(inst);
+
+ buf_type = v4l2_type_to_driver(q->type, __func__);
+ if (!buf_type)
+ return -EINVAL;
+
+ /* queue pending buffers */
+ rc = msm_vidc_queue_deferred_buffers(inst, buf_type);
+ if (rc)
+ return rc;
+
+ /* initialize statistics timer(one time) */
+ if (!inst->stats.time_ms)
+ inst->stats.time_ms = ktime_get_ns() / 1000 / 1000;
+
+ /* schedule to print buffer statistics */
+ rc = schedule_stats_work(inst);
+ if (rc)
+ return rc;
+
+ if ((q->type == INPUT_MPLANE && inst->bufq[OUTPUT_PORT].vb2q->streaming) ||
+ (q->type == OUTPUT_MPLANE && inst->bufq[INPUT_PORT].vb2q->streaming)) {
+ rc = msm_vidc_get_properties(inst);
+ if (rc)
+ return rc;
+ }
+
+ i_vpr_h(inst, "Streamon: %s successful\n", v4l2_type_name(q->type));
+ return rc;
+}
+
+int msm_vidc_stop_streaming(struct msm_vidc_inst *inst, struct vb2_queue *q)
+{
+ int rc = 0;
+
+ if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
+ return -EINVAL;
+ }
+ if (!is_decode_session(inst) && !is_encode_session(inst)) {
+ i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain);
+ return -EINVAL;
+ }
+ i_vpr_h(inst, "Streamoff: %s\n", v4l2_type_name(q->type));
+
+ if (q->type == INPUT_MPLANE) {
+ if (is_decode_session(inst))
+ rc = msm_vdec_streamoff_input(inst);
+ else if (is_encode_session(inst))
+ rc = msm_venc_streamoff_input(inst);
+ } else if (q->type == OUTPUT_MPLANE) {
+ if (is_decode_session(inst))
+ rc = msm_vdec_streamoff_output(inst);
+ else if (is_encode_session(inst))
+ rc = msm_venc_streamoff_output(inst);
+ }
+ if (rc)
+ return rc;
+
+ /* Input port streamoff */
+ if (q->type == INPUT_MPLANE) {
+ /* flush timestamps list */
+ msm_vidc_flush_ts(inst);
+ }
+
+ /* print internal buffer memory usage stats */
+ msm_vidc_print_memory_stats(inst);
+
+ i_vpr_h(inst, "Streamoff: %s successful\n", v4l2_type_name(q->type));
+ return rc;
+}
+
+void msm_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct msm_vidc_inst *inst;
+ int rc = 0;
+
+ if (!q || !q->drv_priv) {
+ d_vpr_e("%s: invalid input, q = %pK\n", __func__, q);
+ return;
+ }
+ inst = q->drv_priv;
+ if (!inst) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return;
+ }
+
+ rc = inst->event_handle(inst, MSM_VIDC_STREAMOFF, q);
+ if (rc) {
+ i_vpr_e(inst, "Streamoff: %s failed\n", v4l2_type_name(q->type));
+ msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
+ }
+}
+
+void msm_vb2_buf_queue(struct vb2_buffer *vb2)
+{
+ int rc = 0;
+ struct msm_vidc_inst *inst;
+ struct dma_buf *dbuf = NULL;
+ struct msm_vidc_core *core;
+ u64 ktime_ns = ktime_get_ns();
+
+ if (!vb2) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return;
+ }
+
+ inst = vb2_get_drv_priv(vb2->vb2_queue);
+ if (!inst || !inst->core) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return;
+ }
+ core = inst->core;
+
+ if (!vb2->planes[0].bytesused) {
+ if (vb2->type == INPUT_MPLANE) {
+ /* Expecting non-zero filledlen on INPUT port */
+ i_vpr_e(inst,
+ "%s: zero bytesused input buffer not supported\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+ }
+
+ inst->last_qbuf_time_ns = ktime_ns;
+
+ if (vb2->type == INPUT_MPLANE) {
+ rc = msm_vidc_update_input_rate(inst, div_u64(ktime_ns, 1000));
+ if (rc)
+ goto exit;
+ }
+
+ /*
+ * Userspace may close fd(from other thread), before driver attempts to call
+ * dma_buf_get() in qbuf(FTB) sequence(for decoder output buffer) which may
+ * lead to different kind of security issues. Add check to compare if dma_buf
+ * address is matching with driver dma_buf_get returned address for that fd.
+ */
+
+ dbuf = call_mem_op(core, dma_buf_get, inst, vb2->planes[0].m.fd);
+ if (dbuf != vb2->planes[0].dbuf) {
+ i_vpr_e(inst, "%s: invalid dmabuf address 0x%p expected 0x%p\n",
+ __func__, dbuf, vb2->planes[0].dbuf);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (is_decode_session(inst))
+ rc = msm_vdec_qbuf(inst, vb2);
+ else if (is_encode_session(inst))
+ rc = msm_venc_qbuf(inst, vb2);
+ else
+ rc = -EINVAL;
+ if (rc) {
+ print_vb2_buffer("failed vb2-qbuf", inst, vb2);
+ goto exit;
+ }
+
+exit:
+ if (dbuf)
+ call_mem_op(core, dma_buf_put, inst, dbuf);
+
+ if (rc) {
+ msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__);
+ vb2_buffer_done(vb2, VB2_BUF_STATE_ERROR);
+ }
+}