| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. | |
| */ | |
| #include <media/v4l2-mem2mem.h> | |
| #include "iris_instance.h" | |
| static bool iris_allow_inst_state_change(struct iris_inst *inst, | |
| 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); | |
| } | |
| static bool iris_inst_allow_sub_state(struct iris_inst *inst, enum iris_inst_sub_state sub_state) | |
| { | |
| if (!sub_state) | |
| return true; | |
| switch (inst->state) { | |
| case IRIS_INST_INIT: | |
| if (sub_state & IRIS_INST_SUB_LOAD_RESOURCES) | |
| return true; | |
| return false; | |
| case IRIS_INST_INPUT_STREAMING: | |
| if (sub_state & (IRIS_INST_SUB_FIRST_IPSC | IRIS_INST_SUB_DRC | | |
| IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_INPUT_PAUSE)) | |
| return true; | |
| return false; | |
| case IRIS_INST_OUTPUT_STREAMING: | |
| if (sub_state & (IRIS_INST_SUB_DRC_LAST | | |
| IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE | | |
| IRIS_INST_SUB_LOAD_RESOURCES)) | |
| return true; | |
| return false; | |
| case IRIS_INST_STREAMING: | |
| if (sub_state & (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)) | |
| return true; | |
| return false; | |
| case IRIS_INST_DEINIT: | |
| if (sub_state & (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)) | |
| return true; | |
| return false; | |
| default: | |
| return false; | |
| } | |
| } | |
| 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; | |
| if (inst->state == IRIS_INST_ERROR) | |
| 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; | |
| if (!iris_inst_allow_sub_state(inst, set_sub_state)) | |
| return -EINVAL; | |
| inst->sub_state |= set_sub_state; | |
| inst->sub_state &= ~clear_sub_state; | |
| if (inst->sub_state != prev_sub_state) | |
| dev_dbg(inst->core->dev, "sub_state changed from %x to %x\n", | |
| prev_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 -EINVAL; | |
| if (inst->state == IRIS_INST_INPUT_STREAMING || | |
| inst->state == IRIS_INST_INIT) | |
| set_sub_state = IRIS_INST_SUB_FIRST_IPSC | 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; | |
| if (inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) | |
| return -EINVAL; | |
| if (!(inst->sub_state & IRIS_INST_SUB_DRAIN)) | |
| return -EINVAL; | |
| set_sub_state = IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE; | |
| return iris_inst_change_sub_state(inst, 0, set_sub_state); | |
| } | |
| int iris_inst_sub_state_change_drc_last(struct iris_inst *inst) | |
| { | |
| enum iris_inst_sub_state set_sub_state; | |
| if (inst->sub_state & IRIS_INST_SUB_DRC_LAST) | |
| return -EINVAL; | |
| if (!(inst->sub_state & IRIS_INST_SUB_DRC) || | |
| !(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE)) | |
| return -EINVAL; | |
| if (inst->sub_state & IRIS_INST_SUB_FIRST_IPSC) | |
| return 0; | |
| 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; | |
| if (V4L2_TYPE_IS_OUTPUT(plane)) { | |
| if (inst->sub_state & IRIS_INST_SUB_DRC && | |
| !(inst->sub_state & IRIS_INST_SUB_DRC_LAST)) | |
| return -EINVAL; | |
| if (inst->sub_state & IRIS_INST_SUB_DRAIN && | |
| !(inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)) | |
| return -EINVAL; | |
| set_sub_state = IRIS_INST_SUB_INPUT_PAUSE; | |
| } else { | |
| set_sub_state = IRIS_INST_SUB_OUTPUT_PAUSE; | |
| } | |
| return iris_inst_change_sub_state(inst, 0, set_sub_state); | |
| } | |
| bool iris_drc_pending(struct iris_inst *inst) | |
| { | |
| return inst->sub_state & IRIS_INST_SUB_DRC && | |
| inst->sub_state & IRIS_INST_SUB_DRC_LAST; | |
| } | |
| bool iris_drain_pending(struct iris_inst *inst) | |
| { | |
| return inst->sub_state & IRIS_INST_SUB_DRAIN && | |
| inst->sub_state & IRIS_INST_SUB_DRAIN_LAST; | |
| } | |
| bool iris_allow_cmd(struct iris_inst *inst, u32 cmd) | |
| { | |
| struct vb2_queue *src_q = v4l2_m2m_get_src_vq(inst->m2m_ctx); | |
| struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); | |
| if (cmd == V4L2_DEC_CMD_START || cmd == V4L2_ENC_CMD_START) { | |
| if (vb2_is_streaming(src_q) || vb2_is_streaming(dst_q)) | |
| if (iris_drc_pending(inst) || iris_drain_pending(inst)) | |
| return true; | |
| } else if (cmd == V4L2_DEC_CMD_STOP || cmd == V4L2_ENC_CMD_STOP) { | |
| if (vb2_is_streaming(src_q)) | |
| if (inst->sub_state != IRIS_INST_SUB_DRAIN) | |
| return true; | |
| } | |
| return false; | |
| } |