[alsa-devel] [PATCH 2/2] ASoC: compress: Add suport for DPCM into compressed audio

Takashi Iwai tiwai at suse.de
Thu Jan 16 16:59:30 CET 2014


At Wed, 15 Jan 2014 16:51:57 +0000,
Liam Girdwood wrote:
> 
> Currently compressed audio streams are statically routed from the /dev
> to the DAI link. Some DSPs can route compressed data to multiple BE DAIs
> like they do for PCM data.
> 
> Add support to allow dynamically routed compressed streams using the existing
> DPCM infrastructure. This patch adds special FE versions of the compressed ops
> that work out the runtime routing.
> 
> Signed-off-by: Liam Girdwood <liam.r.girdwood at linux.intel.com>
> ---
>  include/sound/compress_driver.h |   2 +
>  include/sound/soc.h             |   1 +
>  sound/soc/soc-compress.c        | 301 +++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 303 insertions(+), 1 deletion(-)
> 
> diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> index ae6c3b8..2b2d2fb 100644
> --- a/include/sound/compress_driver.h
> +++ b/include/sound/compress_driver.h
> @@ -32,6 +32,7 @@
>  #include <sound/pcm.h>
>  
>  struct snd_compr_ops;
> +struct snd_pcm_substream;
>  
>  /**
>   * struct snd_compr_runtime: runtime stream description
> @@ -59,6 +60,7 @@ struct snd_compr_runtime {
>  	u64 total_bytes_available;
>  	u64 total_bytes_transferred;
>  	wait_queue_head_t sleep;
> +	struct snd_pcm_substream *fe_substream;
>  	void *private_data;
>  };
>  
> diff --git a/include/sound/soc.h b/include/sound/soc.h
> index 0a600b8d..e0233df 100644
> --- a/include/sound/soc.h
> +++ b/include/sound/soc.h
> @@ -1035,6 +1035,7 @@ struct snd_soc_pcm_runtime {
>  
>  	/* Dynamic PCM BE runtime data */
>  	struct snd_soc_dpcm_runtime dpcm[2];
> +	int fe_compr;
>  
>  	long pmdown_time;
>  	unsigned char pop_wait:1;
> diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
> index 53c9ecd..bcd4954 100644
> --- a/sound/soc/soc-compress.c
> +++ b/sound/soc/soc-compress.c
> @@ -24,6 +24,7 @@
>  #include <sound/compress_driver.h>
>  #include <sound/soc.h>
>  #include <sound/initval.h>
> +#include <sound/soc-dpcm.h>
>  
>  static int soc_compr_open(struct snd_compr_stream *cstream)
>  {
> @@ -75,6 +76,98 @@ out:
>  	return ret;
>  }
>  
> +static int soc_compr_open_fe(struct snd_compr_stream *cstream)
> +{
> +	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;
> +	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
> +	struct snd_soc_dai *codec_dai = fe->codec_dai;
> +	struct snd_soc_dpcm *dpcm;
> +	struct snd_soc_dapm_widget_list *list;
> +	int stream;
> +	int ret = 0;
> +
> +	if (cstream->direction == SND_COMPRESS_PLAYBACK)
> +		stream = SNDRV_PCM_STREAM_PLAYBACK;
> +	else
> +		stream = SNDRV_PCM_STREAM_CAPTURE;
> +
> +	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
> +
> +	if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
> +		ret = platform->driver->compr_ops->open(cstream);
> +		if (ret < 0) {
> +			pr_err("compress asoc: can't open platform %s\n", platform->name);
> +			goto out;
> +		}
> +	}
> +
> +	if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) {
> +		ret = fe->dai_link->compr_ops->startup(cstream);
> +		if (ret < 0) {
> +			pr_err("compress asoc: %s startup failed\n", fe->dai_link->name);
> +			goto machine_err;
> +		}
> +	}
> +
> +	fe->dpcm[stream].runtime = fe_substream->runtime;
> +
> +	if (dpcm_path_get(fe, stream, &list) <= 0) {
> +		dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
> +			fe->dai_link->name, stream ? "capture" : "playback");
> +	}
> +
> +	/* calculate valid and active FE <-> BE dpcms */
> +	dpcm_process_paths(fe, stream, &list, 1);
> +
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
> +
> +	ret = dpcm_be_dai_startup(fe, stream);
> +	if (ret < 0) {
> +		/* clean up all links */
> +		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
> +			dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
> +
> +		dpcm_be_disconnect(fe, stream);
> +		fe->dpcm[stream].runtime = NULL;
> +		goto fe_err;
> +	}
> +
> +	dpcm_clear_pending_state(fe, stream);
> +	dpcm_path_put(&list);
> +
> +	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
> +
> +	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
> +		cpu_dai->playback_active++;
> +		codec_dai->playback_active++;
> +	} else {
> +		cpu_dai->capture_active++;
> +		codec_dai->capture_active++;
> +	}
> +
> +	cpu_dai->active++;
> +	codec_dai->active++;
> +	fe->codec->active++;
> +
> +	mutex_unlock(&fe->card->mutex);
> +
> +	return 0;
> +
> +fe_err:
> +	if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown)
> +		fe->dai_link->compr_ops->shutdown(cstream);
> +machine_err:
> +	if (platform->driver->compr_ops && platform->driver->compr_ops->free)
> +		platform->driver->compr_ops->free(cstream);
> +out:
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
> +	mutex_unlock(&fe->card->mutex);
> +	return ret;
> +}
> +
>  /*
>   * Power down the audio subsystem pmdown_time msecs after close is called.
>   * This is to ensure there are no pops or clicks in between any music tracks
> @@ -164,6 +257,65 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
>  	return 0;
>  }
>  
> +static int soc_compr_free_fe(struct snd_compr_stream *cstream)
> +{
> +	struct snd_soc_pcm_runtime *fe = cstream->private_data;
> +	struct snd_soc_platform *platform = fe->platform;
> +	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
> +	struct snd_soc_dai *codec_dai = fe->codec_dai;
> +	struct snd_soc_dpcm *dpcm;
> +	int stream, ret;
> +
> +	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
> +
> +	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
> +		stream = SNDRV_PCM_STREAM_PLAYBACK;
> +		cpu_dai->playback_active--;
> +		codec_dai->playback_active--;
> +	} else {
> +		stream = SNDRV_PCM_STREAM_CAPTURE;
> +		cpu_dai->capture_active--;
> +		codec_dai->capture_active--;
> +	}
> +
> +	cpu_dai->active--;
> +	codec_dai->active--;
> +	fe->codec->active--;
> +
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
> +
> +	ret = dpcm_be_dai_hw_free(fe, stream);
> +	if (ret < 0)
> +		dev_err(fe->dev, "compressed hw_free failed %d\n", ret);
> +
> +	ret = dpcm_be_dai_shutdown(fe, stream);
> +
> +	/* mark FE's links ready to prune */
> +	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
> +		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
> +
> +	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
> +	else
> +		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
> +
> +	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
> +
> +	dpcm_be_disconnect(fe, stream);
> +
> +	fe->dpcm[stream].runtime = NULL;
> +
> +	if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown)
> +		fe->dai_link->compr_ops->shutdown(cstream);
> +
> +	if (platform->driver->compr_ops && platform->driver->compr_ops->free)
> +		platform->driver->compr_ops->free(cstream);
> +
> +	mutex_unlock(&fe->card->mutex);
> +	return 0;
> +}
> +
>  static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
>  {
>  
> @@ -194,6 +346,59 @@ out:
>  	return ret;
>  }
>  
> +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
> +{
> +	struct snd_soc_pcm_runtime *fe = cstream->private_data;
> +	struct snd_soc_platform *platform = fe->platform;
> +	int ret = 0, stream;
> +
> +	if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
> +		cmd == SND_COMPR_TRIGGER_DRAIN) {
> +
> +		if (platform->driver->compr_ops &&
> +			platform->driver->compr_ops->trigger)
> +		return platform->driver->compr_ops->trigger(cstream, cmd);
> +	}
> +
> +	if (cstream->direction == SND_COMPRESS_PLAYBACK)
> +		stream = SNDRV_PCM_STREAM_PLAYBACK;
> +	else
> +		stream = SNDRV_PCM_STREAM_CAPTURE;
> +
> +
> +	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
> +
> +	if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
> +		ret = platform->driver->compr_ops->trigger(cstream, cmd);
> +		if (ret < 0)
> +			goto out;
> +	}
> +
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
> +
> +	ret = dpcm_be_dai_trigger(fe, stream, cmd);
> +
> +	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;
> +	}
> +
> +out:
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
> +	mutex_unlock(&fe->card->mutex);
> +	return ret;
> +}
> +
>  static int soc_compr_set_params(struct snd_compr_stream *cstream,
>  					struct snd_compr_params *params)
>  {
> @@ -241,6 +446,64 @@ err:
>  	return ret;
>  }
>  
> +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;
> +	struct snd_pcm_hw_params *hw_params;
> +	int ret = 0, stream;
> +
> +	if (cstream->direction == SND_COMPRESS_PLAYBACK)
> +		stream = SNDRV_PCM_STREAM_PLAYBACK;
> +	else
> +		stream = SNDRV_PCM_STREAM_CAPTURE;
> +
> +	hw_params = kzalloc(sizeof(*hw_params), GFP_KERNEL);
> +	if (hw_params == NULL)
> +		return -ENOMEM;

