Unverified Commit b66bfc3a authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown
Browse files

ASoC: SOF: sof-audio: Fix broken early bclk feature for SSP



With the removal of widget setup during BE hw_params, the DAI config IPC
is never sent with the SOF_DAI_CONFIG_FLAGS_HW_PARAMS. This means that
the early bit clock feature required for certain codecs will be broken.

Fix this by saving the config flags sent during BE DAI hw_params and
reusing it when the DAI_CONFIG IPC is sent after the DAI widget is set
up. Also, free the DAI config before the widget is freed.

The DAI_CONFIG IPC sent during the sof_widget_free() does not have the
DAI index information. So, save the dai_index in the config during
hw_params and reuse it during hw_free.

For IPC4, do not clear the node ID during hw_free. It will be needed for
freeing the group_ida during unprepare.

Signed-off-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarRander Wang <rander.wang@intel.com>
Reviewed-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://lore.kernel.org/r/20230307114639.4553-1-peter.ujfalusi@linux.intel.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent ca09e2a3
Loading
Loading
Loading
Loading
+30 −2
Original line number Diff line number Diff line
@@ -2081,6 +2081,8 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
		break;
	case SOF_DAI_INTEL_ALH:
		if (data) {
			/* save the dai_index during hw_params and reuse it for hw_free */
			if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS)
				config->dai_index = data->dai_index;
			config->alh.stream_id = data->dai_data;
		}
@@ -2089,6 +2091,29 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
		break;
	}

	/*
	 * The dai_config op is invoked several times and the flags argument varies as below:
	 * BE DAI hw_params: When the op is invoked during the BE DAI hw_params, flags contains
	 * SOF_DAI_CONFIG_FLAGS_HW_PARAMS along with quirks
	 * FE DAI hw_params: When invoked during FE DAI hw_params after the DAI widget has
	 * just been set up in the DSP, flags is set to SOF_DAI_CONFIG_FLAGS_HW_PARAMS with no
	 * quirks
	 * BE DAI trigger: When invoked during the BE DAI trigger, flags is set to
	 * SOF_DAI_CONFIG_FLAGS_PAUSE and contains no quirks
	 * BE DAI hw_free: When invoked during the BE DAI hw_free, flags is set to
	 * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks
	 * FE DAI hw_free: When invoked during the FE DAI hw_free, flags is set to
	 * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks
	 *
	 * The DAI_CONFIG IPC is sent to the DSP, only after the widget is set up during the FE
	 * DAI hw_params. But since the BE DAI hw_params precedes the FE DAI hw_params, the quirks
	 * need to be preserved when assigning the flags before sending the IPC.
	 * For the case of PAUSE/HW_FREE, since there are no quirks, flags can be used as is.
	 */

	if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS)
		config->flags |= flags;
	else
		config->flags = flags;

	/* only send the IPC if the widget is set up in the DSP */
@@ -2097,6 +2122,9 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
					 &reply, sizeof(reply));
		if (ret < 0)
			dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name);

		/* clear the flags once the IPC has been sent even if it fails */
		config->flags = SOF_DAI_CONFIG_FLAGS_NONE;
	}

	return ret;
+13 −2
Original line number Diff line number Diff line
@@ -980,6 +980,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)

		ipc4_copier = dai->private;
		if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
			struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
			struct sof_ipc4_alh_configuration_blob *blob;
			unsigned int group_id;

@@ -989,6 +990,9 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
					   ALH_MULTI_GTW_BASE;
				ida_free(&alh_group_ida, group_id);
			}

			/* clear the node ID */
			copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
		}
	}

@@ -1940,8 +1944,15 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
		pipeline->skip_during_fe_trigger = true;
		fallthrough;
	case SOF_DAI_INTEL_ALH:
		/*
		 * Do not clear the node ID when this op is invoked with
		 * SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during
		 * unprepare.
		 */
		if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
			copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
			copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);
		}
		break;
	case SOF_DAI_INTEL_DMIC:
	case SOF_DAI_INTEL_SSP:
+25 −3
Original line number Diff line number Diff line
@@ -50,9 +50,27 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
	/* reset route setup status for all routes that contain this widget */
	sof_reset_route_setup_status(sdev, swidget);

	/* free DAI config and continue to free widget even if it fails */
	if (WIDGET_IS_DAI(swidget->id)) {
		struct snd_sof_dai_config_data data;
		unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_FREE;

		data.dai_data = DMA_CHAN_INVALID;

		if (tplg_ops && tplg_ops->dai_config) {
			err = tplg_ops->dai_config(sdev, swidget, flags, &data);
			if (err < 0)
				dev_err(sdev->dev, "failed to free config for widget %s\n",
					swidget->widget->name);
		}
	}

	/* continue to disable core even if IPC fails */
	if (tplg_ops && tplg_ops->widget_free)
		err = tplg_ops->widget_free(sdev, swidget);
	if (tplg_ops && tplg_ops->widget_free) {
		ret = tplg_ops->widget_free(sdev, swidget);
		if (ret < 0 && !err)
			err = ret;
	}

	/*
	 * disable widget core. continue to route setup status and complete flag
@@ -151,8 +169,12 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,

	/* send config for DAI components */
	if (WIDGET_IS_DAI(swidget->id)) {
		unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE;
		unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;

		/*
		 * The config flags saved during BE DAI hw_params will be used for IPC3. IPC4 does
		 * not use the flags argument.
		 */
		if (tplg_ops && tplg_ops->dai_config) {
			ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
			if (ret < 0)