diff options
Diffstat (limited to 'sound/soc/codecs/wm8996.c')
-rw-r--r-- | sound/soc/codecs/wm8996.c | 321 |
1 files changed, 254 insertions, 67 deletions
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 0cdb9d105671..645c980d6b80 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c | |||
@@ -41,12 +41,11 @@ | |||
41 | #define HPOUT2L 4 | 41 | #define HPOUT2L 4 |
42 | #define HPOUT2R 8 | 42 | #define HPOUT2R 8 |
43 | 43 | ||
44 | #define WM8996_NUM_SUPPLIES 4 | 44 | #define WM8996_NUM_SUPPLIES 3 |
45 | static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = { | 45 | static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = { |
46 | "DBVDD", | 46 | "DBVDD", |
47 | "AVDD1", | 47 | "AVDD1", |
48 | "AVDD2", | 48 | "AVDD2", |
49 | "CPVDD", | ||
50 | }; | 49 | }; |
51 | 50 | ||
52 | struct wm8996_priv { | 51 | struct wm8996_priv { |
@@ -71,6 +70,8 @@ struct wm8996_priv { | |||
71 | 70 | ||
72 | struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES]; | 71 | struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES]; |
73 | struct notifier_block disable_nb[WM8996_NUM_SUPPLIES]; | 72 | struct notifier_block disable_nb[WM8996_NUM_SUPPLIES]; |
73 | struct regulator *cpvdd; | ||
74 | int bg_ena; | ||
74 | 75 | ||
75 | struct wm8996_pdata pdata; | 76 | struct wm8996_pdata pdata; |
76 | 77 | ||
@@ -112,7 +113,6 @@ static int wm8996_regulator_event_##n(struct notifier_block *nb, \ | |||
112 | WM8996_REGULATOR_EVENT(0) | 113 | WM8996_REGULATOR_EVENT(0) |
113 | WM8996_REGULATOR_EVENT(1) | 114 | WM8996_REGULATOR_EVENT(1) |
114 | WM8996_REGULATOR_EVENT(2) | 115 | WM8996_REGULATOR_EVENT(2) |
115 | WM8996_REGULATOR_EVENT(3) | ||
116 | 116 | ||
117 | static const u16 wm8996_reg[WM8996_MAX_REGISTER] = { | 117 | static const u16 wm8996_reg[WM8996_MAX_REGISTER] = { |
118 | [WM8996_SOFTWARE_RESET] = 0x8996, | 118 | [WM8996_SOFTWARE_RESET] = 0x8996, |
@@ -414,6 +414,7 @@ static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0); | |||
414 | static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0); | 414 | static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0); |
415 | static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0); | 415 | static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0); |
416 | static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); | 416 | static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); |
417 | static const DECLARE_TLV_DB_SCALE(threedstereo_tlv, -1600, 183, 1); | ||
417 | 418 | ||
418 | static const char *sidetone_hpf_text[] = { | 419 | static const char *sidetone_hpf_text[] = { |
419 | "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz" | 420 | "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz" |
@@ -608,6 +609,14 @@ SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0), | |||
608 | SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0), | 609 | SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0), |
609 | SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0), | 610 | SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0), |
610 | 611 | ||
612 | SOC_SINGLE("DSP1 3D Stereo Switch", WM8996_DSP1_RX_FILTERS_2, 8, 1, 0), | ||
613 | SOC_SINGLE("DSP2 3D Stereo Switch", WM8996_DSP2_RX_FILTERS_2, 8, 1, 0), | ||
614 | |||
615 | SOC_SINGLE_TLV("DSP1 3D Stereo Volume", WM8996_DSP1_RX_FILTERS_2, 10, 15, | ||
616 | 0, threedstereo_tlv), | ||
617 | SOC_SINGLE_TLV("DSP2 3D Stereo Volume", WM8996_DSP2_RX_FILTERS_2, 10, 15, | ||
618 | 0, threedstereo_tlv), | ||
619 | |||
611 | SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4, | 620 | SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4, |
612 | 8, 0, out_digital_tlv), | 621 | 8, 0, out_digital_tlv), |
613 | SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4, | 622 | SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4, |
@@ -632,6 +641,14 @@ SOC_DOUBLE_R("Speaker ZC Switch", WM8996_LEFT_PDM_SPEAKER, | |||
632 | 641 | ||
633 | SOC_SINGLE("DSP1 EQ Switch", WM8996_DSP1_RX_EQ_GAINS_1, 0, 1, 0), | 642 | SOC_SINGLE("DSP1 EQ Switch", WM8996_DSP1_RX_EQ_GAINS_1, 0, 1, 0), |
634 | SOC_SINGLE("DSP2 EQ Switch", WM8996_DSP2_RX_EQ_GAINS_1, 0, 1, 0), | 643 | SOC_SINGLE("DSP2 EQ Switch", WM8996_DSP2_RX_EQ_GAINS_1, 0, 1, 0), |
644 | |||
645 | SOC_SINGLE("DSP1 DRC TXL Switch", WM8996_DSP1_DRC_1, 0, 1, 0), | ||
646 | SOC_SINGLE("DSP1 DRC TXR Switch", WM8996_DSP1_DRC_1, 1, 1, 0), | ||
647 | SOC_SINGLE("DSP1 DRC RX Switch", WM8996_DSP1_DRC_1, 2, 1, 0), | ||
648 | |||
649 | SOC_SINGLE("DSP2 DRC TXL Switch", WM8996_DSP2_DRC_1, 0, 1, 0), | ||
650 | SOC_SINGLE("DSP2 DRC TXR Switch", WM8996_DSP2_DRC_1, 1, 1, 0), | ||
651 | SOC_SINGLE("DSP2 DRC RX Switch", WM8996_DSP2_DRC_1, 2, 1, 0), | ||
635 | }; | 652 | }; |
636 | 653 | ||
637 | static const struct snd_kcontrol_new wm8996_eq_controls[] = { | 654 | static const struct snd_kcontrol_new wm8996_eq_controls[] = { |
@@ -658,19 +675,75 @@ SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0, | |||
658 | eq_tlv), | 675 | eq_tlv), |
659 | }; | 676 | }; |
660 | 677 | ||
678 | static void wm8996_bg_enable(struct snd_soc_codec *codec) | ||
679 | { | ||
680 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); | ||
681 | |||
682 | wm8996->bg_ena++; | ||
683 | if (wm8996->bg_ena == 1) { | ||
684 | snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1, | ||
685 | WM8996_BG_ENA, WM8996_BG_ENA); | ||
686 | msleep(2); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | static void wm8996_bg_disable(struct snd_soc_codec *codec) | ||
691 | { | ||
692 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); | ||
693 | |||
694 | wm8996->bg_ena--; | ||
695 | if (!wm8996->bg_ena) | ||
696 | snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1, | ||
697 | WM8996_BG_ENA, 0); | ||
698 | } | ||
699 | |||
700 | static int bg_event(struct snd_soc_dapm_widget *w, | ||
701 | struct snd_kcontrol *kcontrol, int event) | ||
702 | { | ||
703 | struct snd_soc_codec *codec = w->codec; | ||
704 | int ret = 0; | ||
705 | |||
706 | switch (event) { | ||
707 | case SND_SOC_DAPM_PRE_PMU: | ||
708 | wm8996_bg_enable(codec); | ||
709 | break; | ||
710 | case SND_SOC_DAPM_POST_PMD: | ||
711 | wm8996_bg_disable(codec); | ||
712 | break; | ||
713 | default: | ||
714 | BUG(); | ||
715 | ret = -EINVAL; | ||
716 | } | ||
717 | |||
718 | return ret; | ||
719 | } | ||
720 | |||
661 | static int cp_event(struct snd_soc_dapm_widget *w, | 721 | static int cp_event(struct snd_soc_dapm_widget *w, |
662 | struct snd_kcontrol *kcontrol, int event) | 722 | struct snd_kcontrol *kcontrol, int event) |
663 | { | 723 | { |
724 | struct snd_soc_codec *codec = w->codec; | ||
725 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); | ||
726 | int ret = 0; | ||
727 | |||
664 | switch (event) { | 728 | switch (event) { |
729 | case SND_SOC_DAPM_PRE_PMU: | ||
730 | ret = regulator_enable(wm8996->cpvdd); | ||
731 | if (ret != 0) | ||
732 | dev_err(codec->dev, "Failed to enable CPVDD: %d\n", | ||
733 | ret); | ||
734 | break; | ||
665 | case SND_SOC_DAPM_POST_PMU: | 735 | case SND_SOC_DAPM_POST_PMU: |
666 | msleep(5); | 736 | msleep(5); |
667 | break; | 737 | break; |
738 | case SND_SOC_DAPM_POST_PMD: | ||
739 | regulator_disable_deferred(wm8996->cpvdd, 20); | ||
740 | break; | ||
668 | default: | 741 | default: |
669 | BUG(); | 742 | BUG(); |
670 | return -EINVAL; | 743 | ret = -EINVAL; |
671 | } | 744 | } |
672 | 745 | ||
673 | return 0; | 746 | return ret; |
674 | } | 747 | } |
675 | 748 | ||
676 | static int rmv_short_event(struct snd_soc_dapm_widget *w, | 749 | static int rmv_short_event(struct snd_soc_dapm_widget *w, |
@@ -698,7 +771,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask) | |||
698 | { | 771 | { |
699 | struct i2c_client *i2c = to_i2c_client(codec->dev); | 772 | struct i2c_client *i2c = to_i2c_client(codec->dev); |
700 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); | 773 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); |
701 | int i, ret; | 774 | int ret; |
702 | unsigned long timeout = 200; | 775 | unsigned long timeout = 200; |
703 | 776 | ||
704 | snd_soc_write(codec, WM8996_DC_SERVO_2, mask); | 777 | snd_soc_write(codec, WM8996_DC_SERVO_2, mask); |
@@ -713,15 +786,12 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask) | |||
713 | 786 | ||
714 | } else { | 787 | } else { |
715 | msleep(1); | 788 | msleep(1); |
716 | if (--i) { | 789 | timeout--; |
717 | timeout = 0; | ||
718 | break; | ||
719 | } | ||
720 | } | 790 | } |
721 | 791 | ||
722 | ret = snd_soc_read(codec, WM8996_DC_SERVO_2); | 792 | ret = snd_soc_read(codec, WM8996_DC_SERVO_2); |
723 | dev_dbg(codec->dev, "DC servo state: %x\n", ret); | 793 | dev_dbg(codec->dev, "DC servo state: %x\n", ret); |
724 | } while (ret & mask); | 794 | } while (timeout && ret & mask); |
725 | 795 | ||
726 | if (timeout == 0) | 796 | if (timeout == 0) |
727 | dev_err(codec->dev, "DC servo timed out for %x\n", mask); | 797 | dev_err(codec->dev, "DC servo timed out for %x\n", mask); |
@@ -979,9 +1049,12 @@ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0), | |||
979 | SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0), | 1049 | SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0), |
980 | SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0), | 1050 | SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0), |
981 | SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event, | 1051 | SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event, |
982 | SND_SOC_DAPM_POST_PMU), | 1052 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
983 | 1053 | SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event, | |
1054 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), | ||
984 | SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0), | 1055 | SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0), |
1056 | SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0), | ||
1057 | SND_SOC_DAPM_SUPPLY("MICB2 Audio", WM8996_MICBIAS_2, 4, 1, NULL, 0), | ||
985 | SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0), | 1058 | SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0), |
986 | SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0), | 1059 | SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0), |
987 | 1060 | ||
@@ -1035,14 +1108,14 @@ SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0), | |||
1035 | SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0), | 1108 | SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0), |
1036 | SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0), | 1109 | SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0), |
1037 | 1110 | ||
1038 | SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1, | 1111 | SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 0, |
1039 | WM8996_POWER_MANAGEMENT_4, 9, 0), | 1112 | WM8996_POWER_MANAGEMENT_4, 9, 0), |
1040 | SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2, | 1113 | SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 1, |
1041 | WM8996_POWER_MANAGEMENT_4, 8, 0), | 1114 | WM8996_POWER_MANAGEMENT_4, 8, 0), |
1042 | 1115 | ||
1043 | SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1, | 1116 | SND_SOC_DAPM_AIF_OUT("AIF2TX1", "AIF2 Capture", 0, |
1044 | WM8996_POWER_MANAGEMENT_6, 9, 0), | 1117 | WM8996_POWER_MANAGEMENT_6, 9, 0), |
1045 | SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2, | 1118 | SND_SOC_DAPM_AIF_OUT("AIF2TX0", "AIF2 Capture", 1, |
1046 | WM8996_POWER_MANAGEMENT_6, 8, 0), | 1119 | WM8996_POWER_MANAGEMENT_6, 8, 0), |
1047 | 1120 | ||
1048 | SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5, | 1121 | SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5, |
@@ -1137,17 +1210,23 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = { | |||
1137 | { "Charge Pump", NULL, "SYSCLK" }, | 1210 | { "Charge Pump", NULL, "SYSCLK" }, |
1138 | 1211 | ||
1139 | { "MICB1", NULL, "LDO2" }, | 1212 | { "MICB1", NULL, "LDO2" }, |
1213 | { "MICB1", NULL, "MICB1 Audio" }, | ||
1214 | { "MICB1", NULL, "Bandgap" }, | ||
1140 | { "MICB2", NULL, "LDO2" }, | 1215 | { "MICB2", NULL, "LDO2" }, |
1216 | { "MICB2", NULL, "MICB2 Audio" }, | ||
1217 | { "MICB2", NULL, "Bandgap" }, | ||
1141 | 1218 | ||
1142 | { "IN1L PGA", NULL, "IN2LN" }, | 1219 | { "IN1L PGA", NULL, "IN2LN" }, |
1143 | { "IN1L PGA", NULL, "IN2LP" }, | 1220 | { "IN1L PGA", NULL, "IN2LP" }, |
1144 | { "IN1L PGA", NULL, "IN1LN" }, | 1221 | { "IN1L PGA", NULL, "IN1LN" }, |
1145 | { "IN1L PGA", NULL, "IN1LP" }, | 1222 | { "IN1L PGA", NULL, "IN1LP" }, |
1223 | { "IN1L PGA", NULL, "Bandgap" }, | ||
1146 | 1224 | ||
1147 | { "IN1R PGA", NULL, "IN2RN" }, | 1225 | { "IN1R PGA", NULL, "IN2RN" }, |
1148 | { "IN1R PGA", NULL, "IN2RP" }, | 1226 | { "IN1R PGA", NULL, "IN2RP" }, |
1149 | { "IN1R PGA", NULL, "IN1RN" }, | 1227 | { "IN1R PGA", NULL, "IN1RN" }, |
1150 | { "IN1R PGA", NULL, "IN1RP" }, | 1228 | { "IN1R PGA", NULL, "IN1RP" }, |
1229 | { "IN1R PGA", NULL, "Bandgap" }, | ||
1151 | 1230 | ||
1152 | { "ADCL", NULL, "IN1L PGA" }, | 1231 | { "ADCL", NULL, "IN1L PGA" }, |
1153 | 1232 | ||
@@ -1281,6 +1360,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = { | |||
1281 | { "DAC2R", NULL, "DAC2R Mixer" }, | 1360 | { "DAC2R", NULL, "DAC2R Mixer" }, |
1282 | 1361 | ||
1283 | { "HPOUT2L PGA", NULL, "Charge Pump" }, | 1362 | { "HPOUT2L PGA", NULL, "Charge Pump" }, |
1363 | { "HPOUT2L PGA", NULL, "Bandgap" }, | ||
1284 | { "HPOUT2L PGA", NULL, "DAC2L" }, | 1364 | { "HPOUT2L PGA", NULL, "DAC2L" }, |
1285 | { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" }, | 1365 | { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" }, |
1286 | { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" }, | 1366 | { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" }, |
@@ -1288,6 +1368,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = { | |||
1288 | { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" }, | 1368 | { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" }, |
1289 | 1369 | ||
1290 | { "HPOUT2R PGA", NULL, "Charge Pump" }, | 1370 | { "HPOUT2R PGA", NULL, "Charge Pump" }, |
1371 | { "HPOUT2R PGA", NULL, "Bandgap" }, | ||
1291 | { "HPOUT2R PGA", NULL, "DAC2R" }, | 1372 | { "HPOUT2R PGA", NULL, "DAC2R" }, |
1292 | { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" }, | 1373 | { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" }, |
1293 | { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" }, | 1374 | { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" }, |
@@ -1295,6 +1376,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = { | |||
1295 | { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" }, | 1376 | { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" }, |
1296 | 1377 | ||
1297 | { "HPOUT1L PGA", NULL, "Charge Pump" }, | 1378 | { "HPOUT1L PGA", NULL, "Charge Pump" }, |
1379 | { "HPOUT1L PGA", NULL, "Bandgap" }, | ||
1298 | { "HPOUT1L PGA", NULL, "DAC1L" }, | 1380 | { "HPOUT1L PGA", NULL, "DAC1L" }, |
1299 | { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" }, | 1381 | { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" }, |
1300 | { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" }, | 1382 | { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" }, |
@@ -1302,6 +1384,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = { | |||
1302 | { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" }, | 1384 | { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" }, |
1303 | 1385 | ||
1304 | { "HPOUT1R PGA", NULL, "Charge Pump" }, | 1386 | { "HPOUT1R PGA", NULL, "Charge Pump" }, |
1387 | { "HPOUT1R PGA", NULL, "Bandgap" }, | ||
1305 | { "HPOUT1R PGA", NULL, "DAC1R" }, | 1388 | { "HPOUT1R PGA", NULL, "DAC1R" }, |
1306 | { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" }, | 1389 | { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" }, |
1307 | { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" }, | 1390 | { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" }, |
@@ -1620,14 +1703,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, | |||
1620 | 1703 | ||
1621 | switch (level) { | 1704 | switch (level) { |
1622 | case SND_SOC_BIAS_ON: | 1705 | case SND_SOC_BIAS_ON: |
1623 | break; | ||
1624 | |||
1625 | case SND_SOC_BIAS_PREPARE: | 1706 | case SND_SOC_BIAS_PREPARE: |
1626 | if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { | ||
1627 | snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1, | ||
1628 | WM8996_BG_ENA, WM8996_BG_ENA); | ||
1629 | msleep(2); | ||
1630 | } | ||
1631 | break; | 1707 | break; |
1632 | 1708 | ||
1633 | case SND_SOC_BIAS_STANDBY: | 1709 | case SND_SOC_BIAS_STANDBY: |
@@ -1650,9 +1726,6 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, | |||
1650 | codec->cache_only = false; | 1726 | codec->cache_only = false; |
1651 | snd_soc_cache_sync(codec); | 1727 | snd_soc_cache_sync(codec); |
1652 | } | 1728 | } |
1653 | |||
1654 | snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1, | ||
1655 | WM8996_BG_ENA, 0); | ||
1656 | break; | 1729 | break; |
1657 | 1730 | ||
1658 | case SND_SOC_BIAS_OFF: | 1731 | case SND_SOC_BIAS_OFF: |
@@ -1847,7 +1920,7 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream, | |||
1847 | snd_soc_update_bits(codec, lrclk_reg, WM8996_AIF1RX_RATE_MASK, | 1920 | snd_soc_update_bits(codec, lrclk_reg, WM8996_AIF1RX_RATE_MASK, |
1848 | lrclk); | 1921 | lrclk); |
1849 | snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_2, | 1922 | snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_2, |
1850 | WM8996_DSP1_DIV_SHIFT << dsp_shift, dsp); | 1923 | WM8996_DSP1_DIV_MASK << dsp_shift, dsp); |
1851 | 1924 | ||
1852 | return 0; | 1925 | return 0; |
1853 | } | 1926 | } |
@@ -2041,7 +2114,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
2041 | struct i2c_client *i2c = to_i2c_client(codec->dev); | 2114 | struct i2c_client *i2c = to_i2c_client(codec->dev); |
2042 | struct _fll_div fll_div; | 2115 | struct _fll_div fll_div; |
2043 | unsigned long timeout; | 2116 | unsigned long timeout; |
2044 | int ret, reg; | 2117 | int ret, reg, retry; |
2045 | 2118 | ||
2046 | /* Any change? */ | 2119 | /* Any change? */ |
2047 | if (source == wm8996->fll_src && Fref == wm8996->fll_fref && | 2120 | if (source == wm8996->fll_src && Fref == wm8996->fll_fref && |
@@ -2057,6 +2130,8 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
2057 | snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1, | 2130 | snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1, |
2058 | WM8996_FLL_ENA, 0); | 2131 | WM8996_FLL_ENA, 0); |
2059 | 2132 | ||
2133 | wm8996_bg_disable(codec); | ||
2134 | |||
2060 | return 0; | 2135 | return 0; |
2061 | } | 2136 | } |
2062 | 2137 | ||
@@ -2111,6 +2186,11 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
2111 | 2186 | ||
2112 | snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda); | 2187 | snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda); |
2113 | 2188 | ||
2189 | /* Enable the bandgap if it's not already enabled */ | ||
2190 | ret = snd_soc_read(codec, WM8996_FLL_CONTROL_1); | ||
2191 | if (!(ret & WM8996_FLL_ENA)) | ||
2192 | wm8996_bg_enable(codec); | ||
2193 | |||
2114 | /* Clear any pending completions (eg, from failed startups) */ | 2194 | /* Clear any pending completions (eg, from failed startups) */ |
2115 | try_wait_for_completion(&wm8996->fll_lock); | 2195 | try_wait_for_completion(&wm8996->fll_lock); |
2116 | 2196 | ||
@@ -2128,17 +2208,29 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
2128 | else | 2208 | else |
2129 | timeout = msecs_to_jiffies(2); | 2209 | timeout = msecs_to_jiffies(2); |
2130 | 2210 | ||
2131 | /* Allow substantially longer if we've actually got the IRQ */ | 2211 | /* Allow substantially longer if we've actually got the IRQ, poll |
2212 | * at a slightly higher rate if we don't. | ||
2213 | */ | ||
2132 | if (i2c->irq) | 2214 | if (i2c->irq) |
2133 | timeout *= 1000; | 2215 | timeout *= 10; |
2216 | else | ||
2217 | timeout /= 2; | ||
2134 | 2218 | ||
2135 | ret = wait_for_completion_timeout(&wm8996->fll_lock, timeout); | 2219 | for (retry = 0; retry < 10; retry++) { |
2220 | ret = wait_for_completion_timeout(&wm8996->fll_lock, | ||
2221 | timeout); | ||
2222 | if (ret != 0) { | ||
2223 | WARN_ON(!i2c->irq); | ||
2224 | break; | ||
2225 | } | ||
2136 | 2226 | ||
2137 | if (ret == 0 && i2c->irq) { | 2227 | ret = snd_soc_read(codec, WM8996_INTERRUPT_RAW_STATUS_2); |
2228 | if (ret & WM8996_FLL_LOCK_STS) | ||
2229 | break; | ||
2230 | } | ||
2231 | if (retry == 10) { | ||
2138 | dev_err(codec->dev, "Timed out waiting for FLL\n"); | 2232 | dev_err(codec->dev, "Timed out waiting for FLL\n"); |
2139 | ret = -ETIMEDOUT; | 2233 | ret = -ETIMEDOUT; |
2140 | } else { | ||
2141 | ret = 0; | ||
2142 | } | 2234 | } |
2143 | 2235 | ||
2144 | dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); | 2236 | dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); |
@@ -2297,12 +2389,94 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, | |||
2297 | 2389 | ||
2298 | /* Enable interrupts and we're off */ | 2390 | /* Enable interrupts and we're off */ |
2299 | snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK, | 2391 | snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK, |
2300 | WM8996_IM_MICD_EINT, 0); | 2392 | WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0); |
2301 | 2393 | ||
2302 | return 0; | 2394 | return 0; |
2303 | } | 2395 | } |
2304 | EXPORT_SYMBOL_GPL(wm8996_detect); | 2396 | EXPORT_SYMBOL_GPL(wm8996_detect); |
2305 | 2397 | ||
2398 | static void wm8996_hpdet_irq(struct snd_soc_codec *codec) | ||
2399 | { | ||
2400 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); | ||
2401 | int val, reg, report; | ||
2402 | |||
2403 | /* Assume headphone in error conditions; we need to report | ||
2404 | * something or we stall our state machine. | ||
2405 | */ | ||
2406 | report = SND_JACK_HEADPHONE; | ||
2407 | |||
2408 | reg = snd_soc_read(codec, WM8996_HEADPHONE_DETECT_2); | ||
2409 | if (reg < 0) { | ||
2410 | dev_err(codec->dev, "Failed to read HPDET status\n"); | ||
2411 | goto out; | ||
2412 | } | ||
2413 | |||
2414 | if (!(reg & WM8996_HP_DONE)) { | ||
2415 | dev_err(codec->dev, "Got HPDET IRQ but HPDET is busy\n"); | ||
2416 | goto out; | ||
2417 | } | ||
2418 | |||
2419 | val = reg & WM8996_HP_LVL_MASK; | ||
2420 | |||
2421 | dev_dbg(codec->dev, "HPDET measured %d ohms\n", val); | ||
2422 | |||
2423 | /* If we've got high enough impedence then report as line, | ||
2424 | * otherwise assume headphone. | ||
2425 | */ | ||
2426 | if (val >= 126) | ||
2427 | report = SND_JACK_LINEOUT; | ||
2428 | else | ||
2429 | report = SND_JACK_HEADPHONE; | ||
2430 | |||
2431 | out: | ||
2432 | if (wm8996->jack_mic) | ||
2433 | report |= SND_JACK_MICROPHONE; | ||
2434 | |||
2435 | snd_soc_jack_report(wm8996->jack, report, | ||
2436 | SND_JACK_LINEOUT | SND_JACK_HEADSET); | ||
2437 | |||
2438 | wm8996->detecting = false; | ||
2439 | |||
2440 | /* If the output isn't running re-clamp it */ | ||
2441 | if (!(snd_soc_read(codec, WM8996_POWER_MANAGEMENT_1) & | ||
2442 | (WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT))) | ||
2443 | snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, | ||
2444 | WM8996_HPOUT1L_RMV_SHORT | | ||
2445 | WM8996_HPOUT1R_RMV_SHORT, 0); | ||
2446 | |||
2447 | /* Go back to looking at the microphone */ | ||
2448 | snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1, | ||
2449 | WM8996_JD_MODE_MASK, 0); | ||
2450 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, | ||
2451 | WM8996_MICD_ENA); | ||
2452 | |||
2453 | snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap"); | ||
2454 | snd_soc_dapm_sync(&codec->dapm); | ||
2455 | } | ||
2456 | |||
2457 | static void wm8996_hpdet_start(struct snd_soc_codec *codec) | ||
2458 | { | ||
2459 | /* Unclamp the output, we can't measure while we're shorting it */ | ||
2460 | snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, | ||
2461 | WM8996_HPOUT1L_RMV_SHORT | | ||
2462 | WM8996_HPOUT1R_RMV_SHORT, | ||
2463 | WM8996_HPOUT1L_RMV_SHORT | | ||
2464 | WM8996_HPOUT1R_RMV_SHORT); | ||
2465 | |||
2466 | /* We need bandgap for HPDET */ | ||
2467 | snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap"); | ||
2468 | snd_soc_dapm_sync(&codec->dapm); | ||
2469 | |||
2470 | /* Go into headphone detect left mode */ | ||
2471 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0); | ||
2472 | snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1, | ||
2473 | WM8996_JD_MODE_MASK, 1); | ||
2474 | |||
2475 | /* Trigger a measurement */ | ||
2476 | snd_soc_update_bits(codec, WM8996_HEADPHONE_DETECT_1, | ||
2477 | WM8996_HP_POLL, WM8996_HP_POLL); | ||
2478 | } | ||
2479 | |||
2306 | static void wm8996_micd(struct snd_soc_codec *codec) | 2480 | static void wm8996_micd(struct snd_soc_codec *codec) |
2307 | { | 2481 | { |
2308 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); | 2482 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); |
@@ -2323,28 +2497,36 @@ static void wm8996_micd(struct snd_soc_codec *codec) | |||
2323 | wm8996->jack_mic = false; | 2497 | wm8996->jack_mic = false; |
2324 | wm8996->detecting = true; | 2498 | wm8996->detecting = true; |
2325 | snd_soc_jack_report(wm8996->jack, 0, | 2499 | snd_soc_jack_report(wm8996->jack, 0, |
2326 | SND_JACK_HEADSET | SND_JACK_BTN_0); | 2500 | SND_JACK_LINEOUT | SND_JACK_HEADSET | |
2501 | SND_JACK_BTN_0); | ||
2502 | |||
2327 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, | 2503 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, |
2328 | WM8996_MICD_RATE_MASK, | 2504 | WM8996_MICD_RATE_MASK, |
2329 | WM8996_MICD_RATE_MASK); | 2505 | WM8996_MICD_RATE_MASK); |
2330 | return; | 2506 | return; |
2331 | } | 2507 | } |
2332 | 2508 | ||
2333 | /* If the measurement is very high we've got a microphone but | 2509 | /* If the measurement is very high we've got a microphone, |
2334 | * do a little debounce to account for mechanical issues. | 2510 | * either we just detected one or if we already reported then |
2511 | * we've got a button release event. | ||
2335 | */ | 2512 | */ |
2336 | if (val & 0x400) { | 2513 | if (val & 0x400) { |
2337 | dev_dbg(codec->dev, "Microphone detected\n"); | 2514 | if (wm8996->detecting) { |
2338 | snd_soc_jack_report(wm8996->jack, SND_JACK_HEADSET, | 2515 | dev_dbg(codec->dev, "Microphone detected\n"); |
2339 | SND_JACK_HEADSET | SND_JACK_BTN_0); | 2516 | wm8996->jack_mic = true; |
2340 | wm8996->jack_mic = true; | 2517 | wm8996_hpdet_start(codec); |
2341 | wm8996->detecting = false; | 2518 | |
2342 | 2519 | /* Increase poll rate to give better responsiveness | |
2343 | /* Increase poll rate to give better responsiveness | 2520 | * for buttons */ |
2344 | * for buttons */ | 2521 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, |
2345 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, | 2522 | WM8996_MICD_RATE_MASK, |
2346 | WM8996_MICD_RATE_MASK, | 2523 | 5 << WM8996_MICD_RATE_SHIFT); |
2347 | 5 << WM8996_MICD_RATE_SHIFT); | 2524 | } else { |
2525 | dev_dbg(codec->dev, "Mic button up\n"); | ||
2526 | snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0); | ||
2527 | } | ||
2528 | |||
2529 | return; | ||
2348 | } | 2530 | } |
2349 | 2531 | ||
2350 | /* If we detected a lower impedence during initial startup | 2532 | /* If we detected a lower impedence during initial startup |
@@ -2376,15 +2558,11 @@ static void wm8996_micd(struct snd_soc_codec *codec) | |||
2376 | if (val & 0x3fc) { | 2558 | if (val & 0x3fc) { |
2377 | if (wm8996->jack_mic) { | 2559 | if (wm8996->jack_mic) { |
2378 | dev_dbg(codec->dev, "Mic button detected\n"); | 2560 | dev_dbg(codec->dev, "Mic button detected\n"); |
2379 | snd_soc_jack_report(wm8996->jack, | 2561 | snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0, |
2380 | SND_JACK_HEADSET | SND_JACK_BTN_0, | ||
2381 | SND_JACK_HEADSET | SND_JACK_BTN_0); | ||
2382 | } else { | ||
2383 | dev_dbg(codec->dev, "Headphone detected\n"); | ||
2384 | snd_soc_jack_report(wm8996->jack, | ||
2385 | SND_JACK_HEADPHONE, | ||
2386 | SND_JACK_HEADSET | | ||
2387 | SND_JACK_BTN_0); | 2562 | SND_JACK_BTN_0); |
2563 | } else if (wm8996->detecting) { | ||
2564 | dev_dbg(codec->dev, "Headphone detected\n"); | ||
2565 | wm8996_hpdet_start(codec); | ||
2388 | 2566 | ||
2389 | /* Increase the detection rate a bit for | 2567 | /* Increase the detection rate a bit for |
2390 | * responsiveness. | 2568 | * responsiveness. |
@@ -2392,8 +2570,6 @@ static void wm8996_micd(struct snd_soc_codec *codec) | |||
2392 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, | 2570 | snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, |
2393 | WM8996_MICD_RATE_MASK, | 2571 | WM8996_MICD_RATE_MASK, |
2394 | 7 << WM8996_MICD_RATE_SHIFT); | 2572 | 7 << WM8996_MICD_RATE_SHIFT); |
2395 | |||
2396 | wm8996->detecting = false; | ||
2397 | } | 2573 | } |
2398 | } | 2574 | } |
2399 | } | 2575 | } |
@@ -2412,6 +2588,9 @@ static irqreturn_t wm8996_irq(int irq, void *data) | |||
2412 | } | 2588 | } |
2413 | irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK); | 2589 | irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK); |
2414 | 2590 | ||
2591 | if (!irq_val) | ||
2592 | return IRQ_NONE; | ||
2593 | |||
2415 | snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val); | 2594 | snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val); |
2416 | 2595 | ||
2417 | if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) { | 2596 | if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) { |
@@ -2430,10 +2609,10 @@ static irqreturn_t wm8996_irq(int irq, void *data) | |||
2430 | if (irq_val & WM8996_MICD_EINT) | 2609 | if (irq_val & WM8996_MICD_EINT) |
2431 | wm8996_micd(codec); | 2610 | wm8996_micd(codec); |
2432 | 2611 | ||
2433 | if (irq_val) | 2612 | if (irq_val & WM8996_HP_DONE_EINT) |
2434 | return IRQ_HANDLED; | 2613 | wm8996_hpdet_irq(codec); |
2435 | else | 2614 | |
2436 | return IRQ_NONE; | 2615 | return IRQ_HANDLED; |
2437 | } | 2616 | } |
2438 | 2617 | ||
2439 | static irqreturn_t wm8996_edge_irq(int irq, void *data) | 2618 | static irqreturn_t wm8996_edge_irq(int irq, void *data) |
@@ -2527,7 +2706,6 @@ static int wm8996_probe(struct snd_soc_codec *codec) | |||
2527 | init_completion(&wm8996->fll_lock); | 2706 | init_completion(&wm8996->fll_lock); |
2528 | 2707 | ||
2529 | dapm->idle_bias_off = true; | 2708 | dapm->idle_bias_off = true; |
2530 | dapm->bias_level = SND_SOC_BIAS_OFF; | ||
2531 | 2709 | ||
2532 | ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); | 2710 | ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); |
2533 | if (ret != 0) { | 2711 | if (ret != 0) { |
@@ -2548,7 +2726,13 @@ static int wm8996_probe(struct snd_soc_codec *codec) | |||
2548 | wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0; | 2726 | wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0; |
2549 | wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1; | 2727 | wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1; |
2550 | wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2; | 2728 | wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2; |
2551 | wm8996->disable_nb[3].notifier_call = wm8996_regulator_event_3; | 2729 | |
2730 | wm8996->cpvdd = regulator_get(&i2c->dev, "CPVDD"); | ||
2731 | if (IS_ERR(wm8996->cpvdd)) { | ||
2732 | ret = PTR_ERR(wm8996->cpvdd); | ||
2733 | dev_err(&i2c->dev, "Failed to get CPVDD: %d\n", ret); | ||
2734 | goto err_get; | ||
2735 | } | ||
2552 | 2736 | ||
2553 | /* This should really be moved into the regulator core */ | 2737 | /* This should really be moved into the regulator core */ |
2554 | for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) { | 2738 | for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) { |
@@ -2565,7 +2749,7 @@ static int wm8996_probe(struct snd_soc_codec *codec) | |||
2565 | wm8996->supplies); | 2749 | wm8996->supplies); |
2566 | if (ret != 0) { | 2750 | if (ret != 0) { |
2567 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | 2751 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); |
2568 | goto err_get; | 2752 | goto err_cpvdd; |
2569 | } | 2753 | } |
2570 | 2754 | ||
2571 | if (wm8996->pdata.ldo_ena >= 0) { | 2755 | if (wm8996->pdata.ldo_ena >= 0) { |
@@ -2808,6 +2992,8 @@ err_enable: | |||
2808 | gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); | 2992 | gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); |
2809 | 2993 | ||
2810 | regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); | 2994 | regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); |
2995 | err_cpvdd: | ||
2996 | regulator_put(wm8996->cpvdd); | ||
2811 | err_get: | 2997 | err_get: |
2812 | regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); | 2998 | regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); |
2813 | err: | 2999 | err: |
@@ -2831,6 +3017,7 @@ static int wm8996_remove(struct snd_soc_codec *codec) | |||
2831 | for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) | 3017 | for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) |
2832 | regulator_unregister_notifier(wm8996->supplies[i].consumer, | 3018 | regulator_unregister_notifier(wm8996->supplies[i].consumer, |
2833 | &wm8996->disable_nb[i]); | 3019 | &wm8996->disable_nb[i]); |
3020 | regulator_put(wm8996->cpvdd); | ||
2834 | regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); | 3021 | regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); |
2835 | 3022 | ||
2836 | return 0; | 3023 | return 0; |