ASoC: SOF: IPC: dai: Expand DAI_CONFIG IPC flags
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Thu, 25 Nov 2021 10:15:19 +0000 (12:15 +0200)
committerMark Brown <broonie@kernel.org>
Fri, 26 Nov 2021 13:24:24 +0000 (13:24 +0000)
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 <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20211125101520.291581-10-kai.vehmanen@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/sof/dai.h
sound/soc/sof/intel/hda-dai.c
sound/soc/sof/intel/hda.c
sound/soc/sof/intel/hda.h
sound/soc/sof/sof-audio.c

index 5132bc6..59ee50a 100644 (file)
 #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 {
index 748e8ed..bce5366 100644 (file)
@@ -197,9 +197,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,
@@ -452,9 +452,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,
index 1e1e965..cfe026d 100644 (file)
@@ -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;
@@ -58,6 +58,13 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
                return -EINVAL;
        }
 
+       /* DAI already configured, reset it before reconfiguring it */
+       if (sof_dai->configured) {
+               ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
+               if (ret < 0)
+                       return ret;
+       }
+
        config = &sof_dai->dai_config[sof_dai->current_config];
 
        /*
@@ -71,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,
@@ -87,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;
@@ -110,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));
@@ -166,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,
index 60139ea..72e78c4 100644 (file)
@@ -739,7 +739,7 @@ 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);
 
 #endif
index a275f7b..58a62bf 100644 (file)
@@ -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));