diff options
Diffstat (limited to 'sound/soc/soc-pcm.c')
-rw-r--r-- | sound/soc/soc-pcm.c | 179 |
1 files changed, 145 insertions, 34 deletions
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 42782c01e413..604e7e9a2ef8 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, | |||
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 | } | ||
111 | } | 135 | } |
112 | 136 | ||
113 | return 0; | 137 | return 0; |
114 | } | 138 | } |
115 | 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; | ||
178 | } | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) | ||
184 | { | ||
185 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
186 | struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; | ||
187 | struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver; | ||
188 | struct snd_soc_dai_link *link = rtd->dai_link; | ||
189 | |||
190 | return cpu_driver->symmetric_rates || codec_driver->symmetric_rates || | ||
191 | link->symmetric_rates || cpu_driver->symmetric_channels || | ||
192 | codec_driver->symmetric_channels || link->symmetric_channels || | ||
193 | cpu_driver->symmetric_samplebits || | ||
194 | codec_driver->symmetric_samplebits || | ||
195 | link->symmetric_samplebits; | ||
196 | } | ||
197 | |||
116 | /* | 198 | /* |
117 | * List of sample sizes that might go over the bus for parameter | 199 | * List of sample sizes that might go over the bus for parameter |
118 | * application. There ought to be a wildcard sample size for things | 200 | * application. There ought to be a wildcard sample size for things |
@@ -148,12 +230,12 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream, | |||
148 | } | 230 | } |
149 | } | 231 | } |
150 | 232 | ||
151 | static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw, | 233 | static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime, |
152 | struct snd_soc_pcm_stream *codec_stream, | 234 | struct snd_soc_pcm_stream *codec_stream, |
153 | struct snd_soc_pcm_stream *cpu_stream) | 235 | struct snd_soc_pcm_stream *cpu_stream) |
154 | { | 236 | { |
155 | hw->rate_min = max(codec_stream->rate_min, cpu_stream->rate_min); | 237 | struct snd_pcm_hardware *hw = &runtime->hw; |
156 | hw->rate_max = max(codec_stream->rate_max, cpu_stream->rate_max); | 238 | |
157 | hw->channels_min = max(codec_stream->channels_min, | 239 | hw->channels_min = max(codec_stream->channels_min, |
158 | cpu_stream->channels_min); | 240 | cpu_stream->channels_min); |
159 | hw->channels_max = min(codec_stream->channels_max, | 241 | hw->channels_max = min(codec_stream->channels_max, |
@@ -166,6 +248,13 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw, | |||
166 | if (cpu_stream->rates | 248 | if (cpu_stream->rates |
167 | & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) | 249 | & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) |
168 | hw->rates |= codec_stream->rates; | 250 | hw->rates |= codec_stream->rates; |
251 | |||
252 | snd_pcm_limit_hw_rates(runtime); | ||
253 | |||
254 | hw->rate_min = max(hw->rate_min, cpu_stream->rate_min); | ||
255 | hw->rate_min = max(hw->rate_min, codec_stream->rate_min); | ||
256 | hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max); | ||
257 | hw->rate_max = min_not_zero(hw->rate_max, codec_stream->rate_max); | ||
169 | } | 258 | } |
170 | 259 | ||
171 | /* | 260 | /* |
@@ -235,15 +324,17 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
235 | 324 | ||
236 | /* Check that the codec and cpu DAIs are compatible */ | 325 | /* Check that the codec and cpu DAIs are compatible */ |
237 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 326 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
238 | soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->playback, | 327 | soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback, |
239 | &cpu_dai_drv->playback); | 328 | &cpu_dai_drv->playback); |
240 | } else { | 329 | } else { |
241 | soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->capture, | 330 | soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture, |
242 | &cpu_dai_drv->capture); | 331 | &cpu_dai_drv->capture); |
243 | } | 332 | } |
244 | 333 | ||
334 | if (soc_pcm_has_symmetry(substream)) | ||
335 | runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
336 | |||
245 | ret = -EINVAL; | 337 | ret = -EINVAL; |
246 | snd_pcm_limit_hw_rates(runtime); | ||
247 | if (!runtime->hw.rates) { | 338 | if (!runtime->hw.rates) { |
248 | printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", | 339 | printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", |
249 | codec_dai->name, cpu_dai->name); | 340 | codec_dai->name, cpu_dai->name); |
@@ -390,11 +481,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) | |||
390 | if (!codec_dai->active) | 481 | if (!codec_dai->active) |
391 | codec_dai->rate = 0; | 482 | codec_dai->rate = 0; |
392 | 483 | ||
393 | /* Muting the DAC suppresses artifacts caused during digital | ||
394 | * shutdown, for example from stopping clocks. | ||
395 | */ | ||
396 | snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); | ||
397 | |||
398 | if (cpu_dai->driver->ops->shutdown) | 484 | if (cpu_dai->driver->ops->shutdown) |
399 | cpu_dai->driver->ops->shutdown(substream, cpu_dai); | 485 | cpu_dai->driver->ops->shutdown(substream, cpu_dai); |
400 | 486 | ||
@@ -525,6 +611,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
525 | 611 | ||
526 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | 612 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); |
527 | 613 | ||
614 | ret = soc_pcm_params_symmetry(substream, params); | ||
615 | if (ret) | ||
616 | goto out; | ||
617 | |||
528 | if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { | 618 | if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { |
529 | ret = rtd->dai_link->ops->hw_params(substream, params); | 619 | ret = rtd->dai_link->ops->hw_params(substream, params); |
530 | if (ret < 0) { | 620 | if (ret < 0) { |
@@ -561,9 +651,16 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
561 | } | 651 | } |
562 | } | 652 | } |
563 | 653 | ||
564 | /* store the rate for each DAIs */ | 654 | /* store the parameters for each DAIs */ |
565 | cpu_dai->rate = params_rate(params); | 655 | cpu_dai->rate = params_rate(params); |
656 | cpu_dai->channels = params_channels(params); | ||
657 | cpu_dai->sample_bits = | ||
658 | snd_pcm_format_physical_width(params_format(params)); | ||
659 | |||
566 | codec_dai->rate = params_rate(params); | 660 | codec_dai->rate = params_rate(params); |
661 | codec_dai->channels = params_channels(params); | ||
662 | codec_dai->sample_bits = | ||
663 | snd_pcm_format_physical_width(params_format(params)); | ||
567 | 664 | ||
568 | out: | 665 | out: |
569 | mutex_unlock(&rtd->pcm_mutex); | 666 | mutex_unlock(&rtd->pcm_mutex); |
@@ -594,12 +691,26 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) | |||
594 | struct snd_soc_platform *platform = rtd->platform; | 691 | struct snd_soc_platform *platform = rtd->platform; |
595 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 692 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
596 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 693 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
597 | struct snd_soc_codec *codec = rtd->codec; | 694 | bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
598 | 695 | ||
599 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | 696 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); |
600 | 697 | ||
698 | /* clear the corresponding DAIs parameters when going to be inactive */ | ||
699 | if (cpu_dai->active == 1) { | ||
700 | cpu_dai->rate = 0; | ||
701 | cpu_dai->channels = 0; | ||
702 | cpu_dai->sample_bits = 0; | ||
703 | } | ||
704 | |||
705 | if (codec_dai->active == 1) { | ||
706 | codec_dai->rate = 0; | ||
707 | codec_dai->channels = 0; | ||
708 | codec_dai->sample_bits = 0; | ||
709 | } | ||
710 | |||
601 | /* apply codec digital mute */ | 711 | /* apply codec digital mute */ |
602 | if (!codec->active) | 712 | if ((playback && codec_dai->playback_active == 1) || |
713 | (!playback && codec_dai->capture_active == 1)) | ||
603 | snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); | 714 | snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); |
604 | 715 | ||
605 | /* free any machine hw params */ | 716 | /* free any machine hw params */ |