From 8ac06747fe7360b5dae83280855bd9ef3e9161f4 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Fri, 6 Mar 2026 20:17:54 +0200 Subject: [PATCH 1/3] Audio: Selector: Add support for multiple up/down-mix profiles This patch extends the struct ipc4_selector_coeffs_config. The first 16-bit reserved field is split into to 8-bit field for source and sink channels count. The configuration is changed to array of the previous structs instead of single. When preparing for stream the array is checked for matching number of channels for source and sink and if found the coefficients for the channels counts is selected into use. The change avoids the need for user space to update the configuration in runtime if the blob used for initialization contains all the needed channels up/down-mix profiles. Signed-off-by: Seppo Ingalsuo --- src/audio/selector/selector.c | 195 ++++++++++++++++++++++++++++--- src/include/sof/audio/selector.h | 17 ++- 2 files changed, 194 insertions(+), 18 deletions(-) diff --git a/src/audio/selector/selector.c b/src/audio/selector/selector.c index 40a4a2c5b888..eb51f9eab005 100644 --- a/src/audio/selector/selector.c +++ b/src/audio/selector/selector.c @@ -37,6 +37,10 @@ #include #include +#if CONFIG_IPC_MAJOR_4 +#define SEL_MAX_CONFIG_BLOB_SIZE (SEL_MAX_NUM_CONFIGS * sizeof(struct ipc4_selector_coeffs_config)) +#endif + LOG_MODULE_REGISTER(selector, CONFIG_SOF_LOG_LEVEL); #if CONFIG_IPC_MAJOR_3 @@ -574,7 +578,7 @@ static void build_config(struct comp_data *cd, struct module_config *cfg) /* Build default coefficient array (unity Q10 on diagonal, i.e. pass-through mode) */ memset(&cd->coeffs_config, 0, sizeof(cd->coeffs_config)); for (i = 0; i < MIN(SEL_SOURCE_CHANNELS_MAX, SEL_SINK_CHANNELS_MAX); i++) - cd->coeffs_config.coeffs[i][i] = 1 << 10; + cd->coeffs_config.coeffs[i][i] = SEL_COEF_ONE_Q10; } static int selector_init(struct processing_module *mod) @@ -617,8 +621,8 @@ static int selector_init(struct processing_module *mod) if (!cd) return -ENOMEM; - cd->sel_ipc4_cfg.init_payload_fmt = payload_fmt; md->private = cd; + cd->sel_ipc4_cfg.init_payload_fmt = payload_fmt; if (payload_fmt == IPC4_SEL_INIT_PAYLOAD_BASE_WITH_EXT) { size_t size = sizeof(struct sof_selector_ipc4_pin_config); @@ -733,6 +737,7 @@ static int selector_free(struct processing_module *mod) comp_dbg(mod->dev, "entry"); + mod_free(mod, cd->multi_coeffs_config); mod_free(mod, cd); return 0; @@ -769,13 +774,51 @@ static int selector_set_config(struct processing_module *mod, uint32_t config_id size_t response_size) { struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int n; if (config_id == IPC4_SELECTOR_COEFFS_CONFIG_ID) { - if (data_offset_size != sizeof(cd->coeffs_config)) + if (data_offset_size > SEL_MAX_CONFIG_BLOB_SIZE || + pos != MODULE_CFG_FRAGMENT_SINGLE) { + comp_err(dev, "Failure with size %u pos %u", data_offset_size, pos); return -EINVAL; + } - memcpy_s(&cd->coeffs_config, sizeof(cd->coeffs_config), fragment, data_offset_size); - return 0; + /* The size must be N times the coefficient vectors size of one channels + * up/down mix profile. + */ + n = data_offset_size / sizeof(struct ipc4_selector_coeffs_config); + if (n < 1 || data_offset_size != n * sizeof(struct ipc4_selector_coeffs_config)) { + comp_err(dev, "Invalid configuration size."); + return -EINVAL; + } + + cd->num_configs = n; + if (cd->multi_coeffs_config) { + if (cd->multi_coeffs_config_size < data_offset_size) { + /* Configuration exist but the allocation is too + * small to write over. + */ + mod_free(mod, cd->multi_coeffs_config); + cd->multi_coeffs_config_size = data_offset_size; + cd->multi_coeffs_config = + mod_alloc(mod, cd->multi_coeffs_config_size); + } + } else { + /* No existing configuration */ + cd->multi_coeffs_config_size = data_offset_size; + cd->multi_coeffs_config = mod_alloc(mod, cd->multi_coeffs_config_size); + } + + if (!cd->multi_coeffs_config) { + comp_err(dev, "Failed to allocate configuration blob."); + return -ENOMEM; + } + + /* Copy the configuration and notify for need to re-configure */ + cd->new_config = true; + return memcpy_s(cd->multi_coeffs_config, cd->multi_coeffs_config_size, + fragment, data_offset_size); } return -EINVAL; @@ -788,6 +831,111 @@ static int selector_get_config(struct processing_module *mod, uint32_t config_id return 0; } +/** + * \brief Loop the array of mix coefficients sets and find a set with matching channels + * in and out count. + * \param[in] cd Selector component data. + * \param[in] source_channels Number of channels in source. + * \param[in] sink_channels Number of channels in sink. + * + * \return Pointer to the matching ipc4_selector_coeffs_config if found, or NULL if + * no matching configuration exists. + */ +static struct ipc4_selector_coeffs_config *selector_config_array_search(struct comp_data *cd, + int source_channels, + int sink_channels) +{ + struct ipc4_selector_coeffs_config *found = NULL; + int i; + + for (i = 0; i < cd->num_configs; i++) { + if (cd->multi_coeffs_config[i].source_channels_count == source_channels && + cd->multi_coeffs_config[i].sink_channels_count == sink_channels) { + found = &cd->multi_coeffs_config[i]; + break; + } + } + + return found; +} + +/** + * \brief Get mix coefficients set from configuration blob with multiple coefficients sets. + * Also activate more efficient pass-through copy mode if the coefficients indicate 1:1 + * copy from source to sink. + * \param[in,out] mod Selector base module device. + * \param[in] source_channels Number of channels in source. + * \param[in] sink_channels Number of channels in sink. + * + * \return Error code. + */ +static int selector_find_coefficients(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct ipc4_selector_coeffs_config *config; + uint32_t source_channels = cd->config.in_channels_count; + uint32_t sink_channels = cd->config.out_channels_count; + int16_t coef; + int ret, i, j; + + /* In set_config() the blob is copied to cd->multi_coeffs_config. A legacy blob contains a + * single set of mix coefficients without channels information. A new blob with multiple + * configurations has the source and sink channels count information. If there has been no + * set_config(), then the cd->coeffs_config has been initialized in set_selector_params() + * to mix with coefficients SEL_COEF_ONE_Q10 for matching input and output channels. + */ + if (cd->multi_coeffs_config) { + config = cd->multi_coeffs_config; + if (cd->num_configs > 1) { + config = selector_config_array_search(cd, source_channels, sink_channels); + /* If not found, check if pass-through mix is defined for the max + * channels count (8). + */ + if (!config && source_channels == sink_channels) + config = selector_config_array_search(cd, SEL_SOURCE_CHANNELS_MAX, + SEL_SINK_CHANNELS_MAX); + + if (!config) { + comp_err(dev, "No mix coefficients found for %d to %d channels.", + source_channels, sink_channels); + return -EINVAL; + } + } + + ret = memcpy_s(&cd->coeffs_config, sizeof(struct ipc4_selector_coeffs_config), + config, sizeof(*config)); + if (ret) + return ret; + } + + /* The pass-through copy function can be used if coefficients are a unit matrix for + * 1:1 stream copy. + */ + if (source_channels == sink_channels) { + cd->passthrough = true; + for (i = 0; i < sink_channels; i++) { + for (j = 0; j < source_channels; j++) { + coef = cd->coeffs_config.coeffs[i][j]; + if ((i == j && coef != SEL_COEF_ONE_Q10) || (i != j && coef != 0)) { + cd->passthrough = false; + break; + } + } + } + } else { + cd->passthrough = false; + } + + if (cd->passthrough) + comp_info(dev, "Passthrough mode."); + else + comp_info(dev, "Using coefficients for %d to %d channels.", + source_channels, sink_channels); + + return 0; +} + /** * \brief Copies and processes stream data. * \param[in,out] mod Selector base module device. @@ -799,11 +947,29 @@ static int selector_process(struct processing_module *mod, struct output_stream_buffer *output_buffers, int num_output_buffers) { + struct audio_stream *source; + struct audio_stream *sink; struct comp_data *cd = module_get_private_data(mod); uint32_t avail_frames = input_buffers[0].size; + int ret; comp_dbg(mod->dev, "entry"); + if (cd->new_config) { + cd->new_config = false; + ret = selector_find_coefficients(mod); + if (ret) + return ret; + } + + if (cd->passthrough) { + source = input_buffers->data; + sink = output_buffers->data; + audio_stream_copy(source, 0, sink, 0, avail_frames * cd->config.in_channels_count); + module_update_buffer_position(input_buffers, output_buffers, avail_frames); + return 0; + } + if (avail_frames) /* copy selected channels from in to out */ cd->sel_func(mod, input_buffers, output_buffers, avail_frames); @@ -850,17 +1016,7 @@ static int selector_prepare(struct processing_module *mod, /* get sink data format and period bytes */ cd->sink_format = audio_stream_get_frm_fmt(&sinkb->stream); cd->sink_period_bytes = audio_stream_period_bytes(&sinkb->stream, dev->frames); - - /* There is an assumption that sink component will report out - * proper number of channels [1] for selector to actually - * reduce channel count between source and sink - */ - comp_info(dev, "source sink channel = %u %u", - audio_stream_get_channels(&sourceb->stream), - audio_stream_get_channels(&sinkb->stream)); - sink_size = audio_stream_get_size(&sinkb->stream); - md->mpd.in_buff_size = cd->source_period_bytes; md->mpd.out_buff_size = cd->sink_period_bytes; @@ -891,6 +1047,11 @@ static int selector_prepare(struct processing_module *mod, return -EINVAL; } + if (cd->new_config) { + cd->new_config = false; + return selector_find_coefficients(mod); + } + return 0; } @@ -908,7 +1069,9 @@ static int selector_reset(struct processing_module *mod) cd->source_period_bytes = 0; cd->sink_period_bytes = 0; cd->sel_func = NULL; - + cd->num_configs = 0; + cd->passthrough = false; + cd->new_config = false; return 0; } diff --git a/src/include/sof/audio/selector.h b/src/include/sof/audio/selector.h index cd4aa926c7f4..e977a47af9a9 100644 --- a/src/include/sof/audio/selector.h +++ b/src/include/sof/audio/selector.h @@ -27,6 +27,9 @@ struct comp_buffer; struct comp_dev; +/** \brief Default mix gain. */ +#define SEL_COEF_ONE_Q10 1024 /* int16(1 * 2^10) */ + #if CONFIG_IPC_MAJOR_3 /** \brief Supported channel count on input. */ #define SEL_SOURCE_2CH 2 @@ -43,6 +46,9 @@ struct comp_dev; /** \brief Maximum supported channel count on output. */ #define SEL_SINK_CHANNELS_MAX 8 +/** \brief Maximum number of configurations in the blob received with set_config() */ +#define SEL_MAX_NUM_CONFIGS 8 + #define SEL_NUM_IN_PIN_FMTS 1 #define SEL_NUM_OUT_PIN_FMTS 1 @@ -60,8 +66,10 @@ enum ipc4_selector_config_id { /** \brief IPC4 mixing coefficients configuration. */ struct ipc4_selector_coeffs_config { - uint16_t rsvd0; /**< Unused field, keeps the structure aligned with common layout */ - uint16_t rsvd1; /**< Unused field, keeps the structure aligned with common layout */ + uint8_t source_channels_count; /**< Used when multiple profiles are packed into one blob. */ + uint8_t sink_channels_count; /**< Used when multiple profiles are packed into one blob. */ + uint8_t source_channel_config; /**< Used when multiple profiles are packed into one blob. */ + uint8_t sink_channel_config; /**< Used when multiple profiles are packed into one blob. */ /** Mixing coefficients in Q10 fixed point format */ int16_t coeffs[SEL_SINK_CHANNELS_MAX][SEL_SOURCE_CHANNELS_MAX]; @@ -109,6 +117,8 @@ struct comp_data { #if CONFIG_IPC_MAJOR_4 struct sof_selector_ipc4_config sel_ipc4_cfg; struct ipc4_selector_coeffs_config coeffs_config; + struct ipc4_selector_coeffs_config *multi_coeffs_config; + size_t multi_coeffs_config_size; #endif uint32_t source_period_bytes; /**< source number of period bytes */ @@ -117,6 +127,9 @@ struct comp_data { enum sof_ipc_frame sink_format; /**< sink frame format */ struct sof_sel_config config; /**< component configuration data */ sel_func sel_func; /**< channel selector processing function */ + int num_configs; /**< Number of coefficients sets in configuration blob. */ + bool passthrough; /**< Use a passthrough copy function when no up/down mix. */ + bool new_config; /**< True if new configuration has been received */ }; /** \brief Selector processing functions map. */ From 6d5bacdc27c55c7312ce1a5d67ba7a1d2b0f09d0 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Mon, 9 Mar 2026 13:23:18 +0200 Subject: [PATCH 2/3] Audio: Selector: Tune: Add blob with all up/down-mix profiles This patch modifies the configuration blobs build script sof_selector_blobs.m to create one blob that contains several of them for the purpose to up or down-mix DSP offloaded decoder output. The blob uses the reserved fields of selector configuration to describe source and sink channel counts and channel config values. The exported blob is "stereo_endpoint_playback_updownmix.conf". The single configuration blobs continue to be with the reserved fields as zeros. The .conf blobs may be removed later when other topologies are modified to not use them. Signed-off-by: Seppo Ingalsuo --- src/audio/selector/tune/sof_selector_blobs.m | 143 ++++++++++++------- 1 file changed, 94 insertions(+), 49 deletions(-) diff --git a/src/audio/selector/tune/sof_selector_blobs.m b/src/audio/selector/tune/sof_selector_blobs.m index 83b38be64e3e..1b370d4030de 100644 --- a/src/audio/selector/tune/sof_selector_blobs.m +++ b/src/audio/selector/tune/sof_selector_blobs.m @@ -11,46 +11,59 @@ % SPDX-License-Identifier: BSD-3-Clause % -% Copyright (c) 2025, Intel Corporation. +% Copyright (c) 2025-2026, Intel Corporation. function sof_selector_blobs() % See ITU-R BS.775-4 for mix coefficient values sof_selector_paths(true); + % Values of enum ipc4_channel_config + IPC4_CHANNEL_CONFIG_MONO = 0; + IPC4_CHANNEL_CONFIG_STEREO = 1; + IPC4_CHANNEL_CONFIG_QUATRO = 5; + IPC4_CHANNEL_CONFIG_5_POINT_1 = 8; + IPC4_CHANNEL_CONFIG_7_POINT_1 = 12; + % Matrix for 1:1 pass-through - sel.rsvd0 = 0; - sel.rsvd1 = 0; + sel.ch_count = [8 8]; % Number of channels + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_7_POINT_1]; sel.coeffs = diag(ones(8, 1)); - write_blob(sel, "passthrough"); + passthrough_pack8 = write_blob(sel, "passthrough"); % Stereo to mono downmix + sel.ch_count = [2 1]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_MONO]; sel.coeffs = zeros(8,8); sel.coeffs(1, 1) = 0.7071; sel.coeffs(1, 2) = 0.7071; - write_blob(sel, "downmix_stereo_to_mono"); + stereo_to_mono_pack8 = write_blob(sel, "downmix_stereo_to_mono"); % 5.1 to stereo downmix + sel.ch_count = [6 2]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_STEREO]; fl = 1; fr = 2; fc = 3; lfe = 4; sl = 5; sr = 6; m = zeros(8,8); m(1, fl) = 1.0000; m(1, fr) = 0.0000; m(1, fc) = 0.7071; m(1, sl) = 0.7071; m(1, sr) = 0.0000; m(2, fl) = 0.0000; m(2, fr) = 1.0000; m(2, fc) = 0.7071; m(2, sl) = 0.0000; m(2, sr) = 0.7071; sel.coeffs = m; - write_blob(sel, "downmix_51_to_stereo"); sel.coeffs(1, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to left sel.coeffs(2, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to right - write_blob(sel, "downmix_51_to_stereo_with_lfe"); + sixch_to_stereo_pack8 = write_blob(sel, "downmix_51_to_stereo_with_lfe"); % 5.1 to mono downmix + sel.ch_count = [6 1]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_MONO]; fl = 1; fr = 2; fc = 3; lfe = 4; sl = 5; sr = 6; m = zeros(8,8); m(1, fl) = 0.7071; m(1, fr) = 0.7071; m(1, fc) = 1.0000; m(1, sl) = 0.5000; m(1, sr) = 0.5000; sel.coeffs = m; - write_blob(sel, "downmix_51_to_mono"); sel.coeffs(1, lfe) = 10^(+10/20); - write_blob(sel, "downmix_51_to_mono_with_lfe"); + sixch_to_mono_pack8 = write_blob(sel, "downmix_51_to_mono_with_lfe"); % 7.1 to 5.1 downmix + sel.ch_count = [8 6]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_5_POINT_1]; fl8 = 1; fr8 = 2; fc8 = 3; lfe8 = 4; bl8 = 5; br8 = 6; sl8 = 7; sr8 = 8; fl6 = 1; fr6 = 2; fc6 = 3; lfe6 = 4; sl6 = 5; sr6 = 6; m = zeros(8,8); @@ -63,50 +76,69 @@ function sof_selector_blobs() m(sr6, br8) = 1; m(lfe6, lfe8) = 1; sel.coeffs = m; - write_blob(sel, "downmix_71_to_51"); + eightch_to_sixch_pack8 = write_blob(sel, "downmix_71_to_51"); - % 7.1 to 5.1 downmix + % 7.1 to stereo downmix + sel.ch_count = [8 2]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_STEREO]; fl = 1; fr = 2; fc = 3; lfe = 4; bl = 5; br = 6; sl = 7; sr = 8; m = zeros(8,8); m(1, fl) = 1.0000; m(1, fr) = 0.0000; m(1, fc) = 0.7071; m(1, sl) = 0.7071; m(1, sr) = 0.0000; m(1, bl) = 0.7071; m(1, br) = 0.0000; m(2, fl) = 0.0000; m(2, fr) = 1.0000; m(2, fc) = 0.7071; m(2, sl) = 0.0000; m(2, sr) = 0.7071; m(2, bl) = 0.0000; m(2, br) = 0.7071; sel.coeffs = m; - write_blob(sel, "downmix_71_to_stereo"); sel.coeffs(1, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to left sel.coeffs(2, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to right - write_blob(sel, "downmix_71_to_stereo_with_lfe"); + eightch_to_stereo_pack8 = write_blob(sel, "downmix_71_to_stereo_with_lfe"); % 7.1 to mono downmix + sel.ch_count = [8 1]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_MONO]; fl = 1; fc = 3; fr = 2; sr = 8; br = 6; bl = 5; sl = 7; lfe = 4; m = zeros(8,8); m(1, fl) = 0.7071; m(1, fr) = 0.7071; m(1, fc) = 1.0000; m(1, sl) = 0.5000; m(1, sr) = 0.5000; m(1, bl) = 0.5000; m(1, br) = 0.5000; sel.coeffs = m; - write_blob(sel, "downmix_71_to_mono"); m(1, lfe) = 10^(+19/20); % +10 dB - write_blob(sel, "downmix_71_to_mono_with_lfe"); + eightch_to_mono_pack8 = write_blob(sel, "downmix_71_to_mono_with_lfe"); % mono to stereo upmix + sel.ch_count = [1 2]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_STEREO]; sel.coeffs = zeros(8,8); sel.coeffs(1, 1) = 10^(-3/20); sel.coeffs(2, 1) = 10^(-3/20); - write_blob(sel, "upmix_mono_to_stereo"); + mono_to_stereo_pack8 = write_blob(sel, "upmix_mono_to_stereo"); % mono to 5.1 / 7.1 upmix - fc = 3 + sel.ch_count = [1 6]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_5_POINT_1]; + fc = 3; sel.coeffs = zeros(8,8); sel.coeffs(fc, 1) = 1; - write_blob(sel, "upmix_mono_to_51"); - write_blob(sel, "upmix_mono_to_71"); + mono_to_sixch_pack8 = write_blob(sel, "upmix_mono_to_51"); + sel.ch_count = [1 8]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_7_POINT_1]; + mono_to_eightch_pack8 = write_blob(sel, "upmix_mono_to_71"); % stereo to 5.1 / 7.1 upmix + sel.ch_count = [2 6]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_5_POINT_1]; fl = 1; fr = 2; sel.coeffs = zeros(8,8); sel.coeffs(fl, 1) = 1; sel.coeffs(fr, 2) = 1; - write_blob(sel, "upmix_stereo_to_51"); - write_blob(sel, "upmix_stereo_to_71"); + stereo_to_sixch_pack8 = write_blob(sel, "upmix_stereo_to_51"); + sel.ch_count = [2 8]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_7_POINT_1]; + stereo_to_eightch_pack8 = write_blob(sel, "upmix_stereo_to_71"); + + % For blob format with multiple up/down-mix profiles, intended + % for playback decoder offload conversions. + multi_pack8 = [passthrough_pack8 mono_to_stereo_pack8 sixch_to_stereo_pack8 eightch_to_stereo_pack8]; + write_8bit_packed(multi_pack8, 'stereo_endpoint_playback_updownmix'); % Stereo to L,L,R,R + sel.ch_count = [2 4]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO]; sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ... 1 0 0 0 0 0 0 0 ; ... 0 1 0 0 0 0 0 0 ; ... @@ -118,6 +150,8 @@ function sof_selector_blobs() write_blob(sel, "xover_selector_lr_to_llrr"); % Stereo to R,R,L,L + sel.ch_count = [2 4]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO]; sel.coeffs = [ 0 1 0 0 0 0 0 0 ; ... 0 1 0 0 0 0 0 0 ; ... 1 0 0 0 0 0 0 0 ; ... @@ -129,6 +163,8 @@ function sof_selector_blobs() write_blob(sel, "xover_selector_lr_to_rrll"); % Stereo to L,R,L,R + sel.ch_count = [2 4]; + sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO]; sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ... 0 1 0 0 0 0 0 0 ; ... 1 0 0 0 0 0 0 0 ; ... @@ -142,32 +178,55 @@ function sof_selector_blobs() sof_selector_paths(false); end -function write_blob(sel, blobname) +function pack8 = write_blob(sel, blobname) + pack8 = pack_selector_config(sel); + pack8_copy = pack8; + pack8_copy(1:4) = uint8([0 0 0 0]); + write_8bit_packed(pack8_copy, blobname); +end + +function write_8bit_packed(pack8, blobname) + blob8 = sof_selector_build_blob(pack8); str_config = "selector_config"; str_exported = "Exported with script sof_selector_blobs.m"; - str_howto = "cd tools/tune/selector; octave sof_selector_blobs.m" + str_howto = "cd tools/tune/selector; octave sof_selector_blobs.m"; sof_tools = '../../../../tools'; sof_tplg = fullfile(sof_tools, 'topology'); sof_tplg_selector = fullfile(sof_tplg, 'topology2/include/components/micsel'); + tplg2_fn = sprintf("%s/%s.conf", sof_tplg_selector, blobname); + sof_check_create_dir(tplg2_fn); + sof_tplg2_write(tplg2_fn, blob8, str_config, str_exported, str_howto); +end - sel +function pack8 = pack_selector_config(sel) - sum_coefs = sum(sel.coeffs, 2)' - max_sum_coef = max(sum_coefs) + sum_coefs = sum(sel.coeffs, 2)'; + max_sum_coef = max(sum_coefs); if max_sum_coef > 1 scale = 1 / max_sum_coef; else scale = 1; end - scale sel.coeffs = scale .* sel.coeffs'; + coeffs_vec = reshape(sel.coeffs, 1, []); % convert to row vector + coeffs_q10 = int16(round(coeffs_vec .* 1024)); % Q6.10 + pack8 = uint8(zeros(1, 2 * length(coeffs_q10) + 4)); - blob8 = sof_selector_build_blob(sel); - tplg2_fn = sprintf("%s/%s.conf", sof_tplg_selector, blobname); - sof_check_create_dir(tplg2_fn); - sof_tplg2_write(tplg2_fn, blob8, str_config, str_exported, str_howto); -end + % header + j = 1; + pack8(j:j + 1) = uint8(sel.ch_count); + j = j + 2; + pack8(j:j + 1) = uint8(sel.ch_config); + j = j + 2; + + % coeffs matrix + for i = 1:length(coeffs_q10) + pack8(j:j+1) = int16_to_byte(coeffs_q10(i)); + j = j + 2; + end + + end function sof_selector_paths(enable) @@ -179,15 +238,11 @@ function sof_selector_paths(enable) end end -function blob8 = sof_selector_build_blob(sel) +function blob8 = sof_selector_build_blob(pack8) - s = size(sel.coeffs); blob_type = 0; blob_param_id = 0; % IPC4_SELECTOR_COEFFS_CONFIG_ID - data_length = s(1) * s(2) + 2; - data_size = 2 * data_length; % int16_t matrix - coeffs_vec = reshape(sel.coeffs, 1, []); % convert to row vector - coeffs_q10 = int16(round(coeffs_vec .* 1024)); % Q6.10 + data_size = length(pack8); ipc_ver = 4; [abi_bytes, abi_size] = sof_get_abi(data_size, ipc_ver, blob_type, blob_param_id); blob_size = data_size + abi_size; @@ -195,17 +250,7 @@ function sof_selector_paths(enable) blob8(1:abi_size) = abi_bytes; j = abi_size + 1; - % header - blob8(j:j+1) = int16_to_byte(int16(sel.rsvd0)); - j = j + 2; - blob8(j:j+1) = int16_to_byte(int16(sel.rsvd1)); - j = j + 2; - - % coeffs matrix - for i = 1:(s(1) * s(2)) - blob8(j:j+1) = int16_to_byte(coeffs_q10(i)); - j = j + 2; - end + blob8(j:j+data_size-1) = pack8; end function bytes = int16_to_byte(word) From 1e9d6fc557582be20a87a07a6f4c2c82d3de653a Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Mon, 9 Mar 2026 19:43:00 +0200 Subject: [PATCH 3/3] Tools: Topology: Micsel: Update configuration blobs This patch adds for micsel component configuration blob "stereo_endpoint_playback_updownmix.conf". It contains the needed up-mix and down-mix coefficients to handle playback of mono, 5.1 and 7.1 channels into stereo endpoint. The old configuration blobs with five and seven channels without LFE channel are removed. Despite the file names with "51" and "71" the blobs are not suitable for 6ch or 8ch handling. Signed-off-by: Seppo Ingalsuo --- .../components/micsel/downmix_51_to_mono.conf | 26 ------- .../micsel/downmix_51_to_stereo.conf | 26 ------- .../components/micsel/downmix_71_to_mono.conf | 26 ------- .../micsel/downmix_71_to_stereo.conf | 26 ------- .../stereo_endpoint_playback_updownmix.conf | 75 +++++++++++++++++++ 5 files changed, 75 insertions(+), 104 deletions(-) delete mode 100644 tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf delete mode 100644 tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf delete mode 100644 tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf delete mode 100644 tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf create mode 100644 tools/topology/topology2/include/components/micsel/stereo_endpoint_playback_updownmix.conf diff --git a/tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf b/tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf deleted file mode 100644 index cd1f65b86454..000000000000 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_mono.conf +++ /dev/null @@ -1,26 +0,0 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m -Object.Base.data."selector_config" { - bytes " - 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, - 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xd4,0x00,0xd4,0x00, - 0x2c,0x01,0x00,0x00,0x96,0x00,0x96,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00" -} diff --git a/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf b/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf deleted file mode 100644 index 3f7b32a251d8..000000000000 --- a/tools/topology/topology2/include/components/micsel/downmix_51_to_stereo.conf +++ /dev/null @@ -1,26 +0,0 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m -Object.Base.data."selector_config" { - bytes " - 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, - 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xa8,0x01,0x00,0x00, - 0x2c,0x01,0x00,0x00,0x2c,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xa8,0x01, - 0x2c,0x01,0x00,0x00,0x00,0x00,0x2c,0x01, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00" -} diff --git a/tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf b/tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf deleted file mode 100644 index 1e67d36bf839..000000000000 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_mono.conf +++ /dev/null @@ -1,26 +0,0 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m -Object.Base.data."selector_config" { - bytes " - 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, - 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xa4,0x00,0xa4,0x00, - 0xe8,0x00,0x00,0x00,0x74,0x00,0x74,0x00, - 0x74,0x00,0x74,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00" -} diff --git a/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf b/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf deleted file mode 100644 index df6af38dde34..000000000000 --- a/tools/topology/topology2/include/components/micsel/downmix_71_to_stereo.conf +++ /dev/null @@ -1,26 +0,0 @@ -# Exported with script sof_selector_blobs.m 04-Jun-2025 -# cd tools/tune/selector; octave sof_selector_blobs.m -Object.Base.data."selector_config" { - bytes " - 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, - 0x84,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x01,0x00,0x00, - 0xe8,0x00,0x00,0x00,0xe8,0x00,0x00,0x00, - 0xe8,0x00,0x00,0x00,0x00,0x00,0x48,0x01, - 0xe8,0x00,0x00,0x00,0x00,0x00,0xe8,0x00, - 0x00,0x00,0xe8,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00" -} diff --git a/tools/topology/topology2/include/components/micsel/stereo_endpoint_playback_updownmix.conf b/tools/topology/topology2/include/components/micsel/stereo_endpoint_playback_updownmix.conf new file mode 100644 index 000000000000..70500ef0e0fa --- /dev/null +++ b/tools/topology/topology2/include/components/micsel/stereo_endpoint_playback_updownmix.conf @@ -0,0 +1,75 @@ +# Exported with script sof_selector_blobs.m 10-Mar-2026 +# cd tools/tune/selector; octave sof_selector_blobs.m +Object.Base.data."selector_config" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0x10,0x02,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x08,0x0c,0x0c,0x00,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x04,0x01,0x02,0x00,0x01, + 0xd5,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd5,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x06,0x02,0x08,0x01,0x00,0x01,0x00,0x00, + 0xb5,0x00,0x96,0x01,0xb5,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0xb5,0x00,0x96,0x01,0x00,0x00,0xb5,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x02,0x0c,0x01, + 0xda,0x00,0x00,0x00,0x9a,0x00,0x59,0x01, + 0x9a,0x00,0x00,0x00,0x9a,0x00,0x00,0x00, + 0x00,0x00,0xda,0x00,0x9a,0x00,0x59,0x01, + 0x00,0x00,0x9a,0x00,0x00,0x00,0x9a,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00" +}