From 9ca86c9fd6534c096191d2801c9339def0a2470e Mon Sep 17 00:00:00 2001 From: Pin-chih Lin Date: Tue, 15 Aug 2023 21:48:16 +0800 Subject: [PATCH] smart_amp: revamp to two-layer modular design structure The motivation is depicted in https://github.com/thesofproject/sof/pull/7801 This commit revamps smart_amp component design to two-layer structure, i.e. generic layer and inner model layer. Generic layer is the common part of smart amp process which can be regarded as the glue code interacting between SOF component ops and inner model. While inner model may have various implementations respectively for solution suppliers in a modular way. Signed-off-by: Pin-chih Lin --- src/audio/smart_amp/CMakeLists.txt | 2 + src/audio/smart_amp/Kconfig | 29 +- src/audio/smart_amp/smart_amp.c | 619 +++++++++-------- src/audio/smart_amp/smart_amp_generic.c | 463 ++++++++----- src/audio/smart_amp/smart_amp_maxim_dsm.c | 713 +++++++++++--------- src/audio/smart_amp/smart_amp_passthru.c | 147 ++++ src/include/sof/audio/format.h | 15 + src/include/sof/audio/smart_amp/smart_amp.h | 424 ++++++++---- 8 files changed, 1506 insertions(+), 906 deletions(-) create mode 100644 src/audio/smart_amp/smart_amp_passthru.c diff --git a/src/audio/smart_amp/CMakeLists.txt b/src/audio/smart_amp/CMakeLists.txt index 89048004f16b..387381057804 100644 --- a/src/audio/smart_amp/CMakeLists.txt +++ b/src/audio/smart_amp/CMakeLists.txt @@ -10,4 +10,6 @@ else() sof_add_static_library(dsm ${CMAKE_CURRENT_LIST_DIR}/lib/release/dsm_lib/libdsm.a) endif() target_include_directories(sof PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/dsm_api/inc) +else() +add_local_sources(sof smart_amp_passthru.c) endif() diff --git a/src/audio/smart_amp/Kconfig b/src/audio/smart_amp/Kconfig index 4cbbffbc51d2..02cc6fadc0c5 100644 --- a/src/audio/smart_amp/Kconfig +++ b/src/audio/smart_amp/Kconfig @@ -1,12 +1,30 @@ # SPDX-License-Identifier: BSD-3-Clause -menu "Smart amplifier solutions" - visible if COMP_SMART_AMP +if COMP_SMART_AMP + +choice + prompt "Smart Amplifier solution applied" + default PASSTHRU_AMP + help + The selection for Smart Amplifier component implementation + will depend on the Amplifier solution supplier. It is fair + to treat the supported solutions as mutually exclusive ones. + There should be no more than one solution selected per build + config. When Smart Amplifier is present but no solution is + supported, the passthrough mode will be applied as default. + + config PASSTHRU_AMP + bool "Stream Passthrough" + help + The default option as the passthrough mode while no other + solution is applied. While selected, the feed-forward input + frames will be passed to output after channel remapping. No + gain or latency will be produced. In the meanwhile, the + feedback input frames will be consumed but dropped directly. config MAXIM_DSM bool "Maxim DSM solution" select MAXIM_DSM_STUB if COMP_STUBS - default n help Select to apply Maxim DSM(Dynamic Speaker Management) solution for Smart Amplifier. As the third-party supply, the @@ -14,6 +32,8 @@ menu "Smart amplifier solutions" building the FW binary with this option enabled. The library itself should be statically linked with the SoF FW binary image. +endchoice + config MAXIM_DSM_STUB bool "Maxim DSM solution" depends on MAXIM_DSM @@ -22,5 +42,4 @@ menu "Smart amplifier solutions" Select to build the Maxim DSM adapter with a stub library. This should only be used for CI and testing. -endmenu - +endif diff --git a/src/audio/smart_amp/smart_amp.c b/src/audio/smart_amp/smart_amp.c index 931acc6ce1be..ecf0e3a5d7ce 100644 --- a/src/audio/smart_amp/smart_amp.c +++ b/src/audio/smart_amp/smart_amp.c @@ -3,6 +3,12 @@ // Copyright(c) 2020 Maxim Integrated All rights reserved. // // Author: Ryan Lee +// +// Copyright(c) 2023 Google LLC. +// +// Author: Pin-chih Lin + +#include #include #include @@ -14,201 +20,151 @@ static const struct comp_driver comp_smart_amp; +#if CONFIG_MAXIM_DSM /* 0cd84e80-ebd3-11ea-adc1-0242ac120002 */ -DECLARE_SOF_RT_UUID("Maxim DSM", maxim_dsm_comp_uuid, 0x0cd84e80, 0xebd3, +DECLARE_SOF_RT_UUID("Maxim DSM", smart_amp_comp_uuid, 0x0cd84e80, 0xebd3, 0x11ea, 0xad, 0xc1, 0x02, 0x42, 0xac, 0x12, 0x00, 0x02); -DECLARE_TR_CTX(maxim_dsm_comp_tr, SOF_UUID(maxim_dsm_comp_uuid), +#else /* Passthrough */ +/* 64a794f0-55d3-4bca-9d5b-7b588badd037 */ +DECLARE_SOF_RT_UUID("Passthru Amp", smart_amp_comp_uuid, 0x64a794f0, 0x55d3, + 0x4bca, 0x9d, 0x5b, 0x7b, 0x58, 0x8b, 0xad, 0xd0, 0x37); + +#endif +DECLARE_TR_CTX(smart_amp_comp_tr, SOF_UUID(smart_amp_comp_uuid), LOG_LEVEL_INFO); /* Amp configuration & model calibration data for tuning/debug */ #define SOF_SMART_AMP_CONFIG 0 #define SOF_SMART_AMP_MODEL 1 -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, bool is_feedback); - struct smart_amp_data { struct sof_smart_amp_config config; struct comp_data_blob_handler *model_handler; 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; - /* module handle for speaker protection algorithm */ - struct smart_amp_mod_struct_t *mod_handle; + struct ipc_config_process ipc_config; + + smart_amp_src_func ff_get_frame; /**< function to get stream source */ + smart_amp_src_func fb_get_frame; /**< function to get feedback source */ + smart_amp_sink_func ff_set_frame; /**< function to set sink */ + struct smart_amp_mod_stream ff_mod; /**< feed-forward buffer for mod */ + struct smart_amp_mod_stream fb_mod; /**< feedback buffer for mod */ + struct smart_amp_mod_stream out_mod; /**< output buffer for mod */ + + struct smart_amp_buf mod_mems[MOD_MEMBLK_MAX]; /**< memory blocks for mod */ + + struct smart_amp_mod_data_base *mod_data; /**< inner model data */ }; -static inline void smart_amp_free_memory(struct smart_amp_data *sad, - struct comp_dev *dev) +/* smart_amp_free_mod_memories will be called promptly once getting error on + * internal functions. That is, it may be called multiple times by the hierarchical + * model when an error occurs in the lower function and propagates level-wise to + * the function on top. Always set the pointer to NULL after freed to avoid the + * double-freeing error. + */ +static inline void smart_amp_free_mod_memories(struct smart_amp_data *sad) { - struct smart_amp_mod_struct_t *hspk = sad->mod_handle; - - /* buffer : sof -> spk protection feed forward process */ - rfree(hspk->buf.frame_in); - /* buffer : sof <- spk protection feed forward process */ - rfree(hspk->buf.frame_out); - /* buffer : sof -> spk protection feedback process */ - rfree(hspk->buf.frame_iv); - /* buffer : feed forward process input */ - rfree(hspk->buf.input); - /* buffer : feed forward process output */ - rfree(hspk->buf.output); - /* buffer : feedback voltage */ - rfree(hspk->buf.voltage); - /* buffer : feedback current */ - rfree(hspk->buf.current); - /* buffer : feed forward variable length -> fixed length */ - rfree(hspk->buf.ff.buf); - /* buffer : feed forward variable length <- fixed length */ - rfree(hspk->buf.ff_out.buf); - /* buffer : feedback variable length -> fixed length */ - rfree(hspk->buf.fb.buf); - /* Module handle release */ - rfree(hspk); + /* sof -> mod feed-forward data re-mapping and format conversion */ + rfree(sad->ff_mod.buf.data); + sad->ff_mod.buf.data = NULL; + /* sof -> mod feedback data re-mapping and format conversion */ + rfree(sad->fb_mod.buf.data); + sad->fb_mod.buf.data = NULL; + /* mod -> sof processed data format conversion */ + rfree(sad->out_mod.buf.data); + sad->out_mod.buf.data = NULL; + + /* mem block for mod private data usage */ + rfree(sad->mod_mems[MOD_MEMBLK_PRIVATE].data); + sad->mod_mems[MOD_MEMBLK_PRIVATE].data = NULL; + /* mem block for mod audio frame data usage */ + rfree(sad->mod_mems[MOD_MEMBLK_FRAME].data); + sad->mod_mems[MOD_MEMBLK_FRAME].data = NULL; + /* mem block for mod parameter blob usage */ + rfree(sad->mod_mems[MOD_MEMBLK_PARAM].data); + sad->mod_mems[MOD_MEMBLK_PARAM].data = NULL; + + /* inner model data struct */ + rfree(sad->mod_data); + sad->mod_data = NULL; } -static inline int smart_amp_alloc_memory(struct smart_amp_data *sad, - struct comp_dev *dev) +static inline int smart_amp_buf_alloc(struct smart_amp_buf *buf, size_t size) { - struct smart_amp_mod_struct_t *hspk; - int mem_sz; - int size; - - /* memory allocation for module handle */ - mem_sz = sizeof(struct smart_amp_mod_struct_t); - sad->mod_handle = rballoc(0, SOF_MEM_CAPS_RAM, mem_sz); - if (!sad->mod_handle) - goto err; - memset(sad->mod_handle, 0, mem_sz); - - hspk = sad->mod_handle; + buf->data = rballoc(0, SOF_MEM_CAPS_RAM, size); + if (!buf->data) + return -ENOMEM; + buf->size = size; + return 0; +} - /* buffer : sof -> spk protection feed forward process */ - size = SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t); - hspk->buf.frame_in = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.frame_in) - goto err; - mem_sz += size; +static ssize_t smart_amp_alloc_mod_memblk(struct smart_amp_data *sad, + enum smart_amp_mod_memblk blk) +{ + struct smart_amp_mod_data_base *mod = sad->mod_data; + int ret; + size_t size; - /* buffer : sof <- spk protection feed forward process */ - size = SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t); - hspk->buf.frame_out = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.frame_out) - goto err; - mem_sz += size; + /* query the required size from inner model. */ + ret = mod->mod_ops->query_memblk_size(mod, blk); + if (ret <= 0) + goto error; - /* buffer : sof -> spk protection feedback process */ - size = SMART_AMP_FB_BUF_DB_SZ * sizeof(int32_t); - hspk->buf.frame_iv = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.frame_iv) - goto err; - mem_sz += size; - - /* buffer : feed forward process input */ - size = DSM_FF_BUF_SZ * sizeof(int32_t); - hspk->buf.input = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.input) - goto err; - mem_sz += size; - - /* buffer : feed forward process output */ - size = DSM_FF_BUF_SZ * sizeof(int32_t); - hspk->buf.output = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.output) - goto err; - mem_sz += size; - - /* buffer : feedback voltage */ - size = DSM_FF_BUF_SZ * sizeof(int32_t); - hspk->buf.voltage = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.voltage) - goto err; - mem_sz += size; - - /* buffer : feedback current */ - size = DSM_FF_BUF_SZ * sizeof(int32_t); - hspk->buf.current = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.current) - goto err; - mem_sz += size; - - /* buffer : feed forward variable length -> fixed length */ - size = DSM_FF_BUF_DB_SZ * sizeof(int32_t); - hspk->buf.ff.buf = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.ff.buf) - goto err; - mem_sz += size; - - /* buffer : feed forward variable length <- fixed length */ - size = DSM_FF_BUF_DB_SZ * sizeof(int32_t); - hspk->buf.ff_out.buf = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.ff_out.buf) - goto err; - mem_sz += size; - - /* buffer : feedback variable length -> fixed length */ - size = DSM_FB_BUF_DB_SZ * sizeof(int32_t); - hspk->buf.fb.buf = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->buf.fb.buf) - goto err; - mem_sz += size; - - /* memory allocation of DSM handle */ - size = smart_amp_get_memory_size(hspk, dev); - hspk->dsmhandle = rballoc(0, SOF_MEM_CAPS_RAM, size); - if (!hspk->dsmhandle) - goto err; - memset(hspk->dsmhandle, 0, size); - mem_sz += size; - - comp_dbg(dev, "[DSM] module:%p (%d bytes used)", - hspk, mem_sz); + /* allocate the memory block when returned size > 0. */ + size = ret; + ret = smart_amp_buf_alloc(&sad->mod_mems[blk], size); + if (ret < 0) + goto error; - return 0; -err: - smart_amp_free_memory(sad, dev); - return -ENOMEM; -} + /* provide the memory block information to inner model. */ + ret = mod->mod_ops->set_memblk(mod, blk, &sad->mod_mems[blk]); + if (ret < 0) + goto error; -static void smart_amp_free_caldata(struct comp_dev *dev, - struct smart_amp_caldata *caldata) -{ - if (!caldata->data) - return; + return size; - rfree(caldata->data); - caldata->data = NULL; - caldata->data_size = 0; - caldata->data_pos = 0; +error: + smart_amp_free_mod_memories(sad); + return ret; } -static inline int smart_amp_alloc_caldata(struct comp_dev *dev, - struct smart_amp_caldata *caldata, - uint32_t size) +static int smart_amp_alloc_data_buffers(struct comp_dev *dev, + struct smart_amp_data *sad) { - smart_amp_free_caldata(dev, caldata); - - if (!size) - return 0; + size_t total_size; + int ret; + size_t size; - caldata->data = rballoc(0, SOF_MEM_CAPS_RAM, size); + /* sof -> mod feed-forward data re-mapping and format conversion */ + size = SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t); + ret = smart_amp_buf_alloc(&sad->ff_mod.buf, size); + if (ret < 0) + goto error; + total_size = size; - if (!caldata->data) { - comp_err(dev, "smart_amp_alloc_caldata(): model->data rballoc failed"); - return -ENOMEM; - } + /* sof -> mod feedback data re-mapping and format conversion */ + size = SMART_AMP_FB_BUF_DB_SZ * sizeof(int32_t); + ret = smart_amp_buf_alloc(&sad->fb_mod.buf, size); + if (ret < 0) + goto error; + total_size += size; - bzero(caldata->data, size); - caldata->data_size = size; - caldata->data_pos = 0; + /* mod -> sof processed data format conversion */ + size = SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t); + ret = smart_amp_buf_alloc(&sad->out_mod.buf, size); + if (ret < 0) + goto error; + total_size += size; + comp_dbg(dev, "smart_amp_alloc(): used data buffer %zu bytes", total_size); return 0; + +error: + smart_amp_free_mod_memories(sad); + return ret; } static struct comp_dev *smart_amp_new(const struct comp_driver *drv, @@ -220,7 +176,6 @@ static struct comp_dev *smart_amp_new(const struct comp_driver *drv, struct smart_amp_data *sad; struct sof_smart_amp_config *cfg; size_t bs; - int sz_caldata; int ret; dev = comp_alloc(drv, sizeof(*dev)); @@ -248,39 +203,57 @@ static struct comp_dev *smart_amp_new(const struct comp_driver *drv, memcpy_s(&sad->config, sizeof(struct sof_smart_amp_config), cfg, bs); - if (smart_amp_alloc_memory(sad, dev) != 0) + /* allocate inner model data struct */ + sad->mod_data = mod_data_create(dev); + if (!sad->mod_data) { + comp_err(dev, "smart_amp_new(): failed to allocate nner model data"); goto error; + } - /* Bitwidth information is not available. Use 16bit as default. - * Re-initialize in the prepare function if ncessary - */ - sad->mod_handle->bitwidth = 16; - if (smart_amp_init(sad->mod_handle, dev)) + /* allocate stream buffers for mod */ + ret = smart_amp_alloc_data_buffers(dev, sad); + if (ret) { + comp_err(dev, "smart_amp_new(): failed to allocate data buffers, ret:%d", ret); goto error; + } - /* Get the max. number of parameter to allocate memory for model data */ - sad->mod_handle->param.max_param = - smart_amp_get_num_param(sad->mod_handle, dev); - sz_caldata = sad->mod_handle->param.max_param * DSM_SINGLE_PARAM_SZ; + /* (before init) allocate mem block for mod private data usage */ + ret = smart_amp_alloc_mod_memblk(sad, MOD_MEMBLK_PRIVATE); + if (ret < 0) { + comp_err(dev, "smart_amp_new(): failed to allocate mod private, ret:%d", ret); + goto error; + } + comp_dbg(dev, "smart_amp_new(): used mod private buffer %d bytes", ret); - if (sz_caldata > 0) { - ret = smart_amp_alloc_caldata(dev, &sad->mod_handle->param.caldata, - sz_caldata * sizeof(int32_t)); - if (ret < 0) { - comp_err(dev, "smart_amp_new(): caldata initial failed"); - goto error; - } + /* init model */ + ret = sad->mod_data->mod_ops->init(sad->mod_data); + if (ret) { + comp_err(dev, "smart_amp_new(): failed to init inner model, ret:%d", ret); + goto error; } - /* update full parameter values */ - if (smart_amp_get_all_param(sad->mod_handle, dev) < 0) + /* (after init) allocate mem block for mod audio frame data usage */ + ret = smart_amp_alloc_mod_memblk(sad, MOD_MEMBLK_FRAME); + if (ret < 0) { + comp_err(dev, "smart_amp_new(): failed to allocate mod buffer, ret:%d", ret); goto error; + } + comp_dbg(dev, "smart_amp_new(): used mod data buffer %d bytes", ret); + + /* (after init) allocate mem block for mod parameter blob usage */ + ret = smart_amp_alloc_mod_memblk(sad, MOD_MEMBLK_PARAM); + if (ret < 0) { + comp_err(dev, "smart_amp_new(): failed to allocate mod config, ret:%d", ret); + goto error; + } + comp_dbg(dev, "smart_amp_new(): used mod config buffer %d bytes", ret); dev->state = COMP_STATE_READY; return dev; error: + smart_amp_free_mod_memories(sad); rfree(sad); rfree(dev); return NULL; @@ -295,14 +268,14 @@ static int smart_amp_set_config(struct comp_dev *dev, /* Copy new config, find size from header */ cfg = (struct sof_smart_amp_config *) - ASSUME_ALIGNED(&cdata->data->data, sizeof(uint32_t)); + ASSUME_ALIGNED(&cdata->data->data, sizeof(uint32_t)); bs = cfg->size; - comp_dbg(dev, "smart_amp_set_config(), actual blob size = %u, expected blob size = %u", + comp_dbg(dev, "smart_amp_set_config(), actual blob size = %zu, expected blob size = %zu", 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", + comp_err(dev, "smart_amp_set_config(): invalid blob size, actual blob size = %zu, expected blob size = %zu", bs, sizeof(struct sof_smart_amp_config)); return -EINVAL; } @@ -323,7 +296,7 @@ static int smart_amp_get_config(struct comp_dev *dev, /* Copy back to user space */ bs = sad->config.size; - comp_dbg(dev, "smart_amp_get_config(), actual blob size = %u, expected blob size = %u", + comp_dbg(dev, "smart_amp_get_config(), actual blob size = %zu, expected blob size = %zu", bs, sizeof(struct sof_smart_amp_config)); if (bs == 0 || bs > size) @@ -343,18 +316,20 @@ static int smart_amp_ctrl_get_bin_data(struct comp_dev *dev, int size) { struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_mod_data_base *mod; int ret = 0; assert(sad); + mod = sad->mod_data; switch (cdata->data->type) { case SOF_SMART_AMP_CONFIG: ret = smart_amp_get_config(dev, cdata, size); break; case SOF_SMART_AMP_MODEL: - ret = maxim_dsm_get_param(sad->mod_handle, dev, cdata, size); + ret = mod->mod_ops->get_config(mod, cdata, size); if (ret < 0) { - comp_err(dev, "smart_amp_ctrl_get_bin_data(): parameter read error!"); + comp_err(dev, "smart_amp_ctrl_get_bin_data(): failed to read inner model!"); return ret; } break; @@ -389,9 +364,11 @@ 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); + struct smart_amp_mod_data_base *mod; int ret = 0; assert(sad); + mod = sad->mod_data; if (dev->state < COMP_STATE_READY) { comp_err(dev, "smart_amp_ctrl_set_bin_data(): driver in init!"); @@ -403,9 +380,9 @@ static int smart_amp_ctrl_set_bin_data(struct comp_dev *dev, ret = smart_amp_set_config(dev, cdata); break; case SOF_SMART_AMP_MODEL: - ret = maxim_dsm_set_param(sad->mod_handle, dev, cdata); + ret = mod->mod_ops->set_config(mod, cdata); if (ret < 0) { - comp_err(dev, "smart_amp_ctrl_set_bin_data(): parameter write error!"); + comp_err(dev, "smart_amp_ctrl_set_bin_data(): failed to write inner model!"); return ret; } break; @@ -466,11 +443,12 @@ static void smart_amp_free(struct comp_dev *dev) comp_dbg(dev, "smart_amp_free()"); - smart_amp_free_caldata(dev, &sad->mod_handle->param.caldata); - smart_amp_free_memory(sad, dev); + smart_amp_free_mod_memories(sad); rfree(sad); + sad = NULL; rfree(dev); + dev = NULL; } static int smart_amp_verify_params(struct comp_dev *dev, @@ -531,42 +509,72 @@ static int smart_amp_trigger(struct comp_dev *dev, int cmd) return ret; } -static int smart_amp_process(struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - uint32_t frames, int8_t *chan_map, - bool is_feedback) +static int smart_amp_ff_process(struct comp_dev *dev, + const struct audio_stream *source, + const struct audio_stream *sink, + uint32_t frames, const int8_t *chan_map) { struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_mod_data_base *mod = sad->mod_data; int ret; - if (!is_feedback) - ret = smart_amp_ff_copy(dev, frames, - source, sink, - chan_map, sad->mod_handle, - sad->in_channels, sad->out_channels); - else - ret = smart_amp_fb_copy(dev, frames, - source, sink, - chan_map, sad->mod_handle, - audio_stream_get_channels(source)); - return ret; + sad->ff_mod.consumed = 0; + sad->out_mod.produced = 0; + + if (frames == 0) { + comp_warn(dev, "smart_amp_copy(): feed forward frame size zero warning."); + return 0; + } + + if (frames > SMART_AMP_FF_BUF_DB_SZ) { + comp_err(dev, "smart_amp_copy(): feed forward frame size overflow: %u", frames); + sad->ff_mod.consumed = frames; + return -EINVAL; + } + + sad->ff_get_frame(&sad->ff_mod, frames, source, chan_map); + + ret = mod->mod_ops->ff_proc(mod, frames, &sad->ff_mod, &sad->out_mod); + if (ret) { + comp_err(dev, "smart_amp_copy(): feed forward inner model process error"); + return ret; + } + + sad->ff_set_frame(&sad->out_mod, sad->out_mod.produced, sink); + + return 0; } -static smart_amp_proc get_smart_amp_process(struct comp_dev *dev) +static int smart_amp_fb_process(struct comp_dev *dev, + const struct audio_stream *source, + uint32_t frames, const int8_t *chan_map) { struct smart_amp_data *sad = comp_get_drvdata(dev); - enum sof_ipc_frame fmt = audio_stream_get_frm_fmt(&sad->source_buf->stream); + struct smart_amp_mod_data_base *mod = sad->mod_data; + int ret; - switch (fmt) { - case SOF_IPC_FRAME_S16_LE: - case SOF_IPC_FRAME_S24_4LE: - case SOF_IPC_FRAME_S32_LE: - return smart_amp_process; - default: - comp_err(dev, "smart_amp_process() error: not supported frame format"); - return NULL; + sad->fb_mod.consumed = 0; + + if (frames == 0) { + comp_warn(dev, "smart_amp_copy(): feedback frame size zero warning."); + return 0; } + + if (frames > SMART_AMP_FB_BUF_DB_SZ) { + comp_err(dev, "smart_amp_copy(): feedback frame size overflow: %u", frames); + sad->fb_mod.consumed = frames; + return -EINVAL; + } + + sad->fb_get_frame(&sad->fb_mod, frames, source, chan_map); + + ret = mod->mod_ops->fb_proc(mod, frames, &sad->fb_mod); + if (ret) { + comp_err(dev, "smart_amp_copy(): feedback inner model process error"); + return ret; + } + + return 0; } static int smart_amp_copy(struct comp_dev *dev) @@ -603,27 +611,39 @@ static int smart_amp_copy(struct comp_dev *dev) feedback_bytes = avail_feedback_frames * audio_stream_frame_bytes(&feedback_buf->stream); - comp_dbg(dev, "smart_amp_copy(): processing %d feedback frames (avail_passthrough_frames: %d)", + comp_dbg(dev, "smart_amp_copy(): processing %u feedback frames (avail_passthrough_frames: %u)", avail_feedback_frames, avail_passthrough_frames); /* perform buffer writeback after source_buf process */ buffer_stream_invalidate(feedback_buf, feedback_bytes); - sad->process(dev, &feedback_buf->stream, - &sink_buf->stream, avail_feedback_frames, - sad->config.feedback_ch_map, true); + smart_amp_fb_process(dev, &feedback_buf->stream, + avail_feedback_frames, + sad->config.feedback_ch_map); + + comp_dbg(dev, "smart_amp_copy(): consumed %u feedback frames", + sad->fb_mod.consumed); + if (sad->fb_mod.consumed < avail_feedback_frames) { + feedback_bytes = sad->fb_mod.consumed * + audio_stream_frame_bytes(&feedback_buf->stream); + } comp_update_buffer_consume(feedback_buf, feedback_bytes); } } - /* bytes calculation */ + /* process data */ source_bytes = avail_frames * audio_stream_frame_bytes(&source_buf->stream); - sink_bytes = avail_frames * audio_stream_frame_bytes(&sink_buf->stream); - /* process data */ buffer_stream_invalidate(source_buf, source_bytes); - sad->process(dev, &source_buf->stream, &sink_buf->stream, - avail_frames, sad->config.source_ch_map, false); + smart_amp_ff_process(dev, &source_buf->stream, &sink_buf->stream, + avail_frames, sad->config.source_ch_map); + + comp_dbg(dev, "smart_amp_copy(): processing %u feed forward frames (consumed: %u, produced: %u)", + avail_frames, sad->ff_mod.consumed, sad->out_mod.produced); + if (sad->ff_mod.consumed < avail_frames) + source_bytes = sad->ff_mod.consumed * audio_stream_frame_bytes(&source_buf->stream); + + sink_bytes = sad->out_mod.produced * audio_stream_frame_bytes(&sink_buf->stream); buffer_stream_writeback(sink_buf, sink_bytes); /* source/sink buffer pointers update */ @@ -636,24 +656,79 @@ static int smart_amp_copy(struct comp_dev *dev) static int smart_amp_reset(struct comp_dev *dev) { struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_mod_data_base *mod = sad->mod_data; + int ret; comp_dbg(dev, "smart_amp_reset()"); - sad->process = NULL; - sad->in_channels = 0; - sad->out_channels = 0; + sad->ff_get_frame = NULL; + sad->fb_get_frame = NULL; + sad->ff_set_frame = NULL; + + /* reset inner model */ + ret = mod->mod_ops->reset(mod); + if (ret) + return ret; comp_set_state(dev, COMP_TRIGGER_RESET); return 0; } +/* supported formats: {SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE} + * which are enumerated as 0, 1, and 2. Simply check if supported while fmt <= 2. + */ +static inline bool is_supported_fmt(uint16_t fmt) +{ + return fmt <= SOF_IPC_FRAME_S32_LE; +} + +static int smart_amp_resolve_mod_fmt(struct comp_dev *dev, uint32_t least_req_depth) +{ + struct smart_amp_data *sad = comp_get_drvdata(dev); + struct smart_amp_mod_data_base *mod = sad->mod_data; + const uint16_t *mod_fmts; + int num_mod_fmts; + int ret; + int i; + + /* get supported formats from mod */ + ret = mod->mod_ops->get_supported_fmts(mod, &mod_fmts, &num_mod_fmts); + if (ret) { + comp_err(dev, "smart_amp_resolve_mod_fmt(): failed to get supported formats"); + return ret; + } + + for (i = 0; i < num_mod_fmts; i++) { + if (get_sample_bitdepth(mod_fmts[i]) >= least_req_depth && + is_supported_fmt(mod_fmts[i])) { + /* set frame format to inner model */ + comp_dbg(dev, "smart_amp_resolve_mod_fmt(): set mod format to %u", + mod_fmts[i]); + ret = mod->mod_ops->set_fmt(mod, mod_fmts[i]); + if (ret) { + comp_err(dev, + "smart_amp_resolve_mod_fmt(): failed setting format %u", + mod_fmts[i]); + return ret; + } + /* return the resolved format for later settings. */ + return mod_fmts[i]; + } + } + + comp_err(dev, "smart_amp_resolve_mod_fmt(): failed to resolve the frame format for mod"); + return -EINVAL; +} + static int smart_amp_prepare(struct comp_dev *dev) { struct smart_amp_data *sad = comp_get_drvdata(dev); struct list_item *blist; + uint16_t ff_src_fmt, fb_src_fmt, resolved_mod_fmt; + uint32_t least_req_depth; + uint32_t rate; int ret; - int bitwidth; comp_dbg(dev, "smart_amp_prepare()"); @@ -674,73 +749,67 @@ static int smart_amp_prepare(struct comp_dev *dev) } } + /* sink buffer */ sad->sink_buf = list_first_item(&dev->bsink_list, struct comp_buffer, source_list); - sad->out_channels = audio_stream_get_channels(&sad->sink_buf->stream); + /* get frame format and channels param of stream and feedback source */ + ff_src_fmt = audio_stream_get_frm_fmt(&sad->source_buf->stream); + sad->ff_mod.channels = MIN(SMART_AMP_FF_MAX_CH_NUM, + audio_stream_get_channels(&sad->source_buf->stream)); + sad->out_mod.channels = sad->ff_mod.channels; + /* the least bitdepth required for inner model, which should not be lower than the bitdepth + * for input samples of feed-forward, and feedback if exists. + */ + least_req_depth = get_sample_bitdepth(ff_src_fmt); if (sad->feedback_buf) { + /* forward set channels and rate param to feedback source */ audio_stream_set_channels(&sad->feedback_buf->stream, sad->config.feedback_channels); audio_stream_set_rate(&sad->feedback_buf->stream, audio_stream_get_rate(&sad->source_buf->stream)); + fb_src_fmt = audio_stream_get_frm_fmt(&sad->feedback_buf->stream); + sad->fb_mod.channels = MIN(SMART_AMP_FB_MAX_CH_NUM, + audio_stream_get_channels(&sad->feedback_buf->stream)); - ret = smart_amp_check_audio_fmt(audio_stream_get_rate(&sad->source_buf->stream), - audio_stream_get_channels - (&sad->source_buf->stream)); - if (ret) { - comp_err(dev, "[DSM] Format not supported, sample rate: %d, ch: %d", - audio_stream_get_rate(&sad->source_buf->stream), - audio_stream_get_channels(&sad->source_buf->stream)); - goto error; - } + least_req_depth = MAX(least_req_depth, get_sample_bitdepth(fb_src_fmt)); } - switch (audio_stream_get_frm_fmt(&sad->source_buf->stream)) { - case SOF_IPC_FRAME_S16_LE: - bitwidth = 16; - break; - case SOF_IPC_FRAME_S24_4LE: - bitwidth = 24; - break; - case SOF_IPC_FRAME_S32_LE: - bitwidth = 32; - break; - default: - comp_err(dev, "[DSM] smart_amp_process() error: not supported frame format %d", - audio_stream_get_frm_fmt(&sad->source_buf->stream)); - goto error; - } + /* resolve the frame format for inner model. The return value will be the applied format + * or the negative error code. + */ + ret = smart_amp_resolve_mod_fmt(dev, least_req_depth); + if (ret < 0) + return ret; - sad->mod_handle->bitwidth = bitwidth; - comp_info(dev, "[DSM] Re-initialized for %d bit processing", bitwidth); + resolved_mod_fmt = ret; - ret = smart_amp_init(sad->mod_handle, dev); - if (ret) { - comp_err(dev, "[DSM] Re-initialization error."); - goto error; - } - ret = maxim_dsm_restore_param(sad->mod_handle, dev); - if (ret) { - comp_err(dev, "[DSM] Restoration error."); - goto error; - } + /* set format to mod buffers and get the corresponding src/sink function of channel + * remapping and format conversion. + */ + sad->ff_mod.frame_fmt = resolved_mod_fmt; + sad->out_mod.frame_fmt = resolved_mod_fmt; + sad->ff_get_frame = smart_amp_get_src_func(ff_src_fmt, sad->ff_mod.frame_fmt); + sad->ff_set_frame = smart_amp_get_sink_func(ff_src_fmt, sad->out_mod.frame_fmt); + comp_dbg(dev, "smart_amp_prepare(): ff mod buffer channels:%u fmt_conv:%u -> %u", + sad->ff_mod.channels, ff_src_fmt, sad->ff_mod.frame_fmt); + comp_dbg(dev, "smart_amp_prepare(): output mod buffer channels:%u fmt_conv:%u -> %u", + sad->out_mod.channels, sad->out_mod.frame_fmt, ff_src_fmt); - sad->process = get_smart_amp_process(dev); - if (!sad->process) { - comp_err(dev, "smart_amp_prepare(): get_smart_amp_process failed"); - ret = -EINVAL; + if (sad->feedback_buf) { + sad->fb_mod.frame_fmt = resolved_mod_fmt; + sad->fb_get_frame = smart_amp_get_src_func(fb_src_fmt, sad->fb_mod.frame_fmt); + comp_dbg(dev, "smart_amp_prepare(): fb mod buffer channels:%u fmt_conv:%u -> %u", + sad->fb_mod.channels, fb_src_fmt, sad->fb_mod.frame_fmt); } - -error: - smart_amp_flush(sad->mod_handle, dev); - return ret; + return 0; } static const struct comp_driver comp_smart_amp = { .type = SOF_COMP_SMART_AMP, - .uid = SOF_RT_UUID(maxim_dsm_comp_uuid), - .tctx = &maxim_dsm_comp_tr, + .uid = SOF_RT_UUID(smart_amp_comp_uuid), + .tctx = &smart_amp_comp_tr, .ops = { .create = smart_amp_new, .free = smart_amp_free, diff --git a/src/audio/smart_amp/smart_amp_generic.c b/src/audio/smart_amp/smart_amp_generic.c index 35dffeb0e449..5f6a1698d16b 100644 --- a/src/audio/smart_amp/smart_amp_generic.c +++ b/src/audio/smart_amp/smart_amp_generic.c @@ -3,200 +3,357 @@ // Copyright(c) 2020 Maxim Integrated All rights reserved. // // Author: Ryan Lee +// +// Copyright(c) 2023 Google LLC. +// +// Author: Pin-chih Lin #include #include #include #include -#ifdef CONFIG_GENERIC - -static int32_t smart_amp_ff_generic(int32_t x) +static void remap_s32_to_s32(struct smart_amp_mod_stream *src_mod, uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map) { - /* Add speaker protection feed forward process here */ - return x; + int num_samples_remaining; + int nmax, n, ch, i; + int n_mod = 0; + int src_ch = audio_stream_get_channels(src); + int32_t *src_ptr_base = audio_stream_get_rptr(src); + int32_t *mod_ptr_base = (int32_t *)src_mod->buf.data; + int32_t *src_ptr; + int32_t *mod_ptr; + + /* clean up dst buffer to make sure all 0s on the unmapped channel */ + bzero(mod_ptr_base, src_mod->buf.size); + + num_samples_remaining = frames * src_ch; + while (num_samples_remaining) { + nmax = audio_stream_samples_without_wrap_s32(src, src_ptr_base); + n = MIN(num_samples_remaining, nmax); + + for (ch = 0; ch < src_mod->channels; ch++) { + if (chan_map[ch] == -1) + continue; + + mod_ptr = mod_ptr_base + ch; + src_ptr = src_ptr_base + chan_map[ch]; + n_mod = 0; + for (i = 0; i < n; i += src_ch) { + *mod_ptr = *src_ptr; + mod_ptr += src_mod->channels; + src_ptr += src_ch; + n_mod += src_mod->channels; + } + } + /* update base pointers by forwarding (n / src_ch) frames */ + mod_ptr_base += n_mod; + src_ptr_base += n; + + num_samples_remaining -= n; + src_ptr_base = audio_stream_wrap(src, src_ptr_base); + } } -static void smart_amp_fb_generic(int32_t x) +static void remap_s24_to_s24(struct smart_amp_mod_stream *src_mod, uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map) { - /* Add speaker protection feedback process here */ + remap_s32_to_s32(src_mod, frames, src, chan_map); } -#if CONFIG_FORMAT_S16LE -static void smart_amp_s16_ff_default(const struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - const struct audio_stream *feedback, - uint32_t frames) -{ - int16_t *x; - int16_t *y; - int32_t tmp; - int idx; - int ch; +static void remap_s24_to_s32(struct smart_amp_mod_stream *src_mod, uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map) +{ int i; - int nch = audio_stream_get_channels(source); - - for (ch = 0; ch < nch; ch++) { - idx = ch; - for (i = 0; i < frames; i++) { - x = audio_stream_read_frag_s16(source, idx); - y = audio_stream_read_frag_s16(sink, idx); - tmp = smart_amp_ff_generic(*x << 16); - *y = sat_int16(Q_SHIFT_RND(tmp, 31, 15)); - idx += nch; - } + int n_mod = frames * src_mod->channels; + int32_t *mod_ptr = (int32_t *)src_mod->buf.data; + + remap_s32_to_s32(src_mod, frames, src, chan_map); + + /* one loop for in-place lshift (s24-to-s32) after remapping */ + for (i = 0; i < n_mod; i++) { + *mod_ptr = *mod_ptr << 8; + mod_ptr++; } } -#endif /* CONFIG_FORMAT_S16LE */ -#if CONFIG_FORMAT_S24LE -static void smart_amp_s24_ff_default(const struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - const struct audio_stream *feedback, - uint32_t frames) -{ - int32_t *x; - int32_t *y; - int32_t tmp; - int idx; - int ch; - int i; - int nch = audio_stream_get_channels(source); - - for (ch = 0; ch < nch; ch++) { - idx = ch; - for (i = 0; i < frames; i++) { - x = audio_stream_read_frag_s32(source, idx); - y = audio_stream_read_frag_s32(sink, idx); - tmp = smart_amp_ff_generic(*x << 8); - *y = sat_int24(Q_SHIFT_RND(tmp, 31, 23)); - idx += nch; +static void remap_s16_to_s16(struct smart_amp_mod_stream *src_mod, uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map) +{ + int num_samples_remaining; + int nmax, n, ch, i; + int n_mod = 0; + int src_ch = audio_stream_get_channels(src); + int16_t *src_ptr_base = audio_stream_get_rptr(src); + int16_t *mod_ptr_base = (int16_t *)src_mod->buf.data; + int16_t *src_ptr; + int16_t *mod_ptr; + + /* clean up mod buffer (dst) to keep all-0 data on the unmapped channel */ + bzero(mod_ptr_base, src_mod->buf.size); + + num_samples_remaining = frames * src_ch; + while (num_samples_remaining) { + nmax = audio_stream_samples_without_wrap_s16(src, src_ptr_base); + n = MIN(num_samples_remaining, nmax); + + for (ch = 0; ch < src_mod->channels; ch++) { + if (chan_map[ch] == -1) + continue; + + mod_ptr = mod_ptr_base + ch; + src_ptr = src_ptr_base + chan_map[ch]; + n_mod = 0; + for (i = 0; i < n; i += src_ch) { + *mod_ptr = *src_ptr; + mod_ptr += src_mod->channels; + src_ptr += src_ch; + n_mod += src_mod->channels; + } } + /* update base pointers by forwarding (n / src_ch) frames */ + mod_ptr_base += n_mod; + src_ptr_base += n; + + num_samples_remaining -= n; + src_ptr_base = audio_stream_wrap(src, src_ptr_base); } } -#endif /* CONFIG_FORMAT_S24LE */ -#if CONFIG_FORMAT_S32LE -static void smart_amp_s32_ff_default(const struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - const struct audio_stream *feedback, - uint32_t frames) -{ - int32_t *x; - int32_t *y; - int idx; - int ch; - int i; - int nch = audio_stream_get_channels(source); - - for (ch = 0; ch < nch; ch++) { - idx = ch; - for (i = 0; i < frames; i++) { - x = audio_stream_read_frag_s32(source, idx); - y = audio_stream_read_frag_s32(sink, idx); - *y = smart_amp_ff_generic(*x); - idx += nch; +static void remap_s16_to_b32(struct smart_amp_mod_stream *src_mod, uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map, int lshift) +{ + int num_samples_remaining; + int nmax, n, ch, i; + int n_mod = 0; + int src_ch = audio_stream_get_channels(src); + int16_t *src_ptr_base = audio_stream_get_rptr(src); + int32_t *mod_ptr_base = (int32_t *)src_mod->buf.data; + int16_t *src_ptr; + int32_t *mod_ptr; + + /* clean up dst buffer to make sure all 0s on the unmapped channel */ + bzero(mod_ptr_base, src_mod->buf.size); + + num_samples_remaining = frames * src_ch; + while (num_samples_remaining) { + nmax = audio_stream_samples_without_wrap_s16(src, src_ptr_base); + n = MIN(num_samples_remaining, nmax); + + for (ch = 0; ch < src_mod->channels; ch++) { + if (chan_map[ch] == -1) + continue; + + mod_ptr = mod_ptr_base + ch; + src_ptr = src_ptr_base + chan_map[ch]; + n_mod = 0; + for (i = 0; i < n; i += src_ch) { + *mod_ptr = (int32_t)*src_ptr << lshift; + mod_ptr += src_mod->channels; + src_ptr += src_ch; + n_mod += src_mod->channels; + } } + /* update base pointers by forwarding (n / src_ch) frames */ + mod_ptr_base += n_mod; + src_ptr_base += n; + + num_samples_remaining -= n; + src_ptr_base = audio_stream_wrap(src, src_ptr_base); } } -#endif /* CONFIG_FORMAT_S32LE */ -#if CONFIG_FORMAT_S16LE -static void smart_amp_s16_fb_default(const struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - const struct audio_stream *feedback, - uint32_t frames) -{ - int16_t *x; - int idx; - int ch; - int i; - int nch = audio_stream_get_channels(source); - - for (ch = 0; ch < nch; ch++) { - idx = ch; - for (i = 0; i < frames; i++) { - x = audio_stream_read_frag_s16(feedback, idx); - smart_amp_fb_generic(*x << 16); - idx += nch; +static void remap_s16_to_s24(struct smart_amp_mod_stream *src_mod, uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map) +{ + remap_s16_to_b32(src_mod, frames, src, chan_map, 8 /* lshift */); +} + +static void remap_s16_to_s32(struct smart_amp_mod_stream *src_mod, uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map) +{ + remap_s16_to_b32(src_mod, frames, src, chan_map, 16 /* lshift */); +} + +static void feed_s32_to_s32(const struct smart_amp_mod_stream *sink_mod, uint32_t frames, + const struct audio_stream __sparse_cache *sink) +{ + int num_samples_remaining; + int nmax, n, ch, i; + int sink_ch = audio_stream_get_channels(sink); + int feed_channels = MIN(sink_ch, sink_mod->channels); + int32_t *sink_ptr = audio_stream_get_wptr(sink); + int32_t *mod_ptr = (int32_t *)sink_mod->buf.data; + + num_samples_remaining = frames * sink_ch; + while (num_samples_remaining) { + nmax = audio_stream_samples_without_wrap_s32(sink, sink_ptr); + n = MIN(num_samples_remaining, nmax); + + for (i = 0; i < n; i += sink_ch) { + for (ch = 0; ch < feed_channels; ch++) + *(sink_ptr + ch) = *(mod_ptr + ch); + + sink_ptr += sink_ch; + mod_ptr += sink_mod->channels; } + + num_samples_remaining -= n; + sink_ptr = audio_stream_wrap(sink, sink_ptr); } } -#endif /* CONFIG_FORMAT_S16LE */ -#if CONFIG_FORMAT_S24LE -static void smart_amp_s24_fb_default(const struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - const struct audio_stream *feedback, - uint32_t frames) -{ - int32_t *x; - int idx; - int ch; +static void feed_s24_to_s24(const struct smart_amp_mod_stream *sink_mod, uint32_t frames, + const struct audio_stream __sparse_cache *sink) +{ + feed_s32_to_s32(sink_mod, frames, sink); +} + +static void feed_s32_to_s24(const struct smart_amp_mod_stream *sink_mod, uint32_t frames, + const struct audio_stream __sparse_cache *sink) +{ int i; - int nch = audio_stream_get_channels(source); - - for (ch = 0; ch < nch; ch++) { - idx = ch; - for (i = 0; i < frames; i++) { - x = audio_stream_read_frag_s32(feedback, idx); - smart_amp_fb_generic(*x << 8); - idx += nch; + int sink_ch = audio_stream_get_channels(sink); + int n_mod = frames * sink_mod->channels; + int32_t *mod_ptr = (int32_t *)sink_mod->buf.data; + + /* one loop for in-place rshift (s32-to-s24) before feeding */ + for (i = 0; i < n_mod; i++) { + *mod_ptr = sat_int24(Q_SHIFT_RND(*mod_ptr, 31, 23)); + mod_ptr++; + } + + feed_s32_to_s32(sink_mod, frames, sink); +} + +static void feed_s16_to_s16(const struct smart_amp_mod_stream *sink_mod, uint32_t frames, + const struct audio_stream __sparse_cache *sink) +{ + int num_samples_remaining; + int nmax, n, ch, i; + int sink_ch = audio_stream_get_channels(sink); + int feed_channels = MIN(sink_ch, sink_mod->channels); + int16_t *sink_ptr = audio_stream_get_wptr(sink); + int16_t *mod_ptr = (int16_t *)sink_mod->buf.data; + + num_samples_remaining = frames * sink_ch; + while (num_samples_remaining) { + nmax = audio_stream_samples_without_wrap_s16(sink, sink_ptr); + n = MIN(num_samples_remaining, nmax); + + for (i = 0; i < n; i += sink_ch) { + for (ch = 0; ch < feed_channels; ch++) + *(sink_ptr + ch) = *(mod_ptr + ch); + + sink_ptr += sink_ch; + mod_ptr += sink_mod->channels; } + + num_samples_remaining -= n; + sink_ptr = audio_stream_wrap(sink, sink_ptr); } } -#endif /* CONFIG_FORMAT_S24LE */ -#if CONFIG_FORMAT_S32LE -static void smart_amp_s32_fb_default(const struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - const struct audio_stream *feedback, - uint32_t frames) -{ - int32_t *x; - int idx; - int ch; - int i; - int nch = audio_stream_get_channels(source); - - for (ch = 0; ch < nch; ch++) { - idx = ch; - for (i = 0; i < frames; i++) { - x = audio_stream_read_frag_s32(feedback, idx); - smart_amp_ff_generic(*x); - idx += nch; +static void feed_b32_to_s16(const struct smart_amp_mod_stream *sink_mod, uint32_t frames, + const struct audio_stream __sparse_cache *sink, int mod_fbits) +{ + int num_samples_remaining; + int nmax, n, ch, i; + int sink_ch = audio_stream_get_channels(sink); + int feed_channels = MIN(sink_ch, sink_mod->channels); + int16_t *sink_ptr = audio_stream_get_wptr(sink); + int32_t *mod_ptr = (int32_t *)sink_mod->buf.data; + + num_samples_remaining = frames * sink_ch; + while (num_samples_remaining) { + nmax = audio_stream_samples_without_wrap_s16(sink, sink_ptr); + n = MIN(num_samples_remaining, nmax); + + for (i = 0; i < n; i += sink_ch) { + for (ch = 0; ch < feed_channels; ch++) { + *(sink_ptr + ch) = sat_int16(Q_SHIFT_RND(*(mod_ptr + ch), + mod_fbits, 15)); + } + sink_ptr += sink_ch; + mod_ptr += sink_mod->channels; } + + num_samples_remaining -= n; + sink_ptr = audio_stream_wrap(sink, sink_ptr); } } -#endif /* CONFIG_FORMAT_S32LE */ -const struct smart_amp_func_map smart_amp_function_map[] = { -/* { SOURCE_FORMAT , PROCESSING FUNCTION } */ +static void feed_s24_to_s16(const struct smart_amp_mod_stream *sink_mod, uint32_t frames, + const struct audio_stream __sparse_cache *sink) +{ + feed_b32_to_s16(sink_mod, frames, sink, 23 /* mod_fbits */); +} + +static void feed_s32_to_s16(const struct smart_amp_mod_stream *sink_mod, uint32_t frames, + const struct audio_stream __sparse_cache *sink) +{ + feed_b32_to_s16(sink_mod, frames, sink, 31 /* mod_fbits */); +} + +const struct smart_amp_func_map src_sink_func_map[] = { + /* { comp_fmt, mod_fmt, src_func, sink_func } + * cases are valid only if comp_fmt <= mod_fmt + */ #if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, smart_amp_s16_ff_default }, -#endif /* CONFIG_FORMAT_S16LE */ + { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, &remap_s16_to_s16, &feed_s16_to_s16 }, + #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, smart_amp_s24_ff_default }, -#endif /* CONFIG_FORMAT_S24LE */ + { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, &remap_s16_to_s24, &feed_s24_to_s16 }, +#endif /* CONFIG_FORMAT_S24LE */ + #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, smart_amp_s32_ff_default }, -#endif /* CONFIG_FORMAT_S32LE */ -#if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, smart_amp_s16_fb_default }, -#endif /* CONFIG_FORMAT_S16LE */ + { SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, &remap_s16_to_s32, &feed_s32_to_s16 }, +#endif /* CONFIG_FORMAT_S32LE */ +#endif /* CONFIG_FORMAT_S16LE */ + #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, smart_amp_s24_fb_default }, -#endif /* CONFIG_FORMAT_S24LE */ + { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, &remap_s24_to_s24, &feed_s24_to_s24 }, + +#if CONFIG_FORMAT_S32LE + { SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, &remap_s24_to_s32, &feed_s32_to_s24 }, +#endif /* CONFIG_FORMAT_S32LE */ +#endif /* CONFIG_FORMAT_S24LE */ + #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, smart_amp_s32_fb_default }, -#endif /* CONFIG_FORMAT_S32LE */ + { SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, &remap_s32_to_s32, &feed_s32_to_s32 }, +#endif /* CONFIG_FORMAT_S32LE */ }; -const size_t smart_amp_func_count = ARRAY_SIZE(smart_amp_function_map); -#endif +smart_amp_src_func smart_amp_get_src_func(uint16_t comp_fmt, uint16_t mod_fmt) +{ + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(src_sink_func_map); i++) { + if (comp_fmt == src_sink_func_map[i].comp_fmt && + mod_fmt == src_sink_func_map[i].mod_fmt) + return src_sink_func_map[i].src_func; + } + + return NULL; +} + +smart_amp_sink_func smart_amp_get_sink_func(uint16_t comp_fmt, uint16_t mod_fmt) +{ + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(src_sink_func_map); i++) { + if (comp_fmt == src_sink_func_map[i].comp_fmt && + mod_fmt == src_sink_func_map[i].mod_fmt) + return src_sink_func_map[i].sink_func; + } + + return NULL; +} diff --git a/src/audio/smart_amp/smart_amp_maxim_dsm.c b/src/audio/smart_amp/smart_amp_maxim_dsm.c index 165d159678dc..e45620248dd4 100644 --- a/src/audio/smart_amp/smart_amp_maxim_dsm.c +++ b/src/audio/smart_amp/smart_amp_maxim_dsm.c @@ -20,8 +20,119 @@ #include #include "dsm_api_public.h" -static int maxim_dsm_init(struct smart_amp_mod_struct_t *hspk, struct comp_dev *dev) +/* Maxim DSM(Dynamic Speaker Management) process buffer size */ +#define DSM_FRM_SZ 48 +#define DSM_FF_BUF_SZ (DSM_FRM_SZ * SMART_AMP_FF_MAX_CH_NUM) +#define DSM_FB_BUF_SZ (DSM_FRM_SZ * SMART_AMP_FB_MAX_CH_NUM) + +#define DSM_FF_BUF_DB_SZ (DSM_FF_BUF_SZ * SMART_AMP_FF_MAX_CH_NUM) +#define DSM_FB_BUF_DB_SZ (DSM_FB_BUF_SZ * SMART_AMP_FB_MAX_CH_NUM) + +/* DSM parameter table structure + * +--------------+-----------------+---------------------------------+ + * | ID (4 bytes) | VALUE (4 bytes) | 1st channel : | + * | | | 8 bytes per single parameter | + * +--------------+-----------------+---------------------------------+ + * | ... | ... | Repeat N times for N parameters | + * +--------------+-----------------+---------------------------------+ + * | ID (4 bytes) | VALUE (4 bytes) | 2nd channel : | + * | | | 8 bytes per single parameter | + * +--------------+-----------------+---------------------------------+ + * | ... | ... | Repeat N times for N parameters | + * +--------------+-----------------+---------------------------------+ + */ +enum dsm_param { + DSM_PARAM_ID = 0, + DSM_PARAM_VALUE, + DSM_PARAM_MAX +}; + +#define DSM_SINGLE_PARAM_SZ (DSM_PARAM_MAX * SMART_AMP_FF_MAX_CH_NUM) + +static const int supported_fmt_count = 3; +static const uint16_t supported_fmts[] = { + SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE +}; + +union maxim_dsm_buf { + int16_t *buf16; + int32_t *buf32; +}; + +struct maxim_dsm_ff_buf_struct_t { + int32_t *buf; + int avail; +}; + +struct maxim_dsm_fb_buf_struct_t { + int32_t *buf; + int avail; + int rdy; +}; + +struct smart_amp_buf_struct_t { + /* buffer : feed forward process input */ + int32_t *input; + /* buffer : feed forward process output */ + int32_t *output; + /* buffer : feedback voltage */ + int32_t *voltage; + /* buffer : feedback current */ + int32_t *current; + /* buffer : feed forward variable length -> fixed length */ + struct maxim_dsm_ff_buf_struct_t ff; + /* buffer : feed forward variable length <- fixed length */ + struct maxim_dsm_ff_buf_struct_t ff_out; + /* buffer : feedback variable length -> fixed length */ + struct maxim_dsm_fb_buf_struct_t fb; +}; + +struct param_buf_struct_t { + int id; + int value; +}; + +struct smart_amp_caldata { + uint32_t data_size; /* size of component's model data */ + void *data; /* model data pointer */ + uint32_t data_pos; /* data position for read/write */ +}; + +struct smart_amp_param_struct_t { + struct param_buf_struct_t param; /* variable to keep last parameter ID/value */ + struct smart_amp_caldata caldata; /* model data buffer */ + int pos; /* data position for read/write */ + int max_param; /* keep max number of DSM parameters */ +}; + +/* self-declared inner model data struct */ +struct smart_amp_mod_struct_t { + struct smart_amp_mod_data_base base; + struct smart_amp_buf_struct_t buf; + void *dsmhandle; + /* DSM variables for the initialization */ + int delayedsamples[SMART_AMP_FF_MAX_CH_NUM << 2]; + int circularbuffersize[SMART_AMP_FF_MAX_CH_NUM << 2]; + /* Number of samples of feed forward and feedback frame */ + int ff_fr_sz_samples; + int fb_fr_sz_samples; + int channelmask; + /* Number of channels of DSM */ + int nchannels; + /* Number of samples of feed forward channel */ + int ifsamples; + /* Number of samples of feedback channel */ + int ibsamples; + /* Number of processed samples */ + int ofsamples; + /* Channel bit dempth */ + int bitwidth; + struct smart_amp_param_struct_t param; +}; + +static int maxim_dsm_init(struct smart_amp_mod_struct_t *hspk) { + const struct comp_dev *dev = hspk->base.dev; int *circularbuffersize = hspk->circularbuffersize; int *delayedsamples = hspk->delayedsamples; struct dsm_api_init_ext_t initparam; @@ -33,11 +144,16 @@ static int maxim_dsm_init(struct smart_amp_mod_struct_t *hspk, struct comp_dev * initparam.ipdelayedsamples = delayedsamples; initparam.isamplingrate = DSM_DEFAULT_SAMPLE_RATE; + if (!hspk->dsmhandle) { + comp_err(dev, "[DSM] Initialization failed: dsmhandle not allocated"); + return -EINVAL; + } + retcode = dsm_api_init(hspk->dsmhandle, &initparam, sizeof(struct dsm_api_init_ext_t)); if (retcode != DSM_API_OK) { goto exit; - } else { + } else { hspk->ff_fr_sz_samples = initparam.off_framesizesamples; hspk->fb_fr_sz_samples = @@ -60,8 +176,62 @@ static int maxim_dsm_init(struct smart_amp_mod_struct_t *hspk, struct comp_dev * return (int)retcode; } -static int maxim_dsm_get_all_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev) +static int maxim_dsm_get_num_param(struct smart_amp_mod_struct_t *hspk) +{ + enum DSM_API_MESSAGE retcode; + int cmdblock[DSM_GET_PARAM_SZ_PAYLOAD]; + + /* Get number of parameters */ + cmdblock[DSM_GET_ID_IDX] = DSM_SET_CMD_ID(DSM_API_GET_MAXIMUM_CMD_ID); + retcode = dsm_api_get_params(hspk->dsmhandle, 1, (void *)cmdblock); + if (retcode != DSM_API_OK) + return 0; + + return MIN(DSM_DEFAULT_MAX_NUM_PARAM, cmdblock[DSM_GET_CH1_IDX]); +} + +static int maxim_dsm_get_handle_size(struct smart_amp_mod_struct_t *hspk) +{ + enum DSM_API_MESSAGE retcode; + struct dsm_api_memory_size_ext_t memsize; + int *circularbuffersize = hspk->circularbuffersize; + + memsize.ichannels = DSM_DEFAULT_NUM_CHANNEL; + memsize.ipcircbuffersizebytes = circularbuffersize; + memsize.isamplingrate = DSM_DEFAULT_SAMPLE_RATE; + memsize.omemsizerequestedbytes = 0; + memsize.numeqfilters = DSM_DEFAULT_NUM_EQ; + retcode = dsm_api_get_mem(&memsize, + sizeof(struct dsm_api_memory_size_ext_t)); + if (retcode != DSM_API_OK) + return 0; + + return memsize.omemsizerequestedbytes; +} + +static int maxim_dsm_flush(struct smart_amp_mod_struct_t *hspk) +{ + const struct comp_dev *dev = hspk->base.dev; + + memset(hspk->buf.input, 0, DSM_FF_BUF_SZ * sizeof(int32_t)); + memset(hspk->buf.output, 0, DSM_FF_BUF_SZ * sizeof(int32_t)); + memset(hspk->buf.voltage, 0, DSM_FF_BUF_SZ * sizeof(int32_t)); + memset(hspk->buf.current, 0, DSM_FF_BUF_SZ * sizeof(int32_t)); + + memset(hspk->buf.ff.buf, 0, DSM_FF_BUF_DB_SZ * sizeof(int32_t)); + memset(hspk->buf.ff_out.buf, 0, DSM_FF_BUF_DB_SZ * sizeof(int32_t)); + memset(hspk->buf.fb.buf, 0, DSM_FB_BUF_DB_SZ * sizeof(int32_t)); + + hspk->buf.ff.avail = DSM_FF_BUF_SZ; + hspk->buf.ff_out.avail = 0; + hspk->buf.fb.avail = 0; + + comp_dbg(dev, "[DSM] Reset (handle:%p)", hspk); + + return 0; +} + +static int maxim_dsm_get_all_param(struct smart_amp_mod_struct_t *hspk) { struct smart_amp_caldata *caldata = &hspk->param.caldata; int32_t *db = (int32_t *)caldata->data; @@ -96,7 +266,7 @@ static int maxim_dsm_get_all_param(struct smart_amp_mod_struct_t *hspk, } static int maxim_dsm_get_volatile_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev) + const struct comp_dev *dev) { struct smart_amp_caldata *caldata = &hspk->param.caldata; int32_t *db = (int32_t *)caldata->data; @@ -125,42 +295,10 @@ static int maxim_dsm_get_volatile_param(struct smart_amp_mod_struct_t *hspk, return 0; } -int maxim_dsm_get_param_forced(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev) -{ - struct smart_amp_caldata *caldata = &hspk->param.caldata; - int32_t *db = (int32_t *)caldata->data; - enum DSM_API_MESSAGE retcode; - int cmdblock[DSM_GET_PARAM_SZ_PAYLOAD]; - int num_param = hspk->param.max_param; - int idx; - - /* Update all parameter values from the DSM component */ - for (idx = 0 ; idx <= num_param ; idx++) { - cmdblock[0] = DSM_SET_CMD_ID(idx); - retcode = dsm_api_get_params(hspk->dsmhandle, 1, (void *)cmdblock); - if (retcode != DSM_API_OK) { - /* set zero if the parameter is not readable */ - cmdblock[DSM_GET_CH1_IDX] = 0; - cmdblock[DSM_GET_CH2_IDX] = 0; - } - /* fill the data for the 1st channel 4 byte ID + 4 byte value */ - db[idx * DSM_PARAM_MAX + DSM_PARAM_ID] = DSM_CH1_BITMASK | idx; - db[idx * DSM_PARAM_MAX + DSM_PARAM_VALUE] = cmdblock[DSM_GET_CH1_IDX]; - /* fill the data for the 2nd channel 4 byte ID + 4 byte value - * 2nd channel data have offset for num_param * DSM_PARAM_MAX - */ - db[(idx + num_param) * DSM_PARAM_MAX + DSM_PARAM_ID] = DSM_CH2_BITMASK | idx; - db[(idx + num_param) * DSM_PARAM_MAX + DSM_PARAM_VALUE] = cmdblock[DSM_GET_CH2_IDX]; - } - - return 0; -} - -int maxim_dsm_get_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata, int size) +static int maxim_dsm_get_param(struct smart_amp_mod_struct_t *hspk, + struct sof_ipc_ctrl_data *cdata, int size) { + const struct comp_dev *dev = hspk->base.dev; struct smart_amp_caldata *caldata = &hspk->param.caldata; size_t bs; int ret; @@ -203,10 +341,10 @@ int maxim_dsm_get_param(struct smart_amp_mod_struct_t *hspk, return 0; } -int maxim_dsm_set_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata) +static int maxim_dsm_set_param(struct smart_amp_mod_struct_t *hspk, + struct sof_ipc_ctrl_data *cdata) { + const struct comp_dev *dev = hspk->base.dev; struct smart_amp_param_struct_t *param = &hspk->param; struct smart_amp_caldata *caldata = &hspk->param.caldata; /* Model database */ @@ -265,9 +403,9 @@ int maxim_dsm_set_param(struct smart_amp_mod_struct_t *hspk, return 0; } -int maxim_dsm_restore_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev) +static int maxim_dsm_restore_param(struct smart_amp_mod_struct_t *hspk) { + const struct comp_dev *dev = hspk->base.dev; struct smart_amp_caldata *caldata = &hspk->param.caldata; int32_t *db = (int32_t *)caldata->data; int num_param = hspk->param.max_param; @@ -290,42 +428,74 @@ int maxim_dsm_restore_param(struct smart_amp_mod_struct_t *hspk, return 0; } -static void maxim_dsm_ff_proc(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev, void *in, void *out, - int nsamples, int szsample) +/** + * mod_ops implementation. + */ + +static int maxim_dsm_get_config(struct smart_amp_mod_data_base *mod, + struct sof_ipc_ctrl_data *cdata, uint32_t size) { - union smart_amp_buf buf, buf_out; + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; + + return maxim_dsm_get_param(hspk, cdata, size); +} + +static int maxim_dsm_set_config(struct smart_amp_mod_data_base *mod, + struct sof_ipc_ctrl_data *cdata) +{ + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; + + return maxim_dsm_set_param(hspk, cdata); +} + +static int maxim_dsm_ff_proc(struct smart_amp_mod_data_base *mod, + uint32_t frames, + struct smart_amp_mod_stream *in, + struct smart_amp_mod_stream *out) +{ + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; + union maxim_dsm_buf buf, buf_out; int16_t *input = (int16_t *)hspk->buf.input; int16_t *output = (int16_t *)hspk->buf.output; int32_t *input32 = hspk->buf.input; int32_t *output32 = hspk->buf.output; int *w_ptr = &hspk->buf.ff.avail; int *r_ptr = &hspk->buf.ff_out.avail; - bool is_16bit = szsample == 2 ? true : false; + bool is_16bit = (in->frame_fmt == SOF_IPC_FRAME_S16_LE); + int szsample = (is_16bit ? 2 : 4); + int nsamples = frames * in->channels; int remain; int idx; + int ret = 0; buf.buf16 = (int16_t *)hspk->buf.ff.buf; buf.buf32 = (int32_t *)hspk->buf.ff.buf; buf_out.buf16 = (int16_t *)hspk->buf.ff_out.buf; buf_out.buf32 = (int32_t *)hspk->buf.ff_out.buf; + /* Report all frames consumed even if buffer overflow to prevent source + * congestion. Same for frames produced to keep the stream rolling. + */ + in->consumed = frames; + out->produced = frames; + /* Current pointer(w_ptr) + number of input frames(nsamples) * must be smaller than buffer size limit */ if (*w_ptr + nsamples <= DSM_FF_BUF_DB_SZ) { if (is_16bit) memcpy_s(&buf.buf16[*w_ptr], nsamples * szsample, - in, nsamples * szsample); + in->buf.data, nsamples * szsample); else memcpy_s(&buf.buf32[*w_ptr], nsamples * szsample, - in, nsamples * szsample); + in->buf.data, nsamples * szsample); *w_ptr += nsamples; } else { - comp_warn(dev, + comp_warn(mod->dev, "[DSM] Feed Forward buffer overflow. (w_ptr : %d + %d > %d)", *w_ptr, nsamples, DSM_FF_BUF_DB_SZ); - return; + ret = -EOVERFLOW; + goto error; } /* Run DSM Feedforward process if the buffer is ready */ @@ -387,10 +557,10 @@ static void maxim_dsm_ff_proc(struct smart_amp_mod_struct_t *hspk, /* Output buffer preparation */ if (*r_ptr >= nsamples) { if (is_16bit) - memcpy_s(out, nsamples * szsample, + memcpy_s(out->buf.data, nsamples * szsample, buf_out.buf16, nsamples * szsample); else - memcpy_s(out, nsamples * szsample, + memcpy_s(out->buf.data, nsamples * szsample, buf_out.buf32, nsamples * szsample); remain = (*r_ptr - nsamples); @@ -405,47 +575,65 @@ static void maxim_dsm_ff_proc(struct smart_amp_mod_struct_t *hspk, remain * szsample); } *r_ptr -= nsamples; - } else { - memset(out, 0, nsamples * szsample); - comp_err(dev, - "[DSM] DSM FF process underrun. r_ptr : %d", - *r_ptr); + return ret; } + /* else { */ + comp_err(mod->dev, "[DSM] DSM FF process underrun. r_ptr : %d", *r_ptr); + ret = -ENODATA; + +error: + /* TODO(Maxim): undefined behavior when buffer overflow in previous code. + * It leads to early return and no sample written to output + * buffer. However the sink buffer will still writeback + * avail_frames data copied from output buffer. + */ + /* set all-zero output when buffer overflow or process underrun. */ + memset_s(out->buf.data, out->buf.size, 0, nsamples * szsample); + return ret; } -static void maxim_dsm_fb_proc(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev, void *in, - int nsamples, int szsample) +static int maxim_dsm_fb_proc(struct smart_amp_mod_data_base *mod, + uint32_t frames, + struct smart_amp_mod_stream *in) { - union smart_amp_buf buf; + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; + union maxim_dsm_buf buf; int *w_ptr = &hspk->buf.fb.avail; int16_t *v = (int16_t *)hspk->buf.voltage; int16_t *i = (int16_t *)hspk->buf.current; int32_t *v32 = hspk->buf.voltage; int32_t *i32 = hspk->buf.current; - bool is_16bit = szsample == 2 ? true : false; + bool is_16bit = (in->frame_fmt == SOF_IPC_FRAME_S16_LE); + int szsample = (is_16bit ? 2 : 4); + int nsamples = frames * in->channels; int remain; int idx; buf.buf16 = (int16_t *)hspk->buf.fb.buf; buf.buf32 = hspk->buf.fb.buf; + /* Set all frames consumed even if buffer overflow to prevent source + * congestion. + */ + in->consumed = frames; + /* Current pointer(w_ptr) + number of input frames(nsamples) * must be smaller than buffer size limit */ if (*w_ptr + nsamples <= DSM_FB_BUF_DB_SZ) { if (is_16bit) memcpy_s(&buf.buf16[*w_ptr], nsamples * szsample, - in, nsamples * szsample); + in->buf.data, nsamples * szsample); else memcpy_s(&buf.buf32[*w_ptr], nsamples * szsample, - in, nsamples * szsample); + in->buf.data, nsamples * szsample); *w_ptr += nsamples; } else { - comp_warn(dev, "[DSM] Feedback buffer overflow. w_ptr : %d", + comp_warn(mod->dev, "[DSM] Feedback buffer overflow. w_ptr : %d", *w_ptr); - return; + return -EOVERFLOW; } + /* Run DSM Feedback process if the buffer is ready */ if (*w_ptr >= DSM_FB_BUF_SZ) { if (is_16bit) { @@ -490,189 +678,99 @@ static void maxim_dsm_fb_proc(struct smart_amp_mod_struct_t *hspk, hspk->channelmask, (short *)i32, (short *)v32, &hspk->ibsamples); } -} - -int smart_amp_flush(struct smart_amp_mod_struct_t *hspk, struct comp_dev *dev) -{ - memset(hspk->buf.frame_in, 0, - SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t)); - memset(hspk->buf.frame_out, 0, - SMART_AMP_FF_BUF_DB_SZ * sizeof(int32_t)); - memset(hspk->buf.frame_iv, 0, - SMART_AMP_FB_BUF_DB_SZ * sizeof(int32_t)); - - memset(hspk->buf.input, 0, DSM_FF_BUF_SZ * sizeof(int16_t)); - memset(hspk->buf.output, 0, DSM_FF_BUF_SZ * sizeof(int16_t)); - memset(hspk->buf.voltage, 0, DSM_FF_BUF_SZ * sizeof(int16_t)); - memset(hspk->buf.current, 0, DSM_FF_BUF_SZ * sizeof(int16_t)); - - memset(hspk->buf.ff.buf, 0, DSM_FF_BUF_DB_SZ * sizeof(int32_t)); - memset(hspk->buf.ff_out.buf, 0, DSM_FF_BUF_DB_SZ * sizeof(int32_t)); - memset(hspk->buf.fb.buf, 0, DSM_FB_BUF_DB_SZ * sizeof(int32_t)); - - hspk->buf.ff.avail = DSM_FF_BUF_SZ; - hspk->buf.ff_out.avail = 0; - hspk->buf.fb.avail = 0; - - comp_dbg(dev, "[DSM] Reset (handle:%p)", hspk); - - return 0; -} - -int smart_amp_init(struct smart_amp_mod_struct_t *hspk, struct comp_dev *dev) -{ - return maxim_dsm_init(hspk, dev); -} - -int smart_amp_get_all_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev) -{ - if (maxim_dsm_get_all_param(hspk, dev) < 0) - return -EINVAL; return 0; } -int smart_amp_get_num_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev) +static int maxim_dsm_preinit(struct smart_amp_mod_data_base *mod) { - enum DSM_API_MESSAGE retcode; - int cmdblock[DSM_GET_PARAM_SZ_PAYLOAD]; + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; - /* Get number of parameters */ - cmdblock[DSM_GET_ID_IDX] = DSM_SET_CMD_ID(DSM_API_GET_MAXIMUM_CMD_ID); - retcode = dsm_api_get_params(hspk->dsmhandle, 1, (void *)cmdblock); - if (retcode != DSM_API_OK) - return 0; - - return MIN(DSM_DEFAULT_MAX_NUM_PARAM, cmdblock[DSM_GET_CH1_IDX]); -} - -int smart_amp_get_memory_size(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev) -{ - enum DSM_API_MESSAGE retcode; - struct dsm_api_memory_size_ext_t memsize; - int *circularbuffersize = hspk->circularbuffersize; - - memsize.ichannels = DSM_DEFAULT_NUM_CHANNEL; - memsize.ipcircbuffersizebytes = circularbuffersize; - memsize.isamplingrate = DSM_DEFAULT_SAMPLE_RATE; - memsize.omemsizerequestedbytes = 0; - memsize.numeqfilters = DSM_DEFAULT_NUM_EQ; - retcode = dsm_api_get_mem(&memsize, - sizeof(struct dsm_api_memory_size_ext_t)); - if (retcode != DSM_API_OK) - return 0; - - return memsize.omemsizerequestedbytes; + /* Bitwidth information is not available. Use 16bit as default. + * Re-initialize in the prepare function if ncessary + */ + hspk->bitwidth = 16; + return maxim_dsm_init(hspk); } -int smart_amp_check_audio_fmt(int sample_rate, int ch_num) +static int maxim_dsm_query_memblk_size(struct smart_amp_mod_data_base *mod, + enum smart_amp_mod_memblk blk) { - /* Return error if the format is not supported by DSM component */ - if (sample_rate != DSM_DEFAULT_SAMPLE_RATE) - return -EINVAL; - if (ch_num > DSM_DEFAULT_NUM_CHANNEL) - return -EINVAL; - - return 0; -} + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; + int ret; -static int smart_amp_get_buffer(int32_t *buf, uint32_t frames, - const struct audio_stream *stream, - int8_t *chan_map, uint32_t num_ch) -{ - int idx, ch; - uint32_t in_frag = 0; - union smart_amp_buf input, output; - int index; - - input.buf16 = audio_stream_get_rptr(stream); - input.buf32 = audio_stream_get_rptr(stream); - output.buf16 = (int16_t *)buf; - output.buf32 = (int32_t *)buf; - - switch (audio_stream_get_frm_fmt(stream)) { - case SOF_IPC_FRAME_S16_LE: - for (idx = 0 ; idx < frames ; idx++) { - for (ch = 0 ; ch < num_ch; ch++) { - if (chan_map[ch] == -1) - continue; - index = in_frag + chan_map[ch]; - input.buf16 = - audio_stream_read_frag_s16(stream, - index); - output.buf16[num_ch * idx + ch] = *input.buf16; - } - in_frag += audio_stream_get_channels(stream); - } + switch (blk) { + case MOD_MEMBLK_PRIVATE: + /* Memory size for private data block - dsmhandle */ + ret = maxim_dsm_get_handle_size(hspk); + if (ret <= 0) + comp_err(mod->dev, "[DSM] Get handle size error"); break; - case SOF_IPC_FRAME_S24_4LE: - case SOF_IPC_FRAME_S32_LE: - for (idx = 0 ; idx < frames ; idx++) { - for (ch = 0 ; ch < num_ch ; ch++) { - if (chan_map[ch] == -1) - continue; - index = in_frag + chan_map[ch]; - input.buf32 = - audio_stream_read_frag_s32(stream, - index); - output.buf32[num_ch * idx + ch] = *input.buf32; - } - in_frag += audio_stream_get_channels(stream); + case MOD_MEMBLK_FRAME: + /* Memory size for frame buffer block - smart_amp_buf_struct_t */ + /* smart_amp_buf_struct_t -> input, output, voltage, current */ + ret = 4 * DSM_FF_BUF_SZ * sizeof(int32_t); + /* smart_amp_buf_struct_t -> ff, ff_out, fb */ + ret += 2 * DSM_FF_BUF_DB_SZ * sizeof(int32_t) + DSM_FB_BUF_DB_SZ * sizeof(int32_t); + break; + case MOD_MEMBLK_PARAM: + /* Memory size for param blob block - caldata */ + /* Get the max. number of parameter to allocate memory for model data */ + ret = maxim_dsm_get_num_param(hspk); + if (ret < 0) { + comp_err(mod->dev, "[DSM] Get parameter size error"); + return -EINVAL; } + hspk->param.max_param = ret; + ret = hspk->param.max_param * DSM_SINGLE_PARAM_SZ * sizeof(int32_t); break; default: - return -EINVAL; + ret = -EINVAL; + break; } - return 0; + + return ret; } -static int smart_amp_put_buffer(int32_t *buf, uint32_t frames, - const struct audio_stream *stream, - int8_t *chan_map, uint32_t num_ch_in, - uint32_t num_ch_out) +static int maxim_dsm_set_memblk(struct smart_amp_mod_data_base *mod, + enum smart_amp_mod_memblk blk, + struct smart_amp_buf *buf) { - union smart_amp_buf input, output; - uint32_t out_frag = 0; - int idx, ch; - - input.buf16 = (int16_t *)buf; - input.buf32 = (int32_t *)buf; - output.buf16 = audio_stream_get_wptr(stream); - output.buf32 = audio_stream_get_wptr(stream); - - switch (audio_stream_get_frm_fmt(stream)) { - case SOF_IPC_FRAME_S16_LE: - for (idx = 0 ; idx < frames ; idx++) { - for (ch = 0 ; ch < num_ch_out; ch++) { - if (chan_map[ch] == -1) { - out_frag++; - continue; - } - output.buf16 = - audio_stream_write_frag_s16(stream, - out_frag); - *output.buf16 = input.buf16[num_ch_in * idx + ch]; - out_frag++; - } - } + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; + int32_t *mem_ptr; + + switch (blk) { + case MOD_MEMBLK_PRIVATE: + /* Assign memory to private data */ + hspk->dsmhandle = buf->data; + bzero(hspk->dsmhandle, buf->size); break; - case SOF_IPC_FRAME_S24_4LE: - case SOF_IPC_FRAME_S32_LE: - for (idx = 0 ; idx < frames ; idx++) { - for (ch = 0 ; ch < num_ch_out; ch++) { - if (chan_map[ch] == -1) { - out_frag++; - continue; - } - output.buf32 = - audio_stream_write_frag_s32(stream, - out_frag); - *output.buf32 = input.buf32[num_ch_in * idx + ch]; - out_frag++; - } - } + case MOD_MEMBLK_FRAME: + /* Assign memory to frame buffers */ + mem_ptr = (int32_t *)buf->data; + hspk->buf.input = mem_ptr; + mem_ptr += DSM_FF_BUF_SZ; + hspk->buf.output = mem_ptr; + mem_ptr += DSM_FF_BUF_SZ; + hspk->buf.voltage = mem_ptr; + mem_ptr += DSM_FF_BUF_SZ; + hspk->buf.current = mem_ptr; + mem_ptr += DSM_FF_BUF_SZ; + hspk->buf.ff.buf = mem_ptr; + mem_ptr += DSM_FF_BUF_DB_SZ; + hspk->buf.ff_out.buf = mem_ptr; + mem_ptr += DSM_FF_BUF_DB_SZ; + hspk->buf.fb.buf = mem_ptr; + break; + case MOD_MEMBLK_PARAM: + /* Assign memory to config caldata */ + hspk->param.caldata.data = buf->data; + hspk->param.caldata.data_size = buf->size; + bzero(hspk->param.caldata.data, hspk->param.caldata.data_size); + hspk->param.caldata.data_pos = 0; + + /* update full parameter values */ + if (maxim_dsm_get_all_param(hspk) < 0) + return -EINVAL; break; default: return -EINVAL; @@ -680,110 +778,71 @@ static int smart_amp_put_buffer(int32_t *buf, uint32_t frames, return 0; } -int smart_amp_ff_copy(struct comp_dev *dev, uint32_t frames, - const struct audio_stream *source, - const struct audio_stream *sink, int8_t *chan_map, - struct smart_amp_mod_struct_t *hspk, - uint32_t num_ch_in, uint32_t num_ch_out) +static int maxim_dsm_get_supported_fmts(struct smart_amp_mod_data_base *mod, + const uint16_t **mod_fmts, int *num_mod_fmts) +{ + *num_mod_fmts = supported_fmt_count; + *mod_fmts = supported_fmts; + return 0; +} + +static int maxim_dsm_set_fmt(struct smart_amp_mod_data_base *mod, uint16_t mod_fmt) { + struct smart_amp_mod_struct_t *hspk = (struct smart_amp_mod_struct_t *)mod; int ret; - if (frames == 0) { - comp_warn(dev, "[DSM] feed forward frame size zero warning."); - return 0; - } + comp_dbg(mod->dev, "[DSM] smart_amp_mod_set_fmt(): %u", mod_fmt); - if (frames > SMART_AMP_FF_BUF_DB_SZ) { - comp_err(dev, "[DSM] feed forward frame size overflow : %d", - frames); - return -EINVAL; - } + hspk->bitwidth = get_sample_bitdepth(mod_fmt); - num_ch_in = MIN(num_ch_in, SMART_AMP_FF_MAX_CH_NUM); - num_ch_out = MIN(num_ch_out, SMART_AMP_FF_OUT_MAX_CH_NUM); - - ret = smart_amp_get_buffer(hspk->buf.frame_in, - frames, source, chan_map, - num_ch_in); - if (ret) - goto err; - - switch (audio_stream_get_frm_fmt(source)) { - case SOF_IPC_FRAME_S16_LE: - maxim_dsm_ff_proc(hspk, dev, - hspk->buf.frame_in, - hspk->buf.frame_out, - frames * num_ch_in, sizeof(int16_t)); - break; - case SOF_IPC_FRAME_S24_4LE: - case SOF_IPC_FRAME_S32_LE: - maxim_dsm_ff_proc(hspk, dev, - hspk->buf.frame_in, - hspk->buf.frame_out, - frames * num_ch_in, sizeof(int32_t)); - break; - default: - ret = -EINVAL; - goto err; + ret = maxim_dsm_init(hspk); + if (ret) { + comp_err(mod->dev, "[DSM] Re-initialization error."); + goto error; + } + ret = maxim_dsm_restore_param(hspk); + if (ret) { + comp_err(mod->dev, "[DSM] Restoration error."); + goto error; } - ret = smart_amp_put_buffer(hspk->buf.frame_out, - frames, sink, chan_map, - MIN(num_ch_in, SMART_AMP_FF_MAX_CH_NUM), - MIN(num_ch_out, SMART_AMP_FF_OUT_MAX_CH_NUM)); - if (ret) - goto err; - - return 0; -err: - comp_err(dev, "[DSM] Not supported frame format"); +error: + maxim_dsm_flush(hspk); return ret; } -int smart_amp_fb_copy(struct comp_dev *dev, uint32_t frames, - const struct audio_stream *source, - const struct audio_stream *sink, int8_t *chan_map, - struct smart_amp_mod_struct_t *hspk, - uint32_t num_ch) +static int maxim_dsm_reset(struct smart_amp_mod_data_base *mod) { - int ret; - - if (frames == 0) { - comp_warn(dev, "[DSM] feedback frame size zero warning."); - return 0; - } - - if (frames > SMART_AMP_FB_BUF_DB_SZ) { - comp_err(dev, "[DSM] feedback frame size overflow : %d", - frames); - return -EINVAL; - } + /* no-op for reset */ + return 0; +} - num_ch = MIN(num_ch, SMART_AMP_FB_MAX_CH_NUM); +static const struct inner_model_ops maxim_dsm_ops = { + .init = maxim_dsm_preinit, + .query_memblk_size = maxim_dsm_query_memblk_size, + .set_memblk = maxim_dsm_set_memblk, + .get_supported_fmts = maxim_dsm_get_supported_fmts, + .set_fmt = maxim_dsm_set_fmt, + .ff_proc = maxim_dsm_ff_proc, + .fb_proc = maxim_dsm_fb_proc, + .set_config = maxim_dsm_set_config, + .get_config = maxim_dsm_get_config, + .reset = maxim_dsm_reset +}; + +/** + * mod_data_create() implementation. + */ + +struct smart_amp_mod_data_base *mod_data_create(const struct comp_dev *dev) +{ + struct smart_amp_mod_struct_t *hspk; - ret = smart_amp_get_buffer(hspk->buf.frame_iv, - frames, source, - chan_map, num_ch); - if (ret) - goto err; + hspk = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*hspk)); + if (!hspk) + return NULL; - switch (audio_stream_get_frm_fmt(source)) { - case SOF_IPC_FRAME_S16_LE: - maxim_dsm_fb_proc(hspk, dev, hspk->buf.frame_iv, - frames * num_ch, sizeof(int16_t)); - break; - case SOF_IPC_FRAME_S24_4LE: - case SOF_IPC_FRAME_S32_LE: - maxim_dsm_fb_proc(hspk, dev, hspk->buf.frame_iv, - frames * num_ch, sizeof(int32_t)); - break; - default: - ret = -EINVAL; - goto err; - } - return 0; -err: - comp_err(dev, "[DSM] Not supported frame format : %d", - audio_stream_get_frm_fmt(source)); - return ret; + hspk->base.dev = dev; + hspk->base.mod_ops = &maxim_dsm_ops; + return &hspk->base; } diff --git a/src/audio/smart_amp/smart_amp_passthru.c b/src/audio/smart_amp/smart_amp_passthru.c new file mode 100644 index 000000000000..dd5c2fe3d789 --- /dev/null +++ b/src/audio/smart_amp/smart_amp_passthru.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2023 Google LLC. +// +// Author: Pin-chih Lin + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* self-declared inner model data struct */ +struct passthru_mod_data { + struct smart_amp_mod_data_base base; + uint16_t ff_fmt; + uint16_t fb_fmt; +}; + +static const int supported_fmt_count = 3; +static const uint16_t supported_fmts[] = { + SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE +}; + +/** + * mod ops implementation. + */ + +static int passthru_mod_init(struct smart_amp_mod_data_base *mod) +{ + comp_info(mod->dev, "[PassThru Amp] init"); + return 0; +} + +static int passthru_mod_query_memblk_size(struct smart_amp_mod_data_base *mod, + enum smart_amp_mod_memblk blk) +{ + return 0; +} + +static int passthru_mod_set_memblk(struct smart_amp_mod_data_base *mod, + enum smart_amp_mod_memblk blk, + struct smart_amp_buf *buf) +{ + return 0; +} + +static int passthru_mod_get_supported_fmts(struct smart_amp_mod_data_base *mod, + const uint16_t **mod_fmts, int *num_mod_fmts) +{ + *num_mod_fmts = supported_fmt_count; + *mod_fmts = supported_fmts; + return 0; +} + +static int passthru_mod_set_fmt(struct smart_amp_mod_data_base *mod, uint16_t mod_fmt) +{ + struct passthru_mod_data *pmd = (struct passthru_mod_data *)mod; + + comp_info(mod->dev, "[PassThru Amp] set fmt:%u", mod_fmt); + pmd->ff_fmt = mod_fmt; + pmd->fb_fmt = mod_fmt; + return 0; +} + +static int passthru_mod_ff_proc(struct smart_amp_mod_data_base *mod, + uint32_t frames, + struct smart_amp_mod_stream *in, + struct smart_amp_mod_stream *out) +{ + struct passthru_mod_data *pmd = (struct passthru_mod_data *)mod; + bool is_16bit = (pmd->ff_fmt == SOF_IPC_FRAME_S16_LE); + uint32_t szsample = (is_16bit ? 2 : 4); + uint32_t size = frames * in->channels * szsample; + + comp_dbg(mod->dev, "[PassThru Amp] bypass %u frames", frames); + + /* passthrough all frames */ + memcpy_s(out->buf.data, out->buf.size, in->buf.data, size); + in->consumed = frames; + out->produced = frames; + return 0; +} + +static int passthru_mod_fb_proc(struct smart_amp_mod_data_base *mod, + uint32_t frames, + struct smart_amp_mod_stream *in) +{ + in->consumed = frames; + return 0; +} + +static int passthru_mod_get_config(struct smart_amp_mod_data_base *mod, + struct sof_ipc_ctrl_data *cdata, uint32_t size) +{ + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->size = 0; + return 0; +} + +static int passthru_mod_set_config(struct smart_amp_mod_data_base *mod, + struct sof_ipc_ctrl_data *cdata) +{ + return 0; +} + +static int passthru_mod_reset(struct smart_amp_mod_data_base *mod) +{ + comp_info(mod->dev, "[PassThru Amp] reset"); + return 0; +} + +static const struct inner_model_ops passthru_mod_ops = { + .init = passthru_mod_init, + .query_memblk_size = passthru_mod_query_memblk_size, + .set_memblk = passthru_mod_set_memblk, + .get_supported_fmts = passthru_mod_get_supported_fmts, + .set_fmt = passthru_mod_set_fmt, + .ff_proc = passthru_mod_ff_proc, + .fb_proc = passthru_mod_fb_proc, + .set_config = passthru_mod_set_config, + .get_config = passthru_mod_get_config, + .reset = passthru_mod_reset +}; + +/** + * mod_data_create() implementation. + */ + +struct smart_amp_mod_data_base *mod_data_create(const struct comp_dev *dev) +{ + struct passthru_mod_data *mod; + + mod = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*mod)); + if (!mod) + return NULL; + + mod->base.dev = dev; + mod->base.mod_ops = &passthru_mod_ops; + return &mod->base; +} diff --git a/src/include/sof/audio/format.h b/src/include/sof/audio/format.h index 55b0c8a5debf..a33d3c60fce4 100644 --- a/src/include/sof/audio/format.h +++ b/src/include/sof/audio/format.h @@ -179,6 +179,21 @@ static inline uint32_t get_sample_bytes(enum sof_ipc_frame fmt) } } +static inline uint32_t get_sample_bitdepth(enum sof_ipc_frame fmt) +{ + switch (fmt) { + case SOF_IPC_FRAME_S16_LE: + return 16; + case SOF_IPC_FRAME_S24_4LE: + case SOF_IPC_FRAME_S24_3LE: + return 24; + case SOF_IPC_FRAME_U8: + return 8; + default: + return 32; + } +} + static inline uint32_t get_frame_bytes(enum sof_ipc_frame fmt, uint32_t channels) { diff --git a/src/include/sof/audio/smart_amp/smart_amp.h b/src/include/sof/audio/smart_amp/smart_amp.h index d19f2e00e627..6417d17726a0 100644 --- a/src/include/sof/audio/smart_amp/smart_amp.h +++ b/src/include/sof/audio/smart_amp/smart_amp.h @@ -1,8 +1,12 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2020 Maxim Integrated. All rights reserved. + * Copyright(c) 2020 Maxim Integrated All rights reserved. * * Author: Ryan Lee + * + * Copyright(c) 2023 Google LLC. + * + * Author: Pin-chih Lin */ #ifndef __SOF_AUDIO_DSM_H__ @@ -11,6 +15,46 @@ #include #include +/* Smart Amplifier component is a two-layer structured design, i.e. generic + * layer and inner model layer. The latter can have various implementations + * respectively for Amplifier solution suppliers, while the former is the common + * part of Smart Amp process adaptable for all solutions. + * + * In structural aspect, one can regard generic layer as the glue code that + * wraps inner model in a SOF component. Ops are defined for interaction between + * two layers. Inner model is the solution-specific modular code, which may have + * static libraries linked as needed. The structure is figured below: + * + * SRC(FF) SINK(OUT) +-SRC(FB) bytectl + * +- SMART_AMP |^ comp ops | ^ | ^| + * | +------------------v|-------------v---------|------v-----------|v---------+ + * | | Generic Layer (chan remap/fmt conv) || | + * | | (memory mgr)--+------> :::::::::BUFFERS::::::::::::: |+> CONFIG | + * | +------------------|-|^-----------|---------^------|-----------^|---------+ + * | | || mod ops | | | || + * | +------------------v-v|-----------v---------|------v-----------|v---------+ + * | | Inner Model :::::::::::::::::::::BUFFERS::::::::::::::::::::::: | + * | | (solution-specific impl/wrapper) |+> MODEL | + * | +------------------------------|^------------------------------^----------+ + * +--- v| lib ops | CALDATA + * Static Libs (as needed) ----------+ + * Note: + * - FF(Feed-Forward): un-processed playback frame source + * - FB(Feedback): feedback reference frame source (from the capture pipeline) + * + * As illustrated above, generic layer handles the cross-communication for inner + * model and SOF pipeline flow, as well as the smart-amp common tasks including: + * 1. Channel remapping for input/output frames + * 2. Frame format conversion for input/output frames. It allows inner model to + * work with different format from SOF audio stream. + * (Now it only allows the bitdepth of inner model format >= SOF stream, + * e.g. inner model: S32_LE, SOF stream: S16_LE) + * 3. Full-management of runtime memory. That is, dynamic memory buffers either + * required by generic layer or inner model will be allocated/owned/released + * by generic layer. + * More details are stated in the comment of each struct/function later. + */ + /* Maximum number of channels for algorithm in */ #define SMART_AMP_FF_MAX_CH_NUM 2 /* Maximum number of channels for algorithm out */ @@ -18,173 +62,261 @@ /* Maximum number of channels for feedback */ #define SMART_AMP_FB_MAX_CH_NUM 4 -#define SMART_AMP_FRM_SZ 48 /* samples per 1ms */ +#define SMART_AMP_FRM_SZ 48 /* frames per 1ms */ #define SMART_AMP_FF_BUF_SZ (SMART_AMP_FRM_SZ * SMART_AMP_FF_MAX_CH_NUM) #define SMART_AMP_FB_BUF_SZ (SMART_AMP_FRM_SZ * SMART_AMP_FB_MAX_CH_NUM) -/* Maxim DSM(Dynamic Speaker Manangement) process buffer size */ -#define DSM_FRM_SZ 48 -#define DSM_FF_BUF_SZ (DSM_FRM_SZ * SMART_AMP_FF_MAX_CH_NUM) -#define DSM_FB_BUF_SZ (DSM_FRM_SZ * SMART_AMP_FB_MAX_CH_NUM) - #define SMART_AMP_FF_BUF_DB_SZ\ (SMART_AMP_FF_BUF_SZ * SMART_AMP_FF_MAX_CH_NUM) #define SMART_AMP_FB_BUF_DB_SZ\ (SMART_AMP_FB_BUF_SZ * SMART_AMP_FB_MAX_CH_NUM) -#define DSM_FF_BUF_DB_SZ (DSM_FF_BUF_SZ * SMART_AMP_FF_MAX_CH_NUM) -#define DSM_FB_BUF_DB_SZ (DSM_FB_BUF_SZ * SMART_AMP_FB_MAX_CH_NUM) - -/* DSM parameter table structure - * +--------------+-----------------+---------------------------------+ - * | ID (4 bytes) | VALUE (4 bytes) | 1st channel : | - * | | | 8 bytes per single parameter | - * +--------------+-----------------+---------------------------------+ - * | ... | ... | Repeat N times for N parameters | - * +--------------+-----------------+---------------------------------+ - * | ID (4 bytes) | VALUE (4 bytes) | 2nd channel : | - * | | | 8 bytes per single parameter | - * +--------------+-----------------+---------------------------------+ - * | ... | ... | Repeat N times for N parameters | - * +--------------+-----------------+---------------------------------+ - */ -enum dsm_param { - DSM_PARAM_ID = 0, - DSM_PARAM_VALUE, - DSM_PARAM_MAX -}; -#define DSM_SINGLE_PARAM_SZ (DSM_PARAM_MAX * SMART_AMP_FF_MAX_CH_NUM) +struct inner_model_ops; -union smart_amp_buf { - int16_t *buf16; - int32_t *buf32; +/* The common base for inner model data structs. Inner model should declare its + * model data struct while putting this base as the leading member of struct, e.g. + * struct solution_foo_mod_data { + * struct smart_amp_mod_data_base base; + * uint32_t foo_version; + * struct foo_parameter_set; + * ... + * }; + * + * Then implement mod_data_create() in its own C file, e.g. + * struct smart_amp_mod_data_base *mod_data_create(const struct comp_dev *dev) + * { + * struct solution_foo_mod_data *foo; + * foo = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*foo)); + * assert(foo); + * foo->base.dev = dev; + * foo->base.mod_ops = foo_ops; // declared somewhere as static const + * ... + * return &foo->base; + * } + */ +struct smart_amp_mod_data_base { + const struct comp_dev *dev; /* for logger tracing use only */ + const struct inner_model_ops *mod_ops; }; -struct smart_amp_ff_buf_struct_t { - int32_t *buf; - int avail; +/* The struct of memory buffer managed by generic layer. */ +struct smart_amp_buf { + void *data; + uint32_t size; }; -struct smart_amp_fb_buf_struct_t { - int32_t *buf; - int avail; - int rdy; +/* For memory allocation, generic layer plays the active role that queries the + * required memory size for inner model (then allocate and assign back) in + * some specific moments, i.e. once before and after model initialized. Inner + * model should consider buffers located and allocated onto 3 memory blocks in + * terms of usage: + * PRIVATE - allocated before model init - for libraries internal usage + * FRAME - allocated after model init - for audio frame buffer usage + * PARAM - allocated after model init - for parameter blob usage + */ +enum smart_amp_mod_memblk { + MOD_MEMBLK_PRIVATE = 0, + MOD_MEMBLK_FRAME, + MOD_MEMBLK_PARAM, + MOD_MEMBLK_MAX }; -struct smart_amp_buf_struct_t { - /* buffer : sof -> spk protection feed forward process */ - int32_t *frame_in; - /* buffer : sof <- spk protection feed forward process */ - int32_t *frame_out; - /* buffer : sof -> spk protection feedback process */ - int32_t *frame_iv; - /* buffer : feed forward process input */ - int32_t *input; - /* buffer : feed forward process output */ - int32_t *output; - /* buffer : feedback voltage */ - int32_t *voltage; - /* buffer : feedback current */ - int32_t *current; - /* buffer : feed forward variable length -> fixed length */ - struct smart_amp_ff_buf_struct_t ff; - /* buffer : feed forward variable length <- fixed length */ - struct smart_amp_ff_buf_struct_t ff_out; - /* buffer : feedback variable length -> fixed length */ - struct smart_amp_fb_buf_struct_t fb; +/* The intermediate audio data buffer in generic layer. */ +struct smart_amp_mod_stream { + struct smart_amp_buf buf; + uint32_t channels; + uint16_t frame_fmt; + union { + uint32_t consumed; /* for source (in frames) */ + uint32_t produced; /* for sink (in frames) */ + }; }; -struct param_buf_struct_t { - int id; - int value; -}; +/****************************************************************************** + * Generic functions: * + * The implementation is placed in smart_amp_generic.c * + ******************************************************************************/ -struct smart_amp_caldata { - uint32_t data_size; /* size of component's model data */ - void *data; /* model data pointer */ - uint32_t data_pos; /* data position for read/write */ -}; +typedef void (*smart_amp_src_func)(struct smart_amp_mod_stream *src_mod, + uint32_t frames, + const struct audio_stream __sparse_cache *src, + const int8_t *chan_map); -struct smart_amp_param_struct_t { - struct param_buf_struct_t param; /* variable to keep last parameter ID/value */ - struct smart_amp_caldata caldata; /* model data buffer */ - int pos; /* data position for read/write */ - int max_param; /* keep max number of DSM parameters */ -}; +typedef void (*smart_amp_sink_func)(const struct smart_amp_mod_stream *sink_mod, + uint32_t frames, + const struct audio_stream __sparse_cache *sink); -struct smart_amp_mod_struct_t { - struct smart_amp_buf_struct_t buf; - void *dsmhandle; - /* DSM variables for the initialization */ - int delayedsamples[SMART_AMP_FF_MAX_CH_NUM << 2]; - int circularbuffersize[SMART_AMP_FF_MAX_CH_NUM << 2]; - /* Number of samples of feed forward and feedback frame */ - int ff_fr_sz_samples; - int fb_fr_sz_samples; - int channelmask; - /* Number of channels of DSM */ - int nchannels; - /* Number of samples of feed forward channel */ - int ifsamples; - /* Number of samples of feedback channel */ - int ibsamples; - /* Number of processed samples */ - int ofsamples; - /* Channel bit dempth */ - int bitwidth; - struct smart_amp_param_struct_t param; +struct smart_amp_func_map { + uint16_t comp_fmt; + uint16_t mod_fmt; + smart_amp_src_func src_func; + smart_amp_sink_func sink_func; }; -typedef void (*smart_amp_func)(const struct comp_dev *dev, - const struct audio_stream *source, - const struct audio_stream *sink, - const struct audio_stream *feedback, - uint32_t frames); -struct smart_amp_func_map { - uint16_t frame_fmt; - smart_amp_func func; +extern const struct smart_amp_func_map src_sink_func_map[]; + +smart_amp_src_func smart_amp_get_src_func(uint16_t comp_fmt, uint16_t mod_fmt); +smart_amp_sink_func smart_amp_get_sink_func(uint16_t comp_fmt, uint16_t mod_fmt); + +/****************************************************************************** + * Inner model operations (mod ops): * + * Model implementations are mutual exclusive (separated by Kconfig). It * + * must be only one solution applicable per build. The solution-specific * + * implementation is placed in its own C file smart_amp_${SOLUTION}.c * + ******************************************************************************/ + +/** + * Creates the self-declared model data struct. + * Refer to "struct smart_amp_mod_data_base" for details of declaration. + * Args: + * dev - the comp_dev object for logger tracing use only. + * Returns: + * The pointer of "smart_amp_mod_data_base" which is the leading member in + * the created model data struct. + */ +struct smart_amp_mod_data_base *mod_data_create(const struct comp_dev *dev); + +/* All ops are mandatory. */ +struct inner_model_ops { + /** + * Initializes model. + * It will be called on comp_ops.create() by generic layer. + * Args: + * mod - the pointer of model data struct object. + * Returns: + * 0 or negative error code. + */ + int (*init)(struct smart_amp_mod_data_base *mod); + + /** + * Returns the required bytesize for the specific memblk. + * It will be called on comp_ops.create() by generic layer, before or after + * mod_ops.init() according to the memblk usage. + * Args: + * mod - the pointer of model data struct object. + * blk - the memblk usage type. + * Returns: + * The bytesize or negative error code. + */ + int (*query_memblk_size)(struct smart_amp_mod_data_base *mod, + enum smart_amp_mod_memblk blk); + + /** + * Sets the allocated memblk info. + * It should be called in sequence after mod_ops.query_memblk_size(). + * Args: + * mod - the pointer of model data struct object. + * blk - the memblk usage type. + * buf - the pointer of smart_amp_buf object including allocated memory. + * Returns: + * 0 or negative error code. + */ + int (*set_memblk)(struct smart_amp_mod_data_base *mod, + enum smart_amp_mod_memblk blk, + struct smart_amp_buf *buf); + + /** + * Returns the list of supported frame formats. + * It will be called on comp_ops.prepare() by generic layer. + * + * Inner model should report all supported formats in the list at once. + * Generic layer will resolve the applicable format according to this list and + * the formats requested from external source/sink stream buffers. If it turns + * out to be no format applicable, generic layer will report errors which will + * force the early-termination of the starting process for the whole pipeline. + * Args: + * mod - the pointer of model data struct object. + * mod_fmts - the pointer for returning the supported format list. + * Format values should be aligned to "enum sof_ipc_frame" and + * put in ascending order. + * num_mod_fmts - the pointer for returning the length of mod_fmts. + * Returns: + * 0 or negative error code. + */ + int (*get_supported_fmts)(struct smart_amp_mod_data_base *mod, + const uint16_t **mod_fmts, int *num_mod_fmts); + + /** + * Sets the frame format after resolved. + * It will be called on comp_ops.prepare() by generic layer, in sequence after + * mod_ops.get_supported_fmts() if the format is resolvable. + * + * Inner model should honor the received format on processing. The FF and FB + * frames (if available) will be put to the input buffers in the same format. + * It will be the last function called before audio stream starts processing. + * needed, inner model should execute the preparing tasks as soon as it is + * called. + * Args: + * mod - the pointer of model data struct object. + * mod_fmt - the frame format to be applied.. + * Returns: + * 0 or negative error code. + */ + int (*set_fmt)(struct smart_amp_mod_data_base *mod, uint16_t mod_fmt); + + /** + * Runs the feed-forward process. + * Args: + * mod - the pointer of model data struct object. + * frames - the number of frames to be processed. + * in - the pointer of input stream buffer object. Inner model should set + * "consumed" to the number of conusmed frames. + * out - the pointer of output stream buffer object. Inner model should set + * "produced" to the number of produced frames. + * Returns: + * 0 or negative error code. + */ + int (*ff_proc)(struct smart_amp_mod_data_base *mod, + uint32_t frames, + struct smart_amp_mod_stream *in, + struct smart_amp_mod_stream *out); + + /** + * Runs the feedback process. + * Args: + * mod - the pointer of model data struct object. + * frames - the number of frames to be processed. + * in - the pointer of input stream buffer object. Inner model should set + * "consumed" to the number of conusmed frames. + * Returns: + * 0 or negative error code. + */ + int (*fb_proc)(struct smart_amp_mod_data_base *mod, + uint32_t frames, + struct smart_amp_mod_stream *in); + + /** + * Gets config data from model. + * Args: + * mod - the pointer of model data struct object. + * cdata - the pointer of sof_ipc_ctrl_data object. + * size - the maximal size for config data to read. + * Returns: + * 0 or negative error code. + */ + int (*get_config)(struct smart_amp_mod_data_base *mod, + struct sof_ipc_ctrl_data *cdata, uint32_t size); + + /** + * Sets config data to model. + * Args: + * mod - the pointer of model data struct object. + * cdata - the pointer of sof_ipc_ctrl_data object. + * Returns: + * 0 or negative error code. + */ + int (*set_config)(struct smart_amp_mod_data_base *mod, + struct sof_ipc_ctrl_data *cdata); + + /** + * Resets model. + * It will be called on comp_ops.reset() by generic layer. + * Args: + * mod - the pointer of model data struct object. + * Returns: + * 0 or negative error code. + */ + int (*reset)(struct smart_amp_mod_data_base *mod); }; -/* Component initialization */ -int smart_amp_init(struct smart_amp_mod_struct_t *hspk, struct comp_dev *dev); -/* Component memory flush */ -int smart_amp_flush(struct smart_amp_mod_struct_t *hspk, struct comp_dev *dev); -/* Feed forward processing function */ -int smart_amp_ff_copy(struct comp_dev *dev, uint32_t frames, - const struct audio_stream *source, - const struct audio_stream *sink, int8_t *chan_map, - struct smart_amp_mod_struct_t *hspk, - uint32_t num_ch_in, uint32_t num_ch_out); -/* Feedback processing function */ -int smart_amp_fb_copy(struct comp_dev *dev, uint32_t frames, - const struct audio_stream *source, - const struct audio_stream *sink, int8_t *chan_map, - struct smart_amp_mod_struct_t *hspk, - uint32_t num_ch); -/* memory usage calculation for the component */ -int smart_amp_get_memory_size(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev); -/* supported audio format check */ -int smart_amp_check_audio_fmt(int sample_rate, int ch_num); - -/* this function return number of parameters supported */ -int smart_amp_get_num_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev); -/* this function update whole parameter table */ -int smart_amp_get_all_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev); -/* parameter read function */ -int maxim_dsm_get_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata, int size); -/* parameter write function */ -int maxim_dsm_set_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev, - struct sof_ipc_ctrl_data *cdata); -/* parameter restoration */ -int maxim_dsm_restore_param(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev); -/* parameter forced read, ignore cache */ -int maxim_dsm_get_param_forced(struct smart_amp_mod_struct_t *hspk, - struct comp_dev *dev); #endif