]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
media: iris: implement vb2 streaming ops
authorDikshita Agarwal <quic_dikshita@quicinc.com>
Fri, 7 Feb 2025 07:54:56 +0000 (13:24 +0530)
committerHans Verkuil <hverkuil@xs4all.nl>
Fri, 7 Feb 2025 10:51:33 +0000 (11:51 +0100)
During the stream on operation, send HFI_CMD_START on the capture and
output planes to start processing on the respective planes.

During the stream off operation, send HFI_CMD_STOP to the firmware,
which is a synchronous command. After the response is received by the
firmware, the session is closed on the firmware.

Introduce different states for the instance and state transitions.

IRIS_INST_INIT - video instance is opened.
IRIS_INST_INPUT_STREAMING - stream on is completed on output plane.
IRIS_INST_OUTPUT_STREAMING - stream on is completed on capture plane.
IRIS_INST_STREAMING - stream on is completed on both output and capture
planes.
IRIS_INST_DEINIT - video instance is closed.
IRIS_INST_ERROR - error state.

                   |
                   v
            -------------
  +---------|   INIT    |---------  +
  |         -------------           |
  |            ^    ^               |
  |           /      \              |
  |          /        \             |
  |         v          v            |
  |    -----------    -----------   |
  |   |   INPUT         OUTPUT  |   |
  |---| STREAMING     STREAMING |---|
  |    -----------    -----------   |
  |        ^            ^           |
  |         \          /            |
  |          \        /             |
  |           v      v              |
  |         -------------           |
  |--------|  STREAMING |-----------|
  |         -------------           |
  |               |                 |
  |               |                 |
  |               v                 |
  |          -----------            |
  +-------->|  DEINIT   |<----------+
  |          -----------            |
  |               |                 |
  |               |                 |
  |               v                 |
  |          ----------             |
  +-------->|   ERROR  |<-----------+
             ----------.

Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
Tested-by: Stefan Schmidt <stefan.schmidt@linaro.org> # x1e80100 (Dell XPS 13 9345)
Reviewed-by: Stefan Schmidt <stefan.schmidt@linaro.org>
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8550-QRD
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8550-HDK
Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
18 files changed:
drivers/media/platform/qcom/iris/Makefile
drivers/media/platform/qcom/iris/iris_hfi_common.h
drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
drivers/media/platform/qcom/iris/iris_instance.h
drivers/media/platform/qcom/iris/iris_state.c [new file with mode: 0644]
drivers/media/platform/qcom/iris/iris_state.h
drivers/media/platform/qcom/iris/iris_utils.c
drivers/media/platform/qcom/iris/iris_utils.h
drivers/media/platform/qcom/iris/iris_vb2.c
drivers/media/platform/qcom/iris/iris_vb2.h
drivers/media/platform/qcom/iris/iris_vdec.c
drivers/media/platform/qcom/iris/iris_vdec.h
drivers/media/platform/qcom/iris/iris_vidc.c

index f685d76c2f79a7badee0213f51583edc8a6bfc08..ab16189aa9e6594adc772c8b67376f630c38e58f 100644 (file)
@@ -12,6 +12,7 @@ iris-objs += iris_buffer.o \
              iris_platform_sm8550.o \
              iris_probe.o \
              iris_resources.o \
+             iris_state.o \
              iris_utils.o \
              iris_vidc.o \
              iris_vb2.o \
index eaa2db469c7445ef7074aeddd7e8bff59714454e..8b1c4d156cf277d518eb0dc8ead83ed5f6c9f690 100644 (file)
@@ -49,6 +49,8 @@ struct iris_hfi_command_ops {
        int (*sys_interframe_powercollapse)(struct iris_core *core);
        int (*sys_pc_prep)(struct iris_core *core);
        int (*session_open)(struct iris_inst *inst);
+       int (*session_start)(struct iris_inst *inst, u32 plane);
+       int (*session_stop)(struct iris_inst *inst, u32 plane);
        int (*session_close)(struct iris_inst *inst);
 };
 
index 7ee69c5223ce8876b9ad4efaeb2848e7291e9264..a3b09e8d1f491a0a75be10840a2ffcca91f1bd74 100644 (file)
@@ -71,6 +71,9 @@ static int iris_hfi_gen1_session_open(struct iris_inst *inst)
        struct hfi_session_open_pkt packet;
        int ret;
 
