All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Cezary Rojewski" <cezary.rojewski@intel.com>,
	linux-sound@vger.kernel.org
Subject: Re: [PATCH v3 7/7] ASoC: Intel: avs: SoundWire PCM BE operations
Date: Fri, 06 Nov 2026 12:41:13 +0000	[thread overview]
Message-ID: <sashiko-outbox-201406@kernel.org> (raw)
In-Reply-To: <20261106121557.2338556-8-cezary.rojewski@intel.com>

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] ASoC: Intel: avs: broken sdw_suspended state and m_rt lifecycle
  in pcm_sdw.c
- [High] ASoC: Intel: avs: stream left in SDW_STREAM_DEPREPARED on
  resume in pcm_sdw.c
- [High] ASoC: Intel: avs: bulk PDIs 0 and 1 assigned to PCM streams in
  avs_sdw_dai_startup()
--

commit 18beef33b6f436db2bab29aa861b96311ce4d2f9
Author: Cezary Rojewski <cezary.rojewski@intel.com>

ASoC: Intel: avs: SoundWire PCM BE operations

This commit adds back-end DAI operations and component registration for
SoundWire PCM streams in the Intel AVS driver.

> diff --git a/sound/soc/intel/avs/pcm_sdw.c b/sound/soc/intel/avs/pcm_sdw.c
> new file mode 100644
> index 0000000000000..578162e9fc4dc
> --- /dev/null
> +++ b/sound/soc/intel/avs/pcm_sdw.c
> @@ -0,0 +1,291 @@

[ ... ]

> +static int avs_sdw_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> +	struct sdw_stream_runtime *sdw_stream;
> +	struct snd_soc_acpi_mach *mach;
> +	struct snd_soc_dai *codec_dai;
> +	struct avs_dma_data *data;
> +	struct sdw_cdns_pdi *pdi;
> +	struct sdw_slave *codec;
> +	int dir, ret;
> +
> +	mach = dev_get_platdata(dai->component->card->dev);
> +	codec = mach->pdata;
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		dir = SDW_DATA_DIR_RX;
> +	else
> +		dir = SDW_DATA_DIR_TX;
> +
> +	ret = avs_dai_startup(substream, dai);
> +	if (ret)
> +		return ret;
> +
> +	sdw_stream = sdw_alloc_stream(substream->name, SDW_STREAM_PCM);
> +	if (!sdw_stream) {
> +		ret = -ENOMEM;
> +		goto err_alloc_stream;
> +	}
> +
> +	pdi = sdw_cdns_assign_pdi(codec->bus, dir);
> +	if (!pdi) {
> +		ret = -EBUSY;
> +		goto err_assign_pdi;
> +	}

[Severity: High]
Does sdw_cdns_assign_pdi() assign bulk PDIs 0 and 1 to the first two PCM
substreams on a link?

On Intel cAVS SoundWire controllers, SDW_SHIM_PCMSCAP reports bidirectional
streams in pcm_bd with pcm_in and pcm_out set to 0, where PDI 0 and PDI 1
are reserved for 1-channel bulk transfers and PCM PDIs start at index 2.
Because sdw_cdns_assign_pdi() searches stream->bd starting from index 0,
passing the resulting pdi to sdw_intel_pdi_config() and
sdw_stream_add_master() in avs_sdw_dai_hw_params() enables
CDNS_PORTCTRL_BULK_ENABLE on TX, maps PDI 1 to DataPort 0 in
CDNS_PDI_CONFIG(1) while configuring DataPort 1 in pconfig.num, and
computes bulk ALH stream IDs instead of PCM ALH stream IDs.

