aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r--sound/usb/pcm.c106
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 */
463static 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 */
504static 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