+       if (inst->state != IRIS_INST_DEINIT)
+               return -EALREADY;
+
        packet.shdr.hdr.size = sizeof(struct hfi_session_open_pkt);
        packet.shdr.hdr.pkt_type = HFI_CMD_SYS_SESSION_INIT;
        packet.shdr.session_id = inst->session_id;
@@ -83,7 +86,7 @@ static int iris_hfi_gen1_session_open(struct iris_inst *inst)
        if (ret)
                return ret;
 
-       return iris_wait_for_session_response(inst);
+       return iris_wait_for_session_response(inst, false);
 }
 
 static void iris_hfi_gen1_packet_session_cmd(struct iris_inst *inst,
@@ -104,12 +107,89 @@ static int iris_hfi_gen1_session_close(struct iris_inst *inst)
        return iris_hfi_queue_cmd_write(inst->core, &packet, packet.shdr.hdr.size);
 }
 
+static int iris_hfi_gen1_session_start(struct iris_inst *inst, u32 plane)
+{
+       struct iris_core *core = inst->core;
+       struct hfi_session_pkt packet;
+       int ret;
+
+       if (!V4L2_TYPE_IS_OUTPUT(plane))
+               return 0;
+
+       reinit_completion(&inst->completion);
+       iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_LOAD_RESOURCES);
+
+       ret = iris_hfi_queue_cmd_write(core, &packet, packet.shdr.hdr.size);
+       if (ret)
+               return ret;
+
+       ret = iris_wait_for_session_response(inst, false);
+       if (ret)
+               return ret;
+
+       reinit_completion(&inst->completion);
+       iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_START);
+
+       ret = iris_hfi_queue_cmd_write(core, &packet, packet.shdr.hdr.size);
+       if (ret)
+               return ret;
+
+       return iris_wait_for_session_response(inst, false);
+}
+
+static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane)
+{
+       struct hfi_session_flush_pkt flush_pkt;
+       struct iris_core *core = inst->core;
+       struct hfi_session_pkt pkt;
+       u32 flush_type = 0;
+       int ret = 0;
+
+       if ((V4L2_TYPE_IS_OUTPUT(plane) &&
+            inst->state == IRIS_INST_INPUT_STREAMING) ||
+           (V4L2_TYPE_IS_CAPTURE(plane) &&
+            inst->state == IRIS_INST_OUTPUT_STREAMING) ||
+           inst->state == IRIS_INST_ERROR) {
+               reinit_completion(&inst->completion);
+               iris_hfi_gen1_packet_session_cmd(inst, &pkt, HFI_CMD_SESSION_STOP);
+               ret = iris_hfi_queue_cmd_write(core, &pkt, pkt.shdr.hdr.size);
+               if (!ret)
+                       ret = iris_wait_for_session_response(inst, false);
+
+               reinit_completion(&inst->completion);
+               iris_hfi_gen1_packet_session_cmd(inst, &pkt, HFI_CMD_SESSION_RELEASE_RESOURCES);
+               ret = iris_hfi_queue_cmd_write(core, &pkt, pkt.shdr.hdr.size);
+               if (!ret)
+                       ret = iris_wait_for_session_response(inst, false);
+       } else if (inst->state == IRIS_INST_STREAMING) {
+               if (V4L2_TYPE_IS_OUTPUT(plane))
+                       flush_type = HFI_FLUSH_ALL;
+               else if (V4L2_TYPE_IS_CAPTURE(plane))
+                       flush_type = HFI_FLUSH_OUTPUT;
+
+               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 = flush_type;
+
+               ret = iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
+               if (!ret)
+                       ret = iris_wait_for_session_response(inst, true);
+       }
+
+       return ret;
+}
+
 static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = {
        .sys_init = iris_hfi_gen1_sys_init,
        .sys_image_version = iris_hfi_gen1_sys_image_version,
        .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse,
        .sys_pc_prep = iris_hfi_gen1_sys_pc_prep,
        .session_open = iris_hfi_gen1_session_open,
+       .session_start = iris_hfi_gen1_session_start,
+       .session_stop = iris_hfi_gen1_session_stop,
        .session_close = iris_hfi_gen1_session_close,
 };
 
index 3640f8504db970c0e1a59fa163dacfdca87ad188..1b2bf6afc6cef9486aac273fae66d28f53f85abc 100644 (file)
 #define HFI_CMD_SYS_SESSION_INIT                       0x10007
 #define HFI_CMD_SYS_SESSION_END                                0x10008
 
