Via the SOF_DBG_DSPLESS_MODE sof_debug flag the SOF stack can be asked to not use the DSP for audio.
The use of DSPless mode is governed by the sdev->dspless_mode_selected flag which is only going to be set if the user sets sof_debug=0x8000 and the platform advertises that the DSPless mode is supported on them.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com --- sound/soc/sof/intel/hda-dai-ops.c | 33 +++++++++++++++++++++++++++++- sound/soc/sof/intel/hda-dai.c | 25 ++++++++++++++++++----- sound/soc/sof/intel/hda-pcm.c | 24 +++++++++++++++------- sound/soc/sof/intel/hda-stream.c | 34 ++++++++++++++++--------------- sound/soc/sof/intel/hda.c | 19 ++++++++++------- 5 files changed, 99 insertions(+), 36 deletions(-)
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index de48f13259f1..4b39cecacd68 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -319,13 +319,44 @@ static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = { .post_trigger = hda_ipc3_post_trigger, };
+static struct hdac_ext_stream * +hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + + return stream_to_hdac_ext_stream(hstream); +} + +static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev, + struct hdac_ext_stream *hext_stream, + unsigned int format_val) +{ + /* + * Save the format_val which was adjusted by the maxbps of the codec. + * This information is not available on the FE side since there we are + * using dummy_codec. + */ + hext_stream->hstream.format_val = format_val; +} + +static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { + .get_hext_stream = hda_dspless_get_hext_stream, + .setup_hext_stream = hda_dspless_setup_hext_stream, +}; + #endif
const struct hda_dai_widget_dma_ops * hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) - struct snd_sof_dai *sdai = swidget->private; + struct snd_sof_dai *sdai; + + if (sdev->dspless_mode_selected) + return &hda_dspless_dma_ops; + + sdai = swidget->private;
switch (sdev->pdata->ipc_type) { case SOF_IPC: diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 46a17afdd1ea..0435b7f251aa 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -31,11 +31,18 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, struct snd_sof_dai_config_data *data) { struct snd_sof_widget *swidget = w->dobj.private; - struct snd_soc_component *component = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops; + struct snd_soc_component *component; + struct snd_sof_dev *sdev; int ret;
+ if (!swidget) + return 0; + + component = swidget->scomp; + sdev = snd_soc_component_get_drvdata(component); + tplg_ops = sof_ipc_get_ops(sdev, tplg); + if (tplg_ops && tplg_ops->dai_config) { ret = tplg_ops->dai_config(sdev, swidget, flags, data); if (ret < 0) { @@ -56,13 +63,21 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); struct snd_sof_widget *swidget = w->dobj.private; - struct snd_sof_dai *sdai = swidget->private; + struct snd_sof_dai *sdai; + + /* + * The swidget parameter of hda_select_dai_widget_ops() is ignored in + * case of DSPless mode + */ + if (sdev->dspless_mode_selected) + return hda_select_dai_widget_ops(sdev, NULL); + + sdai = swidget->private;
/* select and set the DAI widget ops if not set already */ if (!sdai->platform_private) { const struct hda_dai_widget_dma_ops *ops = hda_select_dai_widget_ops(sdev, swidget); - if (!ops) return NULL;
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index dc0b359ed9b6..981e7b699bdb 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -101,18 +101,23 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_dma_buffer *dmab; int ret; - u32 size, rate, bits; - - size = params_buffer_bytes(params); - rate = hda_dsp_get_mult_div(sdev, params_rate(params)); - bits = hda_dsp_get_bits(sdev, params_width(params));
hstream->substream = substream;
dmab = substream->runtime->dma_buffer_p;
- hstream->format_val = rate | bits | (params_channels(params) - 1); - hstream->bufsize = size; + /* + * Use the codec required format val (which is link_bps adjusted) when + * the DSP is not in use + */ + if (!sdev->dspless_mode_selected) { + u32 rate = hda_dsp_get_mult_div(sdev, params_rate(params)); + u32 bits = hda_dsp_get_bits(sdev, params_width(params)); + + hstream->format_val = rate | bits | (params_channels(params) - 1); + } + + hstream->bufsize = params_buffer_bytes(params); hstream->period_bytes = params_period_bytes(params); hstream->no_period_wakeup = (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && @@ -249,6 +254,11 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ /* Only S16 and S32 supported by HDA hardware when used without DSP */ + if (sdev->dspless_mode_selected) + snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32); + /* binding pcm substream to hda stream */ substream->runtime->private_data = &dsp_stream->hstream; return 0; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 50e92996f928..87e31a65b314 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -503,11 +503,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
hstream = &hext_stream->hstream; sd_offset = SOF_STREAM_SD_OFFSET(hstream); + mask = BIT(hstream->index);
- /* decouple host and link DMA */ - mask = 0x1 << hstream->index; - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, mask); + /* decouple host and link DMA if the DSP is used */ + if (!sdev->dspless_mode_selected) + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask);
/* clear stream status */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, @@ -608,11 +609,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, * enable decoupled mode */
- if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) /* couple host and link DMA, disable DSP features */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, 0); - }
/* program stream format */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -620,11 +620,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_ADSP_REG_SD_FORMAT, 0xffff, hstream->format_val);
- if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) /* decouple host and link DMA, enable DSP features */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, mask); - }
/* program last valid index */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -677,20 +676,23 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream = container_of(hstream, struct hdac_ext_stream, hstream); - struct hdac_bus *bus = sof_to_bus(sdev); - u32 mask = 0x1 << hstream->index; int ret;
ret = hda_dsp_stream_reset(sdev, hstream); if (ret < 0) return ret;
- spin_lock_irq(&bus->reg_lock); - /* couple host and link DMA if link DMA channel is idle */ - if (!hext_stream->link_locked) - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, - SOF_HDA_REG_PP_PPCTL, mask, 0); - spin_unlock_irq(&bus->reg_lock); + if (!sdev->dspless_mode_selected) { + struct hdac_bus *bus = sof_to_bus(sdev); + u32 mask = BIT(hstream->index); + + spin_lock_irq(&bus->reg_lock); + /* couple host and link DMA if link DMA channel is idle */ + if (!hext_stream->link_locked) + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPCTL, mask, 0); + spin_unlock_irq(&bus->reg_lock); + }
hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 19aad4141a78..a14733aa7a60 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -47,32 +47,34 @@ static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; - u32 interface_mask = 0; + 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 = BIT(SOF_DAI_INTEL_SSP); + interface_mask[0] = BIT(SOF_DAI_INTEL_SSP); break; case SOF_INTEL_CAVS_1_5: case SOF_INTEL_CAVS_1_5_PLUS: - interface_mask = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA); + 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); 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 = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + 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); break; default: break; }
- return interface_mask; + return interface_mask[sdev->dspless_mode_selected]; }
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) @@ -1058,6 +1060,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0; #endif
+ if (sdev->dspless_mode_selected) + hdev->no_ipc_position = 1; + /* set up HDA base */ bus = sof_to_bus(sdev); ret = hda_init(sdev);