diff options
| author | Jaroslav Kysela <perex@perex.cz> | 2010-10-11 04:45:00 -0400 |
|---|---|---|
| committer | Jaroslav Kysela <perex@perex.cz> | 2010-10-11 04:56:09 -0400 |
| commit | b1c73fc8e697eb73e23603e465e9af2711ed4183 (patch) | |
| tree | d722242804e59072f9acb25993606e50a7681147 | |
| parent | ac446fb7e690b317050ed158ba5dfd9273dc9e74 (diff) | |
ALSA: snd-aloop: Fix hw_params restrictions and checking
This patch fixes the hw_params restrictions when first (or playback) stream
sets the final hardware parameters. Also, fix the hw_params checking
in the trigger callback.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
| -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; |
