/* is the current PCM operation for this FE ? */ #if0 staticint snd_soc_dpcm_can_fe_update(struct snd_soc_pcm_runtime *fe, int stream)
{ if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) return1; return0;
} #endif
/* is the current PCM operation for this BE ? */ staticint snd_soc_dpcm_can_be_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)) return1; return0;
}
staticint snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream, constenum 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;
}
}
}
staticconstchar *dpcm_state_string(enum snd_soc_dpcm_state state)
{ switch (state) { case SND_SOC_DPCM_STATE_NEW: return"new"; case SND_SOC_DPCM_STATE_OPEN: return"open"; case SND_SOC_DPCM_STATE_HW_PARAMS: return"hw_params"; case SND_SOC_DPCM_STATE_PREPARE: return"prepare"; case SND_SOC_DPCM_STATE_START: return"start"; case SND_SOC_DPCM_STATE_STOP: return"stop"; case SND_SOC_DPCM_STATE_SUSPEND: return"suspend"; case SND_SOC_DPCM_STATE_PAUSED: return"paused"; case SND_SOC_DPCM_STATE_HW_FREE: return"hw_free"; case SND_SOC_DPCM_STATE_CLOSE: return"close";
}
/** *snd_soc_runtime_action()-Increment/Decrementactivecountfor *PCMruntimecomponents *@rtd:ASoCPCMruntimethatisactivated *@stream:DirectionofthePCMstream *@action:Activatestreamif1.Deactivateif-1. * *Increments/DecrementstheactivecountforalltheDAIsandcomponents *attachedtoaPCMruntime. *Shouldtypicallybecalledwhenastreamisopened. * *Mustbecalledwiththertd->card->pcm_mutexbeingheld
*/ 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;
snd_soc_dpcm_mutex_assert_held(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);
if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) returntrue;
for_each_rtd_components(rtd, i, component) if (component->driver->use_pmdown_time) /* No need to go through all components */ returnfalse;
returntrue;
}
/* DPCM stream event, send event to FE and all active BEs. */ void dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event)
{ struct snd_soc_dpcm *dpcm;
snd_soc_dpcm_mutex_assert_held(fe);
for_each_dpcm_be(fe, dir, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n",
be->dai_link->name, event, dir);
if ((event == SND_SOC_DAPM_STREAM_STOP) &&
(be->dpcm[dir].users >= 1)) continue;
staticint soc_pcm_components_open(struct snd_pcm_substream *substream)
{ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_component *component; int i, ret = 0;
for_each_rtd_components(rtd, i, component) {
ret = snd_soc_component_module_get_when_open(component, substream); if (ret < 0) break;
ret = snd_soc_component_open(component, substream); if (ret < 0) break;
}
return ret;
}
staticint soc_pcm_components_close(struct snd_pcm_substream *substream, int rollback)
{ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_component *component; int i, ret = 0;
for_each_rtd_components(rtd, i, component) { int r = snd_soc_component_close(component, substream, rollback); if (r < 0)
ret = r; /* use last ret */
staticint soc_pcm_clean(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream, int rollback)
{ struct snd_soc_component *component; struct snd_soc_dai *dai; int i;
snd_soc_dpcm_mutex_assert_held(rtd);
if (!rollback) {
snd_soc_runtime_deactivate(rtd, substream->stream);
/* 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);
}
}
for_each_rtd_dais_reverse(rtd, i, dai)
snd_soc_dai_shutdown(dai, substream, rollback);
/* *CalledbyALSAwhenaPCMsubstreamisopened,theruntime->hwrecordis *theninitializedandanyprivatedatacanbeallocated.Thisalsocalls *startupforthecpuDAI,component,machineandcodecDAI.
*/ staticint __soc_pcm_open(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream)
{ struct snd_soc_component *component; struct snd_soc_dai *dai; int i, ret = 0;
snd_soc_dpcm_mutex_assert_held(rtd);
for_each_rtd_components(rtd, i, component)
pinctrl_pm_select_default_state(component->dev);
ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); if (ret < 0) goto err;
ret = soc_pcm_components_open(substream); if (ret < 0) goto err;
ret = snd_soc_link_startup(substream); if (ret < 0) goto err;
/* startup the audio subsystem */
for_each_rtd_dais(rtd, i, dai) {
ret = snd_soc_dai_startup(dai, substream); if (ret < 0) goto err;
}
/* Dynamic PCM DAI links compat checks use dynamic capabilities */ if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) goto dynamic;
/* Check that the codec and cpu DAIs are compatible */
soc_pcm_init_runtime_hw(substream);
soc_pcm_update_symmetry(substream);
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) {
ret = soc_pcm_apply_symmetry(substream, dai); if (ret != 0) goto err;
}
dynamic:
snd_soc_runtime_activate(rtd, substream->stream);
ret = 0;
err: if (ret < 0)
soc_pcm_clean(rtd, substream, 1);
return soc_pcm_ret(rtd, ret);
}
/* PCM open ops for non-DPCM streams */ staticint soc_pcm_open(struct snd_pcm_substream *substream)
{ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int ret;
snd_soc_dpcm_mutex_lock(rtd);
ret = __soc_pcm_open(rtd, substream);
snd_soc_dpcm_mutex_unlock(rtd); return ret;
}
/* *CalledbyALSAwhenthePCMsubstreamisprepared,cansetformat,sample *rate,etc.Thisfunctionisnonatomicandcanbecalledmultipletimes, *itcanrefertotheruntimeinfo.
*/ staticint __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream)
{ struct snd_soc_dai *dai; int i, ret = 0;
snd_soc_dpcm_mutex_assert_held(rtd);
ret = snd_soc_link_prepare(substream); if (ret < 0) goto out;
ret = snd_soc_pcm_component_prepare(substream); if (ret < 0) goto out;
ret = snd_soc_pcm_dai_prepare(substream); if (ret < 0) goto out;
/* cancel any delayed stream shutdown that is pending */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
rtd->pop_wait) {
rtd->pop_wait = 0;
cancel_delayed_work(&rtd->delayed_work);
}
staticint soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream, int rollback)
{ struct snd_soc_dai *dai; int i;
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);
/* free any machine hw params */
snd_soc_link_hw_free(substream, rollback);
/* free any component resources */
snd_soc_pcm_component_hw_free(substream, rollback);
/* now free hw params for the DAIs */
for_each_rtd_dais(rtd, i, dai) if (snd_soc_dai_stream_valid(dai, substream->stream))
snd_soc_dai_hw_free(dai, substream, rollback);
/* *START
*/ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: for (i = 0; i < TRIGGER_MAX; i++) {
r = trigger[start][i](substream, cmd, 0); if (r < 0) break;
}
}
/* *RollbackifSTARTfailed *findcorrespondSTOPcommand
*/ if (r < 0) {
rollback = 1;
ret = r; switch (cmd) { case SNDRV_PCM_TRIGGER_START:
cmd = SNDRV_PCM_TRIGGER_STOP; break; case SNDRV_PCM_TRIGGER_RESUME:
cmd = SNDRV_PCM_TRIGGER_SUSPEND; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
cmd = SNDRV_PCM_TRIGGER_PAUSE_PUSH; break;
}
}
/* *STOP
*/ switch (cmd) { case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: for (i = TRIGGER_MAX; i > 0; i--) {
r = trigger[stop][i - 1](substream, cmd, rollback); if (r < 0)
ret = r;
}
}
/* should be called *after* snd_soc_pcm_component_pointer() */
snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay);
snd_soc_pcm_component_delay(substream, &cpu_delay, &codec_delay);
runtime->delay = cpu_delay + codec_delay;
return offset;
}
/* connect a FE and BE */ staticint dpcm_be_connect(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream)
{ struct snd_pcm_substream *fe_substream; struct snd_pcm_substream *be_substream; struct snd_soc_dpcm *dpcm;
snd_soc_dpcm_mutex_assert_held(fe);
/* only add new dpcms */
for_each_dpcm_be(fe, stream, dpcm) if (dpcm->be == be) return0;
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;
}
dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); if (!dpcm) return -ENOMEM;
/* reparent a BE onto another FE */ staticvoid dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream)
{ struct snd_soc_dpcm *dpcm; struct snd_pcm_substream *fe_substream, *be_substream;
/* reparent if BE is connected to other FEs */ if (!be->dpcm[stream].users) return;
be_substream = snd_soc_dpcm_get_substream(be, stream); if (!be_substream) return;
for_each_dpcm_fe(be, stream, dpcm) { if (dpcm->fe == fe) continue;
/* disconnect a BE and 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_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",
snd_pcm_direction_name(stream),
dpcm->be->dai_link->name);
if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) continue;
/* get BE for DAI widget and stream */ staticstruct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, struct snd_soc_dapm_widget *widget, int stream)
{ struct snd_soc_pcm_runtime *be; struct snd_soc_dapm_widget *w; struct snd_soc_dai *dai; int i;
dev_dbg(card->dev, "ASoC: find BE for widget %s\n", widget->name);
for_each_card_rtds(card, be) {
if (!be->dai_link->no_pcm) continue;
if (!snd_soc_dpcm_get_substream(be, stream)) continue;
for_each_rtd_dais(be, i, dai) {
w = snd_soc_dai_get_widget(dai, stream);
dev_dbg(card->dev, "ASoC: try BE : %s\n",
w ? w->name : "(not set)");
if (w == widget) return be;
}
}
/* Widget provided is not a BE */ return NULL;
}
int widget_in_list(struct snd_soc_dapm_widget_list *list, struct snd_soc_dapm_widget *widget)
{ struct snd_soc_dapm_widget *w; int i;
for_each_dapm_widgets(list, i, w) if (widget == w) return1;
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 = snd_soc_rtd_to_cpu(fe, 0); int paths;
if (fe->dai_link->num_cpus > 1) return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__);
/* get number of valid DAI paths and their widgets */
paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
fe->card->component_chaining ?
NULL : dpcm_end_walk_at_be);
if (paths > 0)
dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
snd_pcm_direction_name(stream)); elseif (paths == 0)
dev_dbg(fe->dev, "ASoC: %s no valid %s path\n", fe->dai_link->name,
snd_pcm_direction_name(stream));
/* is there a valid DAI widget for this BE */
for_each_rtd_dais(dpcm->be, i, dai) { struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(dai, stream);
staticint dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_)
{ struct snd_soc_dpcm *dpcm; int prune = 0;
/* Destroy any old FE <--> BE connections */
for_each_dpcm_be(fe, stream, dpcm) { if (dpcm_be_is_active(dpcm, stream, *list_)) continue;
dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
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);
prune++;
}
dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune); return prune;
}
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_substream->runtime && !fe->fe_compr) returnnew;
/* Create any new FE <--> BE connections */
for_each_dapm_widgets(list, i, widget) {
switch (widget->id) { case snd_soc_dapm_dai_in: if (stream != SNDRV_PCM_STREAM_PLAYBACK) continue; break; case snd_soc_dapm_dai_out: if (stream != SNDRV_PCM_STREAM_CAPTURE) continue; break; default: continue;
}
/* is there a valid BE rtd for this widget */
be = dpcm_get_be(card, widget, stream); if (!be) {
dev_dbg(fe->dev, "ASoC: no BE found for %s\n",
widget->name); continue;
}
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 (dpcm == last) return;
/* is this op for this BE ? */ 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 %s\n",
snd_pcm_direction_name(stream),
dpcm_state_string(be->dpcm[stream].state)); continue;
}
if (--be->dpcm[stream].users != 0) continue;
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) { if (!do_hw_free) continue;
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;
/* 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;
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",
snd_pcm_direction_name(stream)); continue;
}
/* is this op for this BE ? */ 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 %s\n",
snd_pcm_direction_name(stream),
dpcm_state_string(be->dpcm[stream].state)); continue;
}
if (be->dpcm[stream].users++ != 0) continue;
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) continue;
dev_dbg(be->dev, "ASoC: open %s BE %s\n",
snd_pcm_direction_name(stream), be->dai_link->name);
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 %s\n",
snd_pcm_direction_name(stream),
dpcm_state_string(be->dpcm[stream].state));
for_each_rtd_dais(be, i, dai) { /* *SkipDAIswhichdon'tsupportthecurrentstream *type.Seesoc_pcm_init_runtime_hw()formoredetails
*/ if (!snd_soc_dai_stream_valid(dai, stream)) continue;
pcm = snd_soc_dai_get_pcm_stream(dai, stream);
soc_pcm_hw_update_rate(hw, pcm);
}
}
}
staticint dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, int stream)
{ struct snd_soc_dpcm *dpcm; 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;
/* apply symmetry for FE */
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. */
err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); if (err < 0) goto error;
}
/* disable any enabled and non active backends */
for_each_dpcm_be_rollback(fe, stream, dpcm) {
be = dpcm->be;
be_substream = snd_soc_dpcm_get_substream(be, stream);
if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue;
/* only allow hw_free() if no connected FEs are running */ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) continue;
int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd)
{ struct snd_soc_pcm_runtime *be; bool pause_stop_transition; struct snd_soc_dpcm *dpcm; unsignedlong flags; int ret = 0;
be->dpcm[stream].be_start++; if (be->dpcm[stream].be_start != 1) goto next;
if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_PAUSED)
ret = soc_pcm_trigger(be_substream,
SNDRV_PCM_TRIGGER_PAUSE_RELEASE); else
ret = soc_pcm_trigger(be_substream,
SNDRV_PCM_TRIGGER_START); if (ret) {
be->dpcm[stream].be_start--; goto next;
}
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_RESUME: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) goto next;
be->dpcm[stream].be_start++; if (be->dpcm[stream].be_start != 1) goto next;
ret = soc_pcm_trigger(be_substream, cmd); if (ret) {
be->dpcm[stream].be_start--; goto next;
}
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!be->dpcm[stream].be_start &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) goto next;
if (be->dpcm[stream].be_pause != 0)
ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); else
ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_STOP);
if (ret) { if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
be->dpcm[stream].be_start++; if (pause_stop_transition) {
fe->dpcm[stream].fe_pause = true;
be->dpcm[stream].be_pause++;
} goto next;
}
staticint dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, int cmd, bool fe_first)
{ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int ret;
/* call trigger on the frontend before the backend. */ if (fe_first) {
dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
fe->dai_link->name, cmd);
ret = soc_pcm_trigger(substream, cmd); if (ret < 0) goto end;
ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
} /* call trigger on the frontend after the backend. */ else {
ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); if (ret < 0) goto end;
dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
fe->dai_link->name, cmd);
ret = soc_pcm_trigger(substream, cmd);
}
end: return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd);
}
staticint dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{ 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];
switch (trigger) { case SND_SOC_DPCM_TRIGGER_PRE:
fe_first = true; break; case SND_SOC_DPCM_TRIGGER_POST:
fe_first = false; break; default:
dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
fe->dai_link->name);
ret = -EINVAL; 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: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND:
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; break;
}
/* run the stream event for each BE */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
return soc_pcm_ret(fe, err);
}
staticint dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
{ struct snd_soc_dpcm *dpcm; int ret = 0;
dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n",
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 %s\n",
fe->dai_link->name, dpcm_state_string(fe->dpcm[stream].state));
ret = -EINVAL; goto disconnect;
}
/* startup must always be called for new BEs */
ret = dpcm_be_dai_startup(fe, stream); if (ret < 0) goto disconnect;
/* keep going if FE state is > open */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) return0;
ret = dpcm_be_dai_hw_params(fe, stream); if (ret < 0) goto close;
/* keep going if FE state is > hw_params */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) return0;
ret = dpcm_be_dai_prepare(fe, stream); if (ret < 0) goto hw_free;
/* run the stream event for each BE */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
/* keep going if FE state is > prepare */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) return0;
ret = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_START); if (ret < 0) goto hw_free;
staticint soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, intnew)
{ struct snd_soc_dapm_widget_list *list; int stream; int count, paths;
if (!fe->dai_link->dynamic) return0;
if (fe->dai_link->num_cpus > 1) return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__);
/* only check active links */ if (!snd_soc_dai_active(snd_soc_rtd_to_cpu(fe, 0))) return0;
/* DAPM sync will call this to update DSP paths */
dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n", new ? "new" : "old", fe->dai_link->name);
for_each_pcm_streams(stream) {
/* skip if FE doesn't have playback/capture capability */ 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(snd_soc_rtd_to_cpu(fe, 0), stream) ||
!snd_soc_dai_stream_active(snd_soc_rtd_to_codec(fe, 0), stream)) continue;
/* Called by DAPM mixer/mux changes to update audio routing between PCMs and *anyDAIlinks.
*/ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
{ struct snd_soc_pcm_runtime *fe; int ret = 0;
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); if (ret) goto out;
}
/* bring new paths up */
for_each_card_rtds(card, fe) {
ret = soc_dpcm_fe_runtime_update(fe, 1); if (ret) goto out;
}
snd_soc_dpcm_mutex_lock(fe);
ret = dpcm_fe_dai_shutdown(fe_substream);
dpcm_fe_dai_cleanup(fe_substream);
snd_soc_dpcm_mutex_unlock(fe); return ret;
}
staticint dpcm_fe_dai_open(struct snd_pcm_substream *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);
ret = dpcm_path_get(fe, stream, &list); if (ret < 0) goto open_end;
/* calculate valid and active FE <-> BE dpcms */
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);
/* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd)
{ struct snd_soc_component *component; struct snd_pcm *pcm; int ret = 0, playback = 0, capture = 0; int i;
ret = soc_get_playback_capture(rtd, &playback, &capture); if (ret < 0) return ret;
ret = soc_create_pcm(&pcm, rtd, playback, capture); if (ret < 0) return ret;
/* DAPM dai link stream work */ /* *Currentlynothingtodoforc2clinks *Sincec2clinksareinternalnodesintheDAPMgraphand *don'tinterfacewiththeoutsideworldorapplicationlayer *wedon'thavetodoanyspecialhandlingonclose.
*/ if (!rtd->dai_link->c2c_params)
rtd->close_delayed_work_func = snd_soc_close_delayed_work;
/* get the substream for this BE */
struct snd_pcm_substream *
snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
{
return be->pcm->streams[stream].substream;
}
EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.100 Sekunden
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.