ASoC: soc-pcm: add symmetry for channels and sample bits
Some SoCs can only work in mono or stereo mode at one time. So if we let them capture a mono stream while playing a stereo stream, there might be a problem occur to one of these two streams: double paced or slowed down. In soc-pcm.c, we have soc_pcm_apply_symmetry() to apply the rate symmetry. But we don't have one for channels. Likewise, we can treat symmetric_rate as a solution for those SoCs or CODECs which can not handle asymmetrical LRCLK. But it's also impossible for them to handle asymmetrical BCLK. And accodring to BCLK = LRCLK * channel number * slot size(fixed or sample bits), sample bits might also be a problem if they are not using a fixed slot size. Thus, this patch applys symmetry for channels and sample bits. Meanwhile, there might be a race between two substreams if starting simultaneously. Previously, we only added warning to compalin but still using conservative way to let it carry on. However, this patch rejects the second stream with any unmatched parameter to make sure the first existing stream won't be broken. Signed-off-by: Nicolin Chen <b42378@freescale.com> Signed-off-by: Mark Brown <broonie@linaro.org>
This commit is contained in:
parent
6ce4eac1f6
commit
3635bf09a8
|
@ -220,6 +220,8 @@ struct snd_soc_dai_driver {
|
||||||
struct snd_soc_pcm_stream capture;
|
struct snd_soc_pcm_stream capture;
|
||||||
struct snd_soc_pcm_stream playback;
|
struct snd_soc_pcm_stream playback;
|
||||||
unsigned int symmetric_rates:1;
|
unsigned int symmetric_rates:1;
|
||||||
|
unsigned int symmetric_channels:1;
|
||||||
|
unsigned int symmetric_samplebits:1;
|
||||||
|
|
||||||
/* probe ordering - for components with runtime dependencies */
|
/* probe ordering - for components with runtime dependencies */
|
||||||
int probe_order;
|
int probe_order;
|
||||||
|
@ -244,6 +246,8 @@ struct snd_soc_dai {
|
||||||
unsigned int capture_active:1; /* stream is in use */
|
unsigned int capture_active:1; /* stream is in use */
|
||||||
unsigned int playback_active:1; /* stream is in use */
|
unsigned int playback_active:1; /* stream is in use */
|
||||||
unsigned int symmetric_rates:1;
|
unsigned int symmetric_rates:1;
|
||||||
|
unsigned int symmetric_channels:1;
|
||||||
|
unsigned int symmetric_samplebits:1;
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
unsigned int active;
|
unsigned int active;
|
||||||
unsigned char probed:1;
|
unsigned char probed:1;
|
||||||
|
@ -258,6 +262,8 @@ struct snd_soc_dai {
|
||||||
|
|
||||||
/* Symmetry data - only valid if symmetry is being enforced */
|
/* Symmetry data - only valid if symmetry is being enforced */
|
||||||
unsigned int rate;
|
unsigned int rate;
|
||||||
|
unsigned int channels;
|
||||||
|
unsigned int sample_bits;
|
||||||
|
|
||||||
/* parent platform/codec */
|
/* parent platform/codec */
|
||||||
struct snd_soc_platform *platform;
|
struct snd_soc_platform *platform;
|
||||||
|
|
|
@ -879,6 +879,8 @@ struct snd_soc_dai_link {
|
||||||
|
|
||||||
/* Symmetry requirements */
|
/* Symmetry requirements */
|
||||||
unsigned int symmetric_rates:1;
|
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) */
|
/* Do not create a PCM for this DAI link (Backend link) */
|
||||||
unsigned int no_pcm:1;
|
unsigned int no_pcm:1;
|
||||||
|
|
|
@ -84,30 +84,97 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!soc_dai->driver->symmetric_rates &&
|
if (soc_dai->rate && (soc_dai->driver->symmetric_rates ||
|
||||||
!rtd->dai_link->symmetric_rates)
|
rtd->dai_link->symmetric_rates)) {
|
||||||
return 0;
|
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
|
||||||
|
soc_dai->rate);
|
||||||
|
|
||||||
/* This can happen if multiple streams are starting simultaneously -
|
ret = snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||||
* the second can need to get its constraints before the first has
|
SNDRV_PCM_HW_PARAM_RATE,
|
||||||
* picked a rate. Complain and allow the application to carry on.
|
soc_dai->rate, soc_dai->rate);
|
||||||
*/
|
if (ret < 0) {
|
||||||
if (!soc_dai->rate) {
|
dev_err(soc_dai->dev,
|
||||||
dev_warn(soc_dai->dev,
|
"ASoC: Unable to apply rate constraint: %d\n",
|
||||||
"ASoC: Not enforcing symmetric_rates due to race\n");
|
ret);
|
||||||
return 0;
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate);
|
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,
|
ret = snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||||
SNDRV_PCM_HW_PARAM_RATE,
|
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||||
soc_dai->rate, soc_dai->rate);
|
soc_dai->channels,
|
||||||
if (ret < 0) {
|
soc_dai->channels);
|
||||||
dev_err(soc_dai->dev,
|
if (ret < 0) {
|
||||||
"ASoC: Unable to apply rate symmetry constraint: %d\n",
|
dev_err(soc_dai->dev,
|
||||||
ret);
|
"ASoC: Unable to apply channel symmetry constraint: %d\n",
|
||||||
return ret;
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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;
|
return 0;
|
||||||
|
@ -384,11 +451,17 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
|
||||||
codec->active--;
|
codec->active--;
|
||||||
|
|
||||||
/* clear the corresponding DAIs rate when inactive */
|
/* clear the corresponding DAIs rate when inactive */
|
||||||
if (!cpu_dai->active)
|
if (!cpu_dai->active) {
|
||||||
cpu_dai->rate = 0;
|
cpu_dai->rate = 0;
|
||||||
|
cpu_dai->channels = 0;
|
||||||
|
cpu_dai->sample_bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!codec_dai->active)
|
if (!codec_dai->active) {
|
||||||
codec_dai->rate = 0;
|
codec_dai->rate = 0;
|
||||||
|
codec_dai->channels = 0;
|
||||||
|
codec_dai->sample_bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Muting the DAC suppresses artifacts caused during digital
|
/* Muting the DAC suppresses artifacts caused during digital
|
||||||
* shutdown, for example from stopping clocks.
|
* shutdown, for example from stopping clocks.
|
||||||
|
@ -525,6 +598,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
|
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) {
|
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
|
||||||
ret = rtd->dai_link->ops->hw_params(substream, params);
|
ret = rtd->dai_link->ops->hw_params(substream, params);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -561,9 +638,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->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->rate = params_rate(params);
|
||||||
|
codec_dai->channels = params_channels(params);
|
||||||
|
codec_dai->sample_bits =
|
||||||
|
snd_pcm_format_physical_width(params_format(params));
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&rtd->pcm_mutex);
|
mutex_unlock(&rtd->pcm_mutex);
|
||||||
|
|
Loading…
Reference in a new issue