+#define HFI_CMD_SESSION_LOAD_RESOURCES                 0x211001
+#define HFI_CMD_SESSION_START                          0x211002
+#define HFI_CMD_SESSION_STOP                           0x211003
+#define HFI_CMD_SESSION_FLUSH                          0x211008
+#define HFI_CMD_SESSION_RELEASE_RESOURCES              0x21100c
+
 #define HFI_ERR_SESSION_UNSUPPORTED_SETTING            0x1008
 #define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE           0x1010
 #define HFI_ERR_SESSION_INVALID_SCALE_FACTOR           0x1012
@@ -31,6 +37,9 @@
 #define HFI_EVENT_SYS_ERROR                            0x1
 #define HFI_EVENT_SESSION_ERROR                                0x2
 
+#define HFI_FLUSH_OUTPUT                               0x1000002
+#define HFI_FLUSH_OUTPUT2                              0x1000003
+#define HFI_FLUSH_ALL                                  0x1000004
 #define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL                0x5
 #define HFI_PROPERTY_SYS_IMAGE_VERSION                 0x6
 
 #define HFI_MSG_SYS_PROPERTY_INFO                      0x2000a
 
 #define HFI_MSG_EVENT_NOTIFY                           0x21001
+#define HFI_MSG_SESSION_LOAD_RESOURCES                 0x221001
+#define HFI_MSG_SESSION_START                          0x221002
+#define HFI_MSG_SESSION_STOP                           0x221003
+#define HFI_MSG_SESSION_FLUSH                          0x221006
+#define HFI_MSG_SESSION_RELEASE_RESOURCES              0x22100a
 
 struct hfi_pkt_hdr {
        u32 size;
@@ -83,6 +97,11 @@ struct hfi_sys_pc_prep_pkt {
        struct hfi_pkt_hdr hdr;
 };
 
+struct hfi_session_flush_pkt {
+       struct hfi_session_hdr_pkt shdr;
+       u32 flush_type;
+};
+
 struct hfi_msg_event_notify_pkt {
        struct hfi_session_hdr_pkt shdr;
        u32 event_id;
@@ -116,6 +135,11 @@ struct hfi_msg_sys_property_info_pkt {
        u8 data[];
 };
 
+struct hfi_msg_session_flush_done_pkt {
+       struct hfi_msg_session_hdr_pkt shdr;
+       u32 flush_type;
+};
+
 struct hfi_enable {
        u32 enable;
 };
index 18ba5f67dd36d0f18e3ccb269a3a60bb42d588aa..db5858ec04ead29fa5abe7780998b496d4b50c41 100644 (file)
@@ -11,6 +11,7 @@ static void
 iris_hfi_gen1_sys_event_notify(struct iris_core *core, void *packet)
 {
        struct hfi_msg_event_notify_pkt *pkt = packet;
+       struct iris_inst *instance;
 
        if (pkt->event_id == HFI_EVENT_SYS_ERROR)
                dev_err(core->dev, "sys error (type: %x, session id:%x, data1:%x, data2:%x)\n",
@@ -18,6 +19,12 @@ iris_hfi_gen1_sys_event_notify(struct iris_core *core, void *packet)
                        pkt->event_data2);
 
        core->state = IRIS_CORE_ERROR;
+
+       mutex_lock(&core->lock);
+       list_for_each_entry(instance, &core->instances, list)
+               iris_inst_change_state(instance, IRIS_INST_ERROR);
+       mutex_unlock(&core->lock);
+
        schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10));
 }
 
@@ -44,6 +51,7 @@ iris_hfi_gen1_event_session_error(struct iris_inst *inst, struct hfi_msg_event_n
                        pkt->event_data2, pkt->event_data1,
                        pkt->shdr.session_id);
                iris_vb2_queue_error(inst);
+               iris_inst_change_state(inst, IRIS_INST_ERROR);
                break;
        }
 }
@@ -148,6 +156,26 @@ static const struct iris_hfi_gen1_response_pkt_info pkt_infos[] = {
         .pkt = HFI_MSG_SYS_SESSION_END,
         .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
        },
