#define DPCM_MAX_BE_USERS 8
+static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd)
+{
+ return (rtd)->num_cpus == 1 ? asoc_rtd_to_cpu(rtd, 0)->name : "multicpu";
+}
+static inline const char *soc_codec_dai_name(struct snd_soc_pcm_runtime *rtd)
+{
+ return (rtd)->num_codecs == 1 ? asoc_rtd_to_codec(rtd, 0)->name : "multicodec";
+}
+
#ifdef CONFIG_DEBUG_FS
static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
{
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
{
- if (!rtd->dai_link)
- return;
-
if (!rtd->dai_link->dynamic)
return;
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.info = hw->info;
- runtime->hw.formats = hw->formats;
- runtime->hw.period_bytes_min = hw->period_bytes_min;
- runtime->hw.period_bytes_max = hw->period_bytes_max;
- runtime->hw.periods_min = hw->periods_min;
- runtime->hw.periods_max = hw->periods_max;
- runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
- runtime->hw.fifo_size = hw->fifo_size;
+ substream->runtime->hw = *hw;
+
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
int ret;
+ if (!snd_soc_dai_active(soc_dai))
+ return 0;
+
#define __soc_pcm_apply_symmetry(name, NAME) \
if (soc_dai->name && (soc_dai->driver->symmetric_##name || \
rtd->dai_link->symmetric_##name)) { \
struct snd_soc_dai *cpu_dai;
unsigned int symmetry, i;
+ d.name = __func__;
soc_pcm_set_dai_params(&d, params);
-#define __soc_pcm_params_symmetry(name) \
- symmetry = rtd->dai_link->symmetric_##name; \
+#define __soc_pcm_params_symmetry(xxx) \
+ symmetry = rtd->dai_link->symmetric_##xxx; \
for_each_rtd_dais(rtd, i, dai) \
- symmetry |= dai->driver->symmetric_##name; \
+ symmetry |= dai->driver->symmetric_##xxx; \
\
if (symmetry) \
for_each_rtd_cpu_dais(rtd, i, cpu_dai) \
- if (cpu_dai->name && cpu_dai->name != d.name) { \
- dev_err(rtd->dev, "ASoC: unmatched %s symmetry: %d - %d\n", \
- #name, cpu_dai->name, d.name); \
+ if (!snd_soc_dai_is_dummy(cpu_dai) && \
+ cpu_dai->xxx && cpu_dai->xxx != d.xxx) { \
+ dev_err(rtd->dev, "ASoC: unmatched %s symmetry: %s:%d - %s:%d\n", \
+ #xxx, cpu_dai->name, cpu_dai->xxx, d.name, d.xxx); \
return -EINVAL; \
}
return 0;
}
-static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
+static void soc_pcm_update_symmetry(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai_link *link = rtd->dai_link;
dai->driver->symmetric_channels ||
dai->driver->symmetric_sample_bits;
- return symmetry;
+ if (symmetry)
+ substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
}
static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
return soc_pcm_clean(substream, 0);
}
+static int soc_hw_sanity_check(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_pcm_hardware *hw = &substream->runtime->hw;
+ const char *name_cpu = soc_cpu_dai_name(rtd);
+ const char *name_codec = soc_codec_dai_name(rtd);
+ const char *err_msg;
+ struct device *dev = rtd->dev;
+
+ err_msg = "rates";
+ if (!hw->rates)
+ goto config_err;
+
+ err_msg = "formats";
+ if (!hw->formats)
+ goto config_err;
+
+ err_msg = "channels";
+ if (!hw->channels_min || !hw->channels_max ||
+ hw->channels_min > hw->channels_max)
+ goto config_err;
+
+ dev_dbg(dev, "ASoC: %s <-> %s info:\n", name_codec,
+ name_cpu);
+ dev_dbg(dev, "ASoC: rate mask 0x%x\n", hw->rates);
+ dev_dbg(dev, "ASoC: ch min %d max %d\n", hw->channels_min,
+ hw->channels_max);
+ dev_dbg(dev, "ASoC: rate min %d max %d\n", hw->rate_min,
+ hw->rate_max);
+
+ return 0;
+
+config_err:
+ dev_err(dev, "ASoC: %s <-> %s No matching %s\n",
+ name_codec, name_cpu, err_msg);
+ return -EINVAL;
+}
+
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
static int soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_component *component;
struct snd_soc_dai *dai;
- const char *codec_dai_name = "multicodec";
- const char *cpu_dai_name = "multicpu";
int i, ret = 0;
for_each_rtd_components(rtd, i, component)
/* Check that the codec and cpu DAIs are compatible */
soc_pcm_init_runtime_hw(substream);
- if (rtd->num_codecs == 1)
- codec_dai_name = asoc_rtd_to_codec(rtd, 0)->name;
+ soc_pcm_update_symmetry(substream);
- if (rtd->num_cpus == 1)
- cpu_dai_name = asoc_rtd_to_cpu(rtd, 0)->name;
-
- if (soc_pcm_has_symmetry(substream))
- runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
-
- ret = -EINVAL;
- if (!runtime->hw.rates) {
- printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
- codec_dai_name, cpu_dai_name);
- goto err;
- }
- if (!runtime->hw.formats) {
- printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
- codec_dai_name, cpu_dai_name);
- goto err;
- }
- if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
- runtime->hw.channels_min > runtime->hw.channels_max) {
- printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
- codec_dai_name, cpu_dai_name);
+ ret = soc_hw_sanity_check(substream);
+ if (ret < 0)
goto err;
- }
soc_pcm_apply_msb(substream);
/* Symmetry only applies if we've already got an active stream. */
for_each_rtd_dais(rtd, i, dai) {
- if (snd_soc_dai_active(dai)) {
- ret = soc_pcm_apply_symmetry(substream, dai);
- if (ret != 0)
- goto err;
- }
+ ret = soc_pcm_apply_symmetry(substream, dai);
+ if (ret != 0)
+ goto err;
}
-
- pr_debug("ASoC: %s <-> %s info:\n",
- codec_dai_name, cpu_dai_name);
- pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
- pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
- runtime->hw.channels_max);
- pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min,
- runtime->hw.rate_max);
dynamic:
snd_soc_runtime_activate(rtd, substream->stream);
ret = 0;
err:
mutex_unlock(&rtd->card->pcm_mutex);
pm_err:
- if (ret < 0)
+ if (ret < 0) {
soc_pcm_clean(substream, 1);
+ dev_err(rtd->dev, "%s() failed (%d)", __func__, ret);
+ }
return ret;
}
goto out;
ret = snd_soc_pcm_dai_prepare(substream);
- if (ret < 0) {
- dev_err(rtd->dev, "ASoC: DAI prepare error: %d\n", ret);
+ if (ret < 0)
goto out;
- }
/* cancel any delayed stream shutdown that is pending */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
out:
mutex_unlock(&rtd->card->pcm_mutex);
+
+ if (ret < 0)
+ dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+
return ret;
}
out:
mutex_unlock(&rtd->card->pcm_mutex);
- if (ret < 0)
+ if (ret < 0) {
soc_pcm_hw_clean(substream, 1);
+ dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+ }
return ret;
}
fe->card->component_chaining ?
NULL : dpcm_end_walk_at_be);
- dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
+ if (paths > 0)
+ dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
stream ? "capture" : "playback");
+ else if (paths == 0)
+ dev_dbg(fe->dev, "ASoC: %s no valid %s path\n", fe->dai_link->name,
+ stream ? "capture" : "playback");
return paths;
}
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
}
-static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
- int stream)
+void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
+ int do_hw_free, struct snd_soc_dpcm *last)
{
struct snd_soc_dpcm *dpcm;
/* disable any enabled and non active backends */
for_each_dpcm_be(fe, stream, dpcm) {
-
struct snd_soc_pcm_runtime *be = dpcm->be;
struct snd_pcm_substream *be_substream =
snd_soc_dpcm_get_substream(be, stream);
- if (be->dpcm[stream].users == 0)
+ if (dpcm == last)
+ return;
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ if (be->dpcm[stream].users == 0) {
dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
stream ? "capture" : "playback",
be->dpcm[stream].state);
+ continue;
+ }
if (--be->dpcm[stream].users != 0)
continue;
- if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
- continue;
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) {
+ if (!do_hw_free)
+ continue;
+
+ if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) {
+ soc_pcm_hw_free(be_substream);
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+ }
+ }
soc_pcm_close(be_substream);
be_substream->runtime = NULL;
int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
{
+ struct snd_soc_pcm_runtime *be;
struct snd_soc_dpcm *dpcm;
int err, count = 0;
/* only startup BE DAIs that are either sinks or sources to this FE DAI */
for_each_dpcm_be(fe, stream, dpcm) {
+ struct snd_pcm_substream *be_substream;
- struct snd_soc_pcm_runtime *be = dpcm->be;
- struct snd_pcm_substream *be_substream =
- snd_soc_dpcm_get_substream(be, stream);
+ be = dpcm->be;
+ be_substream = snd_soc_dpcm_get_substream(be, stream);
if (!be_substream) {
dev_err(be->dev, "ASoC: no backend %s stream\n",
continue;
/* first time the dpcm is open ? */
- if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
+ if (be->dpcm[stream].users == DPCM_MAX_BE_USERS) {
dev_err(be->dev, "ASoC: too many users %s at open %d\n",
stream ? "capture" : "playback",
be->dpcm[stream].state);
+ continue;
+ }
if (be->dpcm[stream].users++ != 0)
continue;
be_substream->runtime = be->dpcm[stream].runtime;
err = soc_pcm_open(be_substream);
if (err < 0) {
- dev_err(be->dev, "ASoC: BE open failed %d\n", err);
be->dpcm[stream].users--;
if (be->dpcm[stream].users < 0)
dev_err(be->dev, "ASoC: no users %s at unwind %d\n",
return count;
unwind:
- /* disable any enabled and non active backends */
- for_each_dpcm_be_rollback(fe, stream, dpcm) {
- struct snd_soc_pcm_runtime *be = dpcm->be;
- struct snd_pcm_substream *be_substream =
- snd_soc_dpcm_get_substream(be, stream);
-
- if (!snd_soc_dpcm_be_can_update(fe, be, stream))
- continue;
-
- if (be->dpcm[stream].users == 0)
- dev_err(be->dev, "ASoC: no users %s at close %d\n",
- stream ? "capture" : "playback",
- be->dpcm[stream].state);
+ dpcm_be_dai_startup_rollback(fe, stream, dpcm);
- if (--be->dpcm[stream].users != 0)
- continue;
-
- if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
- continue;
-
- soc_pcm_close(be_substream);
- be_substream->runtime = NULL;
- be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
- }
+ dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n",
+ __func__, be->dai_link->name, err);
return err;
}
-static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
- struct snd_soc_pcm_stream *stream)
+static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
+ struct snd_soc_dai *dai;
+ int stream = substream->stream;
+ int i;
+
+ soc_pcm_hw_init(hw);
+
+ for_each_rtd_cpu_dais(fe, i, dai) {
+ struct snd_soc_pcm_stream *cpu_stream;
+
+ /*
+ * Skip CPUs which don't support the current stream
+ * type. See soc_pcm_init_runtime_hw() for more details
+ */
+ if (!snd_soc_dai_stream_valid(dai, stream))
+ continue;
+
+ cpu_stream = snd_soc_dai_get_pcm_stream(dai, stream);
+
+ soc_pcm_hw_update_rate(hw, cpu_stream);
+ soc_pcm_hw_update_chan(hw, cpu_stream);
+ soc_pcm_hw_update_format(hw, cpu_stream);
+ }
- soc_pcm_hw_update_rate(hw, stream);
- soc_pcm_hw_update_chan(hw, stream);
- if (runtime->hw.formats)
- runtime->hw.formats &= stream->formats;
- else
- runtime->hw.formats = stream->formats;
}
-static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
struct snd_soc_dpcm *dpcm;
struct snd_soc_dai *dai;
}
}
-static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
struct snd_soc_dpcm *dpcm;
int stream = substream->stream;
}
}
-static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static void dpcm_runtime_setup_be_rate(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
struct snd_soc_dpcm *dpcm;
int stream = substream->stream;
}
}
-static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_pcm_hardware *hw = &runtime->hw;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai;
- int i;
-
- soc_pcm_hw_init(hw);
-
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- /*
- * Skip CPUs which don't support the current stream
- * type. See soc_pcm_init_runtime_hw() for more details
- */
- if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream))
- continue;
-
- dpcm_init_runtime_hw(runtime,
- snd_soc_dai_get_pcm_stream(cpu_dai,
- substream->stream));
- }
-
- dpcm_runtime_merge_format(substream, runtime);
- dpcm_runtime_merge_chan(substream, runtime);
- dpcm_runtime_merge_rate(substream, runtime);
-}
-
static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
int stream)
{
int i;
/* apply symmetry for FE */
- if (soc_pcm_has_symmetry(fe_substream))
- fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ soc_pcm_update_symmetry(fe_substream);
for_each_rtd_cpu_dais (fe, i, fe_cpu_dai) {
/* Symmetry only applies if we've got an active stream. */
- if (snd_soc_dai_active(fe_cpu_dai)) {
- err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
- if (err < 0)
- return err;
- }
+ err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
+ if (err < 0)
+ goto error;
}
/* apply symmetry for BE */
snd_soc_dpcm_get_substream(be, stream);
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *dai;
- int i;
/* A backend may not have the requested substream */
if (!be_substream)
if (rtd->dai_link->be_hw_params_fixup)
continue;
- if (soc_pcm_has_symmetry(be_substream))
- be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ soc_pcm_update_symmetry(be_substream);
/* Symmetry only applies if we've got an active stream. */
for_each_rtd_dais(rtd, i, dai) {
- if (snd_soc_dai_active(dai)) {
- err = soc_pcm_apply_symmetry(fe_substream, dai);
- if (err < 0)
- return err;
- }
+ err = soc_pcm_apply_symmetry(fe_substream, dai);
+ if (err < 0)
+ goto error;
}
}
+error:
+ if (err < 0)
+ dev_err(fe->dev, "ASoC: %s failed (%d)\n", __func__, err);
- return 0;
+ return err;
}
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
ret = dpcm_be_dai_startup(fe, stream);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret);
+ if (ret < 0)
goto be_err;
- }
dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name);
/* start the DAI frontend */
ret = soc_pcm_open(fe_substream);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret);
+ if (ret < 0)
goto unwind;
- }
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
- dpcm_set_fe_runtime(fe_substream);
+ dpcm_runtime_setup_fe(fe_substream);
+
+ dpcm_runtime_setup_be_format(fe_substream);
+ dpcm_runtime_setup_be_chan(fe_substream);
+ dpcm_runtime_setup_be_rate(fe_substream);
ret = dpcm_apply_symmetry(fe_substream, stream);
- if (ret < 0)
- dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
- ret);
unwind:
if (ret < 0)
dpcm_be_dai_startup_unwind(fe, stream);
be_err:
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
- return ret;
-}
-
-int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
-{
- struct snd_soc_dpcm *dpcm;
-
- /* only shutdown BEs that are either sinks or sources to this FE DAI */
- for_each_dpcm_be(fe, stream, dpcm) {
-
- struct snd_soc_pcm_runtime *be = dpcm->be;
- struct snd_pcm_substream *be_substream =
- snd_soc_dpcm_get_substream(be, stream);
-
- /* is this op for this BE ? */
- if (!snd_soc_dpcm_be_can_update(fe, be, stream))
- continue;
-
- if (be->dpcm[stream].users == 0)
- dev_err(be->dev, "ASoC: no users %s at close - state %d\n",
- stream ? "capture" : "playback",
- be->dpcm[stream].state);
-
- if (--be->dpcm[stream].users != 0)
- continue;
-
- if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) {
- soc_pcm_hw_free(be_substream);
- be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
- }
- dev_dbg(be->dev, "ASoC: close BE %s\n",
- be->dai_link->name);
-
- soc_pcm_close(be_substream);
- be_substream->runtime = NULL;
+ if (ret < 0)
+ dev_err(fe->dev, "%s() failed (%d)\n", __func__, ret);
- be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
- }
- return 0;
+ return ret;
}
static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
return 0;
}
-int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
+void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
}
-
- return 0;
}
static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
- int err, stream = substream->stream;
+ int stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
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);
/* call hw_free on the frontend */
- err = soc_pcm_hw_free(substream);
- if (err < 0)
- dev_err(fe->dev,"ASoC: hw_free FE %s failed\n",
- fe->dai_link->name);
+ soc_pcm_hw_free(substream);
/* only hw_params backends that are either sinks or sources
* to this frontend DAI */
- err = dpcm_be_dai_hw_free(fe, stream);
+ dpcm_be_dai_hw_free(fe, stream);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
{
+ struct snd_soc_pcm_runtime *be;
+ struct snd_pcm_substream *be_substream;
struct snd_soc_dpcm *dpcm;
int ret;
for_each_dpcm_be(fe, stream, dpcm) {
-
- struct snd_soc_pcm_runtime *be = dpcm->be;
- struct snd_pcm_substream *be_substream =
- snd_soc_dpcm_get_substream(be, stream);
+ be = dpcm->be;
+ be_substream = snd_soc_dpcm_get_substream(be, stream);
/* is this op for this BE ? */
if (!snd_soc_dpcm_be_can_update(fe, be, stream))
be->dai_link->name);
ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
- if (ret < 0) {
- dev_err(dpcm->be->dev,
- "ASoC: hw_params BE failed %d\n", ret);
+ if (ret < 0)
goto unwind;
- }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
}
return 0;
unwind:
+ dev_dbg(fe->dev, "ASoC: %s() failed at %s (%d)\n",
+ __func__, be->dai_link->name, ret);
+
/* disable any enabled and non active backends */
for_each_dpcm_be_rollback(fe, stream, dpcm) {
- struct snd_soc_pcm_runtime *be = dpcm->be;
- struct snd_pcm_substream *be_substream =
- snd_soc_dpcm_get_substream(be, stream);
+ be = dpcm->be;
+ be_substream = snd_soc_dpcm_get_substream(be, stream);
if (!snd_soc_dpcm_be_can_update(fe, be, stream))
continue;
memcpy(&fe->dpcm[stream].hw_params, params,
sizeof(struct snd_pcm_hw_params));
ret = dpcm_be_dai_hw_params(fe, stream);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret);
+ if (ret < 0)
goto out;
- }
dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n",
fe->dai_link->name, params_rate(params),
/* call hw_params on the frontend */
ret = soc_pcm_hw_params(substream, params);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret);
+ if (ret < 0)
dpcm_be_dai_hw_free(fe, stream);
- } else
+ else
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
out:
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
+
+ if (ret < 0)
+ dev_err(fe->dev, "ASoC: %s failed (%d)\n", __func__, ret);
+
return ret;
}
int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
int cmd)
{
+ struct snd_soc_pcm_runtime *be;
struct snd_soc_dpcm *dpcm;
int ret = 0;
for_each_dpcm_be(fe, stream, dpcm) {
+ struct snd_pcm_substream *be_substream;
- struct snd_soc_pcm_runtime *be = dpcm->be;
- struct snd_pcm_substream *be_substream =
- snd_soc_dpcm_get_substream(be, stream);
+ be = dpcm->be;
+ be_substream = snd_soc_dpcm_get_substream(be, stream);
/* is this op for this BE ? */
if (!snd_soc_dpcm_be_can_update(fe, be, stream))
ret = soc_pcm_trigger(be_substream, cmd);
if (ret)
- return ret;
+ goto end;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break;
ret = soc_pcm_trigger(be_substream, cmd);
if (ret)
- return ret;
+ goto end;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break;
ret = soc_pcm_trigger(be_substream, cmd);
if (ret)
- return ret;
+ goto end;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break;
ret = soc_pcm_trigger(be_substream, cmd);
if (ret)
- return ret;
+ goto end;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
break;
ret = soc_pcm_trigger(be_substream, cmd);
if (ret)
- return ret;
+ goto end;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
break;
ret = soc_pcm_trigger(be_substream, cmd);
if (ret)
- return ret;
+ goto end;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
break;
}
}
-
+end:
+ if (ret < 0)
+ dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n",
+ __func__, be->dai_link->name, ret);
return ret;
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
be->dai_link->name);
ret = soc_pcm_prepare(be_substream);
- if (ret < 0) {
- dev_err(be->dev, "ASoC: backend prepare failed %d\n",
- ret);
+ if (ret < 0)
break;
- }
be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
}
+
+ if (ret < 0)
+ dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+
return ret;
}
/* call prepare on the frontend */
ret = soc_pcm_prepare(substream);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
- fe->dai_link->name);
+ if (ret < 0)
goto out;
- }
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
+ if (ret < 0)
+ dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+
return ret;
}
fe->dai_link->name);
err = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP);
- if (err < 0)
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
} else {
dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n",
fe->dai_link->name);
err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
- if (err < 0)
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err);
}
- err = dpcm_be_dai_hw_free(fe, stream);
- if (err < 0)
- dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err);
+ dpcm_be_dai_hw_free(fe, stream);
- err = dpcm_be_dai_shutdown(fe, stream);
- if (err < 0)
- dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err);
+ dpcm_be_dai_shutdown(fe, stream);
/* run the stream event for each BE */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
- return 0;
+ if (err < 0)
+ dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, err);
+
+ return err;
}
static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
/* Only start the BE if the FE is ready */
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) {
- ret = -EINVAL;
dev_err(fe->dev, "ASoC: FE %s is not ready %d\n",
fe->dai_link->name, fe->dpcm[stream].state);
ret = -EINVAL;
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
return 0;
-
ret = dpcm_be_dai_prepare(fe, stream);
if (ret < 0)
goto hw_free;
fe->dai_link->name);
ret = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret);
+ if (ret < 0)
goto hw_free;
- }
} else {
dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n",
fe->dai_link->name);
ret = dpcm_be_dai_trigger(fe, stream,
SNDRV_PCM_TRIGGER_START);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
+ if (ret < 0)
goto hw_free;
- }
}
return 0;
}
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+ if (ret < 0)
+ dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+
return ret;
}
struct snd_soc_dapm_widget_list *list;
int stream;
int count, paths;
- int ret;
if (!fe->dai_link->dynamic)
return 0;
continue;
paths = dpcm_path_get(fe, stream, &list);
- if (paths < 0) {
- dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
- fe->dai_link->name,
- stream == SNDRV_PCM_STREAM_PLAYBACK ?
- "playback" : "capture");
+ if (paths < 0)
return paths;
- }
/* update any playback/capture paths */
count = dpcm_process_paths(fe, stream, &list, new);
if (count) {
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
if (new)
- ret = dpcm_run_update_startup(fe, stream);
+ dpcm_run_update_startup(fe, stream);
else
- ret = dpcm_run_update_shutdown(fe, stream);
- if (ret < 0)
- dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
+ dpcm_run_update_shutdown(fe, stream);
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
dpcm_clear_pending_state(fe, stream);
fe->dpcm[stream].runtime = fe_substream->runtime;
ret = dpcm_path_get(fe, stream, &list);
- if (ret < 0) {
+ if (ret < 0)
goto open_end;
- } else if (ret == 0) {
- dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
- fe->dai_link->name, stream ? "capture" : "playback");
- }
/* calculate valid and active FE <-> BE dpcms */
dpcm_process_paths(fe, stream, &list, 1);
else
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name,
- (rtd->num_codecs > 1) ?
- "multicodec" : asoc_rtd_to_codec(rtd, 0)->name, num);
+ soc_codec_dai_name(rtd), num);
ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
capture, pcm);
rtd->ops.page = snd_soc_pcm_component_page;
if (drv->mmap)
rtd->ops.mmap = snd_soc_pcm_component_mmap;
+ if (drv->ack)
+ rtd->ops.ack = snd_soc_pcm_component_ack;
}
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
ret = snd_soc_pcm_component_new(rtd);
- if (ret < 0) {
- dev_err(rtd->dev, "ASoC: pcm constructor failed for dailink %s: %d\n",
- rtd->dai_link->name, ret);
+ if (ret < 0)
return ret;
- }
pcm->no_device_suspend = true;
out:
dev_dbg(rtd->card->dev, "%s <-> %s mapping ok\n",
- (rtd->num_codecs > 1) ? "multicodec" : asoc_rtd_to_codec(rtd, 0)->name,
- (rtd->num_cpus > 1) ? "multicpu" : asoc_rtd_to_cpu(rtd, 0)->name);
+ soc_codec_dai_name(rtd), soc_cpu_dai_name(rtd));
return ret;
}