On Fri Feb 7 13:25:03 2025 +0530, Dikshita Agarwal wrote:
> handle the V4L2_DEC_CMD_STOP by initiating a drain sequence on the
> firmware. Process and decode all OUTPUT buffers, that are  queued by the
> client, before the VIDIOC_DECODER_CMD() was issued and mark the last
> buffer with the V4L2_BUF_FLAG_LAST flag. The decoder is stopped, after
> processing the last buffer.
> 
> Resume the decoder when one of these operations are issued by the client:
> - V4L2_DEC_CMD_START
> - pair of VIDIOC_STREAMOFF() and VIDIOC_STREAMON() on the CAPTURE queue
> - pair of VIDIOC_STREAMOFF() and VIDIOC_STREAMON() on the OUTPUT queue
> 
> Add the handling to resume decoding when client issues
> V4L2_DEC_CMD_START to resume decoding after a source change is detected.
> 
> Tested-by: Stefan Schmidt <stefan.schm...@linaro.org> # x1e80100 (Dell XPS 13 
> 9345)
> Reviewed-by: Stefan Schmidt <stefan.schm...@linaro.org>
> Tested-by: Neil Armstrong <neil.armstr...@linaro.org> # on SM8550-QRD
> Tested-by: Neil Armstrong <neil.armstr...@linaro.org> # on SM8550-HDK
> Signed-off-by: Dikshita Agarwal <quic_diksh...@quicinc.com>
> Signed-off-by: Hans Verkuil <hverk...@xs4all.nl>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/platform/qcom/iris/iris_hfi_common.h |  2 +
 .../platform/qcom/iris/iris_hfi_gen1_command.c     | 13 ++++
 .../platform/qcom/iris/iris_hfi_gen1_defines.h     |  1 +
 .../platform/qcom/iris/iris_hfi_gen1_response.c    | 15 ++++
 .../platform/qcom/iris/iris_hfi_gen2_command.c     | 43 ++++++++++
 .../platform/qcom/iris/iris_hfi_gen2_defines.h     |  2 +
 .../platform/qcom/iris/iris_hfi_gen2_response.c    | 46 ++++++++++-
 drivers/media/platform/qcom/iris/iris_state.c      | 68 ++++++++++++++++
 drivers/media/platform/qcom/iris/iris_state.h      | 13 +++-
 drivers/media/platform/qcom/iris/iris_vb2.c        |  6 +-
 drivers/media/platform/qcom/iris/iris_vdec.c       | 91 +++++++++++++++++++++-
 drivers/media/platform/qcom/iris/iris_vdec.h       |  2 +
 drivers/media/platform/qcom/iris/iris_vidc.c       | 35 +++++++++
 13 files changed, 329 insertions(+), 8 deletions(-)

---

diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h 
b/drivers/media/platform/qcom/iris/iris_hfi_common.h
index 8e14a61c9be4..b2c541367fc6 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -121,6 +121,8 @@ struct iris_hfi_command_ops {
        int (*session_pause)(struct iris_inst *inst, u32 plane);
        int (*session_resume_drc)(struct iris_inst *inst, u32 plane);
        int (*session_stop)(struct iris_inst *inst, u32 plane);
+       int (*session_drain)(struct iris_inst *inst, u32 plane);
+       int (*session_resume_drain)(struct iris_inst *inst, u32 plane);
        int (*session_close)(struct iris_inst *inst);
 };
 
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 e0cb75a112e3..e1fbbb3c196d 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -368,6 +368,18 @@ exit:
        return ret;
 }
 
+static int iris_hfi_gen1_session_drain(struct iris_inst *inst, u32 plane)
+{
+       struct hfi_session_empty_buffer_compressed_pkt ip_pkt = {0};
+
+       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.flags = HFI_BUFFERFLAG_EOS;
+
+       return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, 
ip_pkt.shdr.hdr.size);
+}
+
 static int
 iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt 
*packet,
                                          struct iris_inst *inst, u32 ptype, 
