// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include #include "iris_instance.h" #include "iris_vb2.h" #include "iris_vdec.h" #include "iris_power.h" static int iris_check_core_mbpf(struct iris_inst *inst) { struct iris_core *core = inst->core; struct iris_inst *instance; u32 total_mbpf = 0; mutex_lock(&core->lock); list_for_each_entry(instance, &core->instances, list) total_mbpf += iris_get_mbpf(instance); mutex_unlock(&core->lock); if (total_mbpf > core->iris_platform_data->max_core_mbpf) return -ENOMEM; return 0; } 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); if (V4L2_TYPE_IS_CAPTURE(q->type) && inst->state == IRIS_INST_INIT) return 0; 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)) ret = iris_vdec_streamon_input(inst); else if (V4L2_TYPE_IS_CAPTURE(q->type)) ret = iris_vdec_streamon_output(inst); if (ret) goto error; buf_type = iris_v4l2_type_to_driver(q->type); ret = iris_queue_deferred_buffers(inst, buf_type); 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_vdec_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 (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 (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); } goto exit; } } v4l2_m2m_buf_queue(m2m_ctx, vbuf); ret = iris_vdec_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); }