aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-pcm.c')
-rw-r--r--sound/soc/soc-pcm.c128
1 files changed, 86 insertions, 42 deletions
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 002311afdeaa..6b0136e7cb88 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -301,34 +301,18 @@ static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
return symmetry;
}
-/*
- * List of sample sizes that might go over the bus for parameter
- * application. There ought to be a wildcard sample size for things
- * like the DAC/ADC resolution to use but there isn't right now.
- */
-static int sample_sizes[] = {
- 24, 32,
-};
-
static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int ret, i;
+ int ret;
if (!bits)
return;
- for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) {
- if (bits >= sample_sizes[i])
- continue;
-
- ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0,
- sample_sizes[i], bits);
- if (ret != 0)
- dev_warn(rtd->dev,
- "ASoC: Failed to set MSB %d/%d: %d\n",
- bits, sample_sizes[i], ret);
- }
+ ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, bits);
+ if (ret != 0)
+ dev_warn(rtd->dev, "ASoC: Failed to set MSB %d: %d\n",
+ bits, ret);
}
static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
@@ -654,6 +638,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
codec_dai->rate = 0;
}
+ snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@ -744,7 +730,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
codec_dai);
if (ret < 0) {
dev_err(codec_dai->dev,
- "ASoC: DAI prepare error: %d\n", ret);
+ "ASoC: codec DAI prepare error: %d\n",
+ ret);
goto out;
}
}
@@ -753,8 +740,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) {
ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
if (ret < 0) {
- dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d\n",
- ret);
+ dev_err(cpu_dai->dev,
+ "ASoC: cpu DAI prepare error: %d\n", ret);
goto out;
}
}
@@ -772,6 +759,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
for (i = 0; i < rtd->num_codecs; i++)
snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
substream->stream);
+ snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
out:
mutex_unlock(&rtd->pcm_mutex);
@@ -1522,13 +1510,36 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
}
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/* Set FE's runtime_update state; the state is protected via PCM stream lock
+ * for avoiding the race with trigger callback.
+ * If the state is unset and a trigger is pending while the previous operation,
+ * process the pending trigger action here.
+ */
+static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
+ int stream, enum snd_soc_dpcm_update state)
+{
+ struct snd_pcm_substream *substream =
+ snd_soc_dpcm_get_substream(fe, stream);
+
+ snd_pcm_stream_lock_irq(substream);
+ if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
+ dpcm_fe_dai_do_trigger(substream,
+ fe->dpcm[stream].trigger_pending - 1);
+ fe->dpcm[stream].trigger_pending = 0;
+ }
+ fe->dpcm[stream].runtime_update = state;
+ snd_pcm_stream_unlock_irq(substream);
+}
+
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
struct snd_pcm_runtime *runtime = fe_substream->runtime;
int stream = fe_substream->stream, ret = 0;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
ret = dpcm_be_dai_startup(fe, fe_substream->stream);
if (ret < 0) {
@@ -1550,13 +1561,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
dpcm_set_fe_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
unwind:
dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
be_err:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -1603,7 +1614,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* shutdown the BEs */
dpcm_be_dai_shutdown(fe, substream->stream);
@@ -1617,7 +1628,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
}
@@ -1641,6 +1652,10 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
continue;
+ /* do not free hw if this BE is used by other FE */
+ if (be->dpcm[stream].users > 1)
+ continue;
+
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
@@ -1665,7 +1680,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
int err, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
@@ -1680,7 +1695,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
err = dpcm_be_dai_hw_free(fe, stream);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return 0;
@@ -1773,7 +1788,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
int ret, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
memcpy(&fe->dpcm[substream->stream].hw_params, params,
sizeof(struct snd_pcm_hw_params));
@@ -1796,7 +1811,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
}
@@ -1910,7 +1925,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
-static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream, ret;
@@ -1984,6 +1999,23 @@ out:
return ret;
}
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream;
+
+ /* if FE's runtime_update is already set, we're in race;
+ * process this trigger later at exit
+ */
+ if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
+ fe->dpcm[stream].trigger_pending = cmd + 1;
+ return 0; /* delayed, assuming it's successful */
+ }
+
+ /* we're alone, let's trigger */
+ return dpcm_fe_dai_do_trigger(substream, cmd);
+}
+
int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
@@ -2027,7 +2059,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* there is no point preparing this FE if there are no BEs */
if (list_empty(&fe->dpcm[stream].be_clients)) {
@@ -2054,7 +2086,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
@@ -2201,11 +2233,11 @@ static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_startup(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -2214,11 +2246,11 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_shutdown(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -2248,7 +2280,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
fe->dai_link->name);
/* skip if FE doesn't have playback capability */
- if (!fe->cpu_dai->driver->playback.channels_min)
+ if (!fe->cpu_dai->driver->playback.channels_min
+ || !fe->codec_dai->driver->playback.channels_min)
+ goto capture;
+
+ /* skip if FE isn't currently playing */
+ if (!fe->cpu_dai->playback_active
+ || !fe->codec_dai->playback_active)
goto capture;
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
@@ -2278,7 +2316,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
dpcm_path_put(&list);
capture:
/* skip if FE doesn't have capture capability */
- if (!fe->cpu_dai->driver->capture.channels_min)
+ if (!fe->cpu_dai->driver->capture.channels_min
+ || !fe->codec_dai->driver->capture.channels_min)
+ continue;
+
+ /* skip if FE isn't currently capturing */
+ if (!fe->cpu_dai->capture_active
+ || !fe->codec_dai->capture_active)
continue;
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);