+       {
+        .pkt = HFI_MSG_SESSION_LOAD_RESOURCES,
+        .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+       },
+       {
+        .pkt = HFI_MSG_SESSION_START,
+        .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+       },
+       {
+        .pkt = HFI_MSG_SESSION_STOP,
+        .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+       },
+       {
+        .pkt = HFI_MSG_SESSION_FLUSH,
+        .pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
+       },
+       {
+        .pkt = HFI_MSG_SESSION_RELEASE_RESOURCES,
+        .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+       },
 };
 
 static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response)
@@ -156,6 +184,7 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response
        const struct iris_hfi_gen1_response_pkt_info *pkt_info;
        struct device *dev = core->dev;
        struct hfi_session_pkt *pkt;
+       struct completion *done;
        struct iris_inst *inst;
        bool found = false;
        u32 i;
@@ -205,7 +234,15 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response
                }
 
                mutex_lock(&inst->lock);
-               complete(&inst->completion);
+               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);
+
+               done = pkt_info->pkt == HFI_MSG_SESSION_FLUSH ?
+                       &inst->flush_completion : &inst->completion;
+               complete(done);
                mutex_unlock(&inst->lock);
 
                break;
index a08e844bb4bb3c34914d695771c7e216f2f241d5..b0557917fc527c1891a5bad6a1446dadc3b6095c 100644 (file)
@@ -85,6 +85,18 @@ static int iris_hfi_gen2_sys_pc_prep(struct iris_core *core)
        return ret;
 }
 
+static u32 iris_hfi_gen2_get_port(u32 plane)
+{
+       switch (plane) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               return HFI_PORT_BITSTREAM;
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               return HFI_PORT_RAW;
+       default:
+               return HFI_PORT_NONE;
+       }
+}
+
 static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst)
 {
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
@@ -124,6 +136,9 @@ static int iris_hfi_gen2_session_open(struct iris_inst *inst)
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        int ret;
 
+       if (inst->state != IRIS_INST_DEINIT)
+               return -EALREADY;
+
        inst_hfi_gen2->packet = kzalloc(4096, GFP_KERNEL);
        if (!inst_hfi_gen2->packet)
                return -ENOMEM;
@@ -188,12 +203,58 @@ static int iris_hfi_gen2_session_close(struct iris_inst *inst)
        return ret;
 }
 
