root/drivers/media/platform/qcom/iris/iris_vb2.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>

#include "iris_common.h"
#include "iris_instance.h"
#include "iris_vb2.h"
#include "iris_vdec.h"
#include "iris_venc.h"
#include "iris_power.h"

static int iris_check_inst_mbpf(struct iris_inst *inst)
{
        struct platform_inst_caps *caps;
        u32 mbpf, max_mbpf;

        caps = inst->core->iris_platform_data->inst_caps;
        max_mbpf = caps->max_mbpf;
        mbpf = iris_get_mbpf(inst);
        if (mbpf > max_mbpf)
                return -ENOMEM;

        return 0;
}

static int iris_check_resolution_supported(struct iris_inst *inst)
{
        u32 width, height, min_width, min_height, max_width, max_height;
        struct platform_inst_caps *caps;

        caps = inst->core->iris_platform_data->inst_caps;
        width = inst->fmt_src->fmt.pix_mp.width;
        height = inst->fmt_src->fmt.pix_mp.height;

        min_width = caps->min_frame_width;
        max_width = caps->max_frame_width;
        min_height = caps->min_frame_height;
        max_height = caps->max_frame_height;

        if (!(min_width <= width && width <= max_width) ||
            !(min_height <= height && height <= max_height))
                return -EINVAL;

        return 0;
}

static int iris_check_session_supported(struct iris_inst *inst)
{
        struct iris_core *core = inst->core;
        struct iris_inst *instance = NULL;
        bool found = false;
        int ret;

        list_for_each_entry(instance, &core->instances, list) {
                if (instance == inst)
                        found = true;
        }

        if (!found) {
                ret = -EINVAL;
                goto exit;
        }

        ret = iris_check_core_mbpf(inst);
        if (ret)
                goto exit;

        ret = iris_check_inst_mbpf(inst);
        if (ret)
                goto exit;

        ret = iris_check_resolution_supported(inst);
        if (ret)
                goto exit;

        return 0;
exit:
        dev_err(inst->core->dev, "current session not supported(%d)\n", ret);

        return ret;
}

int iris_vb2_buf_init(struct vb2_buffer *vb2)
{
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
        struct iris_buffer *buf = to_iris_buffer(vbuf);

        buf->device_addr = vb2_dma_contig_plane_dma_addr(vb2, 0);

        return 0;
}

int iris_vb2_queue_setup(struct vb2_queue *q,
                         unsigned int *num_buffers, unsigned int *num_planes,
                         unsigned int sizes[], struct device *alloc_devs[])
{
        struct iris_inst *inst;
        struct iris_core *core;
        struct v4l2_format *f;
        int ret = 0;

        inst = vb2_get_drv_priv(q);

        mutex_lock(&inst->lock);
        if (inst->state == IRIS_INST_ERROR) {
                ret = -EBUSY;
                goto unlock;
        }

        core = inst->core;
        f = V4L2_TYPE_IS_OUTPUT(q->type) ? inst->fmt_src : inst->fmt_dst;

        if (*num_planes) {
                if (*num_planes != f->fmt.pix_mp.num_planes ||
                    sizes[0] < f->fmt.pix_mp.plane_fmt[0].sizeimage)
                        ret = -EINVAL;
                goto unlock;
        }

        ret = iris_check_session_supported(inst);
        if (ret)
                goto unlock;

        if (!inst->once_per_session_set) {
                inst->once_per_session_set = true;

                ret = core->hfi_ops->session_open(inst);
                if (ret) {
                        ret = -EINVAL;
                        dev_err(core->dev, "session open failed\n");
                        goto unlock;
                }

                ret = iris_inst_change_state(inst, IRIS_INST_INIT);
                if (ret)
                        goto unlock;
        }

        *num_planes = 1;
        sizes[0] = f->fmt.pix_mp.plane_fmt[0].sizeimage;

unlock:
        mutex_unlock(&inst->lock);

        return ret;
}

