diff options
Diffstat (limited to 'sound/soc/codecs/wm8960.c')
-rw-r--r-- | sound/soc/codecs/wm8960.c | 209 |
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 | ||
32 | struct 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 | ||
76 | struct wm8960_priv { | 74 | struct 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 | ||
852 | static 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 | ||
866 | struct snd_soc_dai wm8960_dai = { | 874 | static 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 | }; |
883 | EXPORT_SYMBOL_GPL(wm8960_dai); | ||
884 | 891 | ||
885 | static int wm8960_suspend(struct platform_device *pdev, pm_message_t state) | 892 | static 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 | ||
894 | static int wm8960_resume(struct platform_device *pdev) | 900 | static 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 | ||
914 | static struct snd_soc_codec *wm8960_codec; | 918 | static int wm8960_probe(struct snd_soc_codec *codec) |
915 | |||
916 | static 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 | |||
943 | pcm_err: | ||
944 | return ret; | ||
945 | } | ||
946 | |||
947 | /* power down chip */ | ||
948 | static 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 | |||
958 | struct 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 | }; | ||
964 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960); | ||
965 | |||
966 | static 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 | |||
1063 | err_codec: | ||
1064 | snd_soc_unregister_codec(codec); | ||
1065 | err: | ||
1066 | kfree(wm8960); | ||
1067 | return ret; | ||
1068 | } | 981 | } |
1069 | 982 | ||
1070 | static void wm8960_unregister(struct wm8960_priv *wm8960) | 983 | /* power down chip */ |
984 | static 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 | ||
992 | static 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) | ||
1079 | static __devinit int wm8960_i2c_probe(struct i2c_client *i2c, | 1004 | static __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 | ||
1099 | static __devexit int wm8960_i2c_remove(struct i2c_client *client) | 1024 | static __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 | ||
1112 | static struct i2c_driver wm8960_i2c_driver = { | 1037 | static 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 | ||
1122 | static int __init wm8960_modinit(void) | 1048 | static 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 | } |
1134 | module_init(wm8960_modinit); | 1060 | module_init(wm8960_modinit); |
1135 | 1061 | ||
1136 | static void __exit wm8960_exit(void) | 1062 | static 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 | } |
1140 | module_exit(wm8960_exit); | 1068 | module_exit(wm8960_exit); |
1141 | 1069 | ||
1142 | |||
1143 | MODULE_DESCRIPTION("ASoC WM8960 driver"); | 1070 | MODULE_DESCRIPTION("ASoC WM8960 driver"); |
1144 | MODULE_AUTHOR("Liam Girdwood"); | 1071 | MODULE_AUTHOR("Liam Girdwood"); |
1145 | MODULE_LICENSE("GPL"); | 1072 | MODULE_LICENSE("GPL"); |