From 23c7e4739c301cc0dbc625aaf3f8e86042891565 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Wed, 25 Aug 2021 15:24:04 +0300 Subject: [PATCH] Topology: Add development example of demux and EQ band-split pipeline This patch adds topologies sof-apl-nocodec-demux-eq-2ch4ch.tplg and sof-apl-nocodec-demux-eq-4ch4ch.tplg. Playback of 2ch creates 4ch output in format L_lo, L_hi, R_lo, and R_hi. An example band-split at 2 kHz is configured for EQ processing. The low band contains an additional 80 Hz high-pass. The pipeline was tested in UP2 device. The nocodec topology enables an useful SPP loopback mode. The capture PCM is connected to DAI loopback so this pipeline can be tested with simultaneous 2ch aplay and 4ch arecord. Signed-off-by: Seppo Ingalsuo --- .../topology1/development/CMakeLists.txt | 3 +- .../sof-apl-nocodec-demux-eq-2ch4ch.m4 | 92 ++++++++++++ .../sof-apl-nocodec-demux-eq-4ch4ch.m4 | 93 ++++++++++++ .../sof/pipe-demux-eq-iir-playback.m4 | 140 ++++++++++++++++++ tools/tune/eq/example_iir_bandsplit.m | 140 ++++++++++++++++++ 5 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 tools/topology/topology1/development/sof-apl-nocodec-demux-eq-2ch4ch.m4 create mode 100644 tools/topology/topology1/development/sof-apl-nocodec-demux-eq-4ch4ch.m4 create mode 100644 tools/topology/topology1/sof/pipe-demux-eq-iir-playback.m4 create mode 100644 tools/tune/eq/example_iir_bandsplit.m diff --git a/tools/topology/topology1/development/CMakeLists.txt b/tools/topology/topology1/development/CMakeLists.txt index aff0d2c9b..ccff93471 100644 --- a/tools/topology/topology1/development/CMakeLists.txt +++ b/tools/topology/topology1/development/CMakeLists.txt @@ -26,7 +26,8 @@ set(TPLGS "sof-imx8-compr-mp3-wm8960\;sof-imx8-compr-mp3-wm8960" "sof-imx8mp-compr-aac-wm8960\;sof-imx8mp-compr-aac-wm8960" "sof-imx8mp-compr-mp3-wm8960\;sof-imx8mp-compr-mp3-wm8960" - + "sof-apl-nocodec-demux-eq-4ch4ch\;sof-apl-nocodec-demux-eq-4ch4ch" + "sof-apl-nocodec-demux-eq-2ch4ch\;sof-apl-nocodec-demux-eq-2ch4ch" ) diff --git a/tools/topology/topology1/development/sof-apl-nocodec-demux-eq-2ch4ch.m4 b/tools/topology/topology1/development/sof-apl-nocodec-demux-eq-2ch4ch.m4 new file mode 100644 index 000000000..e3d99a002 --- /dev/null +++ b/tools/topology/topology1/development/sof-apl-nocodec-demux-eq-2ch4ch.m4 @@ -0,0 +1,92 @@ +# +# Topology for generic Apollolake board with no codec and digital mic array. +# +# APL Host GW DMAC support max 6 playback and max 6 capture channels so some +# pipelines/PCMs/DAIs are commented out to keep within HW bounds. If these +# are needed then they can be used provided other PCMs/pipelines/SSPs are +# commented out in their place. + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`ssp.m4') +include(`pipeline.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include Apollolake DSP configuration +include(`platform/intel/bxt.m4') +include(`platform/intel/dmic.m4') + +# +# Define the pipelines +# +# PCM0P ---> demux ---> eq_iir ---> SSP0 +# PCM0C <-------------------------- SSP0 + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate, +dnl time_domain, sched_comp) + +# Low Latency playback pipeline 1 on PCM 0 using max 2 channels of s32le. +# Set 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-demux-eq-iir-playback.m4, + 1, 0, 2, s32le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Volume switch capture pipeline 2 on PCM 0 using max 4 channels of s32le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 2, 0, 4, s32le, + 1000, 0, 0, + 48000, 48000, 48000) + + +# +# DAIs configuration +# + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +# playback DAI is SSP0 using 2 periods +# Buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 1, SSP, 0, NoCodec-0, + PIPELINE_SOURCE_1, 4, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + + +# capture DAI is SSP0 using 2 periods +# Buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-capture.m4, + 2, SSP, 0, NoCodec-0, + PIPELINE_SINK_2, 4, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + + +dnl PCM_DUPLEX_ADD(name, pcm_id, playback, capture) +PCM_DUPLEX_ADD(Port0, 0, PIPELINE_PCM_1, PIPELINE_PCM_2) + +# +# BE configurations - overrides config in ACPI if present +# + +dnl DAI_CONFIG(type, dai_index, link_id, name, ssp_config/dmic_config) +DAI_CONFIG(SSP, 0, 0, NoCodec-0, + dnl SSP_CONFIG(format, mclk, bclk, fsync, tdm, ssp_config_data) + SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24576000, codec_mclk_in), + SSP_CLOCK(bclk, 6144000, codec_slave), + SSP_CLOCK(fsync, 48000, codec_slave), + SSP_TDM(4, 32, 15, 15), + dnl SSP_CONFIG_DATA(type, dai_index, valid bits, mclk_id, quirks) + SSP_CONFIG_DATA(SSP, 0, 32, 0, SSP_QUIRK_LBM))) diff --git a/tools/topology/topology1/development/sof-apl-nocodec-demux-eq-4ch4ch.m4 b/tools/topology/topology1/development/sof-apl-nocodec-demux-eq-4ch4ch.m4 new file mode 100644 index 000000000..969433aab --- /dev/null +++ b/tools/topology/topology1/development/sof-apl-nocodec-demux-eq-4ch4ch.m4 @@ -0,0 +1,93 @@ +# +# Topology for generic Apollolake board with no codec and digital mic array. +# +# APL Host GW DMAC support max 6 playback and max 6 capture channels so some +# pipelines/PCMs/DAIs are commented out to keep within HW bounds. If these +# are needed then they can be used provided other PCMs/pipelines/SSPs are +# commented out in their place. + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`ssp.m4') +include(`pipeline.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include Apollolake DSP configuration +include(`platform/intel/bxt.m4') +include(`platform/intel/dmic.m4') + +# +# Define the pipelines +# +# PCM0P ---> demux ---> eq_iir ---> SSP0 +# PCM0C <-------------------------- SSP0 + +dnl PIPELINE_PCM_ADD(pipeline, +dnl pipe id, pcm, max channels, format, +dnl period, priority, core, +dnl pcm_min_rate, pcm_max_rate, pipeline_rate, +dnl time_domain, sched_comp) + +# Low Latency playback pipeline 1 on PCM 0 using max 4 channels of s32le. +# Set 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-demux-eq-iir-playback.m4, + 1, 0, 4, s32le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Volume switch capture pipeline 2 on PCM 0 using max 4 channels of s32le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture.m4, + 2, 0, 4, s32le, + 1000, 0, 0, + 48000, 48000, 48000) + + +# +# DAIs configuration +# + +dnl DAI_ADD(pipeline, +dnl pipe id, dai type, dai_index, dai_be, +dnl buffer, periods, format, +dnl deadline, priority, core, time_domain) + +# playback DAI is SSP0 using 2 periods +# Buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 1, SSP, 0, NoCodec-0, + PIPELINE_SOURCE_1, 4, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + + +# capture DAI is SSP0 using 2 periods +# Buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-capture.m4, + 2, SSP, 0, NoCodec-0, + PIPELINE_SINK_2, 4, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + + +dnl PCM_DUPLEX_ADD(name, pcm_id, playback, capture) +PCM_DUPLEX_ADD(Port0, 0, PIPELINE_PCM_1, PIPELINE_PCM_2) + +# +# BE configurations - overrides config in ACPI if present +# + +dnl DAI_CONFIG(type, dai_index, link_id, name, ssp_config/dmic_config) +DAI_CONFIG(SSP, 0, 0, NoCodec-0, + dnl SSP_CONFIG(format, mclk, bclk, fsync, tdm, ssp_config_data) + SSP_CONFIG(I2S, SSP_CLOCK(mclk, 24576000, codec_mclk_in), + SSP_CLOCK(bclk, 6144000, codec_slave), + SSP_CLOCK(fsync, 48000, codec_slave), + SSP_TDM(4, 32, 15, 15), + dnl SSP_CONFIG_DATA(type, dai_index, valid bits, mclk_id, quirks) + SSP_CONFIG_DATA(SSP, 0, 32, 0, SSP_QUIRK_LBM))) + diff --git a/tools/topology/topology1/sof/pipe-demux-eq-iir-playback.m4 b/tools/topology/topology1/sof/pipe-demux-eq-iir-playback.m4 new file mode 100644 index 000000000..07d1a89ed --- /dev/null +++ b/tools/topology/topology1/sof/pipe-demux-eq-iir-playback.m4 @@ -0,0 +1,140 @@ +# Demux EQ band split pipeline +# +# host PCM_P --B0--> demux(M) --B1--> eq_iir --B2--> sink DAI0 +# + +# Include topology builder +include(`utils.m4') +include(`buffer.m4') +include(`pcm.m4') +include(`muxdemux.m4') +include(`bytecontrol.m4') +include(`eq_iir.m4') + +dnl Configure demux +dnl name, pipeline_id, routing_matrix_rows +dnl Diagonal 1's in routing matrix mean that every input channel is +dnl copied to corresponding output channels in all output streams. +dnl I.e. row index is the input channel, 1 means it is copied to +dnl corresponding output channel (column index), 0 means it is discarded. +dnl There's a separate matrix for all outputs. +define(matrix1, `ROUTE_MATRIX(1, + `BITS_TO_BYTE(1, 0, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(1, 0, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 1, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 1, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,0)')') + +dnl name, num_streams, route_matrix list +MUXDEMUX_CONFIG(demux_priv_1, 1, LIST(` ', `matrix1')) + +# demux Bytes control with max value of 255 +C_CONTROLBYTES(concat(`DEMUX', PIPELINE_ID), PIPELINE_ID, + CONTROLBYTES_OPS(bytes, 258 binds the mixer control to bytes get/put handlers, 258, 258), + CONTROLBYTES_EXTOPS(258 binds the mixer control to bytes get/put handlers, 258, 258), + , , , + CONTROLBYTES_MAX(, 304), + , concat(`demux_priv_', PIPELINE_ID)) + +# EQ IIR Bytes control +define(DEF_EQIIR_COEF, concat(`eqiir_coef_', PIPELINE_ID)) +define(DEF_EQIIR_PRIV, concat(`eqiir_priv_', PIPELINE_ID)) + +# Bandsplit, created with example_iir_bandsplit.m 25-Aug-2021 +CONTROLBYTES_PRIV(DEF_EQIIR_PRIV, +` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,' +` 0xb0,0x00,0x00,0x00,0x00,0x30,0x01,0x03,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' +` 0xb0,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,0x50,0xda,0xf0,0xc0,' +` 0x1e,0x5d,0x0d,0x7f,0x3c,0xdf,0xd6,0x1f,' +` 0x89,0x41,0x52,0xc0,0x3c,0xdf,0xd6,0x1f,' +` 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,' +` 0x99,0x7f,0x00,0x00,0x01,0x00,0x00,0x00,' +` 0x01,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,0x01,0xe1,0xa6,0x1a,' +` 0xff,0x3d,0xb2,0xca,0x01,0xe1,0xa6,0x1a,' +` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"' +) + +C_CONTROLBYTES(DEF_EQIIR_COEF, PIPELINE_ID, + CONTROLBYTES_OPS(bytes, 258 binds the control to bytes get/put handlers, 258, 258), + CONTROLBYTES_EXTOPS(258 binds the control to bytes get/put handlers, 258, 258), + , , , + CONTROLBYTES_MAX(, 1024), + , + DEF_EQIIR_PRIV) + +# +# Components and Buffers +# + +# Host "Speaker Playback" PCM +# with 2 sink and 0 source periods +W_PCM_PLAYBACK(PCM_ID, Speaker Playback, 2, 0, SCHEDULE_CORE) + +# "EQ IIR" has x sink period and 2 source periods +W_EQ_IIR(0, PIPELINE_FORMAT, 2, 2, SCHEDULE_CORE, + LIST(` ', "DEF_EQIIR_COEF")) + +# Mux 0 has 2 sink and source periods. +W_MUXDEMUX(0, 1, PIPELINE_FORMAT, 2, 2, SCHEDULE_CORE, + LIST(` ', concat(`DEMUX', PIPELINE_ID))) + +# Low Latency Buffers +W_BUFFER(0, COMP_BUFFER_SIZE(2, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_HOST_MEM_CAP) +W_BUFFER(1, COMP_BUFFER_SIZE(2, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_COMP_MEM_CAP) +W_BUFFER(2, COMP_BUFFER_SIZE(DAI_PERIODS, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), 4, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_COMP_MEM_CAP) + +# +# Pipeline Graph +# +# host PCM_P --B0--> Demux(M) --B1--> eq_iir --B2--> sink DAI0 + +P_GRAPH(pipe-demux-eq-playback, PIPELINE_ID, + LIST(` ', + `dapm(N_BUFFER(0), N_PCMP(PCM_ID))', + `dapm(N_MUXDEMUX(0), N_BUFFER(0))', + `dapm(N_BUFFER(1), N_MUXDEMUX(0))', + `dapm(N_EQ_IIR(0), N_BUFFER(1))', + `dapm(N_BUFFER(2), N_EQ_IIR(0))')) + +# +# Pipeline Source and Sinks +# +indir(`define', concat(`PIPELINE_SOURCE_', PIPELINE_ID), N_BUFFER(2)) +indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), Speaker Playback PCM_ID) + +# +# PCM Configuration +# + + +# PCM capabilities supported by FW +PCM_CAPABILITIES(Speaker Playback PCM_ID, CAPABILITY_FORMAT_NAME(PIPELINE_FORMAT), 48000, 48000, 2, PIPELINE_CHANNELS, 2, 16, 192, 16384, 65536, 65536) + +undefine(`matrix1') +undefine(`DEF_EQIIR_COEF') +undefine(`DEF_EQIIR_PRIV') diff --git a/tools/tune/eq/example_iir_bandsplit.m b/tools/tune/eq/example_iir_bandsplit.m new file mode 100644 index 000000000..f1c3887ff --- /dev/null +++ b/tools/tune/eq/example_iir_bandsplit.m @@ -0,0 +1,140 @@ +%% Design effect EQs and bundle them to parameter block + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright (c) 2016-2020, Intel Corporation. All rights reserved. +% +% Author: Seppo Ingalsuo + +function example_iir_bandsplit() + +%% Common definitions +fs = 48e3; +tpath = '../../topology/topology1/m4'; +cpath = '../../ctl'; +priv = 'DEF_EQIIR_PRIV'; + +%% -------------------------------------------------- +%% Example: Band-split 2ch to 4ch low and high bands +%% -------------------------------------------------- +blob_fn = fullfile(cpath, 'eq_iir_bandsplit.bin'); +alsa_fn = fullfile(cpath, 'eq_iir_bandsplit.txt'); +tplg_fn = fullfile(tpath, 'eq_iir_bandsplit.m4'); +comment = 'Bandsplit, created with example_iir_bandsplit.m'; + +%% Design IIR loudness equalizer +eq_lo = lo_band_iir(fs); +eq_hi = hi_band_iir(fs); + +%% Quantize and pack filter coefficients plus shifts etc. +bq_lo = eq_iir_blob_quant(eq_lo.p_z, eq_lo.p_p, eq_lo.p_k); +bq_hi = 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 = eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [bq_lo bq_hi]); + +%% Pack and write file +eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, priv, comment) + + +%% ------------------------------------ +%% Done. +%% ------------------------------------ + +end + +%% ------------------- +%% EQ design functions +%% ------------------- + +function eq = lo_band_iir(fs) + + +%% Get defaults for equalizer design +eq = eq_defaults(); +eq.fs = fs; +eq.enable_iir = 1; +eq.norm_type = 'peak'; +eq.norm_offs_db = 0; + +%% Manually setup low-shelf and high shelf parametric equalizers +% +% 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 (or NaN when not applicable) . The +% Third argument is a Q-value (or NaN when not applicable). + +% Low-pass at 2 kHz, add a high-pass at 80 Hz for a small woofer +eq.peq = [ ... + eq.PEQ_HP2 80 NaN NaN ; ... + eq.PEQ_LP2 2000 NaN NaN ; ... + ]; + +%% Design EQ +eq = eq_compute(eq); + +%% Plot +eq_plot(eq); + +end + +function eq = hi_band_iir(fs) + + +%% Get defaults for equalizer design +eq = eq_defaults(); +eq.fs = fs; +eq.enable_iir = 1; +eq.norm_type = 'peak'; +eq.norm_offs_db = 0; + +%% Manually setup low-shelf and high shelf parametric equalizers +% +% 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 (or NaN when not applicable) . The +% Third argument is a Q-value (or NaN when not applicable). + +% High-pass at 2 kHz for a tweeter +eq.peq = [ ... + eq.PEQ_HP2 2000 NaN NaN ; ... + ]; + +%% Design EQ +eq = eq_compute(eq); + +%% Plot +eq_plot(eq); + +end + + + +% Pack and write file common function for all exports +function eq_pack_export(bm, bin_fn, ascii_fn, tplg_fn, priv, note) + +bp = eq_iir_blob_pack(bm); + +if ~isempty(bin_fn) + eq_blob_write(bin_fn, bp); +end + +if ~isempty(ascii_fn) + eq_alsactl_write(ascii_fn, bp); +end + +if ~isempty(tplg_fn) + eq_tplg_write(tplg_fn, bp, priv, note); +end + +end