diff options
Diffstat (limited to 'sound/drivers')
-rw-r--r-- | sound/drivers/aloop.c | 124 |
1 files changed, 106 insertions, 18 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 040030aa9d8e..3c0088272095 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c | |||
@@ -188,7 +188,7 @@ static inline void loopback_timer_stop(struct loopback_pcm *dpcm) | |||
188 | 188 | ||
189 | static int loopback_check_format(struct loopback_cable *cable, int stream) | 189 | static int loopback_check_format(struct loopback_cable *cable, int stream) |
190 | { | 190 | { |
191 | struct snd_pcm_runtime *runtime; | 191 | struct snd_pcm_runtime *runtime, *cruntime; |
192 | struct loopback_setup *setup; | 192 | struct loopback_setup *setup; |
193 | struct snd_card *card; | 193 | struct snd_card *card; |
194 | int check; | 194 | int check; |
@@ -200,11 +200,11 @@ static int loopback_check_format(struct loopback_cable *cable, int stream) | |||
200 | } | 200 | } |
201 | runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> | 201 | runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> |
202 | substream->runtime; | 202 | substream->runtime; |
203 | check = cable->hw.formats != (1ULL << runtime->format) || | 203 | cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> |
204 | cable->hw.rate_min != runtime->rate || | 204 | substream->runtime; |
205 | cable->hw.rate_max != runtime->rate || | 205 | check = runtime->format != cruntime->format || |
206 | cable->hw.channels_min != runtime->channels || | 206 | runtime->rate != cruntime->rate || |
207 | cable->hw.channels_max != runtime->channels; | 207 | runtime->channels != cruntime->channels; |
208 | if (!check) | 208 | if (!check) |
209 | return 0; | 209 | return 0; |
210 | if (stream == SNDRV_PCM_STREAM_CAPTURE) { | 210 | if (stream == SNDRV_PCM_STREAM_CAPTURE) { |
@@ -274,12 +274,42 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
274 | return 0; | 274 | return 0; |
275 | } | 275 | } |
276 | 276 | ||
277 | static void params_change_substream(struct loopback_pcm *dpcm, | ||
278 | struct snd_pcm_runtime *runtime) | ||
279 | { | ||
280 | struct snd_pcm_runtime *dst_runtime; | ||
281 | |||
282 | if (dpcm == NULL || dpcm->substream == NULL) | ||
283 | return; | ||
284 | dst_runtime = dpcm->substream->runtime; | ||
285 | if (dst_runtime == NULL) | ||
286 | return; | ||
287 | dst_runtime->hw = dpcm->cable->hw; | ||
288 | } | ||
289 | |||
290 | static void params_change(struct snd_pcm_substream *substream) | ||
291 | { | ||
292 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
293 | struct loopback_pcm *dpcm = runtime->private_data; | ||
294 | struct loopback_cable *cable = dpcm->cable; | ||
295 | |||
296 | cable->hw.formats = (1ULL << runtime->format); | ||
297 | cable->hw.rate_min = runtime->rate; | ||
298 | cable->hw.rate_max = runtime->rate; | ||
299 | cable->hw.channels_min = runtime->channels; | ||
300 | cable->hw.channels_max = runtime->channels; | ||
301 | params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK], | ||
302 | runtime); | ||
303 | params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE], | ||
304 | runtime); | ||
305 | } | ||
306 | |||
277 | static int loopback_prepare(struct snd_pcm_substream *substream) | 307 | static int loopback_prepare(struct snd_pcm_substream *substream) |
278 | { | 308 | { |
279 | struct snd_pcm_runtime *runtime = substream->runtime; | 309 | struct snd_pcm_runtime *runtime = substream->runtime; |
280 | struct loopback_pcm *dpcm = runtime->private_data; | 310 | struct loopback_pcm *dpcm = runtime->private_data; |
281 | struct loopback_cable *cable = dpcm->cable; | 311 | struct loopback_cable *cable = dpcm->cable; |
282 | unsigned int bps, salign; | 312 | int bps, salign; |
283 | 313 | ||
284 | salign = (snd_pcm_format_width(runtime->format) * | 314 | salign = (snd_pcm_format_width(runtime->format) * |
285 | runtime->channels) / 8; | 315 | runtime->channels) / 8; |
@@ -303,13 +333,10 @@ static int loopback_prepare(struct snd_pcm_substream *substream) | |||
303 | dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); | 333 | dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); |
304 | 334 | ||
305 | mutex_lock(&dpcm->loopback->cable_lock); | 335 | mutex_lock(&dpcm->loopback->cable_lock); |
306 | if (!(cable->valid & ~(1 << substream->stream))) { | 336 | if (!(cable->valid & ~(1 << substream->stream)) || |
307 | cable->hw.formats = (1ULL << runtime->format); | 337 | (get_setup(dpcm)->notify && |
308 | cable->hw.rate_min = runtime->rate; | 338 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) |
309 | cable->hw.rate_max = runtime->rate; | 339 | params_change(substream); |
310 | cable->hw.channels_min = runtime->channels; | ||
311 | cable->hw.channels_max = runtime->channels; | ||
312 | } | ||
313 | cable->valid |= 1 << substream->stream; | 340 | cable->valid |= 1 << substream->stream; |
314 | mutex_unlock(&dpcm->loopback->cable_lock); | 341 | mutex_unlock(&dpcm->loopback->cable_lock); |
315 | 342 | ||
@@ -542,6 +569,47 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream) | |||
542 | return !substream->stream; | 569 | return !substream->stream; |
543 | } | 570 | } |
544 | 571 | ||
572 | static int rule_format(struct snd_pcm_hw_params *params, | ||
573 | struct snd_pcm_hw_rule *rule) | ||
574 | { | ||
575 | |||
576 | struct snd_pcm_hardware *hw = rule->private; | ||
577 | struct snd_mask *maskp = hw_param_mask(params, rule->var); | ||
578 | |||
579 | maskp->bits[0] &= (u_int32_t)hw->formats; | ||
580 | maskp->bits[1] &= (u_int32_t)(hw->formats >> 32); | ||
581 | memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ | ||
582 | if (! maskp->bits[0] && ! maskp->bits[1]) | ||
583 | return -EINVAL; | ||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | static int rule_rate(struct snd_pcm_hw_params *params, | ||
588 | struct snd_pcm_hw_rule *rule) | ||
589 | { | ||
590 | struct snd_pcm_hardware *hw = rule->private; | ||
591 | struct snd_interval t; | ||
592 | |||
593 | t.min = hw->rate_min; | ||
594 | t.max = hw->rate_max; | ||
595 | t.openmin = t.openmax = 0; | ||
596 | t.integer = 0; | ||
597 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
598 | } | ||
599 | |||
600 | static int rule_channels(struct snd_pcm_hw_params *params, | ||
601 | struct snd_pcm_hw_rule *rule) | ||
602 | { | ||
603 | struct snd_pcm_hardware *hw = rule->private; | ||
604 | struct snd_interval t; | ||
605 | |||
606 | t.min = hw->channels_min; | ||
607 | t.max = hw->channels_max; | ||
608 | t.openmin = t.openmax = 0; | ||
609 | t.integer = 0; | ||
610 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | ||
611 | } | ||
612 | |||
545 | static int loopback_open(struct snd_pcm_substream *substream) | 613 | static int loopback_open(struct snd_pcm_substream *substream) |
546 | { | 614 | { |
547 | struct snd_pcm_runtime *runtime = substream->runtime; | 615 | struct snd_pcm_runtime *runtime = substream->runtime; |
@@ -579,14 +647,34 @@ static int loopback_open(struct snd_pcm_substream *substream) | |||
579 | 647 | ||
580 | snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | 648 | snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); |
581 | 649 | ||
650 | /* use dynamic rules based on actual runtime->hw values */ | ||
651 | /* note that the default rules created in the PCM midlevel code */ | ||
652 | /* are cached -> they do not reflect the actual state */ | ||
653 | err = snd_pcm_hw_rule_add(runtime, 0, | ||
654 | SNDRV_PCM_HW_PARAM_FORMAT, | ||
655 | rule_format, &runtime->hw, | ||
656 | SNDRV_PCM_HW_PARAM_FORMAT, -1); | ||
657 | if (err < 0) | ||
658 | goto unlock; | ||
659 | err = snd_pcm_hw_rule_add(runtime, 0, | ||
660 | SNDRV_PCM_HW_PARAM_RATE, | ||
661 | rule_rate, &runtime->hw, | ||
662 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
663 | if (err < 0) | ||
664 | goto unlock; | ||
665 | err = snd_pcm_hw_rule_add(runtime, 0, | ||
666 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
667 | rule_channels, &runtime->hw, | ||
668 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); | ||
669 | if (err < 0) | ||
670 | goto unlock; | ||
671 | |||
582 | runtime->private_data = dpcm; | 672 | runtime->private_data = dpcm; |
583 | runtime->private_free = loopback_runtime_free; | 673 | runtime->private_free = loopback_runtime_free; |
584 | if (get_notify(dpcm) && | 674 | if (get_notify(dpcm)) |
585 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
586 | runtime->hw = loopback_pcm_hardware; | 675 | runtime->hw = loopback_pcm_hardware; |
587 | } else { | 676 | else |
588 | runtime->hw = cable->hw; | 677 | runtime->hw = cable->hw; |
589 | } | ||
590 | unlock: | 678 | unlock: |
591 | mutex_unlock(&loopback->cable_lock); | 679 | mutex_unlock(&loopback->cable_lock); |
592 | return err; | 680 | return err; |