int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
{
        enum iris_buffer_type buf_type;
        struct iris_inst *inst;
        int ret = 0;

        inst = vb2_get_drv_priv(q);

        mutex_lock(&inst->lock);
        if (inst->state == IRIS_INST_ERROR) {
                ret = -EBUSY;
                goto error;
        }

        if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
            !V4L2_TYPE_IS_CAPTURE(q->type)) {
                ret = -EINVAL;
                goto error;
        }

        iris_scale_power(inst);

        ret = iris_check_session_supported(inst);
        if (ret)
                goto error;

        if (V4L2_TYPE_IS_OUTPUT(q->type)) {
                if (inst->domain == DECODER)
                        ret = iris_vdec_streamon_input(inst);
                else
                        ret = iris_venc_streamon_input(inst);
        } else if (V4L2_TYPE_IS_CAPTURE(q->type)) {
                if (inst->domain == DECODER)
                        ret = iris_vdec_streamon_output(inst);
                else
                        ret = iris_venc_streamon_output(inst);
        }
        if (ret)
                goto error;

        buf_type = iris_v4l2_type_to_driver(q->type);

        if (inst->domain == DECODER) {
                if (buf_type == BUF_INPUT)
                        ret = iris_queue_deferred_buffers(inst, BUF_INPUT);

                if (!ret && inst->state == IRIS_INST_STREAMING) {
                        ret = iris_queue_internal_deferred_buffers(inst, BUF_DPB);
                        if (!ret)
                                ret = iris_queue_deferred_buffers(inst, BUF_OUTPUT);
                }
        } else {
                if (inst->state == IRIS_INST_STREAMING) {
                        ret = iris_queue_deferred_buffers(inst, BUF_INPUT);
                        if (!ret)
                                ret = iris_queue_deferred_buffers(inst, BUF_OUTPUT);
                }
        }

        if (ret)
                goto error;

        mutex_unlock(&inst->lock);

        return ret;

error:
        iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED);
        iris_inst_change_state(inst, IRIS_INST_ERROR);
        mutex_unlock(&inst->lock);

        return ret;
}

void iris_vb2_stop_streaming(struct vb2_queue *q)
{
        struct iris_inst *inst;
        int ret = 0;

        inst = vb2_get_drv_priv(q);

        if (V4L2_TYPE_IS_CAPTURE(q->type) && inst->state == IRIS_INST_INIT)
                return;

        mutex_lock(&inst->lock);

        if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
            !V4L2_TYPE_IS_CAPTURE(q->type))
                goto exit;

        ret = iris_session_streamoff(inst, q->type);
        if (ret)
                goto exit;

exit:
        iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
        if (ret)
                iris_inst_change_state(inst, IRIS_INST_ERROR);

        mutex_unlock(&inst->lock);
}

int iris_vb2_buf_prepare(struct vb2_buffer *vb)
{
        struct iris_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);

        if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
                if (vbuf->field == V4L2_FIELD_ANY)
                        vbuf->field = V4L2_FIELD_NONE;
                if (vbuf->field != V4L2_FIELD_NONE)
                        return -EINVAL;
        }

        if (!(inst->sub_state & IRIS_INST_SUB_DRC)) {
                if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
                    vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT))
                        return -EINVAL;
                if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
                    vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT))
                        return -EINVAL;
        }
        return 0;
}

int iris_vb2_buf_out_validate(struct vb2_buffer *vb)
{
        struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);

        v4l2_buf->field = V4L2_FIELD_NONE;

        return 0;
}

void iris_vb2_buf_queue(struct vb2_buffer *vb2)
{
        static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS };
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
        struct v4l2_m2m_ctx *m2m_ctx;
        struct iris_inst *inst;
        int ret = 0;

        inst = vb2_get_drv_priv(vb2->vb2_queue);

        mutex_lock(&inst->lock);
        if (inst->state == IRIS_INST_ERROR) {
                ret = -EBUSY;
                goto exit;
        }

        if (vbuf->field == V4L2_FIELD_ANY)
                vbuf->field = V4L2_FIELD_NONE;

        m2m_ctx = inst->m2m_ctx;

        if (!vb2->planes[0].bytesused && V4L2_TYPE_IS_OUTPUT(vb2->type)) {
                ret = -EINVAL;
                goto exit;
        }

        if (!inst->last_buffer_dequeued && V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) {
                if ((inst->sub_state & IRIS_INST_SUB_DRC &&
                     inst->sub_state & IRIS_INST_SUB_DRC_LAST) ||
                    (inst->sub_state & IRIS_INST_SUB_DRAIN &&
                     inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)) {
                        vbuf->flags |= V4L2_BUF_FLAG_LAST;
                        vbuf->sequence = inst->sequence_cap++;
                        vbuf->field = V4L2_FIELD_NONE;
                        vb2_set_plane_payload(vb2, 0, 0);
                        v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
                        if (!v4l2_m2m_has_stopped(m2m_ctx)) {
                                v4l2_event_queue_fh(&inst->fh, &eos);
                                v4l2_m2m_mark_stopped(m2m_ctx);
                        }
                        inst->last_buffer_dequeued = true;
                        goto exit;
                }
        }

        v4l2_m2m_buf_queue(m2m_ctx, vbuf);

        if (inst->domain == DECODER)
                ret = iris_vdec_qbuf(inst, vbuf);
        else
                ret = iris_venc_qbuf(inst, vbuf);

exit:
        if (ret) {
                iris_inst_change_state(inst, IRIS_INST_ERROR);
                v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
        }
        mutex_unlock(&inst->lock);
}