From 49007601a8c03289e83808a7e9ba2dbc98eba3c9 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 4 Oct 2021 20:25:30 -0700 Subject: [PATCH 01/10] ASoC: SOF: Intel: hda: clear stream before freeing the DAI widget The DAI_CONFIG IPC that is sent during the STOP trigger is used for stopping the DMA in the FW. This must be done after the DMA RUN bit is cleared by the host. So move the call to snd_hdac_ext_link_stream_clear() before hda_link_dai_widget_update() to follow the correct programming sequence for DMA stop for HDA DAIs. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dai.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index f81af21b4f4778..55ec86a732f203 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -325,6 +325,8 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: + snd_hdac_ext_link_stream_clear(link_dev); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; else @@ -343,8 +345,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, } link_dev->link_prepared = 0; - - fallthrough; + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_link_stream_clear(link_dev); break; From cd2cac864159b67b7bf1114f8b984e5b2f655208 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 4 Oct 2021 21:29:30 -0700 Subject: [PATCH 02/10] ASoC: SOF: Intel: hda: Add a helper function for stream reset Add a helper function to perform stream reset. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-stream.c | 76 ++++++++++++++++++-------------- sound/soc/sof/intel/hda.h | 2 + 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 4502a6133735ca..3c255161487db4 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -279,6 +279,45 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) return 0; } +static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) +{ + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 val; + + /* enter stream reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, + SOF_STREAM_SD_OFFSET_CRST); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if (val & SOF_STREAM_SD_OFFSET_CRST) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream reset\n"); + return -ETIMEDOUT; + } + + timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + + /* exit stream reset and wait to read a zero before reading any other register */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0); + + /* wait for hardware to report that stream is out of reset */ + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream to exit reset\n"); + return -ETIMEDOUT; + } + + return 0; +} + int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd) { @@ -437,9 +476,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + int ret; u32 dma_start = SOF_HDA_SD_CTL_DMA_START; - u32 val, mask; + u32 mask; u32 run; if (!stream) { @@ -484,36 +523,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_CL_DMA_SD_INT_MASK); /* stream reset */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x1); - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if (val & 0x1) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: stream reset failed\n"); - return -ETIMEDOUT; - } - - timeout = HDA_DSP_STREAM_RESET_TIMEOUT; - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x0); - - /* wait for hardware to report that stream is out of reset */ - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if ((val & 0x1) == 0) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); - return -ETIMEDOUT; - } + ret = hda_dsp_stream_reset(sdev, hstream); + if (ret < 0) + return ret; if (hstream->posbuf) *hstream->posbuf = 0; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index b60c77cd0d8f62..37a8fa872f1082 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -487,6 +487,8 @@ struct sof_intel_hda_stream { (SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \ + SOF_HDA_ADSP_LOADER_BASE) +#define SOF_STREAM_SD_OFFSET_CRST 0x1 + /* * DSP Core services. */ From c70906e274cfbe3af78bb7c4ff6cef5b39cc5664 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 4 Oct 2021 21:33:23 -0700 Subject: [PATCH 03/10] ASoC: SOF: Intel: hda: reset stream before coupling host and link DMA's The recommended programming sequence for HD-Audio DMA is to reset the stream before coupling the link and host DMA's. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-stream.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 3c255161487db4..e910f68706d9a8 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -666,6 +666,11 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, hstream); struct hdac_bus *bus = sof_to_bus(sdev); u32 mask = 0x1 << stream->index; + int ret; + + ret = hda_dsp_stream_reset(sdev, stream); + if (ret < 0) + return ret; spin_lock_irq(&bus->reg_lock); /* couple host and link DMA if link DMA channel is idle */ From 152184e18dbd50f4c00f247e74fe5dca1ae4ee77 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 4 Oct 2021 22:17:11 -0700 Subject: [PATCH 04/10] ASoC: SOF: pcm: invoke platform hw_free for STOP/SUSPEND triggers snd_sof_pcm_platform_hw_params() will be called when the stream is restarted with a prepare ioctl. This happens in two cases i.e. when a suspended stream is resumed or or when a stream is restarted without intermediate call to sof_pcm_hw_free(). Make sure to call snd_sof_pcm_platform_hw_free() in both these cases to keep it balanced. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1bf7e60be77216..1d0d90551e8f87 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -470,6 +470,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (ret < 0) return ret; + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return ret; + /* free widget list only for SUSPEND trigger */ if (free_widget_list) ret = sof_widget_list_free(sdev, spcm, substream->stream); From 8725d4c102a593170daa6acd015e229f18948325 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 4 Oct 2021 22:22:05 -0700 Subject: [PATCH 05/10] ASoC: SOF: call platform hw_free for paused streams during suspend Paused streams must be stopped and platform hw_free should be invoked during system suspend so they can be restarted properly after system resume. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/sof-audio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index cdad453bf9c1e3..4d19ba8febe1e5 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -751,10 +751,17 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) continue; if (spcm->stream[dir].list) { + /* Free PCM in the DSP */ ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); if (ret < 0) return ret; + /* stop DMA */ + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return ret; + + /* free the DAPM widget list */ ret = sof_widget_list_free(sdev, spcm, dir); if (ret < 0) { dev_err(sdev->dev, "failed to free widgets during suspend\n"); From c2810820cd059a74e1a053e4afc1479625c97452 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 5 Oct 2021 13:05:47 -0700 Subject: [PATCH 06/10] ASoC: SOF: Add a helper for freeing PCM stream Add a helper function to free PCM in the FW, stop the DMA and free the widget list. These actions are performed both during PCM trigger STOP and when a paused stream is freed during system suspend. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 11 ++--------- sound/soc/sof/sof-audio.c | 40 +++++++++++++++++++++++++-------------- sound/soc/sof/sof-audio.h | 2 ++ 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1d0d90551e8f87..3aa708b1ac26f5 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -466,17 +466,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* free PCM if reset_hw_params is set and the STOP IPC is successful */ if (!ret && reset_hw_params) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, + free_widget_list); if (ret < 0) return ret; - - ret = snd_sof_pcm_platform_hw_free(sdev, substream); - if (ret < 0) - return ret; - - /* free widget list only for SUSPEND trigger */ - if (free_widget_list) - ret = sof_widget_list_free(sdev, spcm, substream->stream); } return ret; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 4d19ba8febe1e5..6b86ebc28182f6 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -728,6 +728,31 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) return 0; } +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list) +{ + int ret; + + /* Send PCM_FREE IPC to reset pipeline */ + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; + + /* stop the DMA */ + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return ret; + + /* free widget list */ + if (free_widget_list) { + ret = sof_widget_list_free(sdev, spcm, dir); + if (ret < 0) + dev_err(sdev->dev, "failed to free widgets during suspend\n"); + } + + return ret; +} + /* * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. @@ -751,22 +776,9 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) continue; if (spcm->stream[dir].list) { - /* Free PCM in the DSP */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; - - /* stop DMA */ - ret = snd_sof_pcm_platform_hw_free(sdev, substream); + ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); if (ret < 0) return ret; - - /* free the DAPM widget list */ - ret = sof_widget_list_free(sdev, spcm, dir); - if (ret < 0) { - dev_err(sdev->dev, "failed to free widgets during suspend\n"); - return ret; - } } } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 1c4f59d347177a..e419e7082c28fd 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -267,4 +267,6 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list); #endif From 392c2eb6fd9653ac3b8bb407cbf92b828956a91a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 6 Oct 2021 12:28:37 -0700 Subject: [PATCH 07/10] ASoC: SOF: pcm: move the check for prepared flag Move the check for the prepared flag inside snd_pcm_dsp_pcm_free() to avoid having to check it before every invocation of the function. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 3aa708b1ac26f5..c61cd3cc4f0246 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -107,6 +107,9 @@ int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev struct sof_ipc_reply reply; int ret; + if (!spcm->prepared[substream->stream]) + return 0; + stream.hdr.size = sizeof(stream); stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; stream.comp_id = spcm->stream[substream->stream].comp_id; @@ -178,11 +181,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - if (spcm->prepared[substream->stream]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; - } + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); @@ -298,11 +299,9 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); - if (spcm->prepared[substream->stream]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - err = ret; - } + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + err = ret; ret = sof_widget_list_free(sdev, spcm, substream->stream); if (ret < 0) From 905737ffba15fd7de6ee2e7dfe8ba517e598f6ed Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 6 Oct 2021 12:32:04 -0700 Subject: [PATCH 08/10] ASoC: SOF: align the hw_free sequence with stop Even though the order of stopping the DMA and freeing the widget list is not important, align the sequence to match with the stop trigger to avoid confusion. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index c61cd3cc4f0246..e4446defe51e91 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -299,22 +299,26 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); + /* free PCM in the DSP */ ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); if (ret < 0) err = ret; - ret = sof_widget_list_free(sdev, spcm, substream->stream); - if (ret < 0) - err = ret; - - cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + /* stop DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); if (ret < 0) { dev_err(component->dev, "error: platform hw free failed\n"); err = ret; } + /* free the DAPM widget list */ + ret = sof_widget_list_free(sdev, spcm, substream->stream); + if (ret < 0) + err = ret; + + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + return err; } From 0a799642f7e72e2bb20c597b1cb1de8c72d9c6d1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 6 Oct 2021 12:41:43 -0700 Subject: [PATCH 09/10] ASoC: SOF: IPC: dai: Expand DAI_CONFIG IPC flags Some DAI components, such as HDaudio, need to be stopped in two steps a) stop the DAI component b) stop the DAI DMA This patch enables this two-step stop by expanding the DAI_CONFIG IPC flags and split them into 2 parts. The 4 LSB bits indicate when the DAI_CONFIG IPC is sent, ex: hw_params, hw_free or pause. The 4 MSB bits are used as the quirk flags to be used along with the command flags. The quirk flag called SOF_DAI_CONFIG_FLAGS_2_STEP_STOP shall be set along with the HW_PARAMS command flag, i.e. before the pipeline is started so that the stop/pause trigger op in the FW can take the appropriate action to either perform/skip the DMA stop. If set, the DMA stop will be executed when the DAI_CONFIG IPC is sent during hw_free. In the case of pause, DMA pause will be handled when the DAI_CONFIG IPC is sent with the PAUSE command flag. Along with this, modify the signature for the hda_ctrl_dai_widget_setup/ hda_ctrl_dai_widget_free() functions to take additional flags as an argument and modify all users to pass the appropriate quirk flags. Only the HDA DAI's need to pass the SOF_DAI_CONFIG_FLAGS_2_STEP_STOP quirk flag during hw_params to indicate that it supports two-step stop and pause. Signed-off-by: Ranjani Sridharan --- include/sound/sof/dai.h | 25 +++++++++++++++++++------ sound/soc/sof/intel/hda-dai.c | 8 ++++---- sound/soc/sof/intel/hda.c | 21 ++++++++++++--------- sound/soc/sof/intel/hda.h | 4 ++-- sound/soc/sof/sof-audio.c | 2 +- 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 5132bc60f54bfd..59ee50ac77053d 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -52,12 +52,25 @@ #define SOF_DAI_FMT_INV_MASK 0x0f00 #define SOF_DAI_FMT_CLOCK_PROVIDER_MASK 0xf000 -/* DAI_CONFIG flags */ -#define SOF_DAI_CONFIG_FLAGS_MASK 0x3 -#define SOF_DAI_CONFIG_FLAGS_NONE (0 << 0) /**< DAI_CONFIG sent without stage information */ -#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS (1 << 0) /**< DAI_CONFIG sent during hw_params stage */ -#define SOF_DAI_CONFIG_FLAGS_HW_FREE (2 << 0) /**< DAI_CONFIG sent during hw_free stage */ -#define SOF_DAI_CONFIG_FLAGS_RFU (3 << 0) /**< not used, reserved for future use */ +/* + * DAI_CONFIG flags. The 4 LSB bits are used for the commands, HW_PARAMS, HW_FREE and PAUSE + * representing when the IPC is sent. The 4 MSB bits are used to add quirks along with the above + * commands. + */ +#define SOF_DAI_CONFIG_FLAGS_CMD_MASK 0xF +#define SOF_DAI_CONFIG_FLAGS_NONE 0 /**< DAI_CONFIG sent without stage information */ +#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS BIT(0) /**< DAI_CONFIG sent during hw_params stage */ +#define SOF_DAI_CONFIG_FLAGS_HW_FREE BIT(1) /**< DAI_CONFIG sent during hw_free stage */ +/**< DAI_CONFIG sent during pause trigger. Only available ABI 3.20 onwards */ +#define SOF_DAI_CONFIG_FLAGS_PAUSE BIT(2) +#define SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT 4 +#define SOF_DAI_CONFIG_FLAGS_QUIRK_MASK (0xF << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT) +/* + * This should be used along with the SOF_DAI_CONFIG_FLAGS_HW_PARAMS to indicate that pipeline + * stop/pause and DAI DMA stop/pause should happen in two steps. This change is only available + * ABI 3.20 onwards. + */ +#define SOF_DAI_CONFIG_FLAGS_2_STEP_STOP BIT(0) /** \brief Types of DAI */ enum sof_ipc_dai_type { diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 55ec86a732f203..328fcba6ffbc5e 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -194,9 +194,9 @@ static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, /* set up/free DAI widget and send DAI_CONFIG IPC */ if (widget_setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int hda_link_hw_params(struct snd_pcm_substream *substream, @@ -448,9 +448,9 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd return 0; if (setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int ssp_dai_startup(struct snd_pcm_substream *substream, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index b2e99d35053ea9..13253f087a15bc 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -41,7 +41,7 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; @@ -60,7 +60,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) /* DAI already configured, reset it before reconfiguring it */ if (sof_dai->configured) { - ret = hda_ctrl_dai_widget_free(w); + ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); if (ret < 0) return ret; } @@ -78,8 +78,10 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return ret; } - /* set HW_PARAMS flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_PARAMS); + /* set HW_PARAMS flag along with quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + /* send DAI_CONFIG IPC */ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, @@ -94,7 +96,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return 0; } -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; @@ -117,8 +119,9 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) config = &sof_dai->dai_config[sof_dai->current_config]; - /* set HW_FREE flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_FREE); + /* set HW_FREE flag along with any quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, &reply, sizeof(reply)); @@ -173,9 +176,9 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, config->alh.stream_id = alh_stream_id; if (setup) - return hda_ctrl_dai_widget_setup(w); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - return hda_ctrl_dai_widget_free(w); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int sdw_params_stream(struct device *dev, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 37a8fa872f1082..a5dafc0c712b1b 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -738,8 +738,8 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) struct snd_sof_dai; struct sof_ipc_dai_config; -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w); -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w); +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); #define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */ #define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */ diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 6b86ebc28182f6..1de15b9dd2f0dc 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -57,7 +57,7 @@ static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *da } /* set NONE flag to clear all previous settings */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_NONE); + config->flags = SOF_DAI_CONFIG_FLAGS_NONE; ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, &reply, sizeof(reply)); From 693860c09dad83396da33a7f9d2698ee912b9022 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 6 Oct 2021 21:03:04 -0700 Subject: [PATCH 10/10] ASoC: SOF: Intel: hda: send DAI_CONFIG IPC during pause For HDA DAI's the DMA must be paused after the RUN bit is cleared by the host. So, send the DAI_CONFIG IPC with just the SOF_DAI_CONFIG_FLAGS_PAUSE flag set to indicate this to the firmware. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dai.c | 42 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 328fcba6ffbc5e..35ffb71116c694 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -283,6 +283,36 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, dai); } +static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) +{ + 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); + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + struct sof_ipc_reply reply; + int ret; + + sof_dai = swidget->private; + + if (!sof_dai || !sof_dai->dai_config) { + dev_err(sdev->dev, "No config for DAI %s\n", w->name); + return -EINVAL; + } + + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* set PAUSE command flag */ + config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); + + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name); + + return ret; +} + static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -308,6 +338,9 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, hda_stream = hstream_to_sof_hda_stream(link_dev); dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); + + w = snd_soc_dai_get_widget(dai, substream->stream); + switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: /* set up hw_params */ @@ -327,11 +360,6 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: snd_hdac_ext_link_stream_clear(link_dev); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; - /* * free DAI widget during stop/suspend to keep widget use_count's balanced. */ @@ -348,6 +376,10 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_link_stream_clear(link_dev); + + ret = hda_link_dai_config_pause_push_ipc(w); + if (ret < 0) + return ret; break; default: return -EINVAL;