> +	/* Fulfill the codec needs by setting the sdw_stream. */
> +	codec_dai = snd_soc_rtd_to_codec(rtd, 0);
> +	snd_soc_dai_set_stream(codec_dai, sdw_stream, substream->stream);
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +	data->sdw_stream = sdw_stream;
> +	data->pdi = pdi;
> +	return 0;
> +
> +err_assign_pdi:
> +	sdw_release_stream(sdw_stream);
> +err_alloc_stream:
> +	avs_dai_shutdown(substream, dai);
> +	return ret;
> +}
> +
> +static void avs_sdw_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> +	struct snd_soc_acpi_mach *mach;
> +	struct snd_soc_dai *codec_dai;
> +	struct avs_dma_data *data;
> +	struct sdw_slave *codec;
> +
> +	codec_dai = snd_soc_rtd_to_codec(rtd, 0);
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +	mach = dev_get_platdata(dai->component->card->dev);
> +	codec = mach->pdata;
> +
> +	snd_soc_dai_set_stream(codec_dai, NULL, substream->stream);
> +	sdw_cdns_release_pdi(codec->bus, data->pdi);
> +	sdw_release_stream(data->sdw_stream);
> +	avs_dai_shutdown(substream, dai);
> +}
> +
> +static int avs_sdw_dai_hw_params(struct snd_pcm_substream *substream,
> +				 struct snd_pcm_hw_params *hw_params,
> +				 struct snd_soc_dai *dai)
> +{
> +	struct sdw_stream_config sconfig = {0};
> +	struct sdw_port_config pconfig = {0};
> +	struct snd_soc_acpi_mach *mach;
> +	struct avs_dma_data *data;
> +	struct sdw_cdns_pdi *pdi;
> +	struct sdw_slave *codec;
> +	int ret;
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +	if (data->path)
> +		return 0;
> +
> +	mach = dev_get_platdata(dai->component->card->dev);
> +	codec = mach->pdata;
> +	pdi = data->pdi;
> +	pdi->ch_count = params_channels(hw_params);
> +	pdi->h_ch_num = pdi->ch_count - 1;
> +	pdi->l_ch_num = 0;
> +	sdw_intel_pdi_config(codec->bus, pdi);
> +
> +	if (data->sdw_suspended)
> +		return avs_dai_be_hw_params(substream, hw_params, dai, pdi->intel_alh_id);
> +
> +	sconfig.direction = pdi->dir;
> +	sconfig.ch_count = pdi->ch_count;
> +	sconfig.frame_rate = params_rate(hw_params);
> +	sconfig.type = SDW_STREAM_PCM;
> +	sconfig.bps = snd_pcm_format_width(params_format(hw_params));
> +	pconfig.num = pdi->num;
> +	pconfig.ch_mask = (1 << pdi->ch_count) - 1;
> +
> +	ret = sdw_stream_add_master(codec->bus, &sconfig, &pconfig, 1, data->sdw_stream);
> +	if (ret) {
> +		sdw_stream_remove_master(codec->bus, data->sdw_stream);
> +		return ret;
> +	}
> +
> +	ret = avs_dai_be_hw_params(substream, hw_params, dai, pdi->intel_alh_id);
> +	if (ret)
> +		sdw_stream_remove_master(codec->bus, data->sdw_stream);
> +	return ret;
> +}
> +
> +static int avs_sdw_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_acpi_mach *mach;
> +	struct avs_dma_data *data;
> +	struct sdw_slave *codec;
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!data->path)
> +		return 0;
> +
> +	mach = dev_get_platdata(dai->component->card->dev);
> +	codec = mach->pdata;
> +
> +	avs_path_reset(data->path);
> +	avs_path_free(data->path);
> +	data->path = NULL;
> +	sdw_deprepare_stream(data->sdw_stream);
> +	if (!data->sdw_suspended)
> +		sdw_stream_remove_master(codec->bus, data->sdw_stream);
> +
> +	return 0;
> +}

[Severity: High]
When a suspended stream resumes, does leaving data->sdw_stream in
SDW_STREAM_DEPREPARED cause sdw_prepare_stream() to skip computing its port
and transport parameters?

During suspend, avs_sdw_dai_hw_free() calls sdw_deprepare_stream(), which
sets data->sdw_stream->state to SDW_STREAM_DEPREPARED and subtracts the
stream bandwidth from bus->params.bandwidth.  On resume,
avs_sdw_dai_hw_params() returns early when data->sdw_suspended is set
without restoring the state to SDW_STREAM_CONFIGURED, so
sdw_prepare_stream() runs with update_params set to true while
sdw_compute_port_params() ignores streams in SDW_STREAM_DEPREPARED:

drivers/soundwire/generic_bandwidth_allocation.c:sdw_compute_group_params() {
    ...
		if (m_rt->stream == stream) {
			/* Only runtime during prepare should be added */
			if (stream->state != SDW_STREAM_CONFIGURED)
				continue;
		} else {
			/* Include streams withPrepared or Enabled seamlessly */
			if (m_rt->stream->state != SDW_STREAM_PREPARED &&
			    m_rt->stream->state != SDW_STREAM_ENABLED)
				continue;
		}
    ...
}

