aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8958-dsp2.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-03-16 18:57:47 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-03-22 14:41:27 -0400
commit09e10d7fe509408d15818db6a0299f563668a7ba (patch)
treec00c5cff262e93e0c2fd697fe4ac2359f28fd98e /sound/soc/codecs/wm8958-dsp2.c
parentf20d77ce2663b31c2994462d9ab9143726b67f3e (diff)
ASoC: Add WM8958 VSS support
With appropriate firmware the WM8958 can support Virtual Surround Sound or VSS, widening the stereo audio image for improved user experience. Enable support for this mode of operation when the appropriate firmware can be loaded at runtime. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@ti.com>
Diffstat (limited to 'sound/soc/codecs/wm8958-dsp2.c')
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c363
1 files changed, 355 insertions, 8 deletions
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index 9c1cbe5b61ae..d0e257315d97 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -233,6 +233,68 @@ static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
233 WM8958_MBC_ENA); 233 WM8958_MBC_ENA);
234} 234}
235 235
236static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
237{
238 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
239 struct wm8994_pdata *pdata = wm8994->pdata;
240 int i, ena;
241
242 if (wm8994->mbc_vss)
243 wm8958_dsp2_fw(codec, "MBC+VSS", wm8994->mbc_vss, false);
244
245 snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
246 WM8958_DSP2_ENA, WM8958_DSP2_ENA);
247
248 /* If we've got user supplied settings use them */
249 if (pdata && pdata->num_mbc_cfgs) {
250 struct wm8958_mbc_cfg *cfg
251 = &pdata->mbc_cfgs[wm8994->mbc_cfg];
252
253 for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
254 snd_soc_write(codec, i + 0x2800,
255 cfg->combined_regs[i]);
256 }
257
258 if (pdata && pdata->num_vss_cfgs) {
259 struct wm8958_vss_cfg *cfg
260 = &pdata->vss_cfgs[wm8994->vss_cfg];
261
262 for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
263 snd_soc_write(codec, i + 0x2600, cfg->regs[i]);
264 }
265
266 if (pdata && pdata->num_vss_hpf_cfgs) {
267 struct wm8958_vss_hpf_cfg *cfg
268 = &pdata->vss_hpf_cfgs[wm8994->vss_hpf_cfg];
269
270 for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
271 snd_soc_write(codec, i + 0x2400, cfg->regs[i]);
272 }
273
274 /* Run the DSP */
275 snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
276 WM8958_DSP2_RUNR);
277
278 /* Enable the algorithms we've selected */
279 ena = 0;
280 if (wm8994->mbc_ena[path])
281 ena |= 0x8;
282 if (wm8994->hpf2_ena[path])
283 ena |= 0x4;
284 if (wm8994->hpf1_ena[path])
285 ena |= 0x2;
286 if (wm8994->vss_ena[path])
287 ena |= 0x1;
288
289 snd_soc_write(codec, 0x2201, ena);
290
291 /* Switch the DSP into the data path */
292 snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
293 WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
294 path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
295}
296
297
236static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start) 298static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
237{ 299{
238 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); 300 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
@@ -258,7 +320,8 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
258 } 320 }
259 321
260 /* Do we have both an active AIF and an active algorithm? */ 322 /* Do we have both an active AIF and an active algorithm? */
261 ena = wm8994->mbc_ena[path]; 323 ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||
324 wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path];
262 if (!pwr_reg) 325 if (!pwr_reg)
263 ena = 0; 326 ena = 0;
264 327
@@ -281,11 +344,18 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
281 aif << WM8958_DSP2CLK_SRC_SHIFT | 344 aif << WM8958_DSP2CLK_SRC_SHIFT |
282 WM8958_DSP2CLK_ENA); 345 WM8958_DSP2CLK_ENA);
283 346
284 if (wm8994->mbc_ena[path]) 347 if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
348 wm8994->hpf2_ena[path])
349 wm8958_dsp_start_vss(codec, path);
350 else if (wm8994->mbc_ena[path])
285 wm8958_dsp_start_mbc(codec, path); 351 wm8958_dsp_start_mbc(codec, path);
286 352
287 dev_dbg(codec->dev, "DSP running\n"); 353 wm8994->dsp_active = path;
288 } else { 354
355 dev_dbg(codec->dev, "DSP running in path %d\n", path);
356 }
357
358 if (!start && wm8994->dsp_active == path) {
289 /* If the DSP is already stopped then noop */ 359 /* If the DSP is already stopped then noop */
290 if (!(reg & WM8958_DSP2_ENA)) 360 if (!(reg & WM8958_DSP2_ENA))
291 return; 361 return;
@@ -335,7 +405,8 @@ static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
335 for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) { 405 for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
336 if (i == aif) 406 if (i == aif)
337 continue; 407 continue;
338 if (wm8994->mbc_ena[i]) 408 if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||
409 wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])
339 return 1; 410 return 1;
340 } 411 }
341 412
@@ -426,22 +497,239 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
426 .get = wm8958_mbc_get, .put = wm8958_mbc_put, \ 497 .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
427 .private_value = xval } 498 .private_value = xval }
428 499
500static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
501 struct snd_ctl_elem_value *ucontrol)
502{
503 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
504 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
505 struct wm8994_pdata *pdata = wm8994->pdata;
506 int value = ucontrol->value.integer.value[0];
507 int reg;
508
509 /* Don't allow on the fly reconfiguration */
510 reg = snd_soc_read(codec, WM8994_CLOCKING_1);
511 if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
512 return -EBUSY;
513
514 if (value >= pdata->num_vss_cfgs)
515 return -EINVAL;
516
517 wm8994->vss_cfg = value;
518
519 return 0;
520}
521
522static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
523 struct snd_ctl_elem_value *ucontrol)
524{
525 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
526 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
527
528 ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
529
530 return 0;
531}
532
533static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
534 struct snd_ctl_elem_value *ucontrol)
535{
536 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
537 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
538 struct wm8994_pdata *pdata = wm8994->pdata;
539 int value = ucontrol->value.integer.value[0];
540 int reg;
541
542 /* Don't allow on the fly reconfiguration */
543 reg = snd_soc_read(codec, WM8994_CLOCKING_1);
544 if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
545 return -EBUSY;
546
547 if (value >= pdata->num_vss_hpf_cfgs)
548 return -EINVAL;
549
550 wm8994->vss_hpf_cfg = value;
551
552 return 0;
553}
554
555static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
556 struct snd_ctl_elem_value *ucontrol)
557{
558 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
559 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
560
561 ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
562
563 return 0;
564}
565
566static int wm8958_vss_info(struct snd_kcontrol *kcontrol,
567 struct snd_ctl_elem_info *uinfo)
568{
569 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
570 uinfo->count = 1;
571 uinfo->value.integer.min = 0;
572 uinfo->value.integer.max = 1;
573 return 0;
574}
575
576static int wm8958_vss_get(struct snd_kcontrol *kcontrol,
577 struct snd_ctl_elem_value *ucontrol)
578{
579 int vss = kcontrol->private_value;
580 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
581 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
582
583 ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
584
585 return 0;
586}
587
588static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
589 struct snd_ctl_elem_value *ucontrol)
590{
591 int vss = kcontrol->private_value;
592 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
593 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
594
595 if (ucontrol->value.integer.value[0] > 1)
596 return -EINVAL;
597
598 if (!wm8994->mbc_vss)
599 return -ENODEV;
600
601 if (wm8958_dsp2_busy(wm8994, vss)) {
602 dev_dbg(codec->dev, "DSP2 active on %d already\n", vss);
603 return -EBUSY;
604 }
605
606 wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
607
608 wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]);
609
610 return 0;
611}
612
613
614#define WM8958_VSS_SWITCH(xname, xval) {\
615 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
616 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
617 .info = wm8958_vss_info, \
618 .get = wm8958_vss_get, .put = wm8958_vss_put, \
619 .private_value = xval }
620
621static int wm8958_hpf_info(struct snd_kcontrol *kcontrol,
622 struct snd_ctl_elem_info *uinfo)
623{
624 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
625 uinfo->count = 1;
626 uinfo->value.integer.min = 0;
627 uinfo->value.integer.max = 1;
628 return 0;
629}
630
631static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
632 struct snd_ctl_elem_value *ucontrol)
633{
634 int hpf = kcontrol->private_value;
635 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
636 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
637
638 if (hpf < 3)
639 ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
640 else
641 ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
642
643 return 0;
644}
645
646static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
647 struct snd_ctl_elem_value *ucontrol)
648{
649 int hpf = kcontrol->private_value;
650 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
651 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
652
653 if (ucontrol->value.integer.value[0] > 1)
654 return -EINVAL;
655
656 if (!wm8994->mbc_vss)
657 return -ENODEV;
658
659 if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
660 dev_dbg(codec->dev, "DSP2 active on %d already\n", hpf);
661 return -EBUSY;
662 }
663
664 if (wm8994->eq[hpf % 3])
665 return -EBUSY;
666
667 if (hpf < 3)
668 wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];
669 else
670 wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
671
672 wm8958_dsp_apply(codec, hpf % 3, ucontrol->value.integer.value[0]);
673
674 return 0;
675}
676
677#define WM8958_HPF_SWITCH(xname, xval) {\
678 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
679 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
680 .info = wm8958_hpf_info, \
681 .get = wm8958_hpf_get, .put = wm8958_hpf_put, \
682 .private_value = xval }
683
429static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = { 684static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
430WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0), 685WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
431WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1), 686WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
432WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2), 687WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
433}; 688};
434 689
435static void wm8958_mbc_loaded(const struct firmware *fw, void *context) 690static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
691WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
692WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
693WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
694WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
695WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
696WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
697WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
698WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
699WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
700};
701
702static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
436{ 703{
437 struct snd_soc_codec *codec = context; 704 struct snd_soc_codec *codec = context;
438 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); 705 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
439 706
440 if (fw && wm8958_dsp2_fw(codec, "MBC", fw, true) != 0) { 707 if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
441 mutex_lock(&codec->mutex); 708 mutex_lock(&codec->mutex);
442 wm8994->mbc = fw; 709 wm8994->mbc_vss = fw;
443 mutex_unlock(&codec->mutex); 710 mutex_unlock(&codec->mutex);
444 } 711 }
712
713}
714
715static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
716{
717 struct snd_soc_codec *codec = context;
718 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
719
720 if (wm8958_dsp2_fw(codec, "MBC", fw, true) != 0)
721 return;
722
723 mutex_lock(&codec->mutex);
724 wm8994->mbc = fw;
725 mutex_unlock(&codec->mutex);
726
727 /* We can't have more than one request outstanding at once so
728 * we daisy chain.
729 */
730 request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
731 "wm8958_mbc_vss.wfw", codec->dev, GFP_KERNEL,
732 codec, wm8958_mbc_vss_loaded);
445} 733}
446 734
447void wm8958_dsp2_init(struct snd_soc_codec *codec) 735void wm8958_dsp2_init(struct snd_soc_codec *codec)
@@ -454,6 +742,9 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
454 742
455 snd_soc_add_controls(codec, wm8958_mbc_snd_controls, 743 snd_soc_add_controls(codec, wm8958_mbc_snd_controls,
456 ARRAY_SIZE(wm8958_mbc_snd_controls)); 744 ARRAY_SIZE(wm8958_mbc_snd_controls));
745 snd_soc_add_controls(codec, wm8958_vss_snd_controls,
746 ARRAY_SIZE(wm8958_vss_snd_controls));
747
457 748
458 /* We don't *require* firmware and don't want to delay boot */ 749 /* We don't *require* firmware and don't want to delay boot */
459 request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, 750 request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
@@ -491,5 +782,61 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
491 "Failed to add MBC mode controls: %d\n", ret); 782 "Failed to add MBC mode controls: %d\n", ret);
492 } 783 }
493 784
785 if (pdata->num_vss_cfgs) {
786 struct snd_kcontrol_new control[] = {
787 SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
788 wm8958_get_vss_enum, wm8958_put_vss_enum),
789 };
494 790
791 /* We need an array of texts for the enum API */
792 wm8994->vss_texts = kmalloc(sizeof(char *)
793 * pdata->num_vss_cfgs, GFP_KERNEL);
794 if (!wm8994->vss_texts) {
795 dev_err(wm8994->codec->dev,
796 "Failed to allocate %d VSS config texts\n",
797 pdata->num_vss_cfgs);
798 return;
799 }
800
801 for (i = 0; i < pdata->num_vss_cfgs; i++)
802 wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
803
804 wm8994->vss_enum.max = pdata->num_vss_cfgs;
805 wm8994->vss_enum.texts = wm8994->vss_texts;
806
807 ret = snd_soc_add_controls(wm8994->codec, control, 1);
808 if (ret != 0)
809 dev_err(wm8994->codec->dev,
810 "Failed to add VSS mode controls: %d\n", ret);
811 }
812
813 if (pdata->num_vss_hpf_cfgs) {
814 struct snd_kcontrol_new control[] = {
815 SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
816 wm8958_get_vss_hpf_enum,
817 wm8958_put_vss_hpf_enum),
818 };
819
820 /* We need an array of texts for the enum API */
821 wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
822 * pdata->num_vss_hpf_cfgs, GFP_KERNEL);
823 if (!wm8994->vss_hpf_texts) {
824 dev_err(wm8994->codec->dev,
825 "Failed to allocate %d VSS HPF config texts\n",
826 pdata->num_vss_hpf_cfgs);
827 return;
828 }
829
830 for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
831 wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
832
833 wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs;
834 wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
835
836 ret = snd_soc_add_controls(wm8994->codec, control, 1);
837 if (ret != 0)
838 dev_err(wm8994->codec->dev,
839 "Failed to add VSS HPFmode controls: %d\n",
840 ret);
841 }
495} 842}