diff options
Diffstat (limited to 'sound/soc/codecs/wm8988.c')
-rw-r--r-- | sound/soc/codecs/wm8988.c | 266 |
1 files changed, 90 insertions, 176 deletions
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 19ad590ca0b3..ecbffcea71db 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c | |||
@@ -52,7 +52,8 @@ static const u16 wm8988_reg[] = { | |||
52 | /* codec private data */ | 52 | /* codec private data */ |
53 | struct wm8988_priv { | 53 | struct wm8988_priv { |
54 | unsigned int sysclk; | 54 | unsigned int sysclk; |
55 | struct snd_soc_codec codec; | 55 | enum snd_soc_control_type control_type; |
56 | void *control_data; | ||
56 | struct snd_pcm_hw_constraint_list *sysclk_constraints; | 57 | struct snd_pcm_hw_constraint_list *sysclk_constraints; |
57 | u16 reg_cache[WM8988_NUM_REG]; | 58 | u16 reg_cache[WM8988_NUM_REG]; |
58 | }; | 59 | }; |
@@ -608,8 +609,7 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream, | |||
608 | struct snd_soc_dai *dai) | 609 | struct snd_soc_dai *dai) |
609 | { | 610 | { |
610 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 611 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
611 | struct snd_soc_device *socdev = rtd->socdev; | 612 | struct snd_soc_codec *codec = rtd->codec; |
612 | struct snd_soc_codec *codec = socdev->card->codec; | ||
613 | struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); | 613 | struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); |
614 | u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3; | 614 | u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3; |
615 | u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180; | 615 | u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180; |
@@ -711,8 +711,8 @@ static struct snd_soc_dai_ops wm8988_ops = { | |||
711 | .digital_mute = wm8988_mute, | 711 | .digital_mute = wm8988_mute, |
712 | }; | 712 | }; |
713 | 713 | ||
714 | struct snd_soc_dai wm8988_dai = { | 714 | static struct snd_soc_dai_driver wm8988_dai = { |
715 | .name = "WM8988", | 715 | .name = "wm8988-hifi", |
716 | .playback = { | 716 | .playback = { |
717 | .stream_name = "Playback", | 717 | .stream_name = "Playback", |
718 | .channels_min = 1, | 718 | .channels_min = 1, |
@@ -730,21 +730,15 @@ struct snd_soc_dai wm8988_dai = { | |||
730 | .ops = &wm8988_ops, | 730 | .ops = &wm8988_ops, |
731 | .symmetric_rates = 1, | 731 | .symmetric_rates = 1, |
732 | }; | 732 | }; |
733 | EXPORT_SYMBOL_GPL(wm8988_dai); | ||
734 | 733 | ||
735 | static int wm8988_suspend(struct platform_device *pdev, pm_message_t state) | 734 | static int wm8988_suspend(struct snd_soc_codec *codec, pm_message_t state) |
736 | { | 735 | { |
737 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
738 | struct snd_soc_codec *codec = socdev->card->codec; | ||
739 | |||
740 | wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); | 736 | wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); |
741 | return 0; | 737 | return 0; |
742 | } | 738 | } |
743 | 739 | ||
744 | static int wm8988_resume(struct platform_device *pdev) | 740 | static int wm8988_resume(struct snd_soc_codec *codec) |
745 | { | 741 | { |
746 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
747 | struct snd_soc_codec *codec = socdev->card->codec; | ||
748 | int i; | 742 | int i; |
749 | u8 data[2]; | 743 | u8 data[2]; |
750 | u16 *cache = codec->reg_cache; | 744 | u16 *cache = codec->reg_cache; |
@@ -763,99 +757,23 @@ static int wm8988_resume(struct platform_device *pdev) | |||
763 | return 0; | 757 | return 0; |
764 | } | 758 | } |
765 | 759 | ||
766 | static struct snd_soc_codec *wm8988_codec; | 760 | static int wm8988_probe(struct snd_soc_codec *codec) |
767 | |||
768 | static int wm8988_probe(struct platform_device *pdev) | ||
769 | { | 761 | { |
770 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 762 | struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); |
771 | struct snd_soc_codec *codec; | ||
772 | int ret = 0; | 763 | int ret = 0; |
773 | |||
774 | if (wm8988_codec == NULL) { | ||
775 | dev_err(&pdev->dev, "Codec device not registered\n"); | ||
776 | return -ENODEV; | ||
777 | } | ||
778 | |||
779 | socdev->card->codec = wm8988_codec; | ||
780 | codec = wm8988_codec; | ||
781 | |||
782 | /* register pcms */ | ||
783 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
784 | if (ret < 0) { | ||
785 | dev_err(codec->dev, "failed to create pcms: %d\n", ret); | ||
786 | goto pcm_err; | ||
787 | } | ||
788 | |||
789 | snd_soc_add_controls(codec, wm8988_snd_controls, | ||
790 | ARRAY_SIZE(wm8988_snd_controls)); | ||
791 | snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets, | ||
792 | ARRAY_SIZE(wm8988_dapm_widgets)); | ||
793 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
794 | |||
795 | return ret; | ||
796 | |||
797 | pcm_err: | ||
798 | return ret; | ||
799 | } | ||
800 | |||
801 | static int wm8988_remove(struct platform_device *pdev) | ||
802 | { | ||
803 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
804 | |||
805 | snd_soc_free_pcms(socdev); | ||
806 | snd_soc_dapm_free(socdev); | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | struct snd_soc_codec_device soc_codec_dev_wm8988 = { | ||
812 | .probe = wm8988_probe, | ||
813 | .remove = wm8988_remove, | ||
814 | .suspend = wm8988_suspend, | ||
815 | .resume = wm8988_resume, | ||
816 | }; | ||
817 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988); | ||
818 | |||
819 | static int wm8988_register(struct wm8988_priv *wm8988, | ||
820 | enum snd_soc_control_type control) | ||
821 | { | ||
822 | struct snd_soc_codec *codec = &wm8988->codec; | ||
823 | int ret; | ||
824 | u16 reg; | 764 | u16 reg; |
825 | 765 | ||
826 | if (wm8988_codec) { | 766 | codec->control_data = wm8988->control_data; |
827 | dev_err(codec->dev, "Another WM8988 is registered\n"); | 767 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8988->control_type); |
828 | ret = -EINVAL; | ||
829 | goto err; | ||
830 | } | ||
831 | |||
832 | mutex_init(&codec->mutex); | ||
833 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
834 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
835 | |||
836 | snd_soc_codec_set_drvdata(codec, wm8988); | ||
837 | codec->name = "WM8988"; | ||
838 | codec->owner = THIS_MODULE; | ||
839 | codec->dai = &wm8988_dai; | ||
840 | codec->num_dai = 1; | ||
841 | codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache); | ||
842 | codec->reg_cache = &wm8988->reg_cache; | ||
843 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
844 | codec->set_bias_level = wm8988_set_bias_level; | ||
845 | |||
846 | memcpy(codec->reg_cache, wm8988_reg, | ||
847 | sizeof(wm8988_reg)); | ||
848 | |||
849 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); | ||
850 | if (ret < 0) { | 768 | if (ret < 0) { |
851 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | 769 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
852 | goto err; | 770 | return ret; |
853 | } | 771 | } |
854 | 772 | ||
855 | ret = wm8988_reset(codec); | 773 | ret = wm8988_reset(codec); |
856 | if (ret < 0) { | 774 | if (ret < 0) { |
857 | dev_err(codec->dev, "Failed to issue reset\n"); | 775 | dev_err(codec->dev, "Failed to issue reset\n"); |
858 | goto err; | 776 | return ret; |
859 | } | 777 | } |
860 | 778 | ||
861 | /* set the update bits (we always update left then right) */ | 779 | /* set the update bits (we always update left then right) */ |
@@ -870,139 +788,135 @@ static int wm8988_register(struct wm8988_priv *wm8988, | |||
870 | reg = snd_soc_read(codec, WM8988_RINVOL); | 788 | reg = snd_soc_read(codec, WM8988_RINVOL); |
871 | snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100); | 789 | snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100); |
872 | 790 | ||
873 | wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY); | 791 | wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
874 | |||
875 | wm8988_dai.dev = codec->dev; | ||
876 | |||
877 | wm8988_codec = codec; | ||
878 | |||
879 | ret = snd_soc_register_codec(codec); | ||
880 | if (ret != 0) { | ||
881 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
882 | goto err; | ||
883 | } | ||
884 | 792 | ||
885 | ret = snd_soc_register_dai(&wm8988_dai); | 793 | snd_soc_add_controls(codec, wm8988_snd_controls, |
886 | if (ret != 0) { | 794 | ARRAY_SIZE(wm8988_snd_controls)); |
887 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | 795 | snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets, |
888 | goto err_codec; | 796 | ARRAY_SIZE(wm8988_dapm_widgets)); |
889 | } | 797 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); |
890 | 798 | ||
891 | return 0; | 799 | return 0; |
892 | |||
893 | err_codec: | ||
894 | snd_soc_unregister_codec(codec); | ||
895 | err: | ||
896 | kfree(wm8988); | ||
897 | return ret; | ||
898 | } | 800 | } |
899 | 801 | ||
900 | static void wm8988_unregister(struct wm8988_priv *wm8988) | 802 | static int wm8988_remove(struct snd_soc_codec *codec) |
901 | { | 803 | { |
902 | wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_OFF); | 804 | wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF); |
903 | snd_soc_unregister_dai(&wm8988_dai); | 805 | return 0; |
904 | snd_soc_unregister_codec(&wm8988->codec); | ||
905 | kfree(wm8988); | ||
906 | wm8988_codec = NULL; | ||
907 | } | 806 | } |
908 | 807 | ||
909 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 808 | static struct snd_soc_codec_driver soc_codec_dev_wm8988 = { |
910 | static int wm8988_i2c_probe(struct i2c_client *i2c, | 809 | .probe = wm8988_probe, |
911 | const struct i2c_device_id *id) | 810 | .remove = wm8988_remove, |
811 | .suspend = wm8988_suspend, | ||
812 | .resume = wm8988_resume, | ||
813 | .set_bias_level = wm8988_set_bias_level, | ||
814 | .reg_cache_size = sizeof(wm8988_reg), | ||
815 | .reg_word_size = sizeof(u16), | ||
816 | .reg_cache_default = wm8988_reg, | ||
817 | }; | ||
818 | |||
819 | #if defined(CONFIG_SPI_MASTER) | ||
820 | static int __devinit wm8988_spi_probe(struct spi_device *spi) | ||
912 | { | 821 | { |
913 | struct wm8988_priv *wm8988; | 822 | struct wm8988_priv *wm8988; |
914 | struct snd_soc_codec *codec; | 823 | int ret; |
915 | 824 | ||
916 | wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); | 825 | wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); |
917 | if (wm8988 == NULL) | 826 | if (wm8988 == NULL) |
918 | return -ENOMEM; | 827 | return -ENOMEM; |
919 | 828 | ||
920 | codec = &wm8988->codec; | 829 | wm8988->control_data = spi; |
921 | 830 | wm8988->control_type = SND_SOC_SPI; | |
922 | i2c_set_clientdata(i2c, wm8988); | 831 | spi_set_drvdata(spi, wm8988); |
923 | codec->control_data = i2c; | ||
924 | |||
925 | codec->dev = &i2c->dev; | ||
926 | 832 | ||
927 | return wm8988_register(wm8988, SND_SOC_I2C); | 833 | ret = snd_soc_register_codec(&spi->dev, |
834 | &soc_codec_dev_wm8988, &wm8988_dai, 1); | ||
835 | if (ret < 0) | ||
836 | kfree(wm8988); | ||
837 | return ret; | ||
928 | } | 838 | } |
929 | 839 | ||
930 | static int wm8988_i2c_remove(struct i2c_client *client) | 840 | static int __devexit wm8988_spi_remove(struct spi_device *spi) |
931 | { | 841 | { |
932 | struct wm8988_priv *wm8988 = i2c_get_clientdata(client); | 842 | snd_soc_unregister_codec(&spi->dev); |
933 | wm8988_unregister(wm8988); | 843 | kfree(spi_get_drvdata(spi)); |
934 | return 0; | 844 | return 0; |
935 | } | 845 | } |
936 | 846 | ||
937 | static const struct i2c_device_id wm8988_i2c_id[] = { | 847 | static struct spi_driver wm8988_spi_driver = { |
938 | { "wm8988", 0 }, | ||
939 | { } | ||
940 | }; | ||
941 | MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id); | ||
942 | |||
943 | static struct i2c_driver wm8988_i2c_driver = { | ||
944 | .driver = { | 848 | .driver = { |
945 | .name = "WM8988", | 849 | .name = "wm8988-codec", |
946 | .owner = THIS_MODULE, | 850 | .bus = &spi_bus_type, |
851 | .owner = THIS_MODULE, | ||
947 | }, | 852 | }, |
948 | .probe = wm8988_i2c_probe, | 853 | .probe = wm8988_spi_probe, |
949 | .remove = wm8988_i2c_remove, | 854 | .remove = __devexit_p(wm8988_spi_remove), |
950 | .id_table = wm8988_i2c_id, | ||
951 | }; | 855 | }; |
952 | #endif | 856 | #endif /* CONFIG_SPI_MASTER */ |
953 | 857 | ||
954 | #if defined(CONFIG_SPI_MASTER) | 858 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
955 | static int __devinit wm8988_spi_probe(struct spi_device *spi) | 859 | static __devinit int wm8988_i2c_probe(struct i2c_client *i2c, |
860 | const struct i2c_device_id *id) | ||
956 | { | 861 | { |
957 | struct wm8988_priv *wm8988; | 862 | struct wm8988_priv *wm8988; |
958 | struct snd_soc_codec *codec; | 863 | int ret; |
959 | 864 | ||
960 | wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); | 865 | wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL); |
961 | if (wm8988 == NULL) | 866 | if (wm8988 == NULL) |
962 | return -ENOMEM; | 867 | return -ENOMEM; |
963 | 868 | ||
964 | codec = &wm8988->codec; | 869 | i2c_set_clientdata(i2c, wm8988); |
965 | codec->control_data = spi; | 870 | wm8988->control_data = i2c; |
966 | codec->dev = &spi->dev; | 871 | wm8988->control_type = SND_SOC_I2C; |
967 | |||
968 | dev_set_drvdata(&spi->dev, wm8988); | ||
969 | 872 | ||
970 | return wm8988_register(wm8988, SND_SOC_SPI); | 873 | ret = snd_soc_register_codec(&i2c->dev, |
874 | &soc_codec_dev_wm8988, &wm8988_dai, 1); | ||
875 | if (ret < 0) | ||
876 | kfree(wm8988); | ||
877 | return ret; | ||
971 | } | 878 | } |
972 | 879 | ||
973 | static int __devexit wm8988_spi_remove(struct spi_device *spi) | 880 | static __devexit int wm8988_i2c_remove(struct i2c_client *client) |
974 | { | 881 | { |
975 | struct wm8988_priv *wm8988 = dev_get_drvdata(&spi->dev); | 882 | snd_soc_unregister_codec(&client->dev); |
976 | 883 | kfree(i2c_get_clientdata(client)); | |
977 | wm8988_unregister(wm8988); | ||
978 | |||
979 | return 0; | 884 | return 0; |
980 | } | 885 | } |
981 | 886 | ||
982 | static struct spi_driver wm8988_spi_driver = { | 887 | static const struct i2c_device_id wm8988_i2c_id[] = { |
888 | { "wm8988", 0 }, | ||
889 | { } | ||
890 | }; | ||
891 | MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id); | ||
892 | |||
893 | static struct i2c_driver wm8988_i2c_driver = { | ||
983 | .driver = { | 894 | .driver = { |
984 | .name = "wm8988", | 895 | .name = "wm8988-codec", |
985 | .bus = &spi_bus_type, | 896 | .owner = THIS_MODULE, |
986 | .owner = THIS_MODULE, | ||
987 | }, | 897 | }, |
988 | .probe = wm8988_spi_probe, | 898 | .probe = wm8988_i2c_probe, |
989 | .remove = __devexit_p(wm8988_spi_remove), | 899 | .remove = __devexit_p(wm8988_i2c_remove), |
900 | .id_table = wm8988_i2c_id, | ||
990 | }; | 901 | }; |
991 | #endif | 902 | #endif |
992 | 903 | ||
993 | static int __init wm8988_modinit(void) | 904 | static int __init wm8988_modinit(void) |
994 | { | 905 | { |
995 | int ret; | 906 | int ret = 0; |
996 | |||
997 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 907 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
998 | ret = i2c_add_driver(&wm8988_i2c_driver); | 908 | ret = i2c_add_driver(&wm8988_i2c_driver); |
999 | if (ret != 0) | 909 | if (ret != 0) { |
1000 | pr_err("WM8988: Unable to register I2C driver: %d\n", ret); | 910 | printk(KERN_ERR "Failed to register WM8988 I2C driver: %d\n", |
911 | ret); | ||
912 | } | ||
1001 | #endif | 913 | #endif |
1002 | #if defined(CONFIG_SPI_MASTER) | 914 | #if defined(CONFIG_SPI_MASTER) |
1003 | ret = spi_register_driver(&wm8988_spi_driver); | 915 | ret = spi_register_driver(&wm8988_spi_driver); |
1004 | if (ret != 0) | 916 | if (ret != 0) { |
1005 | pr_err("WM8988: Unable to register SPI driver: %d\n", ret); | 917 | printk(KERN_ERR "Failed to register WM8988 SPI driver: %d\n", |
918 | ret); | ||
919 | } | ||
1006 | #endif | 920 | #endif |
1007 | return ret; | 921 | return ret; |
1008 | } | 922 | } |