aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/sn95031.c
diff options
context:
space:
mode:
authorVinod Koul <vinod.koul@intel.com>2011-02-09 11:14:34 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-02-09 17:32:53 -0500
commit36633237be60c0ec88b11e00d5fa22a305563d03 (patch)
tree1b2ae4b31f9cd19947e13f7225a67128da66be94 /sound/soc/codecs/sn95031.c
parent42aee9b43e76645cda59bce30a101e7574ce913e (diff)
ASoC: sn95031: Add support for reading mic bias
This patch adds support to read the mic bias voltage when a jack is inserted. It uses ADC to measure. Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Harsha Priya <priya.harsha@intel.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/sn95031.c')
-rw-r--r--sound/soc/codecs/sn95031.c127
1 files changed, 123 insertions, 4 deletions
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index b49d79017d3..4cc00177ee3 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -40,12 +40,129 @@
40#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100) 40#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
41#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) 41#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
42 42
43/* adc helper functions */
44
45/* enables mic bias voltage */
46static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
47{
48 snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0));
49 snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
50}
51
52/* Enable/Disable the ADC depending on the argument */
53static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
54{
55 int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
56
57 if (val) {
58 /* Enable and start the ADC */
59 value |= (SN95031_ADC_ENBL | SN95031_ADC_START);
60 value &= (~SN95031_ADC_NO_LOOP);
61 } else {
62 /* Just stop the ADC */
63 value &= (~SN95031_ADC_START);
64 }
65 snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value);
66}
67
43/* 68/*
44 * todo: 69 * finds an empty channel for conversion
45 * capture paths 70 * If the ADC is not enabled then start using 0th channel
46 * jack detection 71 * itself. Otherwise find an empty channel by looking for a
47 * PM functions 72 * channel in which the stopbit is set to 1. returns the index
73 * of the first free channel if succeeds or an error code.
74 *
75 * Context: can sleep
76 *
48 */ 77 */
78static int find_free_channel(struct snd_soc_codec *sn95031_codec)
79{
80 int ret = 0, i, value;
81
82 /* check whether ADC is enabled */
83 value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
84
85 if ((value & SN95031_ADC_ENBL) == 0)
86 return 0;
87
88 /* ADC is already enabled; Looking for an empty channel */
89 for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
90 value = snd_soc_read(sn95031_codec,
91 SN95031_ADC_CHNL_START_ADDR + i);
92 if (value & SN95031_STOPBIT_MASK) {
93 ret = i;
94 break;
95 }
96 }
97 return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret;
98}
99
100/* Initialize the ADC for reading micbias values. Can sleep. */
101static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec)
102{
103 int base_addr, chnl_addr;
104 int value;
105 static int channel_index;
106
107 /* Index of the first channel in which the stop bit is set */
108 channel_index = find_free_channel(sn95031_codec);
109 if (channel_index < 0) {
110 pr_err("No free ADC channels");
111 return channel_index;
112 }
113
114 base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index;
115
116 if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) {
117 /* Reset stop bit for channels other than 0 and 12 */
118 value = snd_soc_read(sn95031_codec, base_addr);
119 /* Set the stop bit to zero */
120 snd_soc_write(sn95031_codec, base_addr, value & 0xEF);
121 /* Index of the first free channel */
122 base_addr++;
123 channel_index++;
124 }
125
126 /* Since this is the last channel, set the stop bit
127 to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
128 snd_soc_write(sn95031_codec, base_addr,
129 SN95031_AUDIO_DETECT_CODE | 0x10);
130
131 chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index;
132 pr_debug("mid_initialize : %x", chnl_addr);
133 configure_adc(sn95031_codec, 1);
134 return chnl_addr;
135}
136
137
138/* reads the ADC registers and gets the mic bias value in mV. */
139static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
140{
141 u16 adc_adr = sn95031_initialize_adc(codec);
142 u16 adc_val1, adc_val2;
143 unsigned int mic_bias;
144
145 sn95031_enable_mic_bias(codec);
146
147 /* Enable the sound card for conversion before reading */
148 snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05);
149 /* Re-toggle the RRDATARD bit */
150 snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04);
151
152 /* Read the higher bits of data */
153 msleep(1000);
154 adc_val1 = snd_soc_read(codec, adc_adr);
155 adc_adr++;
156 adc_val2 = snd_soc_read(codec, adc_adr);
157
158 /* Adding lower two bits to the higher bits */
159 mic_bias = (adc_val1 << 2) + (adc_val2 & 3);
160 mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
161 pr_debug("mic bias = %dmV\n", mic_bias);
162 return mic_bias;
163}
164EXPORT_SYMBOL_GPL(sn95031_get_mic_bias);
165/*end - adc helper functions */
49 166
50static inline unsigned int sn95031_read(struct snd_soc_codec *codec, 167static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
51 unsigned int reg) 168 unsigned int reg)
@@ -663,6 +780,8 @@ static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
663 780
664static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack) 781static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
665{ 782{
783 int micbias = sn95031_get_mic_bias(mfld_jack->codec);
784
666 /* Defaulting to HEADSET for now. 785 /* Defaulting to HEADSET for now.
667 * will change after adding soc-jack detection apis */ 786 * will change after adding soc-jack detection apis */
668 int jack_type = SND_JACK_HEADSET; 787 int jack_type = SND_JACK_HEADSET;