aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8988.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/wm8988.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/wm8988.c')
-rw-r--r--sound/soc/codecs/wm8988.c266
1 files changed, 90 insertions, 176 deletions
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 19ad590ca0b3..ecbffcea71db 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -52,7 +52,8 @@ static const u16 wm8988_reg[] = {
52/* codec private data */ 52/* codec private data */
53struct wm8988_priv { 53struct wm8988_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 struct snd_pcm_hw_constraint_list *sysclk_constraints; 57 struct snd_pcm_hw_constraint_list *sysclk_constraints;
57 u16 reg_cache[WM8988_NUM_REG]; 58 u16 reg_cache[WM8988_NUM_REG];
58}; 59};
@@ -608,8 +609,7 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
608 struct snd_soc_dai *dai) 609 struct snd_soc_dai *dai)
609{ 610{
610 struct snd_soc_pcm_runtime *rtd = substream->private_data; 611 struct snd_soc_pcm_runtime *rtd = substream->private_data;
611 struct snd_soc_device *socdev = rtd->socdev; 612 struct snd_soc_codec *codec = rtd->codec;
612 struct snd_soc_codec *codec = socdev->card->codec;
613 struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); 613 struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
614 u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3; 614 u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
615 u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180; 615 u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
@@ -711,8 +711,8 @@ static struct snd_soc_dai_ops wm8988_ops = {
711 .digital_mute = wm8988_mute, 711 .digital_mute = wm8988_mute,
712}; 712};
713 713
714struct snd_soc_dai wm8988_dai = { 714static struct snd_soc_dai_driver wm8988_dai = {
715 .name = "WM8988", 715 .name = "wm8988-hifi",
716 .playback = { 716 .playback = {
717 .stream_name = "Playback", 717 .stream_name = "Playback",
718 .channels_min = 1, 718 .channels_min = 1,
@@ -730,21 +730,15 @@ struct snd_soc_dai wm8988_dai = {
730 .ops = &wm8988_ops, 730 .ops = &wm8988_ops,
731 .symmetric_rates = 1, 731 .symmetric_rates = 1,
732}; 732};
733EXPORT_SYMBOL_GPL(wm8988_dai);
734 733
735static int wm8988_suspend(struct platform_device *pdev, pm_message_t state) 734static int wm8988_suspend(struct snd_soc_codec *codec, pm_message_t state)
736{ 735{
737 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
738 struct snd_soc_codec *codec = socdev->card->codec;
739
740 wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); 736 wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
741 return 0; 737 return 0;
742} 738}
743 739
744static int wm8988_resume(struct platform_device *pdev) 740static int wm8988_resume(struct snd_soc_codec *codec)
745{ 741{
746 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
747 struct snd_soc_codec *codec = socdev->card->codec;
748 int i; 742 int i;
749 u8 data[2]; 743 u8 data[2];
750 u16 *cache = codec->reg_cache; 744 u16 *cache = codec->reg_cache;
@@ -763,99 +757,23 @@ static int wm8988_resume(struct platform_device *pdev)
763 return 0; 757 return 0;
764} 758}
765 759
766static struct snd_soc_codec *wm8988_codec; 760static int wm8988_probe(struct snd_soc_codec *codec)
767
768static int wm8988_probe(struct platform_device *pdev)
769{ 761{
770 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 762 struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
771 struct snd_soc_codec *codec;
772 int ret = 0; 763 int ret = 0;
773
774 if (wm8988_codec == NULL) {
775 dev_err(&pdev->dev, "Codec device not registered\n");
776 return -ENODEV;
777 }
778
779 socdev->card->codec = wm8988_codec;
780 codec = wm8988_codec;
781
782 /* register pcms */
783 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
784 if (ret < 0) {
785 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
786 goto pcm_err;
787 }
788
789 snd_soc_add_controls(codec, wm8988_snd_controls,
790 ARRAY_SIZE(wm8988_snd_controls));
791 snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
792 ARRAY_SIZE(wm8988_dapm_widgets));
793 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
794
795 return ret;
796
797pcm_err:
798 return ret;
799}
800
801static int wm8988_remove(struct platform_device *pdev)
802{
803 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
804
805 snd_soc_free_pcms(socdev);
806 snd_soc_dapm_free(socdev);
807
808 return 0;
809}
810
811struct snd_soc_codec_device soc_codec_dev_wm8988 = {
812 .probe = wm8988_probe,
813 .remove = wm8988_remove,
814 .suspend = wm8988_suspend,
815 .resume = wm8988_resume,
816};
817EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988);
818
819static int wm8988_register(struct wm8988_priv *wm8988,
820 enum snd_soc_control_type control)
821{
822 struct snd_soc_codec *codec = &wm8988->codec;
823 int ret;
824 u16 reg; 764 u16 reg;
825 765
826 if (wm8988_codec) { 766 codec->control_data = wm8988->control_data;
827 dev_err(codec->dev, "Another WM8988 is registered\n"); 767 ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8988->control_type);
828 ret = -EINVAL;
829 goto err;
830 }
831
832 mutex_init(&codec->mutex);
833 INIT_LIST_HEAD(&codec->dapm_widgets);
834 INIT_LIST_HEAD(&codec->dapm_paths);
835
836 snd_soc_codec_set_drvdata(codec, wm8988);
837 codec->name = "WM8988";
838 codec->owner = THIS_MODULE;
839 codec->dai = &wm8988_dai;
840 codec->num_dai = 1;
841 codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache);
842 codec->reg_cache = &wm8988->reg_cache;
843 codec->bias_level = SND_SOC_BIAS_OFF;
844 codec->set_bias_level = wm8988_set_bias_level;
845
846 memcpy(codec->reg_cache, wm8988_reg,
847 sizeof(wm8988_reg));
848
849 ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
850 if (ret < 0) { 768 if (ret < 0) {
851 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 769 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
852 goto err; 770 return ret;
853 } 771 }
854 772
855 ret = wm8988_reset(codec); 773 ret = wm8988_reset(codec);
856 if (ret < 0) { 774 if (ret < 0) {
857 dev_err(codec->dev, "Failed to issue reset\n"); 775 dev_err(codec->dev, "Failed to issue reset\n");
858 goto err; 776 return ret;
859 } 777 }
860 778
861 /* set the update bits (we always update left then right) */ 779 /* set the update bits (we always update left then right) */
@@ -870,139 +788,135 @@ static int wm8988_register(struct wm8988_priv *wm8988,
870 reg = snd_soc_read(codec, WM8988_RINVOL); 788 reg = snd_soc_read(codec, WM8988_RINVOL);
871 snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100); 789 snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100);
872 790
873 wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY); 791 wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
874
875 wm8988_dai.dev = codec->dev;
876
877 wm8988_codec = codec;
878
879 ret = snd_soc_register_codec(codec);
880 if (ret != 0) {
881 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
882 goto err;
883 }
884 792
885 ret = snd_soc_register_dai(&wm8988_dai); 793 snd_soc_add_controls(codec, wm8988_snd_controls,
886 if (ret != 0) { 794 ARRAY_SIZE(wm8988_snd_controls));
887 dev_err(codec->dev, "Failed to register DAI: %d\n", ret); 795 snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
888 goto err_codec; 796 ARRAY_SIZE(wm8988_dapm_widgets));
889 } 797 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
890 798
891 return 0; 799 return 0;
892
893err_codec:
894 snd_soc_unregister_codec(codec);
895err:
896 kfree(wm8988);
897 return ret;
898} 800}
899 801
900static void wm8988_unregister(struct wm8988_priv *wm8988) 802static int wm8988_remove(struct snd_soc_codec *codec)
901{ 803{
902 wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_OFF); 804 wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
903 snd_soc_unregister_dai(&wm8988_dai); 805 return 0;
904 snd_soc_unregister_codec(&wm8988->codec);
905 kfree(wm8988);
906 wm8988_codec = NULL;
907} 806}
908 807
909#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 808static struct snd_soc_codec_driver soc_codec_dev_wm8988 = {
910static int wm8988_i2c_probe(struct i2c_client *i2c, 809 .probe = wm8988_probe,
911 const struct i2c_device_id *id) 810 .remove = wm8988_remove,
811 .suspend = wm8988_suspend,
812 .resume = wm8988_resume,
813 .set_bias_level = wm8988_set_bias_level,
814 .reg_cache_size = sizeof(wm8988_reg),
815 .reg_word_size = sizeof(u16),
816 .reg_cache_default = wm8988_reg,
817};
818
819#if defined(CONFIG_SPI_MASTER)
820static int __devinit wm8988_spi_probe(struct spi_device *spi)
912{ 821{
913 struct wm8988_priv *wm8988; 822 struct wm8988_priv *wm8988;
914 struct snd_soc_codec *codec; 823 int ret;
915 824
916 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); 825 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
917 if (wm8988 == NULL) 826 if (wm8988 == NULL)
918 return -ENOMEM; 827 return -ENOMEM;
919 828
920 codec = &wm8988->codec; 829 wm8988->control_data = spi;
921 830 wm8988->control_type = SND_SOC_SPI;
922 i2c_set_clientdata(i2c, wm8988); 831 spi_set_drvdata(spi, wm8988);
923 codec->control_data = i2c;
924
925 codec->dev = &i2c->dev;
926 832
927 return wm8988_register(wm8988, SND_SOC_I2C); 833 ret = snd_soc_register_codec(&spi->dev,
834 &soc_codec_dev_wm8988, &wm8988_dai, 1);
835 if (ret < 0)
836 kfree(wm8988);
837 return ret;
928} 838}
929 839
930static int wm8988_i2c_remove(struct i2c_client *client) 840static int __devexit wm8988_spi_remove(struct spi_device *spi)
931{ 841{
932 struct wm8988_priv *wm8988 = i2c_get_clientdata(client); 842 snd_soc_unregister_codec(&spi->dev);
933 wm8988_unregister(wm8988); 843 kfree(spi_get_drvdata(spi));
934 return 0; 844 return 0;
935} 845}
936 846
937static const struct i2c_device_id wm8988_i2c_id[] = { 847static struct spi_driver wm8988_spi_driver = {
938 { "wm8988", 0 },
939 { }
940};
941MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
942
943static struct i2c_driver wm8988_i2c_driver = {
944 .driver = { 848 .driver = {
945 .name = "WM8988", 849 .name = "wm8988-codec",
946 .owner = THIS_MODULE, 850 .bus = &spi_bus_type,
851 .owner = THIS_MODULE,
947 }, 852 },
948 .probe = wm8988_i2c_probe, 853 .probe = wm8988_spi_probe,
949 .remove = wm8988_i2c_remove, 854 .remove = __devexit_p(wm8988_spi_remove),
950 .id_table = wm8988_i2c_id,
951}; 855};
952#endif 856#endif /* CONFIG_SPI_MASTER */
953 857
954#if defined(CONFIG_SPI_MASTER) 858#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
955static int __devinit wm8988_spi_probe(struct spi_device *spi) 859static __devinit int wm8988_i2c_probe(struct i2c_client *i2c,
860 const struct i2c_device_id *id)
956{ 861{
957 struct wm8988_priv *wm8988; 862 struct wm8988_priv *wm8988;
958 struct snd_soc_codec *codec; 863 int ret;
959 864
960 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); 865 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
961 if (wm8988 == NULL) 866 if (wm8988 == NULL)
962 return -ENOMEM; 867 return -ENOMEM;
963 868
964 codec = &wm8988->codec; 869 i2c_set_clientdata(i2c, wm8988);
965 codec->control_data = spi; 870 wm8988->control_data = i2c;
966 codec->dev = &spi->dev; 871 wm8988->control_type = SND_SOC_I2C;
967
968 dev_set_drvdata(&spi->dev, wm8988);
969 872
970 return wm8988_register(wm8988, SND_SOC_SPI); 873 ret = snd_soc_register_codec(&i2c->dev,
874 &soc_codec_dev_wm8988, &wm8988_dai, 1);
875 if (ret < 0)
876 kfree(wm8988);
877 return ret;
971} 878}
972 879
973static int __devexit wm8988_spi_remove(struct spi_device *spi) 880static __devexit int wm8988_i2c_remove(struct i2c_client *client)
974{ 881{
975 struct wm8988_priv *wm8988 = dev_get_drvdata(&spi->dev); 882 snd_soc_unregister_codec(&client->dev);
976 883 kfree(i2c_get_clientdata(client));
977 wm8988_unregister(wm8988);
978
979 return 0; 884 return 0;
980} 885}
981 886
982static struct spi_driver wm8988_spi_driver = { 887static const struct i2c_device_id wm8988_i2c_id[] = {
888 { "wm8988", 0 },
889 { }
890};
891MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
892
893static struct i2c_driver wm8988_i2c_driver = {
983 .driver = { 894 .driver = {
984 .name = "wm8988", 895 .name = "wm8988-codec",
985 .bus = &spi_bus_type, 896 .owner = THIS_MODULE,
986 .owner = THIS_MODULE,
987 }, 897 },
988 .probe = wm8988_spi_probe, 898 .probe = wm8988_i2c_probe,
989 .remove = __devexit_p(wm8988_spi_remove), 899 .remove = __devexit_p(wm8988_i2c_remove),
900 .id_table = wm8988_i2c_id,
990}; 901};
991#endif 902#endif
992 903
993static int __init wm8988_modinit(void) 904static int __init wm8988_modinit(void)
994{ 905{
995 int ret; 906 int ret = 0;
996
997#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 907#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
998 ret = i2c_add_driver(&wm8988_i2c_driver); 908 ret = i2c_add_driver(&wm8988_i2c_driver);
999 if (ret != 0) 909 if (ret != 0) {
1000 pr_err("WM8988: Unable to register I2C driver: %d\n", ret); 910 printk(KERN_ERR "Failed to register WM8988 I2C driver: %d\n",
911 ret);
912 }
1001#endif 913#endif
1002#if defined(CONFIG_SPI_MASTER) 914#if defined(CONFIG_SPI_MASTER)
1003 ret = spi_register_driver(&wm8988_spi_driver); 915 ret = spi_register_driver(&wm8988_spi_driver);
1004 if (ret != 0) 916 if (ret != 0) {
1005 pr_err("WM8988: Unable to register SPI driver: %d\n", ret); 917 printk(KERN_ERR "Failed to register WM8988 SPI driver: %d\n",
918 ret);
919 }
1006#endif 920#endif
1007 return ret; 921 return ret;
1008} 922}