From 0b4568d34fd6732a8122879589cf65d5ef9e7fbb Mon Sep 17 00:00:00 2001 From: Bartosz Kokoszko Date: Mon, 4 May 2020 15:49:29 +0200 Subject: [PATCH 1/3] component: add set of helper functions for large config configuration This commit rewrites functions responsible for large config configuration from detect_test component and puts them in component.c file as generic ones - as a result they can be used by other components. Signed-off-by: Bartosz Kokoszko --- src/CMakeLists.txt | 2 +- src/audio/component.c | 152 ++++++++++++++++++ src/include/sof/audio/component.h | 49 ++++++ src/math/CMakeLists.txt | 6 + test/cmocka/src/audio/component/mock.c | 5 + .../testbench/include/testbench/common_test.h | 1 + 6 files changed, 214 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46b50e131643..4f1fb1388c9b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(arch) add_subdirectory(ipc) add_subdirectory(audio) add_subdirectory(lib) +add_subdirectory(math) add_local_sources(sof spinlock.c) if(CONFIG_LIBRARY) @@ -14,7 +15,6 @@ endif() add_subdirectory(debug) add_subdirectory(drivers) add_subdirectory(init) -add_subdirectory(math) add_subdirectory(schedule) if (CONFIG_TRACE) diff --git a/src/audio/component.c b/src/audio/component.c index 84b6a5e677a3..91f00284238a 100644 --- a/src/audio/component.c +++ b/src/audio/component.c @@ -326,6 +326,158 @@ int comp_verify_params(struct comp_dev *dev, uint32_t flag, return 0; } +void comp_free_model_data(struct comp_dev *dev, struct comp_model_data *model) +{ + if (!model->data) + return; + + rfree(model->data); + model->data = NULL; + model->data_size = 0; + model->crc = 0; + model->data_pos = 0; +} + +int comp_alloc_model_data(struct comp_dev *dev, struct comp_model_data *model, + uint32_t size) +{ + if (!size) + return 0; + + comp_free_model_data(dev, model); + + model->data = rballoc(0, SOF_MEM_CAPS_RAM, size); + + if (!model->data) { + comp_err(dev, "comp_alloc_model_data(): model->data rballoc failed"); + return -ENOMEM; + } + + bzero(model->data, size); + model->data_size = size; + model->data_pos = 0; + model->crc = 0; + + return 0; +} + +int comp_set_model(struct comp_dev *dev, struct comp_model_data *model, + struct sof_ipc_ctrl_data *cdata) +{ + bool done = false; + int ret = 0; + + comp_dbg(dev, "comp_set_model() msg_index = %d, num_elems = %d, remaining = %d ", + cdata->msg_index, cdata->num_elems, + cdata->elems_remaining); + + /* in case when the current package is the first, we should allocate + * memory for whole model data + */ + if (!cdata->msg_index) { + ret = comp_alloc_model_data(dev, model, cdata->data->size); + if (ret < 0) + return ret; + } + + /* return an error in case when we do not have allocated memory for + * model data + */ + if (!model->data) { + comp_err(dev, "comp_set_model(): buffer not allocated"); + return -ENOMEM; + } + + if (!cdata->elems_remaining) { + /* when we receive the last package and do not fill the whole + * allocated buffer, we return an error + */ + if (cdata->num_elems + model->data_pos < model->data_size) { + comp_err(dev, "comp_set_model(): not enough data to fill the buffer"); + // TODO: handle this situation + + return -EINVAL; + } + + /* the whole data were received properly */ + done = true; + comp_dbg(dev, "comp_set_model(): final package received"); + } + + /* return an error in case when received data exceed allocated + * memory + */ + if (cdata->num_elems > model->data_size - model->data_pos) { + comp_err(dev, "comp_set_model(): too much data"); + return -EINVAL; + } + + ret = memcpy_s((char *)model->data + model->data_pos, + model->data_size - model->data_pos, + cdata->data->data, cdata->num_elems); + assert(!ret); + + /* update data_pos variable with received number of elements (num_elem) + */ + model->data_pos += cdata->num_elems; + + /* Update crc value when done */ + if (done) { + model->crc = crc32(0, model->data, model->data_size); + comp_dbg(dev, "comp_set_model() done, memory_size = 0x%x, crc = 0x%08x", + model->data_size, model->crc); + } + + return 0; +} + +int comp_get_model(struct comp_dev *dev, struct comp_model_data *model, + struct sof_ipc_ctrl_data *cdata, int size) +{ + size_t bs; + int ret = 0; + + comp_dbg(dev, "comp_get_model() msg_index = %d, num_elems = %d, remaining = %d ", + cdata->msg_index, cdata->num_elems, + cdata->elems_remaining); + + /* Copy back to user space */ + if (model->data) { + /* reset data_pos variable in case of copying first element */ + if (!cdata->msg_index) { + model->data_pos = 0; + comp_dbg(dev, "comp_get_model() model data_size = 0x%x", + model->data_size); + } + + bs = cdata->num_elems; + + /* return an error in case of mismatch between num_elems and + * required size + */ + if (bs > size) { + comp_err(dev, "comp_get_model(): invalid size %d", bs); + return -EINVAL; + } + + /* copy required size of data */ + ret = memcpy_s(cdata->data->data, size, + (char *)model->data + model->data_pos, + bs); + assert(!ret); + + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->size = model->data_size; + model->data_pos += bs; + + } else { + comp_err(dev, "comp_get_model(): !model->data"); + ret = -EINVAL; + } + + return ret; +} + struct comp_dev *comp_make_shared(struct comp_dev *dev) { struct list_item *old_bsource_list = &dev->bsource_list; diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 2b59fbdb88a5..ef8fc85c8faa 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -472,6 +472,16 @@ struct comp_copy_limits { int sink_frame_bytes; }; +/** \brief Struct for large component configs */ +struct comp_model_data { + uint32_t data_size; /**< size of component's model data */ + void *data; /**< pointer to model data */ + uint32_t crc; /**< crc value of model data */ + uint32_t data_pos; /**< indicates a data position in data + * sending/receiving process + */ +}; + /** \brief Computes size of the component device including ipc config. */ #define COMP_SIZE(x) \ (sizeof(struct comp_dev) - sizeof(struct sof_ipc_comp) + sizeof(x)) @@ -725,6 +735,45 @@ void comp_get_copy_limits_with_lock(struct comp_buffer *source, buffer_unlock(source, source_flags); } +/** + * Frees data for large component configurations. + * + * @param dev Component device + * @param model Component model struct + */ +void comp_free_model_data(struct comp_dev *dev, struct comp_model_data *model); + +/** + * Allocates data for large component configurations. + * + * @param dev Component device + * @param model Component model struct + * @param size Required size. + */ +int comp_alloc_model_data(struct comp_dev *dev, struct comp_model_data *model, + uint32_t size); + +/** + * Gets model data for large component configurations. + * + * @param dev Component device + * @param model Component model struct + * @param cdata IPC ctrl data + */ +int comp_set_model(struct comp_dev *dev, struct comp_model_data *model, + struct sof_ipc_ctrl_data *cdata); +/** + * + * Set model data for large component configurations. + * + * @param dev Component device + * @param model Component model struct + * @param cdata IPC ctrl data + * @param size Required size + */ +int comp_get_model(struct comp_dev *dev, struct comp_model_data *model, + struct sof_ipc_ctrl_data *cdata, int size); + /** * Called by component in params() function in order to set and update some of * downstream (playback) or upstream (capture) buffer parameters with pcm diff --git a/src/math/CMakeLists.txt b/src/math/CMakeLists.txt index 0c0ad5189f52..1a5158f531cc 100644 --- a/src/math/CMakeLists.txt +++ b/src/math/CMakeLists.txt @@ -1,3 +1,9 @@ # SPDX-License-Identifier: BSD-3-Clause add_local_sources(sof numbers.c trig.c decibels.c) + +if(BUILD_LIBRARY) + return() +endif() + +add_local_sources(sof trig.c decibels.c) \ No newline at end of file diff --git a/test/cmocka/src/audio/component/mock.c b/test/cmocka/src/audio/component/mock.c index 1471ea3c9945..b54ceee3e42d 100644 --- a/test/cmocka/src/audio/component/mock.c +++ b/test/cmocka/src/audio/component/mock.c @@ -38,6 +38,11 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev, int32_t bytes) { } +uint32_t crc32(uint32_t base, const void *data, uint32_t bytes) +{ + return 0; +} + struct sof *sof_get(void) { return &sof; diff --git a/tools/testbench/include/testbench/common_test.h b/tools/testbench/include/testbench/common_test.h index 409e3a6a5606..a3dcfc4e766b 100644 --- a/tools/testbench/include/testbench/common_test.h +++ b/tools/testbench/include/testbench/common_test.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #define DEBUG_MSG_LEN 256 From dfd523838c42d891698093b27f4fcc80e252e83e Mon Sep 17 00:00:00 2001 From: Bartosz Kokoszko Date: Mon, 4 May 2020 16:59:58 +0200 Subject: [PATCH 2/3] detector: replace private data model functions with generic ones This commit replaces private detector component functions with generic ones in order to remove code duplication. Signed-off-by: Bartosz Kokoszko --- src/audio/detect_test.c | 168 +++------------------------------------- 1 file changed, 11 insertions(+), 157 deletions(-) diff --git a/src/audio/detect_test.c b/src/audio/detect_test.c index 4745d3fc23f1..6dd9684a5b68 100644 --- a/src/audio/detect_test.c +++ b/src/audio/detect_test.c @@ -56,16 +56,9 @@ DECLARE_SOF_RT_UUID("kd-test", keyword_uuid, 0xeba8d51f, 0x7827, 0x47b5, DECLARE_TR_CTX(keyword_tr, SOF_UUID(keyword_uuid), LOG_LEVEL_INFO); -struct model_data { - uint32_t data_size; - void *data; - uint32_t crc; - uint32_t data_pos; /**< current copy position for model data */ -}; - struct comp_data { struct sof_detect_test_config config; - struct model_data model; + struct comp_model_data model; int32_t activation; uint32_t detected; uint32_t detect_preamble; /**< current keyphrase preamble length */ @@ -199,48 +192,6 @@ static void default_detect_test(struct comp_dev *dev, } } -static void free_mem_load(struct comp_data *cd) -{ - if (!cd) { - comp_cl_err(&comp_keyword, "free_mem_load(): invalid cd"); - return; - } - - if (cd->model.data) { - rfree(cd->model.data); - cd->model.data = NULL; - cd->model.data_size = 0; - cd->model.crc = 0; - cd->model.data_pos = 0; - } -} - -static int alloc_mem_load(struct comp_data *cd, uint32_t size) -{ - if (!size) - return 0; - - if (!cd) { - comp_cl_err(&comp_keyword, "alloc_mem_load(): invalid cd"); - return -EINVAL; - } - - free_mem_load(cd); - - cd->model.data = rballoc(0, SOF_MEM_CAPS_RAM, size); - - if (!cd->model.data) { - comp_cl_err(&comp_keyword, "alloc_mem_load() alloc failed"); - return -ENOMEM; - } - - bzero(cd->model.data, size); - cd->model.data_size = size; - cd->model.data_pos = 0; - - return 0; -} - static int test_keyword_get_threshold(struct comp_dev *dev, int sample_width) { switch (sample_width) { @@ -335,7 +286,7 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv, } } - ret = alloc_mem_load(cd, INITIAL_MODEL_DATA_SIZE); + ret = comp_alloc_model_data(dev, &cd->model, INITIAL_MODEL_DATA_SIZE); if (ret < 0) { comp_err(dev, "test_keyword_new(): model data initial failed"); goto fail; @@ -369,7 +320,7 @@ static void test_keyword_free(struct comp_dev *dev) comp_info(dev, "test_keyword_free()"); ipc_msg_free(cd->msg); - free_mem_load(cd); + comp_free_model_data(dev, &cd->model); rfree(cd); rfree(dev); } @@ -456,70 +407,14 @@ static int test_keyword_set_config(struct comp_dev *dev, return test_keyword_apply_config(dev, cfg); } -static int test_keyword_set_model(struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata) -{ - struct comp_data *cd = comp_get_drvdata(dev); - int ret = 0; - bool done = false; - - comp_dbg(dev, "keyword_ctrl_set_model() msg_index = %d, num_elems = %d, remaining = %d ", - cdata->msg_index, cdata->num_elems, - cdata->elems_remaining); - - if (!cdata->msg_index) { - ret = alloc_mem_load(cd, cdata->data->size); - if (ret < 0) - return ret; - } - - if (!cd->model.data) { - comp_err(dev, "keyword_ctrl_set_model(): buffer not allocated"); - return -EINVAL; - } - - if (!cdata->elems_remaining) { - if (cdata->num_elems + cd->model.data_pos < - cd->model.data_size) { - comp_err(dev, "keyword_ctrl_set_model(): not enough data to fill the buffer"); - - /* TODO: anything to do in such a situation? */ - - return -EINVAL; - } - - done = true; - comp_info(dev, "test_keyword_set_model() final packet received"); - } - - if (cdata->num_elems > - cd->model.data_size - cd->model.data_pos) { - comp_err(dev, "keyword_ctrl_set_model(): too much data"); - return -EINVAL; - } - - ret = memcpy_s((char *)cd->model.data + cd->model.data_pos, - cd->model.data_size - cd->model.data_pos, - cdata->data->data, cdata->num_elems); - assert(!ret); - - cd->model.data_pos += cdata->num_elems; - - if (done) { - /* Set model data done, update crc value */ - cd->model.crc = crc32(0, cd->model.data, - cd->model.data_size); - comp_info(dev, "keyword_ctrl_set_model() done, memory_size = 0x%x, crc = 0x%08x", - cd->model.data_size, cd->model.crc); - } - return 0; -} - static int test_keyword_ctrl_set_bin_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) { + struct comp_data *cd = comp_get_drvdata(dev); int ret = 0; + assert(cd); + if (dev->state != COMP_STATE_READY) { /* It is a valid request but currently this is not * supported during playback/capture. The driver will @@ -536,7 +431,7 @@ static int test_keyword_ctrl_set_bin_data(struct comp_dev *dev, ret = test_keyword_set_config(dev, cdata); break; case SOF_DETECT_TEST_MODEL: - ret = test_keyword_set_model(dev, cdata); + ret = comp_set_model(dev, &cd->model, cdata); break; default: comp_err(dev, "keyword_ctrl_set_bin_data(): unknown binary data type"); @@ -599,62 +494,21 @@ static int test_keyword_get_config(struct comp_dev *dev, return ret; } -static int test_keyword_get_model(struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata, int size) -{ - struct comp_data *cd = comp_get_drvdata(dev); - size_t bs; - int ret = 0; - - comp_dbg(dev, "test_keyword_get_model() msg_index = %d, num_elems = %d, remaining = %d ", - cdata->msg_index, cdata->num_elems, - cdata->elems_remaining); - - /* Copy back to user space */ - if (cd->model.data) { - if (!cdata->msg_index) { - /* reset copy offset */ - cd->model.data_pos = 0; - comp_info(dev, "test_keyword_get_model() model data_size = 0x%x, crc = 0x%08x", - cd->model.data_size, cd->model.crc); - } - - bs = cdata->num_elems; - if (bs > size) { - comp_err(dev, "test_keyword_get_model(): invalid size %d", - bs); - return -EINVAL; - } - - ret = memcpy_s(cdata->data->data, size, - (char *)cd->model.data + cd->model.data_pos, - bs); - assert(!ret); - - cdata->data->abi = SOF_ABI_VERSION; - cdata->data->size = cd->model.data_size; - cd->model.data_pos += bs; - - } else { - comp_err(dev, "test_keyword_get_model(): invalid cd->config"); - ret = -EINVAL; - } - - return ret; -} - static int test_keyword_ctrl_get_bin_data(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata, int size) { + struct comp_data *cd = comp_get_drvdata(dev); int ret = 0; + assert(cd); + switch (cdata->data->type) { case SOF_DETECT_TEST_CONFIG: ret = test_keyword_get_config(dev, cdata, size); break; case SOF_DETECT_TEST_MODEL: - ret = test_keyword_get_model(dev, cdata, size); + ret = comp_get_model(dev, &cd->model, cdata, size); break; default: comp_err(dev, "test_keyword_ctrl_get_bin_data(): unknown binary data type"); From e62458993d0c0aef44152f68f2c550267bf5b468 Mon Sep 17 00:00:00 2001 From: Bartosz Kokoszko Date: Mon, 12 Aug 2019 12:28:15 +0200 Subject: [PATCH 3/3] smart_amp: add smart amplifier test component Smart amplifier should be used in following topology: +------------------------------------------+ +------+ | +---+ +-------------+ +---+ +-------+ | | Host |----->|Buf|->| Smart Amp |->|Buf|->|SSP Dai|-----------+ +------+ | +---+ +-------------+ +---+ +-------+ | | +-----------------^------------------------+ | | | +---+ | |Buf| | +---+ | ^ | +-----------------|--------------------------+ | +------+ | +---+ +--------|------+ +---+ +-------+ | | | Host |<-----|Buf|<-| Demux |<-|Buf|<-|SSP Dai|<--------+ +------+ | +---+ +---------------+ +---+ +-------+ | +--------------------------------------------+ Smart amplifier test component does not implement any specific processing algorithm at the moment. I can pass chosen channels from source (buffer between Host and Smart amplifier) or feedback (buffer between Smart amplifier and Demux component) buffers. Those channels are selected by sending proper config to this component. Smart amplifier can be configured via SOF_SMART_AMP_CONFIG or SOF_SMART_AMP_MODEL data (similarly as it happens in detector case). Signed-off-by: Bartosz Kokoszko --- src/audio/CMakeLists.txt | 5 + src/audio/Kconfig | 7 + src/audio/smart_amp_test.c | 573 +++++++++++++++++++++++++ src/include/sof/audio/smart_amp_test.h | 89 ++++ 4 files changed, 674 insertions(+) create mode 100644 src/audio/smart_amp_test.c create mode 100644 src/include/sof/audio/smart_amp_test.h diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 01ec7be261c2..c423d39261db 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -60,6 +60,11 @@ if(NOT CONFIG_LIBRARY) detect_test.c ) endif() + if(CONFIG_COMP_TEST_SMART_AMP) + add_local_sources(sof + smart_amp_test.c + ) + endif() add_subdirectory(pcm_converter) if(CONFIG_COMP_ASRC) add_subdirectory(asrc) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index 36828d75ceb9..0fa7adb83de0 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -122,6 +122,13 @@ config COMP_DCBLOCK Select for DC Blocking Filter component. This component filters out the DC offset which often originates from a microphone's output. +config COMP_TEST_SMART_AMP + depends on CAVS && !CAVS_VERSION_1_5 + bool "Smart amplifier test component" + default y + help + Select for test smart amplifier component + config COMP_TEST_KEYPHRASE bool "KEYPHRASE_TEST component" default y diff --git a/src/audio/smart_amp_test.c b/src/audio/smart_amp_test.c new file mode 100644 index 000000000000..4e118dc1f1dd --- /dev/null +++ b/src/audio/smart_amp_test.c @@ -0,0 +1,573 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Bartosz Kokoszko + +#include +#include +#include +#include + +static const struct comp_driver comp_smart_amp; + +/* 167a961e-8ae4-11ea-89f1-000c29ce1635 */ +DECLARE_SOF_RT_UUID("smart_amp-test", smart_amp_comp_uuid, 0x167a961e, 0x8ae4, + 0x11ea, 0x89, 0xf1, 0x00, 0x0c, 0x29, 0xce, 0x16, 0x35); + +DECLARE_TR_CTX(smart_amp_comp_tr, SOF_UUID(smart_amp_comp_uuid), + LOG_LEVEL_INFO); + +struct smart_amp_data { + struct sof_smart_amp_config config; + struct comp_model_data model; + + struct comp_buffer *source_buf; /**< stream source buffer */ + struct comp_buffer *feedback_buf; /**< feedback source buffer */ + struct comp_buffer *sink_buf; /**< sink buffer */ + + smart_amp_proc process; + + uint32_t in_channels; + uint32_t out_channels; +}; + +static struct comp_dev *smart_amp_new(const struct comp_driver *drv, + struct sof_ipc_comp *comp) +{ + struct comp_dev *dev; + struct sof_ipc_comp_process *sa; + struct sof_ipc_comp_process *ipc_sa = + (struct sof_ipc_comp_process *)comp; + struct smart_amp_data *sad; + struct sof_smart_amp_config *cfg; + size_t bs; + int ret; + + dev = comp_alloc(drv, COMP_SIZE(struct sof_ipc_comp_process)); + if (!dev) + return NULL; + + sad = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*sad)); + + if (!sad) { + rfree(dev); + return NULL; + } + + comp_set_drvdata(dev, sad); + + sa = COMP_GET_IPC(dev, sof_ipc_comp_process); + + ret = memcpy_s(sa, sizeof(*sa), ipc_sa, + sizeof(struct sof_ipc_comp_process)); + assert(!ret); + + cfg = (struct sof_smart_amp_config *)ipc_sa->data; + bs = ipc_sa->size; + + if ((bs > 0) && (bs < sizeof(struct sof_smart_amp_config))) { + comp_err(dev, "smart_amp_new(): failed to apply config"); + + if (sad) + rfree(sad); + rfree(sad); + return NULL; + } + + memcpy_s(&sad->config, sizeof(struct sof_smart_amp_config), cfg, bs); + + dev->state = COMP_STATE_READY; + + return dev; +} + +static int smart_amp_set_config(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + struct sof_smart_amp_config *cfg; + size_t bs; + + /* Copy new config, find size from header */ + cfg = (struct sof_smart_amp_config *)cdata->data->data; + bs = cfg->size; + + comp_dbg(dev, "smart_amp_set_config(), actual blob size = %u, expected blob size = %u", + bs, sizeof(struct sof_smart_amp_config)); + + if (bs != sizeof(struct sof_smart_amp_config)) { + comp_err(dev, "smart_amp_set_config(): invalid blob size, actual blob size = %u, expected blob size = %u", + bs, sizeof(struct sof_smart_amp_config)); + return -EINVAL; + } + + memcpy_s(&sad->config, sizeof(struct sof_smart_amp_config), cfg, + sizeof(struct sof_smart_amp_config)); + + return 0; +} + +static int smart_amp_get_config(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata, int size) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + size_t bs; + int ret = 0; + + // Copy back to user space + bs = sad->config.size; + + comp_dbg(dev, "smart_amp_set_config(), actual blob size = %u, expected blob size = %u", + bs, sizeof(struct sof_smart_amp_config)); + + if (bs == 0 || bs > size) + return -EINVAL; + + ret = memcpy_s(cdata->data->data, size, &sad->config, bs); + assert(!ret); + + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->size = bs; + + return ret; +} + +static int smart_amp_ctrl_get_bin_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata, + int size) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + int ret = 0; + + assert(sad); + + switch (cdata->data->type) { + case SOF_SMART_AMP_CONFIG: + ret = smart_amp_get_config(dev, cdata, size); + break; + case SOF_SMART_AMP_MODEL: + ret = comp_get_model(dev, &sad->model, cdata, size); + break; + default: + comp_err(dev, "smart_amp_ctrl_get_bin_data(): unknown binary data type"); + break; + } + + return ret; +} + +static int smart_amp_ctrl_get_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata, int size) +{ + int ret = 0; + + comp_info(dev, "smart_amp_ctrl_get_data() size: %d", size); + + switch (cdata->cmd) { + case SOF_CTRL_CMD_BINARY: + ret = smart_amp_ctrl_get_bin_data(dev, cdata, size); + break; + default: + comp_err(dev, "smart_amp_ctrl_get_data(): invalid cdata->cmd"); + return -EINVAL; + } + + return ret; +} + +static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + int ret = 0; + + assert(sad); + + if (dev->state != COMP_STATE_READY) { + /* It is a valid request but currently this is not + * supported during playback/capture. The driver will + * re-send data in next resume when idle and the new + * configuration will be used when playback/capture + * starts. + */ + comp_err(dev, "smart_amp_ctrl_set_bin_data(): driver is busy"); + return -EBUSY; + } + + switch (cdata->data->type) { + case SOF_SMART_AMP_CONFIG: + ret = smart_amp_set_config(dev, cdata); + break; + case SOF_SMART_AMP_MODEL: + ret = comp_set_model(dev, &sad->model, cdata); + break; + default: + comp_err(dev, "smart_amp_ctrl_set_bin_data(): unknown binary data type"); + break; + } + + return ret; +} + +static int smart_amp_ctrl_set_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata) +{ + int ret = 0; + + /* Check version from ABI header */ + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { + comp_err(dev, "smart_amp_ctrl_set_data(): invalid version"); + return -EINVAL; + } + + switch (cdata->cmd) { + case SOF_CTRL_CMD_ENUM: + comp_info(dev, "smart_amp_ctrl_set_data(), SOF_CTRL_CMD_ENUM"); + break; + case SOF_CTRL_CMD_BINARY: + comp_info(dev, "smart_amp_ctrl_set_data(), SOF_CTRL_CMD_BINARY"); + ret = smart_amp_ctrl_set_bin_data(dev, cdata); + break; + default: + comp_err(dev, "smart_amp_ctrl_set_data(): invalid cdata->cmd"); + ret = -EINVAL; + break; + } + + return ret; +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int smart_amp_cmd(struct comp_dev *dev, int cmd, void *data, + int max_data_size) +{ + struct sof_ipc_ctrl_data *cdata = data; + + comp_info(dev, "smart_amp_cmd(): cmd: %d", cmd); + + switch (cmd) { + case COMP_CMD_SET_DATA: + return smart_amp_ctrl_set_data(dev, cdata); + case COMP_CMD_GET_DATA: + return smart_amp_ctrl_get_data(dev, cdata, max_data_size); + default: + return -EINVAL; + } +} + +static void smart_amp_free(struct comp_dev *dev) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + + comp_info(dev, "smart_amp_free()"); + + comp_free_model_data(dev, &sad->model); + rfree(sad); + rfree(dev); +} + +static int smart_amp_verify_params(struct comp_dev *dev, + struct sof_ipc_stream_params *params) +{ + int ret; + + comp_info(dev, "smart_amp_verify_params()"); + + ret = comp_verify_params(dev, BUFF_PARAMS_CHANNELS, params); + if (ret < 0) { + comp_err(dev, "volume_verify_params() error: comp_verify_params() failed."); + return ret; + } + + return 0; +} + +static int smart_amp_params(struct comp_dev *dev, + struct sof_ipc_stream_params *params) +{ + int err; + + comp_info(dev, "smart_amp_params()"); + + err = smart_amp_verify_params(dev, params); + if (err < 0) { + comp_err(dev, "smart_amp_params(): pcm params verification failed."); + return -EINVAL; + } + + return 0; +} + +static int smart_amp_trigger(struct comp_dev *dev, int cmd) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + int ret = 0; + + comp_info(dev, "smart_amp_trigger(), command = %u", cmd); + + ret = comp_set_state(dev, cmd); + + if (ret == COMP_STATUS_STATE_ALREADY_SET) + ret = PPL_STATUS_PATH_STOP; + + switch (cmd) { + case COMP_TRIGGER_START: + case COMP_TRIGGER_RELEASE: + buffer_zero(sad->feedback_buf); + break; + case COMP_TRIGGER_PAUSE: + case COMP_TRIGGER_STOP: + break; + default: + break; + } + + return ret; +} + +static int smart_amp_process_s16(struct comp_dev *dev, + const struct audio_stream *source, + const struct audio_stream *sink, + uint32_t frames, int8_t *chan_map) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + int16_t *src; + int16_t *dest; + uint32_t in_frag = 0; + uint32_t out_frag = 0; + int i; + int j; + + comp_dbg(dev, "smart_amp_process_s16()"); + + for (i = 0; i < frames; i++) { + for (j = 0 ; j < sad->out_channels; j++) { + if (chan_map[j] != -1) { + src = audio_stream_read_frag_s16(source, + in_frag + + chan_map[j]); + dest = audio_stream_write_frag_s16(sink, + out_frag); + *dest = *src; + } + out_frag++; + } + in_frag += source->channels; + } + return 0; +} + +static int smart_amp_process_s32(struct comp_dev *dev, + const struct audio_stream *source, + const struct audio_stream *sink, + uint32_t frames, int8_t *chan_map) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + int32_t *src; + int32_t *dest; + uint32_t in_frag = 0; + uint32_t out_frag = 0; + int i; + int j; + + comp_dbg(dev, "smart_amp_process_s32()"); + + for (i = 0; i < frames; i++) { + for (j = 0 ; j < sad->out_channels; j++) { + if (chan_map[j] != -1) { + src = audio_stream_read_frag_s32(source, + in_frag + + chan_map[j]); + dest = audio_stream_write_frag_s32(sink, + out_frag); + *dest = *src; + } + out_frag++; + } + in_frag += source->channels; + } + + return 0; +} + +static smart_amp_proc get_smart_amp_process(struct comp_dev *dev) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + + switch (sad->source_buf->stream.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + return smart_amp_process_s16; + case SOF_IPC_FRAME_S24_4LE: + case SOF_IPC_FRAME_S32_LE: + return smart_amp_process_s32; + default: + comp_err(dev, "smart_amp_process() error: not supported frame format"); + return NULL; + } +} + +static int smart_amp_copy(struct comp_dev *dev) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + uint32_t avail_passthrough_frames; + uint32_t avail_feedback_frames; + uint32_t avail_frames; + uint32_t source_bytes; + uint32_t sink_bytes; + uint32_t feedback_bytes; + uint32_t source_flags = 0; + uint32_t sink_flags = 0; + uint32_t feedback_flags = 0; + int ret = 0; + + comp_dbg(dev, "smart_amp_copy()"); + + buffer_lock(sad->source_buf, &source_flags); + buffer_lock(sad->sink_buf, &sink_flags); + + /* available bytes and samples calculation */ + avail_passthrough_frames = + audio_stream_avail_frames(&sad->source_buf->stream, + &sad->sink_buf->stream); + + buffer_unlock(sad->source_buf, source_flags); + buffer_unlock(sad->sink_buf, sink_flags); + + avail_frames = avail_passthrough_frames; + + comp_dbg(dev, "smart_amp_copy(): avail_passthrough_frames: %d", + avail_passthrough_frames); + + buffer_lock(sad->feedback_buf, &feedback_flags); + if (sad->feedback_buf->source->state == dev->state) { + /* feedback */ + avail_feedback_frames = sad->feedback_buf->stream.avail / + audio_stream_frame_bytes(&sad->feedback_buf->stream); + + avail_frames = MIN(avail_passthrough_frames, + avail_feedback_frames); + + feedback_bytes = avail_frames * + audio_stream_frame_bytes(&sad->feedback_buf->stream); + + buffer_unlock(sad->feedback_buf, feedback_flags); + + comp_dbg(dev, "smart_amp_copy(): processing %d feedback bytes", + feedback_bytes); + + sad->process(dev, &sad->feedback_buf->stream, + &sad->sink_buf->stream, avail_frames, + sad->config.feedback_ch_map); + + comp_update_buffer_consume(sad->feedback_buf, feedback_bytes); + } + + /* bytes calculation */ + buffer_lock(sad->source_buf, &source_flags); + source_bytes = avail_frames * + audio_stream_frame_bytes(&sad->source_buf->stream); + buffer_unlock(sad->source_buf, source_flags); + + buffer_lock(sad->sink_buf, &sink_flags); + sink_bytes = avail_frames * + audio_stream_frame_bytes(&sad->sink_buf->stream); + buffer_unlock(sad->sink_buf, sink_flags); + + /* process data */ + sad->process(dev, &sad->source_buf->stream, &sad->sink_buf->stream, + avail_frames, sad->config.source_ch_map); + + /* source/sink buffer pointers update */ + comp_update_buffer_consume(sad->source_buf, source_bytes); + comp_update_buffer_produce(sad->sink_buf, sink_bytes); + + return ret; +} + +static int smart_amp_reset(struct comp_dev *dev) +{ + comp_info(dev, "smart_amp_reset()"); + + comp_set_state(dev, COMP_TRIGGER_RESET); + + return 0; +} + +static int smart_amp_prepare(struct comp_dev *dev) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + struct comp_buffer *source_buffer; + struct list_item *blist; + int ret; + + comp_info(dev, "smart_amp_prepare()"); + + ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); + if (ret < 0) + return ret; + + if (ret == COMP_STATUS_STATE_ALREADY_SET) + return PPL_STATUS_PATH_STOP; + + /* searching for stream and feedback source buffers */ + list_for_item(blist, &dev->bsource_list) { + source_buffer = container_of(blist, struct comp_buffer, + sink_list); + + if (source_buffer->source->comp.type == SOF_COMP_DEMUX) + sad->feedback_buf = source_buffer; + else + sad->source_buf = source_buffer; + } + + sad->sink_buf = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + sad->in_channels = sad->source_buf->stream.channels; + sad->out_channels = sad->sink_buf->stream.channels; + + sad->feedback_buf->stream.channels = sad->config.feedback_channels; + + /* TODO: + * ATM feedback buffer frame_fmt is hardcoded to s32_le. It should be + * removed when parameters negotiation between pipelines will prepared + */ + sad->feedback_buf->stream.frame_fmt = SOF_IPC_FRAME_S32_LE; + + sad->process = get_smart_amp_process(dev); + if (!sad->process) { + comp_err(dev, "smart_amp_prepare(): get_smart_amp_process failed"); + return -EINVAL; + } + + return 0; +} + +static const struct comp_driver comp_smart_amp = { + .type = SOF_COMP_SMART_AMP, + .uid = SOF_RT_UUID(smart_amp_comp_uuid), + .tctx = &smart_amp_comp_tr, + .ops = { + .create = smart_amp_new, + .free = smart_amp_free, + .params = smart_amp_params, + .prepare = smart_amp_prepare, + .cmd = smart_amp_cmd, + .trigger = smart_amp_trigger, + .copy = smart_amp_copy, + .reset = smart_amp_reset, + }, +}; + +static SHARED_DATA struct comp_driver_info comp_smart_amp_info = { + .drv = &comp_smart_amp, +}; + +static void sys_comp_smart_amp_init(void) +{ + comp_register(platform_shared_get(&comp_smart_amp_info, + sizeof(comp_smart_amp_info))); +} + +DECLARE_MODULE(sys_comp_smart_amp_init); diff --git a/src/include/sof/audio/smart_amp_test.h b/src/include/sof/audio/smart_amp_test.h new file mode 100644 index 000000000000..0fbe14c20319 --- /dev/null +++ b/src/include/sof/audio/smart_amp_test.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2020 Intel Corporation. All rights reserved. + * + * Author: Bartosz Kokoszko + */ + +#ifndef __SOF_AUDIO_SMART_AMP_H__ +#define __SOF_AUDIO_SMART_AMP_H__ + +#include +#include + +#define SMART_AMP_MAX_STREAM_CHAN 8 + +/** IPC blob types */ +#define SOF_SMART_AMP_CONFIG 0 +#define SOF_SMART_AMP_MODEL 1 + +struct smart_amp_model_data { + uint32_t data_size; + void *data; + uint32_t crc; + uint32_t data_pos; +}; + +typedef int(*smart_amp_proc)(struct comp_dev *dev, + const struct audio_stream *source, + const struct audio_stream *sink, uint32_t frames, + int8_t *chan_map); + +/* Each channel map specifies which channel from input (buffer between host + * and smart amp - source_chan_map[] or feedback buffer between smart amp and + * demux - feedback_chan_map[]) will be copied to specific smart amp output + * channel. Value -1 means that for this output channel we will not take any + * channel from specific input. + * + * E.g. assuming that a smart amplifier input stream is configured + * with channels parameter set to 2, feedback stream with channels set to 8 + * and smart amplifier output stream with channels set to 4 (smart amplifier + * converts stream from 2 to 4 channels) and source/feedback_ch_map's are as + * follows: + * + * source_ch_map = [0, 1, -1, -1, -1 ,-1 ,-1, -1] + * feedback_ch_map = [-1, -1, 0, 1, -1, -1, -1, -1] + * + * As a result smart amplifier test component will procces source and feedback + * streams in following way: + * + * + * + * PLAYBACK + * STREAM +---+ + * | 0 +------------+ + * +---+ | SMART AMPLIFIER + * | 1 +---------+ | OUTPUT + * +---+ | | +---+ + * | +-->+ 0 | + * | +---+ + * +----->+ 1 | + * +---+ + * +---+ +--->+ 2 | + * FEEDBACK | 0 +-----------+ +---+ + * STREAM +---+ +->| 3 | + * | 1 +-------------+ +---+ + * +---+ | 4 | + * | 2 | +---+ + * +---+ | 5 | + * | 3 | +---+ + * +---+ | 6 | + * | 4 | +---+ + * +---+ | 7 | + * | 5 | +---+ + * +---+ + * | 6 | + * +---+ + * | 7 | + * +---+ + * + */ + +struct sof_smart_amp_config { + uint32_t size; + uint32_t feedback_channels; + int8_t source_ch_map[PLATFORM_MAX_CHANNELS]; + int8_t feedback_ch_map[PLATFORM_MAX_CHANNELS]; +}; + +#endif /* __SOF_AUDIO_SMART_AMP_H__ */