aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8750.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/wm8750.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/wm8750.c')
-rw-r--r--sound/soc/codecs/wm8750.c269
1 files changed, 84 insertions, 185 deletions
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index e2c05e3e323a..89863a5bc830 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -52,7 +52,8 @@ static const u16 wm8750_reg[] = {
52/* codec private data */ 52/* codec private data */
53struct wm8750_priv { 53struct wm8750_priv {
54 unsigned int sysclk; 54 unsigned int sysclk;
55 struct snd_soc_codec codec; 55 enum snd_soc_control_type control_type;
56 void *control_data;
56 u16 reg_cache[ARRAY_SIZE(wm8750_reg)]; 57 u16 reg_cache[ARRAY_SIZE(wm8750_reg)];
57}; 58};
58 59
@@ -560,8 +561,7 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
560 struct snd_soc_dai *dai) 561 struct snd_soc_dai *dai)
561{ 562{
562 struct snd_soc_pcm_runtime *rtd = substream->private_data; 563 struct snd_soc_pcm_runtime *rtd = substream->private_data;
563 struct snd_soc_device *socdev = rtd->socdev; 564 struct snd_soc_codec *codec = rtd->codec;
564 struct snd_soc_codec *codec = socdev->card->codec;
565 struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec); 565 struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
566 u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3; 566 u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
567 u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0; 567 u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
@@ -649,8 +649,8 @@ static struct snd_soc_dai_ops wm8750_dai_ops = {
649 .set_sysclk = wm8750_set_dai_sysclk, 649 .set_sysclk = wm8750_set_dai_sysclk,
650}; 650};
651 651
652struct snd_soc_dai wm8750_dai = { 652static struct snd_soc_dai_driver wm8750_dai = {
653 .name = "WM8750", 653 .name = "wm8750-hifi",
654 .playback = { 654 .playback = {
655 .stream_name = "Playback", 655 .stream_name = "Playback",
656 .channels_min = 1, 656 .channels_min = 1,
@@ -665,21 +665,15 @@ struct snd_soc_dai wm8750_dai = {
665 .formats = WM8750_FORMATS,}, 665 .formats = WM8750_FORMATS,},
666 .ops = &wm8750_dai_ops, 666 .ops = &wm8750_dai_ops,
667}; 667};
668EXPORT_SYMBOL_GPL(wm8750_dai);
669 668
670static int wm8750_suspend(struct platform_device *pdev, pm_message_t state) 669static int wm8750_suspend(struct snd_soc_codec *codec, pm_message_t state)
671{ 670{
672 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
673 struct snd_soc_codec *codec = socdev->card->codec;
674
675 wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); 671 wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
676 return 0; 672 return 0;
677} 673}
678 674
679static int wm8750_resume(struct platform_device *pdev) 675static int wm8750_resume(struct snd_soc_codec *codec)
680{ 676{
681 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
682 struct snd_soc_codec *codec = socdev->card->codec;
683 int i; 677 int i;
684 u8 data[2]; 678 u8 data[2];
685 u16 *cache = codec->reg_cache; 679 u16 *cache = codec->reg_cache;
@@ -698,100 +692,22 @@ static int wm8750_resume(struct platform_device *pdev)
698 return 0; 692 return 0;
699} 693}
700 694
701static struct snd_soc_codec *wm8750_codec; 695static int wm8750_probe(struct snd_soc_codec *codec)
702
703static int wm8750_probe(struct platform_device *pdev)
704{ 696{
705 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 697 struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
706 struct snd_soc_codec *codec; 698 int reg, ret;
707 int ret = 0;
708
709 if (!wm8750_codec) {
710 dev_err(&pdev->dev, "WM8750 codec not yet registered\n");
711 return -EINVAL;
712 }
713
714 socdev->card->codec = wm8750_codec;
715 codec = wm8750_codec;
716
717 /* register pcms */
718 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
719 if (ret < 0) {
720 printk(KERN_ERR "wm8750: failed to create pcms\n");
721 goto err;
722 }
723
724 snd_soc_add_controls(codec, wm8750_snd_controls,
725 ARRAY_SIZE(wm8750_snd_controls));
726 wm8750_add_widgets(codec);
727
728 return 0;
729
730err:
731 return ret;
732}
733
734/* power down chip */
735static int wm8750_remove(struct platform_device *pdev)
736{
737 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
738
739 snd_soc_free_pcms(socdev);
740 snd_soc_dapm_free(socdev);
741
742 return 0;
743}
744
745struct snd_soc_codec_device soc_codec_dev_wm8750 = {
746 .probe = wm8750_probe,
747 .remove = wm8750_remove,
748 .suspend = wm8750_suspend,
749 .resume = wm8750_resume,
750};
751EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
752
753/*
754 * initialise the WM8750 driver
755 * register the mixer and dsp interfaces with the kernel
756 */
757static int wm8750_register(struct wm8750_priv *wm8750,
758 enum snd_soc_control_type control)
759{
760 struct snd_soc_codec *codec = &wm8750->codec;
761 int reg, ret = 0;
762
763 if (wm8750_codec) {
764 dev_err(codec->dev, "Multiple WM8750 devices not supported\n");
765 ret = -EINVAL;
766 goto err;
767 }
768
769 mutex_init(&codec->mutex);
770 INIT_LIST_HEAD(&codec->dapm_widgets);
771 INIT_LIST_HEAD(&codec->dapm_paths);
772
773 codec->name = "WM8750";
774 codec->owner = THIS_MODULE;
775 codec->bias_level = SND_SOC_BIAS_STANDBY;
776 codec->set_bias_level = wm8750_set_bias_level;
777 codec->dai = &wm8750_dai;
778 codec->num_dai = 1;
779 codec->reg_cache_size = ARRAY_SIZE(wm8750->reg_cache) + 1;
780 codec->reg_cache = &wm8750->reg_cache;
781 snd_soc_codec_set_drvdata(codec, wm8750);
782
783 memcpy(codec->reg_cache, wm8750_reg, sizeof(wm8750->reg_cache));
784 699
785 ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); 700 codec->control_data = wm8750->control_data;
701 ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8750->control_type);
786 if (ret < 0) { 702 if (ret < 0) {
787 printk(KERN_ERR "wm8750: failed to set cache I/O: %d\n", ret); 703 printk(KERN_ERR "wm8750: failed to set cache I/O: %d\n", ret);
788 goto err; 704 return ret;
789 } 705 }
790 706
791 ret = wm8750_reset(codec); 707 ret = wm8750_reset(codec);
792 if (ret < 0) { 708 if (ret < 0) {
793 printk(KERN_ERR "wm8750: failed to reset: %d\n", ret); 709 printk(KERN_ERR "wm8750: failed to reset: %d\n", ret);
794 goto err; 710 return ret;
795 } 711 }
796 712
797 /* charge output caps */ 713 /* charge output caps */
@@ -815,150 +731,133 @@ static int wm8750_register(struct wm8750_priv *wm8750,
815 reg = snd_soc_read(codec, WM8750_RINVOL); 731 reg = snd_soc_read(codec, WM8750_RINVOL);
816 snd_soc_write(codec, WM8750_RINVOL, reg | 0x0100); 732 snd_soc_write(codec, WM8750_RINVOL, reg | 0x0100);
817 733
818 wm8750_codec = codec; 734 snd_soc_add_controls(codec, wm8750_snd_controls,
819 735 ARRAY_SIZE(wm8750_snd_controls));
820 ret = snd_soc_register_codec(codec); 736 wm8750_add_widgets(codec);
821 if (ret != 0) {
822 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
823 goto err;
824 }
825
826 ret = snd_soc_register_dais(&wm8750_dai, 1);
827 if (ret != 0) {
828 dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
829 goto err_codec;
830 }
831
832 return 0;
833
834err_codec:
835 snd_soc_unregister_codec(codec);
836err:
837 kfree(wm8750);
838 return ret; 737 return ret;
839} 738}
840 739
841static void wm8750_unregister(struct wm8750_priv *wm8750) 740static int wm8750_remove(struct snd_soc_codec *codec)
842{ 741{
843 wm8750_set_bias_level(&wm8750->codec, SND_SOC_BIAS_OFF); 742 wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
844 snd_soc_unregister_dais(&wm8750_dai, 1); 743 return 0;
845 snd_soc_unregister_codec(&wm8750->codec);
846 kfree(wm8750);
847 wm8750_codec = NULL;
848} 744}
849 745
850#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 746static struct snd_soc_codec_driver soc_codec_dev_wm8750 = {
851 747 .probe = wm8750_probe,
852/* 748 .remove = wm8750_remove,
853 * WM8750 2 wire address is determined by GPIO5 749 .suspend = wm8750_suspend,
854 * state during powerup. 750 .resume = wm8750_resume,
855 * low = 0x1a 751 .set_bias_level = wm8750_set_bias_level,
856 * high = 0x1b 752 .reg_cache_size = sizeof(wm8750_reg),
857 */ 753 .reg_word_size = sizeof(u16),
754 .reg_cache_default = wm8750_reg,
755};
858 756
859static int wm8750_i2c_probe(struct i2c_client *i2c, 757#if defined(CONFIG_SPI_MASTER)
860 const struct i2c_device_id *id) 758static int __devinit wm8750_spi_probe(struct spi_device *spi)
861{ 759{
862 struct snd_soc_codec *codec;
863 struct wm8750_priv *wm8750; 760 struct wm8750_priv *wm8750;
761 int ret;
864 762
865 wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL); 763 wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);
866 if (wm8750 == NULL) 764 if (wm8750 == NULL)
867 return -ENOMEM; 765 return -ENOMEM;
868 766
869 codec = &wm8750->codec; 767 wm8750->control_data = spi;
870 codec->control_data = i2c; 768 wm8750->control_type = SND_SOC_SPI;
871 i2c_set_clientdata(i2c, wm8750); 769 spi_set_drvdata(spi, wm8750);
872
873 codec->dev = &i2c->dev;
874 770
875 return wm8750_register(wm8750, SND_SOC_I2C); 771 ret = snd_soc_register_codec(&spi->dev,
772 &soc_codec_dev_wm8750, &wm8750_dai, 1);
773 if (ret < 0)
774 kfree(wm8750);
775 return ret;
876} 776}
877 777
878static int wm8750_i2c_remove(struct i2c_client *client) 778static int __devexit wm8750_spi_remove(struct spi_device *spi)
879{ 779{
880 struct wm8750_priv *wm8750 = i2c_get_clientdata(client); 780 snd_soc_unregister_codec(&spi->dev);
881 wm8750_unregister(wm8750); 781 kfree(spi_get_drvdata(spi));
882 return 0; 782 return 0;
883} 783}
884 784
885static const struct i2c_device_id wm8750_i2c_id[] = { 785static struct spi_driver wm8750_spi_driver = {
886 { "wm8750", 0 },
887 { "wm8987", 0 }, /* WM8987 is register compatible with WM8750 */
888 { }
889};
890MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
891
892static struct i2c_driver wm8750_i2c_driver = {
893 .driver = { 786 .driver = {
894 .name = "WM8750 I2C Codec", 787 .name = "wm8750-codec",
895 .owner = THIS_MODULE, 788 .bus = &spi_bus_type,
789 .owner = THIS_MODULE,
896 }, 790 },
897 .probe = wm8750_i2c_probe, 791 .probe = wm8750_spi_probe,
898 .remove = wm8750_i2c_remove, 792 .remove = __devexit_p(wm8750_spi_remove),
899 .id_table = wm8750_i2c_id,
900}; 793};
901#endif 794#endif /* CONFIG_SPI_MASTER */
902 795
903#if defined(CONFIG_SPI_MASTER) 796#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
904static int __devinit wm8750_spi_probe(struct spi_device *spi) 797static __devinit int wm8750_i2c_probe(struct i2c_client *i2c,
798 const struct i2c_device_id *id)
905{ 799{
906 struct snd_soc_codec *codec;
907 struct wm8750_priv *wm8750; 800 struct wm8750_priv *wm8750;
801 int ret;
908 802
909 wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL); 803 wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);
910 if (wm8750 == NULL) 804 if (wm8750 == NULL)
911 return -ENOMEM; 805 return -ENOMEM;
912 806
913 codec = &wm8750->codec; 807 i2c_set_clientdata(i2c, wm8750);
914 codec->control_data = spi; 808 wm8750->control_data = i2c;
915 codec->dev = &spi->dev; 809 wm8750->control_type = SND_SOC_I2C;
916
917 dev_set_drvdata(&spi->dev, wm8750);
918 810
919 return wm8750_register(wm8750, SND_SOC_SPI); 811 ret = snd_soc_register_codec(&i2c->dev,
812 &soc_codec_dev_wm8750, &wm8750_dai, 1);
813 if (ret < 0)
814 kfree(wm8750);
815 return ret;
920} 816}
921 817
922static int __devexit wm8750_spi_remove(struct spi_device *spi) 818static __devexit int wm8750_i2c_remove(struct i2c_client *client)
923{ 819{
924 struct wm8750_priv *wm8750 = dev_get_drvdata(&spi->dev); 820 snd_soc_unregister_codec(&client->dev);
925 wm8750_unregister(wm8750); 821 kfree(i2c_get_clientdata(client));
926 return 0; 822 return 0;
927} 823}
928 824
929static const struct spi_device_id wm8750_spi_id[] = { 825static const struct i2c_device_id wm8750_i2c_id[] = {
930 { "wm8750", 0 }, 826 { "wm8750", 0 },
931 { "wm8987", 0 }, 827 { "wm8987", 0 },
932 { } 828 { }
933}; 829};
934MODULE_DEVICE_TABLE(spi, wm8750_spi_id); 830MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
935 831
936static struct spi_driver wm8750_spi_driver = { 832static struct i2c_driver wm8750_i2c_driver = {
937 .driver = { 833 .driver = {
938 .name = "WM8750 SPI Codec", 834 .name = "wm8750-codec",
939 .bus = &spi_bus_type, 835 .owner = THIS_MODULE,
940 .owner = THIS_MODULE,
941 }, 836 },
942 .probe = wm8750_spi_probe, 837 .probe = wm8750_i2c_probe,
943 .remove = __devexit_p(wm8750_spi_remove), 838 .remove = __devexit_p(wm8750_i2c_remove),
944 .id_table = wm8750_spi_id, 839 .id_table = wm8750_i2c_id,
945}; 840};
946#endif 841#endif
947 842
948static int __init wm8750_modinit(void) 843static int __init wm8750_modinit(void)
949{ 844{
950 int ret; 845 int ret = 0;
951#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 846#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
952 ret = i2c_add_driver(&wm8750_i2c_driver); 847 ret = i2c_add_driver(&wm8750_i2c_driver);
953 if (ret != 0) 848 if (ret != 0) {
954 pr_err("Failed to register WM8750 I2C driver: %d\n", ret); 849 printk(KERN_ERR "Failed to register wm8750 I2C driver: %d\n",
850 ret);
851 }
955#endif 852#endif
956#if defined(CONFIG_SPI_MASTER) 853#if defined(CONFIG_SPI_MASTER)
957 ret = spi_register_driver(&wm8750_spi_driver); 854 ret = spi_register_driver(&wm8750_spi_driver);
958 if (ret != 0) 855 if (ret != 0) {
959 pr_err("Failed to register WM8750 SPI driver: %d\n", ret); 856 printk(KERN_ERR "Failed to register wm8750 SPI driver: %d\n",
857 ret);
858 }
960#endif 859#endif
961 return 0; 860 return ret;
962} 861}
963module_init(wm8750_modinit); 862module_init(wm8750_modinit);
964 863