diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 427 |
1 files changed, 285 insertions, 142 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 735903a74675..21c69074aa17 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -12,7 +12,7 @@ | |||
12 | * Features: | 12 | * Features: |
13 | * o Changes power status of internal codec blocks depending on the | 13 | * o Changes power status of internal codec blocks depending on the |
14 | * dynamic configuration of codec internal audio paths and active | 14 | * dynamic configuration of codec internal audio paths and active |
15 | * DAC's/ADC's. | 15 | * DACs/ADCs. |
16 | * o Platform power domain - can support external components i.e. amps and | 16 | * o Platform power domain - can support external components i.e. amps and |
17 | * mic/meadphone insertion events. | 17 | * mic/meadphone insertion events. |
18 | * o Automatic Mic Bias support | 18 | * o Automatic Mic Bias support |
@@ -52,23 +52,21 @@ | |||
52 | 52 | ||
53 | /* dapm power sequences - make this per codec in the future */ | 53 | /* dapm power sequences - make this per codec in the future */ |
54 | static int dapm_up_seq[] = { | 54 | static int dapm_up_seq[] = { |
55 | snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic, | 55 | snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias, |
56 | snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac, | 56 | snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux, |
57 | snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_pga, | 57 | snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, |
58 | snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post | 58 | snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, |
59 | snd_soc_dapm_post | ||
59 | }; | 60 | }; |
60 | 61 | ||
61 | static int dapm_down_seq[] = { | 62 | static int dapm_down_seq[] = { |
62 | snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, | 63 | snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, |
63 | snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, | 64 | snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, |
64 | snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, | 65 | snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, |
65 | snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_post | 66 | snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply, |
67 | snd_soc_dapm_post | ||
66 | }; | 68 | }; |
67 | 69 | ||
68 | static int dapm_status = 1; | ||
69 | module_param(dapm_status, int, 0); | ||
70 | MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); | ||
71 | |||
72 | static void pop_wait(u32 pop_time) | 70 | static void pop_wait(u32 pop_time) |
73 | { | 71 | { |
74 | if (pop_time) | 72 | if (pop_time) |
@@ -96,6 +94,48 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( | |||
96 | return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); | 94 | return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); |
97 | } | 95 | } |
98 | 96 | ||
97 | /** | ||
98 | * snd_soc_dapm_set_bias_level - set the bias level for the system | ||
99 | * @socdev: audio device | ||
100 | * @level: level to configure | ||
101 | * | ||
102 | * Configure the bias (power) levels for the SoC audio device. | ||
103 | * | ||
104 | * Returns 0 for success else error. | ||
105 | */ | ||
106 | static int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev, | ||
107 | enum snd_soc_bias_level level) | ||
108 | { | ||
109 | struct snd_soc_card *card = socdev->card; | ||
110 | struct snd_soc_codec *codec = socdev->card->codec; | ||
111 | int ret = 0; | ||
112 | |||
113 | switch (level) { | ||
114 | case SND_SOC_BIAS_ON: | ||
115 | dev_dbg(socdev->dev, "Setting full bias\n"); | ||
116 | break; | ||
117 | case SND_SOC_BIAS_PREPARE: | ||
118 | dev_dbg(socdev->dev, "Setting bias prepare\n"); | ||
119 | break; | ||
120 | case SND_SOC_BIAS_STANDBY: | ||
121 | dev_dbg(socdev->dev, "Setting standby bias\n"); | ||
122 | break; | ||
123 | case SND_SOC_BIAS_OFF: | ||
124 | dev_dbg(socdev->dev, "Setting bias off\n"); | ||
125 | break; | ||
126 | default: | ||
127 | dev_err(socdev->dev, "Setting invalid bias %d\n", level); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | if (card->set_bias_level) | ||
132 | ret = card->set_bias_level(card, level); | ||
133 | if (ret == 0 && codec->set_bias_level) | ||
134 | ret = codec->set_bias_level(codec, level); | ||
135 | |||
136 | return ret; | ||
137 | } | ||
138 | |||
99 | /* set up initial codec paths */ | 139 | /* set up initial codec paths */ |
100 | static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | 140 | static void dapm_set_path_status(struct snd_soc_dapm_widget *w, |
101 | struct snd_soc_dapm_path *p, int i) | 141 | struct snd_soc_dapm_path *p, int i) |
@@ -165,6 +205,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
165 | case snd_soc_dapm_dac: | 205 | case snd_soc_dapm_dac: |
166 | case snd_soc_dapm_micbias: | 206 | case snd_soc_dapm_micbias: |
167 | case snd_soc_dapm_vmid: | 207 | case snd_soc_dapm_vmid: |
208 | case snd_soc_dapm_supply: | ||
168 | p->connect = 1; | 209 | p->connect = 1; |
169 | break; | 210 | break; |
170 | /* does effect routing - dynamically connected */ | 211 | /* does effect routing - dynamically connected */ |
@@ -179,7 +220,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
179 | } | 220 | } |
180 | } | 221 | } |
181 | 222 | ||
182 | /* connect mux widget to it's interconnecting audio paths */ | 223 | /* connect mux widget to its interconnecting audio paths */ |
183 | static int dapm_connect_mux(struct snd_soc_codec *codec, | 224 | static int dapm_connect_mux(struct snd_soc_codec *codec, |
184 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | 225 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, |
185 | struct snd_soc_dapm_path *path, const char *control_name, | 226 | struct snd_soc_dapm_path *path, const char *control_name, |
@@ -202,7 +243,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, | |||
202 | return -ENODEV; | 243 | return -ENODEV; |
203 | } | 244 | } |
204 | 245 | ||
205 | /* connect mixer widget to it's interconnecting audio paths */ | 246 | /* connect mixer widget to its interconnecting audio paths */ |
206 | static int dapm_connect_mixer(struct snd_soc_codec *codec, | 247 | static int dapm_connect_mixer(struct snd_soc_codec *codec, |
207 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | 248 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, |
208 | struct snd_soc_dapm_path *path, const char *control_name) | 249 | struct snd_soc_dapm_path *path, const char *control_name) |
@@ -357,8 +398,9 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, | |||
357 | path->long_name); | 398 | path->long_name); |
358 | ret = snd_ctl_add(codec->card, path->kcontrol); | 399 | ret = snd_ctl_add(codec->card, path->kcontrol); |
359 | if (ret < 0) { | 400 | if (ret < 0) { |
360 | printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n", | 401 | printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n", |
361 | path->long_name); | 402 | path->long_name, |
403 | ret); | ||
362 | kfree(path->long_name); | 404 | kfree(path->long_name); |
363 | path->long_name = NULL; | 405 | path->long_name = NULL; |
364 | return ret; | 406 | return ret; |
@@ -434,6 +476,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) | |||
434 | struct snd_soc_dapm_path *path; | 476 | struct snd_soc_dapm_path *path; |
435 | int con = 0; | 477 | int con = 0; |
436 | 478 | ||
479 | if (widget->id == snd_soc_dapm_supply) | ||
480 | return 0; | ||
481 | |||
437 | if (widget->id == snd_soc_dapm_adc && widget->active) | 482 | if (widget->id == snd_soc_dapm_adc && widget->active) |
438 | return 1; | 483 | return 1; |
439 | 484 | ||
@@ -470,6 +515,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) | |||
470 | struct snd_soc_dapm_path *path; | 515 | struct snd_soc_dapm_path *path; |
471 | int con = 0; | 516 | int con = 0; |
472 | 517 | ||
518 | if (widget->id == snd_soc_dapm_supply) | ||
519 | return 0; | ||
520 | |||
473 | /* active stream ? */ | 521 | /* active stream ? */ |
474 | if (widget->id == snd_soc_dapm_dac && widget->active) | 522 | if (widget->id == snd_soc_dapm_dac && widget->active) |
475 | return 1; | 523 | return 1; |
@@ -521,84 +569,12 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, | |||
521 | } | 569 | } |
522 | EXPORT_SYMBOL_GPL(dapm_reg_event); | 570 | EXPORT_SYMBOL_GPL(dapm_reg_event); |
523 | 571 | ||
524 | /* | 572 | /* Standard power change method, used to apply power changes to most |
525 | * Scan a single DAPM widget for a complete audio path and update the | 573 | * widgets. |
526 | * power status appropriately. | ||
527 | */ | 574 | */ |
528 | static int dapm_power_widget(struct snd_soc_codec *codec, int event, | 575 | static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w) |
529 | struct snd_soc_dapm_widget *w) | ||
530 | { | 576 | { |
531 | int in, out, power_change, power, ret; | 577 | int ret; |
532 | |||
533 | /* vmid - no action */ | ||
534 | if (w->id == snd_soc_dapm_vmid) | ||
535 | return 0; | ||
536 | |||
537 | /* active ADC */ | ||
538 | if (w->id == snd_soc_dapm_adc && w->active) { | ||
539 | in = is_connected_input_ep(w); | ||
540 | dapm_clear_walk(w->codec); | ||
541 | w->power = (in != 0) ? 1 : 0; | ||
542 | dapm_update_bits(w); | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | /* active DAC */ | ||
547 | if (w->id == snd_soc_dapm_dac && w->active) { | ||
548 | out = is_connected_output_ep(w); | ||
549 | dapm_clear_walk(w->codec); | ||
550 | w->power = (out != 0) ? 1 : 0; | ||
551 | dapm_update_bits(w); | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | /* pre and post event widgets */ | ||
556 | if (w->id == snd_soc_dapm_pre) { | ||
557 | if (!w->event) | ||
558 | return 0; | ||
559 | |||
560 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
561 | ret = w->event(w, | ||
562 | NULL, SND_SOC_DAPM_PRE_PMU); | ||
563 | if (ret < 0) | ||
564 | return ret; | ||
565 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
566 | ret = w->event(w, | ||
567 | NULL, SND_SOC_DAPM_PRE_PMD); | ||
568 | if (ret < 0) | ||
569 | return ret; | ||
570 | } | ||
571 | return 0; | ||
572 | } | ||
573 | if (w->id == snd_soc_dapm_post) { | ||
574 | if (!w->event) | ||
575 | return 0; | ||
576 | |||
577 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
578 | ret = w->event(w, | ||
579 | NULL, SND_SOC_DAPM_POST_PMU); | ||
580 | if (ret < 0) | ||
581 | return ret; | ||
582 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
583 | ret = w->event(w, | ||
584 | NULL, SND_SOC_DAPM_POST_PMD); | ||
585 | if (ret < 0) | ||
586 | return ret; | ||
587 | } | ||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | /* all other widgets */ | ||
592 | in = is_connected_input_ep(w); | ||
593 | dapm_clear_walk(w->codec); | ||
594 | out = is_connected_output_ep(w); | ||
595 | dapm_clear_walk(w->codec); | ||
596 | power = (out != 0 && in != 0) ? 1 : 0; | ||
597 | power_change = (w->power == power) ? 0 : 1; | ||
598 | w->power = power; | ||
599 | |||
600 | if (!power_change) | ||
601 | return 0; | ||
602 | 578 | ||
603 | /* call any power change event handlers */ | 579 | /* call any power change event handlers */ |
604 | if (w->event) | 580 | if (w->event) |
@@ -607,7 +583,7 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event, | |||
607 | w->name, w->event_flags); | 583 | w->name, w->event_flags); |
608 | 584 | ||
609 | /* power up pre event */ | 585 | /* power up pre event */ |
610 | if (power && w->event && | 586 | if (w->power && w->event && |
611 | (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { | 587 | (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { |
612 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); | 588 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); |
613 | if (ret < 0) | 589 | if (ret < 0) |
@@ -615,7 +591,7 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event, | |||
615 | } | 591 | } |
616 | 592 | ||
617 | /* power down pre event */ | 593 | /* power down pre event */ |
618 | if (!power && w->event && | 594 | if (!w->power && w->event && |
619 | (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { | 595 | (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { |
620 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); | 596 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); |
621 | if (ret < 0) | 597 | if (ret < 0) |
@@ -623,17 +599,17 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event, | |||
623 | } | 599 | } |
624 | 600 | ||
625 | /* Lower PGA volume to reduce pops */ | 601 | /* Lower PGA volume to reduce pops */ |
626 | if (w->id == snd_soc_dapm_pga && !power) | 602 | if (w->id == snd_soc_dapm_pga && !w->power) |
627 | dapm_set_pga(w, power); | 603 | dapm_set_pga(w, w->power); |
628 | 604 | ||
629 | dapm_update_bits(w); | 605 | dapm_update_bits(w); |
630 | 606 | ||
631 | /* Raise PGA volume to reduce pops */ | 607 | /* Raise PGA volume to reduce pops */ |
632 | if (w->id == snd_soc_dapm_pga && power) | 608 | if (w->id == snd_soc_dapm_pga && w->power) |
633 | dapm_set_pga(w, power); | 609 | dapm_set_pga(w, w->power); |
634 | 610 | ||
635 | /* power up post event */ | 611 | /* power up post event */ |
636 | if (power && w->event && | 612 | if (w->power && w->event && |
637 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { | 613 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { |
638 | ret = w->event(w, | 614 | ret = w->event(w, |
639 | NULL, SND_SOC_DAPM_POST_PMU); | 615 | NULL, SND_SOC_DAPM_POST_PMU); |
@@ -642,7 +618,7 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event, | |||
642 | } | 618 | } |
643 | 619 | ||
644 | /* power down post event */ | 620 | /* power down post event */ |
645 | if (!power && w->event && | 621 | if (!w->power && w->event && |
646 | (w->event_flags & SND_SOC_DAPM_POST_PMD)) { | 622 | (w->event_flags & SND_SOC_DAPM_POST_PMD)) { |
647 | ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); | 623 | ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); |
648 | if (ret < 0) | 624 | if (ret < 0) |
@@ -652,6 +628,116 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event, | |||
652 | return 0; | 628 | return 0; |
653 | } | 629 | } |
654 | 630 | ||
631 | /* Generic check to see if a widget should be powered. | ||
632 | */ | ||
633 | static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) | ||
634 | { | ||
635 | int in, out; | ||
636 | |||
637 | in = is_connected_input_ep(w); | ||
638 | dapm_clear_walk(w->codec); | ||
639 | out = is_connected_output_ep(w); | ||
640 | dapm_clear_walk(w->codec); | ||
641 | return out != 0 && in != 0; | ||
642 | } | ||
643 | |||
644 | /* Check to see if an ADC has power */ | ||
645 | static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) | ||
646 | { | ||
647 | int in; | ||
648 | |||
649 | if (w->active) { | ||
650 | in = is_connected_input_ep(w); | ||
651 | dapm_clear_walk(w->codec); | ||
652 | return in != 0; | ||
653 | } else { | ||
654 | return dapm_generic_check_power(w); | ||
655 | } | ||
656 | } | ||
657 | |||
658 | /* Check to see if a DAC has power */ | ||
659 | static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) | ||
660 | { | ||
661 | int out; | ||
662 | |||
663 | if (w->active) { | ||
664 | out = is_connected_output_ep(w); | ||
665 | dapm_clear_walk(w->codec); | ||
666 | return out != 0; | ||
667 | } else { | ||
668 | return dapm_generic_check_power(w); | ||
669 | } | ||
670 | } | ||
671 | |||
672 | /* Check to see if a power supply is needed */ | ||
673 | static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) | ||
674 | { | ||
675 | struct snd_soc_dapm_path *path; | ||
676 | int power = 0; | ||
677 | |||
678 | /* Check if one of our outputs is connected */ | ||
679 | list_for_each_entry(path, &w->sinks, list_source) { | ||
680 | if (path->sink && path->sink->power_check && | ||
681 | path->sink->power_check(path->sink)) { | ||
682 | power = 1; | ||
683 | break; | ||
684 | } | ||
685 | } | ||
686 | |||
687 | dapm_clear_walk(w->codec); | ||
688 | |||
689 | return power; | ||
690 | } | ||
691 | |||
692 | /* | ||
693 | * Scan a single DAPM widget for a complete audio path and update the | ||
694 | * power status appropriately. | ||
695 | */ | ||
696 | static int dapm_power_widget(struct snd_soc_codec *codec, int event, | ||
697 | struct snd_soc_dapm_widget *w) | ||
698 | { | ||
699 | int ret; | ||
700 | |||
701 | switch (w->id) { | ||
702 | case snd_soc_dapm_pre: | ||
703 | if (!w->event) | ||
704 | return 0; | ||
705 | |||
706 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
707 | ret = w->event(w, | ||
708 | NULL, SND_SOC_DAPM_PRE_PMU); | ||
709 | if (ret < 0) | ||
710 | return ret; | ||
711 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
712 | ret = w->event(w, | ||
713 | NULL, SND_SOC_DAPM_PRE_PMD); | ||
714 | if (ret < 0) | ||
715 | return ret; | ||
716 | } | ||
717 | return 0; | ||
718 | |||
719 | case snd_soc_dapm_post: | ||
720 | if (!w->event) | ||
721 | return 0; | ||
722 | |||
723 | if (event == SND_SOC_DAPM_STREAM_START) { | ||
724 | ret = w->event(w, | ||
725 | NULL, SND_SOC_DAPM_POST_PMU); | ||
726 | if (ret < 0) | ||
727 | return ret; | ||
728 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
729 | ret = w->event(w, | ||
730 | NULL, SND_SOC_DAPM_POST_PMD); | ||
731 | if (ret < 0) | ||
732 | return ret; | ||
733 | } | ||
734 | return 0; | ||
735 | |||
736 | default: | ||
737 | return dapm_generic_apply_power(w); | ||
738 | } | ||
739 | } | ||
740 | |||
655 | /* | 741 | /* |
656 | * Scan each dapm widget for complete audio path. | 742 | * Scan each dapm widget for complete audio path. |
657 | * A complete path is a route that has valid endpoints i.e.:- | 743 | * A complete path is a route that has valid endpoints i.e.:- |
@@ -663,31 +749,102 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event, | |||
663 | */ | 749 | */ |
664 | static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | 750 | static int dapm_power_widgets(struct snd_soc_codec *codec, int event) |
665 | { | 751 | { |
752 | struct snd_soc_device *socdev = codec->socdev; | ||
666 | struct snd_soc_dapm_widget *w; | 753 | struct snd_soc_dapm_widget *w; |
667 | int i, c = 1, *seq = NULL, ret = 0; | 754 | int ret = 0; |
668 | 755 | int i, power; | |
669 | /* do we have a sequenced stream event */ | 756 | int sys_power = 0; |
670 | if (event == SND_SOC_DAPM_STREAM_START) { | 757 | |
671 | c = ARRAY_SIZE(dapm_up_seq); | 758 | INIT_LIST_HEAD(&codec->up_list); |
672 | seq = dapm_up_seq; | 759 | INIT_LIST_HEAD(&codec->down_list); |
673 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | 760 | |
674 | c = ARRAY_SIZE(dapm_down_seq); | 761 | /* Check which widgets we need to power and store them in |
675 | seq = dapm_down_seq; | 762 | * lists indicating if they should be powered up or down. |
763 | */ | ||
764 | list_for_each_entry(w, &codec->dapm_widgets, list) { | ||
765 | switch (w->id) { | ||
766 | case snd_soc_dapm_pre: | ||
767 | list_add_tail(&codec->down_list, &w->power_list); | ||
768 | break; | ||
769 | case snd_soc_dapm_post: | ||
770 | list_add_tail(&codec->up_list, &w->power_list); | ||
771 | break; | ||
772 | |||
773 | default: | ||
774 | if (!w->power_check) | ||
775 | continue; | ||
776 | |||
777 | power = w->power_check(w); | ||
778 | if (power) | ||
779 | sys_power = 1; | ||
780 | |||
781 | if (w->power == power) | ||
782 | continue; | ||
783 | |||
784 | if (power) | ||
785 | list_add_tail(&w->power_list, &codec->up_list); | ||
786 | else | ||
787 | list_add_tail(&w->power_list, | ||
788 | &codec->down_list); | ||
789 | |||
790 | w->power = power; | ||
791 | break; | ||
792 | } | ||
676 | } | 793 | } |
677 | 794 | ||
678 | for (i = 0; i < c; i++) { | 795 | /* If we're changing to all on or all off then prepare */ |
679 | list_for_each_entry(w, &codec->dapm_widgets, list) { | 796 | if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) || |
797 | (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) { | ||
798 | ret = snd_soc_dapm_set_bias_level(socdev, | ||
799 | SND_SOC_BIAS_PREPARE); | ||
800 | if (ret != 0) | ||
801 | pr_err("Failed to prepare bias: %d\n", ret); | ||
802 | } | ||
680 | 803 | ||
804 | /* Power down widgets first; try to avoid amplifying pops. */ | ||
805 | for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) { | ||
806 | list_for_each_entry(w, &codec->down_list, power_list) { | ||
681 | /* is widget in stream order */ | 807 | /* is widget in stream order */ |
682 | if (seq && seq[i] && w->id != seq[i]) | 808 | if (w->id != dapm_down_seq[i]) |
683 | continue; | 809 | continue; |
684 | 810 | ||
685 | ret = dapm_power_widget(codec, event, w); | 811 | ret = dapm_power_widget(codec, event, w); |
686 | if (ret != 0) | 812 | if (ret != 0) |
687 | return ret; | 813 | pr_err("Failed to power down %s: %d\n", |
814 | w->name, ret); | ||
688 | } | 815 | } |
689 | } | 816 | } |
690 | 817 | ||
818 | /* Now power up. */ | ||
819 | for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) { | ||
820 | list_for_each_entry(w, &codec->up_list, power_list) { | ||
821 | /* is widget in stream order */ | ||
822 | if (w->id != dapm_up_seq[i]) | ||
823 | continue; | ||
824 | |||
825 | ret = dapm_power_widget(codec, event, w); | ||
826 | if (ret != 0) | ||
827 | pr_err("Failed to power up %s: %d\n", | ||
828 | w->name, ret); | ||
829 | } | ||
830 | } | ||
831 | |||
832 | /* If we just powered the last thing off drop to standby bias */ | ||
833 | if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { | ||
834 | ret = snd_soc_dapm_set_bias_level(socdev, | ||
835 | SND_SOC_BIAS_STANDBY); | ||
836 | if (ret != 0) | ||
837 | pr_err("Failed to apply standby bias: %d\n", ret); | ||
838 | } | ||
839 | |||
840 | /* If we just powered up then move to active bias */ | ||
841 | if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) { | ||
842 | ret = snd_soc_dapm_set_bias_level(socdev, | ||
843 | SND_SOC_BIAS_ON); | ||
844 | if (ret != 0) | ||
845 | pr_err("Failed to apply active bias: %d\n", ret); | ||
846 | } | ||
847 | |||
691 | return 0; | 848 | return 0; |
692 | } | 849 | } |
693 | 850 | ||
@@ -723,6 +880,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) | |||
723 | case snd_soc_dapm_pga: | 880 | case snd_soc_dapm_pga: |
724 | case snd_soc_dapm_mixer: | 881 | case snd_soc_dapm_mixer: |
725 | case snd_soc_dapm_mixer_named_ctl: | 882 | case snd_soc_dapm_mixer_named_ctl: |
883 | case snd_soc_dapm_supply: | ||
726 | if (w->name) { | 884 | if (w->name) { |
727 | in = is_connected_input_ep(w); | 885 | in = is_connected_input_ep(w); |
728 | dapm_clear_walk(w->codec); | 886 | dapm_clear_walk(w->codec); |
@@ -851,6 +1009,7 @@ static ssize_t dapm_widget_show(struct device *dev, | |||
851 | case snd_soc_dapm_pga: | 1009 | case snd_soc_dapm_pga: |
852 | case snd_soc_dapm_mixer: | 1010 | case snd_soc_dapm_mixer: |
853 | case snd_soc_dapm_mixer_named_ctl: | 1011 | case snd_soc_dapm_mixer_named_ctl: |
1012 | case snd_soc_dapm_supply: | ||
854 | if (w->name) | 1013 | if (w->name) |
855 | count += sprintf(buf + count, "%s: %s\n", | 1014 | count += sprintf(buf + count, "%s: %s\n", |
856 | w->name, w->power ? "On":"Off"); | 1015 | w->name, w->power ? "On":"Off"); |
@@ -883,16 +1042,12 @@ static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); | |||
883 | 1042 | ||
884 | int snd_soc_dapm_sys_add(struct device *dev) | 1043 | int snd_soc_dapm_sys_add(struct device *dev) |
885 | { | 1044 | { |
886 | if (!dapm_status) | ||
887 | return 0; | ||
888 | return device_create_file(dev, &dev_attr_dapm_widget); | 1045 | return device_create_file(dev, &dev_attr_dapm_widget); |
889 | } | 1046 | } |
890 | 1047 | ||
891 | static void snd_soc_dapm_sys_remove(struct device *dev) | 1048 | static void snd_soc_dapm_sys_remove(struct device *dev) |
892 | { | 1049 | { |
893 | if (dapm_status) { | 1050 | device_remove_file(dev, &dev_attr_dapm_widget); |
894 | device_remove_file(dev, &dev_attr_dapm_widget); | ||
895 | } | ||
896 | } | 1051 | } |
897 | 1052 | ||
898 | /* free all dapm widgets and resources */ | 1053 | /* free all dapm widgets and resources */ |
@@ -1015,6 +1170,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, | |||
1015 | case snd_soc_dapm_vmid: | 1170 | case snd_soc_dapm_vmid: |
1016 | case snd_soc_dapm_pre: | 1171 | case snd_soc_dapm_pre: |
1017 | case snd_soc_dapm_post: | 1172 | case snd_soc_dapm_post: |
1173 | case snd_soc_dapm_supply: | ||
1018 | list_add(&path->list, &codec->dapm_paths); | 1174 | list_add(&path->list, &codec->dapm_paths); |
1019 | list_add(&path->list_sink, &wsink->sources); | 1175 | list_add(&path->list_sink, &wsink->sources); |
1020 | list_add(&path->list_source, &wsource->sinks); | 1176 | list_add(&path->list_source, &wsource->sinks); |
@@ -1108,15 +1264,22 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) | |||
1108 | case snd_soc_dapm_switch: | 1264 | case snd_soc_dapm_switch: |
1109 | case snd_soc_dapm_mixer: | 1265 | case snd_soc_dapm_mixer: |
1110 | case snd_soc_dapm_mixer_named_ctl: | 1266 | case snd_soc_dapm_mixer_named_ctl: |
1267 | w->power_check = dapm_generic_check_power; | ||
1111 | dapm_new_mixer(codec, w); | 1268 | dapm_new_mixer(codec, w); |
1112 | break; | 1269 | break; |
1113 | case snd_soc_dapm_mux: | 1270 | case snd_soc_dapm_mux: |
1114 | case snd_soc_dapm_value_mux: | 1271 | case snd_soc_dapm_value_mux: |
1272 | w->power_check = dapm_generic_check_power; | ||
1115 | dapm_new_mux(codec, w); | 1273 | dapm_new_mux(codec, w); |
1116 | break; | 1274 | break; |
1117 | case snd_soc_dapm_adc: | 1275 | case snd_soc_dapm_adc: |
1276 | w->power_check = dapm_adc_check_power; | ||
1277 | break; | ||
1118 | case snd_soc_dapm_dac: | 1278 | case snd_soc_dapm_dac: |
1279 | w->power_check = dapm_dac_check_power; | ||
1280 | break; | ||
1119 | case snd_soc_dapm_pga: | 1281 | case snd_soc_dapm_pga: |
1282 | w->power_check = dapm_generic_check_power; | ||
1120 | dapm_new_pga(codec, w); | 1283 | dapm_new_pga(codec, w); |
1121 | break; | 1284 | break; |
1122 | case snd_soc_dapm_input: | 1285 | case snd_soc_dapm_input: |
@@ -1126,6 +1289,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) | |||
1126 | case snd_soc_dapm_hp: | 1289 | case snd_soc_dapm_hp: |
1127 | case snd_soc_dapm_mic: | 1290 | case snd_soc_dapm_mic: |
1128 | case snd_soc_dapm_line: | 1291 | case snd_soc_dapm_line: |
1292 | w->power_check = dapm_generic_check_power; | ||
1293 | break; | ||
1294 | case snd_soc_dapm_supply: | ||
1295 | w->power_check = dapm_supply_check_power; | ||
1129 | case snd_soc_dapm_vmid: | 1296 | case snd_soc_dapm_vmid: |
1130 | case snd_soc_dapm_pre: | 1297 | case snd_soc_dapm_pre: |
1131 | case snd_soc_dapm_post: | 1298 | case snd_soc_dapm_post: |
@@ -1626,35 +1793,11 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, | |||
1626 | EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); | 1793 | EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); |
1627 | 1794 | ||
1628 | /** | 1795 | /** |
1629 | * snd_soc_dapm_set_bias_level - set the bias level for the system | ||
1630 | * @socdev: audio device | ||
1631 | * @level: level to configure | ||
1632 | * | ||
1633 | * Configure the bias (power) levels for the SoC audio device. | ||
1634 | * | ||
1635 | * Returns 0 for success else error. | ||
1636 | */ | ||
1637 | int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev, | ||
1638 | enum snd_soc_bias_level level) | ||
1639 | { | ||
1640 | struct snd_soc_card *card = socdev->card; | ||
1641 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1642 | int ret = 0; | ||
1643 | |||
1644 | if (card->set_bias_level) | ||
1645 | ret = card->set_bias_level(card, level); | ||
1646 | if (ret == 0 && codec->set_bias_level) | ||
1647 | ret = codec->set_bias_level(codec, level); | ||
1648 | |||
1649 | return ret; | ||
1650 | } | ||
1651 | |||
1652 | /** | ||
1653 | * snd_soc_dapm_enable_pin - enable pin. | 1796 | * snd_soc_dapm_enable_pin - enable pin. |
1654 | * @codec: SoC codec | 1797 | * @codec: SoC codec |
1655 | * @pin: pin name | 1798 | * @pin: pin name |
1656 | * | 1799 | * |
1657 | * Enables input/output pin and it's parents or children widgets iff there is | 1800 | * Enables input/output pin and its parents or children widgets iff there is |
1658 | * a valid audio route and active audio stream. | 1801 | * a valid audio route and active audio stream. |
1659 | * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | 1802 | * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to |
1660 | * do any widget power switching. | 1803 | * do any widget power switching. |
@@ -1670,7 +1813,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); | |||
1670 | * @codec: SoC codec | 1813 | * @codec: SoC codec |
1671 | * @pin: pin name | 1814 | * @pin: pin name |
1672 | * | 1815 | * |
1673 | * Disables input/output pin and it's parents or children widgets. | 1816 | * Disables input/output pin and its parents or children widgets. |
1674 | * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | 1817 | * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to |
1675 | * do any widget power switching. | 1818 | * do any widget power switching. |
1676 | */ | 1819 | */ |