From 064f41174b0baa0654b94edc3a82e43b30c2d653 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 | 134 ++++++++++++++++++++++++-- src/audio/selector/selector_generic.c | 6 +- src/include/sof/audio/selector.h | 18 +++- 3 files changed, 144 insertions(+), 14 deletions(-) diff --git a/src/audio/selector/selector.c b/src/audio/selector/selector.c index 40a4a2c5b888..ead9f68ffd63 100644 --- a/src/audio/selector/selector.c +++ b/src/audio/selector/selector.c @@ -572,9 +572,9 @@ static void build_config(struct comp_data *cd, struct module_config *cfg) cd->config.out_channels_count = out_fmt->channels_count; /* Build default coefficient array (unity Q10 on diagonal, i.e. pass-through mode) */ - memset(&cd->coeffs_config, 0, sizeof(cd->coeffs_config)); + 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) @@ -620,6 +620,18 @@ static int selector_init(struct processing_module *mod) cd->sel_ipc4_cfg.init_payload_fmt = payload_fmt; md->private = cd; + /* Allocate space for max number of configurations. */ + cd->multi_coeffs_config_size = + SEL_MAX_NUM_CONFIGS * sizeof(struct ipc4_selector_coeffs_config); + cd->multi_coeffs_config = mod_zalloc(mod, cd->multi_coeffs_config_size); + if (!cd->multi_coeffs_config) { + mod_free(mod, cd); + return -ENOMEM; + } + + /* Default configuration is set to first configuration */ + cd->coeffs_config = &cd->multi_coeffs_config[0]; + if (payload_fmt == IPC4_SEL_INIT_PAYLOAD_BASE_WITH_EXT) { size_t size = sizeof(struct sof_selector_ipc4_pin_config); @@ -733,6 +745,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,12 +782,26 @@ 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); + int n; if (config_id == IPC4_SELECTOR_COEFFS_CONFIG_ID) { - if (data_offset_size != sizeof(cd->coeffs_config)) + if (data_offset_size > cd->multi_coeffs_config_size) { + comp_err(mod->dev, "The configuration blob is too large"); return -EINVAL; + } - memcpy_s(&cd->coeffs_config, sizeof(cd->coeffs_config), fragment, data_offset_size); + /* 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 (data_offset_size != n * sizeof(struct ipc4_selector_coeffs_config)) { + comp_err(mod->dev, "Invalid configuration size."); + return -EINVAL; + } + + memcpy_s(cd->multi_coeffs_config, cd->multi_coeffs_config_size, + fragment, data_offset_size); + cd->num_configs = n; return 0; } @@ -799,11 +826,23 @@ 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; + uint32_t samples; comp_dbg(mod->dev, "entry"); + if (cd->passthrough) { + source = input_buffers->data; + sink = output_buffers->data; + samples = avail_frames * audio_stream_get_channels(source); + audio_stream_copy(source, 0, sink, 0, samples); + 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); @@ -811,6 +850,83 @@ static int selector_process(struct processing_module *mod, return 0; } +/** + * \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, int source_channels, + int sink_channels) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + int i, j; + int16_t coef; + bool found; + + /* The first config for coefficients always exists. It originates from configuration + * blob for a single mix profile or from default values (1.0) from set_selector_params(). + */ + cd->coeffs_config = cd->multi_coeffs_config; + + if (cd->num_configs > 1) { + /* A pass-through blob with two or more configurations is for the max + * channels (8) amount. + */ + if (source_channels == sink_channels) { + source_channels = SEL_SOURCE_CHANNELS_MAX; + sink_channels = SEL_SINK_CHANNELS_MAX; + } + + found = false; + 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) { + cd->coeffs_config = &cd->multi_coeffs_config[i]; + found = true; + break; + } + } + + if (!found) { + comp_err(dev, "No mix coefficients found for %d to %d channels.", + source_channels, sink_channels); + return -EINVAL; + } + } + + /* 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 Prepares selector component for processing. * \param[in,out] mod Selector base module device. @@ -825,6 +941,8 @@ static int selector_prepare(struct processing_module *mod, struct comp_dev *dev = mod->dev; struct comp_buffer *sinkb, *sourceb; size_t sink_size; + unsigned int source_channels; + unsigned int sink_channels; int ret; comp_dbg(dev, "entry"); @@ -855,9 +973,9 @@ static int selector_prepare(struct processing_module *mod, * 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)); + source_channels = audio_stream_get_channels(&sourceb->stream); + sink_channels = audio_stream_get_channels(&sinkb->stream); + comp_dbg(dev, "source sink channel = %u %u", source_channels, sink_channels); sink_size = audio_stream_get_size(&sinkb->stream); @@ -891,7 +1009,7 @@ static int selector_prepare(struct processing_module *mod, return -EINVAL; } - return 0; + return selector_find_coefficients(mod, source_channels, sink_channels); } /** diff --git a/src/audio/selector/selector_generic.c b/src/audio/selector/selector_generic.c index 5b4d8d8f5edd..9f336536585a 100644 --- a/src/audio/selector/selector_generic.c +++ b/src/audio/selector/selector_generic.c @@ -228,7 +228,7 @@ static void sel_s16le(struct processing_module *mod, struct input_stream_buffer n = MIN(n, nmax); for (i = 0; i < n; i++) { process_frame_s16le(dest, n_chan_sink, src, n_chan_source, - &cd->coeffs_config); + cd->coeffs_config); src += audio_stream_get_channels(source); dest += audio_stream_get_channels(sink); } @@ -301,7 +301,7 @@ static void sel_s24le(struct processing_module *mod, struct input_stream_buffer n = MIN(n, nmax); for (i = 0; i < n; i++) { process_frame_s24le(dest, n_chan_sink, src, n_chan_source, - &cd->coeffs_config); + cd->coeffs_config); src += audio_stream_get_channels(source); dest += audio_stream_get_channels(sink); } @@ -372,7 +372,7 @@ static void sel_s32le(struct processing_module *mod, struct input_stream_buffer n = MIN(n, nmax); for (i = 0; i < n; i++) { process_frame_s32le(dest, n_chan_sink, src, n_chan_source, - &cd->coeffs_config); + cd->coeffs_config); src += audio_stream_get_channels(source); dest += audio_stream_get_channels(sink); } diff --git a/src/include/sof/audio/selector.h b/src/include/sof/audio/selector.h index cd4aa926c7f4..42fba1399116 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; /**< source channels count when num_configs != 0 */ + uint8_t sink_channels_count; /**< sink channels count when num_configs != 0 */ + uint8_t source_channel_config; /**< source channels config when num_configs != 0 */ + uint8_t sink_channel_config; /**< sink channels config when num_configs != 0 */ /** Mixing coefficients in Q10 fixed point format */ int16_t coeffs[SEL_SINK_CHANNELS_MAX][SEL_SOURCE_CHANNELS_MAX]; @@ -108,7 +116,9 @@ typedef void (*sel_func)(struct comp_dev *dev, struct audio_stream *sink, 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 *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,8 @@ 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; /**< two or more if multiple mix blobs received */ + bool passthrough; /**< Use a passthrough copy function when no up/down mix */ }; /** \brief Selector processing functions map. */ From 01532fc48a030da7aaf70b7cbb98200a795fac0d 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 | 145 ++++++++++++------- 1 file changed, 96 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..d3c0130dcd67 100644 --- a/src/audio/selector/tune/sof_selector_blobs.m +++ b/src/audio/selector/tune/sof_selector_blobs.m @@ -11,46 +11,60 @@ % 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.ch_out = 8; 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 +77,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 +151,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 +164,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 +179,56 @@ 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(2 * length(coeffs_q10) + 4); + rsvd1 = 0; - 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 +240,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 +252,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 c7c5e4132411cc0b78f965cd7d1c232a64df3042 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" +}