aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8971.c
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@slimlogic.co.uk>2010-03-17 16:15:21 -0400
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-08-12 09:00:00 -0400
commitf0fba2ad1b6b53d5360125c41953b7afcd6deff0 (patch)
treef6ad50905f8daa616593c978d7ae992e73241180 /sound/soc/codecs/wm8971.c
parentbda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff)
ASoC: multi-component - ASoC Multi-Component Support
This patch extends the ASoC API to allow sound cards to have more than one CODEC and more than one platform DMA controller. This is achieved by dividing some current ASoC structures that contain both driver data and device data into structures that only either contain device data or driver data. i.e. struct snd_soc_codec ---> struct snd_soc_codec (device data) +-> struct snd_soc_codec_driver (driver data) struct snd_soc_platform ---> struct snd_soc_platform (device data) +-> struct snd_soc_platform_driver (driver data) struct snd_soc_dai ---> struct snd_soc_dai (device data) +-> struct snd_soc_dai_driver (driver data) struct snd_soc_device ---> deleted This now allows ASoC to be more tightly aligned with the Linux driver model and also means that every ASoC codec, platform and (platform) DAI is a kernel device. ASoC component private data is now stored as device private data. The ASoC sound card struct snd_soc_card has also been updated to store lists of it's components rather than a pointer to a codec and platform. The PCM runtime struct soc_pcm_runtime now has pointers to all its components. This patch adds DAPM support for ASoC multi-component and removes struct snd_soc_socdev from DAPM core. All DAPM calls are now made on a card, codec or runtime PCM level basis rather than using snd_soc_socdev. Other notable multi-component changes:- * Stream operations now de-reference less structures. * close_delayed work() now runs on a DAI basis rather than looping all DAIs in a card. * PM suspend()/resume() operations can now handle N CODECs and Platforms per sound card. * Added soc_bind_dai_link() to bind the component devices to the sound card. * Added soc_dai_link_probe() and soc_dai_link_remove() to probe and remove DAI link components. * sysfs entries can now be registered per component per card. * snd_soc_new_pcms() functionailty rolled into dai_link_probe(). * snd_soc_register_codec() now does all the codec list and mutex init. This patch changes the probe() and remove() of the CODEC drivers as follows:- o Make CODEC driver a platform driver o Moved all struct snd_soc_codec list, mutex, etc initialiasation to core. o Removed all static codec pointers (drivers now support > 1 codec dev) o snd_soc_register_pcms() now done by core. o snd_soc_register_dai() folded into snd_soc_register_codec(). CS4270 portions: Acked-by: Timur Tabi <timur@freescale.com> Some TLV320aic23 and Cirrus platform fixes. Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> TI CODEC and OMAP fixes Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> Signed-off-by: Jarkko Nikula <jhnikula@gmail.com> Samsung platform and misc fixes :- Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Jassi Brar <jassi.brar@samsung.com> Signed-off-by: Seungwhan Youn <sw.youn@samsung.com> MPC8610 and PPC fixes. Signed-off-by: Timur Tabi <timur@freescale.com> i.MX fixes and some core fixes. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> J4740 platform fixes:- Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> CC: Tony Lindgren <tony@atomide.com> CC: Nicolas Ferre <nicolas.ferre@atmel.com> CC: Kevin Hilman <khilman@deeprootsystems.com> CC: Sascha Hauer <s.hauer@pengutronix.de> CC: Atsushi Nemoto <anemo@mba.ocn.ne.jp> CC: Kuninori Morimoto <morimoto.kuninori@renesas.com> CC: Daniel Gloeckner <dg@emlix.com> CC: Manuel Lauss <mano@roarinelk.homelinux.net> CC: Mike Frysinger <vapier.adi@gmail.com> CC: Arnaud Patard <apatard@mandriva.com> CC: Wan ZongShun <mcuos.com@gmail.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/wm8971.c')
-rw-r--r--sound/soc/codecs/wm8971.c250
1 files changed, 68 insertions, 182 deletions
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index a99620f335d2..ad2692afbb31 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -38,6 +38,8 @@ static struct workqueue_struct *wm8971_workq = NULL;
38 38
39/* codec private data */ 39/* codec private data */
40struct wm8971_priv { 40struct wm8971_priv {
41 enum snd_soc_control_type control_type;
42 void *control_data;
41 unsigned int sysclk; 43 unsigned int sysclk;
42}; 44};
43 45
@@ -492,8 +494,7 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
492 struct snd_soc_dai *dai) 494 struct snd_soc_dai *dai)
493{ 495{
494 struct snd_soc_pcm_runtime *rtd = substream->private_data; 496 struct snd_soc_pcm_runtime *rtd = substream->private_data;
495 struct snd_soc_device *socdev = rtd->socdev; 497 struct snd_soc_codec *codec = rtd->codec;
496 struct snd_soc_codec *codec = socdev->card->codec;
497 struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); 498 struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
498 u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3; 499 u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
499 u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0; 500 u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
@@ -573,8 +574,8 @@ static struct snd_soc_dai_ops wm8971_dai_ops = {
573 .set_sysclk = wm8971_set_dai_sysclk, 574 .set_sysclk = wm8971_set_dai_sysclk,
574}; 575};
575 576
576struct snd_soc_dai wm8971_dai = { 577static struct snd_soc_dai_driver wm8971_dai = {
577 .name = "WM8971", 578 .name = "wm8971-hifi",
578 .playback = { 579 .playback = {
579 .stream_name = "Playback", 580 .stream_name = "Playback",
580 .channels_min = 1, 581 .channels_min = 1,
@@ -589,7 +590,6 @@ struct snd_soc_dai wm8971_dai = {
589 .formats = WM8971_FORMATS,}, 590 .formats = WM8971_FORMATS,},
590 .ops = &wm8971_dai_ops, 591 .ops = &wm8971_dai_ops,
591}; 592};
592EXPORT_SYMBOL_GPL(wm8971_dai);
593 593
594static void wm8971_work(struct work_struct *work) 594static void wm8971_work(struct work_struct *work)
595{ 595{
@@ -598,19 +598,14 @@ static void wm8971_work(struct work_struct *work)
598 wm8971_set_bias_level(codec, codec->bias_level); 598 wm8971_set_bias_level(codec, codec->bias_level);
599} 599}
600 600
601static int wm8971_suspend(struct platform_device *pdev, pm_message_t state) 601static int wm8971_suspend(struct snd_soc_codec *codec, pm_message_t state)
602{ 602{
603 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
604 struct snd_soc_codec *codec = socdev->card->codec;
605
606 wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); 603 wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
607 return 0; 604 return 0;
608} 605}
609 606
610static int wm8971_resume(struct platform_device *pdev) 607static int wm8971_resume(struct snd_soc_codec *codec)
611{ 608{
612 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
613 struct snd_soc_codec *codec = socdev->card->codec;
614 int i; 609 int i;
615 u8 data[2]; 610 u8 data[2];
616 u16 *cache = codec->reg_cache; 611 u16 *cache = codec->reg_cache;
@@ -639,37 +634,27 @@ static int wm8971_resume(struct platform_device *pdev)
639 return 0; 634 return 0;
640} 635}
641 636
642static int wm8971_init(struct snd_soc_device *socdev, 637static int wm8971_probe(struct snd_soc_codec *codec)
643 enum snd_soc_control_type control)
644{ 638{
645 struct snd_soc_codec *codec = socdev->card->codec; 639 struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
646 int reg, ret = 0; 640 int ret = 0;
647 641 u16 reg;
648 codec->name = "WM8971"; 642
649 codec->owner = THIS_MODULE; 643 pr_info("WM8971 Audio Codec %s", WM8971_VERSION);
650 codec->set_bias_level = wm8971_set_bias_level;
651 codec->dai = &wm8971_dai;
652 codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
653 codec->num_dai = 1;
654 codec->reg_cache = kmemdup(wm8971_reg, sizeof(wm8971_reg), GFP_KERNEL);
655
656 if (codec->reg_cache == NULL)
657 return -ENOMEM;
658 644
659 ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); 645 codec->control_data = wm8971->control_data;
646 ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8971->control_type);
660 if (ret < 0) { 647 if (ret < 0) {
661 printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret); 648 printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret);
662 goto err; 649 return ret;
663 } 650 }
664 651
665 wm8971_reset(codec); 652 INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work);
653 wm8971_workq = create_workqueue("wm8971");
654 if (wm8971_workq == NULL)
655 return -ENOMEM;
666 656
667 /* register pcms */ 657 wm8971_reset(codec);
668 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
669 if (ret < 0) {
670 printk(KERN_ERR "wm8971: failed to create pcms\n");
671 goto err;
672 }
673 658
674 /* charge output caps - set vmid to 5k for quick power up */ 659 /* charge output caps - set vmid to 5k for quick power up */
675 reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; 660 reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
@@ -704,40 +689,55 @@ static int wm8971_init(struct snd_soc_device *socdev,
704 wm8971_add_widgets(codec); 689 wm8971_add_widgets(codec);
705 690
706 return ret; 691 return ret;
707
708err:
709 kfree(codec->reg_cache);
710 return ret;
711} 692}
712 693
713/* If the i2c layer weren't so broken, we could pass this kind of data
714 around */
715static struct snd_soc_device *wm8971_socdev;
716 694
717#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) 695/* power down chip */
696static int wm8971_remove(struct snd_soc_codec *codec)
697{
698 wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
699
700 if (wm8971_workq)
701 destroy_workqueue(wm8971_workq);
702 return 0;
703}
704
705static struct snd_soc_codec_driver soc_codec_dev_wm8971 = {
706 .probe = wm8971_probe,
707 .remove = wm8971_remove,
708 .suspend = wm8971_suspend,
709 .resume = wm8971_resume,
710 .set_bias_level = wm8971_set_bias_level,
711 .reg_cache_size = ARRAY_SIZE(wm8971_reg),
712 .reg_word_size = sizeof(u16),
713 .reg_cache_default = wm8971_reg,
714};
718 715
719static int wm8971_i2c_probe(struct i2c_client *i2c, 716#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
720 const struct i2c_device_id *id) 717static __devinit int wm8971_i2c_probe(struct i2c_client *i2c,
718 const struct i2c_device_id *id)
721{ 719{
722 struct snd_soc_device *socdev = wm8971_socdev; 720 struct wm8971_priv *wm8971;
723 struct snd_soc_codec *codec = socdev->card->codec;
724 int ret; 721 int ret;
725 722
726 i2c_set_clientdata(i2c, codec); 723 wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL);
724 if (wm8971 == NULL)
725 return -ENOMEM;
727 726
728 codec->control_data = i2c; 727 i2c_set_clientdata(i2c, wm8971);
728 wm8971->control_data = i2c;
729 729
730 ret = wm8971_init(socdev, SND_SOC_I2C); 730 ret = snd_soc_register_codec(&i2c->dev,
731 &soc_codec_dev_wm8971, &wm8971_dai, 1);
731 if (ret < 0) 732 if (ret < 0)
732 pr_err("failed to initialise WM8971\n"); 733 kfree(wm8971);
733
734 return ret; 734 return ret;
735} 735}
736 736
737static int wm8971_i2c_remove(struct i2c_client *client) 737static __devexit int wm8971_i2c_remove(struct i2c_client *client)
738{ 738{
739 struct snd_soc_codec *codec = i2c_get_clientdata(client); 739 snd_soc_unregister_codec(&client->dev);
740 kfree(codec->reg_cache); 740 kfree(i2c_get_clientdata(client));
741 return 0; 741 return 0;
742} 742}
743 743
@@ -749,148 +749,34 @@ MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id);
749 749
750static struct i2c_driver wm8971_i2c_driver = { 750static struct i2c_driver wm8971_i2c_driver = {
751 .driver = { 751 .driver = {
752 .name = "WM8971 I2C Codec", 752 .name = "wm8971-codec",
753 .owner = THIS_MODULE, 753 .owner = THIS_MODULE,
754 }, 754 },
755 .probe = wm8971_i2c_probe, 755 .probe = wm8971_i2c_probe,
756 .remove = wm8971_i2c_remove, 756 .remove = __devexit_p(wm8971_i2c_remove),
757 .id_table = wm8971_i2c_id, 757 .id_table = wm8971_i2c_id,
758}; 758};
759
760static int wm8971_add_i2c_device(struct platform_device *pdev,
761 const struct wm8971_setup_data *setup)
762{
763 struct i2c_board_info info;
764 struct i2c_adapter *adapter;
765 struct i2c_client *client;
766 int ret;
767
768 ret = i2c_add_driver(&wm8971_i2c_driver);
769 if (ret != 0) {
770 dev_err(&pdev->dev, "can't add i2c driver\n");
771 return ret;
772 }
773
774 memset(&info, 0, sizeof(struct i2c_board_info));
775 info.addr = setup->i2c_address;
776 strlcpy(info.type, "wm8971", I2C_NAME_SIZE);
777
778 adapter = i2c_get_adapter(setup->i2c_bus);
779 if (!adapter) {
780 dev_err(&pdev->dev, "can't get i2c adapter %d\n",
781 setup->i2c_bus);
782 goto err_driver;
783 }
784
785 client = i2c_new_device(adapter, &info);
786 i2c_put_adapter(adapter);
787 if (!client) {
788 dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
789 (unsigned int)info.addr);
790 goto err_driver;
791 }
792
793 return 0;
794
795err_driver:
796 i2c_del_driver(&wm8971_i2c_driver);
797 return -ENODEV;
798}
799
800#endif 759#endif
801 760
802static int wm8971_probe(struct platform_device *pdev) 761static int __init wm8971_modinit(void)
803{ 762{
804 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
805 struct wm8971_setup_data *setup;
806 struct snd_soc_codec *codec;
807 struct wm8971_priv *wm8971;
808 int ret = 0; 763 int ret = 0;
809 764#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
810 pr_info("WM8971 Audio Codec %s", WM8971_VERSION); 765 ret = i2c_add_driver(&wm8971_i2c_driver);
811
812 setup = socdev->codec_data;
813 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
814 if (codec == NULL)
815 return -ENOMEM;
816
817 wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL);
818 if (wm8971 == NULL) {
819 kfree(codec);
820 return -ENOMEM;
821 }
822
823 snd_soc_codec_set_drvdata(codec, wm8971);
824 socdev->card->codec = codec;
825 mutex_init(&codec->mutex);
826 INIT_LIST_HEAD(&codec->dapm_widgets);
827 INIT_LIST_HEAD(&codec->dapm_paths);
828 wm8971_socdev = socdev;
829
830 INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work);
831 wm8971_workq = create_workqueue("wm8971");
832 if (wm8971_workq == NULL) {
833 kfree(snd_soc_codec_get_drvdata(codec));
834 kfree(codec);
835 return -ENOMEM;
836 }
837
838#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
839 if (setup->i2c_address) {
840 ret = wm8971_add_i2c_device(pdev, setup);
841 }
842#endif
843 /* Add other interfaces here */
844
845 if (ret != 0) { 766 if (ret != 0) {
846 destroy_workqueue(wm8971_workq); 767 printk(KERN_ERR "Failed to register WM8971 I2C driver: %d\n",
847 kfree(snd_soc_codec_get_drvdata(codec)); 768 ret);
848 kfree(codec);
849 } 769 }
850
851 return ret;
852}
853
854/* power down chip */
855static int wm8971_remove(struct platform_device *pdev)
856{
857 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
858 struct snd_soc_codec *codec = socdev->card->codec;
859
860 if (codec->control_data)
861 wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
862 if (wm8971_workq)
863 destroy_workqueue(wm8971_workq);
864 snd_soc_free_pcms(socdev);
865 snd_soc_dapm_free(socdev);
866#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
867 i2c_unregister_device(codec->control_data);
868 i2c_del_driver(&wm8971_i2c_driver);
869#endif 770#endif
870 kfree(snd_soc_codec_get_drvdata(codec)); 771 return ret;
871 kfree(codec);
872
873 return 0;
874}
875
876struct snd_soc_codec_device soc_codec_dev_wm8971 = {
877 .probe = wm8971_probe,
878 .remove = wm8971_remove,
879 .suspend = wm8971_suspend,
880 .resume = wm8971_resume,
881};
882
883EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
884
885static int __init wm8971_modinit(void)
886{
887 return snd_soc_register_dai(&wm8971_dai);
888} 772}
889module_init(wm8971_modinit); 773module_init(wm8971_modinit);
890 774
891static void __exit wm8971_exit(void) 775static void __exit wm8971_exit(void)
892{ 776{
893 snd_soc_unregister_dai(&wm8971_dai); 777#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
778 i2c_del_driver(&wm8971_i2c_driver);
779#endif
894} 780}
895module_exit(wm8971_exit); 781module_exit(wm8971_exit);
896 782