+static int iris_hfi_gen2_session_start(struct iris_inst *inst, u32 plane)
+{
+       struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+       iris_hfi_gen2_packet_session_command(inst,
+                                            HFI_CMD_START,
+                                            (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+                                            HFI_HOST_FLAGS_INTR_REQUIRED),
+                                            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 int iris_hfi_gen2_session_stop(struct iris_inst *inst, u32 plane)
+{
+       struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+       int ret = 0;
+
+       reinit_completion(&inst->completion);
+
+       iris_hfi_gen2_packet_session_command(inst,
+                                            HFI_CMD_STOP,
+                                            (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);
+
+       ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+                                      inst_hfi_gen2->packet->size);
+       if (ret)
+               return ret;
+
+       return iris_wait_for_session_response(inst, false);
+}
+
 static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = {
        .sys_init = iris_hfi_gen2_sys_init,
        .sys_image_version = iris_hfi_gen2_sys_image_version,
        .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse,
        .sys_pc_prep = iris_hfi_gen2_sys_pc_prep,
        .session_open = iris_hfi_gen2_session_open,
+       .session_start = iris_hfi_gen2_session_start,
+       .session_stop = iris_hfi_gen2_session_stop,
        .session_close = iris_hfi_gen2_session_close,
 };
 
index 173a554a0d440996a939f4e2df11e407d406ed75..930dbae49dfa93009ce01e89d3c60a990dbbd23f 100644 (file)
@@ -15,6 +15,8 @@
 #define HFI_CMD_POWER_COLLAPSE                 0x01000002
 #define HFI_CMD_OPEN                           0x01000003
 #define HFI_CMD_CLOSE                          0x01000004
+#define HFI_CMD_START                          0x01000005
+#define HFI_CMD_STOP                           0x01000006
 #define HFI_CMD_END                            0x01FFFFFF
 
 #define HFI_PROP_BEGIN                         0x03000000
index e1c43daea6c7dd66faca2fa52866a2a347cad910..53b700ef852ebe5a1562950adb070ba2a3841865 100644 (file)
@@ -101,6 +101,7 @@ static int iris_hfi_gen2_handle_session_error(struct iris_inst *inst,
 
        dev_err(core->dev, "session error received %#x: %s\n", pkt->type, error);
        iris_vb2_queue_error(inst);
+       iris_inst_change_state(inst, IRIS_INST_ERROR);
 
        return 0;
 }
@@ -108,9 +109,17 @@ static int iris_hfi_gen2_handle_session_error(struct iris_inst *inst,
 static int iris_hfi_gen2_handle_system_error(struct iris_core *core,
                                             struct iris_hfi_packet *pkt)
 {
+       struct iris_inst *instance;
+
        dev_err(core->dev, "received system error of type %#x\n", pkt->type);
 
        core->state = IRIS_CORE_ERROR;
+
+       mutex_lock(&core->lock);
+       list_for_each_entry(instance, &core->instances, list)
+               iris_inst_change_state(instance, IRIS_INST_ERROR);
+       mutex_unlock(&core->lock);
+
        schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10));
 
        return 0;
@@ -129,20 +138,32 @@ static int iris_hfi_gen2_handle_system_init(struct iris_core *core,
        return 0;
 }
 
+static void iris_hfi_gen2_handle_session_close(struct iris_inst *inst,
+                                              struct iris_hfi_packet *pkt)
+{
+       if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+               iris_inst_change_state(inst, IRIS_INST_ERROR);
+               return;
+       }
+
+       complete(&inst->completion);
+}
+
 static int iris_hfi_gen2_handle_session_command(struct iris_inst *inst,
                                                struct iris_hfi_packet *pkt)
 {
-       int ret = 0;
-
        switch (pkt->type) {
        case HFI_CMD_CLOSE:
+               iris_hfi_gen2_handle_session_close(inst, pkt);
+               break;
+       case HFI_CMD_STOP:
                complete(&inst->completion);
                break;
        default:
                break;
        }
 
-       return ret;
+       return 0;
 }
 
 static int iris_hfi_gen2_handle_image_version_property(struct iris_core *core,
@@ -247,8 +268,11 @@ static int iris_hfi_gen2_handle_session_response(struct iris_core *core,
                        if (packet->flags & HFI_FW_FLAGS_SESSION_ERROR)
                                iris_hfi_gen2_handle_session_error(inst, packet);
 
-                       if (packet->type > range[i].begin && packet->type < range[i].end)
+                       if (packet->type > range[i].begin && packet->type < range[i].end) {
                                ret = range[i].handle(inst, packet);
+                               if (ret)
+                                       iris_inst_change_state(inst, IRIS_INST_ERROR);
+                       }
                        pkt += packet->size;
                }
        }
index 9f1a1e5ba7c7961c48e9c1cebe1a060532df76ea..6b88daf31011c88ffcfc611592e95f6d81fbd7de 100644 (file)
  * @ctrl_handler: reference of v4l2 ctrl handler
  * @crop: structure of crop info
  * @completion: structure of signal completions
+ * @flush_completion: structure of signal completions for flush cmd
  * @fw_caps: array of supported instance firmware capabilities
  * @buffers: array of different iris buffers
  * @fw_min_count: minimnum count of buffers needed by fw
+ * @state: instance state
  * @once_per_session_set: boolean to set once per session property
  * @m2m_dev:   a reference to m2m device structure
  * @m2m_ctx:   a reference to m2m context structure
@@ -46,9 +48,11 @@ struct iris_inst {
        struct v4l2_ctrl_handler        ctrl_handler;
        struct iris_hfi_rect_desc       crop;
        struct completion               completion;
+       struct completion               flush_completion;
        struct platform_inst_fw_cap     fw_caps[INST_FW_CAP_MAX];
        struct iris_buffers             buffers[BUF_TYPE_MAX];
        u32                             fw_min_count;
+       enum iris_inst_state            state;
        bool                            once_per_session_set;
        struct v4l2_m2m_dev             *m2m_dev;
        struct v4l2_m2m_ctx             *m2m_ctx;
diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c
new file mode 100644 (file)
index 0000000..44362e8
--- /dev/null
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_instance.h"
+
+static bool iris_allow_inst_state_change(struct iris_inst *inst,
+                                        enum iris_inst_state req_state)
+{
+       switch (inst->state) {
+       case IRIS_INST_INIT:
+               if (req_state == IRIS_INST_INPUT_STREAMING ||
+                   req_state == IRIS_INST_OUTPUT_STREAMING ||
+                   req_state == IRIS_INST_DEINIT)
+                       return true;
+               return false;
+       case IRIS_INST_INPUT_STREAMING:
+               if (req_state == IRIS_INST_INIT ||
+                   req_state == IRIS_INST_STREAMING ||
+                   req_state == IRIS_INST_DEINIT)
+                       return true;
+               return false;
+       case IRIS_INST_OUTPUT_STREAMING:
+               if (req_state == IRIS_INST_INIT ||
+                   req_state == IRIS_INST_STREAMING ||
+                   req_state == IRIS_INST_DEINIT)
+                       return true;
+               return false;
+       case IRIS_INST_STREAMING:
+               if (req_state == IRIS_INST_INPUT_STREAMING ||
+                   req_state == IRIS_INST_OUTPUT_STREAMING ||
+                   req_state == IRIS_INST_DEINIT)
+                       return true;
+               return false;
+       case IRIS_INST_DEINIT:
+               if (req_state == IRIS_INST_INIT)
+                       return true;
+               return false;
+       default:
+               return false;
+       }
+}
+
+int iris_inst_change_state(struct iris_inst *inst,
+                          enum iris_inst_state request_state)
+{
+       if (inst->state == IRIS_INST_ERROR)
+               return 0;
+
+       if (inst->state == request_state)
+               return 0;
+
+       if (request_state == IRIS_INST_ERROR)
+               goto change_state;
+
+       if (!iris_allow_inst_state_change(inst, request_state))
+               return -EINVAL;
+
+change_state:
+       inst->state = request_state;
+       dev_dbg(inst->core->dev, "state changed from %x to %x\n",
+               inst->state, request_state);
+
+       return 0;
+}
+
+int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane)
+{
+       enum iris_inst_state new_state = IRIS_INST_ERROR;
+
+       if (V4L2_TYPE_IS_OUTPUT(plane)) {
+               if (inst->state == IRIS_INST_INIT)
+                       new_state = IRIS_INST_INPUT_STREAMING;
+               else if (inst->state == IRIS_INST_OUTPUT_STREAMING)
+                       new_state = IRIS_INST_STREAMING;
+       } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+               if (inst->state == IRIS_INST_INIT)
+                       new_state = IRIS_INST_OUTPUT_STREAMING;
+               else if (inst->state == IRIS_INST_INPUT_STREAMING)
+                       new_state = IRIS_INST_STREAMING;
+       }
+
+       return iris_inst_change_state(inst, new_state);
+}
+
+int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane)
+{
+       enum iris_inst_state new_state = IRIS_INST_ERROR;
+
+       if (V4L2_TYPE_IS_OUTPUT(plane)) {
+               if (inst->state == IRIS_INST_INPUT_STREAMING)
+                       new_state = IRIS_INST_INIT;
+               else if (inst->state == IRIS_INST_STREAMING)
+                       new_state = IRIS_INST_OUTPUT_STREAMING;
+       } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+               if (inst->state == IRIS_INST_OUTPUT_STREAMING)
+                       new_state = IRIS_INST_INIT;
+               else if (inst->state == IRIS_INST_STREAMING)
+                       new_state = IRIS_INST_INPUT_STREAMING;
+       }
+
+       return iris_inst_change_state(inst, new_state);
+}
index 1ffe6fe706bd162dc819638f02a138823d006006..0bf9d0e063ac0921b04ec32861b4e422228f2dda 100644 (file)
@@ -6,6 +6,8 @@
 #ifndef __IRIS_STATE_H__
 #define __IRIS_STATE_H__
 
