aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8961.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/wm8961.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/wm8961.c')
-rw-r--r--sound/soc/codecs/wm8961.c241
1 files changed, 77 insertions, 164 deletions
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 2549d3a297ab..5ebe2c04e5cf 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -288,7 +288,8 @@ static u16 wm8961_reg_defaults[] = {
288}; 288};
289 289
290struct wm8961_priv { 290struct wm8961_priv {
291 struct snd_soc_codec codec; 291 enum snd_soc_control_type control_type;
292 void *control_data;
292 int sysclk; 293 int sysclk;
293 u16 reg_cache[WM8961_MAX_REGISTER]; 294 u16 reg_cache[WM8961_MAX_REGISTER];
294}; 295};
@@ -940,8 +941,8 @@ static struct snd_soc_dai_ops wm8961_dai_ops = {
940 .set_clkdiv = wm8961_set_clkdiv, 941 .set_clkdiv = wm8961_set_clkdiv,
941}; 942};
942 943
943struct snd_soc_dai wm8961_dai = { 944static struct snd_soc_dai_driver wm8961_dai = {
944 .name = "WM8961", 945 .name = "wm8961-hifi",
945 .playback = { 946 .playback = {
946 .stream_name = "HiFi Playback", 947 .stream_name = "HiFi Playback",
947 .channels_min = 1, 948 .channels_min = 1,
@@ -956,140 +957,24 @@ struct snd_soc_dai wm8961_dai = {
956 .formats = WM8961_FORMATS,}, 957 .formats = WM8961_FORMATS,},
957 .ops = &wm8961_dai_ops, 958 .ops = &wm8961_dai_ops,
958}; 959};
959EXPORT_SYMBOL_GPL(wm8961_dai);
960 960
961 961static int wm8961_probe(struct snd_soc_codec *codec)
962static struct snd_soc_codec *wm8961_codec;
963
964static int wm8961_probe(struct platform_device *pdev)
965{ 962{
966 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 963 struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec);
967 struct snd_soc_codec *codec;
968 int ret = 0; 964 int ret = 0;
969
970 if (wm8961_codec == NULL) {
971 dev_err(&pdev->dev, "Codec device not registered\n");
972 return -ENODEV;
973 }
974
975 socdev->card->codec = wm8961_codec;
976 codec = wm8961_codec;
977
978 /* register pcms */
979 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
980 if (ret < 0) {
981 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
982 goto pcm_err;
983 }
984
985 snd_soc_add_controls(codec, wm8961_snd_controls,
986 ARRAY_SIZE(wm8961_snd_controls));
987 snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
988 ARRAY_SIZE(wm8961_dapm_widgets));
989 snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
990
991 return ret;
992
993pcm_err:
994 return ret;
995}
996
997static int wm8961_remove(struct platform_device *pdev)
998{
999 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1000
1001 snd_soc_free_pcms(socdev);
1002 snd_soc_dapm_free(socdev);
1003
1004 return 0;
1005}
1006
1007#ifdef CONFIG_PM
1008static int wm8961_suspend(struct platform_device *pdev, pm_message_t state)
1009{
1010 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1011 struct snd_soc_codec *codec = socdev->card->codec;
1012
1013 wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
1014
1015 return 0;
1016}
1017
1018static int wm8961_resume(struct platform_device *pdev)
1019{
1020 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1021 struct snd_soc_codec *codec = socdev->card->codec;
1022 u16 *reg_cache = codec->reg_cache;
1023 int i;
1024
1025 for (i = 0; i < codec->reg_cache_size; i++) {
1026 if (reg_cache[i] == wm8961_reg_defaults[i])
1027 continue;
1028
1029 if (i == WM8961_SOFTWARE_RESET)
1030 continue;
1031
1032 snd_soc_write(codec, i, reg_cache[i]);
1033 }
1034
1035 wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1036
1037 return 0;
1038}
1039#else
1040#define wm8961_suspend NULL
1041#define wm8961_resume NULL
1042#endif
1043
1044struct snd_soc_codec_device soc_codec_dev_wm8961 = {
1045 .probe = wm8961_probe,
1046 .remove = wm8961_remove,
1047 .suspend = wm8961_suspend,
1048 .resume = wm8961_resume,
1049};
1050EXPORT_SYMBOL_GPL(soc_codec_dev_wm8961);
1051
1052static int wm8961_register(struct wm8961_priv *wm8961)
1053{
1054 struct snd_soc_codec *codec = &wm8961->codec;
1055 int ret;
1056 u16 reg; 965 u16 reg;
1057 966
1058 if (wm8961_codec) { 967 codec->control_data = wm8961->control_data;
1059 dev_err(codec->dev, "Another WM8961 is registered\n");
1060 ret = -EINVAL;
1061 goto err;
1062 }
1063
1064 mutex_init(&codec->mutex);
1065 INIT_LIST_HEAD(&codec->dapm_widgets);
1066 INIT_LIST_HEAD(&codec->dapm_paths);
1067
1068 snd_soc_codec_set_drvdata(codec, wm8961);
1069 codec->name = "WM8961";
1070 codec->owner = THIS_MODULE;
1071 codec->dai = &wm8961_dai;
1072 codec->num_dai = 1;
1073 codec->reg_cache_size = ARRAY_SIZE(wm8961->reg_cache);
1074 codec->reg_cache = &wm8961->reg_cache;
1075 codec->bias_level = SND_SOC_BIAS_OFF;
1076 codec->set_bias_level = wm8961_set_bias_level;
1077 codec->volatile_register = wm8961_volatile_register;
1078
1079 memcpy(codec->reg_cache, wm8961_reg_defaults,
1080 sizeof(wm8961_reg_defaults));
1081
1082 ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); 968 ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
1083 if (ret != 0) { 969 if (ret != 0) {
1084 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 970 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
1085 goto err; 971 return ret;
1086 } 972 }
1087 973
1088 reg = snd_soc_read(codec, WM8961_SOFTWARE_RESET); 974 reg = snd_soc_read(codec, WM8961_SOFTWARE_RESET);
1089 if (reg != 0x1801) { 975 if (reg != 0x1801) {
1090 dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg); 976 dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg);
1091 ret = -EINVAL; 977 return -EINVAL;
1092 goto err;
1093 } 978 }
1094 979
1095 /* This isn't volatile - readback doesn't correspond to write */ 980 /* This isn't volatile - readback doesn't correspond to write */
@@ -1102,7 +987,7 @@ static int wm8961_register(struct wm8961_priv *wm8961)
1102 ret = wm8961_reset(codec); 987 ret = wm8961_reset(codec);
1103 if (ret < 0) { 988 if (ret < 0) {
1104 dev_err(codec->dev, "Failed to issue reset\n"); 989 dev_err(codec->dev, "Failed to issue reset\n");
1105 goto err; 990 return ret;
1106 } 991 }
1107 992
1108 /* Enable class W */ 993 /* Enable class W */
@@ -1140,64 +1025,90 @@ static int wm8961_register(struct wm8961_priv *wm8961)
1140 1025
1141 wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 1026 wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1142 1027
1143 wm8961_dai.dev = codec->dev; 1028 snd_soc_add_controls(codec, wm8961_snd_controls,
1029 ARRAY_SIZE(wm8961_snd_controls));
1030 snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
1031 ARRAY_SIZE(wm8961_dapm_widgets));
1032 snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
1144 1033
1145 wm8961_codec = codec; 1034 return 0;
1035}
1146 1036
1147 ret = snd_soc_register_codec(codec); 1037static int wm8961_remove(struct snd_soc_codec *codec)
1148 if (ret != 0) { 1038{
1149 dev_err(codec->dev, "Failed to register codec: %d\n", ret); 1039 wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
1150 goto err; 1040 return 0;
1151 } 1041}
1152 1042
1153 ret = snd_soc_register_dai(&wm8961_dai); 1043#ifdef CONFIG_PM
1154 if (ret != 0) { 1044static int wm8961_suspend(struct snd_soc_codec *codec, pm_message_t state)
1155 dev_err(codec->dev, "Failed to register DAI: %d\n", ret); 1045{
1156 goto err_codec; 1046 wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
1157 }
1158 1047
1159 return 0; 1048 return 0;
1160
1161err_codec:
1162 snd_soc_unregister_codec(codec);
1163err:
1164 kfree(wm8961);
1165 return ret;
1166} 1049}
1167 1050
1168static void wm8961_unregister(struct wm8961_priv *wm8961) 1051static int wm8961_resume(struct snd_soc_codec *codec)
1169{ 1052{
1170 wm8961_set_bias_level(&wm8961->codec, SND_SOC_BIAS_OFF); 1053 u16 *reg_cache = codec->reg_cache;
1171 snd_soc_unregister_dai(&wm8961_dai); 1054 int i;
1172 snd_soc_unregister_codec(&wm8961->codec); 1055
1173 kfree(wm8961); 1056 for (i = 0; i < codec->driver->reg_cache_size; i++) {
1174 wm8961_codec = NULL; 1057 if (reg_cache[i] == wm8961_reg_defaults[i])
1058 continue;
1059
1060 if (i == WM8961_SOFTWARE_RESET)
1061 continue;
1062
1063 snd_soc_write(codec, i, reg_cache[i]);
1064 }
1065
1066 wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1067
1068 return 0;
1175} 1069}
1070#else
1071#define wm8961_suspend NULL
1072#define wm8961_resume NULL
1073#endif
1176 1074
1075static struct snd_soc_codec_driver soc_codec_dev_wm8961 = {
1076 .probe = wm8961_probe,
1077 .remove = wm8961_remove,
1078 .suspend = wm8961_suspend,
1079 .resume = wm8961_resume,
1080 .set_bias_level = wm8961_set_bias_level,
1081 .reg_cache_size = sizeof(wm8961_reg_defaults),
1082 .reg_word_size = sizeof(u16),
1083 .reg_cache_default = wm8961_reg_defaults,
1084 .volatile_register = wm8961_volatile_register,
1085};
1086
1087#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1177static __devinit int wm8961_i2c_probe(struct i2c_client *i2c, 1088static __devinit int wm8961_i2c_probe(struct i2c_client *i2c,
1178 const struct i2c_device_id *id) 1089 const struct i2c_device_id *id)
1179{ 1090{
1180 struct wm8961_priv *wm8961; 1091 struct wm8961_priv *wm8961;
1181 struct snd_soc_codec *codec; 1092 int ret;
1182 1093
1183 wm8961 = kzalloc(sizeof(struct wm8961_priv), GFP_KERNEL); 1094 wm8961 = kzalloc(sizeof(struct wm8961_priv), GFP_KERNEL);
1184 if (wm8961 == NULL) 1095 if (wm8961 == NULL)
1185 return -ENOMEM; 1096 return -ENOMEM;
1186 1097
1187 codec = &wm8961->codec;
1188
1189 i2c_set_clientdata(i2c, wm8961); 1098 i2c_set_clientdata(i2c, wm8961);
1190 codec->control_data = i2c; 1099 wm8961->control_data = i2c;
1191
1192 codec->dev = &i2c->dev;
1193 1100
1194 return wm8961_register(wm8961); 1101 ret = snd_soc_register_codec(&i2c->dev,
1102 &soc_codec_dev_wm8961, &wm8961_dai, 1);
1103 if (ret < 0)
1104 kfree(wm8961);
1105 return ret;
1195} 1106}
1196 1107
1197static __devexit int wm8961_i2c_remove(struct i2c_client *client) 1108static __devexit int wm8961_i2c_remove(struct i2c_client *client)
1198{ 1109{
1199 struct wm8961_priv *wm8961 = i2c_get_clientdata(client); 1110 snd_soc_unregister_codec(&client->dev);
1200 wm8961_unregister(wm8961); 1111 kfree(i2c_get_clientdata(client));
1201 return 0; 1112 return 0;
1202} 1113}
1203 1114
@@ -1209,35 +1120,37 @@ MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
1209 1120
1210static struct i2c_driver wm8961_i2c_driver = { 1121static struct i2c_driver wm8961_i2c_driver = {
1211 .driver = { 1122 .driver = {
1212 .name = "wm8961", 1123 .name = "wm8961-codec",
1213 .owner = THIS_MODULE, 1124 .owner = THIS_MODULE,
1214 }, 1125 },
1215 .probe = wm8961_i2c_probe, 1126 .probe = wm8961_i2c_probe,
1216 .remove = __devexit_p(wm8961_i2c_remove), 1127 .remove = __devexit_p(wm8961_i2c_remove),
1217 .id_table = wm8961_i2c_id, 1128 .id_table = wm8961_i2c_id,
1218}; 1129};
1130#endif
1219 1131
1220static int __init wm8961_modinit(void) 1132static int __init wm8961_modinit(void)
1221{ 1133{
1222 int ret; 1134 int ret = 0;
1223 1135#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1224 ret = i2c_add_driver(&wm8961_i2c_driver); 1136 ret = i2c_add_driver(&wm8961_i2c_driver);
1225 if (ret != 0) { 1137 if (ret != 0) {
1226 printk(KERN_ERR "Failed to register WM8961 I2C driver: %d\n", 1138 printk(KERN_ERR "Failed to register wm8961 I2C driver: %d\n",
1227 ret); 1139 ret);
1228 } 1140 }
1229 1141#endif
1230 return ret; 1142 return ret;
1231} 1143}
1232module_init(wm8961_modinit); 1144module_init(wm8961_modinit);
1233 1145
1234static void __exit wm8961_exit(void) 1146static void __exit wm8961_exit(void)
1235{ 1147{
1148#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1236 i2c_del_driver(&wm8961_i2c_driver); 1149 i2c_del_driver(&wm8961_i2c_driver);
1150#endif
1237} 1151}
1238module_exit(wm8961_exit); 1152module_exit(wm8961_exit);
1239 1153
1240
1241MODULE_DESCRIPTION("ASoC WM8961 driver"); 1154MODULE_DESCRIPTION("ASoC WM8961 driver");
1242MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 1155MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
1243MODULE_LICENSE("GPL"); 1156MODULE_LICENSE("GPL");