aboutsummaryrefslogtreecommitdiffstats
path: root/sound/drivers
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-10-11 07:56:12 -0400
committerTakashi Iwai <tiwai@suse.de>2010-10-11 07:56:12 -0400
commite799d0bce6b191de6e96e556528243c1d73303ca (patch)
treee61f5996d97f80734f6ca139decb052a3b6c5339 /sound/drivers
parent1d2019fb6be2f318f0aa85be5f224f47a5f006fe (diff)
parent838c364ff05c143fd1810e8ad1469935d6c23a7a (diff)
Merge remote branch 'alsa/devel' into topic/misc
Diffstat (limited to 'sound/drivers')
-rw-r--r--sound/drivers/aloop.c124
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
189static int loopback_check_format(struct loopback_cable *cable, int stream) 189static 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
277static 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
290static 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
277static int loopback_prepare(struct snd_pcm_substream *substream) 307static 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
572static 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
587static 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
600static 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
545static int loopback_open(struct snd_pcm_substream *substream) 613static 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;