+struct iris_inst;
+
 /**
  * enum iris_core_state
  *
@@ -38,4 +40,60 @@ enum iris_core_state {
        IRIS_CORE_ERROR,
 };
 
+/**
+ * enum iris_inst_state
+ *
+ * @IRIS_INST_INIT: video instance is opened.
+ * @IRIS_INST_INPUT_STREAMING: stream on is completed on output plane.
+ * @IRIS_INST_OUTPUT_STREAMING: stream on is completed on capture plane.
+ * @IRIS_INST_STREAMING: stream on is completed on both output and capture planes.
+ * @IRIS_INST_DEINIT: video instance is closed.
+ * @IRIS_INST_ERROR: error state.
+ *                    |
+ *                    V
+ *             -------------
+ *   +--------|     INIT    |----------+
+ *   |         -------------           |
+ *   |            ^   ^                |
+ *   |           /      \              |
+ *   |          /        \             |
+ *   |         v          v            |
+ *   |   -----------    -----------    |
+ *   |   |   INPUT         OUTPUT  |   |
+ *   |---| STREAMING     STREAMING |---|
+ *   |   -----------    -----------    |
+ *   |       ^            ^            |
+ *   |         \          /            |
+ *   |          \        /             |
+ *   |           v      v              |
+ *   |         -------------           |
+ *   |--------|  STREAMING |-----------|
+ *   |        -------------            |
+ *   |               |                 |
+ *   |               |                 |
+ *   |               v                 |
+ *   |          -----------            |
+ *   +-------->|  DEINIT   |<----------+
+ *   |          -----------            |
+ *   |               |                 |
+ *   |               |                 |
+ *   |               v                 |
+ *   |          ----------             |
+ *   +-------->|   ERROR |<------------+
+ *              ----------
+ */
+enum iris_inst_state {
+       IRIS_INST_DEINIT,
+       IRIS_INST_INIT,
+       IRIS_INST_INPUT_STREAMING,
+       IRIS_INST_OUTPUT_STREAMING,
+       IRIS_INST_STREAMING,
+       IRIS_INST_ERROR,
+};
+
+int iris_inst_change_state(struct iris_inst *inst,
+                          enum iris_inst_state request_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);
+
 #endif
