From 85b23c7e1fb34b16f91113827dadc12d9833f827 Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Fri, 27 Nov 2020 11:25:21 +0200 Subject: [PATCH 1/2] mux: add support for enum control into mux Add enum control support for mux/demux component. This implementation imposes certain restrictions for the enums: the definitions of enum controls in topology should have 1 multi channel enum control per output stream. So in case of mux you sould define 1 enum control and in case of demux you should define 1 enum per output stream. If you want to control all audio channel combinations you need to define: (in mux case) 1 enum control with channel_count * stream_count enum values, (in demux case) you need to define separate enum control for all output stream with channel_count amount of enum values. The enum controls are assigned to the streams in same order as streams are defined in mux/demux. Signed-off-by: Jaska Uimonen --- src/audio/mux/mux.c | 135 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/src/audio/mux/mux.c b/src/audio/mux/mux.c index 7be529c1824b..43feeea7b5e3 100644 --- a/src/audio/mux/mux.c +++ b/src/audio/mux/mux.c @@ -155,6 +155,126 @@ static int mux_set_values(struct comp_dev *dev, struct comp_data *cd, return 0; } +/* + * (N:1) Select channels from multiple sources to one sink with enum control. + * + * There's one multi channel enum kcontrol for sink channels that lists all source channels + * including a mute option, (value 0). For example if you have 2 stereo streams as input, enum + * control will be 2 channel, with 5 values in both channels (2 + 2 + mute for both channels). + */ +static int mux_set_enum_values(struct comp_dev *dev, struct comp_data *cd, + struct sof_ipc_ctrl_data *cdata) +{ + int channels = cdata->num_elems; + uint32_t val; + int index; + int row; + int ch; + int i; + int j; + + comp_info(dev, "mux_set_enum_values()"); + + /* clear previous settings */ + for (i = 0; i < MUX_MAX_STREAMS; i++) { + for (j = 0; j < PLATFORM_MAX_CHANNELS; j++) + cd->config.streams[i].mask[j] = 0; + } + + for (i = 0; i < channels; i++) { + ch = cdata->chanv[i].channel; + val = cdata->chanv[i].value; + comp_info(dev, "mux_set_enum_values(), channel = %d, value = %u", + ch, val); + if (ch < 0 || ch >= SOF_IPC_MAX_CHANNELS) { + comp_err(dev, "mux_set_enum_values(), illegal channel = %d", + ch); + return -EINVAL; + } + + if (val) { + index = (val - 1) / channels; + row = (val - 1) % channels; + cd->config.streams[index].mask[row] |= BIT(ch); + } + } + + mux_prepare_look_up_table(dev); + + if (dev->state > COMP_STATE_INIT) + cd->mux = mux_get_processing_function(dev); + + return 0; +} + +/* + * (1:N) Select channels from 1 source to multiple sinks with enum control. + * + * There are multiple enum controls corresponding to each sink. These enums are multi channel + * corresponding to sink channel count and list all source channels. So for example 1 source + * channel can be duplicated to both input channels. Enum contain also mute (value 0). Controls + * are separated by index in sof_ipc_ctrl_data. + */ +static int demux_set_enum_values(struct comp_dev *dev, struct comp_data *cd, + struct sof_ipc_ctrl_data *cdata) +{ + int channels = cdata->num_elems; + uint32_t val; + int row; + int ch; + int i; + + /* clear previous settings */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + cd->config.streams[cdata->index].mask[i] = 0; + + for (i = 0; i < channels; i++) { + ch = cdata->chanv[i].channel; + val = cdata->chanv[i].value; + comp_info(dev, "demux_set_enum_values(), channel = %d, value = %u", + ch, val); + if (ch < 0 || ch >= SOF_IPC_MAX_CHANNELS) { + comp_err(dev, "demux_set_enum_values(), illegal channel = %d", + ch); + return -EINVAL; + } + + if (val) { + row = (val - 1); + cd->config.streams[cdata->index].mask[row] |= BIT(ch); + } + } + + demux_prepare_look_up_table(dev); + + if (dev->state > COMP_STATE_INIT) + cd->demux = demux_get_processing_function(dev); + + return 0; +} + +static int mux_get_enum_values(struct comp_dev *dev, struct comp_data *cd, + struct sof_ipc_ctrl_data *cdata) +{ + int i; + int j; + + comp_info(dev, "mux_ctrl_get_enum_cmd() elems %d", cdata->num_elems); + + for (i = 0; i < cdata->num_elems; i++) { + cdata->chanv[i].channel = i; + for (j = 0; j < PLATFORM_MAX_CHANNELS; j++) { + if (cd->config.streams[cdata->index].mask[i] & BIT(j)) + cdata->chanv[i].value = j + 1; + } + comp_info(dev, "mux_ctrl_get_enum_cmd(), channel = %u, value = %u", + cdata->chanv[i].channel, + cdata->chanv[i].value); + } + + return 0; +} + static struct comp_dev *mux_new(const struct comp_driver *drv, struct sof_ipc_comp *comp) { @@ -290,6 +410,16 @@ static int mux_ctrl_set_cmd(struct comp_dev *dev, ret = mux_set_values(dev, cd, cfg); break; + case SOF_CTRL_CMD_ENUM: + comp_info(dev, "mux_ctrl_set_enum_cmd() elems %d", cdata->num_elems); + cfg = (struct sof_mux_config *) + ASSUME_ALIGNED(cdata->data->data, 4); + + if (dev->comp.type == SOF_COMP_MUX) + ret = mux_set_enum_values(dev, cd, cdata); + else + ret = demux_set_enum_values(dev, cd, cdata); + break; default: comp_err(dev, "mux_ctrl_set_cmd(): invalid cdata->cmd = 0x%08x", cdata->cmd); @@ -324,6 +454,9 @@ static int mux_ctrl_get_cmd(struct comp_dev *dev, cdata->data->abi = SOF_ABI_VERSION; cdata->data->size = reply_size; break; + case SOF_CTRL_CMD_ENUM: + mux_get_enum_values(dev, cd, cdata); + break; default: comp_cl_err(&comp_mux, "mux_ctrl_set_cmd(): invalid cdata->cmd = 0x%08x", cdata->cmd); @@ -344,8 +477,10 @@ static int mux_cmd(struct comp_dev *dev, int cmd, void *data, switch (cmd) { case COMP_CMD_SET_DATA: + case COMP_CMD_SET_VALUE: return mux_ctrl_set_cmd(dev, cdata); case COMP_CMD_GET_DATA: + case COMP_CMD_GET_VALUE: return mux_ctrl_get_cmd(dev, cdata, max_data_size); default: return -EINVAL; From caa4cb08379b7ab651e106a3db032b040e718e3d Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Wed, 16 Dec 2020 09:58:27 +0200 Subject: [PATCH 2/2] topology: add enum controls to mux/demux pipelines Add enum controls to mux/demux pipelines. Also add new hda_generic based mux/demux topologies to help testing in hda devices. Signed-off-by: Jaska Uimonen --- tools/topology/m4/muxdemux.m4 | 3 + tools/topology/sof-hda-generic-demux.m4 | 259 ++++++++++++++++++ tools/topology/sof-hda-generic-mux.m4 | 248 +++++++++++++++++ .../sof/pipe-volume-demux-playback.m4 | 23 +- .../topology/sof/pipe-volume-mux-playback.m4 | 118 ++++++++ .../sof/pipe-volume-playback-sched.m4 | 106 +++++++ 6 files changed, 756 insertions(+), 1 deletion(-) create mode 100644 tools/topology/sof-hda-generic-demux.m4 create mode 100644 tools/topology/sof-hda-generic-mux.m4 create mode 100644 tools/topology/sof/pipe-volume-mux-playback.m4 create mode 100644 tools/topology/sof/pipe-volume-playback-sched.m4 diff --git a/tools/topology/m4/muxdemux.m4 b/tools/topology/m4/muxdemux.m4 index 68fc23506dca..ba602651b8ad 100644 --- a/tools/topology/m4/muxdemux.m4 +++ b/tools/topology/m4/muxdemux.m4 @@ -90,6 +90,9 @@ ifelse(`$2', `0', ` bytes [' $7 ` ]' +` enum [' + $8 +` ]' `}') divert(0)dnl diff --git a/tools/topology/sof-hda-generic-demux.m4 b/tools/topology/sof-hda-generic-demux.m4 new file mode 100644 index 000000000000..9b0697f15ee7 --- /dev/null +++ b/tools/topology/sof-hda-generic-demux.m4 @@ -0,0 +1,259 @@ +# Topology for SKL+ HDA Generic machine +# + +# if XPROC is not defined, define with default pipe +ifdef(`DMICPROC', , `define(DMICPROC, eq-iir-volume)') +ifdef(`DMIC16KPROC', , `define(DMIC16KPROC, eq-iir-volume)') + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`hda.m4') +include(`muxdemux.m4') +include(`abi.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include bxt DSP configuration +include(`platform/intel/bxt.m4') + +# Define pipeline id for intel-generic-dmic.m4 +# to generate dmic setting + +ifelse(CHANNELS, `0', , +` +define(DMIC_PIPELINE_48k_ID, `10') +define(DMIC_PIPELINE_16k_ID, `11') + +include(`platform/intel/intel-generic-dmic.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(0, 1, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 1 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,1 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,1 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,1 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,1 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,1)')') + +define(matrix2, `ROUTE_MATRIX(5, + `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, 0, 1 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,1 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,1 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,1 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,1 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,1)')') + +dnl name, num_streams, route_matrix list +MUXDEMUX_CONFIG(demux_priv_1, 2, LIST(` ', `matrix1,', `matrix2')) +# +# Define the pipelines +# +# PCM0P --> volume (pipe 1) --> HDA Analog (HDA Analog playback) +# PCM0C <-- volume, EQ (pipe 2) <-- HDA Analog (HDA Analog capture) +# PCM1P --> volume (pipe 3) --> HDA Digital (HDA Digital playback) +# PCM1C <-- volume, EQ (pipe 4) <-- HDA Digital (HDA Digital capture) +# PCM3 ----> volume (pipe 7) ----> iDisp1 (HDMI/DP playback, BE link 3) +# PCM4 ----> Volume (pipe 8) ----> iDisp2 (HDMI/DP playback, BE link 4) +# PCM5 ----> volume (pipe 9) ----> iDisp3 (HDMI/DP playback, BE link 5) +# + +# Demux 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-volume-demux-playback.m4, + 1, 0, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency capture pipeline 2 on PCM 0 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-highpass-capture.m4, + 2, 0, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 3 on PCM 1 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 3, 1, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency capture pipeline 4 on PCM 1 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-highpass-capture.m4, + 4, 1, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 7 on PCM 3 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 7, 3, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 8 on PCM 4 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 8, 4, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 9 on PCM 5 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 9, 5, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# +# DAIs configuration +# + +# playback DAI is HDA Analog using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 1, HDA, 0, Analog Playback and Capture, + PIPELINE_SOURCE_1, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# currently this dai is here as "virtual" capture backend +#W_DAI_IN(HDA, 5, PCM4C, s24le, 3, 0) + +# Capture pipeline 5 from demux on PCM 5 using max 2 channels of s32le. +PIPELINE_PCM_ADD(sof/pipe-passthrough-capture-sched.m4, + 5, 4, 2, s32le, + 1000, 1, 0, + 48000, 48000, 48000, + SCHEDULE_TIME_DOMAIN_TIMER, + PIPELINE_PLAYBACK_SCHED_COMP_1) + +# Connect demux to capture +SectionGraph."PIPE_CAP" { + index "0" + + lines [ + # mux to capture + dapm(PIPELINE_SINK_5, PIPELINE_DEMUX_1) + ] +} + +# Connect virtual capture to dai +SectionGraph."PIPE_CAP_VIRT" { + index "5" + + lines [ + # mux to capture + dapm(ECHO REF 5, HDA1.IN) + ] +} + +# capture DAI is HDA Analog using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-capture.m4, + 2, HDA, 1, Analog Playback and Capture, + PIPELINE_SINK_2, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is HDA Digital using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 3, HDA, 2, Digital Playback and Capture, + PIPELINE_SOURCE_3, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# capture DAI is HDA Digital using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-capture.m4, + 4, HDA, 3, Digital Playback and Capture, + PIPELINE_SINK_4, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is iDisp1 using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 7, HDA, 4, iDisp1, + PIPELINE_SOURCE_7, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is iDisp2 using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 8, HDA, 5, iDisp2, + PIPELINE_SOURCE_8, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is iDisp3 using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 9, HDA, 6, iDisp3, + PIPELINE_SOURCE_9, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +PCM_DUPLEX_ADD(HDA Analog, 0, PIPELINE_PCM_1, PIPELINE_PCM_2) +PCM_DUPLEX_ADD(HDA Digital, 1, PIPELINE_PCM_3, PIPELINE_PCM_4) +PCM_PLAYBACK_ADD(HDMI1, 3, PIPELINE_PCM_7) +PCM_PLAYBACK_ADD(HDMI2, 4, PIPELINE_PCM_8) +PCM_PLAYBACK_ADD(HDMI3, 5, PIPELINE_PCM_9) + +PCM_CAPTURE_ADD(EchoRef, 8, PIPELINE_PCM_5) + +# +# BE configurations - overrides config in ACPI if present +# + +# HDA outputs +DAI_CONFIG(HDA, 0, 4, Analog Playback and Capture, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 0, 48000, 2))) +DAI_CONFIG(HDA, 1, 5, Digital Playback and Capture, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 1, 48000, 2))) +# 3 HDMI/DP outputs (ID: 3,4,5) +DAI_CONFIG(HDA, 4, 1, iDisp1, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 4, 48000, 2))) +DAI_CONFIG(HDA, 5, 2, iDisp2, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 5, 48000, 2))) +DAI_CONFIG(HDA, 6, 3, iDisp3, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 6, 48000, 2))) + + +VIRTUAL_DAPM_ROUTE_IN(codec0_in, HDA, 1, IN, 1) +VIRTUAL_DAPM_ROUTE_IN(codec1_in, HDA, 3, IN, 2) +VIRTUAL_DAPM_ROUTE_OUT(codec0_out, HDA, 0, OUT, 3) +VIRTUAL_DAPM_ROUTE_OUT(codec1_out, HDA, 2, OUT, 4) + +# codec2 is not supported in dai links but it exists +# in dapm routes, so hack this one to HDA1 +VIRTUAL_DAPM_ROUTE_IN(codec2_in, HDA, 3, IN, 5) +VIRTUAL_DAPM_ROUTE_OUT(codec2_out, HDA, 2, OUT, 6) + +VIRTUAL_DAPM_ROUTE_OUT(iDisp1_out, HDA, 4, OUT, 7) +VIRTUAL_DAPM_ROUTE_OUT(iDisp2_out, HDA, 5, OUT, 8) +VIRTUAL_DAPM_ROUTE_OUT(iDisp3_out, HDA, 6, OUT, 9) + +VIRTUAL_WIDGET(iDisp3 Tx, out_drv, 0) +VIRTUAL_WIDGET(iDisp2 Tx, out_drv, 1) +VIRTUAL_WIDGET(iDisp1 Tx, out_drv, 2) +VIRTUAL_WIDGET(Analog CPU Playback, out_drv, 3) +VIRTUAL_WIDGET(Digital CPU Playback, out_drv, 4) +VIRTUAL_WIDGET(Alt Analog CPU Playback, out_drv, 5) +VIRTUAL_WIDGET(Analog CPU Capture, input, 6) +VIRTUAL_WIDGET(Digital CPU Capture, input, 7) +VIRTUAL_WIDGET(Alt Analog CPU Capture, input, 8) diff --git a/tools/topology/sof-hda-generic-mux.m4 b/tools/topology/sof-hda-generic-mux.m4 new file mode 100644 index 000000000000..7129d5566f65 --- /dev/null +++ b/tools/topology/sof-hda-generic-mux.m4 @@ -0,0 +1,248 @@ +# Topology for SKL+ HDA Generic machine +# + +# if XPROC is not defined, define with default pipe +ifdef(`DMICPROC', , `define(DMICPROC, eq-iir-volume)') +ifdef(`DMIC16KPROC', , `define(DMIC16KPROC, eq-iir-volume)') + +# Include topology builder +include(`utils.m4') +include(`dai.m4') +include(`pipeline.m4') +include(`hda.m4') +include(`muxdemux.m4') +include(`abi.m4') + +# Include TLV library +include(`common/tlv.m4') + +# Include Token library +include(`sof/tokens.m4') + +# Include bxt DSP configuration +include(`platform/intel/bxt.m4') + +# Define pipeline id for intel-generic-dmic.m4 +# to generate dmic setting + +ifelse(CHANNELS, `0', , +` +define(DMIC_PIPELINE_48k_ID, `10') +define(DMIC_PIPELINE_16k_ID, `11') + +include(`platform/intel/intel-generic-dmic.m4') +' +) + +dnl Configure mux +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(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)', + `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)')') + +define(matrix2, `ROUTE_MATRIX(12, + `BITS_TO_BYTE(0, 0, 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)', + `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(mux_priv_1, 2, LIST(` ', `matrix1,', `matrix2')) + +# +# Define the pipelines +# +# PCM0P --> volume (pipe 1) --> HDA Analog (HDA Analog playback) +# PCM0C <-- volume, EQ (pipe 2) <-- HDA Analog (HDA Analog capture) +# PCM1P --> volume (pipe 3) --> HDA Digital (HDA Digital playback) +# PCM1C <-- volume, EQ (pipe 4) <-- HDA Digital (HDA Digital capture) +# PCM3 ----> volume (pipe 7) ----> iDisp1 (HDMI/DP playback, BE link 3) +# PCM4 ----> Volume (pipe 8) ----> iDisp2 (HDMI/DP playback, BE link 4) +# PCM5 ----> volume (pipe 9) ----> iDisp3 (HDMI/DP playback, BE link 5) +# + +# Demux 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-volume-mux-playback.m4, + 1, 0, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency capture pipeline 2 on PCM 0 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-highpass-capture.m4, + 2, 0, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 3 on PCM 1 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 3, 1, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency capture pipeline 4 on PCM 1 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-highpass-capture.m4, + 4, 1, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 7 on PCM 3 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 7, 3, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 8 on PCM 4 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 8, 4, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# Low Latency playback pipeline 9 on PCM 5 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 0 +PIPELINE_PCM_ADD(sof/pipe-volume-playback.m4, + 9, 5, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +# +# DAIs configuration +# + +# playback DAI is HDA Analog using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 1, HDA, 0, Analog Playback and Capture, + PIPELINE_SOURCE_1, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# Low Latency playback pipeline 3 on PCM 1 using max 2 channels of s24le. +# 1000us deadline on core 0 with priority 1 +PIPELINE_PCM_ADD(sof/pipe-volume-playback-sched.m4, + 12, 8, 2, s24le, + 1000, 1, 0, + 48000, 48000, 48000, + SCHEDULE_TIME_DOMAIN_TIMER, + PIPELINE_PLAYBACK_SCHED_COMP_1) + +# Connect demux to capture +SectionGraph."PIPE_CAP" { + index "0" + + lines [ + # mux to capture + dapm(PIPELINE_MUX_1, PIPELINE_SOURCE_12) + ] +} + +# capture DAI is HDA Analog using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-capture.m4, + 2, HDA, 1, Analog Playback and Capture, + PIPELINE_SINK_2, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is HDA Digital using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 3, HDA, 2, Digital Playback and Capture, + PIPELINE_SOURCE_3, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# capture DAI is HDA Digital using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-capture.m4, + 4, HDA, 3, Digital Playback and Capture, + PIPELINE_SINK_4, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is iDisp1 using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 7, HDA, 4, iDisp1, + PIPELINE_SOURCE_7, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is iDisp2 using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 8, HDA, 5, iDisp2, + PIPELINE_SOURCE_8, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +# playback DAI is iDisp3 using 2 periods +# Dai buffers use s32le format, 1000us deadline on core 0 with priority 0 +DAI_ADD(sof/pipe-dai-playback.m4, + 9, HDA, 6, iDisp3, + PIPELINE_SOURCE_9, 2, s32le, + 1000, 0, 0, SCHEDULE_TIME_DOMAIN_TIMER) + +PCM_DUPLEX_ADD(HDA Analog, 0, PIPELINE_PCM_1, PIPELINE_PCM_2) +PCM_DUPLEX_ADD(HDA Digital, 1, PIPELINE_PCM_3, PIPELINE_PCM_4) +PCM_PLAYBACK_ADD(HDMI1, 3, PIPELINE_PCM_7) +PCM_PLAYBACK_ADD(HDMI2, 4, PIPELINE_PCM_8) +PCM_PLAYBACK_ADD(HDMI3, 5, PIPELINE_PCM_9) +#PCM_PLAYBACK_ADD(Mux, 8, PIPELINE_PCM_12) + + +# +# BE configurations - overrides config in ACPI if present +# + +# HDA outputs +DAI_CONFIG(HDA, 0, 4, Analog Playback and Capture, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 0, 48000, 2))) +DAI_CONFIG(HDA, 1, 5, Digital Playback and Capture, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 1, 48000, 2))) +# 3 HDMI/DP outputs (ID: 3,4,5) +DAI_CONFIG(HDA, 4, 1, iDisp1, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 4, 48000, 2))) +DAI_CONFIG(HDA, 5, 2, iDisp2, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 5, 48000, 2))) +DAI_CONFIG(HDA, 6, 3, iDisp3, + HDA_CONFIG(HDA_CONFIG_DATA(HDA, 6, 48000, 2))) + + +VIRTUAL_DAPM_ROUTE_IN(codec0_in, HDA, 1, IN, 1) +VIRTUAL_DAPM_ROUTE_IN(codec1_in, HDA, 3, IN, 2) +VIRTUAL_DAPM_ROUTE_OUT(codec0_out, HDA, 0, OUT, 3) +VIRTUAL_DAPM_ROUTE_OUT(codec1_out, HDA, 2, OUT, 4) + +# codec2 is not supported in dai links but it exists +# in dapm routes, so hack this one to HDA1 +VIRTUAL_DAPM_ROUTE_IN(codec2_in, HDA, 3, IN, 5) +VIRTUAL_DAPM_ROUTE_OUT(codec2_out, HDA, 2, OUT, 6) + +VIRTUAL_DAPM_ROUTE_OUT(iDisp1_out, HDA, 4, OUT, 7) +VIRTUAL_DAPM_ROUTE_OUT(iDisp2_out, HDA, 5, OUT, 8) +VIRTUAL_DAPM_ROUTE_OUT(iDisp3_out, HDA, 6, OUT, 9) + +VIRTUAL_WIDGET(iDisp3 Tx, out_drv, 0) +VIRTUAL_WIDGET(iDisp2 Tx, out_drv, 1) +VIRTUAL_WIDGET(iDisp1 Tx, out_drv, 2) +VIRTUAL_WIDGET(Analog CPU Playback, out_drv, 3) +VIRTUAL_WIDGET(Digital CPU Playback, out_drv, 4) +VIRTUAL_WIDGET(Alt Analog CPU Playback, out_drv, 5) +VIRTUAL_WIDGET(Analog CPU Capture, input, 6) +VIRTUAL_WIDGET(Digital CPU Capture, input, 7) +VIRTUAL_WIDGET(Alt Analog CPU Capture, input, 8) diff --git a/tools/topology/sof/pipe-volume-demux-playback.m4 b/tools/topology/sof/pipe-volume-demux-playback.m4 index 61dc883e9f26..c0e9be9e8560 100644 --- a/tools/topology/sof/pipe-volume-demux-playback.m4 +++ b/tools/topology/sof/pipe-volume-demux-playback.m4 @@ -21,6 +21,7 @@ include(`pga.m4') include(`muxdemux.m4') include(`mixercontrol.m4') include(`bytecontrol.m4') +include(`enumcontrol.m4') # demux Bytes control with max value of 255 C_CONTROLBYTES(concat(`DEMUX', PIPELINE_ID), PIPELINE_ID, @@ -30,6 +31,25 @@ C_CONTROLBYTES(concat(`DEMUX', PIPELINE_ID), PIPELINE_ID, CONTROLBYTES_MAX(, 304), , concat(`demux_priv_', PIPELINE_ID)) + +CONTROLENUM_LIST(channel_enums_1, LIST(` ', `"off"', `"L1"', `"R1"')) +CONTROLENUM_LIST(channel_enums_2, LIST(` ', `"off"', `"L1"', `"R1"')) + +# Demux enum control +C_CONTROLENUM(demux_control_1, PIPELINE_ID, + channel_enums_1, + LIST(` ', ENUM_CHANNEL(FL, 3, 0), ENUM_CHANNEL(FR, 3, 1)), + CONTROLENUM_OPS(enum, + 257 binds the mixer control to enum get/put handlers, + 257, 257)) + +C_CONTROLENUM(demux_control_2, PIPELINE_ID, + channel_enums_2, + LIST(` ', ENUM_CHANNEL(FL, 3, 0), ENUM_CHANNEL(FR, 3, 1)), + CONTROLENUM_OPS(enum, + 257 binds the mixer control to enum get/put handlers, + 257, 257)) + # Volume Mixer control with max value of 32 C_CONTROLMIXER(Master Playback Volume, PIPELINE_ID, CONTROLMIXER_OPS(volsw, 256 binds the mixer control to volume get/put handlers, 256, 256), @@ -63,7 +83,8 @@ W_PGA(1, PIPELINE_FORMAT, DAI_PERIODS, 2, playback_pga_conf, SCHEDULE_CORE, # Mux 0 has 2 sink and source periods. W_MUXDEMUX(0, 1, PIPELINE_FORMAT, 2, 2, SCHEDULE_CORE, - LIST(` ', concat(`DEMUX', PIPELINE_ID))) + LIST(` ', concat(`DEMUX', PIPELINE_ID)), + LIST(` ', "demux_control_1", "demux_control_2")) # Low Latency Buffers W_BUFFER(0, COMP_BUFFER_SIZE(2, diff --git a/tools/topology/sof/pipe-volume-mux-playback.m4 b/tools/topology/sof/pipe-volume-mux-playback.m4 new file mode 100644 index 000000000000..55361721c6cd --- /dev/null +++ b/tools/topology/sof/pipe-volume-mux-playback.m4 @@ -0,0 +1,118 @@ +# Mux Volume Pipeline +# +# Low Latency Playback with mux and volume. +# +# Pipeline Endpoints for connection are :- +# +# Playback mux +# B2 (DAI buffer) +# +# +# host PCM_P -- B0 --> volume -- B1 --> mux(M) -- B2 --> sink DAI0 +# | +# pipeline n+1 --> DAI +# + +# Include topology builder +include(`utils.m4') +include(`buffer.m4') +include(`pcm.m4') +include(`pga.m4') +include(`muxdemux.m4') +include(`mixercontrol.m4') +include(`bytecontrol.m4') +include(`enumcontrol.m4') + +# demux Bytes control with max value of 255 +C_CONTROLBYTES(concat(`MUX', 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(`mux_priv_', PIPELINE_ID)) + + +CONTROLENUM_LIST(channel_enums_1, LIST(` ', `"off"', `"L1"', `"R1"', `"L2"', `"R2"')) + +# Demux enum control +C_CONTROLENUM(mux_control_1, PIPELINE_ID, + channel_enums_1, + LIST(` ', ENUM_CHANNEL(FL, 3, 0), ENUM_CHANNEL(FR, 3, 1)), + CONTROLENUM_OPS(enum, + 257 binds the mixer control to enum get/put handlers, + 257, 257)) + +# Volume Mixer control with max value of 32 +C_CONTROLMIXER(Master Playback Volume, PIPELINE_ID, + CONTROLMIXER_OPS(volsw, 256 binds the mixer control to volume get/put handlers, 256, 256), + CONTROLMIXER_MAX(, 32), + false, + CONTROLMIXER_TLV(TLV 32 steps from -64dB to 0dB for 2dB, vtlv_m64s2), + Channel register and shift for Front Left/Right, + LIST(` ', KCONTROL_CHANNEL(FL, 1, 0), KCONTROL_CHANNEL(FR, 1, 1))) + +# +# Volume configuration +# + +W_VENDORTUPLES(playback_pga_tokens, sof_volume_tokens, +LIST(` ', `SOF_TKN_VOLUME_RAMP_STEP_TYPE "0"' + ` ', `SOF_TKN_VOLUME_RAMP_STEP_MS "250"')) + +W_DATA(playback_pga_conf, playback_pga_tokens) + +# +# Components and Buffers +# + +# Host "Low latency Playback" PCM +# with 2 sink and 0 source periods +W_PCM_PLAYBACK(PCM_ID, Low Latency Playback, 2, 0, SCHEDULE_CORE) + +# "Master Playback Volume" has 2 source and x sink periods for DAI ping-pong +W_PGA(1, PIPELINE_FORMAT, DAI_PERIODS, 2, playback_pga_conf, SCHEDULE_CORE, + LIST(` ', "PIPELINE_ID Master Playback Volume")) + +# Mux 0 has 2 sink and source periods. +W_MUXDEMUX(0, 0, PIPELINE_FORMAT, 2, 2, SCHEDULE_CORE, + LIST(` ', concat(`MUX', PIPELINE_ID)), + LIST(` ', "mux_control_1")) + +# 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), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_DAI_MEM_CAP) + +# +# Pipeline Graph +# +# host PCM_P --B0--> Demux --B1--> volume ---B2--> sink DAI0 + +P_GRAPH(pipe-ll-playback-PIPELINE_ID, PIPELINE_ID, + LIST(` ', + `dapm(N_BUFFER(0), N_PCMP(PCM_ID))', + `dapm(N_PGA(1), N_BUFFER(0))', + `dapm(N_BUFFER(1), N_PGA(1))', + `dapm(N_MUXDEMUX(0), N_BUFFER(1))', + `dapm(N_BUFFER(2), N_MUXDEMUX(0))')) +# +# Pipeline Source and Sinks +# +indir(`define', concat(`PIPELINE_SOURCE_', PIPELINE_ID), N_BUFFER(2)) +indir(`define', concat(`PIPELINE_MUX_', PIPELINE_ID), N_MUXDEMUX(0)) +indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), Low Latency Playback PCM_ID) + +# +# PCM Configuration +# + + +# PCM capabilities supported by FW +PCM_CAPABILITIES(Low Latency Playback PCM_ID, CAPABILITY_FORMAT_NAME(PIPELINE_FORMAT), 48000, 48000, 2, PIPELINE_CHANNELS, 2, 16, 192, 16384, 65536, 65536) + diff --git a/tools/topology/sof/pipe-volume-playback-sched.m4 b/tools/topology/sof/pipe-volume-playback-sched.m4 new file mode 100644 index 000000000000..b5f2fb0575f2 --- /dev/null +++ b/tools/topology/sof/pipe-volume-playback-sched.m4 @@ -0,0 +1,106 @@ +# Low Latency Passthrough with volume Pipeline and PCM +# +# Pipeline Endpoints for connection are :- +# +# host PCM_P --> B0 --> Volume 0 --> B1 --> sink DAI0 + +# Include topology builder +include(`utils.m4') +include(`buffer.m4') +include(`pcm.m4') +include(`pga.m4') +include(`dai.m4') +include(`mixercontrol.m4') +include(`pipeline.m4') + +# +# Controls +# +# Volume Mixer control with max value of 32 +C_CONTROLMIXER(Master Playback Volume, PIPELINE_ID, + CONTROLMIXER_OPS(volsw, 256 binds the mixer control to volume get/put handlers, 256, 256), + CONTROLMIXER_MAX(, 32), + false, + CONTROLMIXER_TLV(TLV 32 steps from -64dB to 0dB for 2dB, vtlv_m64s2), + Channel register and shift for Front Left/Right, + LIST(` ', KCONTROL_CHANNEL(FL, 1, 0), KCONTROL_CHANNEL(FR, 1, 1))) + +# +# Volume configuration +# + +define(DEF_PGA_TOKENS, concat(`pga_tokens_', PIPELINE_ID)) +define(DEF_PGA_CONF, concat(`pga_conf_', PIPELINE_ID)) + +W_VENDORTUPLES(DEF_PGA_TOKENS, sof_volume_tokens, +LIST(` ', `SOF_TKN_VOLUME_RAMP_STEP_TYPE "2"' + ` ', `SOF_TKN_VOLUME_RAMP_STEP_MS "20"')) + +W_DATA(DEF_PGA_CONF, DEF_PGA_TOKENS) + +# +# Components and Buffers +# + +# Host "Passthrough Playback" PCM +# with 2 sink and 0 source periods +W_PCM_PLAYBACK(PCM_ID, Mux Playback, 2, 0, SCHEDULE_CORE) + +# "Volume" has 2 source and x sink periods +W_PGA(0, PIPELINE_FORMAT, DAI_PERIODS, 2, DEF_PGA_CONF, SCHEDULE_CORE, + LIST(` ', "PIPELINE_ID Master Playback Volume")) + +# Playback 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, SCHEDULE_CORE) +W_BUFFER(1, COMP_BUFFER_SIZE(DAI_PERIODS, + COMP_SAMPLE_SIZE(DAI_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_DAI_MEM_CAP, SCHEDULE_CORE) + +# +# Pipeline Graph +# +# host PCM_P --> B0 --> Volume 0 --> B1 --> sink DAI0 + +P_GRAPH(pipe-mux-playback-PIPELINE_ID, PIPELINE_ID, + LIST(` ', + `dapm(N_BUFFER(0), N_PCMP(PCM_ID))', + `dapm(N_PGA(0), N_BUFFER(0))', + `dapm(N_BUFFER(1), N_PGA(0))')) + +# +# Pipeline Source and Sinks +# +indir(`define', concat(`PIPELINE_SOURCE_', PIPELINE_ID), N_BUFFER(1)) +#indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), Mux Playback PCM_ID) + +W_PIPELINE(SCHED_COMP, SCHEDULE_PERIOD, SCHEDULE_PRIORITY, SCHEDULE_CORE, SCHEDULE_TIME_DOMAIN, pipe_media_schedule_plat) + +# +# PCM Configuration + +# +PCM_CAPABILITIES(Mux Playback PCM_ID, CAPABILITY_FORMAT_NAME(PIPELINE_FORMAT), PCM_MIN_RATE, PCM_MAX_RATE, 2, PIPELINE_CHANNELS, 2, 16, 192, 16384, 65536, 65536) + +# PCM Media Playback +SectionPCM.STR(Mux Playback PCM_ID) { + + index STR(PIPELINE_ID) + + # used for binding to the PCM + id STR(PCM_ID) + + dai.STR(Mux Playback PCM_ID) { + id STR(PCM_ID) + } + + # Playback Configuration + pcm."playback" { + + capabilities STR(Mux Playback PCM_ID) + } +} + +undefine(`DEF_PGA_TOKENS') +undefine(`DEF_PGA_CONF')