diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 524 |
1 files changed, 383 insertions, 141 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index ea370a4f86d5..97738e2ece04 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -42,7 +42,7 @@ | |||
42 | */ | 42 | */ |
43 | static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | 43 | static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { |
44 | 0x00, /* this register not used */ | 44 | 0x00, /* this register not used */ |
45 | 0x93, /* REG_CODEC_MODE (0x1) */ | 45 | 0x91, /* REG_CODEC_MODE (0x1) */ |
46 | 0xc3, /* REG_OPTION (0x2) */ | 46 | 0xc3, /* REG_OPTION (0x2) */ |
47 | 0x00, /* REG_UNKNOWN (0x3) */ | 47 | 0x00, /* REG_UNKNOWN (0x3) */ |
48 | 0x00, /* REG_MICBIAS_CTL (0x4) */ | 48 | 0x00, /* REG_MICBIAS_CTL (0x4) */ |
@@ -117,6 +117,13 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | |||
117 | 0x00, /* REG_MISC_SET_2 (0x49) */ | 117 | 0x00, /* REG_MISC_SET_2 (0x49) */ |
118 | }; | 118 | }; |
119 | 119 | ||
120 | /* codec private data */ | ||
121 | struct twl4030_priv { | ||
122 | unsigned int bypass_state; | ||
123 | unsigned int codec_powered; | ||
124 | unsigned int codec_muted; | ||
125 | }; | ||
126 | |||
120 | /* | 127 | /* |
121 | * read twl4030 register cache | 128 | * read twl4030 register cache |
122 | */ | 129 | */ |
@@ -125,6 +132,9 @@ static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec, | |||
125 | { | 132 | { |
126 | u8 *cache = codec->reg_cache; | 133 | u8 *cache = codec->reg_cache; |
127 | 134 | ||
135 | if (reg >= TWL4030_CACHEREGNUM) | ||
136 | return -EIO; | ||
137 | |||
128 | return cache[reg]; | 138 | return cache[reg]; |
129 | } | 139 | } |
130 | 140 | ||
@@ -151,26 +161,22 @@ static int twl4030_write(struct snd_soc_codec *codec, | |||
151 | return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); | 161 | return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); |
152 | } | 162 | } |
153 | 163 | ||
154 | static void twl4030_clear_codecpdz(struct snd_soc_codec *codec) | 164 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) |
155 | { | 165 | { |
166 | struct twl4030_priv *twl4030 = codec->private_data; | ||
156 | u8 mode; | 167 | u8 mode; |
157 | 168 | ||
158 | mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); | 169 | if (enable == twl4030->codec_powered) |
159 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, | 170 | return; |
160 | mode & ~TWL4030_CODECPDZ); | ||
161 | |||
162 | /* REVISIT: this delay is present in TI sample drivers */ | ||
163 | /* but there seems to be no TRM requirement for it */ | ||
164 | udelay(10); | ||
165 | } | ||
166 | |||
167 | static void twl4030_set_codecpdz(struct snd_soc_codec *codec) | ||
168 | { | ||
169 | u8 mode; | ||
170 | 171 | ||
171 | mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); | 172 | mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); |
172 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, | 173 | if (enable) |
173 | mode | TWL4030_CODECPDZ); | 174 | mode |= TWL4030_CODECPDZ; |
175 | else | ||
176 | mode &= ~TWL4030_CODECPDZ; | ||
177 | |||
178 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
179 | twl4030->codec_powered = enable; | ||
174 | 180 | ||
175 | /* REVISIT: this delay is present in TI sample drivers */ | 181 | /* REVISIT: this delay is present in TI sample drivers */ |
176 | /* but there seems to be no TRM requirement for it */ | 182 | /* but there seems to be no TRM requirement for it */ |
@@ -182,7 +188,7 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
182 | int i; | 188 | int i; |
183 | 189 | ||
184 | /* clear CODECPDZ prior to setting register defaults */ | 190 | /* clear CODECPDZ prior to setting register defaults */ |
185 | twl4030_clear_codecpdz(codec); | 191 | twl4030_codec_enable(codec, 0); |
186 | 192 | ||
187 | /* set all audio section registers to reasonable defaults */ | 193 | /* set all audio section registers to reasonable defaults */ |
188 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) | 194 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) |
@@ -190,6 +196,122 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
190 | 196 | ||
191 | } | 197 | } |
192 | 198 | ||
199 | static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) | ||
200 | { | ||
201 | struct twl4030_priv *twl4030 = codec->private_data; | ||
202 | u8 reg_val; | ||
203 | |||
204 | if (mute == twl4030->codec_muted) | ||
205 | return; | ||
206 | |||
207 | if (mute) { | ||
208 | /* Bypass the reg_cache and mute the volumes | ||
209 | * Headset mute is done in it's own event handler | ||
210 | * Things to mute: Earpiece, PreDrivL/R, CarkitL/R | ||
211 | */ | ||
212 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL); | ||
213 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
214 | reg_val & (~TWL4030_EAR_GAIN), | ||
215 | TWL4030_REG_EAR_CTL); | ||
216 | |||
217 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL); | ||
218 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
219 | reg_val & (~TWL4030_PREDL_GAIN), | ||
220 | TWL4030_REG_PREDL_CTL); | ||
221 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL); | ||
222 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
223 | reg_val & (~TWL4030_PREDR_GAIN), | ||
224 | TWL4030_REG_PREDL_CTL); | ||
225 | |||
226 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL); | ||
227 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
228 | reg_val & (~TWL4030_PRECKL_GAIN), | ||
229 | TWL4030_REG_PRECKL_CTL); | ||
230 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL); | ||
231 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
232 | reg_val & (~TWL4030_PRECKL_GAIN), | ||
233 | TWL4030_REG_PRECKR_CTL); | ||
234 | |||
235 | /* Disable PLL */ | ||
236 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); | ||
237 | reg_val &= ~TWL4030_APLL_EN; | ||
238 | twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); | ||
239 | } else { | ||
240 | /* Restore the volumes | ||
241 | * Headset mute is done in it's own event handler | ||
242 | * Things to restore: Earpiece, PreDrivL/R, CarkitL/R | ||
243 | */ | ||
244 | twl4030_write(codec, TWL4030_REG_EAR_CTL, | ||
245 | twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL)); | ||
246 | |||
247 | twl4030_write(codec, TWL4030_REG_PREDL_CTL, | ||
248 | twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL)); | ||
249 | twl4030_write(codec, TWL4030_REG_PREDR_CTL, | ||
250 | twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL)); | ||
251 | |||
252 | twl4030_write(codec, TWL4030_REG_PRECKL_CTL, | ||
253 | twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL)); | ||
254 | twl4030_write(codec, TWL4030_REG_PRECKR_CTL, | ||
255 | twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL)); | ||
256 | |||
257 | /* Enable PLL */ | ||
258 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); | ||
259 | reg_val |= TWL4030_APLL_EN; | ||
260 | twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); | ||
261 | } | ||
262 | |||
263 | twl4030->codec_muted = mute; | ||
264 | } | ||
265 | |||
266 | static void twl4030_power_up(struct snd_soc_codec *codec) | ||
267 | { | ||
268 | struct twl4030_priv *twl4030 = codec->private_data; | ||
269 | u8 anamicl, regmisc1, byte; | ||
270 | int i = 0; | ||
271 | |||
272 | if (twl4030->codec_powered) | ||
273 | return; | ||
274 | |||
275 | /* set CODECPDZ to turn on codec */ | ||
276 | twl4030_codec_enable(codec, 1); | ||
277 | |||
278 | /* initiate offset cancellation */ | ||
279 | anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | ||
280 | twl4030_write(codec, TWL4030_REG_ANAMICL, | ||
281 | anamicl | TWL4030_CNCL_OFFSET_START); | ||
282 | |||
283 | /* wait for offset cancellation to complete */ | ||
284 | do { | ||
285 | /* this takes a little while, so don't slam i2c */ | ||
286 | udelay(2000); | ||
287 | twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, | ||
288 | TWL4030_REG_ANAMICL); | ||
289 | } while ((i++ < 100) && | ||
290 | ((byte & TWL4030_CNCL_OFFSET_START) == | ||
291 | TWL4030_CNCL_OFFSET_START)); | ||
292 | |||
293 | /* Make sure that the reg_cache has the same value as the HW */ | ||
294 | twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); | ||
295 | |||
296 | /* anti-pop when changing analog gain */ | ||
297 | regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | ||
298 | twl4030_write(codec, TWL4030_REG_MISC_SET_1, | ||
299 | regmisc1 | TWL4030_SMOOTH_ANAVOL_EN); | ||
300 | |||
301 | /* toggle CODECPDZ as per TRM */ | ||
302 | twl4030_codec_enable(codec, 0); | ||
303 | twl4030_codec_enable(codec, 1); | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Unconditional power down | ||
308 | */ | ||
309 | static void twl4030_power_down(struct snd_soc_codec *codec) | ||
310 | { | ||
311 | /* power down */ | ||
312 | twl4030_codec_enable(codec, 0); | ||
313 | } | ||
314 | |||
193 | /* Earpiece */ | 315 | /* Earpiece */ |
194 | static const char *twl4030_earpiece_texts[] = | 316 | static const char *twl4030_earpiece_texts[] = |
195 | {"Off", "DACL1", "DACL2", "DACR1"}; | 317 | {"Off", "DACL1", "DACL2", "DACR1"}; |
@@ -366,6 +488,41 @@ static const struct soc_enum twl4030_micpathtx2_enum = | |||
366 | static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = | 488 | static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = |
367 | SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); | 489 | SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); |
368 | 490 | ||
491 | /* Analog bypass for AudioR1 */ | ||
492 | static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control = | ||
493 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0); | ||
494 | |||
495 | /* Analog bypass for AudioL1 */ | ||
496 | static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control = | ||
497 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0); | ||
498 | |||
499 | /* Analog bypass for AudioR2 */ | ||
500 | static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control = | ||
501 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0); | ||
502 | |||
503 | /* Analog bypass for AudioL2 */ | ||
504 | static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = | ||
505 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0); | ||
506 | |||
507 | /* Digital bypass gain, 0 mutes the bypass */ | ||
508 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { | ||
509 | TLV_DB_RANGE_HEAD(2), | ||
510 | 0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1), | ||
511 | 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), | ||
512 | }; | ||
513 | |||
514 | /* Digital bypass left (TX1L -> RX2L) */ | ||
515 | static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control = | ||
516 | SOC_DAPM_SINGLE_TLV("Volume", | ||
517 | TWL4030_REG_ATX2ARXPGA, 3, 7, 0, | ||
518 | twl4030_dapm_dbypass_tlv); | ||
519 | |||
520 | /* Digital bypass right (TX1R -> RX2R) */ | ||
521 | static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control = | ||
522 | SOC_DAPM_SINGLE_TLV("Volume", | ||
523 | TWL4030_REG_ATX2ARXPGA, 0, 7, 0, | ||
524 | twl4030_dapm_dbypass_tlv); | ||
525 | |||
369 | static int micpath_event(struct snd_soc_dapm_widget *w, | 526 | static int micpath_event(struct snd_soc_dapm_widget *w, |
370 | struct snd_kcontrol *kcontrol, int event) | 527 | struct snd_kcontrol *kcontrol, int event) |
371 | { | 528 | { |
@@ -420,6 +577,79 @@ static int handsfree_event(struct snd_soc_dapm_widget *w, | |||
420 | return 0; | 577 | return 0; |
421 | } | 578 | } |
422 | 579 | ||
580 | static int headsetl_event(struct snd_soc_dapm_widget *w, | ||
581 | struct snd_kcontrol *kcontrol, int event) | ||
582 | { | ||
583 | unsigned char hs_gain, hs_pop; | ||
584 | |||
585 | /* Save the current volume */ | ||
586 | hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET); | ||
587 | hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET); | ||
588 | |||
589 | switch (event) { | ||
590 | case SND_SOC_DAPM_POST_PMU: | ||
591 | /* Do the anti-pop/bias ramp enable according to the TRM */ | ||
592 | hs_pop |= TWL4030_VMID_EN; | ||
593 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
594 | /* Is this needed? Can we just use whatever gain here? */ | ||
595 | twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, | ||
596 | (hs_gain & (~0x0f)) | 0x0a); | ||
597 | hs_pop |= TWL4030_RAMP_EN; | ||
598 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
599 | |||
600 | /* Restore the original volume */ | ||
601 | twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain); | ||
602 | break; | ||
603 | case SND_SOC_DAPM_POST_PMD: | ||
604 | /* Do the anti-pop/bias ramp disable according to the TRM */ | ||
605 | hs_pop &= ~TWL4030_RAMP_EN; | ||
606 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
607 | /* Bypass the reg_cache to mute the headset */ | ||
608 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
609 | hs_gain & (~0x0f), | ||
610 | TWL4030_REG_HS_GAIN_SET); | ||
611 | hs_pop &= ~TWL4030_VMID_EN; | ||
612 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
613 | break; | ||
614 | } | ||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static int bypass_event(struct snd_soc_dapm_widget *w, | ||
619 | struct snd_kcontrol *kcontrol, int event) | ||
620 | { | ||
621 | struct soc_mixer_control *m = | ||
622 | (struct soc_mixer_control *)w->kcontrols->private_value; | ||
623 | struct twl4030_priv *twl4030 = w->codec->private_data; | ||
624 | unsigned char reg; | ||
625 | |||
626 | reg = twl4030_read_reg_cache(w->codec, m->reg); | ||
627 | |||
628 | if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { | ||
629 | /* Analog bypass */ | ||
630 | if (reg & (1 << m->shift)) | ||
631 | twl4030->bypass_state |= | ||
632 | (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); | ||
633 | else | ||
634 | twl4030->bypass_state &= | ||
635 | ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); | ||
636 | } else { | ||
637 | /* Digital bypass */ | ||
638 | if (reg & (0x7 << m->shift)) | ||
639 | twl4030->bypass_state |= (1 << (m->shift ? 5 : 4)); | ||
640 | else | ||
641 | twl4030->bypass_state &= ~(1 << (m->shift ? 5 : 4)); | ||
642 | } | ||
643 | |||
644 | if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { | ||
645 | if (twl4030->bypass_state) | ||
646 | twl4030_codec_mute(w->codec, 0); | ||
647 | else | ||
648 | twl4030_codec_mute(w->codec, 1); | ||
649 | } | ||
650 | return 0; | ||
651 | } | ||
652 | |||
423 | /* | 653 | /* |
424 | * Some of the gain controls in TWL (mostly those which are associated with | 654 | * Some of the gain controls in TWL (mostly those which are associated with |
425 | * the outputs) are implemented in an interesting way: | 655 | * the outputs) are implemented in an interesting way: |
@@ -614,6 +844,17 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); | |||
614 | */ | 844 | */ |
615 | static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); | 845 | static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); |
616 | 846 | ||
847 | static const char *twl4030_rampdelay_texts[] = { | ||
848 | "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", | ||
849 | "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms", | ||
850 | "3495/2581/1748 ms" | ||
851 | }; | ||
852 | |||
853 | static const struct soc_enum twl4030_rampdelay_enum = | ||
854 | SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2, | ||
855 | ARRAY_SIZE(twl4030_rampdelay_texts), | ||
856 | twl4030_rampdelay_texts); | ||
857 | |||
617 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { | 858 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { |
618 | /* Common playback gain controls */ | 859 | /* Common playback gain controls */ |
619 | SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", | 860 | SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", |
@@ -668,23 +909,9 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { | |||
668 | 909 | ||
669 | SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, | 910 | SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, |
670 | 0, 3, 5, 0, input_gain_tlv), | 911 | 0, 3, 5, 0, input_gain_tlv), |
671 | }; | ||
672 | |||
673 | /* add non dapm controls */ | ||
674 | static int twl4030_add_controls(struct snd_soc_codec *codec) | ||
675 | { | ||
676 | int err, i; | ||
677 | |||
678 | for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) { | ||
679 | err = snd_ctl_add(codec->card, | ||
680 | snd_soc_cnew(&twl4030_snd_controls[i], | ||
681 | codec, NULL)); | ||
682 | if (err < 0) | ||
683 | return err; | ||
684 | } | ||
685 | 912 | ||
686 | return 0; | 913 | SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), |
687 | } | 914 | }; |
688 | 915 | ||
689 | static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | 916 | static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { |
690 | /* Left channel inputs */ | 917 | /* Left channel inputs */ |
@@ -714,13 +941,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
714 | 941 | ||
715 | /* DACs */ | 942 | /* DACs */ |
716 | SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback", | 943 | SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback", |
717 | TWL4030_REG_AVDAC_CTL, 0, 0), | 944 | SND_SOC_NOPM, 0, 0), |
718 | SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback", | 945 | SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback", |
719 | TWL4030_REG_AVDAC_CTL, 1, 0), | 946 | SND_SOC_NOPM, 0, 0), |
720 | SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback", | 947 | SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback", |
721 | TWL4030_REG_AVDAC_CTL, 2, 0), | 948 | SND_SOC_NOPM, 0, 0), |
722 | SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback", | 949 | SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback", |
723 | TWL4030_REG_AVDAC_CTL, 3, 0), | 950 | SND_SOC_NOPM, 0, 0), |
724 | 951 | ||
725 | /* Analog PGAs */ | 952 | /* Analog PGAs */ |
726 | SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL, | 953 | SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL, |
@@ -732,6 +959,37 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
732 | SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL, | 959 | SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL, |
733 | 0, 0, NULL, 0), | 960 | 0, 0, NULL, 0), |
734 | 961 | ||
962 | /* Analog bypasses */ | ||
963 | SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, | ||
964 | &twl4030_dapm_abypassr1_control, bypass_event, | ||
965 | SND_SOC_DAPM_POST_REG), | ||
966 | SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, | ||
967 | &twl4030_dapm_abypassl1_control, | ||
968 | bypass_event, SND_SOC_DAPM_POST_REG), | ||
969 | SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, | ||
970 | &twl4030_dapm_abypassr2_control, | ||
971 | bypass_event, SND_SOC_DAPM_POST_REG), | ||
972 | SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, | ||
973 | &twl4030_dapm_abypassl2_control, | ||
974 | bypass_event, SND_SOC_DAPM_POST_REG), | ||
975 | |||
976 | /* Digital bypasses */ | ||
977 | SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0, | ||
978 | &twl4030_dapm_dbypassl_control, bypass_event, | ||
979 | SND_SOC_DAPM_POST_REG), | ||
980 | SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0, | ||
981 | &twl4030_dapm_dbypassr_control, bypass_event, | ||
982 | SND_SOC_DAPM_POST_REG), | ||
983 | |||
984 | SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL, | ||
985 | 0, 0, NULL, 0), | ||
986 | SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL, | ||
987 | 1, 0, NULL, 0), | ||
988 | SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL, | ||
989 | 2, 0, NULL, 0), | ||
990 | SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL, | ||
991 | 3, 0, NULL, 0), | ||
992 | |||
735 | /* Output MUX controls */ | 993 | /* Output MUX controls */ |
736 | /* Earpiece */ | 994 | /* Earpiece */ |
737 | SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0, | 995 | SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0, |
@@ -742,8 +1000,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
742 | SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0, | 1000 | SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0, |
743 | &twl4030_dapm_predriver_control), | 1001 | &twl4030_dapm_predriver_control), |
744 | /* HeadsetL/R */ | 1002 | /* HeadsetL/R */ |
745 | SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0, | 1003 | SND_SOC_DAPM_MUX_E("HeadsetL Mux", SND_SOC_NOPM, 0, 0, |
746 | &twl4030_dapm_hsol_control), | 1004 | &twl4030_dapm_hsol_control, headsetl_event, |
1005 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
747 | SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0, | 1006 | SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0, |
748 | &twl4030_dapm_hsor_control), | 1007 | &twl4030_dapm_hsor_control), |
749 | /* CarkitL/R */ | 1008 | /* CarkitL/R */ |
@@ -782,16 +1041,16 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
782 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| | 1041 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| |
783 | SND_SOC_DAPM_POST_REG), | 1042 | SND_SOC_DAPM_POST_REG), |
784 | 1043 | ||
785 | /* Analog input muxes with power switch for the physical ADCL/R */ | 1044 | /* Analog input muxes with switch for the capture amplifiers */ |
786 | SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route", | 1045 | SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route", |
787 | TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control), | 1046 | TWL4030_REG_ANAMICL, 4, 0, &twl4030_dapm_analoglmic_control), |
788 | SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route", | 1047 | SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route", |
789 | TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control), | 1048 | TWL4030_REG_ANAMICR, 4, 0, &twl4030_dapm_analogrmic_control), |
790 | 1049 | ||
791 | SND_SOC_DAPM_PGA("Analog Left Amplifier", | 1050 | SND_SOC_DAPM_PGA("ADC Physical Left", |
792 | TWL4030_REG_ANAMICL, 4, 0, NULL, 0), | 1051 | TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0), |
793 | SND_SOC_DAPM_PGA("Analog Right Amplifier", | 1052 | SND_SOC_DAPM_PGA("ADC Physical Right", |
794 | TWL4030_REG_ANAMICR, 4, 0, NULL, 0), | 1053 | TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), |
795 | 1054 | ||
796 | SND_SOC_DAPM_PGA("Digimic0 Enable", | 1055 | SND_SOC_DAPM_PGA("Digimic0 Enable", |
797 | TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0), | 1056 | TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0), |
@@ -801,13 +1060,19 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
801 | SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), | 1060 | SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), |
802 | SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), | 1061 | SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), |
803 | SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0), | 1062 | SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0), |
1063 | |||
804 | }; | 1064 | }; |
805 | 1065 | ||
806 | static const struct snd_soc_dapm_route intercon[] = { | 1066 | static const struct snd_soc_dapm_route intercon[] = { |
807 | {"ARXL1_APGA", NULL, "DAC Left1"}, | 1067 | {"Analog L1 Playback Mixer", NULL, "DAC Left1"}, |
808 | {"ARXR1_APGA", NULL, "DAC Right1"}, | 1068 | {"Analog R1 Playback Mixer", NULL, "DAC Right1"}, |
809 | {"ARXL2_APGA", NULL, "DAC Left2"}, | 1069 | {"Analog L2 Playback Mixer", NULL, "DAC Left2"}, |
810 | {"ARXR2_APGA", NULL, "DAC Right2"}, | 1070 | {"Analog R2 Playback Mixer", NULL, "DAC Right2"}, |
1071 | |||
1072 | {"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"}, | ||
1073 | {"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"}, | ||
1074 | {"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"}, | ||
1075 | {"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"}, | ||
811 | 1076 | ||
812 | /* Internal playback routings */ | 1077 | /* Internal playback routings */ |
813 | /* Earpiece */ | 1078 | /* Earpiece */ |
@@ -865,23 +1130,23 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
865 | {"Analog Right Capture Route", "Sub mic", "SUBMIC"}, | 1130 | {"Analog Right Capture Route", "Sub mic", "SUBMIC"}, |
866 | {"Analog Right Capture Route", "AUXR", "AUXR"}, | 1131 | {"Analog Right Capture Route", "AUXR", "AUXR"}, |
867 | 1132 | ||
868 | {"Analog Left Amplifier", NULL, "Analog Left Capture Route"}, | 1133 | {"ADC Physical Left", NULL, "Analog Left Capture Route"}, |
869 | {"Analog Right Amplifier", NULL, "Analog Right Capture Route"}, | 1134 | {"ADC Physical Right", NULL, "Analog Right Capture Route"}, |
870 | 1135 | ||
871 | {"Digimic0 Enable", NULL, "DIGIMIC0"}, | 1136 | {"Digimic0 Enable", NULL, "DIGIMIC0"}, |
872 | {"Digimic1 Enable", NULL, "DIGIMIC1"}, | 1137 | {"Digimic1 Enable", NULL, "DIGIMIC1"}, |
873 | 1138 | ||
874 | /* TX1 Left capture path */ | 1139 | /* TX1 Left capture path */ |
875 | {"TX1 Capture Route", "Analog", "Analog Left Amplifier"}, | 1140 | {"TX1 Capture Route", "Analog", "ADC Physical Left"}, |
876 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, | 1141 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, |
877 | /* TX1 Right capture path */ | 1142 | /* TX1 Right capture path */ |
878 | {"TX1 Capture Route", "Analog", "Analog Right Amplifier"}, | 1143 | {"TX1 Capture Route", "Analog", "ADC Physical Right"}, |
879 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, | 1144 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, |
880 | /* TX2 Left capture path */ | 1145 | /* TX2 Left capture path */ |
881 | {"TX2 Capture Route", "Analog", "Analog Left Amplifier"}, | 1146 | {"TX2 Capture Route", "Analog", "ADC Physical Left"}, |
882 | {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, | 1147 | {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, |
883 | /* TX2 Right capture path */ | 1148 | /* TX2 Right capture path */ |
884 | {"TX2 Capture Route", "Analog", "Analog Right Amplifier"}, | 1149 | {"TX2 Capture Route", "Analog", "ADC Physical Right"}, |
885 | {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, | 1150 | {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, |
886 | 1151 | ||
887 | {"ADC Virtual Left1", NULL, "TX1 Capture Route"}, | 1152 | {"ADC Virtual Left1", NULL, "TX1 Capture Route"}, |
@@ -889,6 +1154,24 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
889 | {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, | 1154 | {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, |
890 | {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, | 1155 | {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, |
891 | 1156 | ||
1157 | /* Analog bypass routes */ | ||
1158 | {"Right1 Analog Loopback", "Switch", "Analog Right Capture Route"}, | ||
1159 | {"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"}, | ||
1160 | {"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"}, | ||
1161 | {"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"}, | ||
1162 | |||
1163 | {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, | ||
1164 | {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, | ||
1165 | {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, | ||
1166 | {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"}, | ||
1167 | |||
1168 | /* Digital bypass routes */ | ||
1169 | {"Right Digital Loopback", "Volume", "TX1 Capture Route"}, | ||
1170 | {"Left Digital Loopback", "Volume", "TX1 Capture Route"}, | ||
1171 | |||
1172 | {"Analog R2 Playback Mixer", NULL, "Right Digital Loopback"}, | ||
1173 | {"Analog L2 Playback Mixer", NULL, "Left Digital Loopback"}, | ||
1174 | |||
892 | }; | 1175 | }; |
893 | 1176 | ||
894 | static int twl4030_add_widgets(struct snd_soc_codec *codec) | 1177 | static int twl4030_add_widgets(struct snd_soc_codec *codec) |
@@ -902,82 +1185,28 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) | |||
902 | return 0; | 1185 | return 0; |
903 | } | 1186 | } |
904 | 1187 | ||
905 | static void twl4030_power_up(struct snd_soc_codec *codec) | ||
906 | { | ||
907 | u8 anamicl, regmisc1, byte, popn; | ||
908 | int i = 0; | ||
909 | |||
910 | /* set CODECPDZ to turn on codec */ | ||
911 | twl4030_set_codecpdz(codec); | ||
912 | |||
913 | /* initiate offset cancellation */ | ||
914 | anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | ||
915 | twl4030_write(codec, TWL4030_REG_ANAMICL, | ||
916 | anamicl | TWL4030_CNCL_OFFSET_START); | ||
917 | |||
918 | |||
919 | /* wait for offset cancellation to complete */ | ||
920 | do { | ||
921 | /* this takes a little while, so don't slam i2c */ | ||
922 | udelay(2000); | ||
923 | twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, | ||
924 | TWL4030_REG_ANAMICL); | ||
925 | } while ((i++ < 100) && | ||
926 | ((byte & TWL4030_CNCL_OFFSET_START) == | ||
927 | TWL4030_CNCL_OFFSET_START)); | ||
928 | |||
929 | /* anti-pop when changing analog gain */ | ||
930 | regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | ||
931 | twl4030_write(codec, TWL4030_REG_MISC_SET_1, | ||
932 | regmisc1 | TWL4030_SMOOTH_ANAVOL_EN); | ||
933 | |||
934 | /* toggle CODECPDZ as per TRM */ | ||
935 | twl4030_clear_codecpdz(codec); | ||
936 | twl4030_set_codecpdz(codec); | ||
937 | |||
938 | /* program anti-pop with bias ramp delay */ | ||
939 | popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
940 | popn &= TWL4030_RAMP_DELAY; | ||
941 | popn |= TWL4030_RAMP_DELAY_645MS; | ||
942 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); | ||
943 | popn |= TWL4030_VMID_EN; | ||
944 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); | ||
945 | |||
946 | /* enable anti-pop ramp */ | ||
947 | popn |= TWL4030_RAMP_EN; | ||
948 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); | ||
949 | } | ||
950 | |||
951 | static void twl4030_power_down(struct snd_soc_codec *codec) | ||
952 | { | ||
953 | u8 popn; | ||
954 | |||
955 | /* disable anti-pop ramp */ | ||
956 | popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
957 | popn &= ~TWL4030_RAMP_EN; | ||
958 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); | ||
959 | |||
960 | /* disable bias out */ | ||
961 | popn &= ~TWL4030_VMID_EN; | ||
962 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); | ||
963 | |||
964 | /* power down */ | ||
965 | twl4030_clear_codecpdz(codec); | ||
966 | } | ||
967 | |||
968 | static int twl4030_set_bias_level(struct snd_soc_codec *codec, | 1188 | static int twl4030_set_bias_level(struct snd_soc_codec *codec, |
969 | enum snd_soc_bias_level level) | 1189 | enum snd_soc_bias_level level) |
970 | { | 1190 | { |
1191 | struct twl4030_priv *twl4030 = codec->private_data; | ||
1192 | |||
971 | switch (level) { | 1193 | switch (level) { |
972 | case SND_SOC_BIAS_ON: | 1194 | case SND_SOC_BIAS_ON: |
973 | twl4030_power_up(codec); | 1195 | twl4030_codec_mute(codec, 0); |
974 | break; | 1196 | break; |
975 | case SND_SOC_BIAS_PREPARE: | 1197 | case SND_SOC_BIAS_PREPARE: |
976 | /* TODO: develop a twl4030_prepare function */ | 1198 | twl4030_power_up(codec); |
1199 | if (twl4030->bypass_state) | ||
1200 | twl4030_codec_mute(codec, 0); | ||
1201 | else | ||
1202 | twl4030_codec_mute(codec, 1); | ||
977 | break; | 1203 | break; |
978 | case SND_SOC_BIAS_STANDBY: | 1204 | case SND_SOC_BIAS_STANDBY: |
979 | /* TODO: develop a twl4030_standby function */ | 1205 | twl4030_power_up(codec); |
980 | twl4030_power_down(codec); | 1206 | if (twl4030->bypass_state) |
1207 | twl4030_codec_mute(codec, 0); | ||
1208 | else | ||
1209 | twl4030_codec_mute(codec, 1); | ||
981 | break; | 1210 | break; |
982 | case SND_SOC_BIAS_OFF: | 1211 | case SND_SOC_BIAS_OFF: |
983 | twl4030_power_down(codec); | 1212 | twl4030_power_down(codec); |
@@ -994,10 +1223,9 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
994 | { | 1223 | { |
995 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 1224 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
996 | struct snd_soc_device *socdev = rtd->socdev; | 1225 | struct snd_soc_device *socdev = rtd->socdev; |
997 | struct snd_soc_codec *codec = socdev->codec; | 1226 | struct snd_soc_codec *codec = socdev->card->codec; |
998 | u8 mode, old_mode, format, old_format; | 1227 | u8 mode, old_mode, format, old_format; |
999 | 1228 | ||
1000 | |||
1001 | /* bit rate */ | 1229 | /* bit rate */ |
1002 | old_mode = twl4030_read_reg_cache(codec, | 1230 | old_mode = twl4030_read_reg_cache(codec, |
1003 | TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; | 1231 | TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; |
@@ -1039,8 +1267,9 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1039 | 1267 | ||
1040 | if (mode != old_mode) { | 1268 | if (mode != old_mode) { |
1041 | /* change rate and set CODECPDZ */ | 1269 | /* change rate and set CODECPDZ */ |
1270 | twl4030_codec_enable(codec, 0); | ||
1042 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 1271 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); |
1043 | twl4030_set_codecpdz(codec); | 1272 | twl4030_codec_enable(codec, 1); |
1044 | } | 1273 | } |
1045 | 1274 | ||
1046 | /* sample size */ | 1275 | /* sample size */ |
@@ -1063,13 +1292,13 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1063 | if (format != old_format) { | 1292 | if (format != old_format) { |
1064 | 1293 | ||
1065 | /* clear CODECPDZ before changing format (codec requirement) */ | 1294 | /* clear CODECPDZ before changing format (codec requirement) */ |
1066 | twl4030_clear_codecpdz(codec); | 1295 | twl4030_codec_enable(codec, 0); |
1067 | 1296 | ||
1068 | /* change format */ | 1297 | /* change format */ |
1069 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1298 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); |
1070 | 1299 | ||
1071 | /* set CODECPDZ afterwards */ | 1300 | /* set CODECPDZ afterwards */ |
1072 | twl4030_set_codecpdz(codec); | 1301 | twl4030_codec_enable(codec, 1); |
1073 | } | 1302 | } |
1074 | return 0; | 1303 | return 0; |
1075 | } | 1304 | } |
@@ -1139,13 +1368,13 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1139 | if (format != old_format) { | 1368 | if (format != old_format) { |
1140 | 1369 | ||
1141 | /* clear CODECPDZ before changing format (codec requirement) */ | 1370 | /* clear CODECPDZ before changing format (codec requirement) */ |
1142 | twl4030_clear_codecpdz(codec); | 1371 | twl4030_codec_enable(codec, 0); |
1143 | 1372 | ||
1144 | /* change format */ | 1373 | /* change format */ |
1145 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1374 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); |
1146 | 1375 | ||
1147 | /* set CODECPDZ afterwards */ | 1376 | /* set CODECPDZ afterwards */ |
1148 | twl4030_set_codecpdz(codec); | 1377 | twl4030_codec_enable(codec, 1); |
1149 | } | 1378 | } |
1150 | 1379 | ||
1151 | return 0; | 1380 | return 0; |
@@ -1154,6 +1383,12 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1154 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) | 1383 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) |
1155 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) | 1384 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) |
1156 | 1385 | ||
1386 | static struct snd_soc_dai_ops twl4030_dai_ops = { | ||
1387 | .hw_params = twl4030_hw_params, | ||
1388 | .set_sysclk = twl4030_set_dai_sysclk, | ||
1389 | .set_fmt = twl4030_set_dai_fmt, | ||
1390 | }; | ||
1391 | |||
1157 | struct snd_soc_dai twl4030_dai = { | 1392 | struct snd_soc_dai twl4030_dai = { |
1158 | .name = "twl4030", | 1393 | .name = "twl4030", |
1159 | .playback = { | 1394 | .playback = { |
@@ -1168,18 +1403,14 @@ struct snd_soc_dai twl4030_dai = { | |||
1168 | .channels_max = 2, | 1403 | .channels_max = 2, |
1169 | .rates = TWL4030_RATES, | 1404 | .rates = TWL4030_RATES, |
1170 | .formats = TWL4030_FORMATS,}, | 1405 | .formats = TWL4030_FORMATS,}, |
1171 | .ops = { | 1406 | .ops = &twl4030_dai_ops, |
1172 | .hw_params = twl4030_hw_params, | ||
1173 | .set_sysclk = twl4030_set_dai_sysclk, | ||
1174 | .set_fmt = twl4030_set_dai_fmt, | ||
1175 | } | ||
1176 | }; | 1407 | }; |
1177 | EXPORT_SYMBOL_GPL(twl4030_dai); | 1408 | EXPORT_SYMBOL_GPL(twl4030_dai); |
1178 | 1409 | ||
1179 | static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) | 1410 | static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) |
1180 | { | 1411 | { |
1181 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1412 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
1182 | struct snd_soc_codec *codec = socdev->codec; | 1413 | struct snd_soc_codec *codec = socdev->card->codec; |
1183 | 1414 | ||
1184 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1415 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); |
1185 | 1416 | ||
@@ -1189,7 +1420,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) | |||
1189 | static int twl4030_resume(struct platform_device *pdev) | 1420 | static int twl4030_resume(struct platform_device *pdev) |
1190 | { | 1421 | { |
1191 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1422 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
1192 | struct snd_soc_codec *codec = socdev->codec; | 1423 | struct snd_soc_codec *codec = socdev->card->codec; |
1193 | 1424 | ||
1194 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1425 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
1195 | twl4030_set_bias_level(codec, codec->suspend_bias_level); | 1426 | twl4030_set_bias_level(codec, codec->suspend_bias_level); |
@@ -1203,7 +1434,7 @@ static int twl4030_resume(struct platform_device *pdev) | |||
1203 | 1434 | ||
1204 | static int twl4030_init(struct snd_soc_device *socdev) | 1435 | static int twl4030_init(struct snd_soc_device *socdev) |
1205 | { | 1436 | { |
1206 | struct snd_soc_codec *codec = socdev->codec; | 1437 | struct snd_soc_codec *codec = socdev->card->codec; |
1207 | int ret = 0; | 1438 | int ret = 0; |
1208 | 1439 | ||
1209 | printk(KERN_INFO "TWL4030 Audio Codec init \n"); | 1440 | printk(KERN_INFO "TWL4030 Audio Codec init \n"); |
@@ -1233,7 +1464,8 @@ static int twl4030_init(struct snd_soc_device *socdev) | |||
1233 | /* power on device */ | 1464 | /* power on device */ |
1234 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1465 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
1235 | 1466 | ||
1236 | twl4030_add_controls(codec); | 1467 | snd_soc_add_controls(codec, twl4030_snd_controls, |
1468 | ARRAY_SIZE(twl4030_snd_controls)); | ||
1237 | twl4030_add_widgets(codec); | 1469 | twl4030_add_widgets(codec); |
1238 | 1470 | ||
1239 | ret = snd_soc_init_card(socdev); | 1471 | ret = snd_soc_init_card(socdev); |
@@ -1258,12 +1490,20 @@ static int twl4030_probe(struct platform_device *pdev) | |||
1258 | { | 1490 | { |
1259 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1491 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
1260 | struct snd_soc_codec *codec; | 1492 | struct snd_soc_codec *codec; |
1493 | struct twl4030_priv *twl4030; | ||
1261 | 1494 | ||
1262 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 1495 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); |
1263 | if (codec == NULL) | 1496 | if (codec == NULL) |
1264 | return -ENOMEM; | 1497 | return -ENOMEM; |
1265 | 1498 | ||
1266 | socdev->codec = codec; | 1499 | twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); |
1500 | if (twl4030 == NULL) { | ||
1501 | kfree(codec); | ||
1502 | return -ENOMEM; | ||
1503 | } | ||
1504 | |||
1505 | codec->private_data = twl4030; | ||
1506 | socdev->card->codec = codec; | ||
1267 | mutex_init(&codec->mutex); | 1507 | mutex_init(&codec->mutex); |
1268 | INIT_LIST_HEAD(&codec->dapm_widgets); | 1508 | INIT_LIST_HEAD(&codec->dapm_widgets); |
1269 | INIT_LIST_HEAD(&codec->dapm_paths); | 1509 | INIT_LIST_HEAD(&codec->dapm_paths); |
@@ -1277,11 +1517,13 @@ static int twl4030_probe(struct platform_device *pdev) | |||
1277 | static int twl4030_remove(struct platform_device *pdev) | 1517 | static int twl4030_remove(struct platform_device *pdev) |
1278 | { | 1518 | { |
1279 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1519 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
1280 | struct snd_soc_codec *codec = socdev->codec; | 1520 | struct snd_soc_codec *codec = socdev->card->codec; |
1281 | 1521 | ||
1282 | printk(KERN_INFO "TWL4030 Audio Codec remove\n"); | 1522 | printk(KERN_INFO "TWL4030 Audio Codec remove\n"); |
1523 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1283 | snd_soc_free_pcms(socdev); | 1524 | snd_soc_free_pcms(socdev); |
1284 | snd_soc_dapm_free(socdev); | 1525 | snd_soc_dapm_free(socdev); |
1526 | kfree(codec->private_data); | ||
1285 | kfree(codec); | 1527 | kfree(codec); |
1286 | 1528 | ||
1287 | return 0; | 1529 | return 0; |