index d5c8e052922cd5e38bfd099a3f47fbca3c69e500..4833830f30d56715320244fe339e09fd515fcef4 100644 (file)
@@ -17,20 +17,23 @@ int iris_get_mbpf(struct iris_inst *inst)
        return NUM_MBS_PER_FRAME(height, width);
 }
 
-int iris_wait_for_session_response(struct iris_inst *inst)
+int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush)
 {
        struct iris_core *core = inst->core;
        u32 hw_response_timeout_val;
+       struct completion *done;
        int ret;
 
        hw_response_timeout_val = core->iris_platform_data->hw_response_timeout;
+       done = is_flush ? &inst->flush_completion : &inst->completion;
 
        mutex_unlock(&inst->lock);
-       ret = wait_for_completion_timeout(&inst->completion,
-                                         msecs_to_jiffies(hw_response_timeout_val));
+       ret = wait_for_completion_timeout(done, msecs_to_jiffies(hw_response_timeout_val));
        mutex_lock(&inst->lock);
-       if (!ret)
+       if (!ret) {
+               iris_inst_change_state(inst, IRIS_INST_ERROR);
                return -ETIMEDOUT;
+       }
 
        return 0;
 }
index 26649b66d978ab9f3a6c942e0b5d019b38106e76..40658a6643cf5bc02720104e2758202027e5ecdd 100644 (file)
@@ -29,6 +29,6 @@ static inline enum iris_buffer_type iris_v4l2_type_to_driver(u32 type)
 
 int iris_get_mbpf(struct iris_inst *inst);
 struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id);
-int iris_wait_for_session_response(struct iris_inst *inst);
+int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush);
 
 #endif
index e9db44515d91f89779ff110950e0a66ef7bc549c..b93da860d336421fd0da58537ed3faf9639484f6 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "iris_instance.h"
 #include "iris_vb2.h"
+#include "iris_vdec.h"
 
 int iris_vb2_queue_setup(struct vb2_queue *q,
                         unsigned int *num_buffers, unsigned int *num_planes,
@@ -18,6 +19,10 @@ int iris_vb2_queue_setup(struct vb2_queue *q,
        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;
@@ -38,6 +43,10 @@ int iris_vb2_queue_setup(struct vb2_queue *q,
                        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;
@@ -48,3 +57,64 @@ unlock:
 
        return ret;
 }
+
+int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       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;
+       }
+
+       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;
+
+       mutex_unlock(&inst->lock);
+
+       return ret;
+
+error:
+       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;
+
+       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;
+
+       iris_vdec_session_streamoff(inst, q->type);
+
+exit:
+       mutex_unlock(&inst->lock);
+}
index d2e71d0596cc4dd746c60fdb348953b9037c443b..3906510fa71f3c1fe6f69b7e2cbcb50b7833a2cb 100644 (file)
@@ -9,4 +9,7 @@
 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);
