diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/soc-compress.c | 154 | ||||
-rw-r--r-- | sound/soc/soc-pcm.c | 230 |
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 */ |