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