summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/soc-compress.c154
-rw-r--r--sound/soc/soc-pcm.c230
2 files changed, 351 insertions, 33 deletions
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 4180aa36c788..c64f230db4e1 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -408,13 +408,48 @@ err:
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_fe(struct snd_compr_stream *cstream,
struct snd_compr_params *params)
{
struct snd_soc_pcm_runtime *fe = cstream->private_data;
struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream;
struct snd_soc_platform *platform = fe->platform;
- int ret = 0, stream;
+ struct snd_soc_pcm_runtime *be_list[DPCM_MAX_BE_USERS];
+ struct snd_soc_dpcm *dpcm;
+ int ret = 0, stream, i, j = 0;
+
+ ASYNC_DOMAIN_EXCLUSIVE(async_domain);
if (cstream->direction == SND_COMPRESS_PLAYBACK)
stream = SNDRV_PCM_STREAM_PLAYBACK;
@@ -423,36 +458,107 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
- ret = platform->driver->compr_ops->set_params(cstream, params);
+ 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
+ */
+ if (platform->driver->compr_ops &&
+ platform->driver->compr_ops->set_params) {
+ ret = platform->driver->compr_ops->set_params(cstream,
+ params);
+ if (ret < 0)
+ goto out;
+ }
+
+ 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;
- }
- if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) {
- ret = fe->dai_link->compr_ops->set_params(cstream);
+ ret = dpcm_be_dai_prepare(fe, stream);
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;
+ } 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);
+ }
+ /* 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
+ */
+ if (platform->driver->compr_ops &&
+ platform->driver->compr_ops->set_params) {
+ ret = platform->driver->compr_ops->set_params(cstream,
+ params);
+ if (ret < 0)
+ goto exit;
+ }
- ret = dpcm_be_dai_prepare(fe, stream);
- if (ret < 0)
- goto out;
+ 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;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 2373c065fa0c..6bc2c887de4a 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1878,6 +1878,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;
@@ -2178,6 +2253,35 @@ out:
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;
@@ -2229,13 +2333,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);
@@ -2248,16 +2429,47 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
goto out;
}
- ret = dpcm_be_dai_prepare(fe, substream->stream);
- if (ret < 0)
- 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) {
- dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
- fe->dai_link->name);
- goto out;
+ /* 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 */