| OLD | NEW |
| 1 /* | 1 /* |
| 2 * ALSA SoC TWL4030 codec driver | 2 * ALSA SoC TWL4030 codec driver |
| 3 * | 3 * |
| 4 * Author: Steve Sakoman, <steve@sakoman.com> | 4 * Author: Steve Sakoman, <steve@sakoman.com> |
| 5 * | 5 * |
| 6 * This program is free software; you can redistribute it and/or | 6 * This program is free software; you can redistribute it and/or |
| 7 * modify it under the terms of the GNU General Public License | 7 * modify it under the terms of the GNU General Public License |
| 8 * version 2 as published by the Free Software Foundation. | 8 * version 2 as published by the Free Software Foundation. |
| 9 * | 9 * |
| 10 * This program is distributed in the hope that it will be useful, but | 10 * This program is distributed in the hope that it will be useful, but |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 #include <linux/delay.h> | 25 #include <linux/delay.h> |
| 26 #include <linux/pm.h> | 26 #include <linux/pm.h> |
| 27 #include <linux/i2c.h> | 27 #include <linux/i2c.h> |
| 28 #include <linux/platform_device.h> | 28 #include <linux/platform_device.h> |
| 29 #include <linux/i2c/twl.h> | 29 #include <linux/i2c/twl.h> |
| 30 #include <linux/slab.h> | 30 #include <linux/slab.h> |
| 31 #include <sound/core.h> | 31 #include <sound/core.h> |
| 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 |
| 39 /* Register descriptions are here */ | 38 /* Register descriptions are here */ |
| 40 #include <linux/mfd/twl4030-codec.h> | 39 #include <linux/mfd/twl4030-codec.h> |
| 41 | 40 |
| 42 /* Shadow register used by the audio driver */ | 41 /* Shadow register used by the audio driver */ |
| 43 #define TWL4030_REG_SW_SHADOW 0x4A | 42 #define TWL4030_REG_SW_SHADOW 0x4A |
| 44 #define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1) | 43 #define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1) |
| 45 | 44 |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 write_to_reg = 1; | 225 write_to_reg = 1; |
| 227 break; | 226 break; |
| 228 } | 227 } |
| 229 if (write_to_reg) | 228 if (write_to_reg) |
| 230 return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 229 return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
| 231 value, reg); | 230 value, reg); |
| 232 } | 231 } |
| 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); |
| 239 int mode; | 248 int mode; |
| 240 | 249 |
| 241 if (enable == twl4030->codec_powered) | 250 if (enable == twl4030->codec_powered) |
| 242 return; | 251 return; |
| 243 | 252 |
| 244 if (enable) | 253 if (enable) |
| 245 mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); | 254 mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 331 | 340 |
| 332 /* initiate offset cancellation */ | 341 /* initiate offset cancellation */ |
| 333 twl4030_codec_enable(codec, 1); | 342 twl4030_codec_enable(codec, 1); |
| 334 | 343 |
| 335 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | 344 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); |
| 336 reg &= ~TWL4030_OFFSET_CNCL_SEL; | 345 reg &= ~TWL4030_OFFSET_CNCL_SEL; |
| 337 reg |= pdata->offset_cncl_path; | 346 reg |= pdata->offset_cncl_path; |
| 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) && |
| 348 ((byte & TWL4030_CNCL_OFFSET_START) == | 361 ((byte & TWL4030_CNCL_OFFSET_START) == |
| 349 TWL4030_CNCL_OFFSET_START)); | 362 TWL4030_CNCL_OFFSET_START)); |
| 350 | 363 |
| 351 /* Make sure that the reg_cache has the same value as the HW */ | 364 /* Make sure that the reg_cache has the same value as the HW */ |
| 352 twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); | 365 twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); |
| 353 | 366 |
| 354 twl4030_codec_enable(codec, 0); | 367 twl4030_codec_enable(codec, 0); |
| (...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 718 } | 731 } |
| 719 | 732 |
| 720 static void headset_ramp(struct snd_soc_codec *codec, int ramp) | 733 static void headset_ramp(struct snd_soc_codec *codec, int ramp) |
| 721 { | 734 { |
| 722 struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; | 735 struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; |
| 723 unsigned char hs_gain, hs_pop; | 736 unsigned char hs_gain, hs_pop; |
| 724 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 737 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
| 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 */ |
| 734 if (pdata && pdata->hs_extmute) { | 750 if (pdata && pdata->hs_extmute) { |
| 735 if (pdata->set_hs_extmute) { | 751 if (pdata->set_hs_extmute) { |
| 736 pdata->set_hs_extmute(1); | 752 pdata->set_hs_extmute(1); |
| 737 } else { | 753 } else { |
| 738 hs_pop |= TWL4030_EXTMUTE; | 754 hs_pop |= TWL4030_EXTMUTE; |
| 739 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 755 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
| 740 } | 756 } |
| 741 } | 757 } |
| 742 | 758 |
| 743 if (ramp) { | 759 if (ramp) { |
| 744 /* Headset ramp-up according to the TRM */ | 760 /* Headset ramp-up according to the TRM */ |
| 745 hs_pop |= TWL4030_VMID_EN; | 761 hs_pop |= TWL4030_VMID_EN; |
| 746 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 762 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
| 747 /* Actually write to the register */ | 763 /* Actually write to the register */ |
| 748 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 764 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
| 749 hs_gain, | 765 hs_gain, |
| 750 TWL4030_REG_HS_GAIN_SET); | 766 TWL4030_REG_HS_GAIN_SET); |
| 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), |
| 767 TWL4030_REG_HS_GAIN_SET); | 781 TWL4030_REG_HS_GAIN_SET); |
| 768 | 782 |
| 769 hs_pop &= ~TWL4030_VMID_EN; | 783 hs_pop &= ~TWL4030_VMID_EN; |
| 770 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 784 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
| 771 } | 785 } |
| 772 | 786 |
| 773 /* Disable external mute */ | 787 /* Disable external mute */ |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 828 } | 842 } |
| 829 return 0; | 843 return 0; |
| 830 } | 844 } |
| 831 | 845 |
| 832 static int digimic_event(struct snd_soc_dapm_widget *w, | 846 static int digimic_event(struct snd_soc_dapm_widget *w, |
| 833 struct snd_kcontrol *kcontrol, int event) | 847 struct snd_kcontrol *kcontrol, int event) |
| 834 { | 848 { |
| 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 |
| 842 /* | 856 /* |
| 843 * Some of the gain controls in TWL (mostly those which are associated with | 857 * Some of the gain controls in TWL (mostly those which are associated with |
| 844 * the outputs) are implemented in an interesting way: | 858 * the outputs) are implemented in an interesting way: |
| 845 * 0x0 : Power down (mute) | 859 * 0x0 : Power down (mute) |
| 846 * 0x1 : 6dB | 860 * 0x1 : 6dB |
| 847 * 0x2 : 0 dB | 861 * 0x2 : 0 dB |
| 848 * 0x3 : -6 dB | 862 * 0x3 : -6 dB |
| (...skipping 765 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1614 {"Voice Digital Loopback", "Volume", "TX2 Capture Route"}, | 1628 {"Voice Digital Loopback", "Volume", "TX2 Capture Route"}, |
| 1615 | 1629 |
| 1616 {"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"}, | 1630 {"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"}, |
| 1617 {"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"}, | 1631 {"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"}, |
| 1618 {"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"}, | 1632 {"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"}, |
| 1619 | 1633 |
| 1620 }; | 1634 }; |
| 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; |
| 1639 |
| 1640 » snd_soc_dapm_new_controls(dapm, twl4030_dapm_widgets, |
| 1625 ARRAY_SIZE(twl4030_dapm_widgets)); | 1641 ARRAY_SIZE(twl4030_dapm_widgets)); |
| 1626 | 1642 » snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); |
| 1627 » snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | |
| 1628 | 1643 |
| 1629 return 0; | 1644 return 0; |
| 1630 } | 1645 } |
| 1631 | 1646 |
| 1632 static int twl4030_set_bias_level(struct snd_soc_codec *codec, | 1647 static int twl4030_set_bias_level(struct snd_soc_codec *codec, |
| 1633 enum snd_soc_bias_level level) | 1648 enum snd_soc_bias_level level) |
| 1634 { | 1649 { |
| 1635 switch (level) { | 1650 switch (level) { |
| 1636 case SND_SOC_BIAS_ON: | 1651 case SND_SOC_BIAS_ON: |
| 1637 break; | 1652 break; |
| 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 } |
| 1652 | 1667 |
| 1653 static void twl4030_constraints(struct twl4030_priv *twl4030, | 1668 static void twl4030_constraints(struct twl4030_priv *twl4030, |
| 1654 struct snd_pcm_substream *mst_substream) | 1669 struct snd_pcm_substream *mst_substream) |
| 1655 { | 1670 { |
| 1656 struct snd_pcm_substream *slv_substream; | 1671 struct snd_pcm_substream *slv_substream; |
| 1657 | 1672 |
| 1658 /* Pick the stream, which need to be constrained */ | 1673 /* Pick the stream, which need to be constrained */ |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1702 twl4030_write(codec, TWL4030_REG_OPTION, reg); | 1717 twl4030_write(codec, TWL4030_REG_OPTION, reg); |
| 1703 } | 1718 } |
| 1704 | 1719 |
| 1705 static int twl4030_startup(struct snd_pcm_substream *substream, | 1720 static int twl4030_startup(struct snd_pcm_substream *substream, |
| 1706 struct snd_soc_dai *dai) | 1721 struct snd_soc_dai *dai) |
| 1707 { | 1722 { |
| 1708 struct snd_soc_pcm_runtime *rtd = substream->private_data; | 1723 struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 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 |
| 1715 * if the DAI has been already configured then constrain this | 1731 * if the DAI has been already configured then constrain this |
| 1716 * substream to match it. */ | 1732 * substream to match it. */ |
| 1717 if (twl4030->configured) | 1733 if (twl4030->configured) |
| 1718 twl4030_constraints(twl4030, twl4030->master_substream); | 1734 twl4030_constraints(twl4030, twl4030->master_substream); |
| 1719 } else { | 1735 } else { |
| 1720 if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & | 1736 if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & |
| 1721 TWL4030_OPTION_1)) { | 1737 TWL4030_OPTION_1)) { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1826 } | 1842 } |
| 1827 | 1843 |
| 1828 /* sample size */ | 1844 /* sample size */ |
| 1829 old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 1845 old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); |
| 1830 format = old_format; | 1846 format = old_format; |
| 1831 format &= ~TWL4030_DATA_WIDTH; | 1847 format &= ~TWL4030_DATA_WIDTH; |
| 1832 switch (params_format(params)) { | 1848 switch (params_format(params)) { |
| 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: |
| 1840 printk(KERN_ERR "TWL4030 hw params: unknown format %d\n", | 1856 printk(KERN_ERR "TWL4030 hw params: unknown format %d\n", |
| 1841 params_format(params)); | 1857 params_format(params)); |
| 1842 return -EINVAL; | 1858 return -EINVAL; |
| 1843 } | 1859 } |
| 1844 | 1860 |
| 1845 if (format != old_format || mode != old_mode) { | 1861 if (format != old_format || mode != old_mode) { |
| 1846 if (twl4030->codec_powered) { | 1862 if (twl4030->codec_powered) { |
| (...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2159 | 2175 |
| 2160 if (tristate) | 2176 if (tristate) |
| 2161 reg |= TWL4030_VIF_TRI_EN; | 2177 reg |= TWL4030_VIF_TRI_EN; |
| 2162 else | 2178 else |
| 2163 reg &= ~TWL4030_VIF_TRI_EN; | 2179 reg &= ~TWL4030_VIF_TRI_EN; |
| 2164 | 2180 |
| 2165 return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg); | 2181 return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg); |
| 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, |
| 2173 .shutdown = twl4030_shutdown, | 2189 .shutdown = twl4030_shutdown, |
| 2174 .hw_params = twl4030_hw_params, | 2190 .hw_params = twl4030_hw_params, |
| 2175 .set_sysclk = twl4030_set_dai_sysclk, | 2191 .set_sysclk = twl4030_set_dai_sysclk, |
| 2176 .set_fmt = twl4030_set_dai_fmt, | 2192 .set_fmt = twl4030_set_dai_fmt, |
| 2177 .set_tristate = twl4030_set_tristate, | 2193 .set_tristate = twl4030_set_tristate, |
| 2178 }; | 2194 }; |
| 2179 | 2195 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2238 struct twl4030_priv *twl4030; | 2254 struct twl4030_priv *twl4030; |
| 2239 | 2255 |
| 2240 twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); | 2256 twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); |
| 2241 if (twl4030 == NULL) { | 2257 if (twl4030 == NULL) { |
| 2242 printk("Can not allocate memroy\n"); | 2258 printk("Can not allocate memroy\n"); |
| 2243 return -ENOMEM; | 2259 return -ENOMEM; |
| 2244 } | 2260 } |
| 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 |
| 2252 snd_soc_add_controls(codec, twl4030_snd_controls, | 2268 snd_soc_add_controls(codec, twl4030_snd_controls, |
| 2253 ARRAY_SIZE(twl4030_snd_controls)); | 2269 ARRAY_SIZE(twl4030_snd_controls)); |
| 2254 twl4030_add_widgets(codec); | 2270 twl4030_add_widgets(codec); |
| 2255 return 0; | 2271 return 0; |
| 2256 } | 2272 } |
| 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 |
| 2266 static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { | 2285 static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { |
| 2267 .probe = twl4030_soc_probe, | 2286 .probe = twl4030_soc_probe, |
| 2268 .remove = twl4030_soc_remove, | 2287 .remove = twl4030_soc_remove, |
| 2269 .suspend = twl4030_soc_suspend, | 2288 .suspend = twl4030_soc_suspend, |
| 2270 .resume = twl4030_soc_resume, | 2289 .resume = twl4030_soc_resume, |
| 2271 .read = twl4030_read_reg_cache, | 2290 .read = twl4030_read_reg_cache, |
| 2272 .write = twl4030_write, | 2291 .write = twl4030_write, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 2284 dev_err(&pdev->dev, "platform_data is missing\n"); | 2303 dev_err(&pdev->dev, "platform_data is missing\n"); |
| 2285 return -EINVAL; | 2304 return -EINVAL; |
| 2286 } | 2305 } |
| 2287 | 2306 |
| 2288 return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030, | 2307 return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030, |
| 2289 twl4030_dai, ARRAY_SIZE(twl4030_dai)); | 2308 twl4030_dai, ARRAY_SIZE(twl4030_dai)); |
| 2290 } | 2309 } |
| 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 |
| 2301 MODULE_ALIAS("platform:twl4030-codec"); | 2317 MODULE_ALIAS("platform:twl4030-codec"); |
| 2302 | 2318 |
| 2303 static struct platform_driver twl4030_codec_driver = { | 2319 static struct platform_driver twl4030_codec_driver = { |
| 2304 .probe = twl4030_codec_probe, | 2320 .probe = twl4030_codec_probe, |
| 2305 .remove = __devexit_p(twl4030_codec_remove), | 2321 .remove = __devexit_p(twl4030_codec_remove), |
| 2306 .driver = { | 2322 .driver = { |
| 2307 .name = "twl4030-codec", | 2323 .name = "twl4030-codec", |
| 2308 .owner = THIS_MODULE, | 2324 .owner = THIS_MODULE, |
| 2309 }, | 2325 }, |
| 2310 }; | 2326 }; |
| 2311 | 2327 |
| 2312 static int __init twl4030_modinit(void) | 2328 static int __init twl4030_modinit(void) |
| 2313 { | 2329 { |
| 2314 return platform_driver_register(&twl4030_codec_driver); | 2330 return platform_driver_register(&twl4030_codec_driver); |
| 2315 } | 2331 } |
| 2316 module_init(twl4030_modinit); | 2332 module_init(twl4030_modinit); |
| 2317 | 2333 |
| 2318 static void __exit twl4030_exit(void) | 2334 static void __exit twl4030_exit(void) |
| 2319 { | 2335 { |
| 2320 platform_driver_unregister(&twl4030_codec_driver); | 2336 platform_driver_unregister(&twl4030_codec_driver); |
| 2321 } | 2337 } |
| 2322 module_exit(twl4030_exit); | 2338 module_exit(twl4030_exit); |
| 2323 | 2339 |
| 2324 MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); | 2340 MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); |
| 2325 MODULE_AUTHOR("Steve Sakoman"); | 2341 MODULE_AUTHOR("Steve Sakoman"); |
| 2326 MODULE_LICENSE("GPL"); | 2342 MODULE_LICENSE("GPL"); |
| OLD | NEW |