diff options
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 126 |
1 files changed, 78 insertions, 48 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index f782ce19bf5a..55e19e1b80ec 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,63 +486,33 @@ 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; | 513 | subs->need_setup_ep = true; |
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 | mutex_lock(&subs->stream->chip->shutdown_mutex); | ||
490 | /* format changed */ | ||
491 | stop_endpoints(subs, 0, 0, 0); | ||
492 | ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt, | ||
493 | subs->sync_endpoint); | ||
494 | if (ret < 0) | ||
495 | goto unlock; | ||
496 | 514 | ||
497 | if (subs->sync_endpoint) | 515 | return 0; |
498 | ret = snd_usb_endpoint_set_params(subs->sync_endpoint, | ||
499 | hw_params, fmt, NULL); | ||
500 | unlock: | ||
501 | mutex_unlock(&subs->stream->chip->shutdown_mutex); | ||
502 | } | ||
503 | |||
504 | if (ret == 0) { | ||
505 | subs->interface = fmt->iface; | ||
506 | subs->altset_idx = fmt->altset_idx; | ||
507 | } | ||
508 | |||
509 | return ret; | ||
510 | } | 516 | } |
511 | 517 | ||
512 | /* | 518 | /* |
@@ -537,6 +543,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) | |||
537 | { | 543 | { |
538 | struct snd_pcm_runtime *runtime = substream->runtime; | 544 | struct snd_pcm_runtime *runtime = substream->runtime; |
539 | struct snd_usb_substream *subs = runtime->private_data; | 545 | struct snd_usb_substream *subs = runtime->private_data; |
546 | struct usb_host_interface *alts; | ||
547 | struct usb_interface *iface; | ||
548 | int ret; | ||
540 | 549 | ||
541 | if (! subs->cur_audiofmt) { | 550 | if (! subs->cur_audiofmt) { |
542 | snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); | 551 | snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); |
@@ -546,6 +555,27 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) | |||
546 | if (snd_BUG_ON(!subs->data_endpoint)) | 555 | if (snd_BUG_ON(!subs->data_endpoint)) |
547 | return -EIO; | 556 | return -EIO; |
548 | 557 | ||
558 | ret = set_format(subs, subs->cur_audiofmt); | ||
559 | if (ret < 0) | ||
560 | return ret; | ||
561 | |||
562 | iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); | ||
563 | alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; | ||
564 | ret = snd_usb_init_sample_rate(subs->stream->chip, | ||
565 | subs->cur_audiofmt->iface, | ||
566 | alts, | ||
567 | subs->cur_audiofmt, | ||
568 | subs->cur_rate); | ||
569 | if (ret < 0) | ||
570 | return ret; | ||
571 | |||
572 | if (subs->need_setup_ep) { | ||
573 | ret = configure_endpoint(subs); | ||
574 | if (ret < 0) | ||
575 | return ret; | ||
576 | subs->need_setup_ep = false; | ||
577 | } | ||
578 | |||
549 | /* some unit conversions in runtime */ | 579 | /* some unit conversions in runtime */ |
550 | subs->data_endpoint->maxframesize = | 580 | subs->data_endpoint->maxframesize = |
551 | bytes_to_frames(runtime, subs->data_endpoint->maxpacksize); | 581 | bytes_to_frames(runtime, subs->data_endpoint->maxpacksize); |