@@ -16,7 +16,10 @@
#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_PAUSE 0x01000011
#define HFI_PROP_IMAGE_VERSION 0x03000001
@@ -208,6 +211,20 @@ enum hfi_picture_type {
#define HFI_SYSTEM_ERROR_END 0x05FFFFFF
+#define HFI_INFORMATION_BEGIN 0x06000000
+
+#define HFI_INFO_UNSUPPORTED 0x06000001
+
+#define HFI_INFO_DATA_CORRUPT 0x06000002
+
+#define HFI_INFO_BUFFER_OVERFLOW 0x06000004
+
+#define HFI_INFO_HFI_FLAG_DRAIN_LAST 0x06000006
+
+#define HFI_INFO_HFI_FLAG_PSC_LAST 0x06000007
+
+#define HFI_INFORMATION_END 0x06FFFFFF
+
struct hfi_debug_header {
u32 size;
u32 debug_level;
@@ -242,6 +259,8 @@ enum hfi_buffer_firmware_flags {
HFI_BUF_FW_FLAG_NONE = 0x00000000,
HFI_BUF_FW_FLAG_RELEASE_DONE = 0x00000001,
HFI_BUF_FW_FLAG_READONLY = 0x00000010,
+ HFI_BUF_FW_FLAG_LAST = 0x10000000,
+ HFI_BUF_FW_FLAG_PSC_LAST = 0x20000000,
};
#endif
@@ -547,7 +547,7 @@ static int iris_release_internal_buffers(struct iris_inst *inst,
return ret;
}
-int iris_release_input_internal_buffers(struct iris_inst *inst)
+static int iris_release_input_internal_buffers(struct iris_inst *inst)
{
int ret = 0;
u32 i = 0;
@@ -581,3 +581,57 @@ int iris_alloc_and_queue_session_int_bufs(struct iris_inst *inst,
return ret;
}
+
+int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst)
+{
+ int ret;
+
+ ret = iris_get_internal_buffers(inst, INPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_release_input_internal_buffers(inst);
+ if (ret)
+ return ret;
+
+ ret = iris_create_input_internal_buffers(inst);
+ if (ret)
+ return ret;
+
+ ret = iris_queue_input_internal_buffers(inst);
+
+ return ret;
+}
+
+int iris_alloc_and_queue_additional_dpb_buffers(struct iris_inst *inst)
+{
+ struct iris_buffers *buffers;
+ struct iris_buffer *buffer;
+ int cur_min_count = 0;
+ int ret;
+ int i;
+
+ ret = iris_get_internal_buf_info(inst, BUF_DPB);
+ if (ret)
+ return ret;
+
+ buffers = iris_get_buffer_list(inst, BUF_DPB);
+ if (!buffers)
+ return -EINVAL;
+
+ list_for_each_entry(buffer, &buffers->list, list)
+ cur_min_count++;
+
+ if (cur_min_count >= buffers->min_count)
+ return 0;
+
+ for (i = cur_min_count; i < buffers->min_count; i++) {
+ ret = iris_create_internal_buffers(inst, BUF_DPB);
+ if (ret)
+ return ret;
+ }
+
+ ret = iris_queue_internal_buffers(inst, BUF_DPB);
+
+ return ret;
+}
@@ -57,8 +57,9 @@ int iris_destroy_internal_buffer(struct iris_inst *inst,
struct iris_buffer *buffer);
int iris_destroy_internal_buffers(struct iris_inst *inst,
u32 plane);
-int iris_release_input_internal_buffers(struct iris_inst *inst);
int iris_alloc_and_queue_session_int_bufs(struct iris_inst *inst,
enum iris_buffer_type buffer_type);
+int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst);
+int iris_alloc_and_queue_additional_dpb_buffers(struct iris_inst *inst);
#endif
@@ -76,6 +76,7 @@ enum iris_buffer_flags {
BUF_FLAG_PFRAME = 0x00000010,
BUF_FLAG_BFRAME = 0x00000020,
BUF_FLAG_ERROR = 0x00000040,
+ BUF_FLAG_LAST = 0x00100000,
};
enum iris_buffer_attributes {
@@ -445,6 +445,124 @@ int iris_hfi_session_subscribe_mode(struct iris_inst *inst,
return ret;
}
+int iris_hfi_pause(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core;
+ int ret = 0;
+
+ if (!inst->packet)
+ return -EINVAL;
+
+ core = inst->core;
+ mutex_lock(&core->lock);
+
+ if (!validate_session(core, inst)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (plane != INPUT_MPLANE && plane != OUTPUT_MPLANE)
+ goto unlock;
+
+ ret = hfi_packet_session_command(inst,
+ HFI_CMD_PAUSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ get_hfi_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (ret)
+ goto unlock;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst->packet);
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+int iris_hfi_resume(struct iris_inst *inst, u32 plane, u32 payload)
+{
+ struct iris_core *core;
+ int ret = 0;
+
+ if (!inst->packet)
+ return -EINVAL;
+
+ core = inst->core;
+ mutex_lock(&core->lock);
+
+ if (!validate_session(core, inst)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (plane != INPUT_MPLANE && plane != OUTPUT_MPLANE)
+ goto unlock;
+
+ ret = hfi_packet_session_command(inst,
+ HFI_CMD_RESUME,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ get_hfi_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_U32,
+ &payload,
+ sizeof(u32));
+ if (ret)
+ goto unlock;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst->packet);
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+int iris_hfi_drain(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core;
+ int ret = 0;
+
+ if (!inst->packet)
+ return -EINVAL;
+
+ core = inst->core;
+ mutex_lock(&core->lock);
+
+ if (!validate_session(core, inst)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (plane != INPUT_MPLANE)
+ goto unlock;
+
+ ret = hfi_packet_session_command(inst,
+ HFI_CMD_DRAIN,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ get_hfi_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (ret)
+ goto unlock;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst->packet);
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
irqreturn_t iris_hfi_isr(int irq, void *data)
{
disable_irq_nosync(irq);
@@ -25,7 +25,9 @@ int iris_hfi_session_set_default_header(struct iris_inst *inst);
int iris_hfi_start(struct iris_inst *inst, u32 plane);
int iris_hfi_stop(struct iris_inst *inst, u32 plane);
-
+int iris_hfi_drain(struct iris_inst *inst, u32 plane);
+int iris_hfi_pause(struct iris_inst *inst, u32 plane);
+int iris_hfi_resume(struct iris_inst *inst, u32 plane, u32 cmd);
int iris_hfi_queue_buffer(struct iris_inst *inst,
struct iris_buffer *buffer);
int iris_hfi_release_buffer(struct iris_inst *inst,
@@ -103,6 +103,10 @@ static int get_driver_buffer_flags(struct iris_inst *inst, u32 hfi_flags)
if (inst->hfi_frame_info.overflow)
driver_flags |= BUF_FLAG_ERROR;
+ if (hfi_flags & HFI_BUF_FW_FLAG_LAST ||
+ hfi_flags & HFI_BUF_FW_FLAG_PSC_LAST)
+ driver_flags |= BUF_FLAG_LAST;
+
return driver_flags;
}
@@ -187,6 +191,46 @@ static int validate_hdr_packet(struct iris_core *core, struct hfi_header *hdr)
return ret;
}
+static int handle_session_info(struct iris_inst *inst,
+ struct hfi_packet *pkt)
+{
+ struct iris_core *core;
+ int ret = 0;
+ char *info;
+
+ core = inst->core;
+
+ switch (pkt->type) {
+ case HFI_INFO_UNSUPPORTED:
+ info = "unsupported";
+ break;
+ case HFI_INFO_DATA_CORRUPT:
+ info = "data corrupt";
+ inst->hfi_frame_info.data_corrupt = 1;
+ break;
+ case HFI_INFO_BUFFER_OVERFLOW:
+ info = "buffer overflow";
+ inst->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);
+ break;
+ default:
+ info = "unknown";
+ break;
+ }
+
+ dev_dbg(core->dev, "session info received %#x: %s\n",
+ pkt->type, info);
+
+ return ret;
+}
+
static int handle_session_error(struct iris_inst *inst,
struct hfi_packet *pkt)
{
@@ -375,9 +419,21 @@ static int handle_output_buffer(struct iris_inst *inst,
bool found;
int ret = 0;
+ 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_RELEASE_DONE)
return handle_release_output_buffer(inst, hfi_buffer);
+ if (hfi_buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST) {
+ ret = iris_inst_sub_state_change_drc_last(inst);
+ if (ret)
+ return ret;
+ }
+
if (!(hfi_buffer->flags & HFI_BUF_FW_FLAG_READONLY))
ret = handle_non_read_only_buffer(inst, hfi_buffer);
@@ -488,6 +544,26 @@ static int handle_release_internal_buffer(struct iris_inst *inst,
return ret;
}
+static int handle_session_stop(struct iris_inst *inst,
+ struct hfi_packet *pkt)
+{
+ int ret = 0;
+ enum signal_session_response signal_type = -1;
+
+ if (pkt->port == HFI_PORT_RAW) {
+ signal_type = SIGNAL_CMD_STOP_OUTPUT;
+ ret = iris_inst_sub_state_change_pause(inst, OUTPUT_MPLANE);
+ } else if (pkt->port == HFI_PORT_BITSTREAM) {
+ signal_type = SIGNAL_CMD_STOP_INPUT;
+ ret = iris_inst_sub_state_change_pause(inst, INPUT_MPLANE);
+ }
+
+ if (signal_type != -1)
+ signal_session_msg_receipt(inst, signal_type);
+
+ return ret;
+}
+
static int handle_session_buffer(struct iris_inst *inst,
struct hfi_packet *pkt)
{
@@ -549,18 +625,30 @@ static int handle_session_buffer(struct iris_inst *inst,
return ret;
}
+static int handle_session_drain(struct iris_inst *inst,
+ struct hfi_packet *pkt)
+{
+ int ret = 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 int handle_src_change(struct iris_inst *inst,
struct hfi_packet *pkt)
{
- int ret = 0;
+ int ret;
- if (pkt->port == HFI_PORT_BITSTREAM)
- ret = vdec_src_change(inst);
- else if (pkt->port == HFI_PORT_RAW)
- ret = 0;
+ if (pkt->port != HFI_PORT_BITSTREAM)
+ return 0;
+ ret = iris_inst_sub_state_change_drc(inst);
if (ret)
- iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return ret;
+
+ ret = vdec_src_change(inst);
return ret;
}
@@ -572,9 +660,14 @@ static int handle_session_command(struct iris_inst *inst,
static const struct iris_hfi_packet_handle hfi_pkt_handle[] = {
{HFI_CMD_OPEN, NULL },
{HFI_CMD_CLOSE, handle_session_close },
+ {HFI_CMD_START, NULL },
+ {HFI_CMD_STOP, handle_session_stop },
+ {HFI_CMD_DRAIN, handle_session_drain },
{HFI_CMD_BUFFER, handle_session_buffer },
{HFI_CMD_SETTINGS_CHANGE, handle_src_change },
{HFI_CMD_SUBSCRIBE_MODE, NULL },
+ {HFI_CMD_PAUSE, NULL },
+ {HFI_CMD_RESUME, NULL },
};
for (i = 0; i < ARRAY_SIZE(hfi_pkt_handle); i++) {
@@ -774,6 +867,7 @@ static int handle_session_response(struct iris_core *core,
int i, j;
static const struct iris_inst_hfi_range be[] = {
{HFI_SESSION_ERROR_BEGIN, HFI_SESSION_ERROR_END, handle_session_error },
+ {HFI_INFORMATION_BEGIN, HFI_INFORMATION_END, handle_session_info },
{HFI_PROP_BEGIN, HFI_PROP_END, handle_session_property },
{HFI_CMD_BEGIN, HFI_CMD_END, handle_session_command },
};
@@ -39,6 +39,7 @@
* @buffers: structure of buffer info
* @fw_min_count: minimnum count of buffers needed by fw
* @state: instance state
+ * @sub_state: instance sub state
* @ipsc_properties_set: boolean to set ipsc properties to fw
* @opsc_properties_set: boolean to set opsc properties to fw
* @hfi_frame_info: structure of frame info
@@ -71,6 +72,7 @@ struct iris_inst {
struct iris_buffers_info buffers;
u32 fw_min_count;
enum iris_inst_state state;
+ enum iris_inst_sub_state sub_state;
bool ipsc_properties_set;
bool opsc_properties_set;
struct iris_hfi_frame_info hfi_frame_info;
@@ -239,3 +239,214 @@ int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane)
return iris_inst_change_state(inst, new_state);
}
+
+struct iris_inst_sub_state_change_allow {
+ enum iris_inst_state state;
+ enum state_change allow;
+ u32 sub_state_mask;
+};
+
+static int iris_inst_allow_sub_state(struct iris_inst *inst, enum iris_inst_sub_state sub_state)
+{
+ int i;
+ static struct iris_inst_sub_state_change_allow sub_state_allow[] = {
+ {IRIS_INST_OPEN, STATE_CHANGE_DISALLOW, IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE |
+ IRIS_INST_SUB_OUTPUT_PAUSE},
+
+ {IRIS_INST_INPUT_STREAMING, STATE_CHANGE_DISALLOW, IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_OUTPUT_PAUSE},
+
+ {IRIS_INST_INPUT_STREAMING, STATE_CHANGE_ALLOW, IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_INPUT_PAUSE },
+
+ {IRIS_INST_OUTPUT_STREAMING, STATE_CHANGE_DISALLOW, IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_INPUT_PAUSE },
+
+ {IRIS_INST_OUTPUT_STREAMING, STATE_CHANGE_ALLOW, IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_OUTPUT_PAUSE},
+
+ {IRIS_INST_STREAMING, STATE_CHANGE_ALLOW, IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE |
+ IRIS_INST_SUB_OUTPUT_PAUSE},
+
+ {IRIS_INST_CLOSE, STATE_CHANGE_ALLOW, IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE |
+ IRIS_INST_SUB_OUTPUT_PAUSE},
+
+ {IRIS_INST_ERROR, STATE_CHANGE_ALLOW, IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE |
+ IRIS_INST_SUB_OUTPUT_PAUSE},
+ };
+
+ if (!sub_state)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(sub_state_allow); i++) {
+ if (sub_state_allow[i].state != inst->state)
+ continue;
+
+ if (sub_state_allow[i].allow == STATE_CHANGE_DISALLOW)
+ if (sub_state_allow[i].sub_state_mask & sub_state) {
+ dev_dbg(inst->core->dev, "state %d with disallowed sub state %x\n",
+ inst->state, sub_state);
+ return -EINVAL;
+ }
+
+ if (sub_state_allow[i].allow == STATE_CHANGE_ALLOW) {
+ if ((sub_state_allow[i].sub_state_mask & sub_state) != sub_state)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+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)
+{
+ enum iris_inst_sub_state prev_sub_state;
+ int ret;
+
+ if (IS_SESSION_ERROR(inst))
+ return 0;
+
+ if (!clear_sub_state && !set_sub_state)
+ return 0;
+
+ if ((clear_sub_state & set_sub_state) ||
+ set_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE ||
+ clear_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE)
+ return -EINVAL;
+
+ prev_sub_state = inst->sub_state;
+
+ ret = iris_inst_allow_sub_state(inst, set_sub_state);
+ if (ret)
+ return ret;
+
+ inst->sub_state |= set_sub_state;
+ inst->sub_state &= ~clear_sub_state;
+
+ if (inst->sub_state != prev_sub_state)
+ dev_dbg(inst->core->dev, "state %d and sub state changed to %x\n",
+ inst->sub_state, inst->sub_state);
+
+ return 0;
+}
+
+int iris_inst_sub_state_change_drc(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state set_sub_state = 0;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC)
+ return STATE_CHANGE_DISALLOW;
+
+ if (inst->state == IRIS_INST_INPUT_STREAMING ||
+ inst->state == IRIS_INST_OPEN)
+ set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
+ else
+ set_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_INPUT_PAUSE;
+
+ 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 = IRIS_INST_SUB_NONE;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)
+ return STATE_CHANGE_DISALLOW;
+
+ if (!(inst->sub_state & IRIS_INST_SUB_DRAIN) ||
+ !(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE))
+ return STATE_CHANGE_DISALLOW;
+
+ 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 = IRIS_INST_SUB_NONE;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC_LAST)
+ return STATE_CHANGE_DISALLOW;
+
+ if (!(inst->sub_state & IRIS_INST_SUB_DRC) ||
+ !(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE))
+ return STATE_CHANGE_DISALLOW;
+
+ set_sub_state = IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
+
+ 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 = IRIS_INST_SUB_NONE;
+
+ if (plane == INPUT_MPLANE) {
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ !(inst->sub_state & IRIS_INST_SUB_DRC_LAST))
+ return STATE_CHANGE_DISALLOW;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ !(inst->sub_state & IRIS_INST_SUB_DRAIN_LAST))
+ return STATE_CHANGE_DISALLOW;
+
+ 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);
+}
+
+bool is_drc_pending(struct iris_inst *inst)
+{
+ return inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST;
+}
+
+bool is_drain_pending(struct iris_inst *inst)
+{
+ return inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+}
+
+bool allow_cmd(struct iris_inst *inst, u32 cmd)
+{
+ if (cmd == V4L2_DEC_CMD_START) {
+ if (inst->state == IRIS_INST_INPUT_STREAMING ||
+ inst->state == IRIS_INST_OUTPUT_STREAMING ||
+ inst->state == IRIS_INST_STREAMING)
+ if (is_drc_pending(inst) || is_drain_pending(inst))
+ return true;
+ } else if (cmd == V4L2_DEC_CMD_STOP) {
+ if (inst->state == IRIS_INST_INPUT_STREAMING ||
+ inst->state == IRIS_INST_STREAMING)
+ if (inst->sub_state != IRIS_INST_SUB_DRAIN)
+ return true;
+ }
+
+ return false;
+}
@@ -25,6 +25,19 @@ enum iris_inst_state {
IRIS_INST_ERROR,
};
+#define IRIS_INST_SUB_NONE 0
+#define IRIS_INST_SUB_STATES 6
+#define IRIS_INST_MAX_SUB_STATE_VALUE ((1 << IRIS_INST_SUB_STATES) - 1)
+
+enum iris_inst_sub_state {
+ IRIS_INST_SUB_DRAIN = BIT(0),
+ IRIS_INST_SUB_DRC = BIT(1),
+ IRIS_INST_SUB_DRAIN_LAST = BIT(2),
+ IRIS_INST_SUB_DRC_LAST = BIT(3),
+ IRIS_INST_SUB_INPUT_PAUSE = BIT(4),
+ IRIS_INST_SUB_OUTPUT_PAUSE = BIT(5),
+};
+
enum state_change {
STATE_CHANGE_ALLOW,
STATE_CHANGE_DISALLOW,
@@ -40,6 +53,9 @@ int iris_change_core_state(struct iris_core *core,
int iris_inst_change_state(struct iris_inst *inst,
enum iris_inst_state request_state);
+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);
bool allow_s_fmt(struct iris_inst *inst, u32 type);
bool allow_reqbufs(struct iris_inst *inst, u32 type);
@@ -51,4 +67,12 @@ bool allow_s_ctrl(struct iris_inst *inst, u32 cap_id);
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 is_drc_pending(struct iris_inst *inst);
+bool is_drain_pending(struct iris_inst *inst);
+bool allow_cmd(struct iris_inst *inst, u32 cmd);
+
#endif
@@ -1066,16 +1066,35 @@ static int vdec_subscribe_dst_change_param(struct iris_inst *inst)
static int process_streamon_input(struct iris_inst *inst)
{
+ enum iris_inst_sub_state set_sub_state = IRIS_INST_SUB_NONE;
int ret;
ret = iris_hfi_start(inst, INPUT_MPLANE);
if (ret)
return ret;
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_inst_change_sub_state(inst, IRIS_INST_SUB_INPUT_PAUSE, 0);
+ if (ret)
+ return ret;
+ }
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC ||
+ inst->sub_state & IRIS_INST_SUB_DRAIN) {
+ if (!(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE)) {
+ ret = iris_hfi_pause(inst, INPUT_MPLANE);
+ if (ret)
+ return ret;
+ set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ }
+
ret = iris_inst_state_change_streamon(inst, INPUT_MPLANE);
if (ret)
return ret;
+ ret = iris_inst_change_sub_state(inst, 0, set_sub_state);
+
return ret;
}
@@ -1127,16 +1146,51 @@ int vdec_streamon_input(struct iris_inst *inst)
static int process_streamon_output(struct iris_inst *inst)
{
+ enum iris_inst_sub_state clear_sub_state = IRIS_INST_SUB_NONE;
+ bool drain_pending = false;
int ret;
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST)
+ clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_alloc_and_queue_input_int_bufs(inst);
+ if (ret)
+ return ret;
+ ret = set_stage(inst, STAGE);
+ if (ret)
+ return ret;
+ ret = set_pipe(inst, PIPE);
+ if (ret)
+ return ret;
+ }
+
+ drain_pending = inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+
+ if (!drain_pending && inst->state == IRIS_INST_INPUT_STREAMING) {
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ }
+
ret = iris_hfi_start(inst, OUTPUT_MPLANE);
if (ret)
return ret;
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE)
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+
ret = iris_inst_state_change_streamon(inst, OUTPUT_MPLANE);
if (ret)
return ret;
+ ret = iris_inst_change_sub_state(inst, clear_sub_state, 0);
+
return ret;
}
@@ -1242,3 +1296,94 @@ int vdec_qbuf(struct iris_inst *inst, struct vb2_buffer *vb2)
return ret;
}
+
+static int process_resume(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state clear_sub_state = IRIS_INST_SUB_NONE;
+ int ret;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST) {
+ clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE);
+ 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) {
+ clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_DRAIN);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_DRAIN);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+ }
+
+ ret = iris_inst_change_sub_state(inst, clear_sub_state, 0);
+
+ return ret;
+}
+
+int vdec_start_cmd(struct iris_inst *inst)
+{
+ int ret;
+
+ vb2_clear_last_buffer_dequeued(inst->vb2q_dst);
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST &&
+ inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_alloc_and_queue_input_int_bufs(inst);
+ if (ret)
+ return ret;
+
+ ret = set_stage(inst, STAGE);
+ if (ret)
+ return ret;
+
+ ret = set_pipe(inst, PIPE);
+ if (ret)
+ return ret;
+ }
+
+ ret = iris_alloc_and_queue_additional_dpb_buffers(inst);
+ if (ret)
+ return ret;
+
+ ret = queue_deferred_buffers(inst, BUF_OUTPUT);
+ if (ret)
+ return ret;
+
+ ret = process_resume(inst);
+
+ return ret;
+}
+
+int vdec_stop_cmd(struct iris_inst *inst)
+{
+ int ret;
+
+ ret = iris_hfi_drain(inst, INPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN);
+
+ return ret;
+}
@@ -19,5 +19,7 @@ int vdec_src_change(struct iris_inst *inst);
int vdec_streamon_input(struct iris_inst *inst);
int vdec_streamon_output(struct iris_inst *inst);
int vdec_qbuf(struct iris_inst *inst, struct vb2_buffer *vb2);
+int vdec_start_cmd(struct iris_inst *inst);
+int vdec_stop_cmd(struct iris_inst *inst);
#endif
@@ -901,7 +901,82 @@ static int vidc_g_selection(struct file *filp, void *fh, struct v4l2_selection *
return ret;
}
-static const struct v4l2_file_operations v4l2_file_ops = {
+static int vidc_try_dec_cmd(struct file *filp, void *fh,
+ struct v4l2_decoder_cmd *dec)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !dec)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (dec->cmd != V4L2_DEC_CMD_STOP && dec->cmd != V4L2_DEC_CMD_START) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ dec->flags = 0;
+ if (dec->cmd == V4L2_DEC_CMD_STOP) {
+ dec->stop.pts = 0;
+ } else if (dec->cmd == V4L2_DEC_CMD_START) {
+ dec->start.speed = 0;
+ dec->start.format = V4L2_DEC_START_FMT_NONE;
+ }
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int vidc_dec_cmd(struct file *filp, void *fh,
+ struct v4l2_decoder_cmd *dec)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !dec)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (dec->cmd != V4L2_DEC_CMD_START &&
+ dec->cmd != V4L2_DEC_CMD_STOP) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (inst->state == IRIS_INST_OPEN)
+ goto unlock;
+
+ if (!allow_cmd(inst, dec->cmd)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (dec->cmd == V4L2_DEC_CMD_START)
+ ret = vdec_start_cmd(inst);
+ else if (dec->cmd == V4L2_DEC_CMD_STOP)
+ ret = vdec_stop_cmd(inst);
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static struct v4l2_file_operations v4l2_file_ops = {
.owner = THIS_MODULE,
.open = vidc_open,
.release = vidc_close,
@@ -950,6 +1025,8 @@ static const struct v4l2_ioctl_ops v4l2_ioctl_ops = {
.vidioc_subscribe_event = vidc_subscribe_event,
.vidioc_unsubscribe_event = vidc_unsubscribe_event,
.vidioc_g_selection = vidc_g_selection,
+ .vidioc_try_decoder_cmd = vidc_try_dec_cmd,
+ .vidioc_decoder_cmd = vidc_dec_cmd,
};
int init_ops(struct iris_core *core)