diff options
Diffstat (limited to 'sound/soc/soc-pcm.c')
-rw-r--r-- | sound/soc/soc-pcm.c | 527 |
1 files changed, 233 insertions, 294 deletions
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 14d85ca1e435..8659089a87a0 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -29,6 +29,15 @@ #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) { @@ -156,9 +165,6 @@ static const struct file_operations dpcm_state_fops = { void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) { - if (!rtd->dai_link) - return; - if (!rtd->dai_link->dynamic) return; @@ -291,15 +297,8 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) 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); @@ -349,6 +348,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, 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)) { \ @@ -382,18 +384,20 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, 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; \ } @@ -405,7 +409,7 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, 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; @@ -422,7 +426,8 @@ static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) 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) @@ -683,6 +688,44 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) 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 @@ -691,11 +734,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) 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) @@ -734,59 +774,30 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) /* 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; } @@ -823,10 +834,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) 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 && @@ -843,6 +852,10 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) out: mutex_unlock(&rtd->card->pcm_mutex); + + if (ret < 0) + dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret); + return ret; } @@ -992,8 +1005,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, 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; } @@ -1275,8 +1290,12 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, 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; } @@ -1410,28 +1429,43 @@ void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) 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; @@ -1441,15 +1475,16 @@ static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, 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", @@ -1462,10 +1497,12 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) 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; @@ -1480,7 +1517,6 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) 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", @@ -1498,51 +1534,48 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) 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; @@ -1576,10 +1609,10 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, } } -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; @@ -1624,10 +1657,10 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, } } -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; @@ -1661,34 +1694,6 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, } } -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) { @@ -1699,16 +1704,13 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, 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 */ @@ -1718,7 +1720,6 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, 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) @@ -1728,20 +1729,20 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_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) @@ -1752,75 +1753,36 @@ 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) @@ -1846,7 +1808,7 @@ 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; @@ -1885,14 +1847,12 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) 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); @@ -1900,14 +1860,11 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) 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); @@ -1918,14 +1875,14 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) 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)) @@ -1957,22 +1914,21 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int 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; @@ -2005,10 +1961,8 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, 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), @@ -2016,29 +1970,33 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, /* 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)) @@ -2056,7 +2014,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ret = soc_pcm_trigger(be_substream, cmd); if (ret) - return ret; + goto end; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; @@ -2066,7 +2024,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ret = soc_pcm_trigger(be_substream, cmd); if (ret) - return ret; + goto end; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; @@ -2076,7 +2034,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ret = soc_pcm_trigger(be_substream, cmd); if (ret) - return ret; + goto end; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; @@ -2090,7 +2048,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ret = soc_pcm_trigger(be_substream, cmd); if (ret) - return ret; + goto end; be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; @@ -2103,7 +2061,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ret = soc_pcm_trigger(be_substream, cmd); if (ret) - return ret; + goto end; be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; break; @@ -2116,13 +2074,16 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, 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); @@ -2288,14 +2249,15 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) 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; } @@ -2324,11 +2286,8 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) /* 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; @@ -2336,6 +2295,9 @@ 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; } @@ -2355,29 +2317,24 @@ static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 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) @@ -2395,7 +2352,6 @@ 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; @@ -2419,7 +2375,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) 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; @@ -2438,20 +2393,16 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) 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; @@ -2476,6 +2427,9 @@ disconnect: } spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); + if (ret < 0) + dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret); + return ret; } @@ -2484,7 +2438,6 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) struct snd_soc_dapm_widget_list *list; int stream; int count, paths; - int ret; if (!fe->dai_link->dynamic) return 0; @@ -2516,24 +2469,17 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) 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); @@ -2615,12 +2561,8 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) 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); @@ -2751,8 +2693,7 @@ static int soc_create_pcm(struct snd_pcm **pcm, 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); @@ -2833,6 +2774,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 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) @@ -2842,17 +2785,13 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 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; } |