diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-03-17 16:23:43 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-03-22 14:41:28 -0400 |
commit | 312158718fe2056703b2744801165a9574560495 (patch) | |
tree | ea5574d489d4381647bf33fd6d2122514bb61ea1 /sound | |
parent | 09e10d7fe509408d15818db6a0299f563668a7ba (diff) |
ASoC: Add WM8958 enhanced EQ support
DSP2 in the WM8958 can be used to support an upgraded EQ for use in
demanding applications.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm8958-dsp2.c | 192 | ||||
-rw-r--r-- | sound/soc/codecs/wm8994.c | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm8994.h | 7 |
3 files changed, 198 insertions, 3 deletions
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index d0e257315d97..74983ee2b87f 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c | |||
@@ -294,6 +294,36 @@ static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path) | |||
294 | path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA); | 294 | path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA); |
295 | } | 295 | } |
296 | 296 | ||
297 | static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path) | ||
298 | { | ||
299 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
300 | struct wm8994_pdata *pdata = wm8994->pdata; | ||
301 | int i; | ||
302 | |||
303 | wm8958_dsp2_fw(codec, "ENH_EQ", wm8994->enh_eq, false); | ||
304 | |||
305 | snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, | ||
306 | WM8958_DSP2_ENA, WM8958_DSP2_ENA); | ||
307 | |||
308 | /* If we've got user supplied settings use them */ | ||
309 | if (pdata && pdata->num_enh_eq_cfgs) { | ||
310 | struct wm8958_enh_eq_cfg *cfg | ||
311 | = &pdata->enh_eq_cfgs[wm8994->enh_eq_cfg]; | ||
312 | |||
313 | for (i = 0; i < ARRAY_SIZE(cfg->regs); i++) | ||
314 | snd_soc_write(codec, i + 0x2200, | ||
315 | cfg->regs[i]); | ||
316 | } | ||
317 | |||
318 | /* Run the DSP */ | ||
319 | snd_soc_write(codec, WM8958_DSP2_EXECCONTROL, | ||
320 | WM8958_DSP2_RUNR); | ||
321 | |||
322 | /* Switch the DSP into the data path */ | ||
323 | snd_soc_update_bits(codec, WM8958_DSP2_CONFIG, | ||
324 | WM8958_MBC_SEL_MASK | WM8958_MBC_ENA, | ||
325 | path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA); | ||
326 | } | ||
297 | 327 | ||
298 | static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start) | 328 | static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start) |
299 | { | 329 | { |
@@ -321,7 +351,8 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start) | |||
321 | 351 | ||
322 | /* Do we have both an active AIF and an active algorithm? */ | 352 | /* Do we have both an active AIF and an active algorithm? */ |
323 | ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] || | 353 | ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] || |
324 | wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path]; | 354 | wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] || |
355 | wm8994->enh_eq_ena[path]; | ||
325 | if (!pwr_reg) | 356 | if (!pwr_reg) |
326 | ena = 0; | 357 | ena = 0; |
327 | 358 | ||
@@ -344,7 +375,9 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start) | |||
344 | aif << WM8958_DSP2CLK_SRC_SHIFT | | 375 | aif << WM8958_DSP2CLK_SRC_SHIFT | |
345 | WM8958_DSP2CLK_ENA); | 376 | WM8958_DSP2CLK_ENA); |
346 | 377 | ||
347 | if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] || | 378 | if (wm8994->enh_eq_ena[path]) |
379 | wm8958_dsp_start_enh_eq(codec, path); | ||
380 | else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] || | ||
348 | wm8994->hpf2_ena[path]) | 381 | wm8994->hpf2_ena[path]) |
349 | wm8958_dsp_start_vss(codec, path); | 382 | wm8958_dsp_start_vss(codec, path); |
350 | else if (wm8994->mbc_ena[path]) | 383 | else if (wm8994->mbc_ena[path]) |
@@ -483,6 +516,9 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol, | |||
483 | return -EBUSY; | 516 | return -EBUSY; |
484 | } | 517 | } |
485 | 518 | ||
519 | if (wm8994->enh_eq_ena[mbc]) | ||
520 | return -EBUSY; | ||
521 | |||
486 | wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0]; | 522 | wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0]; |
487 | 523 | ||
488 | wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]); | 524 | wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]); |
@@ -603,6 +639,9 @@ static int wm8958_vss_put(struct snd_kcontrol *kcontrol, | |||
603 | return -EBUSY; | 639 | return -EBUSY; |
604 | } | 640 | } |
605 | 641 | ||
642 | if (wm8994->enh_eq_ena[vss]) | ||
643 | return -EBUSY; | ||
644 | |||
606 | wm8994->vss_ena[vss] = ucontrol->value.integer.value[0]; | 645 | wm8994->vss_ena[vss] = ucontrol->value.integer.value[0]; |
607 | 646 | ||
608 | wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]); | 647 | wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]); |
@@ -661,7 +700,7 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol, | |||
661 | return -EBUSY; | 700 | return -EBUSY; |
662 | } | 701 | } |
663 | 702 | ||
664 | if (wm8994->eq[hpf % 3]) | 703 | if (wm8994->enh_eq_ena[hpf % 3]) |
665 | return -EBUSY; | 704 | return -EBUSY; |
666 | 705 | ||
667 | if (hpf < 3) | 706 | if (hpf < 3) |
@@ -681,6 +720,97 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol, | |||
681 | .get = wm8958_hpf_get, .put = wm8958_hpf_put, \ | 720 | .get = wm8958_hpf_get, .put = wm8958_hpf_put, \ |
682 | .private_value = xval } | 721 | .private_value = xval } |
683 | 722 | ||
723 | static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol, | ||
724 | struct snd_ctl_elem_value *ucontrol) | ||
725 | { | ||
726 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
727 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
728 | struct wm8994_pdata *pdata = wm8994->pdata; | ||
729 | int value = ucontrol->value.integer.value[0]; | ||
730 | int reg; | ||
731 | |||
732 | /* Don't allow on the fly reconfiguration */ | ||
733 | reg = snd_soc_read(codec, WM8994_CLOCKING_1); | ||
734 | if (reg < 0 || reg & WM8958_DSP2CLK_ENA) | ||
735 | return -EBUSY; | ||
736 | |||
737 | if (value >= pdata->num_enh_eq_cfgs) | ||
738 | return -EINVAL; | ||
739 | |||
740 | wm8994->enh_eq_cfg = value; | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol, | ||
746 | struct snd_ctl_elem_value *ucontrol) | ||
747 | { | ||
748 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
749 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
750 | |||
751 | ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg; | ||
752 | |||
753 | return 0; | ||
754 | } | ||
755 | |||
756 | static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol, | ||
757 | struct snd_ctl_elem_info *uinfo) | ||
758 | { | ||
759 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
760 | uinfo->count = 1; | ||
761 | uinfo->value.integer.min = 0; | ||
762 | uinfo->value.integer.max = 1; | ||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol, | ||
767 | struct snd_ctl_elem_value *ucontrol) | ||
768 | { | ||
769 | int eq = kcontrol->private_value; | ||
770 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
771 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
772 | |||
773 | ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq]; | ||
774 | |||
775 | return 0; | ||
776 | } | ||
777 | |||
778 | static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol, | ||
779 | struct snd_ctl_elem_value *ucontrol) | ||
780 | { | ||
781 | int eq = kcontrol->private_value; | ||
782 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
783 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
784 | |||
785 | if (ucontrol->value.integer.value[0] > 1) | ||
786 | return -EINVAL; | ||
787 | |||
788 | if (!wm8994->enh_eq) | ||
789 | return -ENODEV; | ||
790 | |||
791 | if (wm8958_dsp2_busy(wm8994, eq)) { | ||
792 | dev_dbg(codec->dev, "DSP2 active on %d already\n", eq); | ||
793 | return -EBUSY; | ||
794 | } | ||
795 | |||
796 | if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] || | ||
797 | wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq]) | ||
798 | return -EBUSY; | ||
799 | |||
800 | wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0]; | ||
801 | |||
802 | wm8958_dsp_apply(codec, eq, ucontrol->value.integer.value[0]); | ||
803 | |||
804 | return 0; | ||
805 | } | ||
806 | |||
807 | #define WM8958_ENH_EQ_SWITCH(xname, xval) {\ | ||
808 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ | ||
809 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | ||
810 | .info = wm8958_enh_eq_info, \ | ||
811 | .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \ | ||
812 | .private_value = xval } | ||
813 | |||
684 | static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = { | 814 | static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = { |
685 | WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0), | 815 | WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0), |
686 | WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1), | 816 | WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1), |
@@ -699,6 +829,24 @@ WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4), | |||
699 | WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5), | 829 | WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5), |
700 | }; | 830 | }; |
701 | 831 | ||
832 | static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = { | ||
833 | WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0), | ||
834 | WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1), | ||
835 | WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2), | ||
836 | }; | ||
837 | |||
838 | static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) | ||
839 | { | ||
840 | struct snd_soc_codec *codec = context; | ||
841 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
842 | |||
843 | if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) { | ||
844 | mutex_lock(&codec->mutex); | ||
845 | wm8994->enh_eq = fw; | ||
846 | mutex_unlock(&codec->mutex); | ||
847 | } | ||
848 | } | ||
849 | |||
702 | static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) | 850 | static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) |
703 | { | 851 | { |
704 | struct snd_soc_codec *codec = context; | 852 | struct snd_soc_codec *codec = context; |
@@ -710,6 +858,12 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) | |||
710 | mutex_unlock(&codec->mutex); | 858 | mutex_unlock(&codec->mutex); |
711 | } | 859 | } |
712 | 860 | ||
861 | /* We can't have more than one request outstanding at once so | ||
862 | * we daisy chain. | ||
863 | */ | ||
864 | request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, | ||
865 | "wm8958_enh_eq.wfw", codec->dev, GFP_KERNEL, | ||
866 | codec, wm8958_enh_eq_loaded); | ||
713 | } | 867 | } |
714 | 868 | ||
715 | static void wm8958_mbc_loaded(const struct firmware *fw, void *context) | 869 | static void wm8958_mbc_loaded(const struct firmware *fw, void *context) |
@@ -744,6 +898,8 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) | |||
744 | ARRAY_SIZE(wm8958_mbc_snd_controls)); | 898 | ARRAY_SIZE(wm8958_mbc_snd_controls)); |
745 | snd_soc_add_controls(codec, wm8958_vss_snd_controls, | 899 | snd_soc_add_controls(codec, wm8958_vss_snd_controls, |
746 | ARRAY_SIZE(wm8958_vss_snd_controls)); | 900 | ARRAY_SIZE(wm8958_vss_snd_controls)); |
901 | snd_soc_add_controls(codec, wm8958_enh_eq_snd_controls, | ||
902 | ARRAY_SIZE(wm8958_enh_eq_snd_controls)); | ||
747 | 903 | ||
748 | 904 | ||
749 | /* We don't *require* firmware and don't want to delay boot */ | 905 | /* We don't *require* firmware and don't want to delay boot */ |
@@ -839,4 +995,34 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) | |||
839 | "Failed to add VSS HPFmode controls: %d\n", | 995 | "Failed to add VSS HPFmode controls: %d\n", |
840 | ret); | 996 | ret); |
841 | } | 997 | } |
998 | |||
999 | if (pdata->num_enh_eq_cfgs) { | ||
1000 | struct snd_kcontrol_new control[] = { | ||
1001 | SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum, | ||
1002 | wm8958_get_enh_eq_enum, | ||
1003 | wm8958_put_enh_eq_enum), | ||
1004 | }; | ||
1005 | |||
1006 | /* We need an array of texts for the enum API */ | ||
1007 | wm8994->enh_eq_texts = kmalloc(sizeof(char *) | ||
1008 | * pdata->num_enh_eq_cfgs, GFP_KERNEL); | ||
1009 | if (!wm8994->enh_eq_texts) { | ||
1010 | dev_err(wm8994->codec->dev, | ||
1011 | "Failed to allocate %d enhanced EQ config texts\n", | ||
1012 | pdata->num_enh_eq_cfgs); | ||
1013 | return; | ||
1014 | } | ||
1015 | |||
1016 | for (i = 0; i < pdata->num_enh_eq_cfgs; i++) | ||
1017 | wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name; | ||
1018 | |||
1019 | wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs; | ||
1020 | wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts; | ||
1021 | |||
1022 | ret = snd_soc_add_controls(wm8994->codec, control, 1); | ||
1023 | if (ret != 0) | ||
1024 | dev_err(wm8994->codec->dev, | ||
1025 | "Failed to add enhanced EQ controls: %d\n", | ||
1026 | ret); | ||
1027 | } | ||
842 | } | 1028 | } |
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 01ef5704091e..03ef62462bab 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c | |||
@@ -3142,6 +3142,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) | |||
3142 | release_firmware(wm8994->mbc); | 3142 | release_firmware(wm8994->mbc); |
3143 | if (wm8994->mbc_vss) | 3143 | if (wm8994->mbc_vss) |
3144 | release_firmware(wm8994->mbc_vss); | 3144 | release_firmware(wm8994->mbc_vss); |
3145 | if (wm8994->enh_eq) | ||
3146 | release_firmware(wm8994->enh_eq); | ||
3145 | kfree(wm8994->retune_mobile_texts); | 3147 | kfree(wm8994->retune_mobile_texts); |
3146 | kfree(wm8994->drc_texts); | 3148 | kfree(wm8994->drc_texts); |
3147 | kfree(wm8994); | 3149 | kfree(wm8994); |
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index f337f3d50590..0a1db04b73bd 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h | |||
@@ -87,6 +87,7 @@ struct wm8994_priv { | |||
87 | int hpf1_ena[3]; | 87 | int hpf1_ena[3]; |
88 | int hpf2_ena[3]; | 88 | int hpf2_ena[3]; |
89 | int vss_ena[3]; | 89 | int vss_ena[3]; |
90 | int enh_eq_ena[3]; | ||
90 | 91 | ||
91 | /* Platform dependant DRC configuration */ | 92 | /* Platform dependant DRC configuration */ |
92 | const char **drc_texts; | 93 | const char **drc_texts; |
@@ -114,6 +115,11 @@ struct wm8994_priv { | |||
114 | const char **vss_hpf_texts; | 115 | const char **vss_hpf_texts; |
115 | struct soc_enum vss_hpf_enum; | 116 | struct soc_enum vss_hpf_enum; |
116 | 117 | ||
118 | /* Platform dependant enhanced EQ configuration */ | ||
119 | int enh_eq_cfg; | ||
120 | const char **enh_eq_texts; | ||
121 | struct soc_enum enh_eq_enum; | ||
122 | |||
117 | struct wm8994_micdet micdet[2]; | 123 | struct wm8994_micdet micdet[2]; |
118 | 124 | ||
119 | wm8958_micdet_cb jack_cb; | 125 | wm8958_micdet_cb jack_cb; |
@@ -133,6 +139,7 @@ struct wm8994_priv { | |||
133 | const struct firmware *cur_fw; | 139 | const struct firmware *cur_fw; |
134 | const struct firmware *mbc; | 140 | const struct firmware *mbc; |
135 | const struct firmware *mbc_vss; | 141 | const struct firmware *mbc_vss; |
142 | const struct firmware *enh_eq; | ||
136 | }; | 143 | }; |
137 | 144 | ||
138 | #endif | 145 | #endif |