From 8f51096b12ac956f96410137b7748570ee452162 Mon Sep 17 00:00:00 2001 From: "fy.tsuo" Date: Fri, 9 Apr 2021 11:13:53 +0800 Subject: [PATCH 1/2] topology: Added IGO_NR in sof-tgl-max98373-rt5682 topology. This commit added IGO_NR component into sof-tgl-max98373-rt5682.m4 as DMIC capture PCM99 pipeline. Signed-off-by: fy.tsuo --- .../platform/intel/intel-generic-dmic-kwd.m4 | 26 ++++++++++++++----- tools/topology/sof-smart-amplifier.m4 | 19 +++++++++----- tools/topology/sof-tgl-max98373-rt5682.m4 | 14 +++++++--- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/tools/topology/platform/intel/intel-generic-dmic-kwd.m4 b/tools/topology/platform/intel/intel-generic-dmic-kwd.m4 index 2e63283189ee..2ce96bcc95c9 100644 --- a/tools/topology/platform/intel/intel-generic-dmic-kwd.m4 +++ b/tools/topology/platform/intel/intel-generic-dmic-kwd.m4 @@ -42,7 +42,20 @@ ifdef(`DMIC_DAI_LINK_48k_NAME',`',define(DMIC_DAI_LINK_48k_NAME, `dmic01')) ifdef(`DMIC_DAI_LINK_16k_NAME',`',define(DMIC_DAI_LINK_16k_NAME, `dmic16k')) # DMICPROC is set by makefile, available type: passthrough/eq-iir-volume -ifdef(`DMICPROC', , `define(DMICPROC, passthrough)') +ifdef(`IGO', +`define(DMICPROC, igonr)', +`ifdef(`DMICPROC', , `define(DMICPROC, passthrough)')') + +# Prolong period to 16ms for igo_nr process +ifdef(`IGO', `define(`INTEL_GENERIC_DMIC_KWD_PERIOD', 16000)', `define(`INTEL_GENERIC_DMIC_KWD_PERIOD', 1000)') + +# PCM is fix 16k for igo_nr +ifdef(`IGO', `define(`PCM_RATE', 16000)', `define(`PCM_RATE', 48000)') + +# DMIC setting for igo_nr +ifdef(`IGO', `define(`DMIC_CONFIG_CLK_MIN', 500000)', `define(`DMIC_CONFIG_CLK_MIN', 2400000)') +ifdef(`IGO', `define(`DMIC_CONFIG_CLK_MAX', 1600000)', `define(`DMIC_CONFIG_CLK_MAX', 4800000)') +ifdef(`IGO', `define(`DMIC_CONFIG_SAMPLE_RATE', 16000)', `define(`DMIC_CONFIG_SAMPLE_RATE', 48000)') # # Define the pipelines @@ -63,11 +76,12 @@ define(`PGA_NAME', Dmic0) PIPELINE_PCM_ADD(sof/pipe-`DMICPROC'-capture.m4, DMIC_PIPELINE_48k_ID, DMIC_PCM_48k_ID, CHANNELS, s32le, - 1000, 0, 0, 48000, 48000, 48000) + INTEL_GENERIC_DMIC_KWD_PERIOD, 0, 0, PCM_RATE, PCM_RATE, PCM_RATE) undefine(`PGA_NAME') undefine(`PIPELINE_FILTER1') undefine(`PIPELINE_FILTER2') +undefine(`DMICPROC') # # KWD configuration @@ -96,7 +110,7 @@ dnl deadline, priority, core, time_domain) DAI_ADD(sof/pipe-dai-capture.m4, DMIC_PIPELINE_48k_ID, DMIC, 0, DMIC_DAI_LINK_48k_NAME, concat(`PIPELINE_SINK_', DMIC_PIPELINE_48k_ID), 2, s32le, - 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + INTEL_GENERIC_DMIC_KWD_PERIOD, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) # capture DAI is DMIC 1 using 3 periods # Buffers use s32le format, with 320 frame per 20000us on core 0 with priority 0 @@ -140,16 +154,16 @@ SectionGraph."pipe-sof-generic-keyword-detect" { dnl DAI_CONFIG(type, dai_index, link_id, name, ssp_config/dmic_config) ifelse(CHANNELS, 4, `DAI_CONFIG(DMIC, 0, DMIC_DAI_LINK_48k_ID, DMIC_DAI_LINK_48k_NAME, - DMIC_CONFIG(1, 2400000, 4800000, 40, 60, 48000, + DMIC_CONFIG(1, DMIC_CONFIG_CLK_MIN, DMIC_CONFIG_CLK_MAX, 40, 60, DMIC_CONFIG_SAMPLE_RATE, DMIC_WORD_LENGTH(s32le), 200, DMIC, 0, PDM_CONFIG(DMIC, 0, FOUR_CH_PDM0_PDM1)))', `DAI_CONFIG(DMIC, 0, DMIC_DAI_LINK_48k_ID, DMIC_DAI_LINK_48k_NAME, - DMIC_CONFIG(1, 2400000, 4800000, 40, 60, 48000, + DMIC_CONFIG(1, DMIC_CONFIG_CLK_MIN, DMIC_CONFIG_CLK_MAX, 40, 60, DMIC_CONFIG_SAMPLE_RATE, DMIC_WORD_LENGTH(s32le), 200, DMIC, 0, PDM_CONFIG(DMIC, 0, STEREO_PDM0)))') # dmic16k (ID: 2) DAI_CONFIG(DMIC, 1, DMIC_DAI_LINK_16k_ID, DMIC_DAI_LINK_16k_NAME, - DMIC_CONFIG(1, 2400000, 4800000, 40, 60, 16000, + DMIC_CONFIG(1, DMIC_CONFIG_CLK_MIN, DMIC_CONFIG_CLK_MAX, 40, 60, 16000, DMIC_WORD_LENGTH(s32le), 400, DMIC, 1, PDM_CONFIG(DMIC, 1, STEREO_PDM0))) diff --git a/tools/topology/sof-smart-amplifier.m4 b/tools/topology/sof-smart-amplifier.m4 index 37463e8686c5..7948fbed435f 100644 --- a/tools/topology/sof-smart-amplifier.m4 +++ b/tools/topology/sof-smart-amplifier.m4 @@ -88,6 +88,11 @@ ifdef(`SMART_PCM_ID',`',`fatal_error(note: Need to define PCM ID for sof-smart-a ifdef(`SMART_PCM_NAME',`',`fatal_error(note: Need to define Speaker PCM name for sof-smart-amplifier )') +#The long process time (aprox. 8ms) and process length (16ms in 16k sample rate) of igo_nr starve +#the scheduler and results in SMART_AMP underflow, ending up with smart_amp component reset and close. +#So increase the buffer size of SMART_AMP is necessary. +ifdef(`IGO', `define(`SMART_AMP_PERIOD', 16000)', `define(`SMART_AMP_PERIOD', 1000)') + ifelse(SDW, `1', ` # @@ -122,7 +127,7 @@ dnl time_domain, sched_comp) # Set 1000us deadline on core 0 with priority 0 PIPELINE_PCM_ADD(sof/pipe-smart-amplifier-playback.m4, SMART_PB_PPL_ID, SMART_PCM_ID, SMART_PB_CH_NUM, s32le, - 1000, 0, SMART_AMP_CORE, + SMART_AMP_PERIOD, 0, SMART_AMP_CORE, 48000, 48000, 48000) # Low Latency capture pipeline 2 on PCM 0 using max 2 channels of s32le. @@ -131,13 +136,13 @@ ifelse(SDW, `1', ` PIPELINE_PCM_ADD(sof/pipe-amp-ref-capture.m4, SMART_REF_PPL_ID, eval(SMART_PCM_ID + 1), SMART_REF_CH_NUM, s32le, - 1000, 0, 0, + SMART_AMP_PERIOD, 0, 0, 48000, 48000, 48000) ', ` PIPELINE_PCM_ADD(sof/pipe-amp-ref-capture.m4, SMART_REF_PPL_ID, SMART_PCM_ID, SMART_REF_CH_NUM, s32le, - 1000, 0, SMART_AMP_CORE, + SMART_AMP_PERIOD, 0, SMART_AMP_CORE, 48000, 48000, 48000) ') @@ -157,14 +162,14 @@ ifelse(SDW, `1', DAI_ADD(sof/pipe-dai-playback.m4, SMART_PB_PPL_ID, ALH, SMART_ALH_INDEX, SMART_ALH_PLAYBACK_NAME, SMART_PIPE_SOURCE, 2, s24le, - 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + SMART_AMP_PERIOD, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) # capture DAI is ALH(ALH_INDEX) using 2 periods # Buffers use s32le format, 1000us deadline on core 0 with priority 0 DAI_ADD(sof/pipe-dai-capture.m4, SMART_REF_PPL_ID, ALH, eval(SMART_ALH_INDEX + 1), SMART_ALH_CAPTURE_NAME, SMART_PIPE_SINK, 2, s24le, - 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + SMART_AMP_PERIOD, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) ', ` # playback DAI is SSP(SPP_INDEX) using 2 periods @@ -172,14 +177,14 @@ DAI_ADD(sof/pipe-dai-capture.m4, DAI_ADD(sof/pipe-dai-playback.m4, SMART_PB_PPL_ID, SSP, SMART_SSP_INDEX, SMART_SSP_NAME, SMART_PIPE_SOURCE, 2, s32le, - 1000, 0, SMART_AMP_CORE, SCHEDULE_TIME_DOMAIN_TIMER) + SMART_AMP_PERIOD, 0, SMART_AMP_CORE, SCHEDULE_TIME_DOMAIN_TIMER) # capture DAI is SSP(SSP_INDEX) using 2 periods # Buffers use s32le format, 1000us deadline on core 0 with priority 0 DAI_ADD(sof/pipe-dai-capture.m4, SMART_REF_PPL_ID, SSP, SMART_SSP_INDEX, SMART_SSP_NAME, SMART_PIPE_SINK, 2, s32le, - 1000, 0, SMART_AMP_CORE, SCHEDULE_TIME_DOMAIN_TIMER) + SMART_AMP_PERIOD, 0, SMART_AMP_CORE, SCHEDULE_TIME_DOMAIN_TIMER) ') # Connect demux to smart_amp diff --git a/tools/topology/sof-tgl-max98373-rt5682.m4 b/tools/topology/sof-tgl-max98373-rt5682.m4 index b9ae84d3410f..d139d1f94704 100644 --- a/tools/topology/sof-tgl-max98373-rt5682.m4 +++ b/tools/topology/sof-tgl-max98373-rt5682.m4 @@ -49,7 +49,13 @@ define(`SMART_SSP_NAME', concat(concat(`SSP', AMP_SSP),`-Codec')) #define BE dai_link ID define(`SMART_BE_ID', 7) #define SSP mclk -define(`SSP_MCLK', 24576000) +ifdef(`IGO', `define(`SSP_MCLK', 19200000)', `define(`SSP_MCLK', 24576000)') +#define SSP bclk +ifdef(`IGO', `define(`SSP_BCLK', 2400000)', `define(`SSP_BCLK', 3072000)') +#define SSP_TDM_WIDTH +ifdef(`IGO', `define(`SSP_TDM_WIDTH', 25)', `define(`SSP_TDM_WIDTH', 32)') +#define SSP_CONFIG_DATA_VALID_BITS +ifdef(`IGO', `define(`SSP_CONFIG_DATA_VALID_BITS', 24)', `define(`SSP_CONFIG_DATA_VALID_BITS', 32)') # Playback related define(`SMART_PB_PPL_ID', 1) define(`SMART_PB_CH_NUM', 2) @@ -230,10 +236,10 @@ dnl ssp1-maxmspk, ssp0-RTHeadset #SSP 0 (ID: 0) DAI_CONFIG(SSP, 0, 0, SSP0-Codec, SSP_CONFIG(I2S, SSP_CLOCK(mclk, SSP_MCLK, codec_mclk_in), - SSP_CLOCK(bclk, 3072000, codec_slave), + SSP_CLOCK(bclk, SSP_BCLK, codec_slave), SSP_CLOCK(fsync, 48000, codec_slave), - SSP_TDM(2, 32, 3, 3), - SSP_CONFIG_DATA(SSP, 0, 32))) + SSP_TDM(2, SSP_TDM_WIDTH, 3, 3), + SSP_CONFIG_DATA(SSP, 0, SSP_CONFIG_DATA_VALID_BITS))) # 4 HDMI/DP outputs (ID: 3,4,5,6) DAI_CONFIG(HDA, 0, 3, iDisp1, From 64d5007ebc74f4e5d9fdce0ffcfc0e7e7af35f2f Mon Sep 17 00:00:00 2001 From: "fy.tsuo" Date: Fri, 9 Apr 2021 11:09:07 +0800 Subject: [PATCH 2/2] audio: igo_nr: Added intelliGo noise reduction wrapper code and topology This commit consists of files for adding IGO_NR component in arbitrary topology. The proprietary static library shall be released by intelliGo via private channel upon request. Signed-off-by: fy.tsuo --- src/audio/CMakeLists.txt | 3 + src/audio/Kconfig | 8 + src/audio/igo_nr/CMakeLists.txt | 6 + src/audio/igo_nr/igo_nr.c | 808 +++++++++++++++++++++ src/include/sof/audio/igo_nr/igo_lib.h | 143 ++++ src/include/sof/audio/igo_nr/igo_nr_comp.h | 51 ++ src/include/user/igo_nr.h | 20 + tools/topology/m4/igo_nr.m4 | 66 ++ tools/topology/sof/pipe-igonr-capture.m4 | 90 +++ 9 files changed, 1195 insertions(+) create mode 100644 src/audio/igo_nr/CMakeLists.txt create mode 100644 src/audio/igo_nr/igo_nr.c create mode 100644 src/include/sof/audio/igo_nr/igo_lib.h create mode 100644 src/include/sof/audio/igo_nr/igo_nr_comp.h create mode 100644 src/include/user/igo_nr.h create mode 100644 tools/topology/m4/igo_nr.m4 create mode 100644 tools/topology/sof/pipe-igonr-capture.m4 diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 4b7604cc4851..320d08009105 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -76,6 +76,9 @@ if(NOT CONFIG_LIBRARY) if(CONFIG_COMP_CODEC_ADAPTER) add_subdirectory(codec_adapter) endif() + if(CONFIG_COMP_IGO_NR) + add_subdirectory(igo_nr) + endif() subdirs(pipeline) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index fceb3633578a..4a2f0c0a0b78 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -344,6 +344,14 @@ config COMP_CODEC_ADAPTER "src\include\sof\audio\codec_adapter\interfaces.h". It is possible to link several different codecs and use them in parallel. +config COMP_IGO_NR + bool "IGO NR component" + default n + help + This option enables Intelligo non-speech noise reduction. The feature links to a proprietary + binary libigonr.a that currently is supported on different Xtensa DSP platforms. Please email + support@intelli-go.ai for any questions about the binary. + rsource "codec_adapter/Kconfig" endmenu # "Audio components" diff --git a/src/audio/igo_nr/CMakeLists.txt b/src/audio/igo_nr/CMakeLists.txt new file mode 100644 index 000000000000..7fb2e26ccf53 --- /dev/null +++ b/src/audio/igo_nr/CMakeLists.txt @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: BSD-3-Clause + +target_include_directories(sof_public_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +add_local_sources(sof igo_nr.c) +sof_add_static_library(IGOChrome ${CMAKE_CURRENT_LIST_DIR}/libigonr.a) diff --git a/src/audio/igo_nr/igo_nr.c b/src/audio/igo_nr/igo_nr.c new file mode 100644 index 000000000000..ec6e1b5a7548 --- /dev/null +++ b/src/audio/igo_nr/igo_nr.c @@ -0,0 +1,808 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2021 Intelligo Technology Inc. All rights reserved. +// +// Author: Fu-Yun TSUO + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOF_IGO_NR_MAX_SIZE 4096 /* Max size for coef data in bytes */ + +struct IGO_PARAMS { + uint32_t igo_params_ver; + uint32_t dump_data; + uint32_t nr_bypass; + uint32_t nr_mode1_en; + uint32_t nr_mode3_en; + uint32_t nr_ul_enable; + uint32_t agc_gain; + uint32_t nr_voice_str; + uint32_t nr_level; + uint32_t nr_mode1_floor; + uint32_t nr_mode1_od; + uint32_t nr_mode1_pp_param7; + uint32_t nr_mode1_pp_param8; + uint32_t nr_mode1_pp_param10; + uint32_t nr_mode3_floor; + uint32_t nr_mode1_pp_param53; +} __attribute__((packed)); + +enum IGO_NR_ENUM { + IGO_NR_ONOFF_SWITCH = 0, + IGO_NR_ENUM_LAST, +}; + +static const struct comp_driver comp_igo_nr; + +/* 696ae2bc-2877-11eb-adc1-0242ac120002 */ +DECLARE_SOF_RT_UUID("igo-nr", igo_nr_uuid, 0x696ae2bc, 0x2877, 0x11eb, 0xad, 0xc1, + 0x02, 0x42, 0xac, 0x12, 0x00, 0x02); + +DECLARE_TR_CTX(igo_nr_tr, SOF_UUID(igo_nr_uuid), LOG_LEVEL_INFO); + +#if CONFIG_FORMAT_S16LE +static void igo_nr_capture_s16(struct comp_data *cd, + const struct audio_stream *source, + struct audio_stream *sink, + int32_t src_frames, + int32_t sink_frames) +{ + int32_t in_nch = source->channels; + int32_t out_nch = sink->channels; + int32_t i; + int32_t idx_in = 0; + int32_t idx_out = 0; + int16_t *x; + int16_t *y; +#if CONFIG_DEBUG + int32_t dbg_en = cd->config.igo_params.dump_data == 1 && out_nch > 1; +#endif + + /* Deinterleave the source buffer and keeps the first channel data as input. */ + for (i = 0; i < src_frames; i++) { + x = audio_stream_read_frag_s16(source, idx_in); + cd->in[cd->in_wpt] = (*x); + + cd->in_wpt++; + idx_in += in_nch; + } + + /* Interleave write the processed data into first channel of output buffer. + * Under DEBUG mode, write the input data into second channel interleavedly. + */ + for (i = 0; i < sink_frames; i++) { + y = audio_stream_write_frag_s32(sink, idx_out); + *y = (cd->out[cd->out_rpt]); +#if CONFIG_DEBUG + if (dbg_en) { + y = audio_stream_write_frag_s32(sink, idx_out + 1); + *y = (cd->in[cd->out_rpt]); + } +#endif + cd->out_rpt++; + idx_out += out_nch; + } +} +#endif + +#if CONFIG_FORMAT_S24LE +static void igo_nr_capture_s24(struct comp_data *cd, + const struct audio_stream *source, + struct audio_stream *sink, + int32_t src_frames, + int32_t sink_frames) +{ + int32_t in_nch = source->channels; + int32_t out_nch = sink->channels; + int32_t i; + int32_t idx_in = 0; + int32_t idx_out = 0; + int32_t *x; + int32_t *y; +#if CONFIG_DEBUG + int32_t dbg_en = cd->config.igo_params.dump_data == 1 && out_nch > 1; +#endif + + /* Deinterleave the source buffer and keeps the first channel data as input. */ + for (i = 0; i < src_frames; i++) { + x = audio_stream_read_frag_s32(source, idx_in); + cd->in[cd->in_wpt] = Q_SHIFT_RND(*x, 24, 16); + + cd->in_wpt++; + idx_in += in_nch; + } + + /* Interleave write the processed data into first channel of output buffer. + * Under DEBUG mode, write the input data into second channel interleavedly. + */ + for (i = 0; i < sink_frames; i++) { + y = audio_stream_write_frag_s32(sink, idx_out); + *y = (cd->out[cd->out_rpt]) << 8; + +#if CONFIG_DEBUG + if (dbg_en) { + y = audio_stream_write_frag_s32(sink, idx_out + 1); + *y = (cd->in[cd->out_rpt]) << 8; + } +#endif + cd->out_rpt++; + idx_out += out_nch; + } +} +#endif + +#if CONFIG_FORMAT_S32LE +static void igo_nr_capture_s32(struct comp_data *cd, + const struct audio_stream *source, + struct audio_stream *sink, + int32_t src_frames, + int32_t sink_frames) +{ + int32_t in_nch = source->channels; + int32_t out_nch = sink->channels; + int32_t i; + int32_t idx_in = 0; + int32_t idx_out = 0; + int32_t *x; + int32_t *y; +#if CONFIG_DEBUG + int32_t dbg_en = cd->config.igo_params.dump_data == 1 && out_nch > 1; +#endif + + for (i = 0; i < src_frames; i++) { + x = audio_stream_read_frag_s32(source, idx_in); + cd->in[cd->in_wpt] = Q_SHIFT_RND(*x, 32, 16); + + cd->in_wpt++; + idx_in += in_nch; + } + + /* Deinterleave the source buffer and keeps the first channel data as input. */ + for (i = 0; i < sink_frames; i++) { + y = audio_stream_write_frag_s32(sink, idx_out); + *y = (cd->out[cd->out_rpt]) << 16; + + /* Interleave write the processed data into first channel of output buffer. + * Under DEBUG mode, write the input data into second channel interleavedly. + */ +#if CONFIG_DEBUG + if (dbg_en) { + y = audio_stream_write_frag_s32(sink, idx_out + 1); + *y = (cd->in[cd->out_rpt]) << 16; + } +#endif + cd->out_rpt++; + idx_out += out_nch; + } +} +#endif + +static inline int32_t set_capture_func(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *sourceb; + + sourceb = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + + /* The igo_nr supports S16_LE data. Format converter is needed. */ + switch (sourceb->stream.frame_fmt) { +#if CONFIG_FORMAT_S16LE + case SOF_IPC_FRAME_S16_LE: + comp_info(dev, "set_capture_func(), SOF_IPC_FRAME_S16_LE"); + cd->igo_nr_func = igo_nr_capture_s16; + break; +#endif +#if CONFIG_FORMAT_S24LE + case SOF_IPC_FRAME_S24_4LE: + comp_info(dev, "set_capture_func(), SOF_IPC_FRAME_S24_4LE"); + cd->igo_nr_func = igo_nr_capture_s24; + break; +#endif +#if CONFIG_FORMAT_S32LE + case SOF_IPC_FRAME_S32_LE: + comp_info(dev, "set_capture_func(), SOF_IPC_FRAME_S32_LE"); + cd->igo_nr_func = igo_nr_capture_s32; + break; +#endif + default: + comp_err(dev, "set_capture_func(), invalid frame_fmt"); + return -EINVAL; + } + return 0; +} + +static struct comp_dev *igo_nr_new(const struct comp_driver *drv, + struct sof_ipc_comp *comp) +{ + struct sof_ipc_comp_process *ipc_igo_nr = + (struct sof_ipc_comp_process *)comp; + struct comp_dev *dev; + struct comp_data *cd = NULL; + size_t bs = ipc_igo_nr->size; + int32_t ret; + + comp_cl_info(&comp_igo_nr, "igo_nr_new()"); + + /* Check first that configuration blob size is sane */ + if (bs > SOF_IGO_NR_MAX_SIZE) { + comp_cl_err(&comp_igo_nr, "igo_nr_new() error: configuration blob size = %u > %d", + bs, SOF_IGO_NR_MAX_SIZE); + return NULL; + } + + dev = comp_alloc(drv, COMP_SIZE(struct sof_ipc_comp_process)); + if (!dev) + return NULL; + + memcpy_s(COMP_GET_IPC(dev, sof_ipc_comp_process), + sizeof(struct sof_ipc_comp_process), ipc_igo_nr, + sizeof(struct sof_ipc_comp_process)); + + cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*cd)); + if (!cd) + goto err; + + ret = IgoLibGetInfo(&cd->igo_lib_info); + if (ret != IGO_RET_OK) { + comp_cl_err(&comp_igo_nr, "igo_nr_new(): IgoLibGetInfo() Failed."); + goto err; + } + + cd->p_handle = rballoc(0, SOF_MEM_CAPS_RAM, cd->igo_lib_info.handle_size); + if (!cd->p_handle) { + comp_cl_err(&comp_igo_nr, "igo_nr_new(): igo_handle memory rballoc error"); + goto err; + } + + comp_set_drvdata(dev, cd); + + /* Handler for configuration data */ + cd->model_handler = comp_data_blob_handler_new(dev); + if (!cd->model_handler) { + comp_cl_err(&comp_igo_nr, "igo_nr_new(): comp_data_blob_handler_new() failed."); + goto err; + } + + /* Get configuration data */ + ret = comp_init_data_blob(cd->model_handler, bs, ipc_igo_nr->data); + if (ret < 0) { + comp_cl_err(&comp_igo_nr, "igo_nr_new(): comp_init_data_blob() failed."); + goto err; + } + dev->state = COMP_STATE_READY; + + comp_cl_info(&comp_igo_nr, "igo_nr created"); + + return dev; + +err: + rfree(dev); + rfree(cd); + return NULL; +} + +static void igo_nr_free(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + comp_info(dev, "igo_nr_free()"); + + comp_data_blob_handler_free(cd->model_handler); + + rfree(cd->p_handle); + rfree(cd); + rfree(dev); +} + +static int32_t igo_nr_verify_params(struct comp_dev *dev, + struct sof_ipc_stream_params *params) +{ + int32_t ret; + + comp_dbg(dev, "igo_nr_verify_params()"); + + /* update downstream (playback) or upstream (capture) buffer parameters + */ + ret = comp_verify_params(dev, BUFF_PARAMS_RATE, params); + if (ret < 0) + comp_err(dev, "igo_nr_verify_params(): comp_verify_params() failed."); + + return ret; +} + +/* set component audio stream parameters */ +static int32_t igo_nr_params(struct comp_dev *dev, + struct sof_ipc_stream_params *pcm_params) +{ + struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *sinkb; + struct comp_buffer *sourceb; + int32_t err; + + comp_info(dev, "igo_nr_params()"); + + err = igo_nr_verify_params(dev, pcm_params); + if (err < 0) { + comp_err(dev, "igo_nr_params(): pcm params verification failed."); + return err; + } + + sourceb = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + sinkb = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + /* set source/sink_frames/rate */ + cd->source_rate = sourceb->stream.rate; + cd->sink_rate = sinkb->stream.rate; + if (!cd->sink_rate) { + comp_err(dev, "igo_nr_params(), zero sink rate"); + return -EINVAL; + } + + cd->sink_frames = dev->frames; + cd->source_frames = ceil_divide(dev->frames * cd->source_rate, + cd->sink_rate); + + /* Use empirical add +10 for target frames number to avoid xruns and + * distorted audio in beginning of streaming. It's slightly more than + * min. needed and does not increase much peak load and buffer memory + * consumption. The copy() function will find the limits and process + * less frames after the buffers levels stabilize. + */ + cd->source_frames_max = cd->source_frames + 10; + cd->sink_frames_max = cd->sink_frames + 10; + cd->frames = MAX(cd->source_frames_max, cd->sink_frames_max); + + comp_dbg(dev, "igo_nr_params(), source_rate=%u, sink_rate=%u, source_frames_max=%d, sink_frames_max=%d", + cd->source_rate, cd->sink_rate, + cd->source_frames_max, cd->sink_frames_max); + + /* The igo_nr supports sample rate 16000 only. */ + switch (sourceb->stream.rate) { + case 16000: + comp_info(dev, "igo_nr_params(), sample rate = 16000"); + cd->invalid_param = false; + break; + default: + comp_err(dev, "igo_nr_params(), invalid sample rate"); + cd->invalid_param = true; + return -EINVAL; + } + + return 0; +} + +static inline void igo_nr_set_chan_process(struct comp_dev *dev, int32_t chan) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + if (!cd->process_enable[chan]) + cd->process_enable[chan] = true; +} + +static inline void igo_nr_set_chan_passthrough(struct comp_dev *dev, int32_t chan) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + if (cd->process_enable[chan]) { + cd->process_enable[chan] = false; + IgoLibInit(cd->p_handle, &cd->igo_lib_config, &cd->config.igo_params); + } +} + +static int32_t igo_nr_cmd_get_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata, + int32_t max_size) +{ + struct comp_data *cd = comp_get_drvdata(dev); + int32_t ret; + + switch (cdata->cmd) { + case SOF_CTRL_CMD_BINARY: + comp_info(dev, "igo_nr_cmd_get_data(), SOF_CTRL_CMD_BINARY"); + ret = comp_data_blob_get_cmd(cd->model_handler, cdata, max_size); + break; + default: + comp_err(dev, "igo_nr_cmd_get_data() error: invalid cdata->cmd", cdata->cmd); + ret = -EINVAL; + break; + } + return ret; +} + +static int32_t igo_nr_cmd_get_value(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{ + struct comp_data *cd = comp_get_drvdata(dev); + int32_t j; + int32_t ret = 0; + + switch (cdata->cmd) { + case SOF_CTRL_CMD_ENUM: + comp_info(dev, "igo_nr_cmd_get_value(), SOF_CTRL_CMD_ENUM index=%d", cdata->index); + switch (cdata->index) { + case IGO_NR_ONOFF_SWITCH: + for (j = 0; j < cdata->num_elems; j++) + cdata->chanv[j].value = cd->process_enable[j]; + break; + default: + comp_err(dev, "igo_nr_cmd_get_value() error: invalid cdata->index %d", + cdata->index); + ret = -EINVAL; + break; + } + break; + case SOF_CTRL_CMD_SWITCH: + for (j = 0; j < cdata->num_elems; j++) { + cdata->chanv[j].channel = j; + cdata->chanv[j].value = cd->process_enable[j]; + comp_info(dev, "igo_nr_cmd_get_value(), channel = %u, value = %u", + cdata->chanv[j].channel, + cdata->chanv[j].value); + } + break; + default: + comp_err(dev, "igo_nr_cmd_get_value() error: invalid cdata->cmd %d", cdata->cmd); + ret = -EINVAL; + break; + } + return ret; +} + +static int32_t igo_nr_cmd_set_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata) +{ + struct comp_data *cd = comp_get_drvdata(dev); + int32_t ret; + + switch (cdata->cmd) { + case SOF_CTRL_CMD_BINARY: + comp_info(dev, "igo_nr_cmd_set_data(), SOF_CTRL_CMD_BINARY"); + ret = comp_data_blob_set_cmd(cd->model_handler, cdata); + break; + default: + comp_err(dev, "igo_nr_cmd_set_data() error: invalid cdata->cmd"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int32_t igo_nr_set_chan(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{ + uint32_t val; + int32_t ch; + int32_t j; + + for (j = 0; j < cdata->num_elems; j++) { + ch = cdata->chanv[j].channel; + val = cdata->chanv[j].value; + comp_info(dev, "igo_nr_cmd_set_value(), channel = %d, value = %u", ch, val); + if (ch < 0 || ch >= SOF_IPC_MAX_CHANNELS) { + comp_err(dev, "igo_nr_cmd_set_value(), illegal channel = %d", ch); + return -EINVAL; + } + + if (val) + igo_nr_set_chan_process(dev, ch); + else + igo_nr_set_chan_passthrough(dev, ch); + } + + return 0; +} + +static int32_t igo_nr_cmd_set_value(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata) +{ + int32_t ret; + + switch (cdata->cmd) { + case SOF_CTRL_CMD_ENUM: + comp_dbg(dev, "igo_nr_cmd_set_value(), SOF_CTRL_CMD_ENUM index=%d", cdata->index); + switch (cdata->index) { + case IGO_NR_ONOFF_SWITCH: + ret = igo_nr_set_chan(dev, cdata); + break; + default: + comp_err(dev, "igo_nr_cmd_set_value() error: invalid cdata->index %d", + cdata->index); + ret = -EINVAL; + break; + } + break; + case SOF_CTRL_CMD_SWITCH: + comp_dbg(dev, "igo_nr_cmd_set_value(), SOF_CTRL_CMD_SWITCH, cdata->comp_id = %u", + cdata->comp_id); + ret = igo_nr_set_chan(dev, cdata); + break; + + default: + comp_err(dev, "igo_nr_cmd_set_value() error: invalid cdata->cmd"); + ret = -EINVAL; + break; + } + + return ret; +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int32_t igo_nr_cmd(struct comp_dev *dev, + int32_t cmd, + void *data, + int32_t max_data_size) +{ + struct sof_ipc_ctrl_data *cdata = data; + + comp_info(dev, "igo_nr_cmd()"); + + switch (cmd) { + case COMP_CMD_SET_DATA: + return igo_nr_cmd_set_data(dev, cdata); + case COMP_CMD_GET_DATA: + return igo_nr_cmd_get_data(dev, cdata, max_data_size); + case COMP_CMD_SET_VALUE: + return igo_nr_cmd_set_value(dev, cdata); + case COMP_CMD_GET_VALUE: + return igo_nr_cmd_get_value(dev, cdata); + default: + comp_err(dev, "igo_nr_cmd() error: invalid command"); + return -EINVAL; + } +} + +static void igo_nr_process(struct comp_dev *dev, + struct comp_buffer *source, + struct comp_buffer *sink, + int32_t src_frames, + int32_t sink_frames, + uint32_t source_bytes, + uint32_t sink_bytes) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + buffer_invalidate(source, source_bytes); + + cd->igo_nr_func(cd, &source->stream, &sink->stream, src_frames, sink_frames); + + buffer_writeback(sink, sink_bytes); + + /* calc new free and available */ + comp_update_buffer_consume(source, source_bytes); + comp_update_buffer_produce(sink, sink_bytes); +} + +static void igo_nr_get_igo_params(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + struct sof_igo_nr_config *p_config = comp_get_data_blob(cd->model_handler, NULL, NULL); + comp_info(dev, "igo_nr_get_igo_params()"); + + if (p_config) { + cd->config = *p_config; + + comp_dbg(dev, "config changed"); + comp_dbg(dev, " igo_params_ver %d", + cd->config.igo_params.igo_params_ver); + comp_dbg(dev, " dump_data: %d", + cd->config.igo_params.dump_data); + comp_dbg(dev, " nr_bypass: %d", + cd->config.igo_params.nr_bypass); + comp_dbg(dev, " nr_mode1_en %d", + cd->config.igo_params.nr_mode1_en); + comp_dbg(dev, " nr_mode3_en %d", + cd->config.igo_params.nr_mode3_en); + comp_dbg(dev, " nr_ul_enable %d", + cd->config.igo_params.nr_ul_enable); + comp_dbg(dev, " agc_gain %d", + cd->config.igo_params.agc_gain); + comp_dbg(dev, " nr_voice_str %d", + cd->config.igo_params.nr_voice_str); + comp_dbg(dev, " nr_level %d", + cd->config.igo_params.nr_level); + comp_dbg(dev, " nr_mode1_floor %d", + cd->config.igo_params.nr_mode1_floor); + comp_dbg(dev, " nr_mode1_od %d", + cd->config.igo_params.nr_mode1_od); + comp_dbg(dev, " nr_mode1_pp_param7 %d", + cd->config.igo_params.nr_mode1_pp_param7); + comp_dbg(dev, " nr_mode1_pp_param8 %d", + cd->config.igo_params.nr_mode1_pp_param8); + comp_dbg(dev, " nr_mode1_pp_param10 %d", + cd->config.igo_params.nr_mode1_pp_param10); + comp_dbg(dev, " nr_mode3_floor %d", + cd->config.igo_params.nr_mode3_floor); + comp_dbg(dev, " nr_mode1_pp_param53 %d", + cd->config.igo_params.nr_mode1_pp_param53); + } +} + +/* copy and process stream data from source to sink buffers */ +static int32_t igo_nr_copy(struct comp_dev *dev) +{ + struct comp_copy_limits cl; + struct comp_buffer *sourceb; + struct comp_buffer *sinkb; + struct comp_data *cd = comp_get_drvdata(dev); + int32_t src_frames; + int32_t sink_frames; + + comp_dbg(dev, "igo_nr_copy()"); + + sourceb = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + sinkb = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + /* Check for changed configuration */ + if (comp_is_new_data_blob_available(cd->model_handler)) + igo_nr_get_igo_params(dev); + + /* Get source, sink, number of frames etc. to process. */ + comp_get_copy_limits(sourceb, sinkb, &cl); + + src_frames = audio_stream_get_avail_frames(&sourceb->stream); + sink_frames = audio_stream_get_free_frames(&sinkb->stream); + + comp_dbg(dev, "src_frames = %d, sink_frames = %d.", src_frames, sink_frames); + + /* Run the data consume/produce below */ + src_frames = MIN(src_frames, IGO_FRAME_SIZE - cd->in_wpt); + sink_frames = MIN(sink_frames, IGO_FRAME_SIZE - cd->out_rpt); + + comp_dbg(dev, "consumed src_frames = %d, produced sink_frames = %d.", + src_frames, sink_frames); + + igo_nr_process(dev, sourceb, sinkb, src_frames, sink_frames, + src_frames * cl.source_frame_bytes, + sink_frames * cl.sink_frame_bytes); + + /* Run algorithm if buffers are available */ + if (cd->in_wpt == IGO_FRAME_SIZE && cd->out_rpt == IGO_FRAME_SIZE) { + if (!cd->process_enable[0] || cd->invalid_param) { + memcpy_s(cd->out, IGO_FRAME_SIZE * sizeof(int16_t), + cd->in, IGO_FRAME_SIZE * sizeof(int16_t)); + } else { + IgoLibProcess(cd->p_handle, + &cd->igo_stream_data_in, + &cd->igo_stream_data_ref, + &cd->igo_stream_data_out); + } + + cd->in_wpt = 0; + cd->out_rpt = 0; + } + + return 0; +} + +static void igo_nr_lib_init(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + cd->igo_lib_config.algo_name = "igo_nr"; + cd->igo_lib_config.in_ch_num = 1; + cd->igo_lib_config.ref_ch_num = 0; + cd->igo_lib_config.out_ch_num = 1; + IgoLibInit(cd->p_handle, &cd->igo_lib_config, &cd->config.igo_params); + + cd->igo_stream_data_in.data = cd->in; + cd->igo_stream_data_in.data_width = IGO_DATA_16BIT; + cd->igo_stream_data_in.sample_num = IGO_FRAME_SIZE; + cd->igo_stream_data_in.sampling_rate = 16000; + + cd->igo_stream_data_ref.data = NULL; + cd->igo_stream_data_ref.data_width = 0; + cd->igo_stream_data_ref.sample_num = 0; + cd->igo_stream_data_ref.sampling_rate = 0; + + cd->igo_stream_data_out.data = cd->out; + cd->igo_stream_data_out.data_width = IGO_DATA_16BIT; + cd->igo_stream_data_out.sample_num = IGO_FRAME_SIZE; + cd->igo_stream_data_out.sampling_rate = 16000; +} + +static int32_t igo_nr_prepare(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + int32_t ret; + + comp_dbg(dev, "igo_nr_prepare()"); + + igo_nr_get_igo_params(dev); + + igo_nr_lib_init(dev); + + ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); + if (ret < 0) + return ret; + + if (ret == COMP_STATUS_STATE_ALREADY_SET) + return PPL_STATUS_PATH_STOP; + + /* Clear in/out buffers */ + memset(cd->in, 0, IGO_NR_IN_BUF_LENGTH * sizeof(int16_t)); + memset(cd->out, 0, IGO_NR_OUT_BUF_LENGTH * sizeof(int16_t)); + cd->in_wpt = 0; + cd->out_rpt = 0; + + /* Default NR on */ + cd->process_enable[0] = true; + + return set_capture_func(dev); +} + +static int32_t igo_nr_reset(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + comp_info(dev, "igo_nr_reset()"); + + cd->igo_nr_func = NULL; + + comp_set_state(dev, COMP_TRIGGER_RESET); + return 0; +} + +static int32_t igo_nr_trigger(struct comp_dev *dev, int32_t cmd) +{ + int32_t ret; + + comp_info(dev, "igo_nr_trigger(), command = %u", cmd); + + ret = comp_set_state(dev, cmd); + if (ret == COMP_STATUS_STATE_ALREADY_SET) + ret = PPL_STATUS_PATH_STOP; + + return ret; +} + +static const struct comp_driver comp_igo_nr = { + .uid = SOF_RT_UUID(igo_nr_uuid), + .tctx = &igo_nr_tr, + .ops = { + .create = igo_nr_new, + .free = igo_nr_free, + .params = igo_nr_params, + .cmd = igo_nr_cmd, + .copy = igo_nr_copy, + .prepare = igo_nr_prepare, + .reset = igo_nr_reset, + .trigger = igo_nr_trigger, + }, +}; + +static SHARED_DATA struct comp_driver_info comp_igo_nr_info = { + .drv = &comp_igo_nr, +}; + +UT_STATIC void sys_comp_igo_nr_init(void) +{ + comp_register(platform_shared_get(&comp_igo_nr_info, + sizeof(comp_igo_nr_info))); +} + +DECLARE_MODULE(sys_comp_igo_nr_init); diff --git a/src/include/sof/audio/igo_nr/igo_lib.h b/src/include/sof/audio/igo_nr/igo_lib.h new file mode 100644 index 000000000000..1348c821bbdb --- /dev/null +++ b/src/include/sof/audio/igo_nr/igo_lib.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2021 Intelligo Technology Inc. All rights reserved. + * + * Author: Fu-Yun TSUO + */ + +/******************************************************************************* + * [2017] - [2021] Copyright (c) Intelligo Technology Inc. + * + * This unpublished material is proprietary to Intelligo Technology Inc. + * All rights reserved. The methods and techniques described herein are + * considered trade secrets and/or confidential. Reproduction or + * distribution, in whole or in part, is forbidden except by express written + * permission of Intelligo Technology Inc. + * + *******************************************************************************/ + +#ifndef _IGO_LIB_H_ +#define _IGO_LIB_H_ + +#include + +enum IgoRet { + IGO_RET_OK = 0, + IGO_RET_ERR, + IGO_RET_NO_SERVICE, + IGO_RET_INVL_ARG, + IGO_RET_NO_MEMORY, + IGO_RET_NOT_SUPPORT, + IGO_RET_ALGO_NAME_NOT_FOUND, + IGO_RET_CH_NUM_ERR, + IGO_RET_SAMPLING_RATE_NOT_SUPPORT, + IGO_RET_IN_DATA_ERR, + IGO_RET_REF_DATA_ERR, + IGO_RET_OUT_DATA_ERR, + IGO_RET_PARAM_NOT_FOUND, + IGO_RET_PARAM_READ_ONLY, + IGO_RET_PARAM_WRITE_ONLY, + IGO_RET_PARAM_INVALID_VAL, + IGO_RET_LAST +}; + +enum IgoDataWidth { + IGO_DATA_16BIT = 0, + IGO_DATA_24BIT, + IGO_DATA_LAST +}; + +/** + * @brief IgoLibInfo is used to keep information for iGo library. + * + */ +struct IgoLibInfo { + uint32_t major_version; /* Major version */ + uint32_t minor_version; /* Minor version */ + uint32_t build_version; /* Build version */ + uint32_t ext_version; /* Extension version */ + uint32_t handle_size; /* Size of handle structure */ +}; + +/** + * @brief IgoStreamData is used to keep audio data for iGo library. + * + */ +struct IgoStreamData { + void *data; /* Data array */ + enum IgoDataWidth data_width; /* Specify audio data bit width */ + uint16_t sample_num; /* Sample number in this data bulk */ + uint16_t sampling_rate; /* Sampling rate for the data stream */ +}; + +/** + * @brief IgoLibConfig is used to keep lib configuration for lib instance + * initialization. + * + */ +struct IgoLibConfig { + const char *algo_name; /* Algorithm name */ + uint8_t in_ch_num; /* Input channel number for the algo instance */ + uint8_t ref_ch_num; /* Reference channel number for the algo instance */ + uint8_t out_ch_num; /* Output channel number for the algo instance */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief IgoLibGetInfo() - Retrieve the lib information. + * @param[out] info Lib information. + * + * This API is used to get library detail information. + * + * @return iGo defined return value. + */ +enum IgoRet IgoLibGetInfo(struct IgoLibInfo *info); + +/** + * @brief IgoLibInit() - Initialize iGo lib instance. + * @param[out] handle iGo lib handle. + * @param[in] config Lib configuration for initialization. + * @param[in] param the point of iGO Parameter for initialization. + * + * This API is used to initialize iGo lib instance and get a handle which is + * for control the iGo lib instance. + * + * P.S. The channel number in the config is algorithm dependent. + * + * @return iGo defined return value. + */ +enum IgoRet IgoLibInit(void *handle, + const struct IgoLibConfig *config, + void *param); + +/** + * @brief IgoLibProcess() - Process audio stream. + * @param[in] handle iGo lib handle. + * @param[in] in input audio stream. + * @param[in] ref reference audio stream. + * @param[out] out output audio stream. + * + * This API is used to process audio stream. The default audio sample is 16bit. + * The sampling rate and sample number should be specified in IgoStreamData + * structure. If the channel number > 1 for IgoStreamData, the data should be + * interleaved sample by sample. + * + * Note: IgoLibProcess supports 16k/48k 16bit data only by default. + * If other data format or sampling rate is required, please + * ask intelliGo for support. + * + * @return iGo defined return value. + */ +enum IgoRet IgoLibProcess(void *handle, + const struct IgoStreamData *in, + const struct IgoStreamData *ref, + const struct IgoStreamData *out); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/sof/audio/igo_nr/igo_nr_comp.h b/src/include/sof/audio/igo_nr/igo_nr_comp.h new file mode 100644 index 000000000000..d033838744f6 --- /dev/null +++ b/src/include/sof/audio/igo_nr/igo_nr_comp.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2021 Intelligo Technology Inc. All rights reserved. + * + * Author: Fu-Yun TSUO + */ + +#ifndef __SOF_AUDIO_IGO_NR_CONFIG_H__ +#define __SOF_AUDIO_IGO_NR_CONFIG_H__ + +#include +#include +#include + +#define IGO_FRAME_SIZE (256) +#define IGO_NR_IN_BUF_LENGTH (IGO_FRAME_SIZE) +#define IGO_NR_OUT_BUF_LENGTH (IGO_FRAME_SIZE) + +/* IGO_NR component private data */ +struct comp_data { + void *p_handle; + struct IgoLibInfo igo_lib_info; + struct IgoLibConfig igo_lib_config; + struct IgoStreamData igo_stream_data_in; + struct IgoStreamData igo_stream_data_ref; + struct IgoStreamData igo_stream_data_out; + struct comp_data_blob_handler *model_handler; + struct sof_igo_nr_config config; /**< blob data buffer */ + int16_t in[IGO_NR_IN_BUF_LENGTH]; /**< input samples buffer */ + int16_t out[IGO_NR_IN_BUF_LENGTH]; /**< output samples mix buffer */ + uint16_t in_wpt; + uint16_t out_rpt; + bool process_enable[SOF_IPC_MAX_CHANNELS]; /**< set if channel process is enabled */ + bool invalid_param; /**< sample rate != 16000 */ + uint32_t sink_rate; /* Sample rate in Hz */ + uint32_t source_rate; /* Sample rate in Hz */ + uint32_t sink_format; /* For used PCM sample format */ + uint32_t source_format; /* For used PCM sample format */ + int32_t source_frames; /* Nominal # of frames to process at source */ + int32_t sink_frames; /* Nominal # of frames to process at sink */ + int32_t source_frames_max; /* Max # of frames to process at source */ + int32_t sink_frames_max; /* Max # of frames to process at sink */ + int32_t frames; /* IO buffer length */ + void (*igo_nr_func)(struct comp_data *cd, + const struct audio_stream *source, + struct audio_stream *sink, + int src_frames, + int snk_frames); +}; + +#endif /* __SOF_AUDIO_IGO_NR_CONFIG_H__ */ diff --git a/src/include/user/igo_nr.h b/src/include/user/igo_nr.h new file mode 100644 index 000000000000..4d10a176f40a --- /dev/null +++ b/src/include/user/igo_nr.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2021 Intelligo Technology Inc. All rights reserved. + * + * Author: Fu-Yun TSUO + */ + +#ifndef __USER_IGO_NR_H__ +#define __USER_IGO_NR_H__ + +#include + +struct sof_igo_nr_config { + /* reserved */ + struct IGO_PARAMS igo_params; + + int16_t data[]; +} __attribute__((packed)); + +#endif /* __USER_IGO_NR_H__ */ diff --git a/tools/topology/m4/igo_nr.m4 b/tools/topology/m4/igo_nr.m4 new file mode 100644 index 000000000000..1db0e5f6f8df --- /dev/null +++ b/tools/topology/m4/igo_nr.m4 @@ -0,0 +1,66 @@ +divert(-1) + +dnl Define macro for IGO_NR widget +DECLARE_SOF_RT_UUID("igo-nr", igo_nr_uuid, 0x696ae2bc, 0x2877, 0x11eb, 0xad, 0xc1, + 0x02, 0x42, 0xac, 0x12, 0x00, 0x02) + +dnl N_IGO_NR(name) +define(`N_IGO_NR', `IGONR'PIPELINE_ID`.'$1) + +dnl W_IGO_NR(name, format, periods_sink, periods_source, core, kcontrol0. kcontrol1...etc) +define(`W_IGO_NR', +`SectionVendorTuples."'N_IGO_NR($1)`_tuples_uuid" {' +` tokens "sof_comp_tokens"' +` tuples."uuid" {' +` SOF_TKN_COMP_UUID' STR(igo_nr_uuid) +` }' +`}' +`SectionData."'N_IGO_NR($1)`_data_uuid" {' +` tuples "'N_IGO_NR($1)`_tuples_uuid"' +`}' +`SectionVendorTuples."'N_IGO_NR($1)`_tuples_w" {' +` tokens "sof_comp_tokens"' +` tuples."word" {' +` SOF_TKN_COMP_PERIOD_SINK_COUNT' STR($3) +` SOF_TKN_COMP_PERIOD_SOURCE_COUNT' STR($4) +` SOF_TKN_COMP_CORE_ID' STR($5) +` }' +`}' +`SectionData."'N_IGO_NR($1)`_data_w" {' +` tuples "'N_IGO_NR($1)`_tuples_w"' +`}' +`SectionVendorTuples."'N_IGO_NR($1)`_tuples_str" {' +` tokens "sof_comp_tokens"' +` tuples."string" {' +` SOF_TKN_COMP_FORMAT' STR($2) +` }' +`}' +`SectionData."'N_IGO_NR($1)`_data_str" {' +` tuples "'N_IGO_NR($1)`_tuples_str"' +`}' +`SectionVendorTuples."'N_IGO_NR($1)`_tuples_str_type" {' +` tokens "sof_process_tokens"' +` tuples."string" {' +` SOF_TKN_PROCESS_TYPE' "IGO_NR" +` }' +`}' +`SectionData."'N_IGO_NR($1)`_data_str_type" {' +` tuples "'N_IGO_NR($1)`_tuples_str_type"' +`}' +`SectionWidget."'N_IGO_NR($1)`" {' +` index "'PIPELINE_ID`"' +` type "effect"' +` no_pm "true"' +` data [' +` "'N_IGO_NR($1)`_data_uuid"' +` "'N_IGO_NR($1)`_data_w"' +` "'N_IGO_NR($1)`_data_str"' +` "'N_IGO_NR($1)`_data_str_type"' +` ]' +` bytes [' + $6 +` ]' + +`}') + +divert(0)dnl diff --git a/tools/topology/sof/pipe-igonr-capture.m4 b/tools/topology/sof/pipe-igonr-capture.m4 new file mode 100644 index 000000000000..9cf033062b99 --- /dev/null +++ b/tools/topology/sof/pipe-igonr-capture.m4 @@ -0,0 +1,90 @@ +# Capture Passthrough Pipeline and PCM +# +# Pipeline Endpoints for connection are :- +# +# host PCM_C <-- B0 <-- sink DAI0 + +# Include topology builder +include(`utils.m4') +include(`buffer.m4') +include(`pcm.m4') +include(`dai.m4') +include(`bytecontrol.m4') +include(`pipeline.m4') +include(`igo_nr.m4') + +CONTROLBYTES_PRIV(IGO_NR_priv, +` bytes "0x53,0x4f,0x46,0x00,' +` 0x00,0x00,0x00,0x00,' +` 0x40,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x03,' +` 0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,' +` 0x00,0x20,0x00,0x00,' +` 0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,' +` 0x02,0x00,0x00,0x00,' +` 0x02,0x00,0x00,0x00,' +` 0x02,0x00,0x00,0x00,' +` 0x01,0x00,0x00,0x00,' +` 0x3d,0x00,0x00,0x00,' +` 0x09,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,' +` 0x00,0x20,0x00,0x00,' +` 0x34,0x03,0x00,0x00"' +) + +C_CONTROLBYTES(IGO_NR, PIPELINE_ID, + CONTROLBYTES_OPS(bytes, 258 binds the control to bytes get/put handlers, 258, 258), + CONTROLBYTES_EXTOPS(258 binds the control to bytes get/put handlers, 258, 258), + , , , + CONTROLBYTES_MAX(, 196), + , + IGO_NR_priv) + +# +# Components and Buffers +# + +# Host "Passthrough Capture" PCM +# with 0 sink and 2 source periods +W_PCM_CAPTURE(PCM_ID, Passthrough Capture, 0, DAI_PERIODS, SCHEDULE_CORE) + +W_IGO_NR(0, PIPELINE_FORMAT, 2, DAI_PERIODS, SCHEDULE_CORE, LIST(` ', "IGO_NR")) + +# Capture Buffers +W_BUFFER(0, COMP_BUFFER_SIZE(DAI_PERIODS, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_PASS_MEM_CAP) + +W_BUFFER(1, COMP_BUFFER_SIZE(DAI_PERIODS, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_PASS_MEM_CAP) +# +# Pipeline Graph +# +# host PCM_C <-- B0 <-- NR0 <-- B1<-- sink DAI0 + +P_GRAPH(pipe-nr-capture-PIPELINE_ID, PIPELINE_ID, + LIST(` ', + `dapm(N_PCMC(PCM_ID), N_BUFFER(0))', + `dapm(N_BUFFER(0), N_IGO_NR(0))', + `dapm(N_IGO_NR(0), N_BUFFER(1))')) + +# +# Pipeline Source and Sinks +# +indir(`define', concat(`PIPELINE_SINK_', PIPELINE_ID), N_BUFFER(1)) +indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), Passthrough Capture PCM_ID) + +# +# PCM Configuration +# + +PCM_CAPABILITIES(Passthrough Capture PCM_ID, COMP_FORMAT_NAME(PIPELINE_FORMAT), PCM_MIN_RATE, PCM_MAX_RATE, PIPELINE_CHANNELS, PIPELINE_CHANNELS, 2, 16, 192, 16384, 65536, 65536)