diff --git a/src/arch/xtensa/CMakeLists.txt b/src/arch/xtensa/CMakeLists.txt index 4fed1c262d3a..b06b3ba42d73 100644 --- a/src/arch/xtensa/CMakeLists.txt +++ b/src/arch/xtensa/CMakeLists.txt @@ -212,6 +212,7 @@ add_local_sources(sof xtos/crt1-boards.S xtos/_vectors.S init.c + ipc.c exc-dump.S ) diff --git a/src/arch/xtensa/ipc.c b/src/arch/xtensa/ipc.c new file mode 100644 index 000000000000..0b0f6e669712 --- /dev/null +++ b/src/arch/xtensa/ipc.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Author: Guennadi Liakhovetski + +/** + * @file + * @brief Xtensa IPC per-core data access + */ + +#include +#include +#include +#include + +struct ipc_core_ctx **arch_ipc_get(void) +{ + struct core_context *ctx = (struct core_context *)cpu_read_threadptr(); + + return &ctx->ipc; +} diff --git a/src/arch/xtensa/xtos/xtos-structs.h b/src/arch/xtensa/xtos/xtos-structs.h index aed01254e4e8..02f9f4a4180b 100644 --- a/src/arch/xtensa/xtos/xtos-structs.h +++ b/src/arch/xtensa/xtos/xtos-structs.h @@ -49,12 +49,15 @@ struct xtos_core_data { struct thread_data *thread_data_ptr; }; +struct ipc_core_ctx; + struct core_context { struct thread_data td; struct task *main_task; struct schedulers *schedulers; struct notify *notify; struct idc *idc; + struct ipc_core_ctx *ipc; }; #endif /* __XTOS_XTOS_STRUCTS_H__ */ diff --git a/src/include/sof/ipc/common.h b/src/include/sof/ipc/common.h index 8be355f426c3..39fa6a36b959 100644 --- a/src/include/sof/ipc/common.h +++ b/src/include/sof/ipc/common.h @@ -90,6 +90,13 @@ struct ipc { #define ipc_get_drvdata(ipc) \ ((ipc)->private) +struct ipc_core_ctx { + int error; +}; + +struct ipc_core_ctx **arch_ipc_get(void); +int ipc_init_per_core(void); + extern struct task_ops ipc_task_ops; /** diff --git a/src/include/sof/ipc/topology.h b/src/include/sof/ipc/topology.h index f13de0f9742a..49ec76eae372 100644 --- a/src/include/sof/ipc/topology.h +++ b/src/include/sof/ipc/topology.h @@ -48,7 +48,6 @@ typedef uint32_t ipc_comp; const struct comp_driver *ipc4_get_comp_drv(int module_id); struct comp_dev *ipc4_get_comp_dev(uint32_t comp_id); -int ipc4_add_comp_dev(struct comp_dev *dev); const struct comp_driver *ipc4_get_drv(uint8_t *uuid); int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma); int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma); diff --git a/src/init/init.c b/src/init/init.c index 19bc43cbf070..40ef357f1d00 100644 --- a/src/init/init.c +++ b/src/init/init.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -172,6 +173,10 @@ int secondary_core_init(struct sof *sof) if (err < 0) return err; + err = ipc_init_per_core(); + if (err < 0) + return err; + trace_point(TRACE_BOOT_PLATFORM); #ifndef __ZEPHYR__ @@ -186,6 +191,8 @@ int secondary_core_init(struct sof *sof) static int primary_core_init(int argc, char *argv[], struct sof *sof) { + int err; + /* setup context */ sof->argc = argc; sof->argv = argv; @@ -219,6 +226,10 @@ static int primary_core_init(int argc, char *argv[], struct sof *sof) if (platform_init(sof) < 0) panic(SOF_IPC_PANIC_PLATFORM); + err = ipc_init_per_core(); + if (err < 0) + return err; + trace_point(TRACE_BOOT_PLATFORM); #if CONFIG_NO_SECONDARY_CORE_ROM diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index 247c78ff8718..f7baf33e2280 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -297,3 +297,13 @@ struct task_ops ipc_task_ops = { .complete = ipc_complete_task, .get_deadline = ipc_task_deadline, }; + +int ipc_init_per_core(void) +{ + struct ipc_core_ctx **ctx = arch_ipc_get(); + + *ctx = rzalloc(SOF_MEM_ZONE_SYS, 0, SOF_MEM_CAPS_RAM, + sizeof(**ctx)); + + return *ctx ? 0 : -ENOMEM; +} diff --git a/src/ipc/ipc4/handler.c b/src/ipc/ipc4/handler.c index 56a8aa4346e8..67f0e615c23f 100644 --- a/src/ipc/ipc4/handler.c +++ b/src/ipc/ipc4/handler.c @@ -56,6 +56,7 @@ static int ipc4_create_pipeline(union ipc4_message_header *ipc4) { struct ipc *ipc = ipc_get(); + /* ipc_pipeline_new() sets IPC4 error */ return ipc_pipeline_new(ipc, (ipc_pipe_new *)ipc4); } @@ -67,6 +68,7 @@ static int ipc4_delete_pipeline(union ipc4_message_header *ipc4) pipe = (struct ipc4_pipeline_delete *)ipc4; tr_dbg(&ipc_tr, "ipc4 delete pipeline %x:", (uint32_t)pipe->header.r.instance_id); + /* ipc_pipeline_free() sets IPC4 error */ return ipc_pipeline_free(ipc, pipe->header.r.instance_id); } @@ -180,11 +182,13 @@ static int ipc4_pcm_params(struct ipc_comp_dev *pcm_dev) * |/_ _\| | / \ _\| * ERROR Stop EOS |______\ SAVE * / + * Returns: 0, a negative error code or 1, if processed on a different core */ static int set_pipeline_state(uint32_t id, uint32_t cmd, bool *delayed) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc_comp_dev *pcm_dev; - struct ipc_comp_dev *host; + struct ipc_comp_dev *host = NULL; struct ipc *ipc = ipc_get(); int status; int ret; @@ -195,7 +199,8 @@ static int set_pipeline_state(uint32_t id, uint32_t cmd, bool *delayed) pcm_dev = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, id); if (!pcm_dev) { tr_err(&ipc_tr, "ipc: comp %d not found", id); - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -ENODEV; } status = pcm_dev->pipeline->status; @@ -211,19 +216,26 @@ static int set_pipeline_state(uint32_t id, uint32_t cmd, bool *delayed) host = ipc_get_comp_by_id(ipc, host_id); if (!host) { tr_err(&ipc_tr, "ipc: comp host with ID %d not found", host_id); - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -ENODEV; } /* check core */ - if (!cpu_is_me(host->core)) - return ipc_process_on_core(host->core, false); + if (!cpu_is_me(host->core)) { + /* FIXME: why is COMP_STATE_INIT always processed on primary core? */ + ret = ipc_process_on_core(host->core, false); + if (ret < 0) + ctx->error = IPC4_INVALID_CORE_ID; + return ret; + } } switch (cmd) { case SOF_IPC4_PIPELINE_STATE_RUNNING: if (status != COMP_STATE_PAUSED && status != COMP_STATE_READY) { tr_err(&ipc_tr, "ipc: current status %d", status); - return IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; + return -EINVAL; } /* init params when pipeline is complete or reset */ @@ -231,8 +243,10 @@ static int set_pipeline_state(uint32_t id, uint32_t cmd, bool *delayed) cmd = COMP_TRIGGER_PRE_START; ret = ipc4_pcm_params(host); - if (ret < 0) - return IPC4_INVALID_REQUEST; + if (ret < 0) { + ctx->error = IPC4_INVALID_REQUEST; + return ret; + } } else { cmd = COMP_TRIGGER_PRE_RELEASE; } @@ -242,7 +256,7 @@ static int set_pipeline_state(uint32_t id, uint32_t cmd, bool *delayed) if (status == COMP_STATE_INIT) { ret = ipc_pipeline_complete(ipc, id); if (ret < 0) - ret = IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; return ret; } @@ -256,23 +270,25 @@ static int set_pipeline_state(uint32_t id, uint32_t cmd, bool *delayed) if (ret < 0) { tr_err(&ipc_tr, "ipc: comp %d trigger 0x%x failed %d", id, cmd, ret); - return IPC4_PIPELINE_STATE_NOT_SET; - } else if (ret == PPL_STATUS_SCHEDULED) { - *delayed = true; + ctx->error = IPC4_PIPELINE_STATE_NOT_SET; + return ret; } + + if (ret == PPL_STATUS_SCHEDULED) + *delayed = true; } /* resource is not released by triggering reset which is used by current FW */ ret = pipeline_reset(host->cd->pipeline, host->cd); if (ret < 0) - ret = IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; return ret; case SOF_IPC4_PIPELINE_STATE_PAUSED: if (status == COMP_STATE_INIT) { ret = ipc_pipeline_complete(ipc, id); if (ret < 0) - ret = IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; return ret; } @@ -284,23 +300,31 @@ static int set_pipeline_state(uint32_t id, uint32_t cmd, bool *delayed) break; /* special case- TODO */ case SOF_IPC4_PIPELINE_STATE_EOS: - if (status != COMP_STATE_ACTIVE) - return IPC4_INVALID_REQUEST; + if (status != COMP_STATE_ACTIVE) { + ctx->error = IPC4_INVALID_REQUEST; + return -EINVAL; + } + COMPILER_FALLTHROUGH; case SOF_IPC4_PIPELINE_STATE_SAVED: case SOF_IPC4_PIPELINE_STATE_ERROR_STOP: default: tr_err(&ipc_tr, "ipc: unsupported trigger cmd 0x%x", cmd); - return IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; + return -EOPNOTSUPP; } + /* + * We never get here with status == COMP_STATE_INIT, so host is always + * initialized, but the compiler is still not smart enough + */ + /* trigger the component */ ret = pipeline_trigger(host->cd->pipeline, host->cd, cmd); if (ret < 0) { tr_err(&ipc_tr, "ipc: comp %d trigger 0x%x failed %d", id, cmd, ret); - ret = IPC4_PIPELINE_STATE_NOT_SET; + ctx->error = IPC4_PIPELINE_STATE_NOT_SET; } else if (ret == PPL_STATUS_SCHEDULED) { *delayed = true; - ret = 0; } return ret; @@ -316,7 +340,7 @@ static void ipc_compound_pre_start(int msg_id) static void ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed) { - if (ret) { + if (ret < 0) { tr_err(&ipc_tr, "failed to process msg %d status %d", msg_id, ret); msg_data.delayed_reply--; return; @@ -346,20 +370,18 @@ static void ipc_compound_msg_done(uint32_t msg_id, int error) static int ipc_wait_for_compound_msg(void) { int try_count = 10; - int ret = 0; while (msg_data.delayed_reply) { - /* TODO: update to yield(tmeout) for Zephyr */ + /* TODO: update to yield(timeout) for Zephyr */ wait_delay(10000); if (!try_count--) { - ret = IPC4_FAILURE; tr_err(&ipc_tr, "failed to wait schedule thread"); - break; + return -ETIMEDOUT; } } - return ret; + return 0; } static int ipc4_set_pipeline_state(union ipc4_message_header *ipc4) @@ -368,8 +390,7 @@ static int ipc4_set_pipeline_state(union ipc4_message_header *ipc4) struct ipc4_pipeline_set_state state; uint32_t cmd, ppl_count; uint32_t *ppl_id, id; - int ret = 0; - int i; + int ret, ret_all = 0, i; state.header.dat = ipc4[0].dat; state.data.dat = ipc4[1].dat; @@ -390,15 +411,23 @@ static int ipc4_set_pipeline_state(union ipc4_message_header *ipc4) for (i = 0; i < ppl_count; i++) { bool delayed = false; + /* + * FIXME: With IPCs, directed to multiple pipelines, if they are + * assigned to different cores, it becomes unclear, which core + * should send the response. + */ ipc_compound_pre_start(state.header.r.type); ret = set_pipeline_state(ppl_id[i], cmd, &delayed); ipc_compound_post_start(state.header.r.type, ret, delayed); if (ret < 0) - break; + return ret; + + if (ret) + ret_all = ret; } - return ret; + return ret_all; } static int ipc4_process_chain_dma(union ipc4_message_header *ipc4) @@ -410,21 +439,23 @@ static int ipc4_process_chain_dma(union ipc4_message_header *ipc4) memcpy_s(&cdma, sizeof(cdma), ipc4, sizeof(cdma)); if (cdma.header.r.allocate && cdma.data.r.fifo_size) { + /* ipc4_create_chain_dma() sets IPC4 error */ ret = ipc4_create_chain_dma(ipc, &cdma); - if (ret) + if (ret < 0) tr_err(&ipc_tr, "failed to create chain dma %d", ret); return ret; } msg_data.delayed_reply = 1; + /* ipc4_trigger_chain_dma() sets IPC4 error */ ret = ipc4_trigger_chain_dma(ipc, &cdma); /* it is not scheduled in another thread */ - if (ret != PPL_STATUS_SCHEDULED) { + if (ret == PPL_STATUS_SCHEDULED) { + ret = 0; + } else { msg_data.delayed_reply = 0; msg_data.delayed_error = 0; - } else { - ret = 0; } return ret; @@ -432,65 +463,42 @@ static int ipc4_process_chain_dma(union ipc4_message_header *ipc4) static int ipc4_process_glb_message(union ipc4_message_header *ipc4) { - uint32_t type; - int ret; - - type = ipc4->r.type; + struct ipc_core_ctx *ctx = *arch_ipc_get(); + uint32_t type = ipc4->r.type; switch (type) { + case SOF_IPC4_GLB_CHAIN_DMA: + return ipc4_process_chain_dma(ipc4); + /* pipeline settings */ + case SOF_IPC4_GLB_CREATE_PIPELINE: + return ipc4_create_pipeline(ipc4); + case SOF_IPC4_GLB_DELETE_PIPELINE: + return ipc4_delete_pipeline(ipc4); + case SOF_IPC4_GLB_SET_PIPELINE_STATE: + return ipc4_set_pipeline_state(ipc4); case SOF_IPC4_GLB_BOOT_CONFIG: case SOF_IPC4_GLB_ROM_CONTROL: case SOF_IPC4_GLB_IPCGATEWAY_CMD: case SOF_IPC4_GLB_PERF_MEASUREMENTS_CMD: case SOF_IPC4_GLB_LOAD_MULTIPLE_MODULES: case SOF_IPC4_GLB_UNLOAD_MULTIPLE_MODULES: - tr_err(&ipc_tr, "not implemented ipc message type %d", type); - ret = IPC4_UNAVAILABLE; - break; - - case SOF_IPC4_GLB_CHAIN_DMA: - ret = ipc4_process_chain_dma(ipc4); - break; - - /* pipeline settings */ - case SOF_IPC4_GLB_CREATE_PIPELINE: - ret = ipc4_create_pipeline(ipc4); - break; - case SOF_IPC4_GLB_DELETE_PIPELINE: - ret = ipc4_delete_pipeline(ipc4); - break; - case SOF_IPC4_GLB_SET_PIPELINE_STATE: - ret = ipc4_set_pipeline_state(ipc4); - break; - case SOF_IPC4_GLB_GET_PIPELINE_STATE: case SOF_IPC4_GLB_GET_PIPELINE_CONTEXT_SIZE: case SOF_IPC4_GLB_SAVE_PIPELINE: case SOF_IPC4_GLB_RESTORE_PIPELINE: - tr_err(&ipc_tr, "not implemented ipc message type %d", type); - ret = IPC4_UNAVAILABLE; - break; - /* Loads library (using Code Load or HD/A Host Output DMA) */ case SOF_IPC4_GLB_LOAD_LIBRARY: case SOF_IPC4_GLB_INTERNAL_MESSAGE: - tr_err(&ipc_tr, "not implemented ipc message type %d", type); - ret = IPC4_UNAVAILABLE; - break; - /* Notification (FW to SW driver) */ case SOF_IPC4_GLB_NOTIFICATION: tr_err(&ipc_tr, "not implemented ipc message type %d", type); - ret = IPC4_UNAVAILABLE; break; - default: tr_err(&ipc_tr, "unsupported ipc message type %d", type); - ret = IPC4_UNAVAILABLE; - break; } - return ret; + ctx->error = IPC4_UNAVAILABLE; + return -EOPNOTSUPP; } /* @@ -503,6 +511,7 @@ static int ipc4_process_glb_message(union ipc4_message_header *ipc4) static int ipc4_init_module_instance(union ipc4_message_header *ipc4) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_init_instance module; struct sof_ipc_comp comp; struct comp_dev *dev; @@ -520,7 +529,8 @@ static int ipc4_init_module_instance(union ipc4_message_header *ipc4) tr_err(&ipc_tr, "error: failed to init module %x : %x", (uint32_t)module.header.r.module_id, (uint32_t)module.header.r.instance_id); - return IPC4_MOD_NOT_INITIALIZED; + ctx->error = IPC4_UNAVAILABLE; + return -EINVAL; } return 0; @@ -536,6 +546,7 @@ static int ipc4_bind_module_instance(union ipc4_message_header *ipc4) (uint32_t)bu.header.r.module_id, (uint32_t)bu.header.r.instance_id, (uint32_t)bu.data.r.dst_module_id, (uint32_t)bu.data.r.dst_instance_id); + /* ipc_comp_connect() sets IPC4 error */ return ipc_comp_connect(ipc, (ipc_pipe_comp_connect *)&bu); } @@ -549,11 +560,13 @@ static int ipc4_unbind_module_instance(union ipc4_message_header *ipc4) (uint32_t)bu.header.r.module_id, (uint32_t)bu.header.r.instance_id, (uint32_t)bu.data.r.dst_module_id, (uint32_t)bu.data.r.dst_instance_id); + /* ipc_comp_disconnect() sets IPC4 error */ return ipc_comp_disconnect(ipc, (ipc_pipe_comp_connect *)&bu); } static int ipc4_get_large_config_module_instance(union ipc4_message_header *ipc4) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_large_config_reply reply; struct ipc4_module_large_config config; char *data = ipc_get()->comp_data; @@ -567,11 +580,15 @@ static int ipc4_get_large_config_module_instance(union ipc4_message_header *ipc4 (uint32_t)config.header.r.module_id, (uint32_t)config.header.r.instance_id); drv = ipc4_get_comp_drv(config.header.r.module_id); - if (!drv) - return IPC4_MOD_INVALID_ID; + if (!drv) { + ctx->error = IPC4_MOD_INVALID_ID; + return -ENODEV; + } - if (!drv->ops.get_large_config) - return IPC4_INVALID_REQUEST; + if (!drv->ops.get_large_config) { + ctx->error = IPC4_INVALID_REQUEST; + return -EINVAL; + } /* get component dev for non-basefw since there is no * component dev for basefw @@ -581,8 +598,10 @@ static int ipc4_get_large_config_module_instance(union ipc4_message_header *ipc4 comp_id = IPC4_COMP_ID(config.header.r.module_id, config.header.r.instance_id); dev = ipc4_get_comp_dev(comp_id); - if (!dev) - return IPC4_MOD_INVALID_ID; + if (!dev) { + ctx->error = IPC4_MOD_INVALID_ID; + return -ENODEV; + } } data_offset = config.data.r.data_off_size; @@ -592,25 +611,24 @@ static int ipc4_get_large_config_module_instance(union ipc4_message_header *ipc4 /* set up ipc4 error code for reply data */ if (ret < 0) - ret = IPC4_MOD_INVALID_ID; + ctx->error = IPC4_MOD_INVALID_ID; /* Copy host config and overwrite */ reply.data.dat = config.data.dat; reply.data.r.data_off_size = data_offset; + *(uint32_t *)data = reply.data.dat; + /* The last block, no more data */ if (!config.data.r.final_block && data_offset < SOF_IPC_MSG_MAX_SIZE) reply.data.r.final_block = 1; - /* Indicate last block if error occurs */ - if (ret) - reply.data.r.final_block = 1; - - *(uint32_t *)data = reply.data.dat; - /* no need to allocate memory for reply msg */ - if (ret) + if (ctx->error != IPC4_SUCCESS) { + /* Indicate last block if error occurs */ + reply.data.r.final_block = 1; return ret; + } msg_reply.tx_size = data_offset + sizeof(reply.data.dat); msg_reply.tx_data = rballoc(0, SOF_MEM_CAPS_RAM, msg_reply.tx_size); @@ -624,6 +642,7 @@ static int ipc4_get_large_config_module_instance(union ipc4_message_header *ipc4 static int ipc4_set_large_config_module_instance(union ipc4_message_header *ipc4) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_large_config config; struct comp_dev *dev = NULL; const struct comp_driver *drv; @@ -634,19 +653,25 @@ static int ipc4_set_large_config_module_instance(union ipc4_message_header *ipc4 (uint32_t)config.header.r.module_id, (uint32_t)config.header.r.instance_id); drv = ipc4_get_comp_drv(config.header.r.module_id); - if (!drv) - return IPC4_MOD_INVALID_ID; + if (!drv) { + ctx->error = IPC4_MOD_INVALID_ID; + return -ENOENT; + } - if (!drv->ops.set_large_config) - return IPC4_INVALID_REQUEST; + if (!drv->ops.set_large_config) { + ctx->error = IPC4_INVALID_REQUEST; + return -EOPNOTSUPP; + } if (config.header.r.module_id) { uint32_t comp_id; comp_id = IPC4_COMP_ID(config.header.r.module_id, config.header.r.instance_id); dev = ipc4_get_comp_dev(comp_id); - if (!dev) - return IPC4_MOD_INVALID_ID; + if (!dev) { + ctx->error = IPC4_MOD_INVALID_ID; + return -ENODEV; + } } ret = drv->ops.set_large_config(dev, config.data.r.large_param_id, config.data.r.init_block, @@ -655,7 +680,7 @@ static int ipc4_set_large_config_module_instance(union ipc4_message_header *ipc4 if (ret < 0) { tr_err(&ipc_tr, "failed to set large_config_module_instance %x : %x", (uint32_t)config.header.r.module_id, (uint32_t)config.header.r.instance_id); - ret = IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; } return ret; @@ -663,6 +688,7 @@ static int ipc4_set_large_config_module_instance(union ipc4_message_header *ipc4 static int ipc4_delete_module_instance(union ipc4_message_header *ipc4) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_delete_instance module; struct ipc *ipc = ipc_get(); uint32_t comp_id; @@ -678,7 +704,7 @@ static int ipc4_delete_module_instance(union ipc4_message_header *ipc4) tr_err(&ipc_tr, "failed to delete module instance %x : %x", (uint32_t)module.header.r.module_id, (uint32_t)module.header.r.instance_id); - ret = IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; } return ret; @@ -687,6 +713,7 @@ static int ipc4_delete_module_instance(union ipc4_message_header *ipc4) /* disable power gating on core 0 */ static int ipc4_module_process_d0ix(union ipc4_message_header *ipc4) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_set_d0ix d0ix; uint32_t module_id, instance_id; @@ -698,7 +725,8 @@ static int ipc4_module_process_d0ix(union ipc4_message_header *ipc4) if (d0ix.header.r.module_id || d0ix.header.r.instance_id) { tr_err(&ipc_tr, "invalid resource id %x : %x", module_id, instance_id); - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -EINVAL; } /* only module 0 can be used to set d0ix state */ @@ -713,6 +741,7 @@ static int ipc4_module_process_d0ix(union ipc4_message_header *ipc4) /* power up core 0 */ static int ipc4_module_process_dx(union ipc4_message_header *ipc4) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_set_dx dx; uint32_t module_id, instance_id; @@ -720,12 +749,13 @@ static int ipc4_module_process_dx(union ipc4_message_header *ipc4) module_id = dx.header.r.module_id; instance_id = dx.header.r.instance_id; - tr_dbg(&ipc_tr, "ipc4_module_process_d0ix %x : %x", module_id, instance_id); + tr_dbg(&ipc_tr, "ipc4_module_process_dx %x : %x", module_id, instance_id); /* only module 0 can be used to set dx state */ if (dx.header.r.module_id || dx.header.r.instance_id) { tr_err(&ipc_tr, "invalid resource id %x : %x", module_id, instance_id); - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -EINVAL; } /* nothing to do since core 0 is active now */ @@ -735,51 +765,36 @@ static int ipc4_module_process_dx(union ipc4_message_header *ipc4) static int ipc4_process_module_message(union ipc4_message_header *ipc4) { - uint32_t type; - int ret; + struct ipc_core_ctx *ctx = *arch_ipc_get(); - type = ipc4->r.type; - - switch (type) { + switch (ipc4->r.type) { case SOF_IPC4_MOD_INIT_INSTANCE: - ret = ipc4_init_module_instance(ipc4); - break; - case SOF_IPC4_MOD_CONFIG_GET: - case SOF_IPC4_MOD_CONFIG_SET: - ret = IPC4_UNAVAILABLE; - tr_info(&ipc_tr, "unsupported module CONFIG_GET"); - break; + return ipc4_init_module_instance(ipc4); case SOF_IPC4_MOD_LARGE_CONFIG_GET: - ret = ipc4_get_large_config_module_instance(ipc4); - break; + return ipc4_get_large_config_module_instance(ipc4); case SOF_IPC4_MOD_LARGE_CONFIG_SET: - ret = ipc4_set_large_config_module_instance(ipc4); - break; + return ipc4_set_large_config_module_instance(ipc4); case SOF_IPC4_MOD_BIND: - ret = ipc4_bind_module_instance(ipc4); - break; + return ipc4_bind_module_instance(ipc4); case SOF_IPC4_MOD_UNBIND: - ret = ipc4_unbind_module_instance(ipc4); - break; + return ipc4_unbind_module_instance(ipc4); case SOF_IPC4_MOD_DELETE_INSTANCE: - ret = ipc4_delete_module_instance(ipc4); - break; + return ipc4_delete_module_instance(ipc4); case SOF_IPC4_MOD_SET_D0IX: - ret = ipc4_module_process_d0ix(ipc4); - break; + return ipc4_module_process_d0ix(ipc4); case SOF_IPC4_MOD_SET_DX: - ret = ipc4_module_process_dx(ipc4); - break; + return ipc4_module_process_dx(ipc4); + case SOF_IPC4_MOD_CONFIG_GET: + case SOF_IPC4_MOD_CONFIG_SET: + tr_info(&ipc_tr, "unsupported module CONFIG_GET"); + COMPILER_FALLTHROUGH; case SOF_IPC4_MOD_ENTER_MODULE_RESTORE: case SOF_IPC4_MOD_EXIT_MODULE_RESTORE: - ret = IPC4_UNAVAILABLE; - break; - default: - ret = IPC4_UNAVAILABLE; break; } - return ret; + ctx->error = IPC4_UNAVAILABLE; + return -EOPNOTSUPP; } ipc_cmd_hdr *mailbox_validate(void) @@ -833,6 +848,7 @@ void ipc_msg_reply(struct sof_ipc_reply *reply) void ipc_cmd(ipc_cmd_hdr *_hdr) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); union ipc4_message_header *in = ipc_from_hdr(_hdr); uint32_t *data = ipc_get()->comp_data; enum ipc4_message_target target; @@ -841,6 +857,8 @@ void ipc_cmd(ipc_cmd_hdr *_hdr) if (!in) return; + ctx->error = IPC4_SUCCESS; + /* no process on scheduled thread */ msg_data.delayed_reply = 0; msg_data.delayed_error = 0; @@ -856,6 +874,7 @@ void ipc_cmd(ipc_cmd_hdr *_hdr) target = in->r.msg_tgt; + /* FIXME: seems like more IPCs should be checked to run on secondary cores */ switch (target) { case SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG: err = ipc4_process_glb_message(in); @@ -866,14 +885,15 @@ void ipc_cmd(ipc_cmd_hdr *_hdr) default: /* should not reach here as we only have 2 message types */ tr_err(&ipc_tr, "ipc4: invalid target %d", target); - err = IPC4_UNKNOWN_MESSAGE_TYPE; + ctx->error = IPC4_UNKNOWN_MESSAGE_TYPE; + err = -EINVAL; } - if (err) - tr_err(&ipc_tr, "ipc4: %d failed err %d", target, err); + if (ctx->error != IPC4_SUCCESS) + tr_err(&ipc_tr, "ipc4: %d failed err %d", target, ctx->error); - /* FW sends a ipc message to host if request bit is set*/ - if (in->r.rsp == SOF_IPC4_MESSAGE_DIR_MSG_REQUEST) { + /* FW sends an ipc message to host if request bit is set */ + if (in->r.rsp == SOF_IPC4_MESSAGE_DIR_MSG_REQUEST && err <= 0) { char *data = ipc_get()->comp_data; struct ipc4_message_reply reply; @@ -883,10 +903,11 @@ void ipc_cmd(ipc_cmd_hdr *_hdr) reply.header.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REPLY; reply.header.r.msg_tgt = in->r.msg_tgt; reply.header.r.type = in->r.type; + /* FIXME: if and else below do the same */ if (msg_data.delayed_error) - reply.header.r.status = err; + reply.header.r.status = ctx->error; else - reply.header.r.status = err; + reply.header.r.status = ctx->error; msg_reply.header = reply.header.dat; ipc_msg_send(&msg_reply, data, true); diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 83cd4d1fbdc4..446a711da940 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -52,11 +52,51 @@ void ipc_build_trace_posn(struct sof_ipc_dma_trace_posn *posn) { } +static int ipc4_add_comp_dev(struct comp_dev *dev) +{ + struct ipc_core_ctx *ctx = *arch_ipc_get(); + struct ipc *ipc = ipc_get(); + struct ipc_comp_dev *icd; + + /* allocate the IPC component container */ + icd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, + sizeof(struct ipc_comp_dev)); + if (!icd) { + tr_err(&ipc_tr, "ipc_comp_new(): alloc failed"); + ctx->error = IPC4_OUT_OF_MEMORY; + return -ENOMEM; + } + + icd->cd = dev; + icd->type = COMP_TYPE_COMPONENT; + icd->core = dev->ipc_config.core; + icd->id = dev->ipc_config.id; + + tr_dbg(&ipc_tr, "ipc4_add_comp_dev add comp %x", icd->id); + /* add new component to the list */ + list_item_append(&icd->list, &ipc->comp_list); + + return 0; +}; + +static void ipc4_free_comp_dev(struct comp_dev *dev) +{ + struct ipc *ipc = ipc_get(); + struct ipc_comp_dev *icd = ipc_get_comp_by_id(ipc, dev->ipc_config.id); + + if (!icd) + return; + + list_item_del(&icd->list); + rfree(icd); +} + struct comp_dev *comp_new(struct sof_ipc_comp *comp) { struct comp_ipc_config ipc_config; const struct comp_driver *drv; struct comp_dev *dev; + int ret; drv = ipc4_get_comp_drv(IPC4_MOD_ID(comp->id)); if (!drv) @@ -80,21 +120,28 @@ struct comp_dev *comp_new(struct sof_ipc_comp *comp) dcache_invalidate_region((void *)(MAILBOX_HOSTBOX_BASE), MAILBOX_HOSTBOX_SIZE); + /* Also copies ipc_config to dev->ipc_config */ dev = drv->ops.create(drv, &ipc_config, (void *)MAILBOX_HOSTBOX_BASE); if (!dev) return NULL; + /* ipc4_add_comp_dev() sets IPC4 error */ + ret = ipc4_add_comp_dev(dev); + if (ret < 0) { + drv->ops.free(dev); + return NULL; + } + list_init(&dev->bsource_list); list_init(&dev->bsink_list); - ipc4_add_comp_dev(dev); - return dev; } static int ipc4_create_pipeline(struct ipc *ipc, uint32_t pipeline_id, uint32_t priority, uint32_t memory_size) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc_comp_dev *ipc_pipe; struct pipeline *pipe; @@ -104,14 +151,16 @@ static int ipc4_create_pipeline(struct ipc *ipc, uint32_t pipeline_id, uint32_t if (ipc_pipe) { tr_err(&ipc_tr, "ipc: pipeline id is already taken, pipe_desc->instance_id = %u", pipeline_id); - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -EBUSY; } - /* create the pipeline */ + /* create the pipeline. FIXME: is a component ID of 0 really suitable? */ pipe = pipeline_new(pipeline_id, priority, 0); if (!pipe) { tr_err(&ipc_tr, "ipc: pipeline_new() failed"); - return IPC4_OUT_OF_MEMORY; + ctx->error = IPC4_OUT_OF_MEMORY; + return -ENOMEM; } pipe->time_domain = SOF_TIME_DOMAIN_TIMER; @@ -129,7 +178,8 @@ static int ipc4_create_pipeline(struct ipc *ipc, uint32_t pipeline_id, uint32_t sizeof(struct ipc_comp_dev)); if (!ipc_pipe) { pipeline_free(pipe); - return IPC4_OUT_OF_MEMORY; + ctx->error = IPC4_OUT_OF_MEMORY; + return -ENOMEM; } ipc_pipe->pipeline = pipe; @@ -148,6 +198,7 @@ int ipc_pipeline_new(struct ipc *ipc, ipc_pipe_new *_pipe_desc) tr_dbg(&ipc_tr, "ipc: pipeline id = %u", (uint32_t)pipe_desc->header.r.instance_id); + /* ipc4_create_pipeline() sets IPC4 error */ return ipc4_create_pipeline(ipc, pipe_desc->header.r.instance_id, pipe_desc->header.r.ppl_priority, pipe_desc->header.r.ppl_mem_size); } @@ -158,35 +209,41 @@ static int ipc_pipeline_module_free(uint32_t pipeline_id) struct ipc_comp_dev *icd; int ret; - icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_COMPONENT, pipeline_id); - while (icd) { + /* Find all components on the pipeline and free them */ + while ((icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_COMPONENT, pipeline_id))) { ret = ipc_comp_free(ipc, icd->id); if (ret) return ret; - - icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_COMPONENT, pipeline_id); } - return IPC4_SUCCESS; + return 0; } int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc_comp_dev *ipc_pipe; int ret; /* check whether pipeline exists */ ipc_pipe = ipc_get_comp_by_id(ipc, comp_id); - if (!ipc_pipe) + if (!ipc_pipe) { + ctx->error = IPC4_INVALID_RESOURCE_ID; return -ENODEV; + } /* check core */ - if (!cpu_is_me(ipc_pipe->core)) - return ipc_process_on_core(ipc_pipe->core, false); + if (!cpu_is_me(ipc_pipe->core)) { + ret = ipc_process_on_core(ipc_pipe->core, false); + if (ret < 0) + ctx->error = IPC4_INVALID_CORE_ID; + return ret; + } ret = ipc_pipeline_module_free(ipc_pipe->pipeline->pipeline_id); if (ret) { tr_err(&ipc_tr, "ipc_pipeline_free(): module free () failed"); + ctx->error = IPC4_INVALID_RESOURCE_STATE; return ret; } @@ -194,26 +251,24 @@ int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id) ret = pipeline_free(ipc_pipe->pipeline); if (ret < 0) { tr_err(&ipc_tr, "ipc_pipeline_free(): pipeline_free() failed"); - return IPC4_INVALID_RESOURCE_STATE; + ctx->error = IPC4_INVALID_RESOURCE_STATE; + return ret; } - ipc_pipe->pipeline = NULL; list_item_del(&ipc_pipe->list); rfree(ipc_pipe); - return IPC4_SUCCESS; + return 0; } static struct comp_buffer *ipc4_create_buffer(struct comp_dev *src, struct comp_dev *sink, uint32_t src_queue, uint32_t dst_queue) { - struct ipc4_base_module_cfg *src_cfg, *sink_cfg; - struct comp_buffer *buffer = NULL; + struct ipc4_base_module_cfg *src_cfg; struct sof_ipc_buffer ipc_buf; int buf_size; src_cfg = (struct ipc4_base_module_cfg *)comp_get_drvdata(src); - sink_cfg = (struct ipc4_base_module_cfg *)comp_get_drvdata(sink); /* double it since obs is single buffer size */ buf_size = src_cfg->obs * 2; @@ -222,13 +277,13 @@ static struct comp_buffer *ipc4_create_buffer(struct comp_dev *src, struct comp_ ipc_buf.comp.id = IPC4_COMP_ID(src_queue, dst_queue); ipc_buf.comp.pipeline_id = src->ipc_config.pipeline_id; ipc_buf.comp.core = src->ipc_config.core; - buffer = buffer_new(&ipc_buf); - return buffer; + return buffer_new(&ipc_buf); } int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_bind_unbind *bu; struct comp_buffer *buffer = NULL; struct comp_dev *source; @@ -244,14 +299,16 @@ int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) if (!source || !sink) { tr_err(&ipc_tr, "failed to find src %x, or dst %x", src_id, sink_id); - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -ENODEV; } buffer = ipc4_create_buffer(source, sink, bu->data.r.src_queue, bu->data.r.dst_queue); if (!buffer) { tr_err(&ipc_tr, "failed to allocate buffer to bind %d to %d", src_id, sink_id); - return IPC4_OUT_OF_MEMORY; + ctx->error = IPC4_OUT_OF_MEMORY; + return -ENOMEM; } ret = comp_buffer_connect(source, source->ipc_config.core, buffer, @@ -268,19 +325,26 @@ int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) goto err; } + /* + * FIXME: is the buffer not freed for these errors because it is already + * connected and will be freed normally during tear-down? + */ ret = comp_bind(source, bu); - if (ret < 0) - return IPC4_INVALID_RESOURCE_ID; + if (ret < 0) { + ctx->error = IPC4_INVALID_RESOURCE_ID; + return ret; + } ret = comp_bind(sink, bu); if (ret < 0) - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; - return 0; + return ret; err: buffer_free(buffer); - return IPC4_INVALID_RESOURCE_STATE; + ctx->error = IPC4_INVALID_RESOURCE_STATE; + return ret; } /* when both module instances are parts of the same pipeline Unbind IPC would @@ -290,6 +354,7 @@ int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) */ int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_module_bind_unbind *bu; struct comp_buffer *buffer = NULL; struct comp_dev *src, *sink; @@ -305,11 +370,14 @@ int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) sink = ipc4_get_comp_dev(sink_id); if (!src || !sink) { tr_err(&ipc_tr, "failed to find src %x, or dst %x", src_id, sink_id); - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -ENODEV; } - if (src->pipeline == sink->pipeline) - return IPC4_INVALID_REQUEST; + if (src->pipeline == sink->pipeline) { + ctx->error = IPC4_INVALID_REQUEST; + return -EINVAL; + } buffer_id = IPC4_COMP_ID(bu->data.r.src_queue, bu->data.r.dst_queue); list_for_item(sink_list, &src->bsink_list) { @@ -322,8 +390,10 @@ int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) } } - if (!buffer) - return IPC4_INVALID_RESOURCE_ID; + if (!buffer) { + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -ENOENT; + } irq_local_disable(flags); list_item_del(buffer_comp_list(buffer, PPL_CONN_DIR_COMP_TO_BUFFER)); @@ -335,14 +405,16 @@ int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) buffer_free(buffer); ret = comp_unbind(src, bu); - if (ret < 0) - return IPC4_INVALID_RESOURCE_ID; + if (ret < 0) { + ctx->error = IPC4_INVALID_RESOURCE_ID; + return ret; + } ret = comp_unbind(sink, bu); if (ret < 0) - return IPC4_INVALID_RESOURCE_ID; + ctx->error = IPC4_INVALID_RESOURCE_ID; - return 0; + return ret; } /* dma index may be for playback or capture. Current @@ -350,10 +422,13 @@ int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect) * and the index more than this will be capture. This function * convert dma id to dma channel */ -static inline int process_dma_index(uint32_t dma_id, uint32_t *dir, uint32_t *chan) +static int process_dma_index(uint32_t dma_id, uint32_t *dir, uint32_t *chan) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); + if (dma_id > DAI_NUM_HDA_OUT + DAI_NUM_HDA_IN) { tr_err(&ipc_tr, "dma id %d is out of range", dma_id); + ctx->error = IPC4_INVALID_NODE_ID; return -EINVAL; } @@ -381,7 +456,7 @@ static struct comp_dev *ipc4_create_host(uint32_t pipeline_id, uint32_t id, uint if (!drv) return NULL; - memset_s(&config, sizeof(config), 0, sizeof(config)); + memset(&config, 0, sizeof(config)); config.type = SOF_COMP_HOST; config.pipeline_id = pipeline_id; config.core = 0; @@ -416,7 +491,7 @@ static struct comp_dev *ipc4_create_dai(struct pipeline *pipe, uint32_t id, uint if (!drv) return NULL; - memset_s(&config, sizeof(config), 0, sizeof(config)); + memset(&config, 0, sizeof(config)); config.type = SOF_COMP_DAI; config.pipeline_id = pipe->pipeline_id; config.core = 0; @@ -438,8 +513,10 @@ static struct comp_dev *ipc4_create_dai(struct pipeline *pipe, uint32_t id, uint list_init(&dev->bsink_list); ret = comp_dai_config(dev, &dai, copier_cfg); - if (ret < 0) + if (ret < 0) { + drv->ops.free(dev); return NULL; + } return dev; } @@ -493,6 +570,7 @@ static void construct_config(struct ipc4_copier_module_cfg *copier_cfg, uint32_t int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc4_copier_module_cfg copier_cfg; struct sof_ipc_stream_params params; struct ipc_comp_dev *ipc_pipe; @@ -509,38 +587,54 @@ int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) uint32_t dir, host_chan, link_chan; int ret; - if (process_dma_index(cdma->header.r.host_dma_id, &dir, &host_chan) < 0) - return IPC4_INVALID_NODE_ID; + /* process_dma_index() sets IPC4 error */ + ret = process_dma_index(cdma->header.r.host_dma_id, &dir, &host_chan); + if (ret < 0) + return ret; - if (process_dma_index(cdma->header.r.link_dma_id, &dir, &link_chan) < 0) - return IPC4_INVALID_NODE_ID; + ret = process_dma_index(cdma->header.r.link_dma_id, &dir, &link_chan); + if (ret < 0) + return ret; /* build a pipeline id based on dma id */ pipeline_id = IPC4_COMP_ID(cdma->header.r.host_dma_id + IPC4_MAX_MODULE_COUNT, cdma->header.r.link_dma_id); + /* ipc4_create_pipeline() sets IPC4 error */ ret = ipc4_create_pipeline(ipc, pipeline_id, 0, cdma->data.r.fifo_size); if (ret < 0) { tr_err(&comp_tr, "failed to create pipeline for chain dma"); - return IPC4_INVALID_NODE_ID; + return ret; } ipc_pipe = ipc_get_comp_by_id(ipc, pipeline_id); + if (!ipc_pipe) { + /* + * We just created this pipeline. If we cannot find it again, we + * either have a bug or a race. We won't be able to free it + * either. + */ + ctx->error = IPC4_FAILURE; + return -EFAULT; + } host_id = pipeline_id + 1; host = ipc4_create_host(pipeline_id, host_id, dir); if (!host) { tr_err(&comp_tr, "failed to create host for chain dma"); - return IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; + ret = -ENOENT; + goto e_pipe; } host->period = ipc_pipe->pipeline->period; + /* ipc4_add_comp_dev() sets IPC4 error */ ret = ipc4_add_comp_dev(host); if (ret < 0) - return IPC4_FAILURE; + goto e_host_create; - memset_s(¶ms, sizeof(params), 0, sizeof(params)); - memset_s(&copier_cfg, sizeof(copier_cfg), 0, sizeof(copier_cfg)); + memset(¶ms, 0, sizeof(params)); + memset(&copier_cfg, 0, sizeof(copier_cfg)); construct_config(&copier_cfg, cdma->data.r.fifo_size, ¶ms); params.direction = dir; params.stream_tag = host_chan + 1; @@ -549,14 +643,17 @@ int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) dai = ipc4_create_dai(ipc_pipe->pipeline, dai_id, dir, &copier_cfg); if (!dai) { tr_err(&comp_tr, "failed to create dai for chain dma"); - return IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; + ret = -ENOENT; + goto e_host_add; } dai->period = ipc_pipe->pipeline->period; + /* ipc4_add_comp_dev() sets IPC4 error */ ret = ipc4_add_comp_dev(dai); if (ret < 0) - return IPC4_FAILURE; + goto e_dai_create; buf_id = dai_id + 1; if (dir == SOF_IPC_STREAM_PLAYBACK) { @@ -575,7 +672,9 @@ int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) buf = buffer_new(&ipc_buf); if (!buf) { tr_err(&comp_tr, "failed to create buffer for chain dma"); - return IPC4_OUT_OF_MEMORY; + ctx->error = IPC4_OUT_OF_MEMORY; + ret = -ENOMEM; + goto e_dai_add; } comp_buffer_connect(src, src->ipc_config.core, buf, PPL_CONN_DIR_COMP_TO_BUFFER); @@ -585,9 +684,12 @@ int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) ipc_pipe->pipeline->source_comp = src; ipc_pipe->pipeline->sink_comp = sink; + /* FIXME: shouldn't ipc_pipe->pipeline->core be set? */ ret = ipc_pipeline_complete(ipc, pipeline_id); - if (ret < 0) - ret = IPC4_INVALID_RESOURCE_STATE; + if (ret < 0) { + ctx->error = IPC4_INVALID_RESOURCE_STATE; + goto e_buf; + } /* set up host & dai and start pipeline */ if (cdma->header.r.enable) { @@ -599,27 +701,53 @@ int ipc4_create_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) ret = host->drv->ops.params(host, ¶ms); if (ret < 0) { tr_err(&ipc_tr, "failed to set host params %d", ret); - return IPC4_ERROR_INVALID_PARAM; + ctx->error = IPC4_ERROR_INVALID_PARAM; + goto e_buf; } ret = dai->drv->ops.params(dai, ¶ms); if (ret < 0) { tr_err(&ipc_tr, "failed to set dai params %d", ret); - return IPC4_ERROR_INVALID_PARAM; + ctx->error = IPC4_ERROR_INVALID_PARAM; + goto e_buf; } ret = pipeline_prepare(ipc_pipe->pipeline, host); if (ret < 0) { tr_err(&ipc_tr, "failed to prepare for chain dma %d", ret); - ret = IPC4_INVALID_RESOURCE_STATE; + ctx->error = IPC4_INVALID_RESOURCE_STATE; + goto e_buf; } } + return 0; + +e_buf: + /* undo buffer_new() */ + buffer_free(buf); +e_dai_add: + /* undo ipc4_add_comp_dev() */ + ipc4_free_comp_dev(dai); +e_dai_create: + /* undo ipc4_create_dai() */ + dai->drv->ops.free(dai); +e_host_add: + /* undo ipc4_add_comp_dev() */ + ipc4_free_comp_dev(host); +e_host_create: + /* undo ipc4_create_host() */ + host->drv->ops.free(host); +e_pipe: + /* undo ipc4_create_pipeline() */ + ipc_pipeline_free(ipc_get(), pipeline_id); + return ret; } +/* Can return 0, a negative error, or one of PPL_STATUS_* codes */ int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) { + struct ipc_core_ctx *ctx = *arch_ipc_get(); struct ipc_comp_dev *ipc_pipe; uint32_t pipeline_id; int ret; @@ -627,8 +755,12 @@ int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) pipeline_id = IPC4_COMP_ID(cdma->header.r.host_dma_id + IPC4_MAX_MODULE_COUNT, cdma->header.r.link_dma_id); ipc_pipe = ipc_get_comp_by_id(ipc, pipeline_id); - if (!ipc_pipe) - return IPC4_INVALID_RESOURCE_ID; + if (!ipc_pipe) { + ctx->error = IPC4_INVALID_RESOURCE_ID; + return -ENODEV; + } + + /* FIXME: shouldn't this run on the pipeline core? */ /* pause or release chain dma */ if (!cdma->header.r.enable) { @@ -637,7 +769,8 @@ int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) COMP_TRIGGER_PAUSE); if (ret < 0) { tr_err(&ipc_tr, "failed to disable chain dma %d", ret); - return IPC4_BAD_STATE; + ctx->error = IPC4_BAD_STATE; + return ret; } } @@ -646,22 +779,25 @@ int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) ret = pipeline_reset(ipc_pipe->pipeline, ipc_pipe->pipeline->sink_comp); if (ret < 0) { tr_err(&ipc_tr, "failed to reset chain dma %d", ret); - return IPC4_BAD_STATE; + ctx->error = IPC4_BAD_STATE; + return ret; } + /* ipc_pipeline_free() sets IPC4 error */ ret = ipc_pipeline_free(ipc, pipeline_id); if (ret < 0) { tr_err(&ipc_tr, "failed to free chain dma %d", ret); - return IPC4_BAD_STATE; + return ret; } } - return IPC4_SUCCESS; + return 0; } if (!cdma->header.r.allocate) { tr_err(&ipc_tr, "can't enable chain dma"); - return IPC4_INVALID_REQUEST; + ctx->error = IPC4_INVALID_REQUEST; + return -EINVAL; } switch (ipc_pipe->pipeline->status) { @@ -670,20 +806,21 @@ int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma) COMP_TRIGGER_PRE_RELEASE); if (ret < 0) { tr_err(&ipc_tr, "failed to resume chain dma %d", ret); - return IPC4_BAD_STATE; + ctx->error = IPC4_BAD_STATE; } - break; + return ret; case COMP_STATE_READY: case COMP_STATE_PREPARE: ret = pipeline_trigger(ipc_pipe->pipeline, ipc_pipe->pipeline->sink_comp, COMP_TRIGGER_PRE_START); if (ret < 0) { tr_err(&ipc_tr, "failed to start chain dma %d", ret); - return IPC4_BAD_STATE; + ctx->error = IPC4_BAD_STATE; } + return ret; } - return IPC4_SUCCESS; + return 0; } const struct comp_driver *ipc4_get_drv(uint8_t *uuid) @@ -724,7 +861,6 @@ const struct comp_driver *ipc4_get_drv(uint8_t *uuid) const struct comp_driver *ipc4_get_comp_drv(int module_id) { struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)IMR_BOOT_LDR_MANIFEST_BASE; - const struct comp_driver *drv; struct sof_man_module *mod; int entry_index; @@ -735,9 +871,8 @@ const struct comp_driver *ipc4_get_comp_drv(int module_id) entry_index = module_id; mod = (struct sof_man_module *)((char *)desc + SOF_MAN_MODULE_OFFSET(entry_index)); - drv = ipc4_get_drv(mod->uuid); - return drv; + return ipc4_get_drv(mod->uuid); } struct comp_dev *ipc4_get_comp_dev(uint32_t comp_id) @@ -751,29 +886,3 @@ struct comp_dev *ipc4_get_comp_dev(uint32_t comp_id) return icd->cd; } - -int ipc4_add_comp_dev(struct comp_dev *dev) -{ - struct ipc *ipc = ipc_get(); - struct ipc_comp_dev *icd; - - /* allocate the IPC component container */ - icd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, - sizeof(struct ipc_comp_dev)); - if (!icd) { - tr_err(&ipc_tr, "ipc_comp_new(): alloc failed"); - rfree(icd); - return IPC4_OUT_OF_MEMORY; - } - - icd->cd = dev; - icd->type = COMP_TYPE_COMPONENT; - icd->core = dev->ipc_config.core; - icd->id = dev->ipc_config.id; - - tr_dbg(&ipc_tr, "ipc4_add_comp_dev add comp %x", icd->id); - /* add new component to the list */ - list_item_append(&icd->list, &ipc->comp_list); - - return 0; -}; diff --git a/zephyr/wrapper.c b/zephyr/wrapper.c index 812843a251d4..8811e9373fb6 100644 --- a/zephyr/wrapper.c +++ b/zephyr/wrapper.c @@ -481,6 +481,17 @@ unsigned int _xtos_ints_off(unsigned int mask) return 0; } +static struct ipc_core_ctx *_ipc[CONFIG_CORE_COUNT]; + +/** + * Retrieves IPC context. + * @return Core-local IPC context. + */ +struct ipc_core_ctx **arch_ipc_get(void) +{ + return _ipc + cpu_get_id(); +} + void ipc_send_queued_msg(void); static void ipc_send_queued_callback(void *private_data, enum notify_id event_type,