diff --git a/src/idc/idc.c b/src/idc/idc.c index 70087f097f46..9a630ca6fbf7 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -320,7 +320,7 @@ static int idc_reset(uint32_t comp_id) * \param[in] ppl_id Pipeline id to be triggered. * \return Error code. */ -static int idc_ppl_state(uint32_t ppl_id) +static int idc_ppl_state(uint32_t ppl_id, uint32_t phase) { #if CONFIG_IPC_MAJOR_4 struct ipc *ipc = ipc_get(); @@ -328,7 +328,6 @@ static int idc_ppl_state(uint32_t ppl_id) struct idc_payload *payload = idc_payload_get(idc, cpu_get_id()); struct ipc_comp_dev *ppl_icd; uint32_t cmd = *(uint32_t *)payload; - bool delayed = false; ppl_icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, ppl_id); if (!ppl_icd) { @@ -336,10 +335,26 @@ static int idc_ppl_state(uint32_t ppl_id) return IPC4_INVALID_RESOURCE_ID; } - return set_pipeline_state(ppl_icd, cmd, &delayed); -#else - return 0; + /* if no phase specified, correct it to be a ONESHOT */ + if (!phase) + phase = IDC_PPL_STATE_PHASE_ONESHOT; + + if (phase & IDC_PPL_STATE_PHASE_PREPARE) { + int ret; + + ret = ipc4_pipeline_prepare(ppl_icd, cmd); + if (ret) + return ret; + } + + if (phase & IDC_PPL_STATE_PHASE_TRIGGER) { + bool delayed = false; + + return ipc4_pipeline_trigger(ppl_icd, cmd, &delayed); + } + #endif + return 0; } static void idc_prepare_d0ix(void) @@ -428,7 +443,8 @@ void idc_cmd(struct idc_msg *msg) ret = idc_reset(msg->extension); break; case iTS(IDC_MSG_PPL_STATE): - ret = idc_ppl_state(msg->extension); + ret = idc_ppl_state(msg->extension & IDC_PPL_STATE_PPL_ID_MASK, + IDC_PPL_STATE_PHASE_GET(msg->extension)); break; case iTS(IDC_MSG_PREPARE_D0ix): idc_prepare_d0ix(); diff --git a/src/include/sof/ipc/topology.h b/src/include/sof/ipc/topology.h index 3d2d88e3481f..810df643fc13 100644 --- a/src/include/sof/ipc/topology.h +++ b/src/include/sof/ipc/topology.h @@ -58,7 +58,9 @@ int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma, bool *d int ipc4_process_on_core(uint32_t core, bool blocking); int ipc4_pipeline_complete(struct ipc *ipc, uint32_t comp_id); int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint32_t size); -int set_pipeline_state(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *delayed); +int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd); +int ipc4_pipeline_trigger(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *delayed); + #else #error "No or invalid IPC MAJOR version selected." #endif diff --git a/src/ipc/ipc4/handler.c b/src/ipc/ipc4/handler.c index 51ee1f693742..dfd4969e1f0d 100644 --- a/src/ipc/ipc4/handler.c +++ b/src/ipc/ipc4/handler.c @@ -187,6 +187,24 @@ static bool is_any_ppl_active(void) return false; } +static struct ipc_comp_dev *pipeline_get_host_dev(struct ipc_comp_dev *ppl_icd) +{ + struct ipc_comp_dev *host_dev; + struct ipc *ipc = ipc_get(); + int host_id; + + if (ppl_icd->pipeline->source_comp->direction == SOF_IPC_STREAM_PLAYBACK) + host_id = ppl_icd->pipeline->source_comp->ipc_config.id; + else + host_id = ppl_icd->pipeline->sink_comp->ipc_config.id; + + host_dev = ipc_get_comp_by_id(ipc, host_id); + if (!host_dev) + ipc_cmd_err(&ipc_tr, "comp host with ID %d not found", host_id); + + return host_dev; +} + /* Ipc4 pipeline message <------> ipc3 pipeline message * RUNNING <-------> TRIGGER START * INIT + PAUSED <-------> PIPELINE COMPLETE @@ -215,120 +233,79 @@ static bool is_any_ppl_active(void) * ERROR Stop EOS |______\ SAVE * / */ -int set_pipeline_state(struct ipc_comp_dev *ppl_icd, uint32_t cmd, - bool *delayed) + +int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd) { struct ipc_comp_dev *host = NULL; struct ipc *ipc = ipc_get(); int status; - int ret; + int ret = 0; status = ppl_icd->pipeline->status; - tr_dbg(&ipc_tr, "ipc4 set pipeline %d cmd state %x: from state %x", ppl_icd->id, - cmd, status); - - /* source & sink components are set when pipeline is set to COMP_STATE_INIT */ - if (status != COMP_STATE_INIT) { - int host_id; - - if (ppl_icd->pipeline->source_comp->direction == SOF_IPC_STREAM_PLAYBACK) - host_id = ppl_icd->pipeline->source_comp->ipc_config.id; - else - host_id = ppl_icd->pipeline->sink_comp->ipc_config.id; - - host = ipc_get_comp_by_id(ipc, host_id); - if (!host) { - ipc_cmd_err(&ipc_tr, "ipc: comp host with ID %d not found", host_id); - return IPC4_INVALID_RESOURCE_ID; - } - } + tr_dbg(&ipc_tr, "pipeline %d: initial state: %d, cmd: %d", ppl_icd->id, + status, cmd); switch (cmd) { case SOF_IPC4_PIPELINE_STATE_RUNNING: /* init params when pipeline is complete or reset */ switch (status) { case COMP_STATE_ACTIVE: - /* nothing to do if the pipeline is already running */ - return 0; + case COMP_STATE_PAUSED: + /* No action needed */ + break; case COMP_STATE_READY: - cmd = COMP_TRIGGER_PRE_START; + host = pipeline_get_host_dev(ppl_icd); + if (!host) + return IPC4_INVALID_RESOURCE_ID; - tr_dbg(&ipc_tr, "ipc4: set pipeline %d new cmd state %x: params", - ppl_icd->id, cmd); + tr_dbg(&ipc_tr, "pipeline %d: set params", ppl_icd->id); ret = ipc4_pcm_params(host); if (ret < 0) return IPC4_INVALID_REQUEST; break; - case COMP_STATE_PAUSED: - cmd = COMP_TRIGGER_PRE_RELEASE; - break; default: - ipc_cmd_err(&ipc_tr, "ipc: current status %d", status); + ipc_cmd_err(&ipc_tr, + "pipeline %d: Invalid state for RUNNING: %d", + ppl_icd->id, status); return IPC4_INVALID_REQUEST; } break; case SOF_IPC4_PIPELINE_STATE_RESET: switch (status) { case COMP_STATE_INIT: - tr_dbg(&ipc_tr, "ipc4: set pipeline %d new cmd state %x: reset from init", - ppl_icd->id, cmd); + tr_dbg(&ipc_tr, "pipeline %d: reset from init", ppl_icd->id); ret = ipc4_pipeline_complete(ipc, ppl_icd->id); if (ret < 0) ret = IPC4_INVALID_REQUEST; - return ret; + break; case COMP_STATE_READY: - /* initialized -> pause -> reset */ - return 0; case COMP_STATE_ACTIVE: case COMP_STATE_PAUSED: - tr_dbg(&ipc_tr, "ipc4: set pipeline %d new cmd state %x: pause from reset", - ppl_icd->id, cmd); - ret = pipeline_trigger(host->cd->pipeline, host->cd, COMP_TRIGGER_STOP); - if (ret < 0) { - ipc_cmd_err(&ipc_tr, "ipc: comp %d trigger 0x%x failed %d", - ppl_icd->id, cmd, ret); - return IPC4_PIPELINE_STATE_NOT_SET; - } - if (ret == PPL_STATUS_SCHEDULED) - *delayed = true; + /* No action needed */ break; default: - ipc_cmd_err(&ipc_tr, "ipc: invalid status %d for RESET", status); + ipc_cmd_err(&ipc_tr, + "pipeline %d: Invalid state for RESET: %d", + ppl_icd->id, status); return IPC4_INVALID_REQUEST; } - /* - * reset the pipeline components if STOP trigger is executed in the same thread. - * Otherwise, the pipeline will be reset after the STOP trigger has finished - * executing in the pipeline task. - */ - if (!*delayed) { - tr_dbg(&ipc_tr, "ipc4: set pipeline %d new cmd state %x: delayed reset", - ppl_icd->id, cmd); - ret = pipeline_reset(host->cd->pipeline, host->cd); - if (ret < 0) - ret = IPC4_INVALID_REQUEST; - } - - return ret; + break; case SOF_IPC4_PIPELINE_STATE_PAUSED: switch (status) { case COMP_STATE_INIT: - tr_dbg(&ipc_tr, "ipc4: set pipeline %d new cmd state %x: pause from init", - ppl_icd->id, cmd); + tr_dbg(&ipc_tr, "pipeline %d: pause from init", ppl_icd->id); ret = ipc4_pipeline_complete(ipc, ppl_icd->id); if (ret < 0) ret = IPC4_INVALID_REQUEST; - return ret; - case COMP_STATE_READY: - case COMP_STATE_PAUSED: - /* return if pipeline is not active yet or if it is already paused */ - return 0; + break; + default: + /* No action needed */ + break; } - cmd = COMP_TRIGGER_PAUSE; break; /* special case- TODO */ case SOF_IPC4_PIPELINE_STATE_EOS: @@ -337,23 +314,101 @@ int set_pipeline_state(struct ipc_comp_dev *ppl_icd, uint32_t cmd, case SOF_IPC4_PIPELINE_STATE_SAVED: case SOF_IPC4_PIPELINE_STATE_ERROR_STOP: default: - ipc_cmd_err(&ipc_tr, "ipc: unsupported trigger cmd 0x%x", cmd); + ipc_cmd_err(&ipc_tr, "pipeline %d: unsupported trigger cmd: %d", + ppl_icd->id, cmd); return IPC4_INVALID_REQUEST; } - tr_dbg(&ipc_tr, "ipc4: set pipeline %d new cmd state %x: from state %x", ppl_icd->id, - cmd, status); + return ret; +} + +int ipc4_pipeline_trigger(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *delayed) +{ + struct ipc_comp_dev *host; + int status; + int ret; + + status = ppl_icd->pipeline->status; + tr_dbg(&ipc_tr, "pipeline %d: initial state: %d, cmd: %d", ppl_icd->id, + status, cmd); + + if (status == COMP_STATE_INIT) + return 0; + + host = pipeline_get_host_dev(ppl_icd); + if (!host) + return IPC4_INVALID_RESOURCE_ID; + + switch (cmd) { + case SOF_IPC4_PIPELINE_STATE_RUNNING: + /* init params when pipeline is complete or reset */ + switch (status) { + case COMP_STATE_ACTIVE: + /* nothing to do if the pipeline is already running */ + return 0; + case COMP_STATE_READY: + case COMP_STATE_PREPARE: + cmd = COMP_TRIGGER_PRE_START; + break; + case COMP_STATE_PAUSED: + cmd = COMP_TRIGGER_PRE_RELEASE; + break; + default: + ipc_cmd_err(&ipc_tr, + "pipeline %d: Invalid state for RUNNING: %d", + ppl_icd->id, status); + return IPC4_INVALID_REQUEST; + } + break; + case SOF_IPC4_PIPELINE_STATE_RESET: + switch (status) { + case COMP_STATE_ACTIVE: + case COMP_STATE_PAUSED: + cmd = COMP_TRIGGER_STOP; + break; + default: + return 0; + } + break; + case SOF_IPC4_PIPELINE_STATE_PAUSED: + switch (status) { + case COMP_STATE_INIT: + case COMP_STATE_READY: + case COMP_STATE_PAUSED: + return 0; + default: + cmd = COMP_TRIGGER_PAUSE; + break; + } + + break; + default: + ipc_cmd_err(&ipc_tr, "pipeline %d: unsupported trigger cmd: %d", + ppl_icd->id, cmd); + return IPC4_INVALID_REQUEST; + } /* trigger the component */ ret = pipeline_trigger(host->cd->pipeline, host->cd, cmd); if (ret < 0) { - ipc_cmd_err(&ipc_tr, "ipc: comp %d trigger 0x%x failed %d", ppl_icd->id, cmd, ret); + ipc_cmd_err(&ipc_tr, "pipeline %d: trigger cmd %d failed with: %d", + ppl_icd->id, cmd, ret); ret = IPC4_PIPELINE_STATE_NOT_SET; } else if (ret == PPL_STATUS_SCHEDULED) { - tr_dbg(&ipc_tr, "ipc4: set pipeline %d new cmd state %x: trigger delayed", + tr_dbg(&ipc_tr, "pipeline %d: trigger cmd %d is delayed", ppl_icd->id, cmd); *delayed = true; ret = 0; + } else if (cmd == COMP_TRIGGER_STOP) { + /* + * reset the pipeline components if STOP trigger is executed in + * the same thread. + * Otherwise, the pipeline will be reset after the STOP trigger + * has finished executing in the pipeline task. + */ + ret = pipeline_reset(host->cd->pipeline, host->cd); + if (ret < 0) + ret = IPC4_INVALID_REQUEST; } return ret; @@ -460,6 +515,38 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) } } + /* Run the prepare phase on the pipelines */ + for (i = 0; i < ppl_count; i++) { + ppl_icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, ppl_id[i]); + if (!ppl_icd) { + ipc_cmd_err(&ipc_tr, "ipc: comp %d not found", ppl_id[i]); + return IPC4_INVALID_RESOURCE_ID; + } + + /* Pass IPC to target core + * or use idc if more than one core used + */ + if (!cpu_is_me(ppl_icd->core)) { + if (use_idc) { + struct idc_msg msg = { IDC_MSG_PPL_STATE, + IDC_MSG_PPL_STATE_EXT(ppl_id[i], + IDC_PPL_STATE_PHASE_PREPARE), + ppl_icd->core, + sizeof(cmd), &cmd, }; + + ret = idc_send_msg(&msg, IDC_BLOCKING); + } else { + return ipc4_process_on_core(ppl_icd->core, false); + } + } else { + ret = ipc4_pipeline_prepare(ppl_icd, cmd); + } + + if (ret != 0) + return ret; + } + + /* Run the trigger phase on the pipelines */ for (i = 0; i < ppl_count; i++) { bool delayed = false; @@ -475,7 +562,9 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) if (!cpu_is_me(ppl_icd->core)) { if (use_idc) { struct idc_msg msg = { IDC_MSG_PPL_STATE, - IDC_MSG_PPL_STATE_EXT(ppl_id[i]), ppl_icd->core, + IDC_MSG_PPL_STATE_EXT(ppl_id[i], + IDC_PPL_STATE_PHASE_TRIGGER), + ppl_icd->core, sizeof(cmd), &cmd, }; ret = idc_send_msg(&msg, IDC_BLOCKING); @@ -484,7 +573,7 @@ static int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) } } else { ipc_compound_pre_start(state.primary.r.type); - ret = set_pipeline_state(ppl_icd, cmd, &delayed); + ret = ipc4_pipeline_trigger(ppl_icd, cmd, &delayed); ipc_compound_post_start(state.primary.r.type, ret, delayed); } diff --git a/zephyr/include/rtos/idc.h b/zephyr/include/rtos/idc.h index 7a061733a114..34418a320a22 100644 --- a/zephyr/include/rtos/idc.h +++ b/zephyr/include/rtos/idc.h @@ -108,7 +108,22 @@ /** \brief IDC pipeline set state message. */ #define IDC_MSG_PPL_STATE IDC_TYPE(0xC) -#define IDC_MSG_PPL_STATE_EXT(x) IDC_EXTENSION(x) +#define IDC_PPL_STATE_PPL_ID_SHIFT 0 +#define IDC_PPL_STATE_PPL_ID_MASK MASK(23, 0) +#define IDC_PPL_STATE_PHASE_SHIFT 24 +#define IDC_PPL_STATE_PHASE_MASK MASK(27, 24) +#define IDC_PPL_STATE_PHASE_SET(x) (((x) << IDC_PPL_STATE_PHASE_SHIFT) & \ + IDC_PPL_STATE_PHASE_MASK) +#define IDC_PPL_STATE_PHASE_GET(x) (((x) & IDC_PPL_STATE_PHASE_MASK) >> \ + IDC_PPL_STATE_PHASE_SHIFT) +#define IDC_PPL_STATE_PHASE_PREPARE BIT(0) +#define IDC_PPL_STATE_PHASE_TRIGGER BIT(1) +#define IDC_PPL_STATE_PHASE_ONESHOT (IDC_PPL_STATE_PHASE_PREPARE | \ + IDC_PPL_STATE_PHASE_TRIGGER) + +#define IDC_MSG_PPL_STATE_EXT(_ppl_id, _action) \ + IDC_EXTENSION(((_ppl_id) & IDC_PPL_STATE_PPL_ID_MASK) | \ + IDC_PPL_STATE_PHASE_SET(_action)) /** \brief IDC_MSG_SECONDARY_CORE_CRASHED header fields. */ #define IDC_SCC_CORE_SHIFT 0