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 | 130 |
3 files changed, 115 insertions, 23 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 { | |||
220 | struct snd_soc_pcm_stream capture; | 220 | struct snd_soc_pcm_stream capture; |
221 | struct snd_soc_pcm_stream playback; | 221 | struct snd_soc_pcm_stream playback; |
222 | unsigned int symmetric_rates:1; | 222 | unsigned int symmetric_rates:1; |
223 | unsigned int symmetric_channels:1; | ||
224 | unsigned int symmetric_samplebits:1; | ||
223 | 225 | ||
224 | /* probe ordering - for components with runtime dependencies */ | 226 | /* probe ordering - for components with runtime dependencies */ |
225 | int probe_order; | 227 | int probe_order; |
@@ -244,6 +246,8 @@ struct snd_soc_dai { | |||
244 | unsigned int capture_active:1; /* stream is in use */ | 246 | unsigned int capture_active:1; /* stream is in use */ |
245 | unsigned int playback_active:1; /* stream is in use */ | 247 | unsigned int playback_active:1; /* stream is in use */ |
246 | unsigned int symmetric_rates:1; | 248 | unsigned int symmetric_rates:1; |
249 | unsigned int symmetric_channels:1; | ||
250 | unsigned int symmetric_samplebits:1; | ||
247 | struct snd_pcm_runtime *runtime; | 251 | struct snd_pcm_runtime *runtime; |
248 | unsigned int active; | 252 | unsigned int active; |
249 | unsigned char probed:1; | 253 | unsigned char probed:1; |
@@ -258,6 +262,8 @@ struct snd_soc_dai { | |||
258 | 262 | ||
259 | /* Symmetry data - only valid if symmetry is being enforced */ | 263 | /* Symmetry data - only valid if symmetry is being enforced */ |
260 | unsigned int rate; | 264 | unsigned int rate; |
265 | unsigned int channels; | ||
266 | unsigned int sample_bits; | ||
261 | 267 | ||
262 | /* parent platform/codec */ | 268 | /* parent platform/codec */ |
263 | struct snd_soc_platform *platform; | 269 | 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 { | |||
879 | 879 | ||
880 | /* Symmetry requirements */ | 880 | /* Symmetry requirements */ |
881 | unsigned int symmetric_rates:1; | 881 | unsigned int symmetric_rates:1; |
882 | unsigned int symmetric_channels:1; | ||
883 | unsigned int symmetric_samplebits:1; | ||
882 | 884 | ||
883 | /* Do not create a PCM for this DAI link (Backend link) */ | 885 | /* Do not create a PCM for this DAI link (Backend link) */ |
884 | unsigned int no_pcm:1; | 886 | unsigned int no_pcm:1; |
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 42782c01e413..ed1e077114a2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c | |||
@@ -84,30 +84,97 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, | |||
84 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 84 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
85 | int ret; | 85 | int ret; |
86 | 86 | ||
87 | if (!soc_dai->driver->symmetric_rates && | 87 | if (soc_dai->rate && (soc_dai->driver->symmetric_rates || |
88 | !rtd->dai_link->symmetric_rates) | 88 | rtd->dai_link->symmetric_rates)) { |
89 | return 0; | 89 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", |
90 | soc_dai->rate); | ||
91 | |||
92 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, | ||
93 | SNDRV_PCM_HW_PARAM_RATE, | ||
94 | soc_dai->rate, soc_dai->rate); | ||
95 | if (ret < 0) { | ||
96 | dev_err(soc_dai->dev, | ||
97 | "ASoC: Unable to apply rate constraint: %d\n", | ||
98 | ret); | ||
99 | return ret; | ||
100 | } | ||
101 | } | ||
90 | 102 | ||
91 | /* This can happen if multiple streams are starting simultaneously - | 103 | if (soc_dai->channels && (soc_dai->driver->symmetric_channels || |
92 | * the second can need to get its constraints before the first has | 104 | rtd->dai_link->symmetric_channels)) { |
93 | * picked a rate. Complain and allow the application to carry on. | 105 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", |
94 | */ | 106 | soc_dai->channels); |
95 | if (!soc_dai->rate) { | 107 | |
96 | dev_warn(soc_dai->dev, | 108 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, |
97 | "ASoC: Not enforcing symmetric_rates due to race\n"); | 109 | SNDRV_PCM_HW_PARAM_CHANNELS, |
98 | return 0; | 110 | soc_dai->channels, |
111 | soc_dai->channels); | ||
112 | if (ret < 0) { | ||
113 | dev_err(soc_dai->dev, | ||
114 | "ASoC: Unable to apply channel symmetry constraint: %d\n", | ||
115 | ret); | ||
116 | return ret; | ||
117 | } | ||
99 | } | 118 | } |
100 | 119 | ||
101 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate); | 120 | if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || |
121 | rtd->dai_link->symmetric_samplebits)) { | ||
122 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", | ||
123 | soc_dai->sample_bits); | ||
102 | 124 | ||
103 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, | 125 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, |
104 | SNDRV_PCM_HW_PARAM_RATE, | 126 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, |
105 | soc_dai->rate, soc_dai->rate); | 127 | soc_dai->sample_bits, |
106 | if (ret < 0) { | 128 | soc_dai->sample_bits); |
107 | dev_err(soc_dai->dev, | 129 | if (ret < 0) { |
108 | "ASoC: Unable to apply rate symmetry constraint: %d\n", | 130 | dev_err(soc_dai->dev, |
109 | ret); | 131 | "ASoC: Unable to apply sample bits symmetry constraint: %d\n", |
110 | return ret; | 132 | ret); |
133 | return ret; | ||
134 | } | ||
135 | } | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, | ||
141 | struct snd_pcm_hw_params *params) | ||
142 | { | ||
143 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
144 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
145 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
146 | unsigned int rate, channels, sample_bits, symmetry; | ||
147 | |||
148 | rate = params_rate(params); | ||
149 | channels = params_channels(params); | ||
150 | sample_bits = snd_pcm_format_physical_width(params_format(params)); | ||
151 | |||
152 | /* reject unmatched parameters when applying symmetry */ | ||
153 | symmetry = cpu_dai->driver->symmetric_rates || | ||
154 | codec_dai->driver->symmetric_rates || | ||
155 | rtd->dai_link->symmetric_rates; | ||
156 | if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { | ||
157 | dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", | ||
158 | cpu_dai->rate, rate); | ||
159 | return -EINVAL; | ||
160 | } | ||
161 | |||
162 | symmetry = cpu_dai->driver->symmetric_channels || | ||
163 | codec_dai->driver->symmetric_channels || | ||
164 | rtd->dai_link->symmetric_channels; | ||
165 | if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { | ||
166 | dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", | ||
167 | cpu_dai->channels, channels); | ||
168 | return -EINVAL; | ||
169 | } | ||
170 | |||
171 | symmetry = cpu_dai->driver->symmetric_samplebits || | ||
172 | codec_dai->driver->symmetric_samplebits || | ||
173 | rtd->dai_link->symmetric_samplebits; | ||
174 | if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { | ||
175 | dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", | ||
176 | cpu_dai->sample_bits, sample_bits); | ||
177 | return -EINVAL; | ||
111 | } | 178 | } |
112 | 179 | ||
113 | return 0; | 180 | return 0; |
@@ -384,11 +451,17 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) | |||
384 | codec->active--; | 451 | codec->active--; |
385 | 452 | ||
386 | /* clear the corresponding DAIs rate when inactive */ | 453 | /* clear the corresponding DAIs rate when inactive */ |
387 | if (!cpu_dai->active) | 454 | if (!cpu_dai->active) { |
388 | cpu_dai->rate = 0; | 455 | cpu_dai->rate = 0; |
456 | cpu_dai->channels = 0; | ||
457 | cpu_dai->sample_bits = 0; | ||
458 | } | ||
389 | 459 | ||
390 | if (!codec_dai->active) | 460 | if (!codec_dai->active) { |
391 | codec_dai->rate = 0; | 461 | codec_dai->rate = 0; |
462 | codec_dai->channels = 0; | ||
463 | codec_dai->sample_bits = 0; | ||
464 | } | ||
392 | 465 | ||
393 | /* Muting the DAC suppresses artifacts caused during digital | 466 | /* Muting the DAC suppresses artifacts caused during digital |
394 | * shutdown, for example from stopping clocks. | 467 | * shutdown, for example from stopping clocks. |
@@ -525,6 +598,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
525 | 598 | ||
526 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | 599 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); |
527 | 600 | ||
601 | ret = soc_pcm_params_symmetry(substream, params); | ||
602 | if (ret) | ||
603 | goto out; | ||
604 | |||
528 | if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { | 605 | if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { |
529 | ret = rtd->dai_link->ops->hw_params(substream, params); | 606 | ret = rtd->dai_link->ops->hw_params(substream, params); |
530 | if (ret < 0) { | 607 | if (ret < 0) { |
@@ -561,9 +638,16 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
561 | } | 638 | } |
562 | } | 639 | } |
563 | 640 | ||
564 | /* store the rate for each DAIs */ | 641 | /* store the parameters for each DAIs */ |
565 | cpu_dai->rate = params_rate(params); | 642 | cpu_dai->rate = params_rate(params); |
643 | cpu_dai->channels = params_channels(params); | ||
644 | cpu_dai->sample_bits = | ||
645 | snd_pcm_format_physical_width(params_format(params)); | ||
646 | |||
566 | codec_dai->rate = params_rate(params); | 647 | codec_dai->rate = params_rate(params); |
648 | codec_dai->channels = params_channels(params); | ||
649 | codec_dai->sample_bits = | ||
650 | snd_pcm_format_physical_width(params_format(params)); | ||
567 | 651 | ||
568 | out: | 652 | out: |
569 | mutex_unlock(&rtd->pcm_mutex); | 653 | mutex_unlock(&rtd->pcm_mutex); |