aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8960.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/wm8960.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/wm8960.c')
-rw-r--r--sound/soc/codecs/wm8960.c209
1 files changed, 68 insertions, 141 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 3c6ee61f6c95..8d5efb333c33 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -29,8 +29,6 @@
29 29
30#define AUDIO_NAME "wm8960" 30#define AUDIO_NAME "wm8960"
31 31
32struct snd_soc_codec_device soc_codec_dev_wm8960;
33
34/* R25 - Power 1 */ 32/* R25 - Power 1 */
35#define WM8960_VMID_MASK 0x180 33#define WM8960_VMID_MASK 0x180
36#define WM8960_VREF 0x40 34#define WM8960_VREF 0x40
@@ -75,7 +73,10 @@ static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
75 73
76struct wm8960_priv { 74struct wm8960_priv {
77 u16 reg_cache[WM8960_CACHEREGNUM]; 75 u16 reg_cache[WM8960_CACHEREGNUM];
78 struct snd_soc_codec codec; 76 enum snd_soc_control_type control_type;
77 void *control_data;
78 int (*set_bias_level)(struct snd_soc_codec *,
79 enum snd_soc_bias_level level);
79 struct snd_soc_dapm_widget *lout1; 80 struct snd_soc_dapm_widget *lout1;
80 struct snd_soc_dapm_widget *rout1; 81 struct snd_soc_dapm_widget *rout1;
81 struct snd_soc_dapm_widget *out3; 82 struct snd_soc_dapm_widget *out3;
@@ -507,8 +508,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
507 struct snd_soc_dai *dai) 508 struct snd_soc_dai *dai)
508{ 509{
509 struct snd_soc_pcm_runtime *rtd = substream->private_data; 510 struct snd_soc_pcm_runtime *rtd = substream->private_data;
510 struct snd_soc_device *socdev = rtd->socdev; 511 struct snd_soc_codec *codec = rtd->codec;
511 struct snd_soc_codec *codec = socdev->card->codec;
512 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 512 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
513 u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; 513 u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
514 int i; 514 int i;
@@ -849,6 +849,14 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
849 return 0; 849 return 0;
850} 850}
851 851
852static int wm8960_set_bias_level(struct snd_soc_codec *codec,
853 enum snd_soc_bias_level level)
854{
855 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
856
857 return wm8960->set_bias_level(codec, level);
858}
859
852#define WM8960_RATES SNDRV_PCM_RATE_8000_48000 860#define WM8960_RATES SNDRV_PCM_RATE_8000_48000
853 861
854#define WM8960_FORMATS \ 862#define WM8960_FORMATS \
@@ -863,8 +871,8 @@ static struct snd_soc_dai_ops wm8960_dai_ops = {
863 .set_pll = wm8960_set_dai_pll, 871 .set_pll = wm8960_set_dai_pll,
864}; 872};
865 873
866struct snd_soc_dai wm8960_dai = { 874static struct snd_soc_dai_driver wm8960_dai = {
867 .name = "WM8960", 875 .name = "wm8960-hifi",
868 .playback = { 876 .playback = {
869 .stream_name = "Playback", 877 .stream_name = "Playback",
870 .channels_min = 1, 878 .channels_min = 1,
@@ -880,21 +888,18 @@ struct snd_soc_dai wm8960_dai = {
880 .ops = &wm8960_dai_ops, 888 .ops = &wm8960_dai_ops,
881 .symmetric_rates = 1, 889 .symmetric_rates = 1,
882}; 890};
883EXPORT_SYMBOL_GPL(wm8960_dai);
884 891
885static int wm8960_suspend(struct platform_device *pdev, pm_message_t state) 892static int wm8960_suspend(struct snd_soc_codec *codec, pm_message_t state)
886{ 893{
887 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 894 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
888 struct snd_soc_codec *codec = socdev->card->codec;
889 895
890 codec->set_bias_level(codec, SND_SOC_BIAS_OFF); 896 wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
891 return 0; 897 return 0;
892} 898}
893 899
894static int wm8960_resume(struct platform_device *pdev) 900static int wm8960_resume(struct snd_soc_codec *codec)
895{ 901{
896 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 902 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
897 struct snd_soc_codec *codec = socdev->card->codec;
898 int i; 903 int i;
899 u8 data[2]; 904 u8 data[2];
900 u16 *cache = codec->reg_cache; 905 u16 *cache = codec->reg_cache;
@@ -906,78 +911,19 @@ static int wm8960_resume(struct platform_device *pdev)
906 codec->hw_write(codec->control_data, data, 2); 911 codec->hw_write(codec->control_data, data, 2);
907 } 912 }
908 913
909 codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY); 914 wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
910
911 return 0; 915 return 0;
912} 916}
913 917
914static struct snd_soc_codec *wm8960_codec; 918static int wm8960_probe(struct snd_soc_codec *codec)
915
916static int wm8960_probe(struct platform_device *pdev)
917{
918 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
919 struct snd_soc_codec *codec;
920 int ret = 0;
921
922 if (wm8960_codec == NULL) {
923 dev_err(&pdev->dev, "Codec device not registered\n");
924 return -ENODEV;
925 }
926
927 socdev->card->codec = wm8960_codec;
928 codec = wm8960_codec;
929
930 /* register pcms */
931 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
932 if (ret < 0) {
933 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
934 goto pcm_err;
935 }
936
937 snd_soc_add_controls(codec, wm8960_snd_controls,
938 ARRAY_SIZE(wm8960_snd_controls));
939 wm8960_add_widgets(codec);
940
941 return ret;
942
943pcm_err:
944 return ret;
945}
946
947/* power down chip */
948static int wm8960_remove(struct platform_device *pdev)
949{
950 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
951
952 snd_soc_free_pcms(socdev);
953 snd_soc_dapm_free(socdev);
954
955 return 0;
956}
957
958struct snd_soc_codec_device soc_codec_dev_wm8960 = {
959 .probe = wm8960_probe,
960 .remove = wm8960_remove,
961 .suspend = wm8960_suspend,
962 .resume = wm8960_resume,
963};
964EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
965
966static int wm8960_register(struct wm8960_priv *wm8960,
967 enum snd_soc_control_type control)
968{ 919{
969 struct wm8960_data *pdata = wm8960->codec.dev->platform_data; 920 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
970 struct snd_soc_codec *codec = &wm8960->codec; 921 struct wm8960_data *pdata = dev_get_platdata(codec->dev);
971 int ret; 922 int ret;
972 u16 reg; 923 u16 reg;
973 924
974 if (wm8960_codec) { 925 wm8960->set_bias_level = wm8960_set_bias_level_out3;
975 dev_err(codec->dev, "Another WM8960 is registered\n"); 926 codec->control_data = wm8960->control_data;
976 ret = -EINVAL;
977 goto err;
978 }
979
980 codec->set_bias_level = wm8960_set_bias_level_out3;
981 927
982 if (!pdata) { 928 if (!pdata) {
983 dev_warn(codec->dev, "No platform data supplied\n"); 929 dev_warn(codec->dev, "No platform data supplied\n");
@@ -988,39 +934,22 @@ static int wm8960_register(struct wm8960_priv *wm8960,
988 } 934 }
989 935
990 if (pdata->capless) 936 if (pdata->capless)
991 codec->set_bias_level = wm8960_set_bias_level_capless; 937 wm8960->set_bias_level = wm8960_set_bias_level_capless;
992 } 938 }
993 939
994 mutex_init(&codec->mutex); 940 ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8960->control_type);
995 INIT_LIST_HEAD(&codec->dapm_widgets);
996 INIT_LIST_HEAD(&codec->dapm_paths);
997
998 snd_soc_codec_set_drvdata(codec, wm8960);
999 codec->name = "WM8960";
1000 codec->owner = THIS_MODULE;
1001 codec->bias_level = SND_SOC_BIAS_OFF;
1002 codec->dai = &wm8960_dai;
1003 codec->num_dai = 1;
1004 codec->reg_cache_size = WM8960_CACHEREGNUM;
1005 codec->reg_cache = &wm8960->reg_cache;
1006
1007 memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
1008
1009 ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
1010 if (ret < 0) { 941 if (ret < 0) {
1011 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 942 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
1012 goto err; 943 return ret;
1013 } 944 }
1014 945
1015 ret = wm8960_reset(codec); 946 ret = wm8960_reset(codec);
1016 if (ret < 0) { 947 if (ret < 0) {
1017 dev_err(codec->dev, "Failed to issue reset\n"); 948 dev_err(codec->dev, "Failed to issue reset\n");
1018 goto err; 949 return ret;
1019 } 950 }
1020 951
1021 wm8960_dai.dev = codec->dev; 952 wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1022
1023 codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1024 953
1025 /* Latch the update bits */ 954 /* Latch the update bits */
1026 reg = snd_soc_read(codec, WM8960_LINVOL); 955 reg = snd_soc_read(codec, WM8960_LINVOL);
@@ -1044,62 +973,58 @@ static int wm8960_register(struct wm8960_priv *wm8960,
1044 reg = snd_soc_read(codec, WM8960_ROUT2); 973 reg = snd_soc_read(codec, WM8960_ROUT2);
1045 snd_soc_write(codec, WM8960_ROUT2, reg | 0x100); 974 snd_soc_write(codec, WM8960_ROUT2, reg | 0x100);
1046 975
1047 wm8960_codec = codec; 976 snd_soc_add_controls(codec, wm8960_snd_controls,
1048 977 ARRAY_SIZE(wm8960_snd_controls));
1049 ret = snd_soc_register_codec(codec); 978 wm8960_add_widgets(codec);
1050 if (ret != 0) {
1051 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
1052 goto err;
1053 }
1054
1055 ret = snd_soc_register_dai(&wm8960_dai);
1056 if (ret != 0) {
1057 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
1058 goto err_codec;
1059 }
1060 979
1061 return 0; 980 return 0;
1062
1063err_codec:
1064 snd_soc_unregister_codec(codec);
1065err:
1066 kfree(wm8960);
1067 return ret;
1068} 981}
1069 982
1070static void wm8960_unregister(struct wm8960_priv *wm8960) 983/* power down chip */
984static int wm8960_remove(struct snd_soc_codec *codec)
1071{ 985{
1072 wm8960->codec.set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF); 986 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
1073 snd_soc_unregister_dai(&wm8960_dai); 987
1074 snd_soc_unregister_codec(&wm8960->codec); 988 wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
1075 kfree(wm8960); 989 return 0;
1076 wm8960_codec = NULL;
1077} 990}
1078 991
992static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
993 .probe = wm8960_probe,
994 .remove = wm8960_remove,
995 .suspend = wm8960_suspend,
996 .resume = wm8960_resume,
997 .set_bias_level = wm8960_set_bias_level,
998 .reg_cache_size = ARRAY_SIZE(wm8960_reg),
999 .reg_word_size = sizeof(u16),
1000 .reg_cache_default = wm8960_reg,
1001};
1002
1003#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1079static __devinit int wm8960_i2c_probe(struct i2c_client *i2c, 1004static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
1080 const struct i2c_device_id *id) 1005 const struct i2c_device_id *id)
1081{ 1006{
1082 struct wm8960_priv *wm8960; 1007 struct wm8960_priv *wm8960;
1083 struct snd_soc_codec *codec; 1008 int ret;
1084 1009
1085 wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL); 1010 wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);
1086 if (wm8960 == NULL) 1011 if (wm8960 == NULL)
1087 return -ENOMEM; 1012 return -ENOMEM;
1088 1013
1089 codec = &wm8960->codec;
1090
1091 i2c_set_clientdata(i2c, wm8960); 1014 i2c_set_clientdata(i2c, wm8960);
1092 codec->control_data = i2c; 1015 wm8960->control_data = i2c;
1093
1094 codec->dev = &i2c->dev;
1095 1016
1096 return wm8960_register(wm8960, SND_SOC_I2C); 1017 ret = snd_soc_register_codec(&i2c->dev,
1018 &soc_codec_dev_wm8960, &wm8960_dai, 1);
1019 if (ret < 0)
1020 kfree(wm8960);
1021 return ret;
1097} 1022}
1098 1023
1099static __devexit int wm8960_i2c_remove(struct i2c_client *client) 1024static __devexit int wm8960_i2c_remove(struct i2c_client *client)
1100{ 1025{
1101 struct wm8960_priv *wm8960 = i2c_get_clientdata(client); 1026 snd_soc_unregister_codec(&client->dev);
1102 wm8960_unregister(wm8960); 1027 kfree(i2c_get_clientdata(client));
1103 return 0; 1028 return 0;
1104} 1029}
1105 1030
@@ -1111,35 +1036,37 @@ MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
1111 1036
1112static struct i2c_driver wm8960_i2c_driver = { 1037static struct i2c_driver wm8960_i2c_driver = {
1113 .driver = { 1038 .driver = {
1114 .name = "wm8960", 1039 .name = "wm8960-codec",
1115 .owner = THIS_MODULE, 1040 .owner = THIS_MODULE,
1116 }, 1041 },
1117 .probe = wm8960_i2c_probe, 1042 .probe = wm8960_i2c_probe,
1118 .remove = __devexit_p(wm8960_i2c_remove), 1043 .remove = __devexit_p(wm8960_i2c_remove),
1119 .id_table = wm8960_i2c_id, 1044 .id_table = wm8960_i2c_id,
1120}; 1045};
1046#endif
1121 1047
1122static int __init wm8960_modinit(void) 1048static int __init wm8960_modinit(void)
1123{ 1049{
1124 int ret; 1050 int ret = 0;
1125 1051#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1126 ret = i2c_add_driver(&wm8960_i2c_driver); 1052 ret = i2c_add_driver(&wm8960_i2c_driver);
1127 if (ret != 0) { 1053 if (ret != 0) {
1128 printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n", 1054 printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
1129 ret); 1055 ret);
1130 } 1056 }
1131 1057#endif
1132 return ret; 1058 return ret;
1133} 1059}
1134module_init(wm8960_modinit); 1060module_init(wm8960_modinit);
1135 1061
1136static void __exit wm8960_exit(void) 1062static void __exit wm8960_exit(void)
1137{ 1063{
1064#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1138 i2c_del_driver(&wm8960_i2c_driver); 1065 i2c_del_driver(&wm8960_i2c_driver);
1066#endif
1139} 1067}
1140module_exit(wm8960_exit); 1068module_exit(wm8960_exit);
1141 1069
1142
1143MODULE_DESCRIPTION("ASoC WM8960 driver"); 1070MODULE_DESCRIPTION("ASoC WM8960 driver");
1144MODULE_AUTHOR("Liam Girdwood"); 1071MODULE_AUTHOR("Liam Girdwood");
1145MODULE_LICENSE("GPL"); 1072MODULE_LICENSE("GPL");