diff options
Diffstat (limited to 'sound/soc/soc-pcm.c')
| -rw-r--r-- | sound/soc/soc-pcm.c | 1113 |
1 files changed, 537 insertions, 576 deletions
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 579a44d81d9a..6b134962c71c 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -14,7 +14,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pinctrl/consumer.h> -#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/export.h> @@ -31,67 +30,120 @@ static inline int _soc_pcm_ret(struct snd_soc_pcm_runtime *rtd, const char *func, int ret) { - /* Positive, Zero values are not errors */ - if (ret >= 0) - return ret; - - /* Negative values might be errors */ - switch (ret) { - case -EPROBE_DEFER: - case -ENOTSUPP: - break; - default: - dev_err(rtd->dev, - "ASoC: error at %s on %s: %d\n", - func, rtd->dai_link->name, ret); - } - - return ret; + return snd_soc_ret(rtd->dev, ret, + "at %s() on %s\n", func, rtd->dai_link->name); } -static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd) +/* is the current PCM operation for this FE ? */ +#if 0 +static int snd_soc_dpcm_can_fe_update(struct snd_soc_pcm_runtime *fe, int stream) { - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) + return 1; + return 0; } +#endif -static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd) +/* is the current PCM operation for this BE ? */ +static int snd_soc_dpcm_can_be_update(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) { - mutex_unlock(&rtd->card->pcm_mutex); + if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || + ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) && + be->dpcm[stream].runtime_update)) + return 1; + return 0; } -#define snd_soc_dpcm_mutex_assert_held(rtd) \ - lockdep_assert_held(&(rtd)->card->pcm_mutex) - -static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd, - int stream) +static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, + int stream, + const enum snd_soc_dpcm_state *states, + int num_states) { - snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream)); + struct snd_soc_dpcm *dpcm; + int state; + int ret = 1; + int i; + + for_each_dpcm_fe(be, stream, dpcm) { + + if (dpcm->fe == fe) + continue; + + state = dpcm->fe->dpcm[stream].state; + for (i = 0; i < num_states; i++) { + if (state == states[i]) { + ret = 0; + break; + } + } + } + + /* it's safe to do this BE DAI */ + return ret; } -#define snd_soc_dpcm_stream_lock_irqsave_nested(rtd, stream, flags) \ - snd_pcm_stream_lock_irqsave_nested(snd_soc_dpcm_get_substream(rtd, stream), flags) +/* + * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE + * are not running, paused or suspended for the specified stream direction. + */ +static int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + const enum snd_soc_dpcm_state state[] = { + SND_SOC_DPCM_STATE_START, + SND_SOC_DPCM_STATE_PAUSED, + SND_SOC_DPCM_STATE_SUSPEND, + }; + + return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); +} -static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd, - int stream) +/* + * We can only change hw params a BE DAI if any of it's FE are not prepared, + * running, paused or suspended for the specified stream direction. + */ +static int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) { - snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream)); + const enum snd_soc_dpcm_state state[] = { + SND_SOC_DPCM_STATE_START, + SND_SOC_DPCM_STATE_PAUSED, + SND_SOC_DPCM_STATE_SUSPEND, + SND_SOC_DPCM_STATE_PREPARE, + }; + + return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); } -#define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \ - snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags) +/* + * We can only prepare a BE DAI if any of it's FE are not prepared, + * running or paused for the specified stream direction. + */ +static int snd_soc_dpcm_can_be_prepared(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + const enum snd_soc_dpcm_state state[] = { + SND_SOC_DPCM_STATE_START, + SND_SOC_DPCM_STATE_PAUSED, + SND_SOC_DPCM_STATE_PREPARE, + }; + + return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); +} #define DPCM_MAX_BE_USERS 8 static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd) { - return (rtd)->dai_link->num_cpus == 1 ? asoc_rtd_to_cpu(rtd, 0)->name : "multicpu"; + return (rtd)->dai_link->num_cpus == 1 ? snd_soc_rtd_to_cpu(rtd, 0)->name : "multicpu"; } static inline const char *soc_codec_dai_name(struct snd_soc_pcm_runtime *rtd) { - return (rtd)->dai_link->num_codecs == 1 ? asoc_rtd_to_codec(rtd, 0)->name : "multicodec"; + return (rtd)->dai_link->num_codecs == 1 ? snd_soc_rtd_to_codec(rtd, 0)->name : "multicodec"; } -#ifdef CONFIG_DEBUG_FS static const char *dpcm_state_string(enum snd_soc_dpcm_state state) { switch (state) { @@ -120,6 +172,7 @@ static const char *dpcm_state_string(enum snd_soc_dpcm_state state) return "unknown"; } +#ifdef CONFIG_DEBUG_FS static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, int stream, char *buf, size_t size) { @@ -185,11 +238,9 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, int stream; char *buf; - if (fe->dai_link->num_cpus > 1) { - dev_err(fe->dev, + if (fe->dai_link->num_cpus > 1) + return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__); - return -EINVAL; - } buf = kmalloc(out_count, GFP_KERNEL); if (!buf) @@ -197,7 +248,7 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, snd_soc_dpcm_mutex_lock(fe); for_each_pcm_streams(stream) - if (snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream)) + if (snd_soc_dai_stream_valid(snd_soc_rtd_to_cpu(fe, 0), stream)) offset += dpcm_show_state(fe, stream, buf + offset, out_count - offset); @@ -235,7 +286,7 @@ static void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, int stream) char *name; name = kasprintf(GFP_KERNEL, "%s:%s", dpcm->be->dai_link->name, - stream ? "capture" : "playback"); + snd_pcm_direction_name(stream)); if (name) { dpcm->debugfs_state = debugfs_create_dir( name, dpcm->fe->debugfs_dpcm_root); @@ -273,14 +324,14 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); - snd_soc_dpcm_stream_lock_irq(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_soc_dpcm_stream_unlock_irq(fe, stream); + snd_pcm_stream_unlock_irq(substream); } static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be, @@ -305,6 +356,7 @@ static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be, void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, int stream, int action) { + struct snd_soc_component *component; struct snd_soc_dai *dai; int i; @@ -312,6 +364,13 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, for_each_rtd_dais(rtd, i, dai) snd_soc_dai_action(dai, stream, action); + + /* Increments/Decrements the active count for components without DAIs */ + for_each_rtd_components(rtd, i, component) { + if (component->num_dai) + continue; + component->active += action; + } } EXPORT_SYMBOL_GPL(snd_soc_runtime_action); @@ -320,44 +379,28 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_action); * @rtd: The ASoC PCM runtime that should be checked. * * This function checks whether the power down delay should be ignored for a - * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has + * specific PCM runtime. Returns true if the delay is 0, if the DAI link has * been configured to ignore the delay, or if none of the components benefits * from having the delay. */ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component; - bool ignore = true; int i; if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) return true; for_each_rtd_components(rtd, i, component) - ignore &= !component->driver->use_pmdown_time; + if (component->driver->use_pmdown_time) + /* No need to go through all components */ + return false; - return ignore; + return true; } -/** - * snd_soc_set_runtime_hwparams - set the runtime hardware parameters - * @substream: the pcm substream - * @hw: the hardware parameters - * - * Sets the substream runtime hardware parameters. - */ -int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, - const struct snd_pcm_hardware *hw) -{ - substream->runtime->hw = *hw; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); - /* DPCM stream event, send event to FE and all active BEs. */ -int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, - int event) +void dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event) { struct snd_soc_dpcm *dpcm; @@ -378,48 +421,43 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, } snd_soc_dapm_stream_event(fe, dir, event); - - return 0; } static void soc_pcm_set_dai_params(struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { if (params) { - dai->rate = params_rate(params); - dai->channels = params_channels(params); - dai->sample_bits = snd_pcm_format_physical_width(params_format(params)); + dai->symmetric_rate = params_rate(params); + dai->symmetric_channels = params_channels(params); + dai->symmetric_sample_bits = snd_pcm_format_physical_width(params_format(params)); } else { - dai->rate = 0; - dai->channels = 0; - dai->sample_bits = 0; + dai->symmetric_rate = 0; + dai->symmetric_channels = 0; + dai->symmetric_sample_bits = 0; } } static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, struct snd_soc_dai *soc_dai) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_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)) { \ + if (soc_dai->symmetric_##name && \ + (soc_dai->driver->symmetric_##name || rtd->dai_link->symmetric_##name)) { \ dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %s to %d\n",\ - #name, soc_dai->name); \ + #name, soc_dai->symmetric_##name); \ \ ret = snd_pcm_hw_constraint_single(substream->runtime, \ SNDRV_PCM_HW_PARAM_##NAME,\ - soc_dai->name); \ - if (ret < 0) { \ - dev_err(soc_dai->dev, \ - "ASoC: Unable to apply %s constraint: %d\n",\ - #name, ret); \ - return ret; \ - } \ + soc_dai->symmetric_##name); \ + if (ret < 0) \ + return snd_soc_ret(soc_dai->dev, ret, \ + "Unable to apply %s constraint\n", #name); \ } __soc_pcm_apply_symmetry(rate, RATE); @@ -432,7 +470,7 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai d; struct snd_soc_dai *dai; struct snd_soc_dai *cpu_dai; @@ -449,11 +487,12 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, if (symmetry) \ for_each_rtd_cpu_dais(rtd, i, cpu_dai) \ 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; \ - } + cpu_dai->symmetric_##xxx && \ + cpu_dai->symmetric_##xxx != d.symmetric_##xxx) \ + return snd_soc_ret(rtd->dev, -EINVAL, \ + "unmatched %s symmetry: %s:%d - %s:%d\n", \ + #xxx, cpu_dai->name, cpu_dai->symmetric_##xxx, \ + d.name, d.symmetric_##xxx); /* reject unmatched parameters when applying symmetry */ __soc_pcm_params_symmetry(rate); @@ -465,7 +504,7 @@ static int soc_pcm_params_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_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai_link *link = rtd->dai_link; struct snd_soc_dai *dai; unsigned int symmetry, i; @@ -486,7 +525,7 @@ static void soc_pcm_update_symmetry(struct snd_pcm_substream *substream) static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int ret; if (!bits) @@ -500,7 +539,7 @@ static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int stream = substream->stream; @@ -508,7 +547,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) unsigned int bits = 0, cpu_bits = 0; for_each_rtd_codec_dais(rtd, i, codec_dai) { - struct snd_soc_pcm_stream *pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); + const struct snd_soc_pcm_stream *pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); if (pcm_codec->sig_bits == 0) { bits = 0; @@ -518,7 +557,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - struct snd_soc_pcm_stream *pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); + const struct snd_soc_pcm_stream *pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); if (pcm_cpu->sig_bits == 0) { cpu_bits = 0; @@ -531,18 +570,30 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) soc_pcm_set_msb(substream, cpu_bits); } -static void soc_pcm_hw_init(struct snd_pcm_hardware *hw) +static void soc_pcm_hw_init(struct snd_pcm_hardware *hw, bool force) { - hw->rates = UINT_MAX; - hw->rate_min = 0; - hw->rate_max = UINT_MAX; - hw->channels_min = 0; - hw->channels_max = UINT_MAX; - hw->formats = ULLONG_MAX; + if (force) { + hw->rates = UINT_MAX; + hw->rate_min = 0; + hw->rate_max = UINT_MAX; + hw->channels_min = 0; + hw->channels_max = UINT_MAX; + hw->formats = ULLONG_MAX; + } else { + /* Preserve initialized parameters */ + if (!hw->rates) + hw->rates = UINT_MAX; + if (!hw->rate_max) + hw->rate_max = UINT_MAX; + if (!hw->channels_max) + hw->channels_max = UINT_MAX; + if (!hw->formats) + hw->formats = ULLONG_MAX; + } } static void soc_pcm_hw_update_rate(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->rates = snd_pcm_rate_mask_intersect(hw->rates, p->rates); @@ -555,16 +606,17 @@ static void soc_pcm_hw_update_rate(struct snd_pcm_hardware *hw, } static void soc_pcm_hw_update_chan(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->channels_min = max(hw->channels_min, p->channels_min); hw->channels_max = min(hw->channels_max, p->channels_max); } static void soc_pcm_hw_update_format(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { - hw->formats &= p->formats; + hw->formats &= p->formats; + hw->subformats &= p->subformats; } /** @@ -581,12 +633,12 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, { struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; - struct snd_soc_pcm_stream *codec_stream; - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *codec_stream; + const struct snd_soc_pcm_stream *cpu_stream; unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX; int i; - soc_pcm_hw_init(hw); + soc_pcm_hw_init(hw, true); /* first calculate min/max only for CPUs in the DAI link */ for_each_rtd_cpu_dais(rtd, i, cpu_dai) { @@ -649,7 +701,7 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_calc_hw); static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) { struct snd_pcm_hardware *hw = &substream->runtime->hw; - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); u64 formats = hw->formats; /* @@ -665,7 +717,7 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) static int soc_pcm_components_open(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_component *component; int i, ret = 0; @@ -685,7 +737,7 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream) static int soc_pcm_components_close(struct snd_pcm_substream *substream, int rollback) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_component *component; int i, ret = 0; @@ -711,17 +763,15 @@ static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd, if (!rollback) { snd_soc_runtime_deactivate(rtd, substream->stream); - /* clear the corresponding DAIs parameters when going to be inactive */ + + /* Make sure DAI parameters cleared if the DAI becomes inactive */ for_each_rtd_dais(rtd, i, dai) { if (snd_soc_dai_active(dai) == 0) soc_pcm_set_dai_params(dai, NULL); - - if (snd_soc_dai_stream_active(dai, substream->stream) == 0) - snd_soc_dai_digital_mute(dai, 1, substream->stream); } } - for_each_rtd_dais(rtd, i, dai) + for_each_rtd_dais_reverse(rtd, i, dai) snd_soc_dai_shutdown(dai, substream, rollback); snd_soc_link_shutdown(substream, rollback); @@ -751,7 +801,7 @@ static int __soc_pcm_close(struct snd_soc_pcm_runtime *rtd, /* PCM close ops for non-DPCM streams */ static int soc_pcm_close(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); snd_soc_dpcm_mutex_lock(rtd); __soc_pcm_close(rtd, substream); @@ -761,7 +811,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) static int soc_hw_sanity_check(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_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); @@ -792,9 +842,8 @@ static int soc_hw_sanity_check(struct snd_pcm_substream *substream) return 0; config_err: - dev_err(dev, "ASoC: %s <-> %s No matching %s\n", - name_codec, name_cpu, err_msg); - return -EINVAL; + return snd_soc_ret(dev, -EINVAL, + "%s <-> %s No matching %s\n", name_codec, name_cpu, err_msg); } /* @@ -867,7 +916,7 @@ err: /* PCM open ops for non-DPCM streams */ static int soc_pcm_open(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int ret; snd_soc_dpcm_mutex_lock(rtd); @@ -911,22 +960,37 @@ static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd, snd_soc_dapm_stream_event(rtd, substream->stream, SND_SOC_DAPM_STREAM_START); - for_each_rtd_dais(rtd, i, dai) - snd_soc_dai_digital_mute(dai, 0, substream->stream); + for_each_rtd_dais(rtd, i, dai) { + if (!snd_soc_dai_mute_is_ctrled_at_trigger(dai)) + snd_soc_dai_digital_mute(dai, 0, substream->stream); + } out: - return soc_pcm_ret(rtd, ret); + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ + return ret; } /* PCM prepare ops for non-DPCM streams */ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int ret; snd_soc_dpcm_mutex_lock(rtd); ret = __soc_pcm_prepare(rtd, substream); snd_soc_dpcm_mutex_unlock(rtd); + + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ return ret; } @@ -949,6 +1013,17 @@ static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd, snd_soc_dpcm_mutex_assert_held(rtd); + /* clear the corresponding DAIs parameters when going to be inactive */ + for_each_rtd_dais(rtd, i, dai) { + if (snd_soc_dai_active(dai) == 1) + soc_pcm_set_dai_params(dai, NULL); + + if (snd_soc_dai_stream_active(dai, substream->stream) == 1) { + if (!snd_soc_dai_mute_is_ctrled_at_trigger(dai)) + snd_soc_dai_digital_mute(dai, 1, substream->stream); + } + } + /* run the stream event */ snd_soc_dapm_stream_stop(rtd, substream->stream); @@ -978,7 +1053,7 @@ static int __soc_pcm_hw_free(struct snd_soc_pcm_runtime *rtd, /* hw_free PCM ops for non-DPCM streams */ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int ret; snd_soc_dpcm_mutex_lock(rtd); @@ -992,12 +1067,13 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) * function can also be called multiple times and can allocate buffers * (using snd_pcm_lib_* ). It's non-atomic. */ -static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_substream *substream, +static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; + struct snd_pcm_hw_params tmp_params; int i, ret = 0; snd_soc_dpcm_mutex_assert_held(rtd); @@ -1011,7 +1087,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, goto out; for_each_rtd_codec_dais(rtd, i, codec_dai) { - struct snd_pcm_hw_params codec_params; + unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); /* * Skip CODECs which don't support the current stream type, @@ -1031,29 +1107,26 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, continue; /* copy params for each codec */ - codec_params = *params; + tmp_params = *params; /* fixup params based on TDM slot masks */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - codec_dai->tx_mask) - soc_pcm_codec_params_fixup(&codec_params, - codec_dai->tx_mask); - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && - codec_dai->rx_mask) - soc_pcm_codec_params_fixup(&codec_params, - codec_dai->rx_mask); + if (tdm_mask) + soc_pcm_codec_params_fixup(&tmp_params, tdm_mask); ret = snd_soc_dai_hw_params(codec_dai, substream, - &codec_params); + &tmp_params); if(ret < 0) goto out; - soc_pcm_set_dai_params(codec_dai, &codec_params); - snd_soc_dapm_update_dai(substream, &codec_params, codec_dai); + soc_pcm_set_dai_params(codec_dai, &tmp_params); + snd_soc_dapm_update_dai(substream, &tmp_params, codec_dai); } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + struct snd_soc_dai_link_ch_map *ch_maps; + unsigned int ch_mask = 0; + int j; + /* * Skip CPUs which don't support the current stream * type. See soc_pcm_init_runtime_hw() for more details @@ -1061,13 +1134,30 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) continue; - ret = snd_soc_dai_hw_params(cpu_dai, substream, params); + /* copy params for each cpu */ + tmp_params = *params; + + /* + * construct cpu channel mask by combining ch_mask of each + * codec which maps to the cpu. + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + for_each_rtd_ch_maps(rtd, j, ch_maps) + if (ch_maps->cpu == i) + ch_mask |= ch_maps->ch_mask; + + /* fixup cpu channel number */ + if (ch_mask) + soc_pcm_codec_params_fixup(&tmp_params, ch_mask); + + ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params); if (ret < 0) goto out; /* store the parameters for each DAI */ - soc_pcm_set_dai_params(cpu_dai, params); - snd_soc_dapm_update_dai(substream, params, cpu_dai); + soc_pcm_set_dai_params(cpu_dai, &tmp_params); + snd_soc_dapm_update_dai(substream, &tmp_params, cpu_dai); } ret = snd_soc_pcm_component_hw_params(substream, params); @@ -1082,41 +1172,76 @@ out: static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int ret; snd_soc_dpcm_mutex_lock(rtd); - ret = __soc_pcm_hw_params(rtd, substream, params); + ret = __soc_pcm_hw_params(substream, params); snd_soc_dpcm_mutex_unlock(rtd); return ret; } +#define TRIGGER_MAX 3 +static int (* const trigger[][TRIGGER_MAX])(struct snd_pcm_substream *substream, int cmd, int rollback) = { + [SND_SOC_TRIGGER_ORDER_DEFAULT] = { + snd_soc_link_trigger, + snd_soc_pcm_component_trigger, + snd_soc_pcm_dai_trigger, + }, + [SND_SOC_TRIGGER_ORDER_LDC] = { + snd_soc_link_trigger, + snd_soc_pcm_dai_trigger, + snd_soc_pcm_component_trigger, + }, +}; + static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - int ret = -EINVAL, _ret = 0; + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_component *component; + int ret = 0, r = 0, i; int rollback = 0; + int start = 0, stop = 0; + /* + * select START/STOP sequence + */ + for_each_rtd_components(rtd, i, component) { + if (component->driver->trigger_start) + start = component->driver->trigger_start; + if (component->driver->trigger_stop) + stop = component->driver->trigger_stop; + } + if (rtd->dai_link->trigger_start) + start = rtd->dai_link->trigger_start; + if (rtd->dai_link->trigger_stop) + stop = rtd->dai_link->trigger_stop; + + if (start < 0 || start >= SND_SOC_TRIGGER_ORDER_MAX || + stop < 0 || stop >= SND_SOC_TRIGGER_ORDER_MAX) + return -EINVAL; + + /* + * START + */ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = snd_soc_link_trigger(substream, cmd, 0); - if (ret < 0) - goto start_err; - - ret = snd_soc_pcm_component_trigger(substream, cmd, 0); - if (ret < 0) - goto start_err; - - ret = snd_soc_pcm_dai_trigger(substream, cmd, 0); -start_err: - if (ret < 0) - rollback = 1; + for (i = 0; i < TRIGGER_MAX; i++) { + r = trigger[start][i](substream, cmd, 0); + if (r < 0) + break; + } } - if (rollback) { - _ret = ret; + /* + * Rollback if START failed + * find correspond STOP command + */ + if (r < 0) { + rollback = 1; + ret = r; switch (cmd) { case SNDRV_PCM_TRIGGER_START: cmd = SNDRV_PCM_TRIGGER_STOP; @@ -1130,34 +1255,20 @@ start_err: } } + /* + * STOP + */ switch (cmd) { case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (rtd->dai_link->stop_dma_first) { - ret = snd_soc_pcm_component_trigger(substream, cmd, rollback); - if (ret < 0) - break; - - ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback); - if (ret < 0) - break; - } else { - ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback); - if (ret < 0) - break; - - ret = snd_soc_pcm_component_trigger(substream, cmd, rollback); - if (ret < 0) - break; + for (i = TRIGGER_MAX; i > 0; i--) { + r = trigger[stop][i - 1](substream, cmd, rollback); + if (r < 0) + ret = r; } - ret = snd_soc_link_trigger(substream, cmd, rollback); - break; } - if (_ret) - ret = _ret; - return ret; } @@ -1195,19 +1306,18 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, snd_soc_dpcm_mutex_assert_held(fe); /* only add new dpcms */ - for_each_dpcm_be(fe, stream, dpcm) { - if (dpcm->be == be && dpcm->fe == fe) + for_each_dpcm_be(fe, stream, dpcm) + if (dpcm->be == be) return 0; - } fe_substream = snd_soc_dpcm_get_substream(fe, stream); be_substream = snd_soc_dpcm_get_substream(be, stream); - if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) { - dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n", - __func__); - return -EINVAL; - } + if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) + return snd_soc_ret(be->dev, -EINVAL, + "%s: %s is atomic but %s is nonatomic, invalid configuration\n", + __func__, fe->dai_link->name, be->dai_link->name); + if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) { dev_dbg(be->dev, "FE is nonatomic but BE is not, forcing BE as nonatomic\n"); be_substream->pcm->nonatomic = 1; @@ -1219,15 +1329,14 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, dpcm->be = be; dpcm->fe = fe; - be->dpcm[stream].runtime = fe->dpcm[stream].runtime; dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; - snd_soc_dpcm_stream_lock_irq(fe, stream); + snd_pcm_stream_lock_irq(fe_substream); list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); - snd_soc_dpcm_stream_unlock_irq(fe, stream); + snd_pcm_stream_unlock_irq(fe_substream); dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", - stream ? "capture" : "playback", fe->dai_link->name, + snd_pcm_direction_name(stream), fe->dai_link->name, stream ? "<-" : "->", be->dai_link->name); dpcm_create_debugfs_state(dpcm, stream); @@ -1255,7 +1364,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, continue; dev_dbg(fe->dev, "reparent %s path %s %s %s\n", - stream ? "capture" : "playback", + snd_pcm_direction_name(stream), dpcm->fe->dai_link->name, stream ? "<-" : "->", dpcm->be->dai_link->name); @@ -1269,21 +1378,22 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm, *d; + struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); LIST_HEAD(deleted_dpcms); snd_soc_dpcm_mutex_assert_held(fe); - snd_soc_dpcm_stream_lock_irq(fe, stream); + snd_pcm_stream_lock_irq(substream); for_each_dpcm_be_safe(fe, stream, dpcm, d) { dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", - stream ? "capture" : "playback", + snd_pcm_direction_name(stream), dpcm->be->dai_link->name); if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) continue; dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n", - stream ? "capture" : "playback", fe->dai_link->name, + snd_pcm_direction_name(stream), fe->dai_link->name, stream ? "<-" : "->", dpcm->be->dai_link->name); /* BEs still alive need new FE */ @@ -1292,7 +1402,7 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) list_del(&dpcm->list_be); list_move(&dpcm->list_fe, &deleted_dpcms); } - snd_soc_dpcm_stream_unlock_irq(fe, stream); + snd_pcm_stream_unlock_irq(substream); while (!list_empty(&deleted_dpcms)) { dpcm = list_first_entry(&deleted_dpcms, struct snd_soc_dpcm, @@ -1337,7 +1447,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, return NULL; } -static int widget_in_list(struct snd_soc_dapm_widget_list *list, +int widget_in_list(struct snd_soc_dapm_widget_list *list, struct snd_soc_dapm_widget *widget) { struct snd_soc_dapm_widget *w; @@ -1349,10 +1459,11 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, return 0; } +EXPORT_SYMBOL_GPL(widget_in_list); bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir) { - struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_card *card = snd_soc_dapm_to_card(widget->dapm); struct snd_soc_pcm_runtime *rtd; int stream; @@ -1373,14 +1484,12 @@ EXPORT_SYMBOL_GPL(dpcm_end_walk_at_be); int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list) { - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0); int paths; - if (fe->dai_link->num_cpus > 1) { - dev_err(fe->dev, + if (fe->dai_link->num_cpus > 1) + return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__); - return -EINVAL; - } /* get number of valid DAI paths and their widgets */ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list, @@ -1389,10 +1498,10 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, if (paths > 0) dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, - stream ? "capture" : "playback"); + snd_pcm_direction_name(stream)); else if (paths == 0) dev_dbg(fe->dev, "ASoC: %s no valid %s path\n", fe->dai_link->name, - stream ? "capture" : "playback"); + snd_pcm_direction_name(stream)); return paths; } @@ -1435,7 +1544,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, continue; dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", - stream ? "capture" : "playback", + snd_pcm_direction_name(stream), dpcm->be->dai_link->name, fe->dai_link->name); dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; dpcm_set_be_update_state(dpcm->be, stream, SND_SOC_DPCM_UPDATE_BE); @@ -1446,17 +1555,18 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, return prune; } -static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, - struct snd_soc_dapm_widget_list **list_) +int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list_) { struct snd_soc_card *card = fe->card; struct snd_soc_dapm_widget_list *list = *list_; struct snd_soc_pcm_runtime *be; struct snd_soc_dapm_widget *widget; + struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream); int i, new = 0, err; /* don't connect if FE is not running */ - if (!fe->dpcm[stream].runtime && !fe->fe_compr) + if (!fe_substream->runtime && !fe->fe_compr) return new; /* Create any new FE <--> BE connections */ @@ -1486,10 +1596,13 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, /* * Filter for systems with 'component_chaining' enabled. * This helps to avoid unnecessary re-configuration of an - * already active BE on such systems. + * already active BE on such systems and ensures the BE DAI + * widget is powered ON after hw_params() BE DAI callback. */ if (fe->card->component_chaining && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) continue; @@ -1511,19 +1624,6 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, return new; } -/* - * Find the corresponding BE DAIs that source or sink audio to this - * FE substream. - */ -int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, - int stream, struct snd_soc_dapm_widget_list **list, int new) -{ - if (new) - return dpcm_add_paths(fe, stream, list); - else - return dpcm_prune_paths(fe, stream, list); -} - void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1547,13 +1647,13 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, return; /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_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); + dev_err(be->dev, "ASoC: no users %s at close - state %s\n", + snd_pcm_direction_name(stream), + dpcm_state_string(be->dpcm[stream].state)); continue; } @@ -1578,6 +1678,7 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) { + struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream); struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; int err, count = 0; @@ -1591,19 +1692,19 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) if (!be_substream) { dev_err(be->dev, "ASoC: no backend %s stream\n", - stream ? "capture" : "playback"); + snd_pcm_direction_name(stream)); continue; } /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* first time the dpcm is open ? */ 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); + dev_err(be->dev, "ASoC: too many users %s at open %s\n", + snd_pcm_direction_name(stream), + dpcm_state_string(be->dpcm[stream].state)); continue; } @@ -1615,16 +1716,16 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) continue; dev_dbg(be->dev, "ASoC: open %s BE %s\n", - stream ? "capture" : "playback", be->dai_link->name); + snd_pcm_direction_name(stream), be->dai_link->name); - be_substream->runtime = be->dpcm[stream].runtime; + be_substream->runtime = fe_substream->runtime; err = __soc_pcm_open(be, be_substream); if (err < 0) { be->dpcm[stream].users--; if (be->dpcm[stream].users < 0) - dev_err(be->dev, "ASoC: no users %s at unwind %d\n", - stream ? "capture" : "playback", - be->dpcm[stream].state); + dev_err(be->dev, "ASoC: no users %s at unwind %s\n", + snd_pcm_direction_name(stream), + dpcm_state_string(be->dpcm[stream].state)); be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; goto unwind; @@ -1644,17 +1745,17 @@ unwind: static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_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); + soc_pcm_hw_init(hw, false); for_each_rtd_cpu_dais(fe, i, dai) { - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *cpu_stream; /* * Skip CPUs which don't support the current stream @@ -1674,7 +1775,7 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) 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_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; struct snd_soc_dpcm *dpcm; @@ -1691,7 +1792,7 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *codec_stream; + const struct snd_soc_pcm_stream *codec_stream; int i; for_each_rtd_codec_dais(be, i, dai) { @@ -1711,7 +1812,7 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream) 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_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; struct snd_soc_dpcm *dpcm; @@ -1727,7 +1828,7 @@ static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *cpu_stream; struct snd_soc_dai *dai; int i; @@ -1749,8 +1850,8 @@ static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream) * DAIs connected to a single CPU DAI, use CPU DAI's directly */ if (be->dai_link->num_codecs == 1) { - struct snd_soc_pcm_stream *codec_stream = snd_soc_dai_get_pcm_stream( - asoc_rtd_to_codec(be, 0), stream); + const struct snd_soc_pcm_stream *codec_stream = snd_soc_dai_get_pcm_stream( + snd_soc_rtd_to_codec(be, 0), stream); soc_pcm_hw_update_chan(hw, codec_stream); } @@ -1759,7 +1860,7 @@ static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream) 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_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; struct snd_soc_dpcm *dpcm; @@ -1775,7 +1876,7 @@ static void dpcm_runtime_setup_be_rate(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *pcm; + const struct snd_soc_pcm_stream *pcm; struct snd_soc_dai *dai; int i; @@ -1798,7 +1899,7 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, int stream) { struct snd_soc_dpcm *dpcm; - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(fe_substream); struct snd_soc_dai *fe_cpu_dai; int err = 0; int i; @@ -1825,7 +1926,7 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, if (!be_substream) continue; - rtd = asoc_substream_to_rtd(be_substream); + rtd = snd_soc_substream_to_rtd(be_substream); if (rtd->dai_link->be_hw_params_fixup) continue; @@ -1844,7 +1945,7 @@ error: static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(fe_substream); int stream = fe_substream->stream, ret = 0; dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); @@ -1881,7 +1982,7 @@ be_err: static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int stream = substream->stream; snd_soc_dpcm_mutex_assert_held(fe); @@ -1917,7 +2018,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) snd_soc_dpcm_get_substream(be, stream); /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* only free hw when no longer used - check all FEs */ @@ -1947,7 +2048,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int stream = substream->stream; snd_soc_dpcm_mutex_lock(fe); @@ -1983,7 +2084,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) 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)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* copy params for each dpcm */ @@ -2011,7 +2112,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_params BE %s\n", be->dai_link->name); - ret = __soc_pcm_hw_params(be, be_substream, &hw_params); + ret = __soc_pcm_hw_params(be_substream, &hw_params); if (ret < 0) goto unwind; @@ -2028,7 +2129,7 @@ unwind: be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* only allow hw_free() if no connected FEs are running */ @@ -2050,7 +2151,7 @@ unwind: static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int ret, stream = substream->stream; snd_soc_dpcm_mutex_lock(fe); @@ -2067,7 +2168,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, params_channels(params), params_format(params)); /* call hw_params on the frontend */ - ret = __soc_pcm_hw_params(fe, substream, params); + ret = __soc_pcm_hw_params(substream, params); if (ret < 0) dpcm_be_dai_hw_free(fe, stream); else @@ -2095,10 +2196,10 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); - snd_soc_dpcm_stream_lock_irqsave_nested(be, stream, flags); + snd_pcm_stream_lock_irqsave_nested(be_substream, flags); /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) goto next; dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n", @@ -2242,7 +2343,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, break; } next: - snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags); + snd_pcm_stream_unlock_irqrestore(be_substream, flags); if (ret) break; } @@ -2253,7 +2354,7 @@ EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, int cmd, bool fe_first) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int ret; /* call trigger on the frontend before the backend. */ @@ -2263,78 +2364,41 @@ static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, ret = soc_pcm_trigger(substream, cmd); if (ret < 0) - return ret; + goto end; ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); - return ret; } - /* call trigger on the frontend after the backend. */ - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); - if (ret < 0) - return ret; - - dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", - fe->dai_link->name, cmd); + else { + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + if (ret < 0) + goto end; - ret = soc_pcm_trigger(substream, cmd); + dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); - return ret; + ret = soc_pcm_trigger(substream, cmd); + } +end: + return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd); } static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int stream = substream->stream; int ret = 0; + int fe_first; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; switch (trigger) { case SND_SOC_DPCM_TRIGGER_PRE: - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_DRAIN: - ret = dpcm_dai_trigger_fe_be(substream, cmd, true); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = dpcm_dai_trigger_fe_be(substream, cmd, false); - break; - default: - ret = -EINVAL; - break; - } + fe_first = true; break; case SND_SOC_DPCM_TRIGGER_POST: - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_DRAIN: - ret = dpcm_dai_trigger_fe_be(substream, cmd, false); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = dpcm_dai_trigger_fe_be(substream, cmd, true); - break; - default: - ret = -EINVAL; - break; - } - break; - case SND_SOC_DPCM_TRIGGER_BESPOKE: - /* bespoke trigger() - handles both FE and BEs */ - - dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n", - fe->dai_link->name, cmd); - - ret = snd_soc_pcm_dai_bespoke_trigger(substream, cmd); + fe_first = false; break; default: dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, @@ -2343,12 +2407,26 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) goto out; } - if (ret < 0) { - dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n", - cmd, ret); - goto out; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_DRAIN: + ret = dpcm_dai_trigger_fe_be(substream, cmd, fe_first); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = dpcm_dai_trigger_fe_be(substream, cmd, !fe_first); + break; + default: + ret = -EINVAL; + break; } + if (ret < 0) + goto out; + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -2371,7 +2449,7 @@ out: static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int stream = substream->stream; /* if FE's runtime_update is already set, we're in race; @@ -2398,7 +2476,10 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) snd_soc_dpcm_get_substream(be, stream); /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) + continue; + + if (!snd_soc_dpcm_can_be_prepared(fe, be, stream)) continue; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && @@ -2417,12 +2498,18 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; } - return soc_pcm_ret(fe, ret); + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ + return ret; } static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int stream = substream->stream, ret = 0; snd_soc_dpcm_mutex_lock(fe); @@ -2431,14 +2518,6 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) 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)) { - dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n", - fe->dai_link->name); - ret = -EINVAL; - goto out; - } - ret = dpcm_be_dai_prepare(fe, stream); if (ret < 0) goto out; @@ -2454,31 +2533,23 @@ out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); snd_soc_dpcm_mutex_unlock(fe); - return soc_pcm_ret(fe, ret); + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ + return ret; } static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) { - struct snd_pcm_substream *substream = - snd_soc_dpcm_get_substream(fe, stream); - enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; int err; dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n", - stream ? "capture" : "playback", fe->dai_link->name); - - if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { - /* call bespoke trigger - FE takes care of all BE triggers */ - dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n", - fe->dai_link->name); + snd_pcm_direction_name(stream), fe->dai_link->name); - err = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP); - } 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); - } + err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); dpcm_be_dai_hw_free(fe, stream); @@ -2492,20 +2563,17 @@ static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) { - struct snd_pcm_substream *substream = - snd_soc_dpcm_get_substream(fe, stream); struct snd_soc_dpcm *dpcm; - enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; int ret = 0; dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", - stream ? "capture" : "playback", fe->dai_link->name); + snd_pcm_direction_name(stream), fe->dai_link->name); /* 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) { - dev_err(fe->dev, "ASoC: FE %s is not ready %d\n", - fe->dai_link->name, fe->dpcm[stream].state); + dev_err(fe->dev, "ASoC: FE %s is not ready %s\n", + fe->dai_link->name, dpcm_state_string(fe->dpcm[stream].state)); ret = -EINVAL; goto disconnect; } @@ -2539,23 +2607,9 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) return 0; - if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { - /* call trigger on the frontend - FE takes care of all BE triggers */ - dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n", - fe->dai_link->name); - - ret = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START); - 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) - goto hw_free; - } + ret = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) + goto hw_free; return 0; @@ -2569,7 +2623,7 @@ disconnect: struct snd_soc_pcm_runtime *be = dpcm->be; /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE || @@ -2589,14 +2643,12 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) if (!fe->dai_link->dynamic) return 0; - if (fe->dai_link->num_cpus > 1) { - dev_err(fe->dev, + if (fe->dai_link->num_cpus > 1) + return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__); - return -EINVAL; - } /* only check active links */ - if (!snd_soc_dai_active(asoc_rtd_to_cpu(fe, 0))) + if (!snd_soc_dai_active(snd_soc_rtd_to_cpu(fe, 0))) return 0; /* DAPM sync will call this to update DSP paths */ @@ -2606,13 +2658,13 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) for_each_pcm_streams(stream) { /* skip if FE doesn't have playback/capture capability */ - if (!snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream) || - !snd_soc_dai_stream_valid(asoc_rtd_to_codec(fe, 0), stream)) + if (!snd_soc_dai_stream_valid(snd_soc_rtd_to_cpu(fe, 0), stream) || + !snd_soc_dai_stream_valid(snd_soc_rtd_to_codec(fe, 0), stream)) continue; /* skip if FE isn't currently playing/capturing */ - if (!snd_soc_dai_stream_active(asoc_rtd_to_cpu(fe, 0), stream) || - !snd_soc_dai_stream_active(asoc_rtd_to_codec(fe, 0), stream)) + if (!snd_soc_dai_stream_active(snd_soc_rtd_to_cpu(fe, 0), stream) || + !snd_soc_dai_stream_active(snd_soc_rtd_to_codec(fe, 0), stream)) continue; paths = dpcm_path_get(fe, stream, &list); @@ -2620,7 +2672,14 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) return paths; /* update any playback/capture paths */ - count = dpcm_process_paths(fe, stream, &list, new); + /* + * Find the corresponding BE DAIs that source or sink audio to this + * FE substream. + */ + if (new) + count = dpcm_add_paths(fe, stream, &list); + else + count = dpcm_prune_paths(fe, stream, &list); if (count) { dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); if (new) @@ -2647,7 +2706,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) struct snd_soc_pcm_runtime *fe; int ret = 0; - mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); + snd_soc_dpcm_mutex_lock(card); /* shutdown all old paths first */ for_each_card_rtds(card, fe) { ret = soc_dpcm_fe_runtime_update(fe, 0); @@ -2663,14 +2722,15 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) } out: - mutex_unlock(&card->pcm_mutex); - return ret; + snd_soc_dpcm_mutex_unlock(card); + + return snd_soc_ret(card->dev, ret, "%s() failed\n", __func__); } EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(fe_substream); struct snd_soc_dpcm *dpcm; int stream = fe_substream->stream; @@ -2681,13 +2741,11 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; dpcm_be_disconnect(fe, stream); - - fe->dpcm[stream].runtime = NULL; } static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(fe_substream); int ret; snd_soc_dpcm_mutex_lock(fe); @@ -2701,26 +2759,37 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) { - struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(fe_substream); struct snd_soc_dapm_widget_list *list; int ret; int stream = fe_substream->stream; snd_soc_dpcm_mutex_lock(fe); - fe->dpcm[stream].runtime = fe_substream->runtime; ret = dpcm_path_get(fe, stream, &list); if (ret < 0) goto open_end; /* calculate valid and active FE <-> BE dpcms */ - dpcm_process_paths(fe, stream, &list, 1); + dpcm_add_paths(fe, stream, &list); + + /* There is no point starting up this FE if there are no BEs. */ + if (list_empty(&fe->dpcm[stream].be_clients)) { + /* dev_err_once() for visibility, dev_dbg() for debugging UCM profiles. */ + dev_err_once(fe->dev, "ASoC: no backend DAIs enabled for %s, possibly missing ALSA mixer-based routing or UCM profile\n", + fe->dai_link->name); + dev_dbg(fe->dev, "ASoC: no backend DAIs enabled for %s\n", fe->dai_link->name); + + ret = -EINVAL; + goto put_path; + } ret = dpcm_fe_dai_startup(fe_substream); if (ret < 0) dpcm_fe_dai_cleanup(fe_substream); dpcm_clear_pending_state(fe, stream); +put_path: dpcm_path_put(&list); open_end: snd_soc_dpcm_mutex_unlock(fe); @@ -2730,112 +2799,90 @@ open_end: static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, int *playback, int *capture) { + struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai; + struct snd_soc_dai *codec_dai; + struct snd_soc_dai_link_ch_map *ch_maps; + struct snd_soc_dai *dummy_dai = snd_soc_find_dai(&snd_soc_dummy_dlc); + int cpu_capture; + int cpu_playback; + int has_playback = 0; + int has_capture = 0; int i; - if (rtd->dai_link->dynamic && rtd->dai_link->num_cpus > 1) { - dev_err(rtd->dev, - "DPCM doesn't support Multi CPU for Front-Ends yet\n"); - return -EINVAL; - } + if (dai_link->dynamic && dai_link->num_cpus > 1) + return snd_soc_ret(rtd->dev, -EINVAL, + "DPCM doesn't support Multi CPU for Front-Ends yet\n"); - if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { - int stream; + /* Adapt stream for codec2codec links */ + cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE); + cpu_playback = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_PLAYBACK); - if (rtd->dai_link->dpcm_playback) { - stream = SNDRV_PCM_STREAM_PLAYBACK; + /* + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + for_each_rtd_ch_maps(rtd, i, ch_maps) { + cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu); + codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec); - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - if (snd_soc_dai_stream_valid(cpu_dai, stream)) { - *playback = 1; - break; - } - } - if (!*playback) { - dev_err(rtd->card->dev, - "No CPU DAIs support playback for stream %s\n", - rtd->dai_link->stream_name); - return -EINVAL; - } - } - if (rtd->dai_link->dpcm_capture) { - stream = SNDRV_PCM_STREAM_CAPTURE; + /* + * FIXME + * + * DPCM Codec has been no checked before. + * It should be checked, but it breaks compatibility. + * + * For example there is a case that CPU have loopback capabilities which is used + * for tests on boards where the Codec has no capture capabilities. In this case, + * Codec capture validation check will be fail, but system should allow capture + * capabilities. We have no solution for it today. + */ + if (dai_link->dynamic || dai_link->no_pcm) + codec_dai = dummy_dai; - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - if (snd_soc_dai_stream_valid(cpu_dai, stream)) { - *capture = 1; - break; - } - } + if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && + snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) + has_playback = 1; + if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && + snd_soc_dai_stream_valid(cpu_dai, cpu_capture)) + has_capture = 1; + } - if (!*capture) { - dev_err(rtd->card->dev, - "No CPU DAIs support capture for stream %s\n", - rtd->dai_link->stream_name); - return -EINVAL; - } - } - } else { - struct snd_soc_dai *codec_dai; - - /* Adapt stream for codec2codec links */ - int cpu_capture = rtd->dai_link->params ? - SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; - int cpu_playback = rtd->dai_link->params ? - SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - if (rtd->dai_link->num_cpus == 1) { - cpu_dai = asoc_rtd_to_cpu(rtd, 0); - } else if (rtd->dai_link->num_cpus == rtd->dai_link->num_codecs) { - cpu_dai = asoc_rtd_to_cpu(rtd, i); - } else { - dev_err(rtd->card->dev, - "N cpus to M codecs link is not supported yet\n"); - return -EINVAL; - } + if (dai_link->playback_only) + has_capture = 0; - if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && - snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) - *playback = 1; - if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && - snd_soc_dai_stream_valid(cpu_dai, cpu_capture)) - *capture = 1; - } - } + if (dai_link->capture_only) + has_playback = 0; - if (rtd->dai_link->playback_only) { - *playback = 1; - *capture = 0; - } + if (!has_playback && !has_capture) + return snd_soc_ret(rtd->dev, -EINVAL, + "substream %s has no playback, no capture\n", dai_link->stream_name); - if (rtd->dai_link->capture_only) { - *playback = 0; - *capture = 1; - } + *playback = has_playback; + *capture = has_capture; return 0; } static int soc_create_pcm(struct snd_pcm **pcm, struct snd_soc_pcm_runtime *rtd, - int playback, int capture, int num) + int playback, int capture) { char new_name[64]; int ret; /* create the PCM */ - if (rtd->dai_link->params) { + if (rtd->dai_link->c2c_params) { snprintf(new_name, sizeof(new_name), "codec2codec(%s)", rtd->dai_link->stream_name); - ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, + ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, rtd->id, playback, capture, pcm); } else if (rtd->dai_link->no_pcm) { snprintf(new_name, sizeof(new_name), "(%s)", rtd->dai_link->stream_name); - ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, + ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, rtd->id, playback, capture, pcm); } else { if (rtd->dai_link->dynamic) @@ -2844,23 +2891,22 @@ static int soc_create_pcm(struct snd_pcm **pcm, else snprintf(new_name, sizeof(new_name), "%s %s-%d", rtd->dai_link->stream_name, - soc_codec_dai_name(rtd), num); + soc_codec_dai_name(rtd), rtd->id); - ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, + ret = snd_pcm_new(rtd->card->snd_card, new_name, rtd->id, playback, capture, pcm); } - if (ret < 0) { - dev_err(rtd->card->dev, "ASoC: can't create pcm %s for dailink %s: %d\n", - new_name, rtd->dai_link->name, ret); - return ret; - } - dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name); + if (ret < 0) + return snd_soc_ret(rtd->dev, ret, + "can't create pcm %s for dailink %s\n", new_name, rtd->dai_link->name); + + dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n", rtd->id, new_name); return 0; } /* create a new pcm */ -int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) +int soc_new_pcm(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component; struct snd_pcm *pcm; @@ -2871,7 +2917,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (ret < 0) return ret; - ret = soc_create_pcm(&pcm, rtd, playback, capture, num); + ret = soc_create_pcm(&pcm, rtd, playback, capture); if (ret < 0) return ret; @@ -2882,7 +2928,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * don't interface with the outside world or application layer * we don't have to do any special handling on close. */ - if (!rtd->dai_link->params) + if (!rtd->dai_link->c2c_params) rtd->close_delayed_work_func = snd_soc_close_delayed_work; rtd->pcm = pcm; @@ -2890,7 +2936,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) pcm->private_data = rtd; pcm->no_device_suspend = true; - if (rtd->dai_link->no_pcm || rtd->dai_link->params) { + if (rtd->dai_link->no_pcm || rtd->dai_link->c2c_params) { if (playback) pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; if (capture) @@ -2924,8 +2970,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.ioctl = snd_soc_pcm_component_ioctl; if (drv->sync_stop) rtd->ops.sync_stop = snd_soc_pcm_component_sync_stop; - if (drv->copy_user) - rtd->ops.copy_user = snd_soc_pcm_component_copy_user; + if (drv->copy) + rtd->ops.copy = snd_soc_pcm_component_copy; if (drv->page) rtd->ops.page = snd_soc_pcm_component_page; if (drv->mmap) @@ -2949,27 +2995,6 @@ out: return ret; } -/* is the current PCM operation for this FE ? */ -int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream) -{ - if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) - return 1; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update); - -/* is the current PCM operation for this BE ? */ -int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, - struct snd_soc_pcm_runtime *be, int stream) -{ - if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || - ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) && - be->dpcm[stream].runtime_update)) - return 1; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update); - /* get the substream for this BE */ struct snd_pcm_substream * snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream) @@ -2977,67 +3002,3 @@ struct snd_pcm_substream * return be->pcm->streams[stream].substream; } EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); - -static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, - struct snd_soc_pcm_runtime *be, - int stream, - const enum snd_soc_dpcm_state *states, - int num_states) -{ - struct snd_soc_dpcm *dpcm; - int state; - int ret = 1; - int i; - - for_each_dpcm_fe(be, stream, dpcm) { - - if (dpcm->fe == fe) - continue; - - state = dpcm->fe->dpcm[stream].state; - for (i = 0; i < num_states; i++) { - if (state == states[i]) { - ret = 0; - break; - } - } - } - - /* it's safe to do this BE DAI */ - return ret; -} - -/* - * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE - * are not running, paused or suspended for the specified stream direction. - */ -int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, - struct snd_soc_pcm_runtime *be, int stream) -{ - const enum snd_soc_dpcm_state state[] = { - SND_SOC_DPCM_STATE_START, - SND_SOC_DPCM_STATE_PAUSED, - SND_SOC_DPCM_STATE_SUSPEND, - }; - - return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); - -/* - * We can only change hw params a BE DAI if any of it's FE are not prepared, - * running, paused or suspended for the specified stream direction. - */ -int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, - struct snd_soc_pcm_runtime *be, int stream) -{ - const enum snd_soc_dpcm_state state[] = { - SND_SOC_DPCM_STATE_START, - SND_SOC_DPCM_STATE_PAUSED, - SND_SOC_DPCM_STATE_SUSPEND, - SND_SOC_DPCM_STATE_PREPARE, - }; - - return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); |
