summaryrefslogtreecommitdiff
path: root/sound/soc/sof/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/pcm.c')
-rw-r--r--sound/soc/sof/pcm.c196
1 files changed, 124 insertions, 72 deletions
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index c5c6353f18ce..fe43de1fe96c 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -20,24 +20,6 @@
#include "sof-utils.h"
#include "ops.h"
-/* Create DMA buffer page table for DSP */
-static int create_page_table(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- unsigned char *dma_area, size_t size)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_sof_pcm *spcm;
- struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
- int stream = substream->stream;
-
- spcm = snd_sof_find_spcm_dai(component, rtd);
- if (!spcm)
- return -EINVAL;
-
- return snd_sof_create_page_table(component->dev, dmab,
- spcm->stream[stream].page_table.area, size);
-}
-
/*
* sof pcm period elapse work
*/
@@ -99,8 +81,8 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list,
dpcm_end_walk_at_be);
if (ret < 0) {
- dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name,
- snd_pcm_direction_name(dir));
+ spcm_err(spcm, dir, "dai %s has no valid %s path\n",
+ dai->name, snd_pcm_direction_name(dir));
return ret;
}
@@ -108,8 +90,7 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
if (ret < 0) {
- dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
- spcm->pcm.pcm_id, dir);
+ spcm_err(spcm, dir, "Widget list set up failed\n");
spcm->stream[dir].list = NULL;
snd_soc_dapm_dai_free_widgets(&list);
return ret;
@@ -139,11 +120,13 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
+ spcm_dbg(spcm, substream->stream, "Entry: hw_params\n");
+
/*
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
*/
- if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (spcm->prepared[substream->stream] && pcm_ops && pcm_ops->hw_free) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
return ret;
@@ -151,12 +134,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
spcm->prepared[substream->stream] = false;
}
- dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
-
ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
if (ret < 0) {
- dev_err(component->dev, "platform hw params failed\n");
+ spcm_err(spcm, substream->stream, "platform hw params failed\n");
return ret;
}
@@ -170,9 +150,11 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
/* create compressed page table for audio firmware */
if (runtime->buffer_changed) {
- ret = create_page_table(component, substream, runtime->dma_area,
- runtime->dma_bytes);
+ struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
+ ret = snd_sof_create_page_table(component->dev, dmab,
+ spcm->stream[substream->stream].page_table.area,
+ runtime->dma_bytes);
if (ret < 0)
return ret;
}
@@ -191,6 +173,84 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return 0;
}
+static int sof_pcm_stream_free(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_sof_pcm *spcm, int dir,
+ bool free_widget_list)
+{
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+ int ret;
+ int err = 0;
+
+ if (spcm->prepared[substream->stream]) {
+ /* stop DMA first if needed */
+ if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
+ snd_sof_pcm_platform_trigger(sdev, substream,
+ SNDRV_PCM_TRIGGER_STOP);
+
+ /* free PCM in the DSP */
+ if (pcm_ops && pcm_ops->hw_free) {
+ ret = pcm_ops->hw_free(sdev->component, substream);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "pcm_ops->hw_free failed %d\n", ret);
+ err = ret;
+ }
+ }
+
+ spcm->prepared[substream->stream] = false;
+ spcm->pending_stop[substream->stream] = false;
+ }
+
+ /* reset the DMA */
+ ret = snd_sof_pcm_platform_hw_free(sdev, substream);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "platform hw free failed %d\n", ret);
+ if (!err)
+ err = ret;
+ }
+
+ /* free widget list */
+ if (free_widget_list) {
+ ret = sof_widget_list_free(sdev, spcm, dir);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "sof_widget_list_free failed %d\n", ret);
+ if (!err)
+ err = ret;
+ }
+ }
+
+ return err;
+}
+
+int sof_pcm_free_all_streams(struct snd_sof_dev *sdev)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_sof_pcm *spcm;
+ int dir, ret;
+
+ list_for_each_entry(spcm, &sdev->pcm_list, list) {
+ for_each_pcm_streams(dir) {
+ substream = spcm->stream[dir].substream;
+
+ if (!substream || !substream->runtime ||
+ spcm->stream[dir].suspend_ignored)
+ continue;
+
+ if (spcm->stream[dir].list) {
+ ret = sof_pcm_stream_free(sdev, substream, spcm,
+ dir, true);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int sof_pcm_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -207,8 +267,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
+ spcm_dbg(spcm, substream->stream, "Entry: hw_free\n");
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
@@ -233,6 +292,8 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
+ spcm_dbg(spcm, substream->stream, "Entry: prepare\n");
+
if (spcm->prepared[substream->stream]) {
if (!spcm->pending_stop[substream->stream])
return 0;
@@ -246,15 +307,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
return ret;
}
- dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
-
/* set hw_params */
ret = sof_pcm_hw_params(component,
substream, &spcm->params[substream->stream]);
if (ret < 0) {
- dev_err(component->dev,
- "error: set pcm hw_params after resume\n");
+ spcm_err(spcm, substream->stream,
+ "failed to set hw_params after resume\n");
return ret;
}
@@ -284,8 +342,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
- spcm->pcm.pcm_id, substream->stream, cmd);
+ spcm_dbg(spcm, substream->stream, "Entry: trigger (cmd: %d)\n", cmd);
spcm->pending_stop[substream->stream] = false;
@@ -334,7 +391,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
reset_hw_params = true;
break;
default:
- dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
+ spcm_err(spcm, substream->stream, "Unhandled trigger cmd %d\n", cmd);
return -EINVAL;
}
@@ -436,9 +493,7 @@ static int sof_pcm_open(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: open stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
-
+ spcm_dbg(spcm, substream->stream, "Entry: open\n");
caps = &spcm->pcm.caps[substream->stream];
@@ -458,15 +513,6 @@ static int sof_pcm_open(struct snd_soc_component *component,
*/
runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max);
- dev_dbg(component->dev, "period min %zd max %zd bytes\n",
- runtime->hw.period_bytes_min,
- runtime->hw.period_bytes_max);
- dev_dbg(component->dev, "period count %d max %d\n",
- runtime->hw.periods_min,
- runtime->hw.periods_max);
- dev_dbg(component->dev, "buffer max %zd bytes\n",
- runtime->hw.buffer_bytes_max);
-
/* set wait time - TODO: come from topology */
substream->wait_time = 500;
@@ -476,10 +522,19 @@ static int sof_pcm_open(struct snd_soc_component *component,
spcm->prepared[substream->stream] = false;
ret = snd_sof_pcm_platform_open(sdev, substream);
- if (ret < 0)
- dev_err(component->dev, "error: pcm open failed %d\n", ret);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "platform pcm open failed %d\n", ret);
+ return ret;
+ }
- return ret;
+ spcm_dbg(spcm, substream->stream, "period bytes min %zd, max %zd\n",
+ runtime->hw.period_bytes_min, runtime->hw.period_bytes_max);
+ spcm_dbg(spcm, substream->stream, "period count min %d, max %d\n",
+ runtime->hw.periods_min, runtime->hw.periods_max);
+ spcm_dbg(spcm, substream->stream, "buffer bytes max %zd\n", runtime->hw.buffer_bytes_max);
+
+ return 0;
}
static int sof_pcm_close(struct snd_soc_component *component,
@@ -498,13 +553,12 @@ static int sof_pcm_close(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: close stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
+ spcm_dbg(spcm, substream->stream, "Entry: close\n");
err = snd_sof_pcm_platform_close(sdev, substream);
if (err < 0) {
- dev_err(component->dev, "error: pcm close failed %d\n",
- err);
+ spcm_err(spcm, substream->stream,
+ "platform pcm close failed %d\n", err);
/*
* keep going, no point in preventing the close
* from happening
@@ -538,7 +592,8 @@ static int sof_pcm_new(struct snd_soc_component *component,
return 0;
}
- dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name);
+ dev_dbg(spcm->scomp->dev, "pcm%u (%s): Entry: pcm_construct\n",
+ spcm->pcm.pcm_id, spcm->pcm.pcm_name);
/* do we need to pre-allocate playback audio buffer pages */
if (!spcm->pcm.playback)
@@ -546,16 +601,15 @@ static int sof_pcm_new(struct snd_soc_component *component,
caps = &spcm->pcm.caps[stream];
- /* pre-allocate playback audio buffer pages */
- dev_dbg(component->dev,
- "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n",
- caps->name, caps->buffer_size_min, caps->buffer_size_max);
-
if (!pcm->streams[stream].substream) {
- dev_err(component->dev, "error: NULL playback substream!\n");
+ spcm_err(spcm, stream, "NULL playback substream!\n");
return -EINVAL;
}
+ /* pre-allocate playback audio buffer pages */
+ spcm_dbg(spcm, stream, "allocate %s playback DMA buffer size 0x%x max 0x%x\n",
+ caps->name, caps->buffer_size_min, caps->buffer_size_max);
+
snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0, le32_to_cpu(caps->buffer_size_max));
@@ -568,16 +622,15 @@ capture:
caps = &spcm->pcm.caps[stream];
- /* pre-allocate capture audio buffer pages */
- dev_dbg(component->dev,
- "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n",
- caps->name, caps->buffer_size_min, caps->buffer_size_max);
-
if (!pcm->streams[stream].substream) {
- dev_err(component->dev, "error: NULL capture substream!\n");
+ spcm_err(spcm, stream, "NULL capture substream!\n");
return -EINVAL;
}
+ /* pre-allocate capture audio buffer pages */
+ spcm_dbg(spcm, stream, "allocate %s capture DMA buffer size 0x%x max 0x%x\n",
+ caps->name, caps->buffer_size_min, caps->buffer_size_max);
+
snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0, le32_to_cpu(caps->buffer_size_max));
@@ -659,7 +712,6 @@ static int sof_pcm_probe(struct snd_soc_component *component)
ret);
pm_error:
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return ret;