> +
> +static int avs_sdw_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct avs_dma_data *data;
> +	int ret;
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +
> +	/*
> +	 * There is no guarantee trigger(RESUME) fires, so reset the flag here.
> +	 * Ensures there is no sdw_master leak should hw_free() occur without resuming.
> +	 */
> +	data->sdw_suspended = false;
> +
> +	ret = sdw_prepare_stream(data->sdw_stream);
> +	if (ret) {
> +		dev_err(dai->dev, "sdw stream prepare failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = avs_dai_prepare(substream, dai);
> +	if (ret)
> +		sdw_deprepare_stream(data->sdw_stream);
> +	return ret;
> +}
> +
> +static int avs_sdw_dai_trigger(struct snd_pcm_substream *substream, int cmd,
> +			       struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> +	struct avs_dma_data *data;
> +	int ret = 0;
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +		if (rtd->dai_link->ignore_suspend)
> +			break;
> +		fallthrough;
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		ret = sdw_enable_stream(data->sdw_stream);
> +		if (ret)
> +			break;
> +
> +		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
> +		if (ret)
> +			sdw_disable_stream(data->sdw_stream);
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +		if (rtd->dai_link->ignore_suspend)
> +			break;
> +		data->sdw_suspended = true;
> +		fallthrough;

[Severity: High]
Can tracking suspend via data->sdw_suspended in avs_sdw_dai_trigger() break
resume for non-running streams and leave a freed data->sdw_stream linked on
codec->bus->m_rt_list if resume fails?

snd_pcm_do_suspend() only issues SNDRV_PCM_TRIGGER_SUSPEND when the
substream is running, so a configured substream in PAUSED, PREPARED, SETUP,
or XRUN state enters avs_component_suspend() with data->sdw_suspended still
false.  avs_sdw_dai_hw_free() then calls sdw_stream_remove_master(), which
frees the master and codec slave runtimes and sets data->sdw_stream->state
to SDW_STREAM_RELEASED, but avs_component_resume() only re-runs CPU DAI
hw_params and never calls the codec DAI's hw_params (sdw_stream_add_slave()),
causing sdw_prepare_stream() in avs_sdw_dai_prepare() to fail with -EINVAL
and disconnect the substream.

Conversely, when a running stream suspends (setting data->sdw_suspended =
true) and avs_component_resume_hw_params() fails before
avs_sdw_dai_prepare() clears data->sdw_suspended, closing the substream
skips sdw_stream_remove_master() in avs_sdw_dai_hw_free() (because
!data->path or data->sdw_suspended is true) and frees data->sdw_stream in
avs_sdw_dai_shutdown(), leaving m_rt on codec->bus->m_rt_list with a
dangling m_rt->stream pointer (use-after-free on subsequent SoundWire bus
operations).  The same dangling m_rt occurs if codec DAI hw_params fails
after sdw_stream_add_slave() allocates m_rt on codec->bus before
avs_sdw_dai_hw_params() sets data->path.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261106121557.2338556-1-cezary.rojewski@intel.com?part=7

  reply	other threads:[~2026-11-06 12:41 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 1/7] soundwire: intel: Define stubs when library is disabled Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs Cezary Rojewski
2026-11-06 12:26   ` sashiko-bot
2026-11-06 12:32   ` Cezary Rojewski
2026-11-06 19:59   ` kernel test robot
2026-11-06 12:15 ` [PATCH v3 3/7] soundwire: intel: Split PDIs and DAIs initialization Cezary Rojewski
2026-11-06 12:18   ` sashiko-bot
2026-11-06 12:15 ` [PATCH v3 4/7] ASoC: Intel: avs: Pass irq to dsp_interrupt callback Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 5/7] ASoC: Intel: avs: SoundWire support Cezary Rojewski
2026-11-06 12:33   ` sashiko-bot
2026-11-06 12:15 ` [PATCH v3 6/7] ASoC: Intel: avs: Expose common PCM functions Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 7/7] ASoC: Intel: avs: SoundWire PCM BE operations Cezary Rojewski
2026-11-06 12:41   ` sashiko-bot [this message]
2026-11-06 12:55   ` Pierre-Louis Bossart
2026-11-09  9:09     ` Cezary Rojewski
2026-11-10  8:02       ` Pierre-Louis Bossart
2026-11-10  9:18         ` Cezary Rojewski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-201406@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=cezary.rojewski@intel.com \
    --cc=linux-sound@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.