aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8994.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-11-26 10:21:08 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-11-27 05:32:13 -0500
commitd6addcc9d88aeac4a0cc63a06d36baef04f5dc3b (patch)
tree2cfb1de63cb639e032c1586fc5631d70abe7602d /sound/soc/codecs/wm8994.c
parentc4431df050ff124cae7716e301cead1e8f33c575 (diff)
ASoC: Add WM8958 Multi-band compressor support
The WM8958 features a multi-band compressor which can be enabled on any of the AIF inputs. The MBC allows different gains to be applied to differnt audio bands, providing an improvement in perceived loudness of the signal by avoiding overdriving the output transducers. This patch enables support for the MBC. 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.c198
1 files changed, 186 insertions, 12 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index fb0609315cd6..b30b2dd3f1f4 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -80,6 +80,8 @@ struct wm8994_priv {
80 int dac_rates[2]; 80 int dac_rates[2];
81 int lrclk_shared[2]; 81 int lrclk_shared[2];
82 82
83 int mbc_ena[3];
84
83 /* Platform dependant DRC configuration */ 85 /* Platform dependant DRC configuration */
84 const char **drc_texts; 86 const char **drc_texts;
85 int drc_cfg[WM8994_NUM_DRC]; 87 int drc_cfg[WM8994_NUM_DRC];
@@ -137,6 +139,7 @@ static int wm8994_volatile(unsigned int reg)
137 case WM8994_RATE_STATUS: 139 case WM8994_RATE_STATUS:
138 case WM8994_LDO_1: 140 case WM8994_LDO_1:
139 case WM8994_LDO_2: 141 case WM8994_LDO_2:
142 case WM8958_DSP2_EXECCONTROL:
140 return 1; 143 return 1;
141 default: 144 default:
142 return 0; 145 return 0;
@@ -520,6 +523,168 @@ static const struct soc_enum aif2dacl_src =
520static const struct soc_enum aif2dacr_src = 523static const struct soc_enum aif2dacr_src =
521 SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aif_chan_src_text); 524 SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aif_chan_src_text);
522 525
526static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
527{
528 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
529 int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
530 int ena, reg, aif;
531
532 switch (mbc) {
533 case 0:
534 pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
535 aif = 0;
536 break;
537 case 1:
538 pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
539 aif = 0;
540 break;
541 case 2:
542 pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
543 aif = 1;
544 break;
545 default:
546 BUG();
547 return;
548 }
549
550 /* We can only enable the MBC if the AIF is enabled and we
551 * want it to be enabled. */
552 ena = pwr_reg && wm8994->mbc_ena[mbc];
553
554 reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
555
556 dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n",
557 mbc, start, pwr_reg, reg);
558
559 if (start && ena) {
560 /* If the DSP is already running then noop */
561 if (reg & WM8958_DSP2_ENA)
562 return;
563
564 /* Switch the clock over to the appropriate AIF */
565 snd_soc_update_bits(codec, WM8994_CLOCKING_1,
566 WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
567 aif << WM8958_DSP2CLK_SRC_SHIFT |
568 WM8958_DSP2CLK_ENA);
569
570 snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
571 WM8958_DSP2_ENA, WM8958_DSP2_ENA);
572
573 /* TODO: Apply any user specified MBC settings */
574
575 /* Run the DSP */
576 snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
577 WM8958_DSP2_RUNR);
578
579 /* And we're off! */
580 snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
581 WM8958_MBC_ENA | WM8958_MBC_SEL_MASK,
582 mbc << WM8958_MBC_SEL_SHIFT |
583 WM8958_MBC_ENA);
584 } else {
585 /* If the DSP is already stopped then noop */
586 if (!(reg & WM8958_DSP2_ENA))
587 return;
588
589 snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
590 WM8958_MBC_ENA, 0);
591 snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
592 WM8958_DSP2_ENA, 0);
593 snd_soc_update_bits(codec, WM8994_CLOCKING_1,
594 WM8958_DSP2CLK_ENA, 0);
595 }
596}
597
598static int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
599 struct snd_kcontrol *kcontrol, int event)
600{
601 struct snd_soc_codec *codec = w->codec;
602 int mbc;
603
604 switch (w->shift) {
605 case 13:
606 case 12:
607 mbc = 2;
608 break;
609 case 11:
610 case 10:
611 mbc = 1;
612 break;
613 case 9:
614 case 8:
615 mbc = 0;
616 break;
617 default:
618 BUG();
619 return -EINVAL;
620 }
621
622 switch (event) {
623 case SND_SOC_DAPM_POST_PMU:
624 wm8958_mbc_apply(codec, mbc, 1);
625 break;
626 case SND_SOC_DAPM_POST_PMD:
627 wm8958_mbc_apply(codec, mbc, 0);
628 break;
629 }
630
631 return 0;
632}
633
634static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
635 struct snd_ctl_elem_info *uinfo)
636{
637 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
638 uinfo->count = 1;
639 uinfo->value.integer.min = 0;
640 uinfo->value.integer.max = 1;
641 return 0;
642}
643
644static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
645 struct snd_ctl_elem_value *ucontrol)
646{
647 int mbc = kcontrol->private_value;
648 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
649 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
650
651 ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
652
653 return 0;
654}
655
656static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
657 struct snd_ctl_elem_value *ucontrol)
658{
659 int mbc = kcontrol->private_value;
660 int i;
661 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
662 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
663
664 if (ucontrol->value.integer.value[0] > 1)
665 return -EINVAL;
666
667 for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
668 if (mbc != i && wm8994->mbc_ena[i]) {
669 dev_dbg(codec->dev, "MBC %d active already\n", mbc);
670 return -EBUSY;
671 }
672 }
673
674 wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
675
676 wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]);
677
678 return 0;
679}
680
681#define WM8958_MBC_SWITCH(xname, xval) {\
682 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
683 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
684 .info = wm8958_mbc_info, \
685 .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
686 .private_value = xval }
687
523static const struct snd_kcontrol_new wm8994_snd_controls[] = { 688static const struct snd_kcontrol_new wm8994_snd_controls[] = {
524SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, 689SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
525 WM8994_AIF1_ADC1_RIGHT_VOLUME, 690 WM8994_AIF1_ADC1_RIGHT_VOLUME,
@@ -649,6 +814,9 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
649 814
650static const struct snd_kcontrol_new wm8958_snd_controls[] = { 815static const struct snd_kcontrol_new wm8958_snd_controls[] = {
651SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv), 816SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
817WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
818WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
819WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
652}; 820};
653 821
654static int clk_sys_event(struct snd_soc_dapm_widget *w, 822static int clk_sys_event(struct snd_soc_dapm_widget *w,
@@ -1018,19 +1186,23 @@ SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture",
1018 0, WM8994_POWER_MANAGEMENT_4, 9, 0), 1186 0, WM8994_POWER_MANAGEMENT_4, 9, 0),
1019SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture", 1187SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture",
1020 0, WM8994_POWER_MANAGEMENT_4, 8, 0), 1188 0, WM8994_POWER_MANAGEMENT_4, 8, 0),
1021SND_SOC_DAPM_AIF_IN("AIF1DAC1L", NULL, 0, 1189SND_SOC_DAPM_AIF_IN_E("AIF1DAC1L", NULL, 0,
1022 WM8994_POWER_MANAGEMENT_5, 9, 0), 1190 WM8994_POWER_MANAGEMENT_5, 9, 0, wm8958_aif_ev,
1023SND_SOC_DAPM_AIF_IN("AIF1DAC1R", NULL, 0, 1191 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1024 WM8994_POWER_MANAGEMENT_5, 8, 0), 1192SND_SOC_DAPM_AIF_IN_E("AIF1DAC1R", NULL, 0,
1193 WM8994_POWER_MANAGEMENT_5, 8, 0, wm8958_aif_ev,
1194 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1025 1195
1026SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture", 1196SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture",
1027 0, WM8994_POWER_MANAGEMENT_4, 11, 0), 1197 0, WM8994_POWER_MANAGEMENT_4, 11, 0),
1028SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture", 1198SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture",
1029 0, WM8994_POWER_MANAGEMENT_4, 10, 0), 1199 0, WM8994_POWER_MANAGEMENT_4, 10, 0),
1030SND_SOC_DAPM_AIF_IN("AIF1DAC2L", NULL, 0, 1200SND_SOC_DAPM_AIF_IN_E("AIF1DAC2L", NULL, 0,
1031 WM8994_POWER_MANAGEMENT_5, 11, 0), 1201 WM8994_POWER_MANAGEMENT_5, 11, 0, wm8958_aif_ev,
1032SND_SOC_DAPM_AIF_IN("AIF1DAC2R", NULL, 0, 1202 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1033 WM8994_POWER_MANAGEMENT_5, 10, 0), 1203SND_SOC_DAPM_AIF_IN_E("AIF1DAC2R", NULL, 0,
1204 WM8994_POWER_MANAGEMENT_5, 10, 0, wm8958_aif_ev,
1205 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1034 1206
1035SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0, 1207SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0,
1036 aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)), 1208 aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)),
@@ -1059,10 +1231,12 @@ SND_SOC_DAPM_AIF_OUT("AIF2ADCL", NULL, 0,
1059 WM8994_POWER_MANAGEMENT_4, 13, 0), 1231 WM8994_POWER_MANAGEMENT_4, 13, 0),
1060SND_SOC_DAPM_AIF_OUT("AIF2ADCR", NULL, 0, 1232SND_SOC_DAPM_AIF_OUT("AIF2ADCR", NULL, 0,
1061 WM8994_POWER_MANAGEMENT_4, 12, 0), 1233 WM8994_POWER_MANAGEMENT_4, 12, 0),
1062SND_SOC_DAPM_AIF_IN("AIF2DACL", NULL, 0, 1234SND_SOC_DAPM_AIF_IN_E("AIF2DACL", NULL, 0,
1063 WM8994_POWER_MANAGEMENT_5, 13, 0), 1235 WM8994_POWER_MANAGEMENT_5, 13, 0, wm8958_aif_ev,
1064SND_SOC_DAPM_AIF_IN("AIF2DACR", NULL, 0, 1236 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1065 WM8994_POWER_MANAGEMENT_5, 12, 0), 1237SND_SOC_DAPM_AIF_IN_E("AIF2DACR", NULL, 0,
1238 WM8994_POWER_MANAGEMENT_5, 12, 0, wm8958_aif_ev,
1239 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1066 1240
1067SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), 1241SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
1068SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), 1242SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),