diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-05-23 07:27:03 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-05-23 11:43:12 -0400 |
commit | 4fcbbb67a3cdc7129190a76763480f5ef63e5772 (patch) | |
tree | 8c3b165e45a120ecb31ed51266651b2a9a8f4f66 /sound/soc/codecs/wm8974.c | |
parent | 1a55b3f6ed1d917dd26271dae19dda088d820540 (diff) |
ASoC: Update WM8974 to use standard I2C device probe methods
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/wm8974.c')
-rw-r--r-- | sound/soc/codecs/wm8974.c | 267 |
1 files changed, 116 insertions, 151 deletions
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index b5f1a707cd76..397432a736ef 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright 2006 Wolfson Microelectronics PLC. | 4 | * Copyright 2006 Wolfson Microelectronics PLC. |
5 | * | 5 | * |
6 | * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 6 | * Author: Liam Girdwood <linux@wolfsonmicro.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -28,13 +28,6 @@ | |||
28 | 28 | ||
29 | #include "wm8974.h" | 29 | #include "wm8974.h" |
30 | 30 | ||
31 | struct snd_soc_codec_device soc_codec_dev_wm8974; | ||
32 | |||
33 | /* | ||
34 | * wm8974 register cache | ||
35 | * We can't read the WM8974 register space when we are | ||
36 | * using 2 wire for device control, so we cache them instead. | ||
37 | */ | ||
38 | static const u16 wm8974_reg[WM8974_CACHEREGNUM] = { | 31 | static const u16 wm8974_reg[WM8974_CACHEREGNUM] = { |
39 | 0x0000, 0x0000, 0x0000, 0x0000, | 32 | 0x0000, 0x0000, 0x0000, 0x0000, |
40 | 0x0050, 0x0000, 0x0140, 0x0000, | 33 | 0x0050, 0x0000, 0x0140, 0x0000, |
@@ -53,6 +46,13 @@ static const u16 wm8974_reg[WM8974_CACHEREGNUM] = { | |||
53 | 0x0000, | 46 | 0x0000, |
54 | }; | 47 | }; |
55 | 48 | ||
49 | struct wm8974_priv { | ||
50 | struct snd_soc_codec codec; | ||
51 | u16 reg_cache[WM8974_CACHEREGNUM]; | ||
52 | }; | ||
53 | |||
54 | static struct snd_soc_codec *wm8974_codec; | ||
55 | |||
56 | /* | 56 | /* |
57 | * read wm8974 register cache | 57 | * read wm8974 register cache |
58 | */ | 58 | */ |
@@ -623,217 +623,182 @@ static int wm8974_resume(struct platform_device *pdev) | |||
623 | return 0; | 623 | return 0; |
624 | } | 624 | } |
625 | 625 | ||
626 | /* | 626 | static int wm8974_probe(struct platform_device *pdev) |
627 | * initialise the WM8974 driver | ||
628 | * register the mixer and dsp interfaces with the kernel | ||
629 | */ | ||
630 | static int wm8974_init(struct snd_soc_device *socdev) | ||
631 | { | 627 | { |
632 | struct snd_soc_codec *codec = socdev->card->codec; | 628 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
629 | struct snd_soc_codec *codec; | ||
633 | int ret = 0; | 630 | int ret = 0; |
634 | 631 | ||
635 | codec->name = "WM8974"; | 632 | if (wm8974_codec == NULL) { |
636 | codec->owner = THIS_MODULE; | 633 | dev_err(&pdev->dev, "Codec device not registered\n"); |
637 | codec->read = wm8974_read_reg_cache; | 634 | return -ENODEV; |
638 | codec->write = wm8974_write; | 635 | } |
639 | codec->set_bias_level = wm8974_set_bias_level; | ||
640 | codec->dai = &wm8974_dai; | ||
641 | codec->num_dai = 1; | ||
642 | codec->reg_cache_size = ARRAY_SIZE(wm8974_reg); | ||
643 | codec->reg_cache = kmemdup(wm8974_reg, sizeof(wm8974_reg), GFP_KERNEL); | ||
644 | |||
645 | if (codec->reg_cache == NULL) | ||
646 | return -ENOMEM; | ||
647 | 636 | ||
648 | wm8974_reset(codec); | 637 | socdev->card->codec = wm8974_codec; |
638 | codec = wm8974_codec; | ||
649 | 639 | ||
650 | /* register pcms */ | 640 | /* register pcms */ |
651 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 641 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
652 | if (ret < 0) { | 642 | if (ret < 0) { |
653 | printk(KERN_ERR "wm8974: failed to create pcms\n"); | 643 | dev_err(codec->dev, "failed to create pcms: %d\n", ret); |
654 | goto pcm_err; | 644 | goto pcm_err; |
655 | } | 645 | } |
656 | 646 | ||
657 | /* power on device */ | 647 | snd_soc_add_controls(codec, wm8974_snd_controls, |
658 | wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 648 | ARRAY_SIZE(wm8974_snd_controls)); |
659 | wm8974_add_controls(codec); | ||
660 | wm8974_add_widgets(codec); | 649 | wm8974_add_widgets(codec); |
661 | ret = snd_soc_init_card(socdev); | 650 | ret = snd_soc_init_card(socdev); |
662 | if (ret < 0) { | 651 | if (ret < 0) { |
663 | printk(KERN_ERR "wm8974: failed to register card\n"); | 652 | dev_err(codec->dev, "failed to register card: %d\n", ret); |
664 | goto card_err; | 653 | goto card_err; |
665 | } | 654 | } |
655 | |||
666 | return ret; | 656 | return ret; |
667 | 657 | ||
668 | card_err: | 658 | card_err: |
669 | snd_soc_free_pcms(socdev); | 659 | snd_soc_free_pcms(socdev); |
670 | snd_soc_dapm_free(socdev); | 660 | snd_soc_dapm_free(socdev); |
671 | pcm_err: | 661 | pcm_err: |
672 | kfree(codec->reg_cache); | ||
673 | return ret; | 662 | return ret; |
674 | } | 663 | } |
675 | 664 | ||
676 | static struct snd_soc_device *wm8974_socdev; | 665 | /* power down chip */ |
677 | 666 | static int wm8974_remove(struct platform_device *pdev) | |
678 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | 667 | { |
679 | 668 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | |
680 | /* | ||
681 | * WM8974 2 wire address is 0x1a | ||
682 | */ | ||
683 | #define I2C_DRIVERID_WM8974 0xfefe /* liam - need a proper id */ | ||
684 | |||
685 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
686 | 669 | ||
687 | /* Magic definition of all other variables and things */ | 670 | snd_soc_free_pcms(socdev); |
688 | I2C_CLIENT_INSMOD; | 671 | snd_soc_dapm_free(socdev); |
689 | 672 | ||
690 | static struct i2c_driver wm8974_i2c_driver; | 673 | return 0; |
691 | static struct i2c_client client_template; | 674 | } |
692 | 675 | ||
693 | /* If the i2c layer weren't so broken, we could pass this kind of data | 676 | struct snd_soc_codec_device soc_codec_dev_wm8974 = { |
694 | around */ | 677 | .probe = wm8974_probe, |
678 | .remove = wm8974_remove, | ||
679 | .suspend = wm8974_suspend, | ||
680 | .resume = wm8974_resume, | ||
681 | }; | ||
682 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974); | ||
695 | 683 | ||
696 | static int wm8974_codec_probe(struct i2c_adapter *adap, int addr, int kind) | 684 | static __devinit int wm8974_register(struct wm8974_priv *wm8974) |
697 | { | 685 | { |
698 | struct snd_soc_device *socdev = wm8974_socdev; | ||
699 | struct wm8974_setup_data *setup = socdev->codec_data; | ||
700 | struct snd_soc_codec *codec = socdev->card->codec; | ||
701 | struct i2c_client *i2c; | ||
702 | int ret; | 686 | int ret; |
687 | struct snd_soc_codec *codec = &wm8974->codec; | ||
703 | 688 | ||
704 | if (addr != setup->i2c_address) | 689 | if (wm8974_codec) { |
705 | return -ENODEV; | 690 | dev_err(codec->dev, "Another WM8974 is registered\n"); |
691 | return -EINVAL; | ||
692 | } | ||
706 | 693 | ||
707 | client_template.adapter = adap; | 694 | mutex_init(&codec->mutex); |
708 | client_template.addr = addr; | 695 | INIT_LIST_HEAD(&codec->dapm_widgets); |
696 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
709 | 697 | ||
710 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | 698 | codec->private_data = wm8974; |
711 | if (i2c == NULL) { | 699 | codec->name = "WM8974"; |
712 | kfree(codec); | 700 | codec->owner = THIS_MODULE; |
713 | return -ENOMEM; | 701 | codec->read = wm8974_read_reg_cache; |
714 | } | 702 | codec->write = wm8974_write; |
715 | i2c_set_clientdata(i2c, codec); | 703 | codec->bias_level = SND_SOC_BIAS_OFF; |
716 | codec->control_data = i2c; | 704 | codec->set_bias_level = wm8974_set_bias_level; |
705 | codec->dai = &wm8974_dai; | ||
706 | codec->num_dai = 1; | ||
707 | codec->reg_cache_size = WM8974_CACHEREGNUM; | ||
708 | codec->reg_cache = &wm8974->reg_cache; | ||
717 | 709 | ||
718 | ret = i2c_attach_client(i2c); | 710 | memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg)); |
711 | |||
712 | ret = wm8974_reset(codec); | ||
719 | if (ret < 0) { | 713 | if (ret < 0) { |
720 | pr_err("failed to attach codec at addr %x\n", addr); | 714 | dev_err(codec->dev, "Failed to issue reset\n"); |
721 | goto err; | 715 | return ret; |
722 | } | 716 | } |
723 | 717 | ||
724 | ret = wm8974_init(socdev); | 718 | wm8974_dai.dev = codec->dev; |
725 | if (ret < 0) { | 719 | |
726 | pr_err("failed to initialise WM8974\n"); | 720 | wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
727 | goto err; | 721 | |
722 | wm8974_codec = codec; | ||
723 | |||
724 | ret = snd_soc_register_codec(codec); | ||
725 | if (ret != 0) { | ||
726 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
727 | return ret; | ||
728 | } | 728 | } |
729 | return ret; | ||
730 | 729 | ||
731 | err: | 730 | ret = snd_soc_register_dai(&wm8974_dai); |
732 | kfree(codec); | 731 | if (ret != 0) { |
733 | kfree(i2c); | 732 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); |
734 | return ret; | 733 | snd_soc_unregister_codec(codec); |
735 | } | 734 | return ret; |
735 | } | ||
736 | 736 | ||
737 | static int wm8974_i2c_detach(struct i2c_client *client) | ||
738 | { | ||
739 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
740 | i2c_detach_client(client); | ||
741 | kfree(codec->reg_cache); | ||
742 | kfree(client); | ||
743 | return 0; | 737 | return 0; |
744 | } | 738 | } |
745 | 739 | ||
746 | static int wm8974_i2c_attach(struct i2c_adapter *adap) | 740 | static __devexit void wm8974_unregister(struct wm8974_priv *wm8974) |
747 | { | 741 | { |
748 | return i2c_probe(adap, &addr_data, wm8974_codec_probe); | 742 | wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF); |
743 | snd_soc_unregister_dai(&wm8974_dai); | ||
744 | snd_soc_unregister_codec(&wm8974->codec); | ||
745 | kfree(wm8974); | ||
746 | wm8974_codec = NULL; | ||
749 | } | 747 | } |
750 | 748 | ||
751 | /* corgi i2c codec control layer */ | 749 | static __devinit int wm8974_i2c_probe(struct i2c_client *i2c, |
752 | static struct i2c_driver wm8974_i2c_driver = { | 750 | const struct i2c_device_id *id) |
753 | .driver = { | ||
754 | .name = "WM8974 I2C Codec", | ||
755 | .owner = THIS_MODULE, | ||
756 | }, | ||
757 | .id = I2C_DRIVERID_WM8974, | ||
758 | .attach_adapter = wm8974_i2c_attach, | ||
759 | .detach_client = wm8974_i2c_detach, | ||
760 | .command = NULL, | ||
761 | }; | ||
762 | |||
763 | static struct i2c_client client_template = { | ||
764 | .name = "WM8974", | ||
765 | .driver = &wm8974_i2c_driver, | ||
766 | }; | ||
767 | #endif | ||
768 | |||
769 | static int wm8974_probe(struct platform_device *pdev) | ||
770 | { | 751 | { |
771 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 752 | struct wm8974_priv *wm8974; |
772 | struct wm8974_setup_data *setup; | ||
773 | struct snd_soc_codec *codec; | 753 | struct snd_soc_codec *codec; |
774 | int ret = 0; | ||
775 | 754 | ||
776 | setup = socdev->codec_data; | 755 | wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL); |
777 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 756 | if (wm8974 == NULL) |
778 | if (codec == NULL) | ||
779 | return -ENOMEM; | 757 | return -ENOMEM; |
780 | 758 | ||
781 | socdev->card->codec = codec; | 759 | codec = &wm8974->codec; |
782 | mutex_init(&codec->mutex); | 760 | codec->hw_write = (hw_write_t)i2c_master_send; |
783 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
784 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
785 | 761 | ||
786 | wm8974_socdev = socdev; | 762 | i2c_set_clientdata(i2c, wm8974); |
787 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | 763 | codec->control_data = i2c; |
788 | if (setup->i2c_address) { | ||
789 | normal_i2c[0] = setup->i2c_address; | ||
790 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
791 | ret = i2c_add_driver(&wm8974_i2c_driver); | ||
792 | if (ret != 0) | ||
793 | printk(KERN_ERR "can't add i2c driver"); | ||
794 | } | ||
795 | #else | ||
796 | /* Add other interfaces here */ | ||
797 | #endif | ||
798 | return ret; | ||
799 | } | ||
800 | |||
801 | /* power down chip */ | ||
802 | static int wm8974_remove(struct platform_device *pdev) | ||
803 | { | ||
804 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
805 | struct snd_soc_codec *codec = socdev->card->codec; | ||
806 | 764 | ||
807 | if (codec->control_data) | 765 | codec->dev = &i2c->dev; |
808 | wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
809 | 766 | ||
810 | snd_soc_free_pcms(socdev); | 767 | return wm8974_register(wm8974); |
811 | snd_soc_dapm_free(socdev); | 768 | } |
812 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | ||
813 | i2c_del_driver(&wm8974_i2c_driver); | ||
814 | #endif | ||
815 | kfree(codec); | ||
816 | 769 | ||
770 | static __devexit int wm8974_i2c_remove(struct i2c_client *client) | ||
771 | { | ||
772 | struct wm8974_priv *wm8974 = i2c_get_clientdata(client); | ||
773 | wm8974_unregister(wm8974); | ||
817 | return 0; | 774 | return 0; |
818 | } | 775 | } |
819 | 776 | ||
820 | struct snd_soc_codec_device soc_codec_dev_wm8974 = { | 777 | static const struct i2c_device_id wm8974_i2c_id[] = { |
821 | .probe = wm8974_probe, | 778 | { "wm8974", 0 }, |
822 | .remove = wm8974_remove, | 779 | { } |
823 | .suspend = wm8974_suspend, | 780 | }; |
824 | .resume = wm8974_resume, | 781 | MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); |
782 | |||
783 | static struct i2c_driver wm8974_i2c_driver = { | ||
784 | .driver = { | ||
785 | .name = "WM8974 I2C Codec", | ||
786 | .owner = THIS_MODULE, | ||
787 | }, | ||
788 | .probe = wm8974_i2c_probe, | ||
789 | .remove = __devexit_p(wm8974_i2c_remove), | ||
790 | .id_table = wm8974_i2c_id, | ||
825 | }; | 791 | }; |
826 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974); | ||
827 | 792 | ||
828 | static int __init wm8974_modinit(void) | 793 | static int __init wm8974_modinit(void) |
829 | { | 794 | { |
830 | return snd_soc_register_dai(&wm8974_dai); | 795 | return i2c_add_driver(&wm8974_i2c_driver); |
831 | } | 796 | } |
832 | module_init(wm8974_modinit); | 797 | module_init(wm8974_modinit); |
833 | 798 | ||
834 | static void __exit wm8974_exit(void) | 799 | static void __exit wm8974_exit(void) |
835 | { | 800 | { |
836 | snd_soc_unregister_dai(&wm8974_dai); | 801 | i2c_del_driver(&wm8974_i2c_driver); |
837 | } | 802 | } |
838 | module_exit(wm8974_exit); | 803 | module_exit(wm8974_exit); |
839 | 804 | ||