void *pdata)
@@ -789,6 +801,7 @@ static const struct iris_hfi_command_ops 
iris_hfi_gen1_command_ops = {
        .session_release_buf = iris_hfi_gen1_session_unset_buffers,
        .session_resume_drc = iris_hfi_gen1_session_continue,
        .session_stop = iris_hfi_gen1_session_stop,
+       .session_drain = iris_hfi_gen1_session_drain,
        .session_close = iris_hfi_gen1_session_close,
 };
 
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 c40e0a28b21f..9f246816a286 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -49,6 +49,7 @@
 #define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES 0x1000002
 #define HFI_EVENT_SESSION_SEQUENCE_CHANGED                        0x1000003
 
+#define HFI_BUFFERFLAG_EOS                             0x00000001
 #define HFI_BUFFERFLAG_TIMESTAMPINVALID                        0x00000100
 
 #define HFI_FLUSH_OUTPUT                               0x1000002
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 3a47d9f39695..b72d503dd740 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
@@ -386,6 +386,7 @@ 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;
+       struct hfi_session_flush_pkt flush_pkt;
        u32 timestamp_hi = pkt->time_stamp_hi;
        u32 timestamp_lo = pkt->time_stamp_lo;
        struct iris_core *core = inst->core;
@@ -394,11 +395,25 @@ static void iris_hfi_gen1_session_ftb_done(struct 
iris_inst *inst, void *packet)
        u32 output_tag = pkt->output_tag;
        struct iris_buffer *buf, *iter;
        struct iris_buffers *buffers;
+       u32 hfi_flags = pkt->flags;
        u32 offset = pkt->offset;
        u64 timestamp_us = 0;
        bool found = false;
        u32 flags = 0;
 
+       if ((hfi_flags & HFI_BUFFERFLAG_EOS) && !filled_len) {
+               reinit_completion(&inst->flush_completion);
+
+               flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
+               flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+               flush_pkt.shdr.session_id = inst->session_id;
+               flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
+               iris_hfi_queue_cmd_write(core, &flush_pkt, 
flush_pkt.shdr.hdr.size);
+               iris_inst_sub_state_change_drain_last(inst);
+
+               return;
+       }
+
        if (iris_split_mode_enabled(inst) && pkt->stream_id == 0) {
                buffers = &inst->buffers[BUF_DPB];
                if (!buffers)
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c 
b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
index 8efc6a70a57a..a908b41e2868 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -774,6 +774,47 @@ static int iris_hfi_gen2_session_resume_drc(struct 
iris_inst *inst, u32 plane)
                                        inst_hfi_gen2->packet->size);
 }
 