+
 #endif
index 132b578b34dca56dd218cbcebb41aa37abced7f1..92651d86376d6ca36366919d523ef86642163cb1 100644 (file)
@@ -222,3 +222,78 @@ int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_su
 
        return ret;
 }
+
+static void iris_vdec_kill_session(struct iris_inst *inst)
+{
+       const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+
+       if (!inst->session_id)
+               return;
+
+       hfi_ops->session_close(inst);
+       iris_inst_change_state(inst, IRIS_INST_ERROR);
+}
+
+void iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane)
+{
+       const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+       int ret;
+
+       ret = hfi_ops->session_stop(inst, plane);
+       if (ret)
+               goto error;
+
+       ret = iris_inst_state_change_streamoff(inst, plane);
+       if (ret)
+               goto error;
+
+       return;
+
+error:
+       iris_vdec_kill_session(inst);
+}
+
+static int iris_vdec_process_streamon_input(struct iris_inst *inst)
+{
+       const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+       int ret;
+
+       ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+       if (ret)
+               return ret;
+
+       return iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+int iris_vdec_streamon_input(struct iris_inst *inst)
+{
+       return iris_vdec_process_streamon_input(inst);
+}
+
+static int iris_vdec_process_streamon_output(struct iris_inst *inst)
+{
+       const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+       int ret;
+
+       ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+       if (ret)
+               return ret;
+
+       return iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
+int iris_vdec_streamon_output(struct iris_inst *inst)
+{
+       int ret;
+
+       ret = iris_vdec_process_streamon_output(inst);
+       if (ret)
+               goto error;
+
+       return ret;
+
+error:
+       iris_vdec_session_streamoff(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+       return ret;
+}
index 9f08a13cb6bbefece1fd2c815e90d64390e9111d..a17bb817b6e5bbe74a770ef9ff98d7bf6120665f 100644 (file)
@@ -14,5 +14,8 @@ int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f);
 int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f);
 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);
 
 #endif
index 5b54231f2def7e4d23b8047bdf35bcc13cdb5247..1d10c430c795d9df3503c37eabbc5da160348e06 100644 (file)
@@ -145,10 +145,12 @@ int iris_open(struct file *filp)
 
        inst->core = core;
        inst->session_id = hash32_ptr(inst);
+       inst->state = IRIS_INST_DEINIT;
 
        mutex_init(&inst->lock);
        mutex_init(&inst->ctx_q_lock);
        init_completion(&inst->completion);
+       init_completion(&inst->flush_completion);
 
        iris_v4l2_fh_init(inst);
 
@@ -194,6 +196,9 @@ static void iris_session_close(struct iris_inst *inst)
        bool wait_for_response = true;
        int ret;
 
+       if (inst->state == IRIS_INST_DEINIT)
+               return;
+
        reinit_completion(&inst->completion);
 
        ret = hfi_ops->session_close(inst);
@@ -201,7 +206,7 @@ static void iris_session_close(struct iris_inst *inst)
                wait_for_response = false;
 
        if (wait_for_response)
-               iris_wait_for_session_response(inst);
+               iris_wait_for_session_response(inst, false);
 }
 
 int iris_close(struct file *filp)
@@ -214,6 +219,7 @@ int iris_close(struct file *filp)
        mutex_lock(&inst->lock);
        iris_vdec_inst_deinit(inst);
        iris_session_close(inst);
+       iris_inst_change_state(inst, IRIS_INST_DEINIT);
        iris_v4l2_fh_deinit(inst);
        iris_remove_session(inst);
        mutex_unlock(&inst->lock);
@@ -356,6 +362,8 @@ static struct v4l2_file_operations iris_v4l2_file_ops = {
 
 static const struct vb2_ops iris_vb2_ops = {
        .queue_setup                    = iris_vb2_queue_setup,
+       .start_streaming                = iris_vb2_start_streaming,
+       .stop_streaming                 = iris_vb2_stop_streaming,
 };
 
 static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops = {
@@ -373,6 +381,8 @@ static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops = {
        .vidioc_g_selection             = iris_g_selection,
        .vidioc_subscribe_event         = iris_subscribe_event,
        .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+       .vidioc_streamon                = v4l2_m2m_ioctl_streamon,
+       .vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,
 };
 
 void iris_init_ops(struct iris_core *core)