aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-04-26 16:29:29 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-04-27 13:42:11 -0400
commitc340304dd8855a61a2e1bcdd5bde6b90408481a9 (patch)
tree720076a2eb7c4f6464851ddfa58b6aa66acbe6c5
parentaf31a227e1abee06ccd88c2c52f4fb36b786cebe (diff)
ASoC: wm_hubs: Factor out class W management
Since the analogue portions of the checks for class W are the same over all the devices factor out these checks into wm_hubs and while we're at it also use wm_hubs_dac_hp_direct() to enable class W optimisations on more paths. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--sound/soc/codecs/wm8993.c84
-rw-r--r--sound/soc/codecs/wm8994.c91
-rw-r--r--sound/soc/codecs/wm_hubs.c59
-rw-r--r--sound/soc/codecs/wm_hubs.h6
4 files changed, 82 insertions, 158 deletions
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 8bb005926aa0..36acfccab999 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -218,7 +218,6 @@ struct wm8993_priv {
218 unsigned int sysclk_rate; 218 unsigned int sysclk_rate;
219 unsigned int fs; 219 unsigned int fs;
220 unsigned int bclk; 220 unsigned int bclk;
221 int class_w_users;
222 unsigned int fll_fref; 221 unsigned int fll_fref;
223 unsigned int fll_fout; 222 unsigned int fll_fout;
224 int fll_src; 223 int fll_src;
@@ -824,82 +823,6 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
824 return 0; 823 return 0;
825} 824}
826 825
827/*
828 * When used with DAC outputs only the WM8993 charge pump supports
829 * operation in class W mode, providing very low power consumption
830 * when used with digital sources. Enable and disable this mode
831 * automatically depending on the mixer configuration.
832 *
833 * Currently the only supported paths are the direct DAC->headphone
834 * paths (which provide minimum power consumption anyway).
835 */
836static int class_w_put(struct snd_kcontrol *kcontrol,
837 struct snd_ctl_elem_value *ucontrol)
838{
839 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
840 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
841 struct snd_soc_codec *codec = widget->codec;
842 struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
843 int ret;
844
845 /* Turn it off if we're using the main output mixer */
846 if (ucontrol->value.integer.value[0] == 0) {
847 if (wm8993->class_w_users == 0) {
848 dev_dbg(codec->dev, "Disabling Class W\n");
849 snd_soc_update_bits(codec, WM8993_CLASS_W_0,
850 WM8993_CP_DYN_FREQ |
851 WM8993_CP_DYN_V,
852 0);
853 }
854 wm8993->class_w_users++;
855 }
856
857 /* Implement the change */
858 ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
859
860 /* Enable it if we're using the direct DAC path */
861 if (ucontrol->value.integer.value[0] == 1) {
862 if (wm8993->class_w_users == 1) {
863 dev_dbg(codec->dev, "Enabling Class W\n");
864 snd_soc_update_bits(codec, WM8993_CLASS_W_0,
865 WM8993_CP_DYN_FREQ |
866 WM8993_CP_DYN_V,
867 WM8993_CP_DYN_FREQ |
868 WM8993_CP_DYN_V);
869 }
870 wm8993->class_w_users--;
871 }
872
873 dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
874 wm8993->class_w_users);
875
876 return ret;
877}
878
879#define SOC_DAPM_ENUM_W(xname, xenum) \
880{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
881 .info = snd_soc_info_enum_double, \
882 .get = snd_soc_dapm_get_enum_double, \
883 .put = class_w_put, \
884 .private_value = (unsigned long)&xenum }
885
886static const char *hp_mux_text[] = {
887 "Mixer",
888 "DAC",
889};
890
891static const struct soc_enum hpl_enum =
892 SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
893
894static const struct snd_kcontrol_new hpl_mux =
895 SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum);
896
897static const struct soc_enum hpr_enum =
898 SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
899
900static const struct snd_kcontrol_new hpr_mux =
901 SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum);
902
903static const struct snd_kcontrol_new left_speaker_mixer[] = { 826static const struct snd_kcontrol_new left_speaker_mixer[] = {
904SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0), 827SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
905SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0), 828SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
@@ -986,8 +909,8 @@ SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux),
986SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0), 909SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
987SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0), 910SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
988 911
989SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), 912SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
990SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), 913SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
991 914
992SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0, 915SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
993 left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), 916 left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
@@ -1577,9 +1500,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
1577 return ret; 1500 return ret;
1578 } 1501 }
1579 1502
1580 /* By default we're using the output mixers */
1581 wm8993->class_w_users = 2;
1582
1583 /* Latch volume update bits and default ZC on */ 1503 /* Latch volume update bits and default ZC on */
1584 snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME, 1504 snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
1585 WM8993_DAC_VU, WM8993_DAC_VU); 1505 WM8993_DAC_VU, WM8993_DAC_VU);
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 2475e1c10334..d2db63022607 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -945,27 +945,12 @@ static int vmid_event(struct snd_soc_dapm_widget *w,
945 return 0; 945 return 0;
946} 946}
947 947
948static void wm8994_update_class_w(struct snd_soc_codec *codec) 948static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec)
949{ 949{
950 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
951 int enable = 1;
952 int source = 0; /* GCC flow analysis can't track enable */ 950 int source = 0; /* GCC flow analysis can't track enable */
953 int reg, reg_r; 951 int reg, reg_r;
954 952
955 /* Only support direct DAC->headphone paths */ 953 /* We also need the same AIF source for L/R and only one path */
956 reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1);
957 if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) {
958 dev_vdbg(codec->dev, "HPL connected to output mixer\n");
959 enable = 0;
960 }
961
962 reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2);
963 if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) {
964 dev_vdbg(codec->dev, "HPR connected to output mixer\n");
965 enable = 0;
966 }
967
968 /* We also need the same setting for L/R and only one path */
969 reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING); 954 reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
970 switch (reg) { 955 switch (reg) {
971 case WM8994_AIF2DACL_TO_DAC1L: 956 case WM8994_AIF2DACL_TO_DAC1L:
@@ -982,28 +967,20 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec)
982 break; 967 break;
983 default: 968 default:
984 dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg); 969 dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg);
985 enable = 0; 970 return false;
986 break;
987 } 971 }
988 972
989 reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING); 973 reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
990 if (reg_r != reg) { 974 if (reg_r != reg) {
991 dev_vdbg(codec->dev, "Left and right DAC mixers different\n"); 975 dev_vdbg(codec->dev, "Left and right DAC mixers different\n");
992 enable = 0; 976 return false;
993 } 977 }
994 978
995 if (enable) { 979 /* Set the source up */
996 dev_dbg(codec->dev, "Class W enabled\n"); 980 snd_soc_update_bits(codec, WM8994_CLASS_W_1,
997 snd_soc_update_bits(codec, WM8994_CLASS_W_1, 981 WM8994_CP_DYN_SRC_SEL_MASK, source);
998 WM8994_CP_DYN_PWR |
999 WM8994_CP_DYN_SRC_SEL_MASK,
1000 source | WM8994_CP_DYN_PWR);
1001 982
1002 } else { 983 return true;
1003 dev_dbg(codec->dev, "Class W disabled\n");
1004 snd_soc_update_bits(codec, WM8994_CLASS_W_1,
1005 WM8994_CP_DYN_PWR, 0);
1006 }
1007} 984}
1008 985
1009static int late_enable_ev(struct snd_soc_dapm_widget *w, 986static int late_enable_ev(struct snd_soc_dapm_widget *w,
@@ -1120,45 +1097,6 @@ static int dac_ev(struct snd_soc_dapm_widget *w,
1120 return 0; 1097 return 0;
1121} 1098}
1122 1099
1123static const char *hp_mux_text[] = {
1124 "Mixer",
1125 "DAC",
1126};
1127
1128#define WM8994_HP_ENUM(xname, xenum) \
1129{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
1130 .info = snd_soc_info_enum_double, \
1131 .get = snd_soc_dapm_get_enum_double, \
1132 .put = wm8994_put_hp_enum, \
1133 .private_value = (unsigned long)&xenum }
1134
1135static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
1136 struct snd_ctl_elem_value *ucontrol)
1137{
1138 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
1139 struct snd_soc_dapm_widget *w = wlist->widgets[0];
1140 struct snd_soc_codec *codec = w->codec;
1141 int ret;
1142
1143 ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
1144
1145 wm8994_update_class_w(codec);
1146
1147 return ret;
1148}
1149
1150static const struct soc_enum hpl_enum =
1151 SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text);
1152
1153static const struct snd_kcontrol_new hpl_mux =
1154 WM8994_HP_ENUM("Left Headphone Mux", hpl_enum);
1155
1156static const struct soc_enum hpr_enum =
1157 SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text);
1158
1159static const struct snd_kcontrol_new hpr_mux =
1160 WM8994_HP_ENUM("Right Headphone Mux", hpr_enum);
1161
1162static const char *adc_mux_text[] = { 1100static const char *adc_mux_text[] = {
1163 "ADC", 1101 "ADC",
1164 "DMIC", 1102 "DMIC",
@@ -1270,7 +1208,7 @@ static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
1270 1208
1271 ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); 1209 ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
1272 1210
1273 wm8994_update_class_w(codec); 1211 wm_hubs_update_class_w(codec);
1274 1212
1275 return ret; 1213 return ret;
1276} 1214}
@@ -1413,9 +1351,9 @@ SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
1413SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, 1351SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
1414 right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer), 1352 right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer),
1415 late_enable_ev, SND_SOC_DAPM_PRE_PMU), 1353 late_enable_ev, SND_SOC_DAPM_PRE_PMU),
1416SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux, 1354SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux,
1417 late_enable_ev, SND_SOC_DAPM_PRE_PMU), 1355 late_enable_ev, SND_SOC_DAPM_PRE_PMU),
1418SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux, 1356SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux,
1419 late_enable_ev, SND_SOC_DAPM_PRE_PMU), 1357 late_enable_ev, SND_SOC_DAPM_PRE_PMU),
1420 1358
1421SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) 1359SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
@@ -1429,8 +1367,8 @@ SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
1429 left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), 1367 left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
1430SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, 1368SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
1431 right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), 1369 right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
1432SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), 1370SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
1433SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), 1371SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
1434}; 1372};
1435 1373
1436static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { 1374static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = {
@@ -3829,7 +3767,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
3829 break; 3767 break;
3830 } 3768 }
3831 3769
3832 wm8994_update_class_w(codec); 3770 wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital;
3771 wm_hubs_update_class_w(codec);
3833 3772
3834 wm8994_handle_pdata(wm8994); 3773 wm8994_handle_pdata(wm8994);
3835 3774
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 15aed8b7d981..9106334216c4 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -566,6 +566,65 @@ static int lineout_event(struct snd_soc_dapm_widget *w,
566 return 0; 566 return 0;
567} 567}
568 568
569void wm_hubs_update_class_w(struct snd_soc_codec *codec)
570{
571 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
572 int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ;
573
574 if (!wm_hubs_dac_hp_direct(codec))
575 enable = false;
576
577 if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec))
578 enable = false;
579
580 dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled");
581
582 snd_soc_update_bits(codec, WM8993_CLASS_W_0,
583 WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
584}
585EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
586
587#define WM_HUBS_ENUM_W(xname, xenum) \
588{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
589 .info = snd_soc_info_enum_double, \
590 .get = snd_soc_dapm_get_enum_double, \
591 .put = class_w_put, \
592 .private_value = (unsigned long)&xenum }
593
594static int class_w_put(struct snd_kcontrol *kcontrol,
595 struct snd_ctl_elem_value *ucontrol)
596{
597 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
598 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
599 struct snd_soc_codec *codec = widget->codec;
600 int ret;
601
602 ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
603
604 wm_hubs_update_class_w(codec);
605
606 return ret;
607}
608
609static const char *hp_mux_text[] = {
610 "Mixer",
611 "DAC",
612};
613
614static const struct soc_enum hpl_enum =
615 SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
616
617const struct snd_kcontrol_new wm_hubs_hpl_mux =
618 WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
619EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
620
621static const struct soc_enum hpr_enum =
622 SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
623
624const struct snd_kcontrol_new wm_hubs_hpr_mux =
625 WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
626EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux);
627
569static const struct snd_kcontrol_new in1l_pga[] = { 628static const struct snd_kcontrol_new in1l_pga[] = {
570SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0), 629SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
571SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0), 630SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 8bb9f1b51bf3..71861fc580a3 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -16,6 +16,7 @@
16 16
17#include <linux/completion.h> 17#include <linux/completion.h>
18#include <linux/interrupt.h> 18#include <linux/interrupt.h>
19#include <sound/control.h>
19 20
20struct snd_soc_codec; 21struct snd_soc_codec;
21 22
@@ -32,6 +33,7 @@ struct wm_hubs_data {
32 33
33 bool no_cache_dac_hp_direct; 34 bool no_cache_dac_hp_direct;
34 u16 dac_hp_direct_dcs; 35 u16 dac_hp_direct_dcs;
36 bool (*check_class_w_digital)(struct snd_soc_codec *);
35 37
36 bool lineout1_se; 38 bool lineout1_se;
37 bool lineout1n_ena; 39 bool lineout1n_ena;
@@ -57,5 +59,9 @@ extern irqreturn_t wm_hubs_dcs_done(int irq, void *data);
57extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec); 59extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec);
58extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec, 60extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
59 enum snd_soc_bias_level level); 61 enum snd_soc_bias_level level);
62extern void wm_hubs_update_class_w(struct snd_soc_codec *codec);
63
64extern const struct snd_kcontrol_new wm_hubs_hpl_mux;
65extern const struct snd_kcontrol_new wm_hubs_hpr_mux;
60 66
61#endif 67#endif