+static int iris_hfi_gen2_session_resume_drain(struct iris_inst *inst, u32 
plane)
+{
+       struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+       u32 payload = HFI_CMD_DRAIN;
+
+       iris_hfi_gen2_packet_session_command(inst,
+                                            HFI_CMD_RESUME,
+                                            (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+                                            HFI_HOST_FLAGS_INTR_REQUIRED),
+                                            iris_hfi_gen2_get_port(plane),
+                                            inst->session_id,
+                                            HFI_PAYLOAD_U32,
+                                            &payload,
+                                            sizeof(u32));
+
+       return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+                                       inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_drain(struct iris_inst *inst, u32 plane)
+{
+       struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+       if (!V4L2_TYPE_IS_OUTPUT(plane))
+               return 0;
+
+       iris_hfi_gen2_packet_session_command(inst,
+                                            HFI_CMD_DRAIN,
+                                            (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+                                            HFI_HOST_FLAGS_INTR_REQUIRED |
+                                            HFI_HOST_FLAGS_NON_DISCARDABLE),
+                                            iris_hfi_gen2_get_port(plane),
+                                            inst->session_id,
+                                            HFI_PAYLOAD_NONE,
+                                            NULL,
+                                            0);
+
+       return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+                                       inst_hfi_gen2->packet->size);
+}
+
 static u32 iris_hfi_gen2_buf_type_from_driver(enum iris_buffer_type 
buffer_type)
 {
        switch (buffer_type) {
@@ -900,6 +941,8 @@ static const struct iris_hfi_command_ops 
iris_hfi_gen2_command_ops = {
        .session_pause = iris_hfi_gen2_session_pause,
        .session_resume_drc = iris_hfi_gen2_session_resume_drc,
        .session_stop = iris_hfi_gen2_session_stop,
+       .session_drain = iris_hfi_gen2_session_drain,
+       .session_resume_drain = iris_hfi_gen2_session_resume_drain,
        .session_close = iris_hfi_gen2_session_close,
 };
 
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 620bcb90c35f..806f8bb7f505 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -17,6 +17,7 @@
 #define HFI_CMD_CLOSE                          0x01000004
 #define HFI_CMD_START                          0x01000005
 #define HFI_CMD_STOP                           0x01000006
+#define HFI_CMD_DRAIN                          0x01000007
 #define HFI_CMD_RESUME                         0x01000008
 #define HFI_CMD_BUFFER                         0x01000009
 #define HFI_CMD_SUBSCRIBE_MODE                 0x0100000B
@@ -80,6 +81,7 @@
 #define HFI_INFO_UNSUPPORTED                   0x06000001
 #define HFI_INFO_DATA_CORRUPT                  0x06000002
 #define HFI_INFO_BUFFER_OVERFLOW               0x06000004
+#define HFI_INFO_HFI_FLAG_DRAIN_LAST           0x06000006
 #define HFI_INFO_HFI_FLAG_PSC_LAST             0x06000007
 #define HFI_INFORMATION_END                    0x06FFFFFF
 
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 c7552e041138..b75a01641d5d 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -201,6 +201,10 @@ static int iris_hfi_gen2_handle_session_info(struct 
iris_inst *inst,
                info = "buffer overflow";
                inst_hfi_gen2->hfi_frame_info.overflow = 1;
                break;
+       case HFI_INFO_HFI_FLAG_DRAIN_LAST:
+               info = "drain last flag";
+               ret = iris_inst_sub_state_change_drain_last(inst);
+               break;
        case HFI_INFO_HFI_FLAG_PSC_LAST:
                info = "drc last flag";
                ret = iris_inst_sub_state_change_drc_last(inst);
@@ -337,6 +341,12 @@ static int iris_hfi_gen2_handle_output_buffer(struct 
iris_inst *inst,
        bool found = false;
        int ret;
 
+       if (hfi_buffer->flags & HFI_BUF_FW_FLAG_LAST) {
+               ret = iris_inst_sub_state_change_drain_last(inst);
+               if (ret)
+                       return ret;
+       }
+
        if (hfi_buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST) {
                ret = iris_inst_sub_state_change_drc_last(inst);
                if (ret)
@@ -425,6 +435,21 @@ static int 
iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst,
        return ret;
 }
 
+static int iris_hfi_gen2_handle_session_stop(struct iris_inst *inst,
+                                            struct iris_hfi_packet *pkt)
+{
+       int ret = 0;
+
+       if (pkt->port == HFI_PORT_RAW)
+               ret = iris_inst_sub_state_change_pause(inst, 
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+       else if (pkt->port == HFI_PORT_BITSTREAM)
+               ret = iris_inst_sub_state_change_pause(inst, 
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+       complete(&inst->completion);
+
+       return ret;
+}
+
 static int iris_hfi_gen2_handle_session_buffer(struct iris_inst *inst,
                                               struct iris_hfi_packet *pkt)
 {
@@ -453,6 +478,22 @@ static int iris_hfi_gen2_handle_session_buffer(struct 
iris_inst *inst,
                return iris_hfi_gen2_handle_release_internal_buffer(inst, 
buffer);
 }
 
+static int iris_hfi_gen2_handle_session_drain(struct iris_inst *inst,
+                                             struct iris_hfi_packet *pkt)
+{
+       int ret = 0;
+
+       if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+               iris_inst_change_state(inst, IRIS_INST_ERROR);
+               return 0;
+       }
+
+       if (inst->sub_state & IRIS_INST_SUB_DRAIN)
+               ret = iris_inst_change_sub_state(inst, 0, 
IRIS_INST_SUB_INPUT_PAUSE);
+
+       return ret;
+}
+
 static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
 {
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
@@ -572,7 +613,7 @@ static int iris_hfi_gen2_handle_session_command(struct 
iris_inst *inst,
                iris_hfi_gen2_handle_session_close(inst, pkt);
                break;
        case HFI_CMD_STOP:
-               complete(&inst->completion);
+               iris_hfi_gen2_handle_session_stop(inst, pkt);
                break;
        case HFI_CMD_BUFFER:
                ret = iris_hfi_gen2_handle_session_buffer(inst, pkt);
@@ -580,6 +621,9 @@ static int iris_hfi_gen2_handle_session_command(struct 
iris_inst *inst,
        case HFI_CMD_SETTINGS_CHANGE:
                ret = iris_hfi_gen2_handle_src_change(inst, pkt);
                break;
+       case HFI_CMD_DRAIN:
+               ret = iris_hfi_gen2_handle_session_drain(inst, pkt);
+               break;
        default:
                break;
        }
diff --git a/drivers/media/platform/qcom/iris/iris_state.c 
b/drivers/media/platform/qcom/iris/iris_state.c
index aad7e734d5c8..f12306e735ec 100644
--- a/drivers/media/platform/qcom/iris/iris_state.c
+++ b/drivers/media/platform/qcom/iris/iris_state.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
  */
 
+#include <media/v4l2-mem2mem.h>
+
 #include "iris_instance.h"
 
 static bool iris_allow_inst_state_change(struct iris_inst *inst,
@@ -148,6 +150,21 @@ int iris_inst_sub_state_change_drc(struct iris_inst *inst)
        return iris_inst_change_sub_state(inst, 0, set_sub_state);
 }
 
+int iris_inst_sub_state_change_drain_last(struct iris_inst *inst)
+{
+       enum iris_inst_sub_state set_sub_state;
+
+       if (inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)
+               return -EINVAL;
+
+       if (!(inst->sub_state & IRIS_INST_SUB_DRAIN))
+               return -EINVAL;
+
+       set_sub_state = IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
+
+       return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
 int iris_inst_sub_state_change_drc_last(struct iris_inst *inst)
 {
        enum iris_inst_sub_state set_sub_state;
@@ -166,3 +183,54 @@ int iris_inst_sub_state_change_drc_last(struct iris_inst 
*inst)
 
        return iris_inst_change_sub_state(inst, 0, set_sub_state);
 }
+
+int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane)
+{
+       enum iris_inst_sub_state set_sub_state;
+
+       if (V4L2_TYPE_IS_OUTPUT(plane)) {
+               if (inst->sub_state & IRIS_INST_SUB_DRC &&
+                   !(inst->sub_state & IRIS_INST_SUB_DRC_LAST))
+                       return -EINVAL;
+
+               if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+                   !(inst->sub_state & IRIS_INST_SUB_DRAIN_LAST))
+                       return -EINVAL;
+
+               set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
+       } else {
+               set_sub_state = IRIS_INST_SUB_OUTPUT_PAUSE;
+       }
+
+       return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+static inline bool iris_drc_pending(struct iris_inst *inst)
+{
+       return inst->sub_state & IRIS_INST_SUB_DRC &&
+               inst->sub_state & IRIS_INST_SUB_DRC_LAST;
+}
+
+static inline bool iris_drain_pending(struct iris_inst *inst)
+{
+       return inst->sub_state & IRIS_INST_SUB_DRAIN &&
+               inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+}
+
+bool iris_allow_cmd(struct iris_inst *inst, u32 cmd)
+{
+       struct vb2_queue *src_q = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+       struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+
+       if (cmd == V4L2_DEC_CMD_START) {
+               if (vb2_is_streaming(src_q) || vb2_is_streaming(dst_q))
+                       if (iris_drc_pending(inst) || iris_drain_pending(inst))
+                               return true;
+       } else if (cmd == V4L2_DEC_CMD_STOP) {
+               if (vb2_is_streaming(src_q))
+                       if (inst->sub_state != IRIS_INST_SUB_DRAIN)
+                               return true;
+       }
+
+       return false;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_state.h 
b/drivers/media/platform/qcom/iris/iris_state.h
index b5f0826142f0..a5c0cad4a78c 100644
--- a/drivers/media/platform/qcom/iris/iris_state.h
+++ b/drivers/media/platform/qcom/iris/iris_state.h
@@ -104,6 +104,9 @@ enum iris_inst_state {
  *                   sent to client.
  * @IRIS_INST_SUB_DRC_LAST: indicates last buffer is received from firmware
  *                         as part of source change.
+ * @IRIS_INST_SUB_DRAIN: indicates drain is in progress.
+ * @IRIS_INST_SUB_DRAIN_LAST: indicates last buffer is received from firmware
+ *                           as part of drain sequence.
  * @IRIS_INST_SUB_INPUT_PAUSE: source change is received form firmware. This
  *                            indicates that firmware is paused to process
  *                            any further input frames.
@@ -115,8 +118,10 @@ enum iris_inst_sub_state {
        IRIS_INST_SUB_FIRST_IPSC        = BIT(0),
        IRIS_INST_SUB_DRC               = BIT(1),
        IRIS_INST_SUB_DRC_LAST          = BIT(2),
-       IRIS_INST_SUB_INPUT_PAUSE       = BIT(3),
-       IRIS_INST_SUB_OUTPUT_PAUSE      = BIT(4),
+       IRIS_INST_SUB_DRAIN             = BIT(3),
+       IRIS_INST_SUB_DRAIN_LAST        = BIT(4),
+       IRIS_INST_SUB_INPUT_PAUSE       = BIT(5),
+       IRIS_INST_SUB_OUTPUT_PAUSE      = BIT(6),
 };
 
 int iris_inst_change_state(struct iris_inst *inst,
@@ -124,9 +129,13 @@ int iris_inst_change_state(struct iris_inst *inst,
 int iris_inst_change_sub_state(struct iris_inst *inst,
                               enum iris_inst_sub_state clear_sub_state,
                               enum iris_inst_sub_state set_sub_state);
+
 int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane);
 int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane);
 int iris_inst_sub_state_change_drc(struct iris_inst *inst);
+int iris_inst_sub_state_change_drain_last(struct iris_inst *inst);
 int iris_inst_sub_state_change_drc_last(struct iris_inst *inst);
+int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane);
+bool iris_allow_cmd(struct iris_inst *inst, u32 cmd);
 
 #endif
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c 
b/drivers/media/platform/qcom/iris/iris_vb2.c
index 3b94011533e8..59fc133c9f98 100644
--- a/drivers/media/platform/qcom/iris/iris_vb2.c
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -206,8 +206,10 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2)
        }
 
        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) {
+               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;
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c 
b/drivers/media/platform/qcom/iris/iris_vdec.c
index 47a44978869b..076e3ee7969f 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.c
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -371,6 +371,7 @@ static int iris_vdec_process_streamon_input(struct 
iris_inst *inst)
        }
 
        if (inst->sub_state & IRIS_INST_SUB_DRC ||
+           inst->sub_state & IRIS_INST_SUB_DRAIN ||
            inst->sub_state & IRIS_INST_SUB_FIRST_IPSC) {
                if (!(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE)) {
                        if (hfi_ops->session_pause) {
@@ -422,15 +423,20 @@ int iris_vdec_streamon_input(struct iris_inst *inst)
 static int iris_vdec_process_streamon_output(struct iris_inst *inst)
 {
        const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+       bool drain_active = false, drc_active = false;
        enum iris_inst_sub_state clear_sub_state = 0;
-       bool drc_active = false;
        int ret = 0;
 
+       drain_active = inst->sub_state & IRIS_INST_SUB_DRAIN &&
+               inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+
        drc_active = inst->sub_state & IRIS_INST_SUB_DRC &&
                inst->sub_state & IRIS_INST_SUB_DRC_LAST;
 
        if (drc_active)
                clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+       else if (drain_active)
+               clear_sub_state = IRIS_INST_SUB_DRAIN | 
IRIS_INST_SUB_DRAIN_LAST;
 
        if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
                ret = iris_alloc_and_queue_input_int_bufs(inst);
@@ -446,8 +452,12 @@ static int iris_vdec_process_streamon_output(struct 
iris_inst *inst)
 
        if (inst->state == IRIS_INST_INPUT_STREAMING &&
            inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
-               ret = hfi_ops->session_resume_drc(inst,
-                                                 
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+               if (!drain_active)
+                       ret = hfi_ops->session_resume_drc(inst,
+                                                         
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+               else if (hfi_ops->session_resume_drain)
+                       ret = hfi_ops->session_resume_drain(inst,
+                                                           
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
                if (ret)
                        return ret;
                clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
@@ -565,3 +575,78 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct 
vb2_v4l2_buffer *vbuf)
 
        return iris_queue_buffer(inst, buf);
 }
+
+int iris_vdec_start_cmd(struct iris_inst *inst)
+{
+       const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+       enum iris_inst_sub_state clear_sub_state = 0;
+       struct vb2_queue *dst_vq;
+       int ret;
+
+       dst_vq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+
+       if (inst->sub_state & IRIS_INST_SUB_DRC &&
+           inst->sub_state & IRIS_INST_SUB_DRC_LAST) {
+               vb2_clear_last_buffer_dequeued(dst_vq);
+               clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+
+               if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+                       ret = hfi_ops->session_resume_drc(inst,
+                                                         
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+                       if (ret)
+                               return ret;
+                       clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+               }
+               if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+                       ret = hfi_ops->session_resume_drc(inst,
+                                                         
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+                       if (ret)
+                               return ret;
+                       clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+               }
+       } else if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+                  inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) {
+               vb2_clear_last_buffer_dequeued(dst_vq);
+               clear_sub_state = IRIS_INST_SUB_DRAIN | 
IRIS_INST_SUB_DRAIN_LAST;
+               if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+                       if (hfi_ops->session_resume_drain) {
+                               ret =
+                               hfi_ops->session_resume_drain(inst,
+                                                             
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+                               if (ret)
+                                       return ret;
+                       }
+
+                       clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+               }
+               if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+                       if (hfi_ops->session_resume_drain) {
+                               ret =
+                               hfi_ops->session_resume_drain(inst,
+                                                             
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+                               if (ret)
+                                       return ret;
+                       }
+
+                       clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+               }
+       } else {
+               dev_err(inst->core->dev, "start called before receiving 
last_flag\n");
+               iris_inst_change_state(inst, IRIS_INST_ERROR);
+               return -EBUSY;
+       }
+
+       return iris_inst_change_sub_state(inst, clear_sub_state, 0);
+}
+
+int iris_vdec_stop_cmd(struct iris_inst *inst)
+{
+       const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+       int ret;
+
+       ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+       if (ret)
+               return ret;
+
+       return iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h 
b/drivers/media/platform/qcom/iris/iris_vdec.h
index dfcc2089a1ef..b24932dc511a 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.h
+++ b/drivers/media/platform/qcom/iris/iris_vdec.h
@@ -18,6 +18,8 @@ void iris_vdec_src_change(struct iris_inst *inst);
 int iris_vdec_streamon_input(struct iris_inst *inst);
 int iris_vdec_streamon_output(struct iris_inst *inst);
 int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf);
+int iris_vdec_start_cmd(struct iris_inst *inst);
+int iris_vdec_stop_cmd(struct iris_inst *inst);
 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 2b27df6b1aad..ca0f4e310f77 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -365,6 +365,39 @@ static int iris_subscribe_event(struct v4l2_fh *fh, const 
struct v4l2_event_subs
        return iris_vdec_subscribe_event(inst, sub);
 }
 
+static int iris_dec_cmd(struct file *filp, void *fh,
+                       struct v4l2_decoder_cmd *dec)
+{
+       struct iris_inst *inst = iris_get_inst(filp, NULL);
+       int ret = 0;
+
+       mutex_lock(&inst->lock);
+
+       ret = v4l2_m2m_ioctl_decoder_cmd(filp, fh, dec);
+       if (ret)
+               goto unlock;
+
+       if (inst->state == IRIS_INST_DEINIT)
+               goto unlock;
+
+       if (!iris_allow_cmd(inst, dec->cmd)) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       if (dec->cmd == V4L2_DEC_CMD_START)
+               ret = iris_vdec_start_cmd(inst);
+       else if (dec->cmd == V4L2_DEC_CMD_STOP)
+               ret = iris_vdec_stop_cmd(inst);
+       else
+               ret = -EINVAL;
+
+unlock:
+       mutex_unlock(&inst->lock);
+
+       return ret;
+}
+
 static struct v4l2_file_operations iris_v4l2_file_ops = {
        .owner                          = THIS_MODULE,
        .open                           = iris_open,
@@ -408,6 +441,8 @@ static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops = {
        .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
        .vidioc_streamon                = v4l2_m2m_ioctl_streamon,
        .vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,
+       .vidioc_try_decoder_cmd         = v4l2_m2m_ioctl_try_decoder_cmd,
+       .vidioc_decoder_cmd             = iris_dec_cmd,
 };
 
 void iris_init_ops(struct iris_core *core)

Reply via email to