diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 319 |
1 files changed, 171 insertions, 148 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 33acd8b892dc..21779a6a781a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -504,17 +504,27 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, | |||
504 | return 0; | 504 | return 0; |
505 | } | 505 | } |
506 | 506 | ||
507 | /* create new dapm mixer control */ | 507 | /* |
508 | static int dapm_new_mixer(struct snd_soc_dapm_widget *w) | 508 | * Determine if a kcontrol is shared. If it is, look it up. If it isn't, |
509 | * create it. Either way, add the widget into the control's widget list | ||
510 | */ | ||
511 | static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, | ||
512 | int kci, struct snd_soc_dapm_path *path) | ||
509 | { | 513 | { |
510 | struct snd_soc_dapm_context *dapm = w->dapm; | 514 | struct snd_soc_dapm_context *dapm = w->dapm; |
511 | int i, ret = 0; | ||
512 | size_t name_len, prefix_len; | ||
513 | struct snd_soc_dapm_path *path; | ||
514 | struct snd_card *card = dapm->card->snd_card; | 515 | struct snd_card *card = dapm->card->snd_card; |
515 | const char *prefix; | 516 | const char *prefix; |
517 | size_t prefix_len; | ||
518 | int shared; | ||
519 | struct snd_kcontrol *kcontrol; | ||
516 | struct snd_soc_dapm_widget_list *wlist; | 520 | struct snd_soc_dapm_widget_list *wlist; |
521 | int wlistentries; | ||
517 | size_t wlistsize; | 522 | size_t wlistsize; |
523 | bool wname_in_long_name, kcname_in_long_name; | ||
524 | size_t name_len; | ||
525 | char *long_name; | ||
526 | const char *name; | ||
527 | int ret; | ||
518 | 528 | ||
519 | if (dapm->codec) | 529 | if (dapm->codec) |
520 | prefix = dapm->codec->name_prefix; | 530 | prefix = dapm->codec->name_prefix; |
@@ -526,103 +536,141 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) | |||
526 | else | 536 | else |
527 | prefix_len = 0; | 537 | prefix_len = 0; |
528 | 538 | ||
529 | /* add kcontrol */ | 539 | shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci], |
530 | for (i = 0; i < w->num_kcontrols; i++) { | 540 | &kcontrol); |
531 | 541 | ||
532 | /* match name */ | 542 | if (kcontrol) { |
533 | list_for_each_entry(path, &w->sources, list_sink) { | 543 | wlist = kcontrol->private_data; |
544 | wlistentries = wlist->num_widgets + 1; | ||
545 | } else { | ||
546 | wlist = NULL; | ||
547 | wlistentries = 1; | ||
548 | } | ||
534 | 549 | ||
535 | /* mixer/mux paths name must match control name */ | 550 | wlistsize = sizeof(struct snd_soc_dapm_widget_list) + |
536 | if (path->name != (char *)w->kcontrol_news[i].name) | 551 | wlistentries * sizeof(struct snd_soc_dapm_widget *); |
537 | continue; | 552 | wlist = krealloc(wlist, wlistsize, GFP_KERNEL); |
553 | if (wlist == NULL) { | ||
554 | dev_err(dapm->dev, "ASoC: can't allocate widget list for %s\n", | ||
555 | w->name); | ||
556 | return -ENOMEM; | ||
557 | } | ||
558 | wlist->num_widgets = wlistentries; | ||
559 | wlist->widgets[wlistentries - 1] = w; | ||
538 | 560 | ||
539 | if (w->kcontrols[i]) { | 561 | if (!kcontrol) { |
540 | path->kcontrol = w->kcontrols[i]; | 562 | if (shared) { |
541 | continue; | 563 | wname_in_long_name = false; |
564 | kcname_in_long_name = true; | ||
565 | } else { | ||
566 | switch (w->id) { | ||
567 | case snd_soc_dapm_switch: | ||
568 | case snd_soc_dapm_mixer: | ||
569 | wname_in_long_name = true; | ||
570 | kcname_in_long_name = true; | ||
571 | break; | ||
572 | case snd_soc_dapm_mixer_named_ctl: | ||
573 | wname_in_long_name = false; | ||
574 | kcname_in_long_name = true; | ||
575 | break; | ||
576 | case snd_soc_dapm_mux: | ||
577 | case snd_soc_dapm_virt_mux: | ||
578 | case snd_soc_dapm_value_mux: | ||
579 | wname_in_long_name = true; | ||
580 | kcname_in_long_name = false; | ||
581 | break; | ||
582 | default: | ||
583 | kfree(wlist); | ||
584 | return -EINVAL; | ||
542 | } | 585 | } |
586 | } | ||
587 | |||
588 | if (wname_in_long_name && kcname_in_long_name) { | ||
589 | name_len = strlen(w->name) - prefix_len + 1 + | ||
590 | strlen(w->kcontrol_news[kci].name) + 1; | ||
543 | 591 | ||
544 | wlistsize = sizeof(struct snd_soc_dapm_widget_list) + | 592 | long_name = kmalloc(name_len, GFP_KERNEL); |
545 | sizeof(struct snd_soc_dapm_widget *), | 593 | if (long_name == NULL) { |
546 | wlist = kzalloc(wlistsize, GFP_KERNEL); | 594 | kfree(wlist); |
547 | if (wlist == NULL) { | ||
548 | dev_err(dapm->dev, | ||
549 | "ASoC: can't allocate widget list for %s\n", | ||
550 | w->name); | ||
551 | return -ENOMEM; | 595 | return -ENOMEM; |
552 | } | 596 | } |
553 | wlist->num_widgets = 1; | 597 | |
554 | wlist->widgets[0] = w; | 598 | /* |
555 | 599 | * The control will get a prefix from the control | |
556 | /* add dapm control with long name. | 600 | * creation process but we're also using the same |
557 | * for dapm_mixer this is the concatenation of the | 601 | * prefix for widgets so cut the prefix off the |
558 | * mixer and kcontrol name. | 602 | * front of the widget name. |
559 | * for dapm_mixer_named_ctl this is simply the | ||
560 | * kcontrol name. | ||
561 | */ | 603 | */ |
562 | name_len = strlen(w->kcontrol_news[i].name) + 1; | 604 | snprintf(long_name, name_len, "%s %s", |
563 | if (w->id != snd_soc_dapm_mixer_named_ctl) | 605 | w->name + prefix_len, |
564 | name_len += 1 + strlen(w->name); | 606 | w->kcontrol_news[kci].name); |
607 | long_name[name_len - 1] = '\0'; | ||
608 | |||
609 | name = long_name; | ||
610 | } else if (wname_in_long_name) { | ||
611 | long_name = NULL; | ||
612 | name = w->name + prefix_len; | ||
613 | } else { | ||
614 | long_name = NULL; | ||
615 | name = w->kcontrol_news[kci].name; | ||
616 | } | ||
565 | 617 | ||
566 | path->long_name = kmalloc(name_len, GFP_KERNEL); | 618 | kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name, |
619 | prefix); | ||
620 | ret = snd_ctl_add(card, kcontrol); | ||
621 | if (ret < 0) { | ||
622 | dev_err(dapm->dev, | ||
623 | "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", | ||
624 | w->name, name, ret); | ||
625 | kfree(wlist); | ||
626 | kfree(long_name); | ||
627 | return ret; | ||
628 | } | ||
567 | 629 | ||
568 | if (path->long_name == NULL) { | 630 | path->long_name = long_name; |
569 | kfree(wlist); | 631 | } |
570 | return -ENOMEM; | ||
571 | } | ||
572 | 632 | ||
573 | switch (w->id) { | 633 | kcontrol->private_data = wlist; |
574 | default: | 634 | w->kcontrols[kci] = kcontrol; |
575 | /* The control will get a prefix from | 635 | path->kcontrol = kcontrol; |
576 | * the control creation process but | ||
577 | * we're also using the same prefix | ||
578 | * for widgets so cut the prefix off | ||
579 | * the front of the widget name. | ||
580 | */ | ||
581 | snprintf((char *)path->long_name, name_len, | ||
582 | "%s %s", w->name + prefix_len, | ||
583 | w->kcontrol_news[i].name); | ||
584 | break; | ||
585 | case snd_soc_dapm_mixer_named_ctl: | ||
586 | snprintf((char *)path->long_name, name_len, | ||
587 | "%s", w->kcontrol_news[i].name); | ||
588 | break; | ||
589 | } | ||
590 | 636 | ||
591 | ((char *)path->long_name)[name_len - 1] = '\0'; | 637 | return 0; |
638 | } | ||
592 | 639 | ||
593 | path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i], | 640 | /* create new dapm mixer control */ |
594 | wlist, path->long_name, | 641 | static int dapm_new_mixer(struct snd_soc_dapm_widget *w) |
595 | prefix); | 642 | { |
596 | ret = snd_ctl_add(card, path->kcontrol); | 643 | int i, ret; |
597 | if (ret < 0) { | 644 | struct snd_soc_dapm_path *path; |
598 | dev_err(dapm->dev, "ASoC: failed to add widget" | 645 | |
599 | " %s dapm kcontrol %s: %d\n", | 646 | /* add kcontrol */ |
600 | w->name, path->long_name, ret); | 647 | for (i = 0; i < w->num_kcontrols; i++) { |
601 | kfree(wlist); | 648 | /* match name */ |
602 | kfree(path->long_name); | 649 | list_for_each_entry(path, &w->sources, list_sink) { |
603 | path->long_name = NULL; | 650 | /* mixer/mux paths name must match control name */ |
604 | return ret; | 651 | if (path->name != (char *)w->kcontrol_news[i].name) |
652 | continue; | ||
653 | |||
654 | if (w->kcontrols[i]) { | ||
655 | path->kcontrol = w->kcontrols[i]; | ||
656 | continue; | ||
605 | } | 657 | } |
606 | w->kcontrols[i] = path->kcontrol; | 658 | |
659 | ret = dapm_create_or_share_mixmux_kcontrol(w, i, path); | ||
660 | if (ret < 0) | ||
661 | return ret; | ||
607 | } | 662 | } |
608 | } | 663 | } |
609 | return ret; | 664 | |
665 | return 0; | ||
610 | } | 666 | } |
611 | 667 | ||
612 | /* create new dapm mux control */ | 668 | /* create new dapm mux control */ |
613 | static int dapm_new_mux(struct snd_soc_dapm_widget *w) | 669 | static int dapm_new_mux(struct snd_soc_dapm_widget *w) |
614 | { | 670 | { |
615 | struct snd_soc_dapm_context *dapm = w->dapm; | 671 | struct snd_soc_dapm_context *dapm = w->dapm; |
616 | struct snd_soc_dapm_path *path = NULL; | 672 | struct snd_soc_dapm_path *path; |
617 | struct snd_kcontrol *kcontrol; | ||
618 | struct snd_card *card = dapm->card->snd_card; | ||
619 | const char *prefix; | ||
620 | size_t prefix_len; | ||
621 | int ret; | 673 | int ret; |
622 | struct snd_soc_dapm_widget_list *wlist; | ||
623 | int shared, wlistentries; | ||
624 | size_t wlistsize; | ||
625 | const char *name; | ||
626 | 674 | ||
627 | if (w->num_kcontrols != 1) { | 675 | if (w->num_kcontrols != 1) { |
628 | dev_err(dapm->dev, | 676 | dev_err(dapm->dev, |
@@ -631,65 +679,19 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) | |||
631 | return -EINVAL; | 679 | return -EINVAL; |
632 | } | 680 | } |
633 | 681 | ||
634 | shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0], | 682 | path = list_first_entry(&w->sources, struct snd_soc_dapm_path, |
635 | &kcontrol); | 683 | list_sink); |
636 | if (kcontrol) { | 684 | if (!path) { |
637 | wlist = kcontrol->private_data; | 685 | dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name); |
638 | wlistentries = wlist->num_widgets + 1; | 686 | return -EINVAL; |
639 | } else { | ||
640 | wlist = NULL; | ||
641 | wlistentries = 1; | ||
642 | } | ||
643 | wlistsize = sizeof(struct snd_soc_dapm_widget_list) + | ||
644 | wlistentries * sizeof(struct snd_soc_dapm_widget *), | ||
645 | wlist = krealloc(wlist, wlistsize, GFP_KERNEL); | ||
646 | if (wlist == NULL) { | ||
647 | dev_err(dapm->dev, | ||
648 | "ASoC: can't allocate widget list for %s\n", w->name); | ||
649 | return -ENOMEM; | ||
650 | } | ||
651 | wlist->num_widgets = wlistentries; | ||
652 | wlist->widgets[wlistentries - 1] = w; | ||
653 | |||
654 | if (!kcontrol) { | ||
655 | if (dapm->codec) | ||
656 | prefix = dapm->codec->name_prefix; | ||
657 | else | ||
658 | prefix = NULL; | ||
659 | |||
660 | if (shared) { | ||
661 | name = w->kcontrol_news[0].name; | ||
662 | prefix_len = 0; | ||
663 | } else { | ||
664 | name = w->name; | ||
665 | if (prefix) | ||
666 | prefix_len = strlen(prefix) + 1; | ||
667 | else | ||
668 | prefix_len = 0; | ||
669 | } | ||
670 | |||
671 | /* | ||
672 | * The control will get a prefix from the control creation | ||
673 | * process but we're also using the same prefix for widgets so | ||
674 | * cut the prefix off the front of the widget name. | ||
675 | */ | ||
676 | kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist, | ||
677 | name + prefix_len, prefix); | ||
678 | ret = snd_ctl_add(card, kcontrol); | ||
679 | if (ret < 0) { | ||
680 | dev_err(dapm->dev, "ASoC: failed to add kcontrol %s: %d\n", | ||
681 | w->name, ret); | ||
682 | kfree(wlist); | ||
683 | return ret; | ||
684 | } | ||
685 | } | 687 | } |
686 | 688 | ||
687 | kcontrol->private_data = wlist; | 689 | ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path); |
688 | 690 | if (ret < 0) | |
689 | w->kcontrols[0] = kcontrol; | 691 | return ret; |
690 | 692 | ||
691 | list_for_each_entry(path, &w->sources, list_sink) | 693 | list_for_each_entry(path, &w->sources, list_sink) |
692 | path->kcontrol = kcontrol; | 694 | path->kcontrol = w->kcontrols[0]; |
693 | 695 | ||
694 | return 0; | 696 | return 0; |
695 | } | 697 | } |
@@ -705,14 +707,33 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) | |||
705 | } | 707 | } |
706 | 708 | ||
707 | /* reset 'walked' bit for each dapm path */ | 709 | /* reset 'walked' bit for each dapm path */ |
708 | static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) | 710 | static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm, |
711 | struct list_head *sink) | ||
709 | { | 712 | { |
710 | struct snd_soc_dapm_path *p; | 713 | struct snd_soc_dapm_path *p; |
711 | 714 | ||
712 | list_for_each_entry(p, &dapm->card->paths, list) | 715 | list_for_each_entry(p, sink, list_source) { |
713 | p->walked = 0; | 716 | if (p->walked) { |
717 | p->walked = 0; | ||
718 | dapm_clear_walk_output(dapm, &p->sink->sinks); | ||
719 | } | ||
720 | } | ||
714 | } | 721 | } |
715 | 722 | ||
723 | static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm, | ||
724 | struct list_head *source) | ||
725 | { | ||
726 | struct snd_soc_dapm_path *p; | ||
727 | |||
728 | list_for_each_entry(p, source, list_sink) { | ||
729 | if (p->walked) { | ||
730 | p->walked = 0; | ||
731 | dapm_clear_walk_input(dapm, &p->source->sources); | ||
732 | } | ||
733 | } | ||
734 | } | ||
735 | |||
736 | |||
716 | /* We implement power down on suspend by checking the power state of | 737 | /* We implement power down on suspend by checking the power state of |
717 | * the ALSA card - when we are suspending the ALSA state for the card | 738 | * the ALSA card - when we are suspending the ALSA state for the card |
718 | * is set to D3. | 739 | * is set to D3. |
@@ -995,13 +1016,17 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | |||
995 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); | 1016 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
996 | dapm_reset(card); | 1017 | dapm_reset(card); |
997 | 1018 | ||
998 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | 1019 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
999 | paths = is_connected_output_ep(dai->playback_widget, list); | 1020 | paths = is_connected_output_ep(dai->playback_widget, list); |
1000 | else | 1021 | dapm_clear_walk_output(&card->dapm, |
1022 | &dai->playback_widget->sinks); | ||
1023 | } else { | ||
1001 | paths = is_connected_input_ep(dai->capture_widget, list); | 1024 | paths = is_connected_input_ep(dai->capture_widget, list); |
1025 | dapm_clear_walk_input(&card->dapm, | ||
1026 | &dai->capture_widget->sources); | ||
1027 | } | ||
1002 | 1028 | ||
1003 | trace_snd_soc_dapm_connected(paths, stream); | 1029 | trace_snd_soc_dapm_connected(paths, stream); |
1004 | dapm_clear_walk(&card->dapm); | ||
1005 | mutex_unlock(&card->dapm_mutex); | 1030 | mutex_unlock(&card->dapm_mutex); |
1006 | 1031 | ||
1007 | return paths; | 1032 | return paths; |
@@ -1104,9 +1129,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) | |||
1104 | DAPM_UPDATE_STAT(w, power_checks); | 1129 | DAPM_UPDATE_STAT(w, power_checks); |
1105 | 1130 | ||
1106 | in = is_connected_input_ep(w, NULL); | 1131 | in = is_connected_input_ep(w, NULL); |
1107 | dapm_clear_walk(w->dapm); | 1132 | dapm_clear_walk_input(w->dapm, &w->sources); |
1108 | out = is_connected_output_ep(w, NULL); | 1133 | out = is_connected_output_ep(w, NULL); |
1109 | dapm_clear_walk(w->dapm); | 1134 | dapm_clear_walk_output(w->dapm, &w->sinks); |
1110 | return out != 0 && in != 0; | 1135 | return out != 0 && in != 0; |
1111 | } | 1136 | } |
1112 | 1137 | ||
@@ -1129,7 +1154,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) | |||
1129 | 1154 | ||
1130 | if (w->active) { | 1155 | if (w->active) { |
1131 | in = is_connected_input_ep(w, NULL); | 1156 | in = is_connected_input_ep(w, NULL); |
1132 | dapm_clear_walk(w->dapm); | 1157 | dapm_clear_walk_input(w->dapm, &w->sources); |
1133 | return in != 0; | 1158 | return in != 0; |
1134 | } else { | 1159 | } else { |
1135 | return dapm_generic_check_power(w); | 1160 | return dapm_generic_check_power(w); |
@@ -1145,7 +1170,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) | |||
1145 | 1170 | ||
1146 | if (w->active) { | 1171 | if (w->active) { |
1147 | out = is_connected_output_ep(w, NULL); | 1172 | out = is_connected_output_ep(w, NULL); |
1148 | dapm_clear_walk(w->dapm); | 1173 | dapm_clear_walk_output(w->dapm, &w->sinks); |
1149 | return out != 0; | 1174 | return out != 0; |
1150 | } else { | 1175 | } else { |
1151 | return dapm_generic_check_power(w); | 1176 | return dapm_generic_check_power(w); |
@@ -1177,8 +1202,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) | |||
1177 | return 1; | 1202 | return 1; |
1178 | } | 1203 | } |
1179 | 1204 | ||
1180 | dapm_clear_walk(w->dapm); | ||
1181 | |||
1182 | return 0; | 1205 | return 0; |
1183 | } | 1206 | } |
1184 | 1207 | ||
@@ -1759,9 +1782,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file, | |||
1759 | return -ENOMEM; | 1782 | return -ENOMEM; |
1760 | 1783 | ||
1761 | in = is_connected_input_ep(w, NULL); | 1784 | in = is_connected_input_ep(w, NULL); |
1762 | dapm_clear_walk(w->dapm); | 1785 | dapm_clear_walk_input(w->dapm, &w->sources); |
1763 | out = is_connected_output_ep(w, NULL); | 1786 | out = is_connected_output_ep(w, NULL); |
1764 | dapm_clear_walk(w->dapm); | 1787 | dapm_clear_walk_output(w->dapm, &w->sinks); |
1765 | 1788 | ||
1766 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", | 1789 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", |
1767 | w->name, w->power ? "On" : "Off", | 1790 | w->name, w->power ? "On" : "Off", |