diff options
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 106 |
1 files changed, 99 insertions, 7 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 769821c30031..c6593101c049 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c | |||
@@ -454,6 +454,103 @@ add_sync_ep: | |||
454 | } | 454 | } |
455 | 455 | ||
456 | /* | 456 | /* |
457 | * Return the score of matching two audioformats. | ||
458 | * Veto the audioformat if: | ||
459 | * - It has no channels for some reason. | ||
460 | * - Requested PCM format is not supported. | ||
461 | * - Requested sample rate is not supported. | ||
462 | */ | ||
463 | static int match_endpoint_audioformats(struct audioformat *fp, | ||
464 | struct audioformat *match, int rate, | ||
465 | snd_pcm_format_t pcm_format) | ||
466 | { | ||
467 | int i; | ||
468 | int score = 0; | ||
469 | |||
470 | if (fp->channels < 1) { | ||
471 | snd_printdd("%s: (fmt @%p) no channels\n", __func__, fp); | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | if (!(fp->formats & (1ULL << pcm_format))) { | ||
476 | snd_printdd("%s: (fmt @%p) no match for format %d\n", __func__, | ||
477 | fp, pcm_format); | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | for (i = 0; i < fp->nr_rates; i++) { | ||
482 | if (fp->rate_table[i] == rate) { | ||
483 | score++; | ||
484 | break; | ||
485 | } | ||
486 | } | ||
487 | if (!score) { | ||
488 | snd_printdd("%s: (fmt @%p) no match for rate %d\n", __func__, | ||
489 | fp, rate); | ||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | if (fp->channels == match->channels) | ||
494 | score++; | ||
495 | |||
496 | snd_printdd("%s: (fmt @%p) score %d\n", __func__, fp, score); | ||
497 | |||
498 | return score; | ||
499 | } | ||
500 | |||
501 | /* | ||
502 | * Configure the sync ep using the rate and pcm format of the data ep. | ||
503 | */ | ||
504 | static int configure_sync_endpoint(struct snd_usb_substream *subs) | ||
505 | { | ||
506 | int ret; | ||
507 | struct audioformat *fp; | ||
508 | struct audioformat *sync_fp = NULL; | ||
509 | int cur_score = 0; | ||
510 | int sync_period_bytes = subs->period_bytes; | ||
511 | struct snd_usb_substream *sync_subs = | ||
512 | &subs->stream->substream[subs->direction ^ 1]; | ||
513 | |||
514 | /* Try to find the best matching audioformat. */ | ||
515 | list_for_each_entry(fp, &sync_subs->fmt_list, list) { | ||
516 | int score = match_endpoint_audioformats(fp, subs->cur_audiofmt, | ||
517 | subs->cur_rate, subs->pcm_format); | ||
518 | |||
519 | if (score > cur_score) { | ||
520 | sync_fp = fp; | ||
521 | cur_score = score; | ||
522 | } | ||
523 | } | ||
524 | |||
525 | if (unlikely(sync_fp == NULL)) { | ||
526 | snd_printk(KERN_ERR "%s: no valid audioformat for sync ep %x found\n", | ||
527 | __func__, sync_subs->ep_num); | ||
528 | return -EINVAL; | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * Recalculate the period bytes if channel number differ between | ||
533 | * data and sync ep audioformat. | ||
534 | */ | ||
535 | if (sync_fp->channels != subs->channels) { | ||
536 | sync_period_bytes = (subs->period_bytes / subs->channels) * | ||
537 | sync_fp->channels; | ||
538 | snd_printdd("%s: adjusted sync ep period bytes (%d -> %d)\n", | ||
539 | __func__, subs->period_bytes, sync_period_bytes); | ||
540 | } | ||
541 | |||
542 | ret = snd_usb_endpoint_set_params(subs->sync_endpoint, | ||
543 | subs->pcm_format, | ||
544 | sync_fp->channels, | ||
545 | sync_period_bytes, | ||
546 | subs->cur_rate, | ||
547 | sync_fp, | ||
548 | NULL); | ||
549 | |||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | /* | ||
457 | * configure endpoint params | 554 | * configure endpoint params |
458 | * | 555 | * |
459 | * called during initial setup and upon resume | 556 | * called during initial setup and upon resume |
@@ -475,13 +572,8 @@ static int configure_endpoint(struct snd_usb_substream *subs) | |||
475 | return ret; | 572 | return ret; |
476 | 573 | ||
477 | if (subs->sync_endpoint) | 574 | if (subs->sync_endpoint) |
478 | ret = snd_usb_endpoint_set_params(subs->sync_endpoint, | 575 | ret = configure_sync_endpoint(subs); |
479 | subs->pcm_format, | 576 | |
480 | subs->channels, | ||
481 | subs->period_bytes, | ||
482 | subs->cur_rate, | ||
483 | subs->cur_audiofmt, | ||
484 | NULL); | ||
485 | return ret; | 577 | return ret; |
486 | } | 578 | } |
487 | 579 | ||