On 20/11/2024 15:46, Dikshita Agarwal wrote: > In stream on, send HFI_CMD_START on capture and output planes to start > the processing on respective planes. > > During stream off, send HFI_CMD_STOP to firmware which is a synchronous > command. After the response is received from firmware, the session is > closed on firmware. > > Introduce different states for 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 |<-----------+ > ----------. > > Signed-off-by: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx> Reviewed-by: Hans Verkuil <hverkuil@xxxxxxxxx> Regards, Hans > --- > drivers/media/platform/qcom/iris/Makefile | 1 + > drivers/media/platform/qcom/iris/iris_hfi_common.h | 2 + > .../platform/qcom/iris/iris_hfi_gen1_command.c | 82 +++++++++++++++- > .../platform/qcom/iris/iris_hfi_gen1_defines.h | 24 +++++ > .../platform/qcom/iris/iris_hfi_gen1_response.c | 39 +++++++- > .../platform/qcom/iris/iris_hfi_gen2_command.c | 61 ++++++++++++ > .../platform/qcom/iris/iris_hfi_gen2_defines.h | 2 + > .../platform/qcom/iris/iris_hfi_gen2_response.c | 32 ++++++- > drivers/media/platform/qcom/iris/iris_instance.h | 4 + > drivers/media/platform/qcom/iris/iris_state.c | 104 +++++++++++++++++++++ > drivers/media/platform/qcom/iris/iris_state.h | 58 ++++++++++++ > drivers/media/platform/qcom/iris/iris_utils.c | 11 ++- > drivers/media/platform/qcom/iris/iris_utils.h | 2 +- > drivers/media/platform/qcom/iris/iris_vb2.c | 70 ++++++++++++++ > drivers/media/platform/qcom/iris/iris_vb2.h | 3 + > drivers/media/platform/qcom/iris/iris_vdec.c | 75 +++++++++++++++ > drivers/media/platform/qcom/iris/iris_vdec.h | 3 + > drivers/media/platform/qcom/iris/iris_vidc.c | 12 ++- > 18 files changed, 573 insertions(+), 12 deletions(-) > > diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile > index f685d76c2f79..ab16189aa9e6 100644 > --- a/drivers/media/platform/qcom/iris/Makefile > +++ b/drivers/media/platform/qcom/iris/Makefile > @@ -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 \ > diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h > index eaa2db469c74..8b1c4d156cf2 100644 > --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h > +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h > @@ -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); > }; > > 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 7ee69c5223ce..a3b09e8d1f49 100644 > --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c > +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c > @@ -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, > }; > > 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 3640f8504db9..1b2bf6afc6ce 100644 > --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h > +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h > @@ -23,6 +23,12 @@ > #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 > > @@ -41,6 +50,11 @@ > #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; > }; > 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 18ba5f67dd36..db5858ec04ea 100644 > --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c > +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c > @@ -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; > 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 a08e844bb4bb..b0557917fc52 100644 > --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c > +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c > @@ -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, > }; > > 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 6be8a6ff7924..4cbd31448ff5 100644 > --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h > +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h > @@ -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 > 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 a7d8c5ff7f2f..0bd43a07394a 100644 > --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c > +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c > @@ -98,6 +98,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; > } > @@ -105,9 +106,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; > @@ -126,20 +135,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, > @@ -244,8 +265,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; > } > } > diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h > index 16b463cec4f4..f40df09e5323 100644 > --- a/drivers/media/platform/qcom/iris/iris_instance.h > +++ b/drivers/media/platform/qcom/iris/iris_instance.h > @@ -26,9 +26,11 @@ > * @ctrl_handler: reference of v4l2 ctrl handler > * @crop: structure of crop info > * @completions: structure of signal completions > + * @flush_completions: 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 > @@ -47,9 +49,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 > index 000000000000..44362e8fe18f > --- /dev/null > +++ b/drivers/media/platform/qcom/iris/iris_state.c > @@ -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); > +} > diff --git a/drivers/media/platform/qcom/iris/iris_state.h b/drivers/media/platform/qcom/iris/iris_state.h > index 776262615195..8a25c0c27df4 100644 > --- a/drivers/media/platform/qcom/iris/iris_state.h > +++ b/drivers/media/platform/qcom/iris/iris_state.h > @@ -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 > diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c > index d5c8e052922c..4833830f30d5 100644 > --- a/drivers/media/platform/qcom/iris/iris_utils.c > +++ b/drivers/media/platform/qcom/iris/iris_utils.c > @@ -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; > } > diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h > index 26649b66d978..40658a6643cf 100644 > --- a/drivers/media/platform/qcom/iris/iris_utils.h > +++ b/drivers/media/platform/qcom/iris/iris_utils.h > @@ -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 > diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c > index e9db44515d91..b93da860d336 100644 > --- a/drivers/media/platform/qcom/iris/iris_vb2.c > +++ b/drivers/media/platform/qcom/iris/iris_vb2.c > @@ -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 @@ int iris_vb2_queue_setup(struct vb2_queue *q, > > 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); > +} > diff --git a/drivers/media/platform/qcom/iris/iris_vb2.h b/drivers/media/platform/qcom/iris/iris_vb2.h > index d2e71d0596cc..3906510fa71f 100644 > --- a/drivers/media/platform/qcom/iris/iris_vb2.h > +++ b/drivers/media/platform/qcom/iris/iris_vb2.h > @@ -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 > diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c > index 5571c24a7417..615a780bf010 100644 > --- a/drivers/media/platform/qcom/iris/iris_vdec.c > +++ b/drivers/media/platform/qcom/iris/iris_vdec.c > @@ -225,3 +225,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; > +} > diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h > index 9f08a13cb6bb..a17bb817b6e5 100644 > --- a/drivers/media/platform/qcom/iris/iris_vdec.h > +++ b/drivers/media/platform/qcom/iris/iris_vdec.h > @@ -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 > diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c > index 26fdf03ab972..eb850f7da82c 100644 > --- a/drivers/media/platform/qcom/iris/iris_vidc.c > +++ b/drivers/media/platform/qcom/iris/iris_vidc.c > @@ -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) >