diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 56 |
1 files changed, 36 insertions, 20 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index cbebec6ba1ba..e4d464b937d6 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -32,7 +32,6 @@ | |||
32 | #include <sound/pcm.h> | 32 | #include <sound/pcm.h> |
33 | #include <sound/pcm_params.h> | 33 | #include <sound/pcm_params.h> |
34 | #include <sound/soc.h> | 34 | #include <sound/soc.h> |
35 | #include <sound/soc-dapm.h> | ||
36 | #include <sound/initval.h> | 35 | #include <sound/initval.h> |
37 | #include <sound/tlv.h> | 36 | #include <sound/tlv.h> |
38 | 37 | ||
@@ -233,6 +232,16 @@ static int twl4030_write(struct snd_soc_codec *codec, | |||
233 | return 0; | 232 | return 0; |
234 | } | 233 | } |
235 | 234 | ||
235 | static inline void twl4030_wait_ms(int time) | ||
236 | { | ||
237 | if (time < 60) { | ||
238 | time *= 1000; | ||
239 | usleep_range(time, time + 500); | ||
240 | } else { | ||
241 | msleep(time); | ||
242 | } | ||
243 | } | ||
244 | |||
236 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | 245 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) |
237 | { | 246 | { |
238 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 247 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
@@ -338,10 +347,14 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
338 | twl4030_write(codec, TWL4030_REG_ANAMICL, | 347 | twl4030_write(codec, TWL4030_REG_ANAMICL, |
339 | reg | TWL4030_CNCL_OFFSET_START); | 348 | reg | TWL4030_CNCL_OFFSET_START); |
340 | 349 | ||
341 | /* wait for offset cancellation to complete */ | 350 | /* |
351 | * Wait for offset cancellation to complete. | ||
352 | * Since this takes a while, do not slam the i2c. | ||
353 | * Start polling the status after ~20ms. | ||
354 | */ | ||
355 | msleep(20); | ||
342 | do { | 356 | do { |
343 | /* this takes a little while, so don't slam i2c */ | 357 | usleep_range(1000, 2000); |
344 | udelay(2000); | ||
345 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, | 358 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, |
346 | TWL4030_REG_ANAMICL); | 359 | TWL4030_REG_ANAMICL); |
347 | } while ((i++ < 100) && | 360 | } while ((i++ < 100) && |
@@ -725,9 +738,12 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) | |||
725 | /* Base values for ramp delay calculation: 2^19 - 2^26 */ | 738 | /* Base values for ramp delay calculation: 2^19 - 2^26 */ |
726 | unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, | 739 | unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, |
727 | 8388608, 16777216, 33554432, 67108864}; | 740 | 8388608, 16777216, 33554432, 67108864}; |
741 | unsigned int delay; | ||
728 | 742 | ||
729 | hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET); | 743 | hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET); |
730 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | 744 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); |
745 | delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / | ||
746 | twl4030->sysclk) + 1; | ||
731 | 747 | ||
732 | /* Enable external mute control, this dramatically reduces | 748 | /* Enable external mute control, this dramatically reduces |
733 | * the pop-noise */ | 749 | * the pop-noise */ |
@@ -751,16 +767,14 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) | |||
751 | hs_pop |= TWL4030_RAMP_EN; | 767 | hs_pop |= TWL4030_RAMP_EN; |
752 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 768 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
753 | /* Wait ramp delay time + 1, so the VMID can settle */ | 769 | /* Wait ramp delay time + 1, so the VMID can settle */ |
754 | mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / | 770 | twl4030_wait_ms(delay); |
755 | twl4030->sysclk) + 1); | ||
756 | } else { | 771 | } else { |
757 | /* Headset ramp-down _not_ according to | 772 | /* Headset ramp-down _not_ according to |
758 | * the TRM, but in a way that it is working */ | 773 | * the TRM, but in a way that it is working */ |
759 | hs_pop &= ~TWL4030_RAMP_EN; | 774 | hs_pop &= ~TWL4030_RAMP_EN; |
760 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 775 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
761 | /* Wait ramp delay time + 1, so the VMID can settle */ | 776 | /* Wait ramp delay time + 1, so the VMID can settle */ |
762 | mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / | 777 | twl4030_wait_ms(delay); |
763 | twl4030->sysclk) + 1); | ||
764 | /* Bypass the reg_cache to mute the headset */ | 778 | /* Bypass the reg_cache to mute the headset */ |
765 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 779 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
766 | hs_gain & (~0x0f), | 780 | hs_gain & (~0x0f), |
@@ -835,7 +849,7 @@ static int digimic_event(struct snd_soc_dapm_widget *w, | |||
835 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); | 849 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); |
836 | 850 | ||
837 | if (twl4030->digimic_delay) | 851 | if (twl4030->digimic_delay) |
838 | mdelay(twl4030->digimic_delay); | 852 | twl4030_wait_ms(twl4030->digimic_delay); |
839 | return 0; | 853 | return 0; |
840 | } | 854 | } |
841 | 855 | ||
@@ -1621,10 +1635,11 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1621 | 1635 | ||
1622 | static int twl4030_add_widgets(struct snd_soc_codec *codec) | 1636 | static int twl4030_add_widgets(struct snd_soc_codec *codec) |
1623 | { | 1637 | { |
1624 | snd_soc_dapm_new_controls(codec, twl4030_dapm_widgets, | 1638 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
1625 | ARRAY_SIZE(twl4030_dapm_widgets)); | ||
1626 | 1639 | ||
1627 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | 1640 | snd_soc_dapm_new_controls(dapm, twl4030_dapm_widgets, |
1641 | ARRAY_SIZE(twl4030_dapm_widgets)); | ||
1642 | snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); | ||
1628 | 1643 | ||
1629 | return 0; | 1644 | return 0; |
1630 | } | 1645 | } |
@@ -1638,14 +1653,14 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, | |||
1638 | case SND_SOC_BIAS_PREPARE: | 1653 | case SND_SOC_BIAS_PREPARE: |
1639 | break; | 1654 | break; |
1640 | case SND_SOC_BIAS_STANDBY: | 1655 | case SND_SOC_BIAS_STANDBY: |
1641 | if (codec->bias_level == SND_SOC_BIAS_OFF) | 1656 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) |
1642 | twl4030_codec_enable(codec, 1); | 1657 | twl4030_codec_enable(codec, 1); |
1643 | break; | 1658 | break; |
1644 | case SND_SOC_BIAS_OFF: | 1659 | case SND_SOC_BIAS_OFF: |
1645 | twl4030_codec_enable(codec, 0); | 1660 | twl4030_codec_enable(codec, 0); |
1646 | break; | 1661 | break; |
1647 | } | 1662 | } |
1648 | codec->bias_level = level; | 1663 | codec->dapm.bias_level = level; |
1649 | 1664 | ||
1650 | return 0; | 1665 | return 0; |
1651 | } | 1666 | } |
@@ -1709,6 +1724,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream, | |||
1709 | struct snd_soc_codec *codec = rtd->codec; | 1724 | struct snd_soc_codec *codec = rtd->codec; |
1710 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 1725 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
1711 | 1726 | ||
1727 | snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); | ||
1712 | if (twl4030->master_substream) { | 1728 | if (twl4030->master_substream) { |
1713 | twl4030->slave_substream = substream; | 1729 | twl4030->slave_substream = substream; |
1714 | /* The DAI has one configuration for playback and capture, so | 1730 | /* The DAI has one configuration for playback and capture, so |
@@ -1833,7 +1849,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1833 | case SNDRV_PCM_FORMAT_S16_LE: | 1849 | case SNDRV_PCM_FORMAT_S16_LE: |
1834 | format |= TWL4030_DATA_WIDTH_16S_16W; | 1850 | format |= TWL4030_DATA_WIDTH_16S_16W; |
1835 | break; | 1851 | break; |
1836 | case SNDRV_PCM_FORMAT_S24_LE: | 1852 | case SNDRV_PCM_FORMAT_S32_LE: |
1837 | format |= TWL4030_DATA_WIDTH_32S_24W; | 1853 | format |= TWL4030_DATA_WIDTH_32S_24W; |
1838 | break; | 1854 | break; |
1839 | default: | 1855 | default: |
@@ -2166,7 +2182,7 @@ static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate) | |||
2166 | } | 2182 | } |
2167 | 2183 | ||
2168 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) | 2184 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) |
2169 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) | 2185 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) |
2170 | 2186 | ||
2171 | static struct snd_soc_dai_ops twl4030_dai_hifi_ops = { | 2187 | static struct snd_soc_dai_ops twl4030_dai_hifi_ops = { |
2172 | .startup = twl4030_startup, | 2188 | .startup = twl4030_startup, |
@@ -2245,7 +2261,7 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) | |||
2245 | snd_soc_codec_set_drvdata(codec, twl4030); | 2261 | snd_soc_codec_set_drvdata(codec, twl4030); |
2246 | /* Set the defaults, and power up the codec */ | 2262 | /* Set the defaults, and power up the codec */ |
2247 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; | 2263 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; |
2248 | codec->idle_bias_off = 1; | 2264 | codec->dapm.idle_bias_off = 1; |
2249 | 2265 | ||
2250 | twl4030_init_chip(codec); | 2266 | twl4030_init_chip(codec); |
2251 | 2267 | ||
@@ -2257,9 +2273,12 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) | |||
2257 | 2273 | ||
2258 | static int twl4030_soc_remove(struct snd_soc_codec *codec) | 2274 | static int twl4030_soc_remove(struct snd_soc_codec *codec) |
2259 | { | 2275 | { |
2276 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
2277 | |||
2260 | /* Reset registers to their chip default before leaving */ | 2278 | /* Reset registers to their chip default before leaving */ |
2261 | twl4030_reset_registers(codec); | 2279 | twl4030_reset_registers(codec); |
2262 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 2280 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); |
2281 | kfree(twl4030); | ||
2263 | return 0; | 2282 | return 0; |
2264 | } | 2283 | } |
2265 | 2284 | ||
@@ -2291,10 +2310,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
2291 | 2310 | ||
2292 | static int __devexit twl4030_codec_remove(struct platform_device *pdev) | 2311 | static int __devexit twl4030_codec_remove(struct platform_device *pdev) |
2293 | { | 2312 | { |
2294 | struct twl4030_priv *twl4030 = dev_get_drvdata(&pdev->dev); | ||
2295 | |||
2296 | snd_soc_unregister_codec(&pdev->dev); | 2313 | snd_soc_unregister_codec(&pdev->dev); |
2297 | kfree(twl4030); | ||
2298 | return 0; | 2314 | return 0; |
2299 | } | 2315 | } |
2300 | 2316 | ||