diff options
author | Dimitris Papastamos <dp@opensource.wolfsonmicro.com> | 2011-02-11 11:32:11 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-02-13 14:44:54 -0500 |
commit | 173efa09e4c807a2a764509ddd593ad13a44d1df (patch) | |
tree | 112a5b62f90b587daa2f389ab07e05b913a5aff6 /sound/soc/codecs | |
parent | 3088e3b4963d26d6f6f54987f595b974ed6d48d8 (diff) |
ASoC: WM8994: Improve robustness in some use cases
Ensure that on disabling certain registers such as AIF1DAC1L,
AIF1DAC1R etc. the AIF1CLK and AIF2CLK remain enabled. Similarly
when enabling those registers, AIF1CLK and AIF2CLK will remain
disabled.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: stable@kernel.org
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/wm8994.c | 142 |
1 files changed, 133 insertions, 9 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 37b8aa8a680f..bd0cfdd1386f 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c | |||
@@ -107,6 +107,9 @@ struct wm8994_priv { | |||
107 | 107 | ||
108 | int revision; | 108 | int revision; |
109 | struct wm8994_pdata *pdata; | 109 | struct wm8994_pdata *pdata; |
110 | |||
111 | unsigned int aif1clk_enable:1; | ||
112 | unsigned int aif2clk_enable:1; | ||
110 | }; | 113 | }; |
111 | 114 | ||
112 | static int wm8994_readable(unsigned int reg) | 115 | static int wm8994_readable(unsigned int reg) |
@@ -1004,6 +1007,82 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec) | |||
1004 | } | 1007 | } |
1005 | } | 1008 | } |
1006 | 1009 | ||
1010 | static int late_enable_ev(struct snd_soc_dapm_widget *w, | ||
1011 | struct snd_kcontrol *kcontrol, int event) | ||
1012 | { | ||
1013 | struct snd_soc_codec *codec = w->codec; | ||
1014 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
1015 | |||
1016 | switch (event) { | ||
1017 | case SND_SOC_DAPM_PRE_PMU: | ||
1018 | if (wm8994->aif1clk_enable) | ||
1019 | snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, | ||
1020 | WM8994_AIF1CLK_ENA_MASK, | ||
1021 | WM8994_AIF1CLK_ENA); | ||
1022 | if (wm8994->aif2clk_enable) | ||
1023 | snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, | ||
1024 | WM8994_AIF2CLK_ENA_MASK, | ||
1025 | WM8994_AIF2CLK_ENA); | ||
1026 | break; | ||
1027 | } | ||
1028 | |||
1029 | return 0; | ||
1030 | } | ||
1031 | |||
1032 | static int late_disable_ev(struct snd_soc_dapm_widget *w, | ||
1033 | struct snd_kcontrol *kcontrol, int event) | ||
1034 | { | ||
1035 | struct snd_soc_codec *codec = w->codec; | ||
1036 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
1037 | |||
1038 | switch (event) { | ||
1039 | case SND_SOC_DAPM_POST_PMD: | ||
1040 | if (wm8994->aif1clk_enable) { | ||
1041 | snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, | ||
1042 | WM8994_AIF1CLK_ENA_MASK, 0); | ||
1043 | wm8994->aif1clk_enable = 0; | ||
1044 | } | ||
1045 | if (wm8994->aif2clk_enable) { | ||
1046 | snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, | ||
1047 | WM8994_AIF2CLK_ENA_MASK, 0); | ||
1048 | wm8994->aif2clk_enable = 0; | ||
1049 | } | ||
1050 | break; | ||
1051 | } | ||
1052 | |||
1053 | return 0; | ||
1054 | } | ||
1055 | |||
1056 | static int aif1clk_ev(struct snd_soc_dapm_widget *w, | ||
1057 | struct snd_kcontrol *kcontrol, int event) | ||
1058 | { | ||
1059 | struct snd_soc_codec *codec = w->codec; | ||
1060 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
1061 | |||
1062 | switch (event) { | ||
1063 | case SND_SOC_DAPM_PRE_PMU: | ||
1064 | wm8994->aif1clk_enable = 1; | ||
1065 | break; | ||
1066 | } | ||
1067 | |||
1068 | return 0; | ||
1069 | } | ||
1070 | |||
1071 | static int aif2clk_ev(struct snd_soc_dapm_widget *w, | ||
1072 | struct snd_kcontrol *kcontrol, int event) | ||
1073 | { | ||
1074 | struct snd_soc_codec *codec = w->codec; | ||
1075 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
1076 | |||
1077 | switch (event) { | ||
1078 | case SND_SOC_DAPM_PRE_PMU: | ||
1079 | wm8994->aif2clk_enable = 1; | ||
1080 | break; | ||
1081 | } | ||
1082 | |||
1083 | return 0; | ||
1084 | } | ||
1085 | |||
1007 | static const char *hp_mux_text[] = { | 1086 | static const char *hp_mux_text[] = { |
1008 | "Mixer", | 1087 | "Mixer", |
1009 | "DAC", | 1088 | "DAC", |
@@ -1272,6 +1351,29 @@ static const struct soc_enum aif2dacr_src_enum = | |||
1272 | static const struct snd_kcontrol_new aif2dacr_src_mux = | 1351 | static const struct snd_kcontrol_new aif2dacr_src_mux = |
1273 | SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); | 1352 | SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); |
1274 | 1353 | ||
1354 | static const struct snd_soc_dapm_widget wm8994_lateclk_revd_widgets[] = { | ||
1355 | SND_SOC_DAPM_SUPPLY("AIF1CLK", SND_SOC_NOPM, 0, 0, aif1clk_ev, | ||
1356 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), | ||
1357 | SND_SOC_DAPM_SUPPLY("AIF2CLK", SND_SOC_NOPM, 0, 0, aif2clk_ev, | ||
1358 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), | ||
1359 | |||
1360 | SND_SOC_DAPM_PGA_E("Late DAC1L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, | ||
1361 | late_enable_ev, SND_SOC_DAPM_PRE_PMU), | ||
1362 | SND_SOC_DAPM_PGA_E("Late DAC1R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, | ||
1363 | late_enable_ev, SND_SOC_DAPM_PRE_PMU), | ||
1364 | SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, | ||
1365 | late_enable_ev, SND_SOC_DAPM_PRE_PMU), | ||
1366 | SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, | ||
1367 | late_enable_ev, SND_SOC_DAPM_PRE_PMU), | ||
1368 | |||
1369 | SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) | ||
1370 | }; | ||
1371 | |||
1372 | static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = { | ||
1373 | SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), | ||
1374 | SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0) | ||
1375 | }; | ||
1376 | |||
1275 | static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { | 1377 | static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { |
1276 | SND_SOC_DAPM_INPUT("DMIC1DAT"), | 1378 | SND_SOC_DAPM_INPUT("DMIC1DAT"), |
1277 | SND_SOC_DAPM_INPUT("DMIC2DAT"), | 1379 | SND_SOC_DAPM_INPUT("DMIC2DAT"), |
@@ -1284,9 +1386,6 @@ SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8994_CLOCKING_1, 3, 0, NULL, 0), | |||
1284 | SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8994_CLOCKING_1, 2, 0, NULL, 0), | 1386 | SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8994_CLOCKING_1, 2, 0, NULL, 0), |
1285 | SND_SOC_DAPM_SUPPLY("DSPINTCLK", WM8994_CLOCKING_1, 1, 0, NULL, 0), | 1387 | SND_SOC_DAPM_SUPPLY("DSPINTCLK", WM8994_CLOCKING_1, 1, 0, NULL, 0), |
1286 | 1388 | ||
1287 | SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), | ||
1288 | SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0), | ||
1289 | |||
1290 | SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", NULL, | 1389 | SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", NULL, |
1291 | 0, WM8994_POWER_MANAGEMENT_4, 9, 0), | 1390 | 0, WM8994_POWER_MANAGEMENT_4, 9, 0), |
1292 | SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", NULL, | 1391 | SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", NULL, |
@@ -1516,14 +1615,12 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1516 | { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" }, | 1615 | { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" }, |
1517 | 1616 | ||
1518 | /* DAC1 inputs */ | 1617 | /* DAC1 inputs */ |
1519 | { "DAC1L", NULL, "DAC1L Mixer" }, | ||
1520 | { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" }, | 1618 | { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" }, |
1521 | { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, | 1619 | { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, |
1522 | { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, | 1620 | { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, |
1523 | { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, | 1621 | { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, |
1524 | { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, | 1622 | { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, |
1525 | 1623 | ||
1526 | { "DAC1R", NULL, "DAC1R Mixer" }, | ||
1527 | { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" }, | 1624 | { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" }, |
1528 | { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, | 1625 | { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, |
1529 | { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, | 1626 | { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, |
@@ -1532,7 +1629,6 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1532 | 1629 | ||
1533 | /* DAC2/AIF2 outputs */ | 1630 | /* DAC2/AIF2 outputs */ |
1534 | { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" }, | 1631 | { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" }, |
1535 | { "DAC2L", NULL, "AIF2DAC2L Mixer" }, | ||
1536 | { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" }, | 1632 | { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" }, |
1537 | { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, | 1633 | { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, |
1538 | { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, | 1634 | { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, |
@@ -1540,7 +1636,6 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1540 | { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" }, | 1636 | { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" }, |
1541 | 1637 | ||
1542 | { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" }, | 1638 | { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" }, |
1543 | { "DAC2R", NULL, "AIF2DAC2R Mixer" }, | ||
1544 | { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" }, | 1639 | { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" }, |
1545 | { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, | 1640 | { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, |
1546 | { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, | 1641 | { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, |
@@ -1584,6 +1679,24 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1584 | { "Right Headphone Mux", "DAC", "DAC1R" }, | 1679 | { "Right Headphone Mux", "DAC", "DAC1R" }, |
1585 | }; | 1680 | }; |
1586 | 1681 | ||
1682 | static const struct snd_soc_dapm_route wm8994_lateclk_revd_intercon[] = { | ||
1683 | { "DAC1L", NULL, "Late DAC1L Enable PGA" }, | ||
1684 | { "Late DAC1L Enable PGA", NULL, "DAC1L Mixer" }, | ||
1685 | { "DAC1R", NULL, "Late DAC1R Enable PGA" }, | ||
1686 | { "Late DAC1R Enable PGA", NULL, "DAC1R Mixer" }, | ||
1687 | { "DAC2L", NULL, "Late DAC2L Enable PGA" }, | ||
1688 | { "Late DAC2L Enable PGA", NULL, "AIF2DAC2L Mixer" }, | ||
1689 | { "DAC2R", NULL, "Late DAC2R Enable PGA" }, | ||
1690 | { "Late DAC2R Enable PGA", NULL, "AIF2DAC2R Mixer" } | ||
1691 | }; | ||
1692 | |||
1693 | static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = { | ||
1694 | { "DAC1L", NULL, "DAC1L Mixer" }, | ||
1695 | { "DAC1R", NULL, "DAC1R Mixer" }, | ||
1696 | { "DAC2L", NULL, "AIF2DAC2L Mixer" }, | ||
1697 | { "DAC2R", NULL, "AIF2DAC2R Mixer" }, | ||
1698 | }; | ||
1699 | |||
1587 | static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { | 1700 | static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { |
1588 | { "AIF1DACDAT", NULL, "AIF2DACDAT" }, | 1701 | { "AIF1DACDAT", NULL, "AIF2DACDAT" }, |
1589 | { "AIF2DACDAT", NULL, "AIF1DACDAT" }, | 1702 | { "AIF2DACDAT", NULL, "AIF1DACDAT" }, |
@@ -3125,6 +3238,12 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) | |||
3125 | case WM8994: | 3238 | case WM8994: |
3126 | snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, | 3239 | snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, |
3127 | ARRAY_SIZE(wm8994_specific_dapm_widgets)); | 3240 | ARRAY_SIZE(wm8994_specific_dapm_widgets)); |
3241 | if (wm8994->revision < 4) | ||
3242 | snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, | ||
3243 | ARRAY_SIZE(wm8994_lateclk_revd_widgets)); | ||
3244 | else | ||
3245 | snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, | ||
3246 | ARRAY_SIZE(wm8994_lateclk_widgets)); | ||
3128 | break; | 3247 | break; |
3129 | case WM8958: | 3248 | case WM8958: |
3130 | snd_soc_add_controls(codec, wm8958_snd_controls, | 3249 | snd_soc_add_controls(codec, wm8958_snd_controls, |
@@ -3143,10 +3262,15 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) | |||
3143 | snd_soc_dapm_add_routes(dapm, wm8994_intercon, | 3262 | snd_soc_dapm_add_routes(dapm, wm8994_intercon, |
3144 | ARRAY_SIZE(wm8994_intercon)); | 3263 | ARRAY_SIZE(wm8994_intercon)); |
3145 | 3264 | ||
3146 | if (wm8994->revision < 4) | 3265 | if (wm8994->revision < 4) { |
3147 | snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon, | 3266 | snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon, |
3148 | ARRAY_SIZE(wm8994_revd_intercon)); | 3267 | ARRAY_SIZE(wm8994_revd_intercon)); |
3149 | 3268 | snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon, | |
3269 | ARRAY_SIZE(wm8994_lateclk_revd_intercon)); | ||
3270 | } else { | ||
3271 | snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, | ||
3272 | ARRAY_SIZE(wm8994_lateclk_intercon)); | ||
3273 | } | ||
3150 | break; | 3274 | break; |
3151 | case WM8958: | 3275 | case WM8958: |
3152 | snd_soc_dapm_add_routes(dapm, wm8958_intercon, | 3276 | snd_soc_dapm_add_routes(dapm, wm8958_intercon, |