[PATCH v2 0/5] soundwire: handle stream at the dailink level
Currently, stream is handled at the dai level. But we have to handle stream at the dailink level in the multi-cpu dailink usage.
changes in v2: - Add kernel doc - Use single line trace log
Pierre-Louis Bossart (5): soundwire: intel: implement get_sdw_stream() operations soundwire: stream: add helper to startup/shutdown streams soundwire: intel: remove stream allocation/free soundwire: cadence: allocate/free dma_data in set_sdw_stream soundwire: intel: don't free dma_data in DAI shutdown
Documentation/driver-api/soundwire/stream.rst | 11 ++- drivers/soundwire/cadence_master.c | 52 +++++++--- drivers/soundwire/intel.c | 82 ++++------------ drivers/soundwire/stream.c | 98 +++++++++++++++++++ include/linux/soundwire/sdw.h | 2 + 5 files changed, 167 insertions(+), 78 deletions(-)
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
This is needed to retrieve the information when the stream is allocated at the dai_link level.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- drivers/soundwire/intel.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 950e4cf09f2a..3f2f23cf8020 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -883,6 +883,22 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, return cdns_set_sdw_stream(dai, stream, false, direction); }
+static void *intel_get_sdw_stream(struct snd_soc_dai *dai, + int direction) +{ + struct sdw_cdns_dma_data *dma; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dma = dai->playback_dma_data; + else + dma = dai->capture_dma_data; + + if (!dma) + return NULL; + + return dma->stream; +} + static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .startup = intel_startup, .hw_params = intel_hw_params, @@ -891,6 +907,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, + .get_sdw_stream = intel_get_sdw_stream, };
static const struct snd_soc_dai_ops intel_pdm_dai_ops = { @@ -901,6 +918,7 @@ static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, + .get_sdw_stream = intel_get_sdw_stream, };
static const struct snd_soc_component_driver dai_component = {
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
To handle streams at the dailink level, expose two helpers that will be called from machine drivers.
Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Guennadi Liakhovetski guennadi.liakhovetski@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- Documentation/driver-api/soundwire/stream.rst | 11 ++- drivers/soundwire/stream.c | 98 +++++++++++++++++++ include/linux/soundwire/sdw.h | 2 + 3 files changed, 110 insertions(+), 1 deletion(-)
diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst index 1b386076402c..8858cea7bfe0 100644 --- a/Documentation/driver-api/soundwire/stream.rst +++ b/Documentation/driver-api/soundwire/stream.rst @@ -293,6 +293,10 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to
int sdw_alloc_stream(char * stream_name);
+The SoundWire core provides a sdw_startup_stream() helper function, +typically called during a dailink .startup() callback, which performs +stream allocation and sets the stream pointer for all DAIs +connected to a stream.
SDW_STREAM_CONFIGURED ~~~~~~~~~~~~~~~~~~~~~ @@ -509,7 +513,12 @@ In .shutdown() the data structure maintaining stream state are freed up.
void sdw_release_stream(struct sdw_stream_runtime * stream);
-Not Supported +The SoundWire core provides a sdw_shutdown_stream() helper function, +typically called during a dailink .shutdown() callback, which clears +the stream pointer for all DAIS connected to a stream and releases the +memory allocated for the stream. + + Not Supported =============
1. A single port with multiple channels supported cannot be used between two diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a9a72574b34a..6bc2ff29c202 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw.h> +#include <sound/soc.h> #include "bus.h"
/* @@ -1826,3 +1827,100 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) return ret; } EXPORT_SYMBOL(sdw_deprepare_stream); + +static int set_stream(struct snd_pcm_substream *substream, + struct sdw_stream_runtime *sdw_stream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai; + int ret = 0; + int i; + + /* Set stream pointer on all DAIs */ + for_each_rtd_dais(rtd, i, dai) { + ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); + if (ret < 0) { + dev_err(rtd->dev, "failed to set stream pointer on dai %s", dai->name); + break; + } + } + + return ret; +} + +/** + * sdw_startup_stream() - Startup SoundWire stream + * + * @stream: Soundwire stream + * + * Documentation/driver-api/soundwire/stream.rst explains this API in detail + */ +int sdw_startup_stream(void *sdw_substream) +{ + struct snd_pcm_substream *substream = sdw_substream; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream; + char *name; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + name = kasprintf(GFP_KERNEL, "%s-Playback", substream->name); + else + name = kasprintf(GFP_KERNEL, "%s-Capture", substream->name); + + if (!name) + return -ENOMEM; + + sdw_stream = sdw_alloc_stream(name); + if (!sdw_stream) { + dev_err(rtd->dev, "alloc stream failed for substream DAI %s", substream->name); + ret = -ENOMEM; + goto error; + } + + ret = set_stream(substream, sdw_stream); + if (ret < 0) + goto release_stream; + return 0; + +release_stream: + sdw_release_stream(sdw_stream); + set_stream(substream, NULL); +error: + kfree(name); + return ret; +} +EXPORT_SYMBOL(sdw_startup_stream); + +/** + * sdw_shutdown_stream() - Shutdown SoundWire stream + * + * @stream: Soundwire stream + * + * Documentation/driver-api/soundwire/stream.rst explains this API in detail + */ +void sdw_shutdown_stream(void *sdw_substream) +{ + struct snd_pcm_substream *substream = sdw_substream; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream; + struct snd_soc_dai *dai; + + /* Find stream from first CPU DAI */ + dai = asoc_rtd_to_cpu(rtd, 0); + + sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + + if (!sdw_stream) { + dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + return; + } + + /* release memory */ + kfree(sdw_stream->name); + sdw_release_stream(sdw_stream); + + /* clear DAI data */ + set_stream(substream, NULL); +} +EXPORT_SYMBOL(sdw_shutdown_stream); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 9c27a32df9bb..51ecbd8faa8c 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -952,10 +952,12 @@ int sdw_stream_remove_master(struct sdw_bus *bus, struct sdw_stream_runtime *stream); int sdw_stream_remove_slave(struct sdw_slave *slave, struct sdw_stream_runtime *stream); +int sdw_startup_stream(void *sdw_substream); int sdw_prepare_stream(struct sdw_stream_runtime *stream); int sdw_enable_stream(struct sdw_stream_runtime *stream); int sdw_disable_stream(struct sdw_stream_runtime *stream); int sdw_deprepare_stream(struct sdw_stream_runtime *stream); +void sdw_shutdown_stream(void *sdw_substream); int sdw_bus_prep_clk_stop(struct sdw_bus *bus); int sdw_bus_clk_stop(struct sdw_bus *bus); int sdw_bus_exit_clk_stop(struct sdw_bus *bus);
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
To support streaming across multiple links, the stream allocation/free needs to be at the dailink level, not the dai.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- drivers/soundwire/intel.c | 57 +-------------------------------------- 1 file changed, 1 insertion(+), 56 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 3f2f23cf8020..2e1e1088a743 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -625,57 +625,6 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * DAI routines */
-static int sdw_stream_setup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct sdw_stream_runtime *sdw_stream = NULL; - char *name; - int i, ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - name = kasprintf(GFP_KERNEL, "%s-Playback", dai->name); - else - name = kasprintf(GFP_KERNEL, "%s-Capture", dai->name); - - if (!name) - return -ENOMEM; - - sdw_stream = sdw_alloc_stream(name); - if (!sdw_stream) { - dev_err(dai->dev, "alloc stream failed for DAI %s", dai->name); - ret = -ENOMEM; - goto error; - } - - /* Set stream pointer on CPU DAI */ - ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); - if (ret < 0) { - dev_err(dai->dev, "failed to set stream pointer on cpu dai %s", - dai->name); - goto release_stream; - } - - /* Set stream pointer on all CODEC DAIs */ - for (i = 0; i < rtd->num_codecs; i++) { - ret = snd_soc_dai_set_sdw_stream(asoc_rtd_to_codec(rtd, i), sdw_stream, - substream->stream); - if (ret < 0) { - dev_err(dai->dev, "failed to set stream pointer on codec dai %s", - asoc_rtd_to_codec(rtd, i)->name); - goto release_stream; - } - } - - return 0; - -release_stream: - sdw_release_stream(sdw_stream); -error: - kfree(name); - return ret; -} - static int intel_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -683,8 +632,7 @@ static int intel_startup(struct snd_pcm_substream *substream, * TODO: add pm_runtime support here, the startup callback * will make sure the IP is 'active' */ - - return sdw_stream_setup(substream, dai); + return 0; }
static int intel_hw_params(struct snd_pcm_substream *substream, @@ -852,9 +800,6 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; }
- kfree(dma->stream->name); - sdw_release_stream(dma->stream); - return 0; }
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
The current memory allocation is somewhat strange: the dma_data is allocated in set_sdw_stream, but released in the intel DAI shutdown. This no longer works with the multi-cpu implementation, since the dma_data is released in the dai shutdown which takes place before the dailink shutdown.
Move to a more symmetric allocation where the dma_data is allocated with non-NULL SoundWire stream, and conversely released when a NULL stream is provided - for consistency with the stream startup and shutdown operations.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- drivers/soundwire/cadence_master.c | 52 ++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 14 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 9ea87538b9ef..613dbd415b91 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1437,25 +1437,49 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai, struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dma_data *dma;
- dma = kzalloc(sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; + if (stream) { + /* first paranoia check */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dma = dai->playback_dma_data; + else + dma = dai->capture_dma_data;
- if (pcm) - dma->stream_type = SDW_STREAM_PCM; - else - dma->stream_type = SDW_STREAM_PDM; + if (dma) { + dev_err(dai->dev, + "dma_data already allocated for dai %s\n", + dai->name); + return -EINVAL; + }
- dma->bus = &cdns->bus; - dma->link_id = cdns->instance; + /* allocate and set dma info */ + dma = kzalloc(sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM;
- dma->stream = stream; + if (pcm) + dma->stream_type = SDW_STREAM_PCM; + else + dma->stream_type = SDW_STREAM_PDM;
- if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = dma; - else - dai->capture_dma_data = dma; + dma->bus = &cdns->bus; + dma->link_id = cdns->instance;
+ dma->stream = stream; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = dma; + else + dai->capture_dma_data = dma; + } else { + /* for NULL stream we release allocated dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + kfree(dai->playback_dma_data); + dai->playback_dma_data = NULL; + } else { + kfree(dai->capture_dma_data); + dai->capture_dma_data = NULL; + } + } return 0; } EXPORT_SYMBOL(cdns_set_sdw_stream);
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Now that the DMA data is allocated/freed in set_sdw_stream(), remove free operations.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- drivers/soundwire/intel.c | 7 ------- 1 file changed, 7 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 2e1e1088a743..7a65414e5714 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -806,14 +806,7 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) static void intel_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct sdw_cdns_dma_data *dma;
- dma = snd_soc_dai_get_dma_data(dai, substream); - if (!dma) - return; - - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(dma); }
static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
participants (2)
-
Bard Liao
-
Vinod Koul