diff options
Diffstat (limited to 'sound/soc/codecs/wm8510.c')
-rw-r--r-- | sound/soc/codecs/wm8510.c | 221 |
1 files changed, 149 insertions, 72 deletions
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 3d998e6a997e..d8ca2da8d634 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.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 <lrg@slimlogic.co.uk> |
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 |
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/pm.h> | 18 | #include <linux/pm.h> |
19 | #include <linux/i2c.h> | 19 | #include <linux/i2c.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/spi/spi.h> | ||
21 | #include <sound/core.h> | 22 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
23 | #include <sound/pcm_params.h> | 24 | #include <sound/pcm_params.h> |
@@ -27,7 +28,6 @@ | |||
27 | 28 | ||
28 | #include "wm8510.h" | 29 | #include "wm8510.h" |
29 | 30 | ||
30 | #define AUDIO_NAME "wm8510" | ||
31 | #define WM8510_VERSION "0.6" | 31 | #define WM8510_VERSION "0.6" |
32 | 32 | ||
33 | struct snd_soc_codec_device soc_codec_dev_wm8510; | 33 | struct snd_soc_codec_device soc_codec_dev_wm8510; |
@@ -55,6 +55,9 @@ static const u16 wm8510_reg[WM8510_CACHEREGNUM] = { | |||
55 | 0x0001, | 55 | 0x0001, |
56 | }; | 56 | }; |
57 | 57 | ||
58 | #define WM8510_POWER1_BIASEN 0x08 | ||
59 | #define WM8510_POWER1_BUFIOEN 0x10 | ||
60 | |||
58 | /* | 61 | /* |
59 | * read wm8510 register cache | 62 | * read wm8510 register cache |
60 | */ | 63 | */ |
@@ -199,7 +202,7 @@ SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0), | |||
199 | }; | 202 | }; |
200 | 203 | ||
201 | static const struct snd_kcontrol_new wm8510_boost_controls[] = { | 204 | static const struct snd_kcontrol_new wm8510_boost_controls[] = { |
202 | SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 0), | 205 | SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 1), |
203 | SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0), | 206 | SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0), |
204 | SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0), | 207 | SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0), |
205 | }; | 208 | }; |
@@ -224,9 +227,9 @@ SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0), | |||
224 | SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0), | 227 | SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0), |
225 | SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0), | 228 | SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0), |
226 | 229 | ||
227 | SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0, | 230 | SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0, |
228 | &wm8510_micpga_controls[0], | 231 | &wm8510_micpga_controls[0], |
229 | ARRAY_SIZE(wm8510_micpga_controls)), | 232 | ARRAY_SIZE(wm8510_micpga_controls)), |
230 | SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, | 233 | SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, |
231 | &wm8510_boost_controls[0], | 234 | &wm8510_boost_controls[0], |
232 | ARRAY_SIZE(wm8510_boost_controls)), | 235 | ARRAY_SIZE(wm8510_boost_controls)), |
@@ -526,23 +529,35 @@ static int wm8510_mute(struct snd_soc_dai *dai, int mute) | |||
526 | static int wm8510_set_bias_level(struct snd_soc_codec *codec, | 529 | static int wm8510_set_bias_level(struct snd_soc_codec *codec, |
527 | enum snd_soc_bias_level level) | 530 | enum snd_soc_bias_level level) |
528 | { | 531 | { |
532 | u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3; | ||
529 | 533 | ||
530 | switch (level) { | 534 | switch (level) { |
531 | case SND_SOC_BIAS_ON: | 535 | case SND_SOC_BIAS_ON: |
532 | wm8510_write(codec, WM8510_POWER1, 0x1ff); | ||
533 | wm8510_write(codec, WM8510_POWER2, 0x1ff); | ||
534 | wm8510_write(codec, WM8510_POWER3, 0x1ff); | ||
535 | break; | ||
536 | case SND_SOC_BIAS_PREPARE: | 536 | case SND_SOC_BIAS_PREPARE: |
537 | power1 |= 0x1; /* VMID 50k */ | ||
538 | wm8510_write(codec, WM8510_POWER1, power1); | ||
539 | break; | ||
540 | |||
537 | case SND_SOC_BIAS_STANDBY: | 541 | case SND_SOC_BIAS_STANDBY: |
542 | power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; | ||
543 | |||
544 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | ||
545 | /* Initial cap charge at VMID 5k */ | ||
546 | wm8510_write(codec, WM8510_POWER1, power1 | 0x3); | ||
547 | mdelay(100); | ||
548 | } | ||
549 | |||
550 | power1 |= 0x2; /* VMID 500k */ | ||
551 | wm8510_write(codec, WM8510_POWER1, power1); | ||
538 | break; | 552 | break; |
553 | |||
539 | case SND_SOC_BIAS_OFF: | 554 | case SND_SOC_BIAS_OFF: |
540 | /* everything off, dac mute, inactive */ | 555 | wm8510_write(codec, WM8510_POWER1, 0); |
541 | wm8510_write(codec, WM8510_POWER1, 0x0); | 556 | wm8510_write(codec, WM8510_POWER2, 0); |
542 | wm8510_write(codec, WM8510_POWER2, 0x0); | 557 | wm8510_write(codec, WM8510_POWER3, 0); |
543 | wm8510_write(codec, WM8510_POWER3, 0x0); | ||
544 | break; | 558 | break; |
545 | } | 559 | } |
560 | |||
546 | codec->bias_level = level; | 561 | codec->bias_level = level; |
547 | return 0; | 562 | return 0; |
548 | } | 563 | } |
@@ -640,6 +655,7 @@ static int wm8510_init(struct snd_soc_device *socdev) | |||
640 | } | 655 | } |
641 | 656 | ||
642 | /* power on device */ | 657 | /* power on device */ |
658 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
643 | wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 659 | wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
644 | wm8510_add_controls(codec); | 660 | wm8510_add_controls(codec); |
645 | wm8510_add_widgets(codec); | 661 | wm8510_add_widgets(codec); |
@@ -665,90 +681,144 @@ static struct snd_soc_device *wm8510_socdev; | |||
665 | /* | 681 | /* |
666 | * WM8510 2 wire address is 0x1a | 682 | * WM8510 2 wire address is 0x1a |
667 | */ | 683 | */ |
668 | #define I2C_DRIVERID_WM8510 0xfefe /* liam - need a proper id */ | ||
669 | |||
670 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
671 | 684 | ||
672 | /* Magic definition of all other variables and things */ | 685 | static int wm8510_i2c_probe(struct i2c_client *i2c, |
673 | I2C_CLIENT_INSMOD; | 686 | const struct i2c_device_id *id) |
674 | |||
675 | static struct i2c_driver wm8510_i2c_driver; | ||
676 | static struct i2c_client client_template; | ||
677 | |||
678 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
679 | around */ | ||
680 | |||
681 | static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
682 | { | 687 | { |
683 | struct snd_soc_device *socdev = wm8510_socdev; | 688 | struct snd_soc_device *socdev = wm8510_socdev; |
684 | struct wm8510_setup_data *setup = socdev->codec_data; | ||
685 | struct snd_soc_codec *codec = socdev->codec; | 689 | struct snd_soc_codec *codec = socdev->codec; |
686 | struct i2c_client *i2c; | ||
687 | int ret; | 690 | int ret; |
688 | 691 | ||
689 | if (addr != setup->i2c_address) | ||
690 | return -ENODEV; | ||
691 | |||
692 | client_template.adapter = adap; | ||
693 | client_template.addr = addr; | ||
694 | |||
695 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
696 | if (i2c == NULL) | ||
697 | return -ENOMEM; | ||
698 | |||
699 | i2c_set_clientdata(i2c, codec); | 692 | i2c_set_clientdata(i2c, codec); |
700 | codec->control_data = i2c; | 693 | codec->control_data = i2c; |
701 | 694 | ||
702 | ret = i2c_attach_client(i2c); | ||
703 | if (ret < 0) { | ||
704 | pr_err("failed to attach codec at addr %x\n", addr); | ||
705 | goto err; | ||
706 | } | ||
707 | |||
708 | ret = wm8510_init(socdev); | 695 | ret = wm8510_init(socdev); |
709 | if (ret < 0) { | 696 | if (ret < 0) |
710 | pr_err("failed to initialise WM8510\n"); | 697 | pr_err("failed to initialise WM8510\n"); |
711 | goto err; | ||
712 | } | ||
713 | return ret; | ||
714 | 698 | ||
715 | err: | ||
716 | kfree(i2c); | ||
717 | return ret; | 699 | return ret; |
718 | } | 700 | } |
719 | 701 | ||
720 | static int wm8510_i2c_detach(struct i2c_client *client) | 702 | static int wm8510_i2c_remove(struct i2c_client *client) |
721 | { | 703 | { |
722 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 704 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
723 | i2c_detach_client(client); | ||
724 | kfree(codec->reg_cache); | 705 | kfree(codec->reg_cache); |
725 | kfree(client); | ||
726 | return 0; | 706 | return 0; |
727 | } | 707 | } |
728 | 708 | ||
729 | static int wm8510_i2c_attach(struct i2c_adapter *adap) | 709 | static const struct i2c_device_id wm8510_i2c_id[] = { |
730 | { | 710 | { "wm8510", 0 }, |
731 | return i2c_probe(adap, &addr_data, wm8510_codec_probe); | 711 | { } |
732 | } | 712 | }; |
713 | MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); | ||
733 | 714 | ||
734 | /* corgi i2c codec control layer */ | ||
735 | static struct i2c_driver wm8510_i2c_driver = { | 715 | static struct i2c_driver wm8510_i2c_driver = { |
736 | .driver = { | 716 | .driver = { |
737 | .name = "WM8510 I2C Codec", | 717 | .name = "WM8510 I2C Codec", |
738 | .owner = THIS_MODULE, | 718 | .owner = THIS_MODULE, |
739 | }, | 719 | }, |
740 | .id = I2C_DRIVERID_WM8510, | 720 | .probe = wm8510_i2c_probe, |
741 | .attach_adapter = wm8510_i2c_attach, | 721 | .remove = wm8510_i2c_remove, |
742 | .detach_client = wm8510_i2c_detach, | 722 | .id_table = wm8510_i2c_id, |
743 | .command = NULL, | ||
744 | }; | 723 | }; |
745 | 724 | ||
746 | static struct i2c_client client_template = { | 725 | static int wm8510_add_i2c_device(struct platform_device *pdev, |
747 | .name = "WM8510", | 726 | const struct wm8510_setup_data *setup) |
748 | .driver = &wm8510_i2c_driver, | 727 | { |
749 | }; | 728 | struct i2c_board_info info; |
729 | struct i2c_adapter *adapter; | ||
730 | struct i2c_client *client; | ||
731 | int ret; | ||
732 | |||
733 | ret = i2c_add_driver(&wm8510_i2c_driver); | ||
734 | if (ret != 0) { | ||
735 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
736 | return ret; | ||
737 | } | ||
738 | |||
739 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
740 | info.addr = setup->i2c_address; | ||
741 | strlcpy(info.type, "wm8510", I2C_NAME_SIZE); | ||
742 | |||
743 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
744 | if (!adapter) { | ||
745 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
746 | setup->i2c_bus); | ||
747 | goto err_driver; | ||
748 | } | ||
749 | |||
750 | client = i2c_new_device(adapter, &info); | ||
751 | i2c_put_adapter(adapter); | ||
752 | if (!client) { | ||
753 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
754 | (unsigned int)info.addr); | ||
755 | goto err_driver; | ||
756 | } | ||
757 | |||
758 | return 0; | ||
759 | |||
760 | err_driver: | ||
761 | i2c_del_driver(&wm8510_i2c_driver); | ||
762 | return -ENODEV; | ||
763 | } | ||
750 | #endif | 764 | #endif |
751 | 765 | ||
766 | #if defined(CONFIG_SPI_MASTER) | ||
767 | static int __devinit wm8510_spi_probe(struct spi_device *spi) | ||
768 | { | ||
769 | struct snd_soc_device *socdev = wm8510_socdev; | ||
770 | struct snd_soc_codec *codec = socdev->codec; | ||
771 | int ret; | ||
772 | |||
773 | codec->control_data = spi; | ||
774 | |||
775 | ret = wm8510_init(socdev); | ||
776 | if (ret < 0) | ||
777 | dev_err(&spi->dev, "failed to initialise WM8510\n"); | ||
778 | |||
779 | return ret; | ||
780 | } | ||
781 | |||
782 | static int __devexit wm8510_spi_remove(struct spi_device *spi) | ||
783 | { | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static struct spi_driver wm8510_spi_driver = { | ||
788 | .driver = { | ||
789 | .name = "wm8510", | ||
790 | .bus = &spi_bus_type, | ||
791 | .owner = THIS_MODULE, | ||
792 | }, | ||
793 | .probe = wm8510_spi_probe, | ||
794 | .remove = __devexit_p(wm8510_spi_remove), | ||
795 | }; | ||
796 | |||
797 | static int wm8510_spi_write(struct spi_device *spi, const char *data, int len) | ||
798 | { | ||
799 | struct spi_transfer t; | ||
800 | struct spi_message m; | ||
801 | u8 msg[2]; | ||
802 | |||
803 | if (len <= 0) | ||
804 | return 0; | ||
805 | |||
806 | msg[0] = data[0]; | ||
807 | msg[1] = data[1]; | ||
808 | |||
809 | spi_message_init(&m); | ||
810 | memset(&t, 0, (sizeof t)); | ||
811 | |||
812 | t.tx_buf = &msg[0]; | ||
813 | t.len = len; | ||
814 | |||
815 | spi_message_add_tail(&t, &m); | ||
816 | spi_sync(spi, &m); | ||
817 | |||
818 | return len; | ||
819 | } | ||
820 | #endif /* CONFIG_SPI_MASTER */ | ||
821 | |||
752 | static int wm8510_probe(struct platform_device *pdev) | 822 | static int wm8510_probe(struct platform_device *pdev) |
753 | { | 823 | { |
754 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 824 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
@@ -771,14 +841,17 @@ static int wm8510_probe(struct platform_device *pdev) | |||
771 | wm8510_socdev = socdev; | 841 | wm8510_socdev = socdev; |
772 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 842 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
773 | if (setup->i2c_address) { | 843 | if (setup->i2c_address) { |
774 | normal_i2c[0] = setup->i2c_address; | ||
775 | codec->hw_write = (hw_write_t)i2c_master_send; | 844 | codec->hw_write = (hw_write_t)i2c_master_send; |
776 | ret = i2c_add_driver(&wm8510_i2c_driver); | 845 | ret = wm8510_add_i2c_device(pdev, setup); |
846 | } | ||
847 | #endif | ||
848 | #if defined(CONFIG_SPI_MASTER) | ||
849 | if (setup->spi) { | ||
850 | codec->hw_write = (hw_write_t)wm8510_spi_write; | ||
851 | ret = spi_register_driver(&wm8510_spi_driver); | ||
777 | if (ret != 0) | 852 | if (ret != 0) |
778 | printk(KERN_ERR "can't add i2c driver"); | 853 | printk(KERN_ERR "can't add spi driver"); |
779 | } | 854 | } |
780 | #else | ||
781 | /* Add other interfaces here */ | ||
782 | #endif | 855 | #endif |
783 | 856 | ||
784 | if (ret != 0) | 857 | if (ret != 0) |
@@ -798,8 +871,12 @@ static int wm8510_remove(struct platform_device *pdev) | |||
798 | snd_soc_free_pcms(socdev); | 871 | snd_soc_free_pcms(socdev); |
799 | snd_soc_dapm_free(socdev); | 872 | snd_soc_dapm_free(socdev); |
800 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 873 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
874 | i2c_unregister_device(codec->control_data); | ||
801 | i2c_del_driver(&wm8510_i2c_driver); | 875 | i2c_del_driver(&wm8510_i2c_driver); |
802 | #endif | 876 | #endif |
877 | #if defined(CONFIG_SPI_MASTER) | ||
878 | spi_unregister_driver(&wm8510_spi_driver); | ||
879 | #endif | ||
803 | kfree(codec); | 880 | kfree(codec); |
804 | 881 | ||
805 | return 0; | 882 | return 0; |