diff options
author | Dylan Reid <dgreid@chromium.org> | 2012-09-18 12:49:48 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-09-19 02:08:11 -0400 |
commit | 61a709504b079110cd5b12ea9a4590ffea687a5c (patch) | |
tree | f49a7338767f2117c4167904a878f27ec9955162 /sound/usb/pcm.c | |
parent | 35ec7aa29833de350f51922736aefe22ebf76c4d (diff) |
ALSA: usb-audio: Move configuration to prepare.
Move interface and endpoint configuration from hw_params to prepare
callback. During system suspend/resume when the USB device power isn't
cycled the interface and endpoint configuration need to be set before
audio playback can continue. Resume involves another call to prepare
but not to hw_params, moving it here allows a playing stream to continue
after resume.
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 134 |
1 files changed, 74 insertions, 60 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 62ab4fd5880a..ae783d40f55a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c | |||
@@ -82,8 +82,7 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream | |||
82 | /* | 82 | /* |
83 | * find a matching audio format | 83 | * find a matching audio format |
84 | */ | 84 | */ |
85 | static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format, | 85 | static struct audioformat *find_format(struct snd_usb_substream *subs) |
86 | unsigned int rate, unsigned int channels) | ||
87 | { | 86 | { |
88 | struct list_head *p; | 87 | struct list_head *p; |
89 | struct audioformat *found = NULL; | 88 | struct audioformat *found = NULL; |
@@ -92,16 +91,17 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned | |||
92 | list_for_each(p, &subs->fmt_list) { | 91 | list_for_each(p, &subs->fmt_list) { |
93 | struct audioformat *fp; | 92 | struct audioformat *fp; |
94 | fp = list_entry(p, struct audioformat, list); | 93 | fp = list_entry(p, struct audioformat, list); |
95 | if (!(fp->formats & (1uLL << format))) | 94 | if (!(fp->formats & (1uLL << subs->pcm_format))) |
96 | continue; | 95 | continue; |
97 | if (fp->channels != channels) | 96 | if (fp->channels != subs->channels) |
98 | continue; | 97 | continue; |
99 | if (rate < fp->rate_min || rate > fp->rate_max) | 98 | if (subs->cur_rate < fp->rate_min || |
99 | subs->cur_rate > fp->rate_max) | ||
100 | continue; | 100 | continue; |
101 | if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { | 101 | if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { |
102 | unsigned int i; | 102 | unsigned int i; |
103 | for (i = 0; i < fp->nr_rates; i++) | 103 | for (i = 0; i < fp->nr_rates; i++) |
104 | if (fp->rate_table[i] == rate) | 104 | if (fp->rate_table[i] == subs->cur_rate) |
105 | break; | 105 | break; |
106 | if (i >= fp->nr_rates) | 106 | if (i >= fp->nr_rates) |
107 | continue; | 107 | continue; |
@@ -436,6 +436,42 @@ add_sync_ep: | |||
436 | } | 436 | } |
437 | 437 | ||
438 | /* | 438 | /* |
439 | * configure endpoint params | ||
440 | * | ||
441 | * called during initial setup and upon resume | ||
442 | */ | ||
443 | static int configure_endpoint(struct snd_usb_substream *subs) | ||
444 | { | ||
445 | int ret; | ||
446 | |||
447 | mutex_lock(&subs->stream->chip->shutdown_mutex); | ||
448 | /* format changed */ | ||
449 | stop_endpoints(subs, 0, 0, 0); | ||
450 | ret = snd_usb_endpoint_set_params(subs->data_endpoint, | ||
451 | subs->pcm_format, | ||
452 | subs->channels, | ||
453 | subs->period_bytes, | ||
454 | subs->cur_rate, | ||
455 | subs->cur_audiofmt, | ||
456 | subs->sync_endpoint); | ||
457 | if (ret < 0) | ||
458 | goto unlock; | ||
459 | |||
460 | if (subs->sync_endpoint) | ||
461 | ret = snd_usb_endpoint_set_params(subs->data_endpoint, | ||
462 | subs->pcm_format, | ||
463 | subs->channels, | ||
464 | subs->period_bytes, | ||
465 | subs->cur_rate, | ||
466 | subs->cur_audiofmt, | ||
467 | NULL); | ||
468 | |||
469 | unlock: | ||
470 | mutex_unlock(&subs->stream->chip->shutdown_mutex); | ||
471 | return ret; | ||
472 | } | ||
473 | |||
474 | /* | ||
439 | * hw_params callback | 475 | * hw_params callback |
440 | * | 476 | * |
441 | * allocate a buffer and set the given audio format. | 477 | * allocate a buffer and set the given audio format. |
@@ -450,75 +486,32 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, | |||
450 | { | 486 | { |
451 | struct snd_usb_substream *subs = substream->runtime->private_data; | 487 | struct snd_usb_substream *subs = substream->runtime->private_data; |
452 | struct audioformat *fmt; | 488 | struct audioformat *fmt; |
453 | unsigned int channels, rate, format; | 489 | int ret; |
454 | int ret, changed; | ||
455 | 490 | ||
456 | ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, | 491 | ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, |
457 | params_buffer_bytes(hw_params)); | 492 | params_buffer_bytes(hw_params)); |
458 | if (ret < 0) | 493 | if (ret < 0) |
459 | return ret; | 494 | return ret; |
460 | 495 | ||
461 | format = params_format(hw_params); | 496 | subs->pcm_format = params_format(hw_params); |
462 | rate = params_rate(hw_params); | 497 | subs->period_bytes = params_period_bytes(hw_params); |
463 | channels = params_channels(hw_params); | 498 | subs->channels = params_channels(hw_params); |
464 | fmt = find_format(subs, format, rate, channels); | 499 | subs->cur_rate = params_rate(hw_params); |
500 | |||
501 | fmt = find_format(subs); | ||
465 | if (!fmt) { | 502 | if (!fmt) { |
466 | snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n", | 503 | snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n", |
467 | format, rate, channels); | 504 | subs->pcm_format, subs->cur_rate, subs->channels); |
468 | return -EINVAL; | 505 | return -EINVAL; |
469 | } | 506 | } |
470 | 507 | ||
471 | changed = subs->cur_audiofmt != fmt || | ||
472 | subs->period_bytes != params_period_bytes(hw_params) || | ||
473 | subs->cur_rate != rate; | ||
474 | if ((ret = set_format(subs, fmt)) < 0) | 508 | if ((ret = set_format(subs, fmt)) < 0) |
475 | return ret; | 509 | return ret; |
476 | 510 | ||
477 | if (subs->cur_rate != rate) { | 511 | subs->interface = fmt->iface; |
478 | struct usb_host_interface *alts; | 512 | subs->altset_idx = fmt->altset_idx; |
479 | struct usb_interface *iface; | ||
480 | iface = usb_ifnum_to_if(subs->dev, fmt->iface); | ||
481 | alts = &iface->altsetting[fmt->altset_idx]; | ||
482 | ret = snd_usb_init_sample_rate(subs->stream->chip, fmt->iface, alts, fmt, rate); | ||
483 | if (ret < 0) | ||
484 | return ret; | ||
485 | subs->cur_rate = rate; | ||
486 | } | ||
487 | |||
488 | if (changed) { | ||
489 | subs->period_bytes = params_period_bytes(hw_params); | ||
490 | 513 | ||
491 | mutex_lock(&subs->stream->chip->shutdown_mutex); | 514 | return 0; |
492 | /* format changed */ | ||
493 | stop_endpoints(subs, 0, 0, 0); | ||
494 | ret = snd_usb_endpoint_set_params(subs->data_endpoint, | ||
495 | format, | ||
496 | channels, | ||
497 | subs->period_bytes, | ||
498 | rate, | ||
499 | fmt, | ||
500 | subs->sync_endpoint); | ||
501 | if (ret < 0) | ||
502 | goto unlock; | ||
503 | |||
504 | if (subs->sync_endpoint) | ||
505 | ret = snd_usb_endpoint_set_params(subs->data_endpoint, | ||
506 | format, | ||
507 | channels, | ||
508 | subs->period_bytes, | ||
509 | rate, | ||
510 | fmt, | ||
511 | NULL); | ||
512 | unlock: | ||
513 | mutex_unlock(&subs->stream->chip->shutdown_mutex); | ||
514 | } | ||
515 | |||
516 | if (ret == 0) { | ||
517 | subs->interface = fmt->iface; | ||
518 | subs->altset_idx = fmt->altset_idx; | ||
519 | } | ||
520 | |||
521 | return ret; | ||
522 | } | 515 | } |
523 | 516 | ||
524 | /* | 517 | /* |
@@ -549,6 +542,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) | |||
549 | { | 542 | { |
550 | struct snd_pcm_runtime *runtime = substream->runtime; | 543 | struct snd_pcm_runtime *runtime = substream->runtime; |
551 | struct snd_usb_substream *subs = runtime->private_data; | 544 | struct snd_usb_substream *subs = runtime->private_data; |
545 | struct usb_host_interface *alts; | ||
546 | struct usb_interface *iface; | ||
547 | int ret; | ||
552 | 548 | ||
553 | if (! subs->cur_audiofmt) { | 549 | if (! subs->cur_audiofmt) { |
554 | snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); | 550 | snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); |
@@ -558,6 +554,24 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) | |||
558 | if (snd_BUG_ON(!subs->data_endpoint)) | 554 | if (snd_BUG_ON(!subs->data_endpoint)) |
559 | return -EIO; | 555 | return -EIO; |
560 | 556 | ||
557 | ret = set_format(subs, subs->cur_audiofmt); | ||
558 | if (ret < 0) | ||
559 | return ret; | ||
560 | |||
561 | iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); | ||
562 | alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; | ||
563 | ret = snd_usb_init_sample_rate(subs->stream->chip, | ||
564 | subs->cur_audiofmt->iface, | ||
565 | alts, | ||
566 | subs->cur_audiofmt, | ||
567 | subs->cur_rate); | ||
568 | if (ret < 0) | ||
569 | return ret; | ||
570 | |||
571 | ret = configure_endpoint(subs); | ||
572 | if (ret < 0) | ||
573 | return ret; | ||
574 | |||
561 | /* some unit conversions in runtime */ | 575 | /* some unit conversions in runtime */ |
562 | subs->data_endpoint->maxframesize = | 576 | subs->data_endpoint->maxframesize = |
563 | bytes_to_frames(runtime, subs->data_endpoint->maxpacksize); | 577 | bytes_to_frames(runtime, subs->data_endpoint->maxpacksize); |