diff options
| -rw-r--r-- | sound/soc/soc-dapm.c | 270 |
1 files changed, 136 insertions, 134 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1d6a9b3ceb27..687784463db9 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 | } |
