diff options
| -rw-r--r-- | include/sound/soc-dai.h | 6 | ||||
| -rw-r--r-- | include/sound/soc.h | 2 | ||||
| -rw-r--r-- | sound/soc/soc-pcm.c | 158 | 
3 files changed, 138 insertions, 28 deletions
| diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 800c101bb096..243d3b689699 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -220,6 +220,8 @@ struct snd_soc_dai_driver {  	struct snd_soc_pcm_stream capture;  	struct snd_soc_pcm_stream playback;  	unsigned int symmetric_rates:1; +	unsigned int symmetric_channels:1; +	unsigned int symmetric_samplebits:1;  	/* probe ordering - for components with runtime dependencies */  	int probe_order; @@ -244,6 +246,8 @@ struct snd_soc_dai {  	unsigned int capture_active:1;		/* stream is in use */  	unsigned int playback_active:1;		/* stream is in use */  	unsigned int symmetric_rates:1; +	unsigned int symmetric_channels:1; +	unsigned int symmetric_samplebits:1;  	struct snd_pcm_runtime *runtime;  	unsigned int active;  	unsigned char probed:1; @@ -258,6 +262,8 @@ struct snd_soc_dai {  	/* Symmetry data - only valid if symmetry is being enforced */  	unsigned int rate; +	unsigned int channels; +	unsigned int sample_bits;  	/* parent platform/codec */  	struct snd_soc_platform *platform; diff --git a/include/sound/soc.h b/include/sound/soc.h index 1f741cb24f33..1cda7d343d16 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -879,6 +879,8 @@ struct snd_soc_dai_link {  	/* Symmetry requirements */  	unsigned int symmetric_rates:1; +	unsigned int symmetric_channels:1; +	unsigned int symmetric_samplebits:1;  	/* Do not create a PCM for this DAI link (Backend link) */  	unsigned int no_pcm:1; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 42782c01e413..f3592f142832 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -84,35 +84,117 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,  	struct snd_soc_pcm_runtime *rtd = substream->private_data;  	int ret; -	if (!soc_dai->driver->symmetric_rates && -	    !rtd->dai_link->symmetric_rates) -		return 0; +	if (soc_dai->rate && (soc_dai->driver->symmetric_rates || +				rtd->dai_link->symmetric_rates)) { +		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", +				soc_dai->rate); + +		ret = snd_pcm_hw_constraint_minmax(substream->runtime, +						SNDRV_PCM_HW_PARAM_RATE, +						soc_dai->rate, soc_dai->rate); +		if (ret < 0) { +			dev_err(soc_dai->dev, +				"ASoC: Unable to apply rate constraint: %d\n", +				ret); +			return ret; +		} +	} -	/* This can happen if multiple streams are starting simultaneously - -	 * the second can need to get its constraints before the first has -	 * picked a rate.  Complain and allow the application to carry on. -	 */ -	if (!soc_dai->rate) { -		dev_warn(soc_dai->dev, -			 "ASoC: Not enforcing symmetric_rates due to race\n"); -		return 0; +	if (soc_dai->channels && (soc_dai->driver->symmetric_channels || +				rtd->dai_link->symmetric_channels)) { +		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", +				soc_dai->channels); + +		ret = snd_pcm_hw_constraint_minmax(substream->runtime, +						SNDRV_PCM_HW_PARAM_CHANNELS, +						soc_dai->channels, +						soc_dai->channels); +		if (ret < 0) { +			dev_err(soc_dai->dev, +				"ASoC: Unable to apply channel symmetry constraint: %d\n", +				ret); +			return ret; +		}  	} -	dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate); +	if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || +				rtd->dai_link->symmetric_samplebits)) { +		dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", +				soc_dai->sample_bits); -	ret = snd_pcm_hw_constraint_minmax(substream->runtime, -					   SNDRV_PCM_HW_PARAM_RATE, -					   soc_dai->rate, soc_dai->rate); -	if (ret < 0) { -		dev_err(soc_dai->dev, -			"ASoC: Unable to apply rate symmetry constraint: %d\n", -			ret); -		return ret; +		ret = snd_pcm_hw_constraint_minmax(substream->runtime, +						SNDRV_PCM_HW_PARAM_SAMPLE_BITS, +						soc_dai->sample_bits, +						soc_dai->sample_bits); +		if (ret < 0) { +			dev_err(soc_dai->dev, +				"ASoC: Unable to apply sample bits symmetry constraint: %d\n", +				ret); +			return ret; +		}  	}  	return 0;  } +static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, +				struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +	struct snd_soc_dai *codec_dai = rtd->codec_dai; +	unsigned int rate, channels, sample_bits, symmetry; + +	rate = params_rate(params); +	channels = params_channels(params); +	sample_bits = snd_pcm_format_physical_width(params_format(params)); + +	/* reject unmatched parameters when applying symmetry */ +	symmetry = cpu_dai->driver->symmetric_rates || +		codec_dai->driver->symmetric_rates || +		rtd->dai_link->symmetric_rates; +	if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { +		dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", +				cpu_dai->rate, rate); +		return -EINVAL; +	} + +	symmetry = cpu_dai->driver->symmetric_channels || +		codec_dai->driver->symmetric_channels || +		rtd->dai_link->symmetric_channels; +	if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { +		dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", +				cpu_dai->channels, channels); +		return -EINVAL; +	} + +	symmetry = cpu_dai->driver->symmetric_samplebits || +		codec_dai->driver->symmetric_samplebits || +		rtd->dai_link->symmetric_samplebits; +	if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { +		dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", +				cpu_dai->sample_bits, sample_bits); +		return -EINVAL; +	} + +	return 0; +} + +static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; +	struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver; +	struct snd_soc_dai_link *link = rtd->dai_link; + +	return cpu_driver->symmetric_rates || codec_driver->symmetric_rates || +		link->symmetric_rates || cpu_driver->symmetric_channels || +		codec_driver->symmetric_channels || link->symmetric_channels || +		cpu_driver->symmetric_samplebits || +		codec_driver->symmetric_samplebits || +		link->symmetric_samplebits; +} +  /*   * List of sample sizes that might go over the bus for parameter   * application.  There ought to be a wildcard sample size for things @@ -242,6 +324,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)  			&cpu_dai_drv->capture);  	} +	if (soc_pcm_has_symmetry(substream)) +		runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; +  	ret = -EINVAL;  	snd_pcm_limit_hw_rates(runtime);  	if (!runtime->hw.rates) { @@ -383,13 +468,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)  	codec_dai->active--;  	codec->active--; -	/* clear the corresponding DAIs rate when inactive */ -	if (!cpu_dai->active) -		cpu_dai->rate = 0; - -	if (!codec_dai->active) -		codec_dai->rate = 0; -  	/* Muting the DAC suppresses artifacts caused during digital  	 * shutdown, for example from stopping clocks.  	 */ @@ -525,6 +603,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); +	ret = soc_pcm_params_symmetry(substream, params); +	if (ret) +		goto out; +  	if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {  		ret = rtd->dai_link->ops->hw_params(substream, params);  		if (ret < 0) { @@ -561,9 +643,16 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,  		}  	} -	/* store the rate for each DAIs */ +	/* store the parameters for each DAIs */  	cpu_dai->rate = params_rate(params); +	cpu_dai->channels = params_channels(params); +	cpu_dai->sample_bits = +		snd_pcm_format_physical_width(params_format(params)); +  	codec_dai->rate = params_rate(params); +	codec_dai->channels = params_channels(params); +	codec_dai->sample_bits = +		snd_pcm_format_physical_width(params_format(params));  out:  	mutex_unlock(&rtd->pcm_mutex); @@ -598,6 +687,19 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)  	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); +	/* clear the corresponding DAIs parameters when going to be inactive */ +	if (cpu_dai->active == 1) { +		cpu_dai->rate = 0; +		cpu_dai->channels = 0; +		cpu_dai->sample_bits = 0; +	} + +	if (codec_dai->active == 1) { +		codec_dai->rate = 0; +		codec_dai->channels = 0; +		codec_dai->sample_bits = 0; +	} +  	/* apply codec digital mute */  	if (!codec->active)  		snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); | 
