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