aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8961.c
diff options
context:
space:
mode:
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");