From d95851d4fb77b928c184dcabeb16f96e09691b1b Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Wed, 27 Aug 2025 19:03:14 +0300 Subject: [PATCH 1/6] Audio: EQIIR/Selector: Add scripts to produce crossover blobs This patch adds to script sof_selector_blobs.m export of stereo stream upmix to duplicated channels. The duplicated channels are then filtered with desired crossover characteristics. The added script for IIR EQ sof_example_lr4.m exports 4th order Linkwitz-Riley files for crossover low-pass and high-pass. The added example should be quite typical for laptop PCs. With larger speakers the frequency could be lower, with smaller higher. Also the crossover order could be changed to lower and higher. The 4th order should be quite typical. Signed-off-by: Seppo Ingalsuo --- src/audio/eq_iir/tune/sof_example_lr4.m | 183 +++++++++++++++++++ src/audio/selector/tune/sof_selector_blobs.m | 44 ++++- 2 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 src/audio/eq_iir/tune/sof_example_lr4.m diff --git a/src/audio/eq_iir/tune/sof_example_lr4.m b/src/audio/eq_iir/tune/sof_example_lr4.m new file mode 100644 index 000000000000..d38111da6b98 --- /dev/null +++ b/src/audio/eq_iir/tune/sof_example_lr4.m @@ -0,0 +1,183 @@ +% sof_example_lr4 - Design 4th order Linkwitz–Riley filter bank +% +% This script is run without arguments. It creates IIR equalizer +% blobs for crossover filter bank for 2-way speaker and four +% channels stream. The exported configurations are Linkwitz-Riley +% 4th order with crossover frequency at 2 kHz. The filters are +% in order: +% - low, high, low, high +% - low, low, high, high +% - high, high, low, low +% + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright (c) 2025, Intel Corporation. +% +% Author: Seppo Ingalsuo + +function sof_example_lr4() + +%% Common definitions +fs = 48e3; +fc = 2e3; +sof_tools = '../../../../tools'; +tpath = fullfile(sof_tools, 'topology/topology2/include/components/eqiir'); +cpath = fullfile(sof_tools, 'ctl/ipc4/eq_iir'); + +sof_eq_paths(1); + +%% -------------------------------------------------- +%% Example: Band-split 2ch to 4ch low and high bands +%% -------------------------------------------------- +design_name = sprintf('xover_lr4_%dhz_lhlh_%dkhz', fc, round(fs/1000)); +blob_fn = fullfile(cpath, [design_name '.bin']); +alsa_fn = fullfile(cpath, [design_name '.txt']); +tplg_fn = fullfile(tpath, [design_name '.conf']); +comment = 'LR4 filter bank coefficients'; +howto = 'cd src/audio/eq_iir/tune; octave sof_example_lr4.m'; + +% Design low-pass and high-pass filters +eq_lo = lo_band_iir(fs, fc); +eq_hi = hi_band_iir(fs, fc); + +% Quantize and pack filter coefficients plus shifts etc. +bq_lo = sof_eq_iir_blob_quant(eq_lo.p_z, eq_lo.p_p, eq_lo.p_k); +bq_hi = sof_eq_iir_blob_quant(eq_hi.p_z, eq_hi.p_p, eq_hi.p_k); + +% Build blob +channels_in_config = 4; % Setup max 4 channels EQ +assign_response = [0 1 0 1]; % Order: lo, hi, lo, hi +num_responses = 2; % Two responses: lo, hi +bm = sof_eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [bq_lo bq_hi]); + +% Pack and write file +sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto) + +%% -------------------------------------------------- +%% Example: Same but filters order is lo, lo, hi, hi +%% -------------------------------------------------- + +design_name = sprintf('xover_lr4_%dhz_llhh_%dkhz', fc, round(fs/1000)); +blob_fn = fullfile(cpath, [design_name '.bin']); +alsa_fn = fullfile(cpath, [design_name '.txt']); +tplg_fn = fullfile(tpath, [design_name '.conf']); + +assign_response = [0 0 1 1]; +num_responses = 2; +bm = sof_eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [bq_lo bq_hi]); + +% Pack and write file +sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto) + +%% -------------------------------------------------- +%% Example: Same but filters order is hi, hi, lo, lo +%% -------------------------------------------------- + +design_name = sprintf('xover_lr4_%dhz_hhll_%dkhz', fc, round(fs/1000)); +blob_fn = fullfile(cpath, [design_name '.bin']); +alsa_fn = fullfile(cpath, [design_name '.txt']); +tplg_fn = fullfile(tpath, [design_name '.conf']); + +assign_response = [1 1 0 0]; +num_responses = 2; +bm = sof_eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [bq_lo bq_hi]); + +% Pack and write file +sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto) + +%% ------------------------------------ +%% Done. +%% ------------------------------------ + +sof_eq_paths(0); +end + +%% ------------------- +%% EQ design functions +%% ------------------- + +function eq = lo_band_iir(fs, fc) + + +%% Get defaults for equalizer design +eq = sof_eq_defaults(); +eq.fs = fs; +eq.enable_iir = 1; +eq.iir_norm_type = 'peak'; +eq.iir_norm_offs_db = 0; + +% Parametric EQs are PEQ_HP1, PEQ_HP2, PEQ_LP1, PEQ_LP2, PEQ_LS1, +% PEQ_LS2, PEQ_HS1, PEQ_HS2 = 8, PEQ_PN2, PEQ_LP4, and PEQ_HP4. +% +% Parametric EQs take as second argument the cutoff frequency in Hz +% and as second argument a dB value (can use 0 for LP2). The +% Third argument is a Q-value (can use 0 for LP2). + +% Two 2nd order butterworth low-pass filters for 4th order Linkwitz–Riley +eq.peq = [ ... + eq.PEQ_LP2 fc 0 0 ; ... + eq.PEQ_LP2 fc 0 0 ; ... + ]; + +%% Design EQ +eq = sof_eq_compute(eq); + +%% Plot +sof_eq_plot(eq); + +end + +function eq = hi_band_iir(fs, fc) + + +%% Get defaults for equalizer design +eq = sof_eq_defaults(); +eq.fs = fs; +eq.enable_iir = 1; +eq.iir_norm_type = 'peak'; +eq.iir_norm_offs_db = 0; + +% Two 2nd order high-pass filters for 4th order Linkwitz–Riley +eq.peq = [ ... + eq.PEQ_HP2 fc 0 0 ; ... + eq.PEQ_HP2 fc 0 0 ; ... + ]; + +%% Design EQ +eq = sof_eq_compute(eq); + +%% Plot +sof_eq_plot(eq); + +end + + + +% Pack and write file common function for all exports +function sof_eq_pack_export(bm, bin_fn, ascii_fn, tplg_fn, note, howto) + +bp = sof_eq_iir_blob_pack(bm, 4); % IPC4 + +if ~isempty(bin_fn) + sof_ucm_blob_write(bin_fn, bp); +end + +if ~isempty(ascii_fn) + sof_alsactl_write(ascii_fn, bp); +end + +if ~isempty(tplg_fn) + sof_tplg2_write(tplg_fn, bp, 'IIR', note, howto); +end + +end diff --git a/src/audio/selector/tune/sof_selector_blobs.m b/src/audio/selector/tune/sof_selector_blobs.m index 10ea1879b74a..83b38be64e3e 100644 --- a/src/audio/selector/tune/sof_selector_blobs.m +++ b/src/audio/selector/tune/sof_selector_blobs.m @@ -1,4 +1,13 @@ -% Export configuration blobs for Selector +% sof_selector_blobs - export configuration blobs for Selector +% +% This script is run without arguments. It exports a number of +% configuration blobs for selector/micsel component. The first +% category of blobs are for upmix and downmix between mono to +% 7.1 channel audio formats. +% +% The second category is for duplicating stereo to four channels +% for 2-way speaker crossover filter. +% % SPDX-License-Identifier: BSD-3-Clause % @@ -97,6 +106,39 @@ function sof_selector_blobs() write_blob(sel, "upmix_stereo_to_51"); write_blob(sel, "upmix_stereo_to_71"); + % Stereo to L,L,R,R + 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 ; ... + 0 1 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ]; + write_blob(sel, "xover_selector_lr_to_llrr"); + + % Stereo to R,R,L,L + 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 ; ... + 1 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ]; + write_blob(sel, "xover_selector_lr_to_rrll"); + + % Stereo to L,R,L,R + 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 ; ... + 0 1 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ; ... + 0 0 0 0 0 0 0 0 ]; + write_blob(sel, "xover_selector_lr_to_lrlr"); + sof_selector_paths(false); end From 28b91d4da2ec5e9978d2b23b0a00ce632ad00448 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Fri, 5 Sep 2025 16:10:28 +0300 Subject: [PATCH 2/6] Tools: Topology: Copy sdw-amp-generic.conf to sdw-amp-xover.conf The files are in this patch identical and next patches will do the modifications to add the DSP crossover filter feature. Signed-off-by: Seppo Ingalsuo --- .../platform/intel/sdw-amp-xover.conf | 834 ++++++++++++++++++ 1 file changed, 834 insertions(+) create mode 100644 tools/topology/topology2/platform/intel/sdw-amp-xover.conf diff --git a/tools/topology/topology2/platform/intel/sdw-amp-xover.conf b/tools/topology/topology2/platform/intel/sdw-amp-xover.conf new file mode 100644 index 000000000000..303db9515253 --- /dev/null +++ b/tools/topology/topology2/platform/intel/sdw-amp-xover.conf @@ -0,0 +1,834 @@ +# route and pipeline index start from pcm id * 10 + +Define { + SDW_SPK_STREAM 'SDW1-Playback' + SDW_SPK_IN_STREAM 'SDW1-Capture' + ALH_2ND_SPK_ID 22 + ALH_3RD_SPK_ID 23 + ALH_2ND_SPK_IN_ID 32 + ALH_3RD_SPK_IN_ID 33 + SDW_AMP_BE_ID 2 + SDW_AMP_IN_BE_ID 3 + AMP_FEEDBACK_CH 2 + AMP_FEEDBACK_CH_PER_LINK 2 + SDW_AMP_FEEDBACK true + AMP_PLAYBACK_NAME 'Speaker Playback' +} + +Object.Dai.ALH [ + { + dai_index 20 + id $SDW_AMP_BE_ID + direction "playback" + name $SDW_SPK_STREAM + default_hw_conf_id 0 + rate 48000 + channels 2 + + Object.Base.hw_config.1 { + id 0 + name "ALH514" + } + } +] + +IncludeByKey.PASSTHROUGH { +"false" { + Object.Pipeline { + host-copier-gain-mixin-playback [ + { + index 20 + + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback [ + { + index 21 + + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME IIR Eq bytes' + } + } + Object.Widget.eqfir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME FIR Eq bytes' + } + } + Object.Widget.drc.1 { + Object.Control { + bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC bytes' + } + mixer."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC switch' + } + } + } + } + ] + } + "false" { + mixout-gain-alh-dai-copier-playback [ + { + index 21 + + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + } + } + + } + } +"true" { + Object.Pipeline.host-gateway-playback [ + { + index 20 + Object.Widget.host-copier.1 { + stream_name "sdw amplifiers" + pcm_id 2 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 3 + Object.Base.output_audio_format [ + { + out_bit_depth 16 + out_valid_bit_depth 16 + } + { + out_bit_depth 32 + out_valid_bit_depth 24 + } + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + } + ] + + Object.Widget { + alh-copier [ + { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_pins 1 + direction playback + type dai_in + index 21 + num_input_audio_formats 3 + Object.Base.input_audio_format [ + { + in_bit_depth 16 + in_valid_bit_depth 16 + } + { + in_bit_depth 32 + in_valid_bit_depth 24 + } + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + pipeline [ + { + index 21 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + } +} + +IncludeByKey.NUM_SDW_AMP_LINKS { +"2" { + Define { + AMP_FEEDBACK_CH 4 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + # Add a virtual widget to connect the aggregated 2nd DAI copier + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + IncludeByKey.PASSTHROUGH { + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + ] + } + "true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "virtual.sdw-amp" + } + ] + } + } + Object.Base.route [ + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + ] + } + +"3" { + Define { + AMP_FEEDBACK_CH 6 + AMP_FEEDBACK_CH_PER_LINK 2 + } + + Object.Widget { + alh-copier [ + { + index $ALH_2ND_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 1 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + { + index $ALH_3RD_SPK_ID + type dai_in + stream_name $SDW_SPK_STREAM + dai_index 2 + type "dai_in" + direction "playback" + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_input_pins 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] + } + ] + IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + alh-copier [ + { + index $ALH_2ND_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 1 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + { + index $ALH_3RD_SPK_IN_ID + type dai_out + stream_name $SDW_SPK_IN_STREAM + dai_index 2 + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + } + ] + } + } + pipeline [ + { + index $ALH_2ND_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_2ND_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + { + index $ALH_3RD_SPK_IN_ID + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + virtual [ + { + name 'virtual.sdw-amp' + type output + index $ALH_2ND_SPK_ID + } + { + name 'virtual.sdw-amp' + type output + index $ALH_3RD_SPK_ID + } + ] + } + + # Add the connection from the gain module to the aggregated 2nd DAI copier + # via the virtual widget. The virtual widget ensures that the routes between + # the gain and copier do not get established in the firmware. These are purely + # to show the existence of aggregation in the topology graph. + IncludeByKey.PASSTHROUGH { + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "virtual.sdw-amp" + } + ] + } + "true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "virtual.sdw-amp" + } + ] + } + } + Object.Base.route [ + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.2" + } + ] + } +} + +Object.PCM.pcm [ + { + name "Speaker" + id 2 + direction "playback" + Object.Base.fe_dai.1 { + name "Speaker" + } + + Object.PCM.pcm_caps.1 { + name "sdw amplifiers" + formats 'S16_LE,S24_LE,S32_LE' + } + } +] + +IncludeByKey.PASSTHROUGH { +"false" { + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + Object.Base.route [ + { + source "drc.21.1" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + "false" { + Object.Base.route [ + { + source "gain.21.1" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] + } + } + Object.Base.route [ + { + source 'mixin.20.1' + sink 'mixout.21.1' + } + { + source 'host-copier.2.playback' + sink 'gain.20.1' + } + ] +} +"true" { + Object.Base.route [ + { + source "host-copier.2.playback" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + ] +} +} + +IncludeByKey.SDW_AMP_FEEDBACK { + "true" { + Object.Dai.ALH [ + { + dai_index 30 + id $SDW_AMP_IN_BE_ID + direction "capture" + name $SDW_SPK_IN_STREAM + default_hw_conf_id 1 + rate 48000 + channels $AMP_FEEDBACK_CH_PER_LINK + + Object.Base.hw_config.1 { + id 1 + name "ALH515" + } + } + ] + Object.Pipeline { + host-gateway-capture [ + { + index 30 + + Object.Widget.host-copier.1 { + stream_name "amp feedback" + pcm_id 3 + + IncludeByKey.AMP_FEEDBACK_CH { + "6" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 6 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + { + out_channels 6 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + ] + } + "8" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 8 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + { + out_channels 8 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + ] + } + } + } + } + ] + } + Object.Widget { + alh-copier [ + { + index 31 + type dai_out + stream_name $SDW_SPK_IN_STREAM + type "dai_out" + direction "capture" + node_type $ALH_LINK_INPUT_CLASS + num_input_audio_formats 1 + num_output_audio_formats 1 + num_output_pins 1 + + Object.Base.input_audio_format [ + { + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + in_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + in_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + in_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + in_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + + in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + Object.Base.output_audio_format [ + { + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 + + IncludeByKey.AMP_FEEDBACK_CH { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + "6" { + out_ch_cfg $CHANNEL_CONFIG_5_POINT_1 + out_ch_map $CHANNEL_MAP_5_POINT_1 + } + "8" { + out_ch_cfg $CHANNEL_CONFIG_7_POINT_1 + out_ch_map $CHANNEL_MAP_7_POINT_1 + } + } + } + ] + } + ] + pipeline [ + { + index 31 + priority 0 + lp_mode 0 + dynamic_pipeline 1 + } + ] + } + Object.PCM.pcm [ + { + name "Amp feedback" + id 3 + direction "capture" + Object.Base.fe_dai.1 { + name "Amp feedback" + } + + Object.PCM.pcm_caps.1 { + name "amp feedback" + formats 'S16_LE,S24_LE,S32_LE' + channels_min $AMP_FEEDBACK_CH + channels_max $AMP_FEEDBACK_CH + } + } + ] + Object.Base.route [ + { + source "alh-copier.$SDW_SPK_IN_STREAM.0" + sink "host-copier.3.capture" + } + ] + } +} From 3ff72667724191b2b16a7d3418231d166179dc0f Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Fri, 5 Sep 2025 16:31:41 +0300 Subject: [PATCH 3/6] Tools: Topology: Modify sdw-amp-xover.conf to add crossover This patch replaces the speaker endpoint render pipeline with a version that adds selector/micsel and EQIIR components to perform processing for crossover filter. When the channels count is set to four for stereo woofer and tweeter the channel map and channel configuration for alh-copier are set to 4ch (the used define has somewhat confusing name 3.1). The selector input is two channels and the output four channels. The EQIIR input and output are four channels. The selector and IIR blobs are set to perform desired channels duplication and filtering. The patch also modifies amplifier feedback stream and host copier for 4ch. The feedback data can be also used to debug the playback feature. The crossover processed playback can be captured if the kernel and the amplifiers are set to use the feedback. The PASSTHROUGH define support is removed from this topology variant to keep it simpler. The normal sdw-amp-generic.conf should be used for the need. TODO: Add FIR for delays adjust with fractional delay response. Signed-off-by: Bard Liao Signed-off-by: Seppo Ingalsuo --- .../platform/intel/sdw-amp-xover.conf | 783 +++++++++++------- 1 file changed, 476 insertions(+), 307 deletions(-) diff --git a/tools/topology/topology2/platform/intel/sdw-amp-xover.conf b/tools/topology/topology2/platform/intel/sdw-amp-xover.conf index 303db9515253..3347d4911bc4 100644 --- a/tools/topology/topology2/platform/intel/sdw-amp-xover.conf +++ b/tools/topology/topology2/platform/intel/sdw-amp-xover.conf @@ -12,18 +12,17 @@ Define { AMP_FEEDBACK_CH 2 AMP_FEEDBACK_CH_PER_LINK 2 SDW_AMP_FEEDBACK true - AMP_PLAYBACK_NAME 'Speaker Playback' + AMP_PLAYBACK_NAME 'Speaker Playback' } Object.Dai.ALH [ { dai_index 20 - id $SDW_AMP_BE_ID + id $SDW_AMP_BE_ID direction "playback" name $SDW_SPK_STREAM default_hw_conf_id 0 rate 48000 - channels 2 Object.Base.hw_config.1 { id 0 @@ -32,218 +31,380 @@ Object.Dai.ALH [ } ] -IncludeByKey.PASSTHROUGH { -"false" { - Object.Pipeline { - host-copier-gain-mixin-playback [ - { - index 20 - - Object.Widget.host-copier.1 { - stream_name "sdw amplifiers" - pcm_id 2 - } - Object.Widget.gain.1 { - Object.Control.mixer.1 { - name 'Pre Mixer $AMP_PLAYBACK_NAME Volume' - } - } - } - ] - - IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { - "true" { - mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback [ - { - index 21 - - Object.Widget.alh-copier.1 { - stream_name $SDW_SPK_STREAM - node_type $ALH_LINK_OUTPUT_CLASS - num_input_audio_formats 3 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 - } - { - in_bit_depth 32 - in_valid_bit_depth 24 - } - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - num_output_audio_formats 1 - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth $SDW_LINK_VALID_BITS - out_sample_type $SAMPLE_TYPE_MSB_INTEGER - out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" - } - ] - } - Object.Widget.gain.1 { - Object.Control.mixer.1 { - name 'Post Mixer $AMP_PLAYBACK_NAME Volume' - } - } - - Object.Widget.eqiir.1 { - Object.Control.bytes."1" { - name 'Post Mixer $AMP_PLAYBACK_NAME IIR Eq bytes' - } - } - Object.Widget.eqfir.1 { - Object.Control.bytes."1" { - name 'Post Mixer $AMP_PLAYBACK_NAME FIR Eq bytes' - } - } - Object.Widget.drc.1 { - Object.Control { - bytes."1" { - name 'Post Mixer $AMP_PLAYBACK_NAME DRC bytes' - } - mixer."1" { - name 'Post Mixer $AMP_PLAYBACK_NAME DRC switch' - } - } - } - } - ] - } - "false" { - mixout-gain-alh-dai-copier-playback [ - { - index 21 - - Object.Widget.alh-copier.1 { - stream_name $SDW_SPK_STREAM - node_type $ALH_LINK_OUTPUT_CLASS - num_input_audio_formats 3 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 - } - { - in_bit_depth 32 - in_valid_bit_depth 24 - } - { - in_bit_depth 32 - in_valid_bit_depth 32 - } - ] - num_output_audio_formats 1 - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth $SDW_LINK_VALID_BITS - out_sample_type $SAMPLE_TYPE_MSB_INTEGER - out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" - } - ] - } - Object.Widget.gain.1 { - Object.Control.mixer.1 { - name 'Post Mixer $AMP_PLAYBACK_NAME Volume' - } - } - } - ] - } - } - - } - } -"true" { - Object.Pipeline.host-gateway-playback [ +Object.Pipeline { + host-copier-gain-mixin-playback [ { - index 20 + index 20 + Object.Widget.host-copier.1 { stream_name "sdw amplifiers" pcm_id 2 - num_input_audio_formats 3 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 + } + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Pre Mixer $AMP_PLAYBACK_NAME Volume' + } + } + } + ] + + IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { + "true" { + mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback [ + { + index 21 + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_bit_depth 32 + in_valid_bit_depth 32 + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] } - { - in_bit_depth 32 - in_valid_bit_depth 24 + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } } - { - in_bit_depth 32 - in_valid_bit_depth 32 + Object.Widget.eqiir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME IIR Eq bytes' + } } - ] - num_output_audio_formats 3 - Object.Base.output_audio_format [ - { - out_bit_depth 16 - out_valid_bit_depth 16 + Object.Widget.eqfir.1 { + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME FIR Eq bytes' + } } - { - out_bit_depth 32 - out_valid_bit_depth 24 + Object.Widget.drc.1 { + Object.Control { + bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC bytes' + } + mixer."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME DRC switch' + } + } } - { - out_bit_depth 32 - out_valid_bit_depth 32 + Object.Widget.micsel.1 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover Selector bytes' + } } - ] - } + Object.Widget.eqiir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover IIR bytes' + } + } + Object.Widget.eqfir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover FIR bytes' + } + } + } + ] } - ] - - Object.Widget { - alh-copier [ - { - stream_name $SDW_SPK_STREAM - node_type $ALH_LINK_OUTPUT_CLASS - num_input_pins 1 - direction playback - type dai_in - index 21 - num_input_audio_formats 3 - Object.Base.input_audio_format [ - { - in_bit_depth 16 - in_valid_bit_depth 16 + "false" { + mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback [ + { + index 21 + Object.Widget.alh-copier.1 { + stream_name $SDW_SPK_STREAM + node_type $ALH_LINK_OUTPUT_CLASS + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_bit_depth 32 + in_valid_bit_depth 32 + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS + out_sample_type $SAMPLE_TYPE_MSB_INTEGER + out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + } + ] } - { - in_bit_depth 32 - in_valid_bit_depth 24 + Object.Widget.gain.1 { + Object.Control.mixer.1 { + name 'Post Mixer $AMP_PLAYBACK_NAME Volume' + } } - { - in_bit_depth 32 - in_valid_bit_depth 32 + Object.Widget.micsel.1 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover Selector bytes' + } } - ] - num_output_audio_formats 1 - Object.Base.output_audio_format [ - { - out_bit_depth 32 - out_valid_bit_depth $SDW_LINK_VALID_BITS - out_sample_type $SAMPLE_TYPE_MSB_INTEGER - out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" + Object.Widget.eqiir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover IIR bytes' + } } - ] - } - ] - pipeline [ - { - index 21 - priority 0 - lp_mode 0 - dynamic_pipeline 1 - } - ] - } + Object.Widget.eqfir.2 { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + name 'Post Mixer $AMP_PLAYBACK_NAME Xover FIR bytes' + } + } + } + ] + } } } @@ -269,14 +430,35 @@ IncludeByKey.NUM_SDW_AMP_LINKS { num_input_pins 1 Object.Base.input_audio_format [ { - in_bit_depth 32 - in_valid_bit_depth 32 + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + in_ch_cfg $CHANNEL_CONFIG_STEREO + in_ch_map $CHANNEL_MAP_STEREO + } + "4" { + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + in_bit_depth 32 + in_valid_bit_depth 32 + in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" } ] Object.Base.output_audio_format [ { - out_bit_depth 32 - out_valid_bit_depth $SDW_LINK_VALID_BITS + IncludeByKey.SDW_AMP_NUM_CHANNELS { + "2" { + out_ch_cfg $CHANNEL_CONFIG_STEREO + out_ch_map $CHANNEL_MAP_STEREO + } + "4" { + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + } + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS out_sample_type $SAMPLE_TYPE_MSB_INTEGER out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" } @@ -300,16 +482,18 @@ IncludeByKey.NUM_SDW_AMP_LINKS { Object.Base.input_audio_format [ { - in_bit_depth 32 - in_valid_bit_depth $SDW_LINK_VALID_BITS + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS in_sample_type $SAMPLE_TYPE_MSB_INTEGER in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" } ] Object.Base.output_audio_format [ { - out_bit_depth 32 - out_valid_bit_depth 32 + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 } ] } @@ -344,28 +528,14 @@ IncludeByKey.NUM_SDW_AMP_LINKS { # via the virtual widget. The virtual widget ensures that the routes between # the gain and copier do not get established in the firmware. These are purely # to show the existence of aggregation in the topology graph. - IncludeByKey.PASSTHROUGH { - "false" { - Object.Base.route [ - { - source "gain.21.1" - sink "virtual.sdw-amp" - } - ] - } - "true" { - Object.Base.route [ - { - source "host-copier.2.playback" - sink "virtual.sdw-amp" - } - ] - } - } Object.Base.route [ { - source "virtual.sdw-amp" - sink "alh-copier.$SDW_SPK_STREAM.1" + source "eqfir.21.2" + sink "virtual.sdw-amp" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" } ] } @@ -391,14 +561,14 @@ IncludeByKey.NUM_SDW_AMP_LINKS { num_input_pins 1 Object.Base.input_audio_format [ { - in_bit_depth 32 - in_valid_bit_depth 32 + in_bit_depth 32 + in_valid_bit_depth 32 } ] Object.Base.output_audio_format [ { - out_bit_depth 32 - out_valid_bit_depth $SDW_LINK_VALID_BITS + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS out_sample_type $SAMPLE_TYPE_MSB_INTEGER out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" } @@ -417,14 +587,14 @@ IncludeByKey.NUM_SDW_AMP_LINKS { num_input_pins 1 Object.Base.input_audio_format [ { - in_bit_depth 32 - in_valid_bit_depth 32 + in_bit_depth 32 + in_valid_bit_depth 32 } ] Object.Base.output_audio_format [ { - out_bit_depth 32 - out_valid_bit_depth $SDW_LINK_VALID_BITS + out_bit_depth 32 + out_valid_bit_depth $SDW_LINK_VALID_BITS out_sample_type $SAMPLE_TYPE_MSB_INTEGER out_fmt_cfg "$[($out_channels | ($out_valid_bit_depth * 256))]" } @@ -448,16 +618,18 @@ IncludeByKey.NUM_SDW_AMP_LINKS { Object.Base.input_audio_format [ { - in_bit_depth 32 - in_valid_bit_depth $SDW_LINK_VALID_BITS + in_channels $AMP_FEEDBACK_CH + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS in_sample_type $SAMPLE_TYPE_MSB_INTEGER in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" } ] Object.Base.output_audio_format [ { - out_bit_depth 32 - out_valid_bit_depth 32 + out_channels $AMP_FEEDBACK_CH + out_bit_depth 32 + out_valid_bit_depth 32 } ] } @@ -475,16 +647,17 @@ IncludeByKey.NUM_SDW_AMP_LINKS { Object.Base.input_audio_format [ { - in_bit_depth 32 - in_valid_bit_depth $SDW_LINK_VALID_BITS + in_bit_depth 32 + in_valid_bit_depth $SDW_LINK_VALID_BITS in_sample_type $SAMPLE_TYPE_MSB_INTEGER + in_channels $AMP_FEEDBACK_CH in_fmt_cfg "$[($in_channels | ($in_valid_bit_depth * 256))]" } ] Object.Base.output_audio_format [ { - out_bit_depth 32 - out_valid_bit_depth 32 + out_bit_depth 32 + out_valid_bit_depth 32 } ] } @@ -535,32 +708,18 @@ IncludeByKey.NUM_SDW_AMP_LINKS { # via the virtual widget. The virtual widget ensures that the routes between # the gain and copier do not get established in the firmware. These are purely # to show the existence of aggregation in the topology graph. - IncludeByKey.PASSTHROUGH { - "false" { - Object.Base.route [ - { - source "gain.21.1" - sink "virtual.sdw-amp" - } - ] - } - "true" { - Object.Base.route [ - { - source "host-copier.2.playback" - sink "virtual.sdw-amp" - } - ] - } - } Object.Base.route [ { - source "virtual.sdw-amp" - sink "alh-copier.$SDW_SPK_STREAM.1" + source "gain.21.1" + sink "virtual.sdw-amp" } { - source "virtual.sdw-amp" - sink "alh-copier.$SDW_SPK_STREAM.2" + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.1" + } + { + source "virtual.sdw-amp" + sink "alh-copier.$SDW_SPK_STREAM.2" } ] } @@ -582,53 +741,27 @@ Object.PCM.pcm [ } ] -IncludeByKey.PASSTHROUGH { -"false" { - IncludeByKey.SDW_SPK_ENHANCED_PLAYBACK { - "true" { - Object.Base.route [ - { - source "drc.21.1" - sink "alh-copier.$SDW_SPK_STREAM.0" - } - ] - } - "false" { - Object.Base.route [ - { - source "gain.21.1" - sink "alh-copier.$SDW_SPK_STREAM.0" - } - ] - } - } - Object.Base.route [ - { - source 'mixin.20.1' - sink 'mixout.21.1' - } - { - source 'host-copier.2.playback' - sink 'gain.20.1' - } - ] -} -"true" { - Object.Base.route [ - { - source "host-copier.2.playback" - sink "alh-copier.$SDW_SPK_STREAM.0" - } - ] -} -} +Object.Base.route [ + { + source "eqfir.21.2" + sink "alh-copier.$SDW_SPK_STREAM.0" + } + { + source 'mixin.20.1' + sink 'mixout.21.1' + } + { + source 'host-copier.2.playback' + sink 'gain.20.1' + } +] IncludeByKey.SDW_AMP_FEEDBACK { "true" { Object.Dai.ALH [ { dai_index 30 - id $SDW_AMP_IN_BE_ID + id $SDW_AMP_IN_BE_ID direction "capture" name $SDW_SPK_IN_STREAM default_hw_conf_id 1 @@ -651,6 +784,42 @@ IncludeByKey.SDW_AMP_FEEDBACK { pcm_id 3 IncludeByKey.AMP_FEEDBACK_CH { + "4" { + num_input_audio_formats 1 + num_output_audio_formats 3 + Object.Base.input_audio_format [ + { + in_channels 4 + in_bit_depth 32 + in_valid_bit_depth 32 + in_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + in_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + Object.Base.output_audio_format [ + { + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 24 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_channels 4 + out_bit_depth 32 + out_valid_bit_depth 32 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + { + out_channels 4 + out_bit_depth 16 + out_valid_bit_depth 16 + out_ch_cfg $CHANNEL_CONFIG_3_POINT_1 + out_ch_map $CHANNEL_MAP_3_POINT_1 + } + ] + } "6" { num_input_audio_formats 1 num_output_audio_formats 3 From 11b32e66019b4cd3ef7a49c6522d108eceac8fe0 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Mon, 8 Sep 2025 14:04:57 +0300 Subject: [PATCH 4/6] Tools: Topology: Add blobs for crossover filter usage This patch adds blobs for selector and EQIIR for an example Linkwitz-Riley 4th order crossover with a few channels order options. Signed-off-by: Seppo Ingalsuo --- .../eqiir/xover_lr4_2000hz_hhll_48khz.conf | 35 +++++++++++++++++++ .../eqiir/xover_lr4_2000hz_lhlh_48khz.conf | 35 +++++++++++++++++++ .../eqiir/xover_lr4_2000hz_llhh_48khz.conf | 35 +++++++++++++++++++ .../micsel/xover_selector_lr_to_llrr.conf | 26 ++++++++++++++ .../micsel/xover_selector_lr_to_lrlr.conf | 26 ++++++++++++++ .../micsel/xover_selector_lr_to_rrll.conf | 26 ++++++++++++++ 6 files changed, 183 insertions(+) create mode 100644 tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf create mode 100644 tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf create mode 100644 tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf create mode 100644 tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf create mode 100644 tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf create mode 100644 tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf diff --git a/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf new file mode 100644 index 000000000000..8338a33decb2 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf @@ -0,0 +1,35 @@ +# LR4 filter bank coefficients 08-Sep-2025 +# cd src/audio/eq_iir/tune; octave sof_example_lr4.m +Object.Base.data."IIR" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2d,0x3a,0xcd,0xd3, + 0xc0,0xf5,0x82,0x68,0xb9,0x41,0x76,0x00, + 0x72,0x83,0xec,0x00,0xb9,0x41,0x76,0x00, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x2d,0x3a,0xcd,0xd3,0xc0,0xf5,0x82,0x68, + 0xb9,0x41,0x76,0x00,0x72,0x83,0xec,0x00, + 0xb9,0x41,0x76,0x00,0xff,0xff,0xff,0xff, + 0x65,0x7f,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x7e,0x70,0xdd,0xd6, + 0xc0,0xf5,0x82,0x68,0xb6,0x9e,0x44,0x1a, + 0x94,0xc2,0x76,0xcb,0xb6,0x9e,0x44,0x1a, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x7e,0x70,0xdd,0xd6,0xc0,0xf5,0x82,0x68, + 0xb6,0x9e,0x44,0x1a,0x94,0xc2,0x76,0xcb, + 0xb6,0x9e,0x44,0x1a,0xfe,0xff,0xff,0xff, + 0x92,0x41,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf new file mode 100644 index 000000000000..a7c04f3372a7 --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf @@ -0,0 +1,35 @@ +# LR4 filter bank coefficients 08-Sep-2025 +# cd src/audio/eq_iir/tune; octave sof_example_lr4.m +Object.Base.data."IIR" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2d,0x3a,0xcd,0xd3, + 0xc0,0xf5,0x82,0x68,0xb9,0x41,0x76,0x00, + 0x72,0x83,0xec,0x00,0xb9,0x41,0x76,0x00, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x2d,0x3a,0xcd,0xd3,0xc0,0xf5,0x82,0x68, + 0xb9,0x41,0x76,0x00,0x72,0x83,0xec,0x00, + 0xb9,0x41,0x76,0x00,0xff,0xff,0xff,0xff, + 0x65,0x7f,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x7e,0x70,0xdd,0xd6, + 0xc0,0xf5,0x82,0x68,0xb6,0x9e,0x44,0x1a, + 0x94,0xc2,0x76,0xcb,0xb6,0x9e,0x44,0x1a, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x7e,0x70,0xdd,0xd6,0xc0,0xf5,0x82,0x68, + 0xb6,0x9e,0x44,0x1a,0x94,0xc2,0x76,0xcb, + 0xb6,0x9e,0x44,0x1a,0xfe,0xff,0xff,0xff, + 0x92,0x41,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf new file mode 100644 index 000000000000..8612f617c9be --- /dev/null +++ b/tools/topology/topology2/include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf @@ -0,0 +1,35 @@ +# LR4 filter bank coefficients 08-Sep-2025 +# cd src/audio/eq_iir/tune; octave sof_example_lr4.m +Object.Base.data."IIR" { + bytes " + 0x53,0x4f,0x46,0x34,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x01,0xd0,0x01,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xcc,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 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,0x01,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2d,0x3a,0xcd,0xd3, + 0xc0,0xf5,0x82,0x68,0xb9,0x41,0x76,0x00, + 0x72,0x83,0xec,0x00,0xb9,0x41,0x76,0x00, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x2d,0x3a,0xcd,0xd3,0xc0,0xf5,0x82,0x68, + 0xb9,0x41,0x76,0x00,0x72,0x83,0xec,0x00, + 0xb9,0x41,0x76,0x00,0xff,0xff,0xff,0xff, + 0x65,0x7f,0x00,0x00,0x02,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x7e,0x70,0xdd,0xd6, + 0xc0,0xf5,0x82,0x68,0xb6,0x9e,0x44,0x1a, + 0x94,0xc2,0x76,0xcb,0xb6,0x9e,0x44,0x1a, + 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, + 0x7e,0x70,0xdd,0xd6,0xc0,0xf5,0x82,0x68, + 0xb6,0x9e,0x44,0x1a,0x94,0xc2,0x76,0xcb, + 0xb6,0x9e,0x44,0x1a,0xfe,0xff,0xff,0xff, + 0x92,0x41,0x00,0x00" +} diff --git a/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf new file mode 100644 index 000000000000..4b4be2b34840 --- /dev/null +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_llrr.conf @@ -0,0 +1,26 @@ +# Exported with script sof_selector_blobs.m 08-Sep-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,0x00,0x04,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,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,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/xover_selector_lr_to_lrlr.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf new file mode 100644 index 000000000000..70489a0a0040 --- /dev/null +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_lrlr.conf @@ -0,0 +1,26 @@ +# Exported with script sof_selector_blobs.m 08-Sep-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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,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/xover_selector_lr_to_rrll.conf b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf new file mode 100644 index 000000000000..d88ba2063cd6 --- /dev/null +++ b/tools/topology/topology2/include/components/micsel/xover_selector_lr_to_rrll.conf @@ -0,0 +1,26 @@ +# Exported with script sof_selector_blobs.m 08-Sep-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,0x00,0x00,0x00,0x04, + 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,0x04,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,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00" +} From 21beadb67b0c8c74dc097ba282444fd85a5c54ba Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Mon, 8 Sep 2025 14:10:35 +0300 Subject: [PATCH 5/6] Tools: Topology: add speaker render pipelines with crossover filter The two pipelines are for SDW_SPK_ENHANCED_PLAYBACK true and false. They add to the base pipeline classes the selector component to duplicate channels in a stream and IIR filter to perform the low-pass and high-pass responses. The FIR filter is added to optionally delay the woofer channels. All added processing is pass-through until enabled with SDW_AMP_XOVER_SELECTOR_PARAMS, SDW_AMP_XOVER_EQIIR_PARAMS, and SDW_AMP_XOVER_EQFIR_PARAMS. Signed-off-by: Seppo Ingalsuo --- ...l-eqiir-eqfir-alh-dai-copier-playback.conf | 132 ++++++++++++++++++ ...l-eqiir-eqfir-alh-dai-copier-playback.conf | 132 ++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf create mode 100644 tools/topology/topology2/include/pipelines/cavs/mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..cc95dd9799a7 --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.conf @@ -0,0 +1,132 @@ +# +# BE playback pipeline: mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback."N" { +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + + + + +Class.Pipeline."mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback" { + + SubTreeCopy.baseclass { + # this class extends the pipeline class. + source "Class.Pipeline.mixout-gain-eqiir-eqfir-drc-alh-dai-copier-playback" + + # target node is not defined which means that the new subtree will be copied to + # the parent node containing the SubTreeCopy node i.e in this case the + # Class.Pipeline.mixout-gain-eqiir-eqfir-drc-micsel-eqiir-eqfir-alh-dai-copier-playback {} node. + + # default copy type is to extend the base class ie the widgets and routes + # will be added to the existing list of widgets/routes in the base class + + tree { + Object.Widget { + micsel."1" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control { + bytes."1" { + IncludeByKey.SDW_AMP_XOVER_SELECTOR_PARAMS { + "default" "include/components/micsel/passthrough.conf" + "xover_selector_lr_to_llrr" "include/components/micsel/xover_selector_lr_to_llrr.conf" + "xover_selector_lr_to_lrlr" "include/components/micsel/xover_selector_lr_to_lrlr.conf" + } + } + } + } + + eqiir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQIIR_PARAMS { + "default" "include/components/eqiir/passthrough.conf" + "xover_lr4_2000hz_lhlh_48khz" "include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf" + "xover_lr4_2000hz_llhh_48khz" "include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf" + "xover_lr4_2000hz_hhll_48khz" "include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf" + } + } + } + eqfir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQFIR_PARAMS { + "default" "include/components/eqfir/passthrough.conf" + "xover_dd00_100us_48khz.conf" "include/components/eqfir/xover_dd00_100us_48khz.conf" + "xover_00dd_100us_48khz.conf" "include/components/eqfir/xover_00dd_100us_48khz.conf" + } + } + } + } + + Object.Base { + !route [ + { + source drc.$index.1 + sink micsel.$index.1 + } + { + source micsel.$index.1 + sink eqiir.$index.2 + } + { + source eqiir.$index.2 + sink eqfir.$index.2 + } + ] + } + } + } +} diff --git a/tools/topology/topology2/include/pipelines/cavs/mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf new file mode 100644 index 000000000000..a12503d5d83a --- /dev/null +++ b/tools/topology/topology2/include/pipelines/cavs/mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.conf @@ -0,0 +1,132 @@ +# +# BE playback pipeline: mixout-gain-micsel-eqiir-eqfir-alh-dai-copier. +# +# All attributes defined herein are namespaced +# by alsatplg to "Object.Pipeline.mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback.N.attribute_name" +# +# Usage: mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback pipeline object can be instantiated as: +# +# Object.Pipeline.mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback."N" { +# time_domain "timer" +# } +# +# Where N is the unique pipeline ID within the same alsaconf node. +# + + + + +Class.Pipeline."mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback" { + + SubTreeCopy.baseclass { + # this class extends the pipeline class. + source "Class.Pipeline.mixout-gain-alh-dai-copier-playback" + + # target node is not defined which means that the new subtree will be copied to + # the parent node containing the SubTreeCopy node i.e in this case the + # Class.Pipeline.mixout-gain-micsel-eqiir-eqfir-alh-dai-copier-playback {} node. + + # default copy type is to extend the base class ie the widgets and routes + # will be added to the existing list of widgets/routes in the base class + + tree { + Object.Widget { + micsel."1" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control { + bytes."1" { + IncludeByKey.SDW_AMP_XOVER_SELECTOR_PARAMS { + "default" "include/components/micsel/passthrough.conf" + "xover_selector_lr_to_llrr" "include/components/micsel/xover_selector_lr_to_llrr.conf" + "xover_selector_lr_to_lrlr" "include/components/micsel/xover_selector_lr_to_lrlr.conf" + } + } + } + } + + eqiir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQIIR_PARAMS { + "default" "include/components/eqiir/passthrough.conf" + "xover_lr4_2000hz_lhlh_48khz" "include/components/eqiir/xover_lr4_2000hz_lhlh_48khz.conf" + "xover_lr4_2000hz_llhh_48khz" "include/components/eqiir/xover_lr4_2000hz_llhh_48khz.conf" + "xover_lr4_2000hz_hhll_48khz" "include/components/eqiir/xover_lr4_2000hz_hhll_48khz.conf" + } + } + } + eqfir."2" { + num_input_audio_formats 1 + Object.Base.input_audio_format [ + { + in_channels $SDW_AMP_NUM_CHANNELS + in_bit_depth 32 + in_valid_bit_depth 32 + } + ] + num_output_audio_formats 1 + Object.Base.output_audio_format [ + { + out_channels $SDW_AMP_NUM_CHANNELS + out_bit_depth 32 + out_valid_bit_depth 32 + } + ] + Object.Control.bytes."1" { + IncludeByKey.SDW_AMP_XOVER_EQFIR_PARAMS { + "default" "include/components/eqfir/passthrough.conf" + "xover_dd00_100us_48khz.conf" "include/components/eqfir/xover_dd00_100us_48khz.conf" + "xover_00dd_100us_48khz.conf" "include/components/eqfir/xover_00dd_100us_48khz.conf" + } + } + } + } + + Object.Base { + !route [ + { + source gain.$index.1 + sink micsel.$index.1 + } + { + source micsel.$index.1 + sink eqiir.$index.2 + } + { + source eqiir.$index.2 + sink eqfir.$index.2 + } + ] + } + } + } +} From b0ab82b3361f18125967617ffe1e47e157eb6c51 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Wed, 27 Aug 2025 11:22:37 +0300 Subject: [PATCH 6/6] Tools: Topology: Build topologies with 2-way speaker crossover This patch modifies cavs-sdw.conf to use sdw-amp-xover.conf when crossover feature is enabled with SDW_AMP_XOVER set to true. The built SDCA function topologies are sof-sdca-2amp-id2-xover.tplg and sof-sdca-2amp-feedback-id3-xover.tplg. The currently built topology duplicates a stereo L,R stream into L,R,L,R stream and applies 4th order Linkwitz-Riley low-pass for the first pair and high-pass for the second pair. The crossover frequency is set to 2 kHz as example. The crossover parameters should be changed to match the acoustical design of speaker acoustics of the device. Also there could be different channels mappings and high/low-pass apply need. Other variants can be created with the Octave scripts in the next patch. Signed-off-by: Bard Liao Signed-off-by: Seppo Ingalsuo --- tools/topology/topology2/cavs-sdw.conf | 20 +++++++++++++++++-- .../tplg-targets-sdca-generic.cmake | 14 +++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/tools/topology/topology2/cavs-sdw.conf b/tools/topology/topology2/cavs-sdw.conf index 4a88c4bd0bad..25b30c227885 100644 --- a/tools/topology/topology2/cavs-sdw.conf +++ b/tools/topology/topology2/cavs-sdw.conf @@ -13,7 +13,9 @@ + + @@ -92,6 +94,11 @@ Define { EFX_MIC_TDFB_PARAMS "line2_generic_pm10deg" EFX_MIC_DRC_PARAMS "dmic_default" DMIC0_DAI_EQIIR "highpass_40hz_20db" + SDW_AMP_NUM_CHANNELS 2 + SDW_AMP_XOVER false + SDW_AMP_XOVER_SELECTOR_PARAMS default + SDW_AMP_XOVER_EQIIR_PARAMS default + SDW_AMP_XOVER_EQFIR_PARAMS default } # override defaults with platform-specific config @@ -119,8 +126,17 @@ IncludeByKey.NUM_HDMIS { "[3-4]" "platform/intel/hdmi-generic.conf" } -IncludeByKey.NUM_SDW_AMP_LINKS { -"[1-3]" "platform/intel/sdw-amp-generic.conf" +IncludeByKey.SDW_AMP_XOVER { + "false" { + IncludeByKey.NUM_SDW_AMP_LINKS { + "[1-3]" "platform/intel/sdw-amp-generic.conf" + } + } + "true" { + IncludeByKey.NUM_SDW_AMP_LINKS { + "[1-3]" "platform/intel/sdw-amp-xover.conf" + } + } } IncludeByKey.SDW_DMIC { diff --git a/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake b/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake index 689439afd637..b07b26212c51 100644 --- a/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake +++ b/tools/topology/topology2/production/tplg-targets-sdca-generic.cmake @@ -25,4 +25,18 @@ SDW_DMIC_STREAM=Capture-SmartMic" "cavs-sdw\;sof-hdmi-pcm5-id6\;SDW_JACK=false,HDMI1_ID=6,HDMI2_ID=7,HDMI3_ID=8" "cavs-sdw\;sof-hdmi-pcm5-id7\;SDW_JACK=false,HDMI1_ID=7,HDMI2_ID=8,HDMI3_ID=9" +# Topology for speaker with 2-way crossover filter in SOF +# with channels order L-low, R-low, L-high, R-high +"cavs-sdw\;sof-sdca-2amp-id2-xover\;NUM_SDW_AMP_LINKS=2,SDW_JACK=false,\ +SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0,\ +SDW_AMP_NUM_CHANNELS=4,SDW_AMP_XOVER=true,\ +SDW_AMP_XOVER_SELECTOR_PARAMS=xover_selector_lr_to_lrlr,\ +SDW_AMP_XOVER_EQIIR_PARAMS=xover_lr4_2000hz_llhh_48khz" + +"cavs-sdw\;sof-sdca-2amp-feedback-id3-xover\;NUM_SDW_AMP_LINKS=2,\ +SDW_JACK=false,SDW_AMP_FEEDBACK=true,SDW_SPK_STREAM=Playback-SmartAmp,\ +SDW_SPK_IN_STREAM=Capture-SmartAmp,NUM_HDMIS=0,\ +SDW_AMP_NUM_CHANNELS=4,SDW_AMP_XOVER=true,\ +SDW_AMP_XOVER_SELECTOR_PARAMS=xover_selector_lr_to_lrlr,\ +SDW_AMP_XOVER_EQIIR_PARAMS=xover_lr4_2000hz_llhh_48khz" )