diff options
Diffstat (limited to 'sound/soc/codecs/wm8971.c')
-rw-r--r-- | sound/soc/codecs/wm8971.c | 247 |
1 files changed, 63 insertions, 184 deletions
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index a99620f335d2..63f6dbf5d070 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c | |||
@@ -30,14 +30,13 @@ | |||
30 | 30 | ||
31 | #include "wm8971.h" | 31 | #include "wm8971.h" |
32 | 32 | ||
33 | #define WM8971_VERSION "0.9" | ||
34 | |||
35 | #define WM8971_REG_COUNT 43 | 33 | #define WM8971_REG_COUNT 43 |
36 | 34 | ||
37 | static struct workqueue_struct *wm8971_workq = NULL; | 35 | static struct workqueue_struct *wm8971_workq = NULL; |
38 | 36 | ||
39 | /* codec private data */ | 37 | /* codec private data */ |
40 | struct wm8971_priv { | 38 | struct wm8971_priv { |
39 | enum snd_soc_control_type control_type; | ||
41 | unsigned int sysclk; | 40 | unsigned int sysclk; |
42 | }; | 41 | }; |
43 | 42 | ||
@@ -492,8 +491,7 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream, | |||
492 | struct snd_soc_dai *dai) | 491 | struct snd_soc_dai *dai) |
493 | { | 492 | { |
494 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 493 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
495 | struct snd_soc_device *socdev = rtd->socdev; | 494 | struct snd_soc_codec *codec = rtd->codec; |
496 | struct snd_soc_codec *codec = socdev->card->codec; | ||
497 | struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); | 495 | struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); |
498 | u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3; | 496 | u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3; |
499 | u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0; | 497 | u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0; |
@@ -573,8 +571,8 @@ static struct snd_soc_dai_ops wm8971_dai_ops = { | |||
573 | .set_sysclk = wm8971_set_dai_sysclk, | 571 | .set_sysclk = wm8971_set_dai_sysclk, |
574 | }; | 572 | }; |
575 | 573 | ||
576 | struct snd_soc_dai wm8971_dai = { | 574 | static struct snd_soc_dai_driver wm8971_dai = { |
577 | .name = "WM8971", | 575 | .name = "wm8971-hifi", |
578 | .playback = { | 576 | .playback = { |
579 | .stream_name = "Playback", | 577 | .stream_name = "Playback", |
580 | .channels_min = 1, | 578 | .channels_min = 1, |
@@ -589,7 +587,6 @@ struct snd_soc_dai wm8971_dai = { | |||
589 | .formats = WM8971_FORMATS,}, | 587 | .formats = WM8971_FORMATS,}, |
590 | .ops = &wm8971_dai_ops, | 588 | .ops = &wm8971_dai_ops, |
591 | }; | 589 | }; |
592 | EXPORT_SYMBOL_GPL(wm8971_dai); | ||
593 | 590 | ||
594 | static void wm8971_work(struct work_struct *work) | 591 | static void wm8971_work(struct work_struct *work) |
595 | { | 592 | { |
@@ -598,19 +595,14 @@ static void wm8971_work(struct work_struct *work) | |||
598 | wm8971_set_bias_level(codec, codec->bias_level); | 595 | wm8971_set_bias_level(codec, codec->bias_level); |
599 | } | 596 | } |
600 | 597 | ||
601 | static int wm8971_suspend(struct platform_device *pdev, pm_message_t state) | 598 | static int wm8971_suspend(struct snd_soc_codec *codec, pm_message_t state) |
602 | { | 599 | { |
603 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
604 | struct snd_soc_codec *codec = socdev->card->codec; | ||
605 | |||
606 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); | 600 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); |
607 | return 0; | 601 | return 0; |
608 | } | 602 | } |
609 | 603 | ||
610 | static int wm8971_resume(struct platform_device *pdev) | 604 | static int wm8971_resume(struct snd_soc_codec *codec) |
611 | { | 605 | { |
612 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
613 | struct snd_soc_codec *codec = socdev->card->codec; | ||
614 | int i; | 606 | int i; |
615 | u8 data[2]; | 607 | u8 data[2]; |
616 | u16 *cache = codec->reg_cache; | 608 | u16 *cache = codec->reg_cache; |
@@ -639,37 +631,24 @@ static int wm8971_resume(struct platform_device *pdev) | |||
639 | return 0; | 631 | return 0; |
640 | } | 632 | } |
641 | 633 | ||
642 | static int wm8971_init(struct snd_soc_device *socdev, | 634 | static int wm8971_probe(struct snd_soc_codec *codec) |
643 | enum snd_soc_control_type control) | ||
644 | { | 635 | { |
645 | struct snd_soc_codec *codec = socdev->card->codec; | 636 | struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); |
646 | int reg, ret = 0; | 637 | int ret = 0; |
647 | 638 | u16 reg; | |
648 | codec->name = "WM8971"; | ||
649 | codec->owner = THIS_MODULE; | ||
650 | codec->set_bias_level = wm8971_set_bias_level; | ||
651 | codec->dai = &wm8971_dai; | ||
652 | codec->reg_cache_size = ARRAY_SIZE(wm8971_reg); | ||
653 | codec->num_dai = 1; | ||
654 | codec->reg_cache = kmemdup(wm8971_reg, sizeof(wm8971_reg), GFP_KERNEL); | ||
655 | |||
656 | if (codec->reg_cache == NULL) | ||
657 | return -ENOMEM; | ||
658 | 639 | ||
659 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); | 640 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8971->control_type); |
660 | if (ret < 0) { | 641 | if (ret < 0) { |
661 | printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret); | 642 | printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret); |
662 | goto err; | 643 | return ret; |
663 | } | 644 | } |
664 | 645 | ||
665 | wm8971_reset(codec); | 646 | INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work); |
647 | wm8971_workq = create_workqueue("wm8971"); | ||
648 | if (wm8971_workq == NULL) | ||
649 | return -ENOMEM; | ||
666 | 650 | ||
667 | /* register pcms */ | 651 | wm8971_reset(codec); |
668 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
669 | if (ret < 0) { | ||
670 | printk(KERN_ERR "wm8971: failed to create pcms\n"); | ||
671 | goto err; | ||
672 | } | ||
673 | 652 | ||
674 | /* charge output caps - set vmid to 5k for quick power up */ | 653 | /* charge output caps - set vmid to 5k for quick power up */ |
675 | reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; | 654 | reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; |
@@ -704,40 +683,54 @@ static int wm8971_init(struct snd_soc_device *socdev, | |||
704 | wm8971_add_widgets(codec); | 683 | wm8971_add_widgets(codec); |
705 | 684 | ||
706 | return ret; | 685 | return ret; |
707 | |||
708 | err: | ||
709 | kfree(codec->reg_cache); | ||
710 | return ret; | ||
711 | } | 686 | } |
712 | 687 | ||
713 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
714 | around */ | ||
715 | static struct snd_soc_device *wm8971_socdev; | ||
716 | 688 | ||
717 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | 689 | /* power down chip */ |
690 | static int wm8971_remove(struct snd_soc_codec *codec) | ||
691 | { | ||
692 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
693 | |||
694 | if (wm8971_workq) | ||
695 | destroy_workqueue(wm8971_workq); | ||
696 | return 0; | ||
697 | } | ||
718 | 698 | ||
719 | static int wm8971_i2c_probe(struct i2c_client *i2c, | 699 | static struct snd_soc_codec_driver soc_codec_dev_wm8971 = { |
720 | const struct i2c_device_id *id) | 700 | .probe = wm8971_probe, |
701 | .remove = wm8971_remove, | ||
702 | .suspend = wm8971_suspend, | ||
703 | .resume = wm8971_resume, | ||
704 | .set_bias_level = wm8971_set_bias_level, | ||
705 | .reg_cache_size = ARRAY_SIZE(wm8971_reg), | ||
706 | .reg_word_size = sizeof(u16), | ||
707 | .reg_cache_default = wm8971_reg, | ||
708 | }; | ||
709 | |||
710 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
711 | static __devinit int wm8971_i2c_probe(struct i2c_client *i2c, | ||
712 | const struct i2c_device_id *id) | ||
721 | { | 713 | { |
722 | struct snd_soc_device *socdev = wm8971_socdev; | 714 | struct wm8971_priv *wm8971; |
723 | struct snd_soc_codec *codec = socdev->card->codec; | ||
724 | int ret; | 715 | int ret; |
725 | 716 | ||
726 | i2c_set_clientdata(i2c, codec); | 717 | wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL); |
718 | if (wm8971 == NULL) | ||
719 | return -ENOMEM; | ||
727 | 720 | ||
728 | codec->control_data = i2c; | 721 | i2c_set_clientdata(i2c, wm8971); |
729 | 722 | ||
730 | ret = wm8971_init(socdev, SND_SOC_I2C); | 723 | ret = snd_soc_register_codec(&i2c->dev, |
724 | &soc_codec_dev_wm8971, &wm8971_dai, 1); | ||
731 | if (ret < 0) | 725 | if (ret < 0) |
732 | pr_err("failed to initialise WM8971\n"); | 726 | kfree(wm8971); |
733 | |||
734 | return ret; | 727 | return ret; |
735 | } | 728 | } |
736 | 729 | ||
737 | static int wm8971_i2c_remove(struct i2c_client *client) | 730 | static __devexit int wm8971_i2c_remove(struct i2c_client *client) |
738 | { | 731 | { |
739 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 732 | snd_soc_unregister_codec(&client->dev); |
740 | kfree(codec->reg_cache); | 733 | kfree(i2c_get_clientdata(client)); |
741 | return 0; | 734 | return 0; |
742 | } | 735 | } |
743 | 736 | ||
@@ -749,148 +742,34 @@ MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id); | |||
749 | 742 | ||
750 | static struct i2c_driver wm8971_i2c_driver = { | 743 | static struct i2c_driver wm8971_i2c_driver = { |
751 | .driver = { | 744 | .driver = { |
752 | .name = "WM8971 I2C Codec", | 745 | .name = "wm8971-codec", |
753 | .owner = THIS_MODULE, | 746 | .owner = THIS_MODULE, |
754 | }, | 747 | }, |
755 | .probe = wm8971_i2c_probe, | 748 | .probe = wm8971_i2c_probe, |
756 | .remove = wm8971_i2c_remove, | 749 | .remove = __devexit_p(wm8971_i2c_remove), |
757 | .id_table = wm8971_i2c_id, | 750 | .id_table = wm8971_i2c_id, |
758 | }; | 751 | }; |
759 | |||
760 | static int wm8971_add_i2c_device(struct platform_device *pdev, | ||
761 | const struct wm8971_setup_data *setup) | ||
762 | { | ||
763 | struct i2c_board_info info; | ||
764 | struct i2c_adapter *adapter; | ||
765 | struct i2c_client *client; | ||
766 | int ret; | ||
767 | |||
768 | ret = i2c_add_driver(&wm8971_i2c_driver); | ||
769 | if (ret != 0) { | ||
770 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
771 | return ret; | ||
772 | } | ||
773 | |||
774 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
775 | info.addr = setup->i2c_address; | ||
776 | strlcpy(info.type, "wm8971", I2C_NAME_SIZE); | ||
777 | |||
778 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
779 | if (!adapter) { | ||
780 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
781 | setup->i2c_bus); | ||
782 | goto err_driver; | ||
783 | } | ||
784 | |||
785 | client = i2c_new_device(adapter, &info); | ||
786 | i2c_put_adapter(adapter); | ||
787 | if (!client) { | ||
788 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
789 | (unsigned int)info.addr); | ||
790 | goto err_driver; | ||
791 | } | ||
792 | |||
793 | return 0; | ||
794 | |||
795 | err_driver: | ||
796 | i2c_del_driver(&wm8971_i2c_driver); | ||
797 | return -ENODEV; | ||
798 | } | ||
799 | |||
800 | #endif | 752 | #endif |
801 | 753 | ||
802 | static int wm8971_probe(struct platform_device *pdev) | 754 | static int __init wm8971_modinit(void) |
803 | { | 755 | { |
804 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
805 | struct wm8971_setup_data *setup; | ||
806 | struct snd_soc_codec *codec; | ||
807 | struct wm8971_priv *wm8971; | ||
808 | int ret = 0; | 756 | int ret = 0; |
809 | 757 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | |
810 | pr_info("WM8971 Audio Codec %s", WM8971_VERSION); | 758 | ret = i2c_add_driver(&wm8971_i2c_driver); |
811 | |||
812 | setup = socdev->codec_data; | ||
813 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
814 | if (codec == NULL) | ||
815 | return -ENOMEM; | ||
816 | |||
817 | wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL); | ||
818 | if (wm8971 == NULL) { | ||
819 | kfree(codec); | ||
820 | return -ENOMEM; | ||
821 | } | ||
822 | |||
823 | snd_soc_codec_set_drvdata(codec, wm8971); | ||
824 | socdev->card->codec = codec; | ||
825 | mutex_init(&codec->mutex); | ||
826 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
827 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
828 | wm8971_socdev = socdev; | ||
829 | |||
830 | INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work); | ||
831 | wm8971_workq = create_workqueue("wm8971"); | ||
832 | if (wm8971_workq == NULL) { | ||
833 | kfree(snd_soc_codec_get_drvdata(codec)); | ||
834 | kfree(codec); | ||
835 | return -ENOMEM; | ||
836 | } | ||
837 | |||
838 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | ||
839 | if (setup->i2c_address) { | ||
840 | ret = wm8971_add_i2c_device(pdev, setup); | ||
841 | } | ||
842 | #endif | ||
843 | /* Add other interfaces here */ | ||
844 | |||
845 | if (ret != 0) { | 759 | if (ret != 0) { |
846 | destroy_workqueue(wm8971_workq); | 760 | printk(KERN_ERR "Failed to register WM8971 I2C driver: %d\n", |
847 | kfree(snd_soc_codec_get_drvdata(codec)); | 761 | ret); |
848 | kfree(codec); | ||
849 | } | 762 | } |
850 | |||
851 | return ret; | ||
852 | } | ||
853 | |||
854 | /* power down chip */ | ||
855 | static int wm8971_remove(struct platform_device *pdev) | ||
856 | { | ||
857 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
858 | struct snd_soc_codec *codec = socdev->card->codec; | ||
859 | |||
860 | if (codec->control_data) | ||
861 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
862 | if (wm8971_workq) | ||
863 | destroy_workqueue(wm8971_workq); | ||
864 | snd_soc_free_pcms(socdev); | ||
865 | snd_soc_dapm_free(socdev); | ||
866 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | ||
867 | i2c_unregister_device(codec->control_data); | ||
868 | i2c_del_driver(&wm8971_i2c_driver); | ||
869 | #endif | 763 | #endif |
870 | kfree(snd_soc_codec_get_drvdata(codec)); | 764 | return ret; |
871 | kfree(codec); | ||
872 | |||
873 | return 0; | ||
874 | } | ||
875 | |||
876 | struct snd_soc_codec_device soc_codec_dev_wm8971 = { | ||
877 | .probe = wm8971_probe, | ||
878 | .remove = wm8971_remove, | ||
879 | .suspend = wm8971_suspend, | ||
880 | .resume = wm8971_resume, | ||
881 | }; | ||
882 | |||
883 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971); | ||
884 | |||
885 | static int __init wm8971_modinit(void) | ||
886 | { | ||
887 | return snd_soc_register_dai(&wm8971_dai); | ||
888 | } | 765 | } |
889 | module_init(wm8971_modinit); | 766 | module_init(wm8971_modinit); |
890 | 767 | ||
891 | static void __exit wm8971_exit(void) | 768 | static void __exit wm8971_exit(void) |
892 | { | 769 | { |
893 | snd_soc_unregister_dai(&wm8971_dai); | 770 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
771 | i2c_del_driver(&wm8971_i2c_driver); | ||
772 | #endif | ||
894 | } | 773 | } |
895 | module_exit(wm8971_exit); | 774 | module_exit(wm8971_exit); |
896 | 775 | ||