From 7610c581ec660433b0f44985668bb765c6a5d2e7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 Aug 2023 14:55:45 -0500 Subject: [PATCH 1/7] ASoC: SOF: Intel: hda-dai: fix channel map configuration for aggregated dailink The existing code derives the channel map used to program the HDaudio link DMA from the hw_params, but that is not quite right in the case of aggregation. The code in soc-pcm.c splits the hw_params depending on the codec_ch_map, and we need to reconstruct the channel-map to insert the data in the right places. This issue is seen only on amplifier feedback capture where the data from the second amplifier was replaced by that of the first amplifier. Note that the loop iterator of the macro for_each_rtd_cpu_dais() is reused in a following loop. This is different to all existing usages of that macro, hence the use of a boolean flag to avoid an access to an uninitialized variable. Fixes: 2960ee5c4814 ("ASoC: SOF: Intel: hda-dai: add helpers for SoundWire callbacks") Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 318a21c12cd005..b9cea4e19c55f0 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -434,10 +434,17 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, int link_id) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); const struct hda_dai_widget_dma_ops *ops; struct hdac_ext_stream *hext_stream; + struct snd_soc_dai *codec_dai; + struct snd_soc_dai *dai; struct snd_sof_dev *sdev; + bool cpu_dai_found = false; + int cpu_dai_id; + int ch_mask; int ret; + int j; ret = non_hda_dai_hw_params(substream, params, cpu_dai); if (ret < 0) { @@ -452,9 +459,29 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, if (!hext_stream) return -ENODEV; - /* in the case of SoundWire we need to program the PCMSyCM registers */ + /* + * in the case of SoundWire we need to program the PCMSyCM registers. In case + * of aggregated devices, we need to define the channel mask for each sublink + * by reconstructing the split done in soc-pcm.c + */ + for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) { + if (dai == cpu_dai) { + cpu_dai_found = true; + break; + } + } + + if (!cpu_dai_found) + return -ENODEV; + + ch_mask = 0; + for_each_rtd_codec_dais(rtd, j, codec_dai) { + if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == cpu_dai_id) + ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask; + } + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, - GENMASK(params_channels(params) - 1, 0), + ch_mask, hdac_stream(hext_stream)->stream_tag, substream->stream); if (ret < 0) { From 86080e65409e177be1e0c0378d3e0514f4d1cbe5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 Aug 2023 15:01:04 -0500 Subject: [PATCH 2/7] ASoC: SOF: Intel: hda-dai-ops: fix HDaudio link format The generic format calculation is shared between SSP, DMIC and SoundWire. In the latter case, the dailink may handle more channels than what each DAI can handle. This is specifically the case for aggregated configurations, specifically for capture where we may want to handle a 4ch stream using two DAIs. The issue is that soc-pcm.c already splits the channels on a per-dai basis, so we need to count the total number of channels handled by the dailink. This isn't very elegant but the information on the dailink configuration is not available at the DAI level. The net effect of this confusion between DAI and dailink channels was that the data was recorded 2x slower than normal, and when played at the normal rate a 2x frequency shift can be heard. Fixes: 12547730e5b7 ("ASoC: SOF: Intel: hda-dai-ops: add/select DMA ops for SSP") Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai-ops.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 012a75f366ab4c..f1d762f94df8a3 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -237,15 +237,32 @@ static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; unsigned int format_val; + u32 ch_mask = 0; + int num_channels; + int codec_dai_id; - format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), + /* + * if the multiple dais are handled by the same dailink, we may need to update the + * stream channel count - the params are modified in soc-pcm based on the codec_ch_maps info + */ + for_each_rtd_codec_dais(rtd, codec_dai_id, codec_dai) + ch_mask |= rtd->dai_link->codec_ch_maps[codec_dai_id].ch_mask; + + num_channels = hweight_long(ch_mask); + if (num_channels != params_channels(params)) + dev_dbg(sdev->dev, "configuring stream format for %d channels, params_channels was %d\n", + num_channels, params_channels(params)); + + format_val = snd_hdac_calc_stream_format(params_rate(params), num_channels, params_format(params), params_physical_width(params), 0); dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, - params_rate(params), params_channels(params), params_format(params)); + params_rate(params), num_channels, params_format(params)); return format_val; } From aea26460f7efdbc799eef82c77887c04ff010705 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 15 Aug 2023 11:46:30 -0500 Subject: [PATCH 3/7] ASoC: SOF: ipc4-topology: change chain_dma handling in dai_config The chain_dma mode is currently only handled for HDaudio, but can be used for orther DAIs starting with LunarLake. Move the chain_dma handling earlier. Error detection for the chain_dma case for older platforms is handled at a different level. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/ipc4-topology.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index bf91c878616296..0e541884da588c 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2759,13 +2759,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * if (!data) return 0; + if (pipeline->use_chain_dma) { + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); + return 0; + } + switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA: - if (pipeline->use_chain_dma) { - pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; - pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); - break; - } gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; fallthrough; From 748c7583bb8071c474b93ef9be62a40db17cfa07 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 18 Aug 2023 14:23:42 -0500 Subject: [PATCH 4/7] ASoC: SOF: ops: add new 'is_chain_dma_supported' callback IPC4 introduced a 'chain-dma' mode when host and link DMA are connected by firmware without using a regular pipeline or the ability to add intermediate connections. This mode is not available on all platforms and all links, so add a platform-specific callback to help the SOF ipc4-topology core handle different hardware+firmware configurations. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/ops.h | 9 +++++++++ sound/soc/sof/sof-priv.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index a494bdef373927..adad0a220b987e 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -555,6 +555,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, sof_ops(sdev)->set_mach_params(mach, sdev); } +static inline bool +snd_sof_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type) +{ + if (sof_ops(sdev) && sof_ops(sdev)->is_chain_dma_supported) + return sof_ops(sdev)->is_chain_dma_supported(sdev, dai_type); + + return false; +} + /** * snd_sof_dsp_register_poll_timeout - Periodically poll an address * until a condition is met or a timeout occurs diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d4f6702e93dcb8..f3d63ae0a8695a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -336,6 +336,8 @@ struct snd_sof_dsp_ops { struct snd_soc_dai_driver *drv; int num_drv; + bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */ + /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ u32 hw_info; From 636ab5893f018e77014d9a740d17e3e17fe3d456 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 18 Aug 2023 14:27:55 -0500 Subject: [PATCH 5/7] ASoC: SOF: Intel: hda: add 'is_chain_dma_supported' callback Reuse existing function to get the interface mask and expose it to the SOF core with a callback - the main user is the IPC4 topology so only HDaudio platforms provide this callback. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-common-ops.c | 1 + sound/soc/sof/intel/hda.c | 63 ++++++++++++++++++++++------ sound/soc/sof/intel/hda.h | 5 +++ sound/soc/sof/sof-priv.h | 7 ++++ 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 8e1cd0babd32c5..4b6cf3528a717e 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -81,6 +81,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, + .is_chain_dma_supported = hda_is_chain_dma_supported, /* PM */ .suspend = hda_dsp_suspend, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index a1732af2b1be85..6bcd8cbfd8c99a 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -46,44 +46,83 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) +static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask) { const struct sof_intel_dsp_desc *chip; - u32 interface_mask[2] = { 0 }; chip = get_chip_info(sdev->pdata); switch (chip->hw_ip_version) { case SOF_INTEL_TANGIER: case SOF_INTEL_BAYTRAIL: case SOF_INTEL_BROADWELL: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP); + interface_mask[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP); break; case SOF_INTEL_CAVS_1_5: case SOF_INTEL_CAVS_1_5_PLUS: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA); - interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA); break; case SOF_INTEL_CAVS_1_8: case SOF_INTEL_CAVS_2_0: case SOF_INTEL_CAVS_2_5: case SOF_INTEL_ACE_1_0: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); - interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA); break; case SOF_INTEL_ACE_2_0: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); - interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */ + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + /* all interfaces accessible without DSP */ + interface_mask[SOF_DAI_HOST_ACCESS] = + interface_mask[SOF_DAI_DSP_ACCESS]; break; default: break; } +} + +static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) +{ + u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 }; + + hda_get_interfaces(sdev, interface_mask); return interface_mask[sdev->dspless_mode_selected]; } +bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type) +{ + u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 }; + const struct sof_intel_dsp_desc *chip; + + if (sdev->dspless_mode_selected) + return false; + + hda_get_interfaces(sdev, interface_mask); + + if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type))) + return false; + + if (dai_type == SOF_DAI_INTEL_HDA) + return true; + + switch (dai_type) { + case SOF_DAI_INTEL_SSP: + case SOF_DAI_INTEL_DMIC: + case SOF_DAI_INTEL_ALH: + chip = get_chip_info(sdev->pdata); + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return false; + return true; + default: + return false; + } +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) /* diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 7c575ba9462cd8..93920c00a2d77e 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -573,6 +573,11 @@ struct sof_intel_hda_stream { #define SOF_STREAM_SD_OFFSET_CRST 0x1 +/* + * DAI support + */ +bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type); + /* * DSP Core services. */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f3d63ae0a8695a..28d3c451942a00 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -157,6 +157,13 @@ struct sof_firmware { u32 payload_offset; }; +enum sof_dai_access { + SOF_DAI_DSP_ACCESS, /* access from DSP only */ + SOF_DAI_HOST_ACCESS, /* access from host only */ + + SOF_DAI_ACCESS_NUM +}; + /* * SOF DSP HW abstraction operations. * Used to abstract DSP HW architecture and any IO busses between host CPU From 931e1e433a1484b460335b1c114499c8a7c904bc Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 15 Aug 2023 11:55:04 -0500 Subject: [PATCH 6/7] ASoC: SOF: Intel: hda-dai-ops: enable chain_dma for ALH Use the existing callbacks and mix/match of HDaudio and SoundWire support. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai-ops.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f1d762f94df8a3..bb35a3a4d298ce 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -538,6 +538,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .get_hlink = hda_get_hlink, }; +static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = { + .get_hext_stream = hda_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .trigger = hda_trigger, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = sdw_get_hlink, +}; + static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -636,6 +647,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg } case SOF_IPC_TYPE_4: { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct sof_ipc4_copier *ipc4_copier = sdai->private; const struct sof_intel_dsp_desc *chip; @@ -643,15 +656,10 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA: - { - struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->use_chain_dma) return &hda_ipc4_chain_dma_ops; return &hda_ipc4_dma_ops; - } case SOF_DAI_INTEL_SSP: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; @@ -663,6 +671,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg case SOF_DAI_INTEL_ALH: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; + if (pipeline->use_chain_dma) + return &sdw_ipc4_chain_dma_ops; return &sdw_ipc4_dma_ops; default: From ad29504da7d99b290f7419edd03e3711b9e28dcb Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 15 Aug 2023 11:51:06 -0500 Subject: [PATCH 7/7] ASoC: SOF: ipc4-topology: allow chain_dma for all supported DAIs Now that we have a 'is_chain_dma_supported' callback we can use it to double-check possible disconnects between a topology file enabling chain-dma for a DAI and the hardware/firmware capabilities. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/ipc4-topology.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 0e541884da588c..e0293c643f536c 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -487,6 +487,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) { struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_widget *pipe_widget; @@ -530,10 +531,11 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; - if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) { - dev_err(scomp->dev, - "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", - ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); + + if (pipeline->use_chain_dma && + !snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) { + dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n", + ipc4_copier->dai_type); ret = -ENODEV; goto free_available_fmt; }