ASoC: qcom: sdw: fix memory leak for sdw_stream_runtime

For some reason we endedup allocating sdw_stream_runtime for every cpu dai,
this has two issues.
1. we never set snd_soc_dai_set_stream for non soundwire dai, which
   means there is no way that we can free this, resulting in memory leak
2. startup and shutdown callbacks can be called without
   hw_params callback called. This combination results in memory leak
because machine driver sruntime array pointer is only set in hw_params
callback.

Fix this by
 1. adding a helper function to get sdw_runtime for substream
which can be used by shutdown callback to get hold of sruntime to free.
 2. only allocate sdw_runtime for soundwire dais.

Fixes: d32bac9cb0 ("ASoC: qcom: Add helper for allocating Soundwire stream runtime")
Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Cc: Stable@vger.kernel.org
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Tested-by: Steev Klimaszewski <threeway@gmail.com> # Thinkpad X13s
Link: https://patch.msgid.link/20251022143349.1081513-2-srinivas.kandagatla@oss.qualcomm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Srinivas Kandagatla
2025-10-22 15:33:46 +01:00
committed by Mark Brown
parent 45f5c9eec4
commit bcba172793
6 changed files with 64 additions and 50 deletions

View File

@@ -317,7 +317,7 @@ static void sc7280_snd_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_card *card = rtd->card;
struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
switch (cpu_dai->id) {
case MI2S_PRIMARY:

View File

@@ -73,7 +73,7 @@ static void sc8280xp_snd_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id];
struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
pdata->sruntime[cpu_dai->id] = NULL;
sdw_release_stream(sruntime);

View File

@@ -7,6 +7,37 @@
#include <sound/soc.h>
#include "sdw.h"
static bool qcom_snd_is_sdw_dai(int id)
{
switch (id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_TX_0:
case WSA_CODEC_DMA_RX_1:
case WSA_CODEC_DMA_TX_1:
case WSA_CODEC_DMA_TX_2:
case RX_CODEC_DMA_RX_0:
case TX_CODEC_DMA_TX_0:
case RX_CODEC_DMA_RX_1:
case TX_CODEC_DMA_TX_1:
case RX_CODEC_DMA_RX_2:
case TX_CODEC_DMA_TX_2:
case RX_CODEC_DMA_RX_3:
case TX_CODEC_DMA_TX_3:
case RX_CODEC_DMA_RX_4:
case TX_CODEC_DMA_TX_4:
case RX_CODEC_DMA_RX_5:
case TX_CODEC_DMA_TX_5:
case RX_CODEC_DMA_RX_6:
case RX_CODEC_DMA_RX_7:
case SLIMBUS_0_RX...SLIMBUS_6_TX:
return true;
default:
break;
}
return false;
}
/**
* qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
* @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
@@ -29,6 +60,9 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
int ret, i, j;
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return 0;
sruntime = sdw_alloc_stream(cpu_dai->name, SDW_STREAM_PCM);
if (!sruntime)
return -ENOMEM;
@@ -89,19 +123,8 @@ int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
if (!sruntime)
return 0;
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_RX_1:
case RX_CODEC_DMA_RX_0:
case RX_CODEC_DMA_RX_1:
case TX_CODEC_DMA_TX_0:
case TX_CODEC_DMA_TX_1:
case TX_CODEC_DMA_TX_2:
case TX_CODEC_DMA_TX_3:
break;
default:
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return 0;
}
if (*stream_prepared)
return 0;
@@ -129,9 +152,7 @@ int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct sdw_stream_runtime **psruntime)
struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
@@ -139,21 +160,23 @@ int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
struct sdw_stream_runtime *sruntime;
int i;
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
case RX_CODEC_DMA_RX_0:
case RX_CODEC_DMA_RX_1:
case TX_CODEC_DMA_TX_0:
case TX_CODEC_DMA_TX_1:
case TX_CODEC_DMA_TX_2:
case TX_CODEC_DMA_TX_3:
for_each_rtd_codec_dais(rtd, i, codec_dai) {
sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
if (sruntime != ERR_PTR(-ENOTSUPP))
*psruntime = sruntime;
}
break;
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return NULL;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
if (sruntime != ERR_PTR(-ENOTSUPP))
return sruntime;
}
return NULL;
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_get_stream);
int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct sdw_stream_runtime **psruntime)
{
*psruntime = qcom_snd_sdw_get_stream(substream);
return 0;
@@ -166,23 +189,13 @@ int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_RX_1:
case RX_CODEC_DMA_RX_0:
case RX_CODEC_DMA_RX_1:
case TX_CODEC_DMA_TX_0:
case TX_CODEC_DMA_TX_1:
case TX_CODEC_DMA_TX_2:
case TX_CODEC_DMA_TX_3:
if (sruntime && *stream_prepared) {
sdw_disable_stream(sruntime);
sdw_deprepare_stream(sruntime);
*stream_prepared = false;
}
break;
default:
break;
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return 0;
if (sruntime && *stream_prepared) {
sdw_disable_stream(sruntime);
sdw_deprepare_stream(sruntime);
*stream_prepared = false;
}
return 0;

View File

@@ -10,6 +10,7 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream);
int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
struct sdw_stream_runtime *runtime,
bool *stream_prepared);
struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *stream);
int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct sdw_stream_runtime **psruntime);

View File

@@ -117,7 +117,7 @@ static void sm8250_snd_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
data->sruntime[cpu_dai->id] = NULL;
sdw_release_stream(sruntime);

View File

@@ -55,7 +55,7 @@ static void x1e80100_snd_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
data->sruntime[cpu_dai->id] = NULL;
sdw_release_stream(sruntime);