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