Re: [PATCH v6 20/28] media: iris: implement vb2 ops for buf_queue and firmware response
From: Hans Verkuil
Date: Fri Dec 06 2024 - 04:53:30 EST
On 20/11/2024 15:46, Dikshita Agarwal wrote:
> Implement vb2 ops for buf queue. Below are the different buffer
> attributes:
> BUF_ATTR_DEFERRED - buffer queued by client but not submitted to
> firmware.
> BUF_ATTR_PENDING_RELEASE - buffers requested to be released from
> firmware.
> BUF_ATTR_QUEUED - buffers submitted to firmware.
> BUF_ATTR_DEQUEUED - buffers received from firmware.
> BUF_ATTR_BUFFER_DONE - buffers sent back to vb2.
>
> Signed-off-by: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx>
> ---
> drivers/media/platform/qcom/iris/Kconfig | 1 +
> drivers/media/platform/qcom/iris/iris_buffer.c | 117 +++++++++++++
> drivers/media/platform/qcom/iris/iris_buffer.h | 2 +
> .../platform/qcom/iris/iris_hfi_gen1_command.c | 53 ++++++
> .../platform/qcom/iris/iris_hfi_gen1_defines.h | 80 +++++++++
> .../platform/qcom/iris/iris_hfi_gen1_response.c | 167 ++++++++++++++++++-
> drivers/media/platform/qcom/iris/iris_hfi_gen2.h | 2 +
> .../platform/qcom/iris/iris_hfi_gen2_defines.h | 22 +++
> .../platform/qcom/iris/iris_hfi_gen2_response.c | 183 ++++++++++++++++++++-
> drivers/media/platform/qcom/iris/iris_instance.h | 8 +
> drivers/media/platform/qcom/iris/iris_utils.c | 16 ++
> drivers/media/platform/qcom/iris/iris_utils.h | 16 ++
> drivers/media/platform/qcom/iris/iris_vb2.c | 98 ++++++++++-
> drivers/media/platform/qcom/iris/iris_vb2.h | 4 +
> drivers/media/platform/qcom/iris/iris_vdec.c | 151 ++++++++++++++++-
> drivers/media/platform/qcom/iris/iris_vdec.h | 3 +-
> drivers/media/platform/qcom/iris/iris_vidc.c | 14 ++
> 17 files changed, 917 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig
> index f92cc7fe9378..3c803a05305a 100644
> --- a/drivers/media/platform/qcom/iris/Kconfig
> +++ b/drivers/media/platform/qcom/iris/Kconfig
> @@ -5,6 +5,7 @@ config VIDEO_QCOM_IRIS
> select V4L2_MEM2MEM_DEV
> select QCOM_MDT_LOADER if ARCH_QCOM
> select QCOM_SCM
> + select VIDEOBUF2_DMA_CONTIG
> help
> This is a V4L2 driver for Qualcomm iris video accelerator
> hardware. It accelerates decoding operations on various
> diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
> index e9d372580b5f..ac35ff334345 100644
> --- a/drivers/media/platform/qcom/iris/iris_buffer.c
> +++ b/drivers/media/platform/qcom/iris/iris_buffer.c
> @@ -3,6 +3,7 @@
> * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> +#include <media/v4l2-event.h>
> #include <media/v4l2-mem2mem.h>
>
> #include "iris_buffer.h"
> @@ -434,6 +435,36 @@ int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst)
> return 0;
> }
>
> +int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *buffer, *n;
> + struct iris_buffer *buf;
> + int ret;
> +
> + if (buf_type == BUF_INPUT) {
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (!(buf->attr & BUF_ATTR_DEFERRED))
> + continue;
> + ret = iris_queue_buffer(inst, buf);
> + if (ret)
> + return ret;
> + }
> + } else {
> + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (!(buf->attr & BUF_ATTR_DEFERRED))
> + continue;
> + ret = iris_queue_buffer(inst, buf);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> void iris_vb2_queue_error(struct iris_inst *inst)
> {
> struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> @@ -444,3 +475,89 @@ void iris_vb2_queue_error(struct iris_inst *inst)
> q = v4l2_m2m_get_dst_vq(m2m_ctx);
> vb2_queue_error(q);
> }
> +
> +static struct vb2_v4l2_buffer *
> +iris_helper_find_buf(struct iris_inst *inst, u32 type, u32 idx)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> +
> + if (V4L2_TYPE_IS_OUTPUT(type))
> + return v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, idx);
> + else
> + return v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, idx);
> +}
> +
> +static void iris_get_ts_metadata(struct iris_inst *inst, u64 timestamp_ns,
> + struct vb2_v4l2_buffer *vbuf)
> +{
> + u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
> + u32 i;
> +
> + for (i = 0; i < ARRAY_SIZE(inst->tss); ++i) {
> + if (inst->tss[i].ts_ns != timestamp_ns)
> + continue;
> +
> + vbuf->flags &= ~mask;
> + vbuf->flags |= inst->tss[i].flags;
> + vbuf->timecode = inst->tss[i].tc;
> + return;
> + }
> +
> + vbuf->flags &= ~mask;
> + vbuf->flags |= inst->tss[inst->metadata_idx].flags;
> + vbuf->timecode = inst->tss[inst->metadata_idx].tc;
> +}
> +
> +int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct vb2_v4l2_buffer *vbuf;
> + struct vb2_buffer *vb2;
> + u32 type, state;
> +
> + switch (buf->type) {
> + case BUF_INPUT:
> + type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> + break;
> + case BUF_OUTPUT:
> + type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> + break;
> + default:
> + return 0; /* Internal DPB Buffers */
> + }
> +
> + vbuf = iris_helper_find_buf(inst, type, buf->index);
> + if (!vbuf)
> + return -EINVAL;
> +
> + vb2 = &vbuf->vb2_buf;
> +
> + if (buf->flags & V4L2_BUF_FLAG_ERROR)
> + state = VB2_BUF_STATE_ERROR;
> + else
> + state = VB2_BUF_STATE_DONE;
> +
> + vbuf->flags |= buf->flags;
> +
> + if (V4L2_TYPE_IS_CAPTURE(type)) {
> + vb2_set_plane_payload(vb2, 0, buf->data_size);
> + vbuf->sequence = inst->sequence_cap++;
> + iris_get_ts_metadata(inst, buf->timestamp, vbuf);
> + } else {
> + vbuf->sequence = inst->sequence_out++;
> + }
> +
> + if (vbuf->flags & V4L2_BUF_FLAG_LAST) {
> + if (!v4l2_m2m_has_stopped(m2m_ctx) &&
> + inst->subscriptions & V4L2_EVENT_EOS) {
> + const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
> +
> + v4l2_event_queue_fh(&inst->fh, &ev);
> + v4l2_m2m_mark_stopped(m2m_ctx);
This makes no sense: calling v4l2_m2m_mark_stopped() is independent of whether someone subscribed
to the EOS event. And you can also just call v4l2_event_queue_fh: the event framework knows if
someone has subscribed to this event or not.
So just drop the 'inst->subscriptions & V4L2_EVENT_EOS' check.
Regards,
Hans
> + }
> + }
> + vb2->timestamp = buf->timestamp;
> + v4l2_m2m_buf_done(vbuf, state);
> +
> + return 0;
> +}
> diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h
> index e202524663eb..06fb32c798f9 100644
> --- a/drivers/media/platform/qcom/iris/iris_buffer.h
> +++ b/drivers/media/platform/qcom/iris/iris_buffer.h
> @@ -109,6 +109,8 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf
> int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane);
> int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst);
> int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf);
> +int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type);
> +int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf);
> void iris_vb2_queue_error(struct iris_inst *inst);
>
> #endif
> diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
> index 603ca485992d..03f7e6ea4bf3 100644
> --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
> +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
> @@ -180,6 +180,10 @@ static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane)
> ret = iris_hfi_queue_cmd_write(core, &pkt, pkt.shdr.hdr.size);
> if (!ret)
> ret = iris_wait_for_session_response(inst, false);
> + iris_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
> + VB2_BUF_STATE_ERROR);
> + iris_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> + VB2_BUF_STATE_ERROR);
> } else if (inst->state == IRIS_INST_STREAMING) {
> if (V4L2_TYPE_IS_OUTPUT(plane))
> flush_type = HFI_FLUSH_ALL;
> @@ -201,6 +205,50 @@ static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane)
> return ret;
> }
>
> +static int iris_hfi_gen1_queue_input_buffer(struct iris_inst *inst, struct iris_buffer *buf)
> +{
> + struct hfi_session_empty_buffer_compressed_pkt ip_pkt;
> +
> + ip_pkt.shdr.hdr.size = sizeof(struct hfi_session_empty_buffer_compressed_pkt);
> + ip_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
> + ip_pkt.shdr.session_id = inst->session_id;
> + ip_pkt.time_stamp_hi = upper_32_bits(buf->timestamp);
> + ip_pkt.time_stamp_lo = lower_32_bits(buf->timestamp);
> + ip_pkt.flags = buf->flags;
> + ip_pkt.mark_target = 0;
> + ip_pkt.mark_data = 0;
> + ip_pkt.offset = buf->data_offset;
> + ip_pkt.alloc_len = buf->buffer_size;
> + ip_pkt.filled_len = buf->data_size;
> + ip_pkt.input_tag = buf->index;
> + ip_pkt.packet_buffer = buf->device_addr;
> +
> + return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size);
> +}
> +
> +static int iris_hfi_gen1_queue_output_buffer(struct iris_inst *inst, struct iris_buffer *buf)
> +{
> + struct hfi_session_fill_buffer_pkt op_pkt;
> +
> + op_pkt.shdr.hdr.size = sizeof(struct hfi_session_fill_buffer_pkt);
> + op_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FILL_BUFFER;
> + op_pkt.shdr.session_id = inst->session_id;
> + op_pkt.output_tag = buf->index;
> + op_pkt.packet_buffer = buf->device_addr;
> + op_pkt.extradata_buffer = 0;
> + op_pkt.alloc_len = buf->buffer_size;
> + op_pkt.filled_len = buf->data_size;
> + op_pkt.offset = buf->data_offset;
> + op_pkt.data = 0;
> +
> + if (buf->type == BUF_OUTPUT && iris_split_mode_enabled(inst))
> + op_pkt.stream_id = 1;
> + else
> + op_pkt.stream_id = 0;
> +
> + return iris_hfi_queue_cmd_write(inst->core, &op_pkt, op_pkt.shdr.hdr.size);
> +}
> +
> static int iris_hfi_gen1_queue_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf)
> {
> struct hfi_session_set_buffers_pkt *int_pkt;
> @@ -240,6 +288,11 @@ static int iris_hfi_gen1_queue_internal_buffer(struct iris_inst *inst, struct ir
> static int iris_hfi_gen1_session_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
> {
> switch (buf->type) {
> + case BUF_INPUT:
> + return iris_hfi_gen1_queue_input_buffer(inst, buf);
> + case BUF_OUTPUT:
> + case BUF_DPB:
> + return iris_hfi_gen1_queue_output_buffer(inst, buf);
> case BUF_PERSIST:
> case BUF_BIN:
> case BUF_SCRATCH_1:
> diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
> index cabd91eafc92..108449d703e1 100644
> --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
> +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
> @@ -29,11 +29,14 @@
> #define HFI_CMD_SESSION_LOAD_RESOURCES 0x211001
> #define HFI_CMD_SESSION_START 0x211002
> #define HFI_CMD_SESSION_STOP 0x211003
> +#define HFI_CMD_SESSION_EMPTY_BUFFER 0x211004
> +#define HFI_CMD_SESSION_FILL_BUFFER 0x211005
> #define HFI_CMD_SESSION_FLUSH 0x211008
> #define HFI_CMD_SESSION_RELEASE_BUFFERS 0x21100b
> #define HFI_CMD_SESSION_RELEASE_RESOURCES 0x21100c
>
> #define HFI_ERR_SESSION_UNSUPPORTED_SETTING 0x1008
> +#define HFI_ERR_SESSION_UNSUPPORTED_STREAM 0x100d
> #define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE 0x1010
> #define HFI_ERR_SESSION_INVALID_SCALE_FACTOR 0x1012
> #define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED 0x1013
> @@ -41,6 +44,8 @@
> #define HFI_EVENT_SYS_ERROR 0x1
> #define HFI_EVENT_SESSION_ERROR 0x2
>
> +#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
> +
> #define HFI_FLUSH_OUTPUT 0x1000002
> #define HFI_FLUSH_OUTPUT2 0x1000003
> #define HFI_FLUSH_ALL 0x1000004
> @@ -84,9 +89,19 @@
> #define HFI_MSG_SESSION_START 0x221002
> #define HFI_MSG_SESSION_STOP 0x221003
> #define HFI_MSG_SESSION_FLUSH 0x221006
> +#define HFI_MSG_SESSION_EMPTY_BUFFER 0x221007
> +#define HFI_MSG_SESSION_FILL_BUFFER 0x221008
> #define HFI_MSG_SESSION_RELEASE_RESOURCES 0x22100a
> #define HFI_MSG_SESSION_RELEASE_BUFFERS 0x22100c
>
> +#define HFI_PICTURE_I 0x00000001
> +#define HFI_PICTURE_P 0x00000002
> +#define HFI_PICTURE_B 0x00000004
> +#define HFI_PICTURE_IDR 0x00000008
> +#define HFI_FRAME_NOTCODED 0x7f002000
> +#define HFI_FRAME_YUV 0x7f004000
> +#define HFI_UNUSED_PICT 0x10000000
> +
> struct hfi_pkt_hdr {
> u32 size;
> u32 pkt_type;
> @@ -144,6 +159,34 @@ struct hfi_session_set_buffers_pkt {
> u32 buffer_info[];
> };
>
> +struct hfi_session_empty_buffer_compressed_pkt {
> + struct hfi_session_hdr_pkt shdr;
> + u32 time_stamp_hi;
> + u32 time_stamp_lo;
> + u32 flags;
> + u32 mark_target;
> + u32 mark_data;
> + u32 offset;
> + u32 alloc_len;
> + u32 filled_len;
> + u32 input_tag;
> + u32 packet_buffer;
> + u32 extradata_buffer;
> + u32 data;
> +};
> +
> +struct hfi_session_fill_buffer_pkt {
> + struct hfi_session_hdr_pkt shdr;
> + u32 stream_id;
> + u32 offset;
> + u32 alloc_len;
> + u32 filled_len;
> + u32 output_tag;
> + u32 packet_buffer;
> + u32 extradata_buffer;
> + u32 data;
> +};
> +
> struct hfi_session_flush_pkt {
> struct hfi_session_hdr_pkt shdr;
> u32 flush_type;
> @@ -258,6 +301,43 @@ struct hfi_multi_stream {
> u32 enable;
> };
>
> +struct hfi_msg_session_empty_buffer_done_pkt {
> + struct hfi_msg_session_hdr_pkt shdr;
> + u32 offset;
> + u32 filled_len;
> + u32 input_tag;
> + u32 packet_buffer;
> + u32 extradata_buffer;
> + u32 data[];
> +};
> +
> +struct hfi_msg_session_fbd_uncompressed_plane0_pkt {
> + struct hfi_session_hdr_pkt shdr;
> + u32 stream_id;
> + u32 view_id;
> + u32 error_type;
> + u32 time_stamp_hi;
> + u32 time_stamp_lo;
> + u32 flags;
> + u32 mark_target;
> + u32 mark_data;
> + u32 stats;
> + u32 alloc_len;
> + u32 filled_len;
> + u32 offset;
> + u32 frame_width;
> + u32 frame_height;
> + u32 start_x_coord;
> + u32 start_y_coord;
> + u32 input_tag;
> + u32 input_tag2;
> + u32 output_tag;
> + u32 picture_type;
> + u32 packet_buffer;
> + u32 extradata_buffer;
> + u32 data[];
> +};
> +
> struct hfi_msg_session_release_buffers_done_pkt {
> struct hfi_msg_session_hdr_pkt shdr;
> u32 num_buffers;
> diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
> index a84bb00388d9..23a8bf29e381 100644
> --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
> +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
> @@ -3,6 +3,8 @@
> * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> +#include <media/v4l2-mem2mem.h>
> +
> #include "iris_hfi_gen1.h"
> #include "iris_hfi_gen1_defines.h"
> #include "iris_instance.h"
> @@ -130,6 +132,143 @@ static void iris_hfi_gen1_sys_property_info(struct iris_core *core, void *packet
> }
> }
>
> +static void iris_hfi_gen1_session_etb_done(struct iris_inst *inst, void *packet)
> +{
> + struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet;
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *m2m_buffer, *n;
> + struct iris_buffer *buf = NULL;
> + bool found = false;
> +
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
> + buf = to_iris_buffer(&m2m_buffer->vb);
> + if (buf->index == pkt->input_tag) {
> + found = true;
> + break;
> + }
> + }
> + if (!found)
> + goto error;
> +
> + if (pkt->shdr.error_type == HFI_ERR_SESSION_UNSUPPORTED_STREAM) {
> + buf->flags = V4L2_BUF_FLAG_ERROR;
> + iris_vb2_queue_error(inst);
> + iris_inst_change_state(inst, IRIS_INST_ERROR);
> + }
> +
> + if (!(buf->attr & BUF_ATTR_QUEUED))
> + return;
> +
> + buf->attr &= ~BUF_ATTR_QUEUED;
> +
> + if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
> + buf->attr |= BUF_ATTR_BUFFER_DONE;
> + iris_vb2_buffer_done(inst, buf);
> + }
> +
> + return;
> +
> +error:
> + iris_inst_change_state(inst, IRIS_INST_ERROR);
> + dev_err(inst->core->dev, "error in etb done\n");
> +}
> +
> +static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
> +{
> + struct hfi_msg_session_fbd_uncompressed_plane0_pkt *pkt = packet;
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *m2m_buffer, *n;
> + u32 timestamp_hi = pkt->time_stamp_hi;
> + u32 timestamp_lo = pkt->time_stamp_lo;
> + struct iris_core *core = inst->core;
> + u32 filled_len = pkt->filled_len;
> + u32 pic_type = pkt->picture_type;
> + u32 output_tag = pkt->output_tag;
> + struct iris_buffer *buf, *iter;
> + struct iris_buffers *buffers;
> + u32 offset = pkt->offset;
> + u64 timestamp_us = 0;
> + bool found = false;
> + u32 flags = 0;
> +
> + if (iris_split_mode_enabled(inst) && pkt->stream_id == 0) {
> + buffers = &inst->buffers[BUF_DPB];
> + if (!buffers)
> + goto error;
> +
> + found = false;
> + list_for_each_entry(iter, &buffers->list, list) {
> + if (!(iter->attr & BUF_ATTR_QUEUED))
> + continue;
> +
> + found = (iter->index == output_tag &&
> + iter->data_offset == offset);
> +
> + if (found) {
> + buf = iter;
> + break;
> + }
> + }
> + } else {
> + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) {
> + buf = to_iris_buffer(&m2m_buffer->vb);
> + if (!(buf->attr & BUF_ATTR_QUEUED))
> + continue;
> +
> + found = (buf->index == output_tag &&
> + buf->data_offset == offset);
> +
> + if (found)
> + break;
> + }
> + }
> + if (!found)
> + goto error;
> +
> + buf->data_offset = offset;
> + buf->data_size = filled_len;
> +
> + if (filled_len) {
> + timestamp_us = timestamp_hi;
> + timestamp_us = (timestamp_us << 32) | timestamp_lo;
> + } else {
> + flags |= V4L2_BUF_FLAG_LAST;
> + }
> + buf->timestamp = timestamp_us;
> +
> + switch (pic_type) {
> + case HFI_PICTURE_IDR:
> + case HFI_PICTURE_I:
> + flags |= V4L2_BUF_FLAG_KEYFRAME;
> + break;
> + case HFI_PICTURE_P:
> + flags |= V4L2_BUF_FLAG_PFRAME;
> + break;
> + case HFI_PICTURE_B:
> + flags |= V4L2_BUF_FLAG_BFRAME;
> + break;
> + case HFI_FRAME_NOTCODED:
> + case HFI_UNUSED_PICT:
> + case HFI_FRAME_YUV:
> + default:
> + break;
> + }
> +
> + buf->attr &= ~BUF_ATTR_QUEUED;
> + buf->attr |= BUF_ATTR_DEQUEUED;
> + buf->attr |= BUF_ATTR_BUFFER_DONE;
> +
> + buf->flags |= flags;
> +
> + iris_vb2_buffer_done(inst, buf);
> +
> + return;
> +
> +error:
> + iris_inst_change_state(inst, IRIS_INST_ERROR);
> + dev_err(core->dev, "error in ftb done\n");
> +}
> +
> struct iris_hfi_gen1_response_pkt_info {
> u32 pkt;
> u32 pkt_sz;
> @@ -168,6 +307,14 @@ static const struct iris_hfi_gen1_response_pkt_info pkt_infos[] = {
> .pkt = HFI_MSG_SESSION_STOP,
> .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
> },
> + {
> + .pkt = HFI_MSG_SESSION_EMPTY_BUFFER,
> + .pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt),
> + },
> + {
> + .pkt = HFI_MSG_SESSION_FILL_BUFFER,
> + .pkt_sz = sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_pkt),
> + },
> {
> .pkt = HFI_MSG_SESSION_FLUSH,
> .pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
> @@ -238,15 +385,21 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response
> }
>
> mutex_lock(&inst->lock);
> - struct hfi_msg_session_hdr_pkt *shdr;
> + if (hdr->pkt_type == HFI_MSG_SESSION_EMPTY_BUFFER) {
> + iris_hfi_gen1_session_etb_done(inst, hdr);
> + } else if (hdr->pkt_type == HFI_MSG_SESSION_FILL_BUFFER) {
> + iris_hfi_gen1_session_ftb_done(inst, hdr);
> + } else {
> + struct hfi_msg_session_hdr_pkt *shdr;
>
> - shdr = (struct hfi_msg_session_hdr_pkt *)hdr;
> - if (shdr->error_type != HFI_ERR_NONE)
> - iris_inst_change_state(inst, IRIS_INST_ERROR);
> + shdr = (struct hfi_msg_session_hdr_pkt *)hdr;
> + if (shdr->error_type != HFI_ERR_NONE)
> + iris_inst_change_state(inst, IRIS_INST_ERROR);
>
> - done = pkt_info->pkt == HFI_MSG_SESSION_FLUSH ?
> - &inst->flush_completion : &inst->completion;
> - complete(done);
> + done = pkt_info->pkt == HFI_MSG_SESSION_FLUSH ?
> + &inst->flush_completion : &inst->completion;
> + complete(done);
> + }
> mutex_unlock(&inst->lock);
>
> break;
> diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
> index 0a946c1e3a4c..b9d3749a10ef 100644
> --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
> +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
> @@ -20,6 +20,7 @@ struct iris_core;
> * @packet: HFI packet
> * @ipsc_properties_set: boolean to set ipsc properties to fw
> * @opsc_properties_set: boolean to set opsc properties to fw
> + * @hfi_frame_info: structure of frame info
> * @src_subcr_params: subscription params to fw on input port
> * @dst_subcr_params: subscription params to fw on output port
> */
> @@ -28,6 +29,7 @@ struct iris_inst_hfi_gen2 {
> struct iris_hfi_header *packet;
> bool ipsc_properties_set;
> bool opsc_properties_set;
> + struct iris_hfi_frame_info hfi_frame_info;
> struct hfi_subscription_params src_subcr_params;
> struct hfi_subscription_params dst_subcr_params;
> };
> diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
> index 3db132909eba..69273885be21 100644
> --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
> +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
> @@ -70,6 +70,12 @@
> #define HFI_SYS_ERROR_WD_TIMEOUT 0x05000001
> #define HFI_SYSTEM_ERROR_END 0x05FFFFFF
>
> +#define HFI_INFORMATION_BEGIN 0x06000000
> +#define HFI_INFO_UNSUPPORTED 0x06000001
> +#define HFI_INFO_DATA_CORRUPT 0x06000002
> +#define HFI_INFO_BUFFER_OVERFLOW 0x06000004
> +#define HFI_INFORMATION_END 0x06FFFFFF
> +
> enum hfi_property_mode_type {
> HFI_MODE_PORT_SETTINGS_CHANGE = 0x00000001,
> HFI_MODE_PROPERTY = 0x00000002,
> @@ -91,6 +97,15 @@ enum hfi_codec_type {
> HFI_CODEC_ENCODE_AVC = 2,
> };
>
> +enum hfi_picture_type {
> + HFI_PICTURE_IDR = 0x00000001,
> + HFI_PICTURE_P = 0x00000002,
> + HFI_PICTURE_B = 0x00000004,
> + HFI_PICTURE_I = 0x00000008,
> + HFI_PICTURE_CRA = 0x00000010,
> + HFI_PICTURE_BLA = 0x00000020,
> +};
> +
> enum hfi_buffer_type {
> HFI_BUFFER_BITSTREAM = 0x00000001,
> HFI_BUFFER_RAW = 0x00000002,
> @@ -114,6 +129,13 @@ enum hfi_buffer_host_flags {
> HFI_BUF_HOST_FLAGS_CB_NON_SECURE = 0x00000200,
> };
>
> +enum hfi_buffer_firmware_flags {
> + HFI_BUF_FW_FLAG_RELEASE_DONE = 0x00000001,
> + HFI_BUF_FW_FLAG_READONLY = 0x00000010,
> + HFI_BUF_FW_FLAG_LAST = 0x10000000,
> + HFI_BUF_FW_FLAG_PSC_LAST = 0x20000000,
> +};
> +
> enum hfi_packet_firmware_flags {
> HFI_FW_FLAGS_SUCCESS = 0x00000001,
> HFI_FW_FLAGS_INFORMATION = 0x00000002,
> diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
> index 9639c7eabd6b..77c4f92b76e1 100644
> --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
> +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
> @@ -3,6 +3,8 @@
> * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> +#include <media/v4l2-mem2mem.h>
> +
> #include "iris_hfi_gen2.h"
> #include "iris_hfi_gen2_defines.h"
> #include "iris_hfi_gen2_packet.h"
> @@ -81,6 +83,29 @@ static bool iris_hfi_gen2_is_valid_hfi_port(u32 port, u32 buffer_type)
> return true;
> }
>
> +static int iris_hfi_gen2_get_driver_buffer_flags(struct iris_inst *inst, u32 hfi_flags)
> +{
> + u32 keyframe = HFI_PICTURE_IDR | HFI_PICTURE_I | HFI_PICTURE_CRA | HFI_PICTURE_BLA;
> + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
> + u32 driver_flags = 0;
> +
> + if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe)
> + driver_flags |= V4L2_BUF_FLAG_KEYFRAME;
> + else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_P)
> + driver_flags |= V4L2_BUF_FLAG_PFRAME;
> + else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_B)
> + driver_flags |= V4L2_BUF_FLAG_BFRAME;
> +
> + if (inst_hfi_gen2->hfi_frame_info.data_corrupt || inst_hfi_gen2->hfi_frame_info.overflow)
> + driver_flags |= V4L2_BUF_FLAG_ERROR;
> +
> + if (hfi_flags & HFI_BUF_FW_FLAG_LAST ||
> + hfi_flags & HFI_BUF_FW_FLAG_PSC_LAST)
> + driver_flags |= V4L2_BUF_FLAG_LAST;
> +
> + return driver_flags;
> +}
> +
> static bool iris_hfi_gen2_validate_packet_payload(struct iris_hfi_packet *pkt)
> {
> u32 payload_size = 0;
> @@ -154,6 +179,37 @@ static int iris_hfi_gen2_validate_hdr_packet(struct iris_core *core, struct iris
> return 0;
> }
>
> +static int iris_hfi_gen2_handle_session_info(struct iris_inst *inst,
> + struct iris_hfi_packet *pkt)
> +{
> + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
> + struct iris_core *core = inst->core;
> + int ret = 0;
> + char *info;
> +
> + switch (pkt->type) {
> + case HFI_INFO_UNSUPPORTED:
> + info = "unsupported";
> + break;
> + case HFI_INFO_DATA_CORRUPT:
> + info = "data corrupt";
> + inst_hfi_gen2->hfi_frame_info.data_corrupt = 1;
> + break;
> + case HFI_INFO_BUFFER_OVERFLOW:
> + info = "buffer overflow";
> + inst_hfi_gen2->hfi_frame_info.overflow = 1;
> + break;
> + default:
> + info = "unknown";
> + break;
> + }
> +
> + dev_dbg(core->dev, "session info received %#x: %s\n",
> + pkt->type, info);
> +
> + return ret;
> +}
> +
> static int iris_hfi_gen2_handle_session_error(struct iris_inst *inst,
> struct iris_hfi_packet *pkt)
> {
> @@ -234,19 +290,108 @@ static void iris_hfi_gen2_handle_session_close(struct iris_inst *inst,
> complete(&inst->completion);
> }
>
> +static int iris_hfi_gen2_handle_input_buffer(struct iris_inst *inst,
> + struct iris_hfi_buffer *buffer)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *m2m_buffer, *n;
> + struct iris_buffer *buf;
> + bool found = false;
> +
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
> + buf = to_iris_buffer(&m2m_buffer->vb);
> + if (buf->index == buffer->index) {
> + found = true;
> + break;
> + }
> + }
> + if (!found)
> + return -EINVAL;
> +
> + if (!(buf->attr & BUF_ATTR_QUEUED))
> + return -EINVAL;
> +
> + buf->attr &= ~BUF_ATTR_QUEUED;
> + buf->attr |= BUF_ATTR_DEQUEUED;
> +
> + buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, buffer->flags);
> +
> + return 0;
> +}
> +
> +static int iris_hfi_gen2_handle_output_buffer(struct iris_inst *inst,
> + struct iris_hfi_buffer *hfi_buffer)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *m2m_buffer, *n;
> + struct iris_buffer *buf;
> + bool found = false;
> +
> + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) {
> + buf = to_iris_buffer(&m2m_buffer->vb);
> + if (buf->index == hfi_buffer->index &&
> + buf->device_addr == hfi_buffer->base_address &&
> + buf->data_offset == hfi_buffer->data_offset) {
> + found = true;
> + break;
> + }
> + }
> + if (!found)
> + return -EINVAL;
> +
> + if (!(buf->attr & BUF_ATTR_QUEUED))
> + return -EINVAL;
> +
> + buf->data_offset = hfi_buffer->data_offset;
> + buf->data_size = hfi_buffer->data_size;
> + buf->timestamp = hfi_buffer->timestamp;
> +
> + buf->attr &= ~BUF_ATTR_QUEUED;
> + buf->attr |= BUF_ATTR_DEQUEUED;
> +
> + buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, hfi_buffer->flags);
> +
> + return 0;
> +}
> +
> +static void iris_hfi_gen2_handle_dequeue_buffers(struct iris_inst *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *buffer, *n;
> + struct iris_buffer *buf = NULL;
> +
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (buf->attr & BUF_ATTR_DEQUEUED) {
> + buf->attr &= ~BUF_ATTR_DEQUEUED;
> + if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
> + buf->attr |= BUF_ATTR_BUFFER_DONE;
> + iris_vb2_buffer_done(inst, buf);
> + }
> + }
> + }
> +
> + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (buf->attr & BUF_ATTR_DEQUEUED) {
> + buf->attr &= ~BUF_ATTR_DEQUEUED;
> + if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
> + buf->attr |= BUF_ATTR_BUFFER_DONE;
> + iris_vb2_buffer_done(inst, buf);
> + }
> + }
> + }
> +}
> +
> static int iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst,
> struct iris_hfi_buffer *buffer)
> {
> + u32 buf_type = iris_hfi_gen2_buf_type_to_driver(buffer->type);
> + struct iris_buffers *buffers = &inst->buffers[buf_type];
> struct iris_buffer *buf, *iter;
> - struct iris_buffers *buffers;
> - u32 buf_type;
> + bool found = false;
> int ret = 0;
> - bool found;
>
> - buf_type = iris_hfi_gen2_buf_type_to_driver(buffer->type);
> - buffers = &inst->buffers[buf_type];
> -
> - found = false;
> list_for_each_entry(iter, &buffers->list, list) {
> if (iter->device_addr == buffer->base_address) {
> found = true;
> @@ -258,7 +403,6 @@ static int iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst,
> return -EINVAL;
>
> buf->attr &= ~BUF_ATTR_QUEUED;
> -
> if (buf->attr & BUF_ATTR_PENDING_RELEASE)
> ret = iris_destroy_internal_buffer(inst, buf);
>
> @@ -285,7 +429,12 @@ static int iris_hfi_gen2_handle_session_buffer(struct iris_inst *inst,
> if (!iris_hfi_gen2_is_valid_hfi_port(pkt->port, buffer->type))
> return 0;
>
> - return iris_hfi_gen2_handle_release_internal_buffer(inst, buffer);
> + if (buffer->type == HFI_BUFFER_BITSTREAM)
> + return iris_hfi_gen2_handle_input_buffer(inst, buffer);
> + else if (buffer->type == HFI_BUFFER_RAW)
> + return iris_hfi_gen2_handle_output_buffer(inst, buffer);
> + else
> + return iris_hfi_gen2_handle_release_internal_buffer(inst, buffer);
> }
>
> static int iris_hfi_gen2_handle_session_command(struct iris_inst *inst,
> @@ -347,6 +496,12 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
> case HFI_PROP_LEVEL:
> inst_hfi_gen2->src_subcr_params.level = pkt->payload[0];
> break;
> + case HFI_PROP_PICTURE_TYPE:
> + inst_hfi_gen2->hfi_frame_info.picture_type = pkt->payload[0];
> + break;
> + case HFI_PROP_NO_OUTPUT:
> + inst_hfi_gen2->hfi_frame_info.no_output = 1;
> + break;
> case HFI_PROP_QUALITY_MODE:
> case HFI_PROP_STAGE:
> case HFI_PROP_PIPE:
> @@ -433,14 +588,18 @@ static int iris_hfi_gen2_handle_system_response(struct iris_core *core,
> static int iris_hfi_gen2_handle_session_response(struct iris_core *core,
> struct iris_hfi_header *hdr)
> {
> + struct iris_inst_hfi_gen2 *inst_hfi_gen2;
> struct iris_hfi_packet *packet;
> struct iris_inst *inst;
> + bool dequeue = false;
> int ret = 0;
> u32 i, j;
> u8 *pkt;
> static const struct iris_hfi_gen2_inst_hfi_range range[] = {
> {HFI_SESSION_ERROR_BEGIN, HFI_SESSION_ERROR_END,
> iris_hfi_gen2_handle_session_error},
> + {HFI_INFORMATION_BEGIN, HFI_INFORMATION_END,
> + iris_hfi_gen2_handle_session_info},
> {HFI_PROP_BEGIN, HFI_PROP_END,
> iris_hfi_gen2_handle_session_property},
> {HFI_CMD_BEGIN, HFI_CMD_END,
> @@ -452,6 +611,8 @@ static int iris_hfi_gen2_handle_session_response(struct iris_core *core,
> return -EINVAL;
>
> mutex_lock(&inst->lock);
> + inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
> + memset(&inst_hfi_gen2->hfi_frame_info, 0, sizeof(struct iris_hfi_frame_info));
>
> pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
> for (i = 0; i < ARRAY_SIZE(range); i++) {
> @@ -462,6 +623,7 @@ static int iris_hfi_gen2_handle_session_response(struct iris_core *core,
> iris_hfi_gen2_handle_session_error(inst, packet);
>
> if (packet->type > range[i].begin && packet->type < range[i].end) {
> + dequeue |= (packet->type == HFI_CMD_BUFFER);
> ret = range[i].handle(inst, packet);
> if (ret)
> iris_inst_change_state(inst, IRIS_INST_ERROR);
> @@ -470,6 +632,9 @@ static int iris_hfi_gen2_handle_session_response(struct iris_core *core,
> }
> }
>
> + if (dequeue)
> + iris_hfi_gen2_handle_dequeue_buffers(inst);
> +
> mutex_unlock(&inst->lock);
>
> return ret;
> diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
> index f40df09e5323..6d6fe9e2e8c0 100644
> --- a/drivers/media/platform/qcom/iris/iris_instance.h
> +++ b/drivers/media/platform/qcom/iris/iris_instance.h
> @@ -35,6 +35,10 @@
> * @m2m_dev: a reference to m2m device structure
> * @m2m_ctx: a reference to m2m context structure
> * @subscriptions: variable to hold current events subscriptions
> + * @sequence_cap: a sequence counter for capture queue
> + * @sequence_out: a sequence counter for output queue
> + * @tss: timestamp metadata
> + * @metadata_idx: index for metadata buffer
> */
>
> struct iris_inst {
> @@ -58,6 +62,10 @@ struct iris_inst {
> struct v4l2_m2m_dev *m2m_dev;
> struct v4l2_m2m_ctx *m2m_ctx;
> unsigned int subscriptions;
> + u32 sequence_cap;
> + u32 sequence_out;
> + struct iris_ts_metadata tss[VIDEO_MAX_FRAME];
> + u32 metadata_idx;
> };
>
> #endif
> diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c
> index 8bcfa97db97d..83c70d6a2d90 100644
> --- a/drivers/media/platform/qcom/iris/iris_utils.c
> +++ b/drivers/media/platform/qcom/iris/iris_utils.c
> @@ -4,6 +4,7 @@
> */
>
> #include <linux/pm_runtime.h>
> +#include <media/v4l2-mem2mem.h>
>
> #include "iris_instance.h"
> #include "iris_utils.h"
> @@ -36,6 +37,21 @@ bool iris_split_mode_enabled(struct iris_inst *inst)
> return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12;
> }
>
> +void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
> + enum vb2_buffer_state state)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct vb2_v4l2_buffer *buf;
> +
> + if (V4L2_TYPE_IS_OUTPUT(type)) {
> + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx)))
> + v4l2_m2m_buf_done(buf, state);
> + } else if (V4L2_TYPE_IS_CAPTURE(type)) {
> + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx)))
> + v4l2_m2m_buf_done(buf, state);
> + }
> +}
> +
> int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush)
> {
> struct iris_core *core = inst->core;
> diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h
> index 3400847f5e72..49869cf7a376 100644
> --- a/drivers/media/platform/qcom/iris/iris_utils.h
> +++ b/drivers/media/platform/qcom/iris/iris_utils.h
> @@ -16,6 +16,20 @@ struct iris_hfi_rect_desc {
> u32 height;
> };
>
> +struct iris_hfi_frame_info {
> + u32 picture_type;
> + u32 no_output;
> + u32 data_corrupt;
> + u32 overflow;
> +};
> +
> +struct iris_ts_metadata {
> + u64 ts_ns;
> + u64 ts_us;
> + u32 flags;
> + struct v4l2_timecode tc;
> +};
> +
> #define NUM_MBS_PER_FRAME(height, width) \
> (DIV_ROUND_UP(height, 16) * DIV_ROUND_UP(width, 16))
>
> @@ -32,6 +46,8 @@ bool iris_res_is_less_than(u32 width, u32 height,
> int iris_get_mbpf(struct iris_inst *inst);
> bool iris_split_mode_enabled(struct iris_inst *inst);
> struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id);
> +void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
> + enum vb2_buffer_state state);
> int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush);
>
> #endif
> diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
> index b93da860d336..770e51f9ef91 100644
> --- a/drivers/media/platform/qcom/iris/iris_vb2.c
> +++ b/drivers/media/platform/qcom/iris/iris_vb2.c
> @@ -3,10 +3,23 @@
> * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
> */
>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/v4l2-mem2mem.h>
> +
> #include "iris_instance.h"
> #include "iris_vb2.h"
> #include "iris_vdec.h"
>
> +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[])
> @@ -60,6 +73,7 @@ int iris_vb2_queue_setup(struct vb2_queue *q,
>
> 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;
>
> @@ -87,11 +101,18 @@ int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
> 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);
>
> @@ -101,6 +122,7 @@ int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
> void iris_vb2_stop_streaming(struct vb2_queue *q)
> {
> struct iris_inst *inst;
> + int ret = 0;
>
> inst = vb2_get_drv_priv(q);
>
> @@ -113,8 +135,82 @@ void iris_vb2_stop_streaming(struct vb2_queue *q)
> !V4L2_TYPE_IS_CAPTURE(q->type))
> goto exit;
>
> - iris_vdec_session_streamoff(inst, q->type);
> + 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)
> +{
> + 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;
> + }
> +
> + 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);
> }
> diff --git a/drivers/media/platform/qcom/iris/iris_vb2.h b/drivers/media/platform/qcom/iris/iris_vb2.h
> index 3906510fa71f..a88565fdd3e4 100644
> --- a/drivers/media/platform/qcom/iris/iris_vb2.h
> +++ b/drivers/media/platform/qcom/iris/iris_vb2.h
> @@ -6,10 +6,14 @@
> #ifndef __IRIS_VB2_H__
> #define __IRIS_VB2_H__
>
> +int iris_vb2_buf_init(struct vb2_buffer *vb2);
> int iris_vb2_queue_setup(struct vb2_queue *q,
> unsigned int *num_buffers, unsigned int *num_planes,
> unsigned int sizes[], struct device *alloc_devs[]);
> int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count);
> void iris_vb2_stop_streaming(struct vb2_queue *q);
> +int iris_vb2_buf_prepare(struct vb2_buffer *vb);
> +int iris_vb2_buf_out_validate(struct vb2_buffer *vb);
> +void iris_vb2_buf_queue(struct vb2_buffer *vb2);
>
> #endif
> diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
> index c26396bb11c5..4c99847cec6e 100644
> --- a/drivers/media/platform/qcom/iris/iris_vdec.c
> +++ b/drivers/media/platform/qcom/iris/iris_vdec.c
> @@ -226,6 +226,68 @@ int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_su
> return ret;
> }
>
> +static int iris_vdec_get_num_queued_buffers(struct iris_inst *inst,
> + enum iris_buffer_type type)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *buffer, *n;
> + struct iris_buffer *buf;
> + u32 count = 0;
> +
> + switch (type) {
> + case BUF_INPUT:
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (!(buf->attr & BUF_ATTR_QUEUED))
> + continue;
> + count++;
> + }
> + return count;
> + case BUF_OUTPUT:
> + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (!(buf->attr & BUF_ATTR_QUEUED))
> + continue;
> + count++;
> + }
> + return count;
> + default:
> + return count;
> + }
> +}
> +
> +static void iris_vdec_flush_deferred_buffers(struct iris_inst *inst,
> + enum iris_buffer_type type)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
> + struct v4l2_m2m_buffer *buffer, *n;
> + struct iris_buffer *buf;
> +
> + if (type == BUF_INPUT) {
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (buf->attr & BUF_ATTR_DEFERRED) {
> + if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
> + buf->attr |= BUF_ATTR_BUFFER_DONE;
> + buf->data_size = 0;
> + iris_vb2_buffer_done(inst, buf);
> + }
> + }
> + }
> + } else {
> + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
> + buf = to_iris_buffer(&buffer->vb);
> + if (buf->attr & BUF_ATTR_DEFERRED) {
> + if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
> + buf->attr |= BUF_ATTR_BUFFER_DONE;
> + buf->data_size = 0;
> + iris_vb2_buffer_done(inst, buf);
> + }
> + }
> + }
> + }
> +}
> +
> static void iris_vdec_kill_session(struct iris_inst *inst)
> {
> const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
> @@ -237,23 +299,47 @@ static void iris_vdec_kill_session(struct iris_inst *inst)
> iris_inst_change_state(inst, IRIS_INST_ERROR);
> }
>
> -void iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane)
> +int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane)
> {
> const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
> + enum iris_buffer_type buffer_type;
> + u32 count;
> int ret;
>
> + switch (plane) {
> + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> + buffer_type = BUF_INPUT;
> + break;
> + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> + buffer_type = BUF_OUTPUT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> ret = hfi_ops->session_stop(inst, plane);
> if (ret)
> goto error;
>
> + count = iris_vdec_get_num_queued_buffers(inst, buffer_type);
> + if (count) {
> + ret = -EINVAL;
> + goto error;
> + }
> +
> ret = iris_inst_state_change_streamoff(inst, plane);
> if (ret)
> goto error;
>
> - return;
> + iris_vdec_flush_deferred_buffers(inst, buffer_type);
> +
> + return 0;
>
> error:
> iris_vdec_kill_session(inst);
> + iris_vdec_flush_deferred_buffers(inst, buffer_type);
> +
> + return ret;
> }
>
> static int iris_vdec_process_streamon_input(struct iris_inst *inst)
> @@ -343,3 +429,64 @@ int iris_vdec_streamon_output(struct iris_inst *inst)
>
> return ret;
> }
> +
> +static int
> +iris_vdec_vb2_buffer_to_driver(struct vb2_buffer *vb2, struct iris_buffer *buf)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
> +
> + buf->type = iris_v4l2_type_to_driver(vb2->type);
> + buf->index = vb2->index;
> + buf->fd = vb2->planes[0].m.fd;
> + buf->buffer_size = vb2->planes[0].length;
> + buf->data_offset = vb2->planes[0].data_offset;
> + buf->data_size = vb2->planes[0].bytesused - vb2->planes[0].data_offset;
> + buf->flags = vbuf->flags;
> + buf->timestamp = vb2->timestamp;
> + buf->attr = 0;
> +
> + return 0;
> +}
> +
> +static void
> +iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
> +{
> + u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
> + struct vb2_buffer *vb = &vbuf->vb2_buf;
> + u64 ts_us = vb->timestamp;
> +
> + if (inst->metadata_idx >= ARRAY_SIZE(inst->tss))
> + inst->metadata_idx = 0;
> +
> + do_div(ts_us, NSEC_PER_USEC);
> +
> + inst->tss[inst->metadata_idx].flags = vbuf->flags & mask;
> + inst->tss[inst->metadata_idx].tc = vbuf->timecode;
> + inst->tss[inst->metadata_idx].ts_us = ts_us;
> + inst->tss[inst->metadata_idx].ts_ns = vb->timestamp;
> +
> + inst->metadata_idx++;
> +}
> +
> +int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
> +{
> + struct iris_buffer *buf = to_iris_buffer(vbuf);
> + struct vb2_buffer *vb2 = &vbuf->vb2_buf;
> + struct vb2_queue *q;
> + int ret;
> +
> + ret = iris_vdec_vb2_buffer_to_driver(vb2, buf);
> + if (ret)
> + return ret;
> +
> + if (buf->type == BUF_INPUT)
> + iris_set_ts_metadata(inst, vbuf);
> +
> + q = v4l2_m2m_get_vq(inst->m2m_ctx, vb2->type);
> + if (!vb2_is_streaming(q)) {
> + buf->attr |= BUF_ATTR_DEFERRED;
> + return 0;
> + }
> +
> + return iris_queue_buffer(inst, buf);
> +}
> diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h
> index a17bb817b6e5..998d4970a42b 100644
> --- a/drivers/media/platform/qcom/iris/iris_vdec.h
> +++ b/drivers/media/platform/qcom/iris/iris_vdec.h
> @@ -16,6 +16,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f);
> int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub);
> int iris_vdec_streamon_input(struct iris_inst *inst);
> int iris_vdec_streamon_output(struct iris_inst *inst);
> -void iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane);
> +int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf);
> +int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane);
>
> #endif
> diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
> index 4e7780110212..8a1c35f99538 100644
> --- a/drivers/media/platform/qcom/iris/iris_vidc.c
> +++ b/drivers/media/platform/qcom/iris/iris_vidc.c
> @@ -7,6 +7,7 @@
> #include <media/v4l2-event.h>
> #include <media/v4l2-ioctl.h>
> #include <media/v4l2-mem2mem.h>
> +#include <media/videobuf2-dma-contig.h>
>
> #include "iris_vidc.h"
> #include "iris_instance.h"
> @@ -98,6 +99,7 @@ iris_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_
> src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> src_vq->ops = inst->core->iris_vb2_ops;
> + src_vq->mem_ops = &vb2_dma_contig_memops;
> src_vq->drv_priv = inst;
> src_vq->buf_struct_size = sizeof(struct iris_buffer);
> src_vq->min_reqbufs_allocation = MIN_BUFFERS;
> @@ -111,6 +113,7 @@ iris_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_
> dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> dst_vq->ops = inst->core->iris_vb2_ops;
> + dst_vq->mem_ops = &vb2_dma_contig_memops;
> dst_vq->drv_priv = inst;
> dst_vq->buf_struct_size = sizeof(struct iris_buffer);
> dst_vq->min_reqbufs_allocation = MIN_BUFFERS;
> @@ -372,9 +375,13 @@ static struct v4l2_file_operations iris_v4l2_file_ops = {
> };
>
> static const struct vb2_ops iris_vb2_ops = {
> + .buf_init = iris_vb2_buf_init,
> .queue_setup = iris_vb2_queue_setup,
> .start_streaming = iris_vb2_start_streaming,
> .stop_streaming = iris_vb2_stop_streaming,
> + .buf_prepare = iris_vb2_buf_prepare,
> + .buf_out_validate = iris_vb2_buf_out_validate,
> + .buf_queue = iris_vb2_buf_queue,
> };
>
> static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops = {
> @@ -388,6 +395,13 @@ static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops = {
> .vidioc_g_fmt_vid_out_mplane = iris_g_fmt_vid_mplane,
> .vidioc_enum_framesizes = iris_enum_framesizes,
> .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> + .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
> .vidioc_querycap = iris_querycap,
> .vidioc_g_selection = iris_g_selection,
> .vidioc_subscribe_event = iris_subscribe_event,
>