I see no kfree() for this.


> +	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 (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;
> +	}
> +
> +	memcpy(&fe->dpcm[fe_substream->stream].hw_params, params,
> +			sizeof(struct snd_pcm_hw_params));

Does this really work?  params is snd_compr_params and hw_parmas is
snd_pcm_hw_params, isn't it?

For avoiding such a mistake, you can avoid memcpy:
	*fe->dpcm[fe_substream->stream].hw_params = *params;


Takashi


> +
> +	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;
> +
> +	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
> +	else
> +		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
> +
> +	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
> +
> +out:
> +	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
> +	mutex_unlock(&fe->card->mutex);
> +	return ret;
> +}
> +
>  static int soc_compr_get_params(struct snd_compr_stream *cstream,
>  					struct snd_codec *params)
>  {
> @@ -360,6 +623,7 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream,
>  
>  	return ret;
>  }
> +
>  /* ASoC Compress operations */
>  static struct snd_compr_ops soc_compr_ops = {
>  	.open		= soc_compr_open,
> @@ -375,6 +639,21 @@ static struct snd_compr_ops soc_compr_ops = {
>  	.get_codec_caps = soc_compr_get_codec_caps
>  };
>  
> +/* ASoC Dynamic Compress operations */
> +static struct snd_compr_ops soc_compr_dyn_ops = {
> +	.open		= soc_compr_open_fe,
> +	.free		= soc_compr_free_fe,
> +	.set_params	= soc_compr_set_params_fe,
> +	.get_params	= soc_compr_get_params,
> +	.set_metadata   = soc_compr_set_metadata,
> +	.get_metadata	= soc_compr_get_metadata,
> +	.trigger	= soc_compr_trigger_fe,
> +	.pointer	= soc_compr_pointer,
> +	.ack		= soc_compr_ack,
> +	.get_caps	= soc_compr_get_caps,
> +	.get_codec_caps = soc_compr_get_codec_caps
> +};
> +
>  /* create a new compress */
>  int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
>  {
> @@ -383,6 +662,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
>  	struct snd_soc_dai *codec_dai = rtd->codec_dai;
>  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
>  	struct snd_compr *compr;
> +	struct snd_pcm *be_pcm;
>  	char new_name[64];
>  	int ret = 0, direction = 0;
>  
> @@ -410,7 +690,26 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
>  		ret = -ENOMEM;
>  		goto compr_err;
>  	}
> -	memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
> +
> +	if (rtd->dai_link->dynamic) {
> +		snprintf(new_name, sizeof(new_name), "(%s)",
> +			rtd->dai_link->stream_name);
> +
> +		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
> +				1, 0, &be_pcm);
> +		if (ret < 0) {
> +			dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n",
> +				rtd->dai_link->name);
> +			goto compr_err;
> +		}
> +
> +		rtd->pcm = be_pcm;
> +		rtd->fe_compr = 1;
> +		be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
> +		be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
> +		memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops));
> +	} else
> +		memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
>  
>  	/* Add copy callback for not memory mapped DSPs */
>  	if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
> -- 
> 1.8.3.2
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 


More information about the Alsa-devel mailing list