diff options
Diffstat (limited to 'sound/soc/codecs/cs4270.c')
| -rw-r--r-- | sound/soc/codecs/cs4270.c | 91 |
1 files changed, 75 insertions, 16 deletions
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index ffe122d1cd76..dfbeb2db61b3 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <sound/initval.h> | 28 | #include <sound/initval.h> |
| 29 | #include <linux/i2c.h> | 29 | #include <linux/i2c.h> |
| 30 | #include <linux/delay.h> | 30 | #include <linux/delay.h> |
| 31 | #include <linux/regulator/consumer.h> | ||
| 31 | 32 | ||
| 32 | #include "cs4270.h" | 33 | #include "cs4270.h" |
| 33 | 34 | ||
| @@ -106,6 +107,10 @@ | |||
| 106 | #define CS4270_MUTE_DAC_A 0x01 | 107 | #define CS4270_MUTE_DAC_A 0x01 |
| 107 | #define CS4270_MUTE_DAC_B 0x02 | 108 | #define CS4270_MUTE_DAC_B 0x02 |
| 108 | 109 | ||
| 110 | static const char *supply_names[] = { | ||
| 111 | "va", "vd", "vlc" | ||
| 112 | }; | ||
| 113 | |||
| 109 | /* Private data for the CS4270 */ | 114 | /* Private data for the CS4270 */ |
| 110 | struct cs4270_private { | 115 | struct cs4270_private { |
| 111 | struct snd_soc_codec codec; | 116 | struct snd_soc_codec codec; |
| @@ -114,6 +119,9 @@ struct cs4270_private { | |||
| 114 | unsigned int mode; /* The mode (I2S or left-justified) */ | 119 | unsigned int mode; /* The mode (I2S or left-justified) */ |
| 115 | unsigned int slave_mode; | 120 | unsigned int slave_mode; |
| 116 | unsigned int manual_mute; | 121 | unsigned int manual_mute; |
| 122 | |||
| 123 | /* power domain regulators */ | ||
| 124 | struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; | ||
| 117 | }; | 125 | }; |
| 118 | 126 | ||
| 119 | /** | 127 | /** |
| @@ -192,6 +200,11 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { | |||
| 192 | * This function must be called by the machine driver's 'startup' function, | 200 | * This function must be called by the machine driver's 'startup' function, |
| 193 | * otherwise the list of supported sample rates will not be available in | 201 | * otherwise the list of supported sample rates will not be available in |
| 194 | * time for ALSA. | 202 | * time for ALSA. |
| 203 | * | ||
| 204 | * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause | ||
| 205 | * theoretically possible sample rates to be enabled. Call it again with a | ||
| 206 | * proper value set one the external clock is set (most probably you would do | ||
| 207 | * that from a machine's driver 'hw_param' hook. | ||
| 195 | */ | 208 | */ |
| 196 | static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, | 209 | static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
| 197 | int clk_id, unsigned int freq, int dir) | 210 | int clk_id, unsigned int freq, int dir) |
| @@ -205,20 +218,27 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
| 205 | 218 | ||
| 206 | cs4270->mclk = freq; | 219 | cs4270->mclk = freq; |
| 207 | 220 | ||
| 208 | for (i = 0; i < NUM_MCLK_RATIOS; i++) { | 221 | if (cs4270->mclk) { |
| 209 | unsigned int rate = freq / cs4270_mode_ratios[i].ratio; | 222 | for (i = 0; i < NUM_MCLK_RATIOS; i++) { |
| 210 | rates |= snd_pcm_rate_to_rate_bit(rate); | 223 | unsigned int rate = freq / cs4270_mode_ratios[i].ratio; |
| 211 | if (rate < rate_min) | 224 | rates |= snd_pcm_rate_to_rate_bit(rate); |
| 212 | rate_min = rate; | 225 | if (rate < rate_min) |
| 213 | if (rate > rate_max) | 226 | rate_min = rate; |
| 214 | rate_max = rate; | 227 | if (rate > rate_max) |
| 215 | } | 228 | rate_max = rate; |
| 216 | /* FIXME: soc should support a rate list */ | 229 | } |
| 217 | rates &= ~SNDRV_PCM_RATE_KNOT; | 230 | /* FIXME: soc should support a rate list */ |
| 231 | rates &= ~SNDRV_PCM_RATE_KNOT; | ||
| 218 | 232 | ||
| 219 | if (!rates) { | 233 | if (!rates) { |
| 220 | dev_err(codec->dev, "could not find a valid sample rate\n"); | 234 | dev_err(codec->dev, "could not find a valid sample rate\n"); |
| 221 | return -EINVAL; | 235 | return -EINVAL; |
| 236 | } | ||
| 237 | } else { | ||
| 238 | /* enable all possible rates */ | ||
| 239 | rates = SNDRV_PCM_RATE_8000_192000; | ||
| 240 | rate_min = 8000; | ||
| 241 | rate_max = 192000; | ||
| 222 | } | 242 | } |
| 223 | 243 | ||
| 224 | codec_dai->playback.rates = rates; | 244 | codec_dai->playback.rates = rates; |
| @@ -579,7 +599,8 @@ static int cs4270_probe(struct platform_device *pdev) | |||
| 579 | { | 599 | { |
| 580 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 600 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
| 581 | struct snd_soc_codec *codec = cs4270_codec; | 601 | struct snd_soc_codec *codec = cs4270_codec; |
| 582 | int ret; | 602 | struct cs4270_private *cs4270 = codec->private_data; |
| 603 | int i, ret; | ||
| 583 | 604 | ||
| 584 | /* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */ | 605 | /* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */ |
| 585 | socdev->card->codec = codec; | 606 | socdev->card->codec = codec; |
| @@ -599,8 +620,26 @@ static int cs4270_probe(struct platform_device *pdev) | |||
| 599 | goto error_free_pcms; | 620 | goto error_free_pcms; |
| 600 | } | 621 | } |
| 601 | 622 | ||
| 623 | /* get the power supply regulators */ | ||
| 624 | for (i = 0; i < ARRAY_SIZE(supply_names); i++) | ||
| 625 | cs4270->supplies[i].supply = supply_names[i]; | ||
| 626 | |||
| 627 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies), | ||
| 628 | cs4270->supplies); | ||
| 629 | if (ret < 0) | ||
| 630 | goto error_free_pcms; | ||
| 631 | |||
| 632 | ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), | ||
| 633 | cs4270->supplies); | ||
| 634 | if (ret < 0) | ||
| 635 | goto error_free_regulators; | ||
| 636 | |||
| 602 | return 0; | 637 | return 0; |
| 603 | 638 | ||
| 639 | error_free_regulators: | ||
| 640 | regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), | ||
| 641 | cs4270->supplies); | ||
| 642 | |||
| 604 | error_free_pcms: | 643 | error_free_pcms: |
| 605 | snd_soc_free_pcms(socdev); | 644 | snd_soc_free_pcms(socdev); |
| 606 | 645 | ||
| @@ -616,8 +655,12 @@ error_free_pcms: | |||
| 616 | static int cs4270_remove(struct platform_device *pdev) | 655 | static int cs4270_remove(struct platform_device *pdev) |
| 617 | { | 656 | { |
| 618 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 657 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
| 658 | struct snd_soc_codec *codec = cs4270_codec; | ||
| 659 | struct cs4270_private *cs4270 = codec->private_data; | ||
| 619 | 660 | ||
| 620 | snd_soc_free_pcms(socdev); | 661 | snd_soc_free_pcms(socdev); |
| 662 | regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); | ||
| 663 | regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); | ||
| 621 | 664 | ||
| 622 | return 0; | 665 | return 0; |
| 623 | }; | 666 | }; |
| @@ -799,17 +842,33 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id); | |||
| 799 | static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg) | 842 | static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg) |
| 800 | { | 843 | { |
| 801 | struct snd_soc_codec *codec = cs4270_codec; | 844 | struct snd_soc_codec *codec = cs4270_codec; |
| 802 | int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL; | 845 | struct cs4270_private *cs4270 = codec->private_data; |
| 846 | int reg, ret; | ||
| 803 | 847 | ||
| 804 | return snd_soc_write(codec, CS4270_PWRCTL, reg); | 848 | reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL; |
| 849 | if (reg < 0) | ||
| 850 | return reg; | ||
| 851 | |||
| 852 | ret = snd_soc_write(codec, CS4270_PWRCTL, reg); | ||
| 853 | if (ret < 0) | ||
| 854 | return ret; | ||
| 855 | |||
| 856 | regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), | ||
| 857 | cs4270->supplies); | ||
| 858 | |||
| 859 | return 0; | ||
| 805 | } | 860 | } |
| 806 | 861 | ||
| 807 | static int cs4270_soc_resume(struct platform_device *pdev) | 862 | static int cs4270_soc_resume(struct platform_device *pdev) |
| 808 | { | 863 | { |
| 809 | struct snd_soc_codec *codec = cs4270_codec; | 864 | struct snd_soc_codec *codec = cs4270_codec; |
| 865 | struct cs4270_private *cs4270 = codec->private_data; | ||
| 810 | struct i2c_client *i2c_client = codec->control_data; | 866 | struct i2c_client *i2c_client = codec->control_data; |
| 811 | int reg; | 867 | int reg; |
| 812 | 868 | ||
| 869 | regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), | ||
| 870 | cs4270->supplies); | ||
| 871 | |||
| 813 | /* In case the device was put to hard reset during sleep, we need to | 872 | /* In case the device was put to hard reset during sleep, we need to |
| 814 | * wait 500ns here before any I2C communication. */ | 873 | * wait 500ns here before any I2C communication. */ |
| 815 | ndelay(500); | 874 | ndelay(500); |
