diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-11-26 10:21:07 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-11-27 05:32:13 -0500 |
commit | c4431df050ff124cae7716e301cead1e8f33c575 (patch) | |
tree | ce6f2adc59834b24ef02040a9eab6ea36b51e6de /sound/soc/codecs/wm8994.c | |
parent | 3a42315740fa80bb4579eb25fedec9d09ff154e7 (diff) |
ASoC: Implement support for enhanced AIF3 on WM8958
Additional audio routing options are available on the WM8958 audio
interface 3. Add support for these.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/wm8994.c')
-rw-r--r-- | sound/soc/codecs/wm8994.c | 166 |
1 files changed, 157 insertions, 9 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 8232d5e73194..fb0609315cd6 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c | |||
@@ -647,6 +647,10 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0, | |||
647 | eq_tlv), | 647 | eq_tlv), |
648 | }; | 648 | }; |
649 | 649 | ||
650 | static const struct snd_kcontrol_new wm8958_snd_controls[] = { | ||
651 | SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv), | ||
652 | }; | ||
653 | |||
650 | static int clk_sys_event(struct snd_soc_dapm_widget *w, | 654 | static int clk_sys_event(struct snd_soc_dapm_widget *w, |
651 | struct snd_kcontrol *kcontrol, int event) | 655 | struct snd_kcontrol *kcontrol, int event) |
652 | { | 656 | { |
@@ -953,14 +957,47 @@ static const struct snd_kcontrol_new aif2adc_mux = | |||
953 | SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum); | 957 | SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum); |
954 | 958 | ||
955 | static const char *aif3adc_text[] = { | 959 | static const char *aif3adc_text[] = { |
956 | "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", | 960 | "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", "Mono PCM", |
957 | }; | 961 | }; |
958 | 962 | ||
959 | static const struct soc_enum aif3adc_enum = | 963 | static const struct soc_enum wm8994_aif3adc_enum = |
960 | SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 3, aif3adc_text); | 964 | SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 3, aif3adc_text); |
961 | 965 | ||
962 | static const struct snd_kcontrol_new aif3adc_mux = | 966 | static const struct snd_kcontrol_new wm8994_aif3adc_mux = |
963 | SOC_DAPM_ENUM("AIF3ADC Mux", aif3adc_enum); | 967 | SOC_DAPM_ENUM("AIF3ADC Mux", wm8994_aif3adc_enum); |
968 | |||
969 | static const struct soc_enum wm8958_aif3adc_enum = | ||
970 | SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 4, aif3adc_text); | ||
971 | |||
972 | static const struct snd_kcontrol_new wm8958_aif3adc_mux = | ||
973 | SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum); | ||
974 | |||
975 | static const char *mono_pcm_out_text[] = { | ||
976 | "None", "AIF2ADCL", "AIF2ADCR", | ||
977 | }; | ||
978 | |||
979 | static const struct soc_enum mono_pcm_out_enum = | ||
980 | SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 9, 3, mono_pcm_out_text); | ||
981 | |||
982 | static const struct snd_kcontrol_new mono_pcm_out_mux = | ||
983 | SOC_DAPM_ENUM("Mono PCM Out Mux", mono_pcm_out_enum); | ||
984 | |||
985 | static const char *aif2dac_src_text[] = { | ||
986 | "AIF2", "AIF3", | ||
987 | }; | ||
988 | |||
989 | /* Note that these two control shouldn't be simultaneously switched to AIF3 */ | ||
990 | static const struct soc_enum aif2dacl_src_enum = | ||
991 | SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 7, 2, aif2dac_src_text); | ||
992 | |||
993 | static const struct snd_kcontrol_new aif2dacl_src_mux = | ||
994 | SOC_DAPM_ENUM("AIF2DACL Mux", aif2dacl_src_enum); | ||
995 | |||
996 | static const struct soc_enum aif2dacr_src_enum = | ||
997 | SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 8, 2, aif2dac_src_text); | ||
998 | |||
999 | static const struct snd_kcontrol_new aif2dacr_src_mux = | ||
1000 | SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); | ||
964 | 1001 | ||
965 | static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { | 1002 | static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { |
966 | SND_SOC_DAPM_INPUT("DMIC1DAT"), | 1003 | SND_SOC_DAPM_INPUT("DMIC1DAT"), |
@@ -1034,7 +1071,6 @@ SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), | |||
1034 | SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux), | 1071 | SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux), |
1035 | SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux), | 1072 | SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux), |
1036 | SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux), | 1073 | SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux), |
1037 | SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &aif3adc_mux), | ||
1038 | 1074 | ||
1039 | SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), | 1075 | SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), |
1040 | SND_SOC_DAPM_AIF_IN("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), | 1076 | SND_SOC_DAPM_AIF_IN("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), |
@@ -1072,8 +1108,18 @@ SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, | |||
1072 | SND_SOC_DAPM_POST("Debug log", post_ev), | 1108 | SND_SOC_DAPM_POST("Debug log", post_ev), |
1073 | }; | 1109 | }; |
1074 | 1110 | ||
1075 | static const struct snd_soc_dapm_route intercon[] = { | 1111 | static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = { |
1112 | SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux), | ||
1113 | }; | ||
1076 | 1114 | ||
1115 | static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = { | ||
1116 | SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux), | ||
1117 | SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux), | ||
1118 | SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux), | ||
1119 | SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8958_aif3adc_mux), | ||
1120 | }; | ||
1121 | |||
1122 | static const struct snd_soc_dapm_route intercon[] = { | ||
1077 | { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys }, | 1123 | { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys }, |
1078 | { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys }, | 1124 | { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys }, |
1079 | 1125 | ||
@@ -1181,9 +1227,6 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1181 | { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, | 1227 | { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, |
1182 | { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, | 1228 | { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, |
1183 | 1229 | ||
1184 | { "AIF2DACL", NULL, "AIF2DAC Mux" }, | ||
1185 | { "AIF2DACR", NULL, "AIF2DAC Mux" }, | ||
1186 | |||
1187 | { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" }, | 1230 | { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" }, |
1188 | { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, | 1231 | { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, |
1189 | { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" }, | 1232 | { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" }, |
@@ -1256,6 +1299,26 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1256 | { "Right Headphone Mux", "DAC", "DAC1R" }, | 1299 | { "Right Headphone Mux", "DAC", "DAC1R" }, |
1257 | }; | 1300 | }; |
1258 | 1301 | ||
1302 | static const struct snd_soc_dapm_route wm8994_intercon[] = { | ||
1303 | { "AIF2DACL", NULL, "AIF2DAC Mux" }, | ||
1304 | { "AIF2DACR", NULL, "AIF2DAC Mux" }, | ||
1305 | }; | ||
1306 | |||
1307 | static const struct snd_soc_dapm_route wm8958_intercon[] = { | ||
1308 | { "AIF2DACL", NULL, "AIF2DACL Mux" }, | ||
1309 | { "AIF2DACR", NULL, "AIF2DACR Mux" }, | ||
1310 | |||
1311 | { "AIF2DACL Mux", "AIF2", "AIF2DAC Mux" }, | ||
1312 | { "AIF2DACL Mux", "AIF3", "AIF3DACDAT" }, | ||
1313 | { "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" }, | ||
1314 | { "AIF2DACR Mux", "AIF3", "AIF3DACDAT" }, | ||
1315 | |||
1316 | { "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" }, | ||
1317 | { "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" }, | ||
1318 | |||
1319 | { "AIF3ADC Mux", "Mono PCM", "Mono PCM Out Mux" }, | ||
1320 | }; | ||
1321 | |||
1259 | /* The size in bits of the FLL divide multiplied by 10 | 1322 | /* The size in bits of the FLL divide multiplied by 10 |
1260 | * to allow rounding later */ | 1323 | * to allow rounding later */ |
1261 | #define FIXED_FLL_SIZE ((1 << 16) * 10) | 1324 | #define FIXED_FLL_SIZE ((1 << 16) * 10) |
@@ -1635,6 +1698,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, | |||
1635 | static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) | 1698 | static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
1636 | { | 1699 | { |
1637 | struct snd_soc_codec *codec = dai->codec; | 1700 | struct snd_soc_codec *codec = dai->codec; |
1701 | struct wm8994 *control = codec->control_data; | ||
1638 | int ms_reg; | 1702 | int ms_reg; |
1639 | int aif1_reg; | 1703 | int aif1_reg; |
1640 | int ms = 0; | 1704 | int ms = 0; |
@@ -1719,6 +1783,13 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |||
1719 | return -EINVAL; | 1783 | return -EINVAL; |
1720 | } | 1784 | } |
1721 | 1785 | ||
1786 | /* The AIF2 format configuration needs to be mirrored to AIF3 | ||
1787 | * on WM8958 if it's in use so just do it all the time. */ | ||
1788 | if (control->type == WM8958 && dai->id == 2) | ||
1789 | snd_soc_update_bits(codec, WM8958_AIF3_CONTROL_1, | ||
1790 | WM8994_AIF1_LRCLK_INV | | ||
1791 | WM8958_AIF3_FMT_MASK, aif1); | ||
1792 | |||
1722 | snd_soc_update_bits(codec, aif1_reg, | 1793 | snd_soc_update_bits(codec, aif1_reg, |
1723 | WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV | | 1794 | WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV | |
1724 | WM8994_AIF1_FMT_MASK, | 1795 | WM8994_AIF1_FMT_MASK, |
@@ -1759,6 +1830,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, | |||
1759 | struct snd_soc_dai *dai) | 1830 | struct snd_soc_dai *dai) |
1760 | { | 1831 | { |
1761 | struct snd_soc_codec *codec = dai->codec; | 1832 | struct snd_soc_codec *codec = dai->codec; |
1833 | struct wm8994 *control = codec->control_data; | ||
1762 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | 1834 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); |
1763 | int aif1_reg; | 1835 | int aif1_reg; |
1764 | int bclk_reg; | 1836 | int bclk_reg; |
@@ -1797,6 +1869,14 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, | |||
1797 | dev_dbg(codec->dev, "AIF2 using split LRCLK\n"); | 1869 | dev_dbg(codec->dev, "AIF2 using split LRCLK\n"); |
1798 | } | 1870 | } |
1799 | break; | 1871 | break; |
1872 | case 3: | ||
1873 | switch (control->type) { | ||
1874 | case WM8958: | ||
1875 | aif1_reg = WM8958_AIF3_CONTROL_1; | ||
1876 | break; | ||
1877 | default: | ||
1878 | return 0; | ||
1879 | } | ||
1800 | default: | 1880 | default: |
1801 | return -EINVAL; | 1881 | return -EINVAL; |
1802 | } | 1882 | } |
@@ -1900,6 +1980,47 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, | |||
1900 | return 0; | 1980 | return 0; |
1901 | } | 1981 | } |
1902 | 1982 | ||
1983 | static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream, | ||
1984 | struct snd_pcm_hw_params *params, | ||
1985 | struct snd_soc_dai *dai) | ||
1986 | { | ||
1987 | struct snd_soc_codec *codec = dai->codec; | ||
1988 | struct wm8994 *control = codec->control_data; | ||
1989 | int aif1_reg; | ||
1990 | int aif1 = 0; | ||
1991 | |||
1992 | switch (dai->id) { | ||
1993 | case 3: | ||
1994 | switch (control->type) { | ||
1995 | case WM8958: | ||
1996 | aif1_reg = WM8958_AIF3_CONTROL_1; | ||
1997 | break; | ||
1998 | default: | ||
1999 | return 0; | ||
2000 | } | ||
2001 | default: | ||
2002 | return 0; | ||
2003 | } | ||
2004 | |||
2005 | switch (params_format(params)) { | ||
2006 | case SNDRV_PCM_FORMAT_S16_LE: | ||
2007 | break; | ||
2008 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
2009 | aif1 |= 0x20; | ||
2010 | break; | ||
2011 | case SNDRV_PCM_FORMAT_S24_LE: | ||
2012 | aif1 |= 0x40; | ||
2013 | break; | ||
2014 | case SNDRV_PCM_FORMAT_S32_LE: | ||
2015 | aif1 |= 0x60; | ||
2016 | break; | ||
2017 | default: | ||
2018 | return -EINVAL; | ||
2019 | } | ||
2020 | |||
2021 | return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); | ||
2022 | } | ||
2023 | |||
1903 | static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) | 2024 | static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) |
1904 | { | 2025 | { |
1905 | struct snd_soc_codec *codec = codec_dai->codec; | 2026 | struct snd_soc_codec *codec = codec_dai->codec; |
@@ -1981,6 +2102,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = { | |||
1981 | }; | 2102 | }; |
1982 | 2103 | ||
1983 | static struct snd_soc_dai_ops wm8994_aif3_dai_ops = { | 2104 | static struct snd_soc_dai_ops wm8994_aif3_dai_ops = { |
2105 | .hw_params = wm8994_aif3_hw_params, | ||
1984 | .set_tristate = wm8994_set_tristate, | 2106 | .set_tristate = wm8994_set_tristate, |
1985 | }; | 2107 | }; |
1986 | 2108 | ||
@@ -2511,9 +2633,35 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) | |||
2511 | ARRAY_SIZE(wm8994_snd_controls)); | 2633 | ARRAY_SIZE(wm8994_snd_controls)); |
2512 | snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets, | 2634 | snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets, |
2513 | ARRAY_SIZE(wm8994_dapm_widgets)); | 2635 | ARRAY_SIZE(wm8994_dapm_widgets)); |
2636 | |||
2637 | switch (control->type) { | ||
2638 | case WM8994: | ||
2639 | snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, | ||
2640 | ARRAY_SIZE(wm8994_specific_dapm_widgets)); | ||
2641 | break; | ||
2642 | case WM8958: | ||
2643 | snd_soc_add_controls(codec, wm8958_snd_controls, | ||
2644 | ARRAY_SIZE(wm8958_snd_controls)); | ||
2645 | snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets, | ||
2646 | ARRAY_SIZE(wm8958_dapm_widgets)); | ||
2647 | break; | ||
2648 | } | ||
2649 | |||
2650 | |||
2514 | wm_hubs_add_analogue_routes(codec, 0, 0); | 2651 | wm_hubs_add_analogue_routes(codec, 0, 0); |
2515 | snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); | 2652 | snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); |
2516 | 2653 | ||
2654 | switch (control->type) { | ||
2655 | case WM8994: | ||
2656 | snd_soc_dapm_add_routes(dapm, wm8994_intercon, | ||
2657 | ARRAY_SIZE(wm8994_intercon)); | ||
2658 | break; | ||
2659 | case WM8958: | ||
2660 | snd_soc_dapm_add_routes(dapm, wm8958_intercon, | ||
2661 | ARRAY_SIZE(wm8958_intercon)); | ||
2662 | break; | ||
2663 | } | ||
2664 | |||
2517 | return 0; | 2665 | return 0; |
2518 | 2666 | ||
2519 | err_irq: | 2667 | err_irq: |