Loading include/sound/compress_driver.h +1 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ struct snd_compr_stream { bool metadata_set; bool next_track; void *private_data; struct snd_soc_pcm_runtime *be; }; /** Loading include/sound/soc-dpcm.h +7 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ struct snd_soc_pcm_runtime; #define DPCM_MAX_BE_USERS 8 /* * Types of runtime_update to perform. e.g. originated from FE PCM ops * or audio route changes triggered by muxes/mixers. Loading Loading @@ -83,6 +84,7 @@ struct snd_soc_dpcm { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_state; #endif int stream; }; /* Loading Loading @@ -145,8 +147,13 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int tream); int dpcm_fe_dai_hw_params_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, struct snd_pcm_hw_params *hw_params, int stream); int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd); int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_fe_dai_prepare_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream); int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event); Loading include/sound/soc.h +14 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/regmap.h> #include <linux/log2.h> #include <linux/async.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/compress_driver.h> Loading Loading @@ -904,6 +905,14 @@ struct snd_soc_dai_link_component { const char *dai_name; }; enum snd_soc_async_ops { ASYNC_DPCM_SND_SOC_OPEN = 1 << 0, ASYNC_DPCM_SND_SOC_CLOSE = 1 << 1, ASYNC_DPCM_SND_SOC_PREPARE = 1 << 2, ASYNC_DPCM_SND_SOC_HW_PARAMS = 1 << 3, ASYNC_DPCM_SND_SOC_FREE = 1 << 4, }; struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ Loading Loading @@ -1008,6 +1017,9 @@ struct snd_soc_dai_link { struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ /* this value determines what all ops can be started asynchronously */ enum snd_soc_async_ops async_ops; }; struct snd_soc_codec_conf { Loading Loading @@ -1164,6 +1176,8 @@ struct snd_soc_pcm_runtime { long pmdown_time; /* err in case of ops failed */ int err_ops; /* runtime devices */ struct snd_pcm *pcm; struct snd_compr *compr; Loading sound/soc/soc-compress.c +154 −14 Original line number Diff line number Diff line Loading @@ -465,6 +465,37 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) return ret; } static void dpcm_be_hw_params_prepare(void *data) { struct snd_compr_stream *cstream = data; struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_pcm_runtime *be = cstream->be; int stream, ret; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; else stream = SNDRV_PCM_STREAM_CAPTURE; ret = dpcm_fe_dai_hw_params_be(fe, be, &fe->dpcm[stream].hw_params, stream); if (ret < 0) { fe->err_ops = ret; return; } ret = dpcm_fe_dai_prepare_be(fe, be, stream); if (ret < 0) { fe->err_ops = ret; return; } } static void dpcm_be_hw_params_prepare_async(void *data, async_cookie_t cookie) { dpcm_be_hw_params_prepare(data); } static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { Loading Loading @@ -538,7 +569,11 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; int ret = 0, __ret, stream; struct snd_soc_pcm_runtime *be_list[DPCM_MAX_BE_USERS]; struct snd_soc_dpcm *dpcm; int ret = 0, __ret, stream, i, j = 0; ASYNC_DOMAIN_EXCLUSIVE(async_domain); if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; Loading Loading @@ -571,6 +606,14 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } if (!(fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_HW_PARAMS)) { /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well * expectation is that platform and machine will configure * everything for this compress path, like configuring pcm * port for codec */ for_each_rtdcom(fe, rtdcom) { component = rtdcom->component; Loading @@ -578,18 +621,115 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, !component->driver->compr_ops->set_params) continue; __ret = component->driver->compr_ops->set_params(cstream, params); __ret = component->driver->compr_ops->set_params( cstream, params); if (__ret < 0) ret = __ret; } if (ret < 0) goto out; if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ret = fe->dai_link->compr_ops->set_params(cstream); if (ret < 0) goto out; } /* * Create an empty hw_params for the BE as the machine * driver must fix this up to match DSP decoder and * ASRC configuration. * I.e. machine driver fixup for compressed BE is * mandatory. */ memset(&fe->dpcm[fe_substream->stream].hw_params, 0, sizeof(struct snd_pcm_hw_params)); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ret = dpcm_be_dai_hw_params(fe, stream); if (ret < 0) goto out; ret = dpcm_be_dai_prepare(fe, stream); if (ret < 0) goto out; } else { /* * Create an empty hw_params for the BE as the machine * driver must fix this up to match DSP decoder and * ASRC configuration. * I.e. machine driver fixup for compressed BE is * mandatory. */ memset(&fe->dpcm[fe_substream->stream].hw_params, 0, sizeof(struct snd_pcm_hw_params)); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; if (be->dai_link->async_ops & ASYNC_DPCM_SND_SOC_HW_PARAMS) { cstream->be = be; async_schedule_domain( dpcm_be_hw_params_prepare_async, cstream, &async_domain); } else { be_list[j++] = be; } } for (i = 0; i < j; i++) { cstream->be = be_list[i]; dpcm_be_hw_params_prepare(cstream); } if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) { ret = cpu_dai->driver->cops->set_params( cstream, params, cpu_dai); if (ret < 0) goto exit; } /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well * expectation is that platform and machine will configure * everything this compress path, like configuring pcm port * for codec */ for_each_rtdcom(fe, rtdcom) { component = rtdcom->component; if (!component->driver->compr_ops || !component->driver->compr_ops->set_params) continue; __ret = component->driver->compr_ops->set_params( cstream, params); if (__ret < 0) ret = __ret; } if (ret < 0) goto exit; dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ret = fe->dai_link->compr_ops->set_params(cstream); if (ret < 0) goto exit; } exit: async_synchronize_full_domain(&async_domain); if (fe->err_ops < 0 || ret < 0) goto out; } dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; Loading sound/soc/soc-pcm.c +221 −9 Original line number Diff line number Diff line Loading @@ -2217,6 +2217,81 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) return 0; } int dpcm_fe_dai_hw_params_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, struct snd_pcm_hw_params *params, int stream) { int ret; struct snd_soc_dpcm *dpcm; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) return 0; /* only allow hw_params() if no connected FEs are running */ if (!snd_soc_dpcm_can_be_params(fe, be, stream)) return 0; if ((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_HW_FREE)) return 0; dev_dbg(be->dev, "ASoC: hw_params BE %s\n", fe->dai_link->name); /* perform any hw_params fixups */ if (be->dai_link->be_hw_params_fixup) { ret = be->dai_link->be_hw_params_fixup(be, params); if (ret < 0) { dev_err(be->dev, "ASoC: hw_params BE fixup failed %d\n", ret); goto unwind; } } ret = soc_pcm_hw_params(be_substream, params); if (ret < 0) { dev_err(be->dev, "ASoC: hw_params BE failed %d\n", ret); goto unwind; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; return 0; unwind: /* disable any enabled and non active backends */ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; /* only allow hw_free() if no connected FEs are running */ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) continue; if ((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_HW_FREE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) continue; soc_pcm_hw_free(be_substream); } return ret; } int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; Loading Loading @@ -2517,6 +2592,35 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } int dpcm_fe_dai_prepare_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream) { struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); int ret = 0; /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) return 0; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) return 0; dev_dbg(be->dev, "ASoC: prepare BE %s\n", fe->dai_link->name); ret = soc_pcm_prepare(be_substream); if (ret < 0) { dev_err(be->dev, "ASoC: backend prepare failed %d\n", ret); return ret; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; return ret; } static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *fe = substream->private_data; Loading Loading @@ -2569,13 +2673,90 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) return ret; } static void dpcm_be_async_prepare(void *data, async_cookie_t cookie) { struct snd_soc_dpcm *dpcm = data; struct snd_soc_pcm_runtime *be = dpcm->be; int stream = dpcm->stream; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); int ret; dev_dbg(be->dev, "%s ASoC: prepare BE %s\n", __func__, dpcm->fe->dai_link->name); ret = soc_pcm_prepare(be_substream); if (ret < 0) { be->err_ops = ret; dev_err(be->dev, "ASoC: backend prepare failed %d\n", ret); return; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; } void dpcm_be_dai_prepare_async(struct snd_soc_pcm_runtime *fe, int stream, struct async_domain *domain) { struct snd_soc_dpcm *dpcm; struct snd_soc_dpcm *dpcm_async[DPCM_MAX_BE_USERS]; int i = 0, j; list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; be->err_ops = 0; /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) continue; /* does this BE support async op ?*/ if ((fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE) && (be->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) { dpcm->stream = stream; async_schedule_domain(dpcm_be_async_prepare, dpcm, domain); } else { dpcm_async[i++] = dpcm; } } for (j = 0; j < i; j++) { struct snd_soc_dpcm *dpcm = dpcm_async[j]; struct snd_soc_pcm_runtime *be = dpcm->be; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); int ret; dev_dbg(be->dev, "ASoC: prepare BE %s\n", dpcm->fe->dai_link->name); ret = soc_pcm_prepare(be_substream); if (ret < 0) { dev_err(be->dev, "ASoC: backend prepare failed %d\n", ret); be->err_ops = ret; return; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; } } static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *fe = substream->private_data; struct snd_soc_dpcm *dpcm; int stream = substream->stream, ret = 0; ASYNC_DOMAIN_EXCLUSIVE(async_domain); mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); fe->err_ops = 0; dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); Loading @@ -2588,17 +2769,48 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) goto out; } if (!(fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) { ret = dpcm_be_dai_prepare(fe, substream->stream); if (ret < 0) goto out; /* call prepare on the frontend */ ret = soc_pcm_prepare(substream); if (ret < 0) { dev_err(fe->dev, "ASoC: prepare FE %s failed\n", fe->dai_link->name); goto out; } } else { dpcm_be_dai_prepare_async(fe, substream->stream, &async_domain); /* call prepare on the frontend */ ret = soc_pcm_prepare(substream); if (ret < 0) { fe->err_ops = ret; dev_err(fe->dev, "ASoC: prepare FE %s failed\n", fe->dai_link->name); } async_synchronize_full_domain(&async_domain); /* check if any BE failed */ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; if (be->err_ops < 0) { ret = be->err_ops; goto out; } } /* check if FE failed */ if (fe->err_ops < 0) { ret = fe->err_ops; goto out; } } /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); Loading Loading
include/sound/compress_driver.h +1 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ struct snd_compr_stream { bool metadata_set; bool next_track; void *private_data; struct snd_soc_pcm_runtime *be; }; /** Loading
include/sound/soc-dpcm.h +7 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ struct snd_soc_pcm_runtime; #define DPCM_MAX_BE_USERS 8 /* * Types of runtime_update to perform. e.g. originated from FE PCM ops * or audio route changes triggered by muxes/mixers. Loading Loading @@ -83,6 +84,7 @@ struct snd_soc_dpcm { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_state; #endif int stream; }; /* Loading Loading @@ -145,8 +147,13 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int tream); int dpcm_fe_dai_hw_params_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, struct snd_pcm_hw_params *hw_params, int stream); int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd); int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_fe_dai_prepare_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream); int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event); Loading
include/sound/soc.h +14 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/regmap.h> #include <linux/log2.h> #include <linux/async.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/compress_driver.h> Loading Loading @@ -904,6 +905,14 @@ struct snd_soc_dai_link_component { const char *dai_name; }; enum snd_soc_async_ops { ASYNC_DPCM_SND_SOC_OPEN = 1 << 0, ASYNC_DPCM_SND_SOC_CLOSE = 1 << 1, ASYNC_DPCM_SND_SOC_PREPARE = 1 << 2, ASYNC_DPCM_SND_SOC_HW_PARAMS = 1 << 3, ASYNC_DPCM_SND_SOC_FREE = 1 << 4, }; struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ Loading Loading @@ -1008,6 +1017,9 @@ struct snd_soc_dai_link { struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ /* this value determines what all ops can be started asynchronously */ enum snd_soc_async_ops async_ops; }; struct snd_soc_codec_conf { Loading Loading @@ -1164,6 +1176,8 @@ struct snd_soc_pcm_runtime { long pmdown_time; /* err in case of ops failed */ int err_ops; /* runtime devices */ struct snd_pcm *pcm; struct snd_compr *compr; Loading
sound/soc/soc-compress.c +154 −14 Original line number Diff line number Diff line Loading @@ -465,6 +465,37 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) return ret; } static void dpcm_be_hw_params_prepare(void *data) { struct snd_compr_stream *cstream = data; struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_pcm_runtime *be = cstream->be; int stream, ret; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; else stream = SNDRV_PCM_STREAM_CAPTURE; ret = dpcm_fe_dai_hw_params_be(fe, be, &fe->dpcm[stream].hw_params, stream); if (ret < 0) { fe->err_ops = ret; return; } ret = dpcm_fe_dai_prepare_be(fe, be, stream); if (ret < 0) { fe->err_ops = ret; return; } } static void dpcm_be_hw_params_prepare_async(void *data, async_cookie_t cookie) { dpcm_be_hw_params_prepare(data); } static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { Loading Loading @@ -538,7 +569,11 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; int ret = 0, __ret, stream; struct snd_soc_pcm_runtime *be_list[DPCM_MAX_BE_USERS]; struct snd_soc_dpcm *dpcm; int ret = 0, __ret, stream, i, j = 0; ASYNC_DOMAIN_EXCLUSIVE(async_domain); if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; Loading Loading @@ -571,6 +606,14 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } if (!(fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_HW_PARAMS)) { /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well * expectation is that platform and machine will configure * everything for this compress path, like configuring pcm * port for codec */ for_each_rtdcom(fe, rtdcom) { component = rtdcom->component; Loading @@ -578,18 +621,115 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, !component->driver->compr_ops->set_params) continue; __ret = component->driver->compr_ops->set_params(cstream, params); __ret = component->driver->compr_ops->set_params( cstream, params); if (__ret < 0) ret = __ret; } if (ret < 0) goto out; if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ret = fe->dai_link->compr_ops->set_params(cstream); if (ret < 0) goto out; } /* * Create an empty hw_params for the BE as the machine * driver must fix this up to match DSP decoder and * ASRC configuration. * I.e. machine driver fixup for compressed BE is * mandatory. */ memset(&fe->dpcm[fe_substream->stream].hw_params, 0, sizeof(struct snd_pcm_hw_params)); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ret = dpcm_be_dai_hw_params(fe, stream); if (ret < 0) goto out; ret = dpcm_be_dai_prepare(fe, stream); if (ret < 0) goto out; } else { /* * Create an empty hw_params for the BE as the machine * driver must fix this up to match DSP decoder and * ASRC configuration. * I.e. machine driver fixup for compressed BE is * mandatory. */ memset(&fe->dpcm[fe_substream->stream].hw_params, 0, sizeof(struct snd_pcm_hw_params)); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; if (be->dai_link->async_ops & ASYNC_DPCM_SND_SOC_HW_PARAMS) { cstream->be = be; async_schedule_domain( dpcm_be_hw_params_prepare_async, cstream, &async_domain); } else { be_list[j++] = be; } } for (i = 0; i < j; i++) { cstream->be = be_list[i]; dpcm_be_hw_params_prepare(cstream); } if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) { ret = cpu_dai->driver->cops->set_params( cstream, params, cpu_dai); if (ret < 0) goto exit; } /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well * expectation is that platform and machine will configure * everything this compress path, like configuring pcm port * for codec */ for_each_rtdcom(fe, rtdcom) { component = rtdcom->component; if (!component->driver->compr_ops || !component->driver->compr_ops->set_params) continue; __ret = component->driver->compr_ops->set_params( cstream, params); if (__ret < 0) ret = __ret; } if (ret < 0) goto exit; dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ret = fe->dai_link->compr_ops->set_params(cstream); if (ret < 0) goto exit; } exit: async_synchronize_full_domain(&async_domain); if (fe->err_ops < 0 || ret < 0) goto out; } dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; Loading
sound/soc/soc-pcm.c +221 −9 Original line number Diff line number Diff line Loading @@ -2217,6 +2217,81 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) return 0; } int dpcm_fe_dai_hw_params_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, struct snd_pcm_hw_params *params, int stream) { int ret; struct snd_soc_dpcm *dpcm; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) return 0; /* only allow hw_params() if no connected FEs are running */ if (!snd_soc_dpcm_can_be_params(fe, be, stream)) return 0; if ((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_HW_FREE)) return 0; dev_dbg(be->dev, "ASoC: hw_params BE %s\n", fe->dai_link->name); /* perform any hw_params fixups */ if (be->dai_link->be_hw_params_fixup) { ret = be->dai_link->be_hw_params_fixup(be, params); if (ret < 0) { dev_err(be->dev, "ASoC: hw_params BE fixup failed %d\n", ret); goto unwind; } } ret = soc_pcm_hw_params(be_substream, params); if (ret < 0) { dev_err(be->dev, "ASoC: hw_params BE failed %d\n", ret); goto unwind; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; return 0; unwind: /* disable any enabled and non active backends */ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; /* only allow hw_free() if no connected FEs are running */ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) continue; if ((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_HW_FREE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) continue; soc_pcm_hw_free(be_substream); } return ret; } int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; Loading Loading @@ -2517,6 +2592,35 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } int dpcm_fe_dai_prepare_be(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream) { struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); int ret = 0; /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) return 0; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) return 0; dev_dbg(be->dev, "ASoC: prepare BE %s\n", fe->dai_link->name); ret = soc_pcm_prepare(be_substream); if (ret < 0) { dev_err(be->dev, "ASoC: backend prepare failed %d\n", ret); return ret; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; return ret; } static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *fe = substream->private_data; Loading Loading @@ -2569,13 +2673,90 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) return ret; } static void dpcm_be_async_prepare(void *data, async_cookie_t cookie) { struct snd_soc_dpcm *dpcm = data; struct snd_soc_pcm_runtime *be = dpcm->be; int stream = dpcm->stream; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); int ret; dev_dbg(be->dev, "%s ASoC: prepare BE %s\n", __func__, dpcm->fe->dai_link->name); ret = soc_pcm_prepare(be_substream); if (ret < 0) { be->err_ops = ret; dev_err(be->dev, "ASoC: backend prepare failed %d\n", ret); return; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; } void dpcm_be_dai_prepare_async(struct snd_soc_pcm_runtime *fe, int stream, struct async_domain *domain) { struct snd_soc_dpcm *dpcm; struct snd_soc_dpcm *dpcm_async[DPCM_MAX_BE_USERS]; int i = 0, j; list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; be->err_ops = 0; /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) continue; /* does this BE support async op ?*/ if ((fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE) && (be->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) { dpcm->stream = stream; async_schedule_domain(dpcm_be_async_prepare, dpcm, domain); } else { dpcm_async[i++] = dpcm; } } for (j = 0; j < i; j++) { struct snd_soc_dpcm *dpcm = dpcm_async[j]; struct snd_soc_pcm_runtime *be = dpcm->be; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); int ret; dev_dbg(be->dev, "ASoC: prepare BE %s\n", dpcm->fe->dai_link->name); ret = soc_pcm_prepare(be_substream); if (ret < 0) { dev_err(be->dev, "ASoC: backend prepare failed %d\n", ret); be->err_ops = ret; return; } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; } } static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *fe = substream->private_data; struct snd_soc_dpcm *dpcm; int stream = substream->stream, ret = 0; ASYNC_DOMAIN_EXCLUSIVE(async_domain); mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); fe->err_ops = 0; dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); Loading @@ -2588,17 +2769,48 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) goto out; } if (!(fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) { ret = dpcm_be_dai_prepare(fe, substream->stream); if (ret < 0) goto out; /* call prepare on the frontend */ ret = soc_pcm_prepare(substream); if (ret < 0) { dev_err(fe->dev, "ASoC: prepare FE %s failed\n", fe->dai_link->name); goto out; } } else { dpcm_be_dai_prepare_async(fe, substream->stream, &async_domain); /* call prepare on the frontend */ ret = soc_pcm_prepare(substream); if (ret < 0) { fe->err_ops = ret; dev_err(fe->dev, "ASoC: prepare FE %s failed\n", fe->dai_link->name); } async_synchronize_full_domain(&async_domain); /* check if any BE failed */ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { struct snd_soc_pcm_runtime *be = dpcm->be; if (be->err_ops < 0) { ret = be->err_ops; goto out; } } /* check if FE failed */ if (fe->err_ops < 0) { ret = fe->err_ops; goto out; } } /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); Loading