diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 1116 |
1 files changed, 849 insertions, 267 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index df7c8c281d2f..4dbb853eef5a 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -115,6 +115,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | |||
115 | 0x00, /* REG_VIBRA_PWM_SET (0x47) */ | 115 | 0x00, /* REG_VIBRA_PWM_SET (0x47) */ |
116 | 0x00, /* REG_ANAMIC_GAIN (0x48) */ | 116 | 0x00, /* REG_ANAMIC_GAIN (0x48) */ |
117 | 0x00, /* REG_MISC_SET_2 (0x49) */ | 117 | 0x00, /* REG_MISC_SET_2 (0x49) */ |
118 | 0x00, /* REG_SW_SHADOW (0x4A) - Shadow, non HW register */ | ||
118 | }; | 119 | }; |
119 | 120 | ||
120 | /* codec private data */ | 121 | /* codec private data */ |
@@ -125,6 +126,17 @@ struct twl4030_priv { | |||
125 | 126 | ||
126 | struct snd_pcm_substream *master_substream; | 127 | struct snd_pcm_substream *master_substream; |
127 | struct snd_pcm_substream *slave_substream; | 128 | struct snd_pcm_substream *slave_substream; |
129 | |||
130 | unsigned int configured; | ||
131 | unsigned int rate; | ||
132 | unsigned int sample_bits; | ||
133 | unsigned int channels; | ||
134 | |||
135 | unsigned int sysclk; | ||
136 | |||
137 | /* Headset output state handling */ | ||
138 | unsigned int hsl_enabled; | ||
139 | unsigned int hsr_enabled; | ||
128 | }; | 140 | }; |
129 | 141 | ||
130 | /* | 142 | /* |
@@ -161,7 +173,11 @@ static int twl4030_write(struct snd_soc_codec *codec, | |||
161 | unsigned int reg, unsigned int value) | 173 | unsigned int reg, unsigned int value) |
162 | { | 174 | { |
163 | twl4030_write_reg_cache(codec, reg, value); | 175 | twl4030_write_reg_cache(codec, reg, value); |
164 | return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); | 176 | if (likely(reg < TWL4030_REG_SW_SHADOW)) |
177 | return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, | ||
178 | reg); | ||
179 | else | ||
180 | return 0; | ||
165 | } | 181 | } |
166 | 182 | ||
167 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | 183 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) |
@@ -188,6 +204,7 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | |||
188 | 204 | ||
189 | static void twl4030_init_chip(struct snd_soc_codec *codec) | 205 | static void twl4030_init_chip(struct snd_soc_codec *codec) |
190 | { | 206 | { |
207 | u8 *cache = codec->reg_cache; | ||
191 | int i; | 208 | int i; |
192 | 209 | ||
193 | /* clear CODECPDZ prior to setting register defaults */ | 210 | /* clear CODECPDZ prior to setting register defaults */ |
@@ -195,7 +212,7 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
195 | 212 | ||
196 | /* set all audio section registers to reasonable defaults */ | 213 | /* set all audio section registers to reasonable defaults */ |
197 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) | 214 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) |
198 | twl4030_write(codec, i, twl4030_reg[i]); | 215 | twl4030_write(codec, i, cache[i]); |
199 | 216 | ||
200 | } | 217 | } |
201 | 218 | ||
@@ -232,7 +249,7 @@ static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) | |||
232 | TWL4030_REG_PRECKL_CTL); | 249 | TWL4030_REG_PRECKL_CTL); |
233 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL); | 250 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL); |
234 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 251 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
235 | reg_val & (~TWL4030_PRECKL_GAIN), | 252 | reg_val & (~TWL4030_PRECKR_GAIN), |
236 | TWL4030_REG_PRECKR_CTL); | 253 | TWL4030_REG_PRECKR_CTL); |
237 | 254 | ||
238 | /* Disable PLL */ | 255 | /* Disable PLL */ |
@@ -316,104 +333,60 @@ static void twl4030_power_down(struct snd_soc_codec *codec) | |||
316 | } | 333 | } |
317 | 334 | ||
318 | /* Earpiece */ | 335 | /* Earpiece */ |
319 | static const char *twl4030_earpiece_texts[] = | 336 | static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = { |
320 | {"Off", "DACL1", "DACL2", "DACR1"}; | 337 | SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0), |
321 | 338 | SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_EAR_CTL, 1, 1, 0), | |
322 | static const unsigned int twl4030_earpiece_values[] = | 339 | SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_EAR_CTL, 2, 1, 0), |
323 | {0x0, 0x1, 0x2, 0x4}; | 340 | SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_EAR_CTL, 3, 1, 0), |
324 | 341 | }; | |
325 | static const struct soc_enum twl4030_earpiece_enum = | ||
326 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, 0x7, | ||
327 | ARRAY_SIZE(twl4030_earpiece_texts), | ||
328 | twl4030_earpiece_texts, | ||
329 | twl4030_earpiece_values); | ||
330 | |||
331 | static const struct snd_kcontrol_new twl4030_dapm_earpiece_control = | ||
332 | SOC_DAPM_VALUE_ENUM("Route", twl4030_earpiece_enum); | ||
333 | 342 | ||
334 | /* PreDrive Left */ | 343 | /* PreDrive Left */ |
335 | static const char *twl4030_predrivel_texts[] = | 344 | static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls[] = { |
336 | {"Off", "DACL1", "DACL2", "DACR2"}; | 345 | SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDL_CTL, 0, 1, 0), |
337 | 346 | SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PREDL_CTL, 1, 1, 0), | |
338 | static const unsigned int twl4030_predrivel_values[] = | 347 | SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDL_CTL, 2, 1, 0), |
339 | {0x0, 0x1, 0x2, 0x4}; | 348 | SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDL_CTL, 3, 1, 0), |
340 | 349 | }; | |
341 | static const struct soc_enum twl4030_predrivel_enum = | ||
342 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, 0x7, | ||
343 | ARRAY_SIZE(twl4030_predrivel_texts), | ||
344 | twl4030_predrivel_texts, | ||
345 | twl4030_predrivel_values); | ||
346 | |||
347 | static const struct snd_kcontrol_new twl4030_dapm_predrivel_control = | ||
348 | SOC_DAPM_VALUE_ENUM("Route", twl4030_predrivel_enum); | ||
349 | 350 | ||
350 | /* PreDrive Right */ | 351 | /* PreDrive Right */ |
351 | static const char *twl4030_predriver_texts[] = | 352 | static const struct snd_kcontrol_new twl4030_dapm_predriver_controls[] = { |
352 | {"Off", "DACR1", "DACR2", "DACL2"}; | 353 | SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDR_CTL, 0, 1, 0), |
353 | 354 | SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PREDR_CTL, 1, 1, 0), | |
354 | static const unsigned int twl4030_predriver_values[] = | 355 | SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDR_CTL, 2, 1, 0), |
355 | {0x0, 0x1, 0x2, 0x4}; | 356 | SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDR_CTL, 3, 1, 0), |
356 | 357 | }; | |
357 | static const struct soc_enum twl4030_predriver_enum = | ||
358 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, 0x7, | ||
359 | ARRAY_SIZE(twl4030_predriver_texts), | ||
360 | twl4030_predriver_texts, | ||
361 | twl4030_predriver_values); | ||
362 | |||
363 | static const struct snd_kcontrol_new twl4030_dapm_predriver_control = | ||
364 | SOC_DAPM_VALUE_ENUM("Route", twl4030_predriver_enum); | ||
365 | 358 | ||
366 | /* Headset Left */ | 359 | /* Headset Left */ |
367 | static const char *twl4030_hsol_texts[] = | 360 | static const struct snd_kcontrol_new twl4030_dapm_hsol_controls[] = { |
368 | {"Off", "DACL1", "DACL2"}; | 361 | SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 0, 1, 0), |
369 | 362 | SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_HS_SEL, 1, 1, 0), | |
370 | static const struct soc_enum twl4030_hsol_enum = | 363 | SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_HS_SEL, 2, 1, 0), |
371 | SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1, | 364 | }; |
372 | ARRAY_SIZE(twl4030_hsol_texts), | ||
373 | twl4030_hsol_texts); | ||
374 | |||
375 | static const struct snd_kcontrol_new twl4030_dapm_hsol_control = | ||
376 | SOC_DAPM_ENUM("Route", twl4030_hsol_enum); | ||
377 | 365 | ||
378 | /* Headset Right */ | 366 | /* Headset Right */ |
379 | static const char *twl4030_hsor_texts[] = | 367 | static const struct snd_kcontrol_new twl4030_dapm_hsor_controls[] = { |
380 | {"Off", "DACR1", "DACR2"}; | 368 | SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 3, 1, 0), |
381 | 369 | SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_HS_SEL, 4, 1, 0), | |
382 | static const struct soc_enum twl4030_hsor_enum = | 370 | SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_HS_SEL, 5, 1, 0), |
383 | SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4, | 371 | }; |
384 | ARRAY_SIZE(twl4030_hsor_texts), | ||
385 | twl4030_hsor_texts); | ||
386 | |||
387 | static const struct snd_kcontrol_new twl4030_dapm_hsor_control = | ||
388 | SOC_DAPM_ENUM("Route", twl4030_hsor_enum); | ||
389 | 372 | ||
390 | /* Carkit Left */ | 373 | /* Carkit Left */ |
391 | static const char *twl4030_carkitl_texts[] = | 374 | static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls[] = { |
392 | {"Off", "DACL1", "DACL2"}; | 375 | SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKL_CTL, 0, 1, 0), |
393 | 376 | SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PRECKL_CTL, 1, 1, 0), | |
394 | static const struct soc_enum twl4030_carkitl_enum = | 377 | SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PRECKL_CTL, 2, 1, 0), |
395 | SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1, | 378 | }; |
396 | ARRAY_SIZE(twl4030_carkitl_texts), | ||
397 | twl4030_carkitl_texts); | ||
398 | |||
399 | static const struct snd_kcontrol_new twl4030_dapm_carkitl_control = | ||
400 | SOC_DAPM_ENUM("Route", twl4030_carkitl_enum); | ||
401 | 379 | ||
402 | /* Carkit Right */ | 380 | /* Carkit Right */ |
403 | static const char *twl4030_carkitr_texts[] = | 381 | static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = { |
404 | {"Off", "DACR1", "DACR2"}; | 382 | SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKR_CTL, 0, 1, 0), |
405 | 383 | SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PRECKR_CTL, 1, 1, 0), | |
406 | static const struct soc_enum twl4030_carkitr_enum = | 384 | SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PRECKR_CTL, 2, 1, 0), |
407 | SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1, | 385 | }; |
408 | ARRAY_SIZE(twl4030_carkitr_texts), | ||
409 | twl4030_carkitr_texts); | ||
410 | |||
411 | static const struct snd_kcontrol_new twl4030_dapm_carkitr_control = | ||
412 | SOC_DAPM_ENUM("Route", twl4030_carkitr_enum); | ||
413 | 386 | ||
414 | /* Handsfree Left */ | 387 | /* Handsfree Left */ |
415 | static const char *twl4030_handsfreel_texts[] = | 388 | static const char *twl4030_handsfreel_texts[] = |
416 | {"Voice", "DACL1", "DACL2", "DACR2"}; | 389 | {"Voice", "AudioL1", "AudioL2", "AudioR2"}; |
417 | 390 | ||
418 | static const struct soc_enum twl4030_handsfreel_enum = | 391 | static const struct soc_enum twl4030_handsfreel_enum = |
419 | SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0, | 392 | SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0, |
@@ -423,9 +396,13 @@ static const struct soc_enum twl4030_handsfreel_enum = | |||
423 | static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control = | 396 | static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control = |
424 | SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum); | 397 | SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum); |
425 | 398 | ||
399 | /* Handsfree Left virtual mute */ | ||
400 | static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control = | ||
401 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 0, 1, 0); | ||
402 | |||
426 | /* Handsfree Right */ | 403 | /* Handsfree Right */ |
427 | static const char *twl4030_handsfreer_texts[] = | 404 | static const char *twl4030_handsfreer_texts[] = |
428 | {"Voice", "DACR1", "DACR2", "DACL2"}; | 405 | {"Voice", "AudioR1", "AudioR2", "AudioL2"}; |
429 | 406 | ||
430 | static const struct soc_enum twl4030_handsfreer_enum = | 407 | static const struct soc_enum twl4030_handsfreer_enum = |
431 | SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0, | 408 | SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0, |
@@ -435,37 +412,48 @@ static const struct soc_enum twl4030_handsfreer_enum = | |||
435 | static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control = | 412 | static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control = |
436 | SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); | 413 | SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); |
437 | 414 | ||
438 | /* Left analog microphone selection */ | 415 | /* Handsfree Right virtual mute */ |
439 | static const char *twl4030_analoglmic_texts[] = | 416 | static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control = |
440 | {"Off", "Main mic", "Headset mic", "AUXL", "Carkit mic"}; | 417 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 1, 1, 0); |
441 | 418 | ||
442 | static const unsigned int twl4030_analoglmic_values[] = | 419 | /* Vibra */ |
443 | {0x0, 0x1, 0x2, 0x4, 0x8}; | 420 | /* Vibra audio path selection */ |
421 | static const char *twl4030_vibra_texts[] = | ||
422 | {"AudioL1", "AudioR1", "AudioL2", "AudioR2"}; | ||
444 | 423 | ||
445 | static const struct soc_enum twl4030_analoglmic_enum = | 424 | static const struct soc_enum twl4030_vibra_enum = |
446 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, 0xf, | 425 | SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 2, |
447 | ARRAY_SIZE(twl4030_analoglmic_texts), | 426 | ARRAY_SIZE(twl4030_vibra_texts), |
448 | twl4030_analoglmic_texts, | 427 | twl4030_vibra_texts); |
449 | twl4030_analoglmic_values); | ||
450 | 428 | ||
451 | static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control = | 429 | static const struct snd_kcontrol_new twl4030_dapm_vibra_control = |
452 | SOC_DAPM_VALUE_ENUM("Route", twl4030_analoglmic_enum); | 430 | SOC_DAPM_ENUM("Route", twl4030_vibra_enum); |
453 | 431 | ||
454 | /* Right analog microphone selection */ | 432 | /* Vibra path selection: local vibrator (PWM) or audio driven */ |
455 | static const char *twl4030_analogrmic_texts[] = | 433 | static const char *twl4030_vibrapath_texts[] = |
456 | {"Off", "Sub mic", "AUXR"}; | 434 | {"Local vibrator", "Audio"}; |
457 | 435 | ||
458 | static const unsigned int twl4030_analogrmic_values[] = | 436 | static const struct soc_enum twl4030_vibrapath_enum = |
459 | {0x0, 0x1, 0x4}; | 437 | SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 4, |
438 | ARRAY_SIZE(twl4030_vibrapath_texts), | ||
439 | twl4030_vibrapath_texts); | ||
460 | 440 | ||
461 | static const struct soc_enum twl4030_analogrmic_enum = | 441 | static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control = |
462 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, 0x5, | 442 | SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum); |
463 | ARRAY_SIZE(twl4030_analogrmic_texts), | ||
464 | twl4030_analogrmic_texts, | ||
465 | twl4030_analogrmic_values); | ||
466 | 443 | ||
467 | static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control = | 444 | /* Left analog microphone selection */ |
468 | SOC_DAPM_VALUE_ENUM("Route", twl4030_analogrmic_enum); | 445 | static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = { |
446 | SOC_DAPM_SINGLE("Main mic", TWL4030_REG_ANAMICL, 0, 1, 0), | ||
447 | SOC_DAPM_SINGLE("Headset mic", TWL4030_REG_ANAMICL, 1, 1, 0), | ||
448 | SOC_DAPM_SINGLE("AUXL", TWL4030_REG_ANAMICL, 2, 1, 0), | ||
449 | SOC_DAPM_SINGLE("Carkit mic", TWL4030_REG_ANAMICL, 3, 1, 0), | ||
450 | }; | ||
451 | |||
452 | /* Right analog microphone selection */ | ||
453 | static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = { | ||
454 | SOC_DAPM_SINGLE("Sub mic", TWL4030_REG_ANAMICR, 0, 1, 0), | ||
455 | SOC_DAPM_SINGLE("AUXR", TWL4030_REG_ANAMICR, 2, 1, 0), | ||
456 | }; | ||
469 | 457 | ||
470 | /* TX1 L/R Analog/Digital microphone selection */ | 458 | /* TX1 L/R Analog/Digital microphone selection */ |
471 | static const char *twl4030_micpathtx1_texts[] = | 459 | static const char *twl4030_micpathtx1_texts[] = |
@@ -507,6 +495,10 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control = | |||
507 | static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = | 495 | static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = |
508 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0); | 496 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0); |
509 | 497 | ||
498 | /* Analog bypass for Voice */ | ||
499 | static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = | ||
500 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); | ||
501 | |||
510 | /* Digital bypass gain, 0 mutes the bypass */ | 502 | /* Digital bypass gain, 0 mutes the bypass */ |
511 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { | 503 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { |
512 | TLV_DB_RANGE_HEAD(2), | 504 | TLV_DB_RANGE_HEAD(2), |
@@ -526,6 +518,18 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control = | |||
526 | TWL4030_REG_ATX2ARXPGA, 0, 7, 0, | 518 | TWL4030_REG_ATX2ARXPGA, 0, 7, 0, |
527 | twl4030_dapm_dbypass_tlv); | 519 | twl4030_dapm_dbypass_tlv); |
528 | 520 | ||
521 | /* | ||
522 | * Voice Sidetone GAIN volume control: | ||
523 | * from -51 to -10 dB in 1 dB steps (mute instead of -51 dB) | ||
524 | */ | ||
525 | static DECLARE_TLV_DB_SCALE(twl4030_dapm_dbypassv_tlv, -5100, 100, 1); | ||
526 | |||
527 | /* Digital bypass voice: sidetone (VUL -> VDL)*/ | ||
528 | static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control = | ||
529 | SOC_DAPM_SINGLE_TLV("Volume", | ||
530 | TWL4030_REG_VSTPGA, 0, 0x29, 0, | ||
531 | twl4030_dapm_dbypassv_tlv); | ||
532 | |||
529 | static int micpath_event(struct snd_soc_dapm_widget *w, | 533 | static int micpath_event(struct snd_soc_dapm_widget *w, |
530 | struct snd_kcontrol *kcontrol, int event) | 534 | struct snd_kcontrol *kcontrol, int event) |
531 | { | 535 | { |
@@ -556,63 +560,143 @@ static int micpath_event(struct snd_soc_dapm_widget *w, | |||
556 | return 0; | 560 | return 0; |
557 | } | 561 | } |
558 | 562 | ||
559 | static int handsfree_event(struct snd_soc_dapm_widget *w, | 563 | static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp) |
560 | struct snd_kcontrol *kcontrol, int event) | ||
561 | { | 564 | { |
562 | struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value; | ||
563 | unsigned char hs_ctl; | 565 | unsigned char hs_ctl; |
564 | 566 | ||
565 | hs_ctl = twl4030_read_reg_cache(w->codec, e->reg); | 567 | hs_ctl = twl4030_read_reg_cache(codec, reg); |
566 | 568 | ||
567 | if (hs_ctl & TWL4030_HF_CTL_REF_EN) { | 569 | if (ramp) { |
570 | /* HF ramp-up */ | ||
571 | hs_ctl |= TWL4030_HF_CTL_REF_EN; | ||
572 | twl4030_write(codec, reg, hs_ctl); | ||
573 | udelay(10); | ||
568 | hs_ctl |= TWL4030_HF_CTL_RAMP_EN; | 574 | hs_ctl |= TWL4030_HF_CTL_RAMP_EN; |
569 | twl4030_write(w->codec, e->reg, hs_ctl); | 575 | twl4030_write(codec, reg, hs_ctl); |
576 | udelay(40); | ||
570 | hs_ctl |= TWL4030_HF_CTL_LOOP_EN; | 577 | hs_ctl |= TWL4030_HF_CTL_LOOP_EN; |
571 | twl4030_write(w->codec, e->reg, hs_ctl); | ||
572 | hs_ctl |= TWL4030_HF_CTL_HB_EN; | 578 | hs_ctl |= TWL4030_HF_CTL_HB_EN; |
573 | twl4030_write(w->codec, e->reg, hs_ctl); | 579 | twl4030_write(codec, reg, hs_ctl); |
574 | } else { | 580 | } else { |
575 | hs_ctl &= ~(TWL4030_HF_CTL_RAMP_EN | TWL4030_HF_CTL_LOOP_EN | 581 | /* HF ramp-down */ |
576 | | TWL4030_HF_CTL_HB_EN); | 582 | hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN; |
577 | twl4030_write(w->codec, e->reg, hs_ctl); | 583 | hs_ctl &= ~TWL4030_HF_CTL_HB_EN; |
584 | twl4030_write(codec, reg, hs_ctl); | ||
585 | hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN; | ||
586 | twl4030_write(codec, reg, hs_ctl); | ||
587 | udelay(40); | ||
588 | hs_ctl &= ~TWL4030_HF_CTL_REF_EN; | ||
589 | twl4030_write(codec, reg, hs_ctl); | ||
578 | } | 590 | } |
591 | } | ||
579 | 592 | ||
593 | static int handsfreelpga_event(struct snd_soc_dapm_widget *w, | ||
594 | struct snd_kcontrol *kcontrol, int event) | ||
595 | { | ||
596 | switch (event) { | ||
597 | case SND_SOC_DAPM_POST_PMU: | ||
598 | handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 1); | ||
599 | break; | ||
600 | case SND_SOC_DAPM_POST_PMD: | ||
601 | handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 0); | ||
602 | break; | ||
603 | } | ||
580 | return 0; | 604 | return 0; |
581 | } | 605 | } |
582 | 606 | ||
583 | static int headsetl_event(struct snd_soc_dapm_widget *w, | 607 | static int handsfreerpga_event(struct snd_soc_dapm_widget *w, |
584 | struct snd_kcontrol *kcontrol, int event) | 608 | struct snd_kcontrol *kcontrol, int event) |
585 | { | 609 | { |
610 | switch (event) { | ||
611 | case SND_SOC_DAPM_POST_PMU: | ||
612 | handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 1); | ||
613 | break; | ||
614 | case SND_SOC_DAPM_POST_PMD: | ||
615 | handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 0); | ||
616 | break; | ||
617 | } | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static void headset_ramp(struct snd_soc_codec *codec, int ramp) | ||
622 | { | ||
586 | unsigned char hs_gain, hs_pop; | 623 | unsigned char hs_gain, hs_pop; |
624 | struct twl4030_priv *twl4030 = codec->private_data; | ||
625 | /* Base values for ramp delay calculation: 2^19 - 2^26 */ | ||
626 | unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, | ||
627 | 8388608, 16777216, 33554432, 67108864}; | ||
587 | 628 | ||
588 | /* Save the current volume */ | 629 | hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET); |
589 | hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET); | 630 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); |
590 | hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET); | ||
591 | 631 | ||
592 | switch (event) { | 632 | if (ramp) { |
593 | case SND_SOC_DAPM_POST_PMU: | 633 | /* Headset ramp-up according to the TRM */ |
594 | /* Do the anti-pop/bias ramp enable according to the TRM */ | ||
595 | hs_pop |= TWL4030_VMID_EN; | 634 | hs_pop |= TWL4030_VMID_EN; |
596 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 635 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
597 | /* Is this needed? Can we just use whatever gain here? */ | 636 | twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain); |
598 | twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, | ||
599 | (hs_gain & (~0x0f)) | 0x0a); | ||
600 | hs_pop |= TWL4030_RAMP_EN; | 637 | hs_pop |= TWL4030_RAMP_EN; |
601 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 638 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
602 | 639 | } else { | |
603 | /* Restore the original volume */ | 640 | /* Headset ramp-down _not_ according to |
604 | twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain); | 641 | * the TRM, but in a way that it is working */ |
605 | break; | ||
606 | case SND_SOC_DAPM_POST_PMD: | ||
607 | /* Do the anti-pop/bias ramp disable according to the TRM */ | ||
608 | hs_pop &= ~TWL4030_RAMP_EN; | 642 | hs_pop &= ~TWL4030_RAMP_EN; |
609 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 643 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
644 | /* Wait ramp delay time + 1, so the VMID can settle */ | ||
645 | mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / | ||
646 | twl4030->sysclk) + 1); | ||
610 | /* Bypass the reg_cache to mute the headset */ | 647 | /* Bypass the reg_cache to mute the headset */ |
611 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 648 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
612 | hs_gain & (~0x0f), | 649 | hs_gain & (~0x0f), |
613 | TWL4030_REG_HS_GAIN_SET); | 650 | TWL4030_REG_HS_GAIN_SET); |
651 | |||
614 | hs_pop &= ~TWL4030_VMID_EN; | 652 | hs_pop &= ~TWL4030_VMID_EN; |
615 | twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 653 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
654 | } | ||
655 | } | ||
656 | |||
657 | static int headsetlpga_event(struct snd_soc_dapm_widget *w, | ||
658 | struct snd_kcontrol *kcontrol, int event) | ||
659 | { | ||
660 | struct twl4030_priv *twl4030 = w->codec->private_data; | ||
661 | |||
662 | switch (event) { | ||
663 | case SND_SOC_DAPM_POST_PMU: | ||
664 | /* Do the ramp-up only once */ | ||
665 | if (!twl4030->hsr_enabled) | ||
666 | headset_ramp(w->codec, 1); | ||
667 | |||
668 | twl4030->hsl_enabled = 1; | ||
669 | break; | ||
670 | case SND_SOC_DAPM_POST_PMD: | ||
671 | /* Do the ramp-down only if both headsetL/R is disabled */ | ||
672 | if (!twl4030->hsr_enabled) | ||
673 | headset_ramp(w->codec, 0); | ||
674 | |||
675 | twl4030->hsl_enabled = 0; | ||
676 | break; | ||
677 | } | ||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | static int headsetrpga_event(struct snd_soc_dapm_widget *w, | ||
682 | struct snd_kcontrol *kcontrol, int event) | ||
683 | { | ||
684 | struct twl4030_priv *twl4030 = w->codec->private_data; | ||
685 | |||
686 | switch (event) { | ||
687 | case SND_SOC_DAPM_POST_PMU: | ||
688 | /* Do the ramp-up only once */ | ||
689 | if (!twl4030->hsl_enabled) | ||
690 | headset_ramp(w->codec, 1); | ||
691 | |||
692 | twl4030->hsr_enabled = 1; | ||
693 | break; | ||
694 | case SND_SOC_DAPM_POST_PMD: | ||
695 | /* Do the ramp-down only if both headsetL/R is disabled */ | ||
696 | if (!twl4030->hsl_enabled) | ||
697 | headset_ramp(w->codec, 0); | ||
698 | |||
699 | twl4030->hsr_enabled = 0; | ||
616 | break; | 700 | break; |
617 | } | 701 | } |
618 | return 0; | 702 | return 0; |
@@ -624,7 +708,7 @@ static int bypass_event(struct snd_soc_dapm_widget *w, | |||
624 | struct soc_mixer_control *m = | 708 | struct soc_mixer_control *m = |
625 | (struct soc_mixer_control *)w->kcontrols->private_value; | 709 | (struct soc_mixer_control *)w->kcontrols->private_value; |
626 | struct twl4030_priv *twl4030 = w->codec->private_data; | 710 | struct twl4030_priv *twl4030 = w->codec->private_data; |
627 | unsigned char reg; | 711 | unsigned char reg, misc; |
628 | 712 | ||
629 | reg = twl4030_read_reg_cache(w->codec, m->reg); | 713 | reg = twl4030_read_reg_cache(w->codec, m->reg); |
630 | 714 | ||
@@ -636,14 +720,34 @@ static int bypass_event(struct snd_soc_dapm_widget *w, | |||
636 | else | 720 | else |
637 | twl4030->bypass_state &= | 721 | twl4030->bypass_state &= |
638 | ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); | 722 | ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); |
723 | } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) { | ||
724 | /* Analog voice bypass */ | ||
725 | if (reg & (1 << m->shift)) | ||
726 | twl4030->bypass_state |= (1 << 4); | ||
727 | else | ||
728 | twl4030->bypass_state &= ~(1 << 4); | ||
729 | } else if (m->reg == TWL4030_REG_VSTPGA) { | ||
730 | /* Voice digital bypass */ | ||
731 | if (reg) | ||
732 | twl4030->bypass_state |= (1 << 5); | ||
733 | else | ||
734 | twl4030->bypass_state &= ~(1 << 5); | ||
639 | } else { | 735 | } else { |
640 | /* Digital bypass */ | 736 | /* Digital bypass */ |
641 | if (reg & (0x7 << m->shift)) | 737 | if (reg & (0x7 << m->shift)) |
642 | twl4030->bypass_state |= (1 << (m->shift ? 5 : 4)); | 738 | twl4030->bypass_state |= (1 << (m->shift ? 7 : 6)); |
643 | else | 739 | else |
644 | twl4030->bypass_state &= ~(1 << (m->shift ? 5 : 4)); | 740 | twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6)); |
645 | } | 741 | } |
646 | 742 | ||
743 | /* Enable master analog loopback mode if any analog switch is enabled*/ | ||
744 | misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1); | ||
745 | if (twl4030->bypass_state & 0x1F) | ||
746 | misc |= TWL4030_FMLOOP_EN; | ||
747 | else | ||
748 | misc &= ~TWL4030_FMLOOP_EN; | ||
749 | twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc); | ||
750 | |||
647 | if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { | 751 | if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { |
648 | if (twl4030->bypass_state) | 752 | if (twl4030->bypass_state) |
649 | twl4030_codec_mute(w->codec, 0); | 753 | twl4030_codec_mute(w->codec, 0); |
@@ -810,6 +914,48 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, | |||
810 | return err; | 914 | return err; |
811 | } | 915 | } |
812 | 916 | ||
917 | /* Codec operation modes */ | ||
918 | static const char *twl4030_op_modes_texts[] = { | ||
919 | "Option 2 (voice/audio)", "Option 1 (audio)" | ||
920 | }; | ||
921 | |||
922 | static const struct soc_enum twl4030_op_modes_enum = | ||
923 | SOC_ENUM_SINGLE(TWL4030_REG_CODEC_MODE, 0, | ||
924 | ARRAY_SIZE(twl4030_op_modes_texts), | ||
925 | twl4030_op_modes_texts); | ||
926 | |||
927 | int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, | ||
928 | struct snd_ctl_elem_value *ucontrol) | ||
929 | { | ||
930 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
931 | struct twl4030_priv *twl4030 = codec->private_data; | ||
932 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | ||
933 | unsigned short val; | ||
934 | unsigned short mask, bitmask; | ||
935 | |||
936 | if (twl4030->configured) { | ||
937 | printk(KERN_ERR "twl4030 operation mode cannot be " | ||
938 | "changed on-the-fly\n"); | ||
939 | return -EBUSY; | ||
940 | } | ||
941 | |||
942 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) | ||
943 | ; | ||
944 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | ||
945 | return -EINVAL; | ||
946 | |||
947 | val = ucontrol->value.enumerated.item[0] << e->shift_l; | ||
948 | mask = (bitmask - 1) << e->shift_l; | ||
949 | if (e->shift_l != e->shift_r) { | ||
950 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | ||
951 | return -EINVAL; | ||
952 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; | ||
953 | mask |= (bitmask - 1) << e->shift_r; | ||
954 | } | ||
955 | |||
956 | return snd_soc_update_bits(codec, e->reg, mask, val); | ||
957 | } | ||
958 | |||
813 | /* | 959 | /* |
814 | * FGAIN volume control: | 960 | * FGAIN volume control: |
815 | * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) | 961 | * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) |
@@ -824,6 +970,12 @@ static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1); | |||
824 | static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0); | 970 | static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0); |
825 | 971 | ||
826 | /* | 972 | /* |
973 | * Voice Downlink GAIN volume control: | ||
974 | * from -37 to 12 dB in 1 dB steps (mute instead of -37 dB) | ||
975 | */ | ||
976 | static DECLARE_TLV_DB_SCALE(digital_voice_downlink_tlv, -3700, 100, 1); | ||
977 | |||
978 | /* | ||
827 | * Analog playback gain | 979 | * Analog playback gain |
828 | * -24 dB to 12 dB in 2 dB steps | 980 | * -24 dB to 12 dB in 2 dB steps |
829 | */ | 981 | */ |
@@ -864,7 +1016,32 @@ static const struct soc_enum twl4030_rampdelay_enum = | |||
864 | ARRAY_SIZE(twl4030_rampdelay_texts), | 1016 | ARRAY_SIZE(twl4030_rampdelay_texts), |
865 | twl4030_rampdelay_texts); | 1017 | twl4030_rampdelay_texts); |
866 | 1018 | ||
1019 | /* Vibra H-bridge direction mode */ | ||
1020 | static const char *twl4030_vibradirmode_texts[] = { | ||
1021 | "Vibra H-bridge direction", "Audio data MSB", | ||
1022 | }; | ||
1023 | |||
1024 | static const struct soc_enum twl4030_vibradirmode_enum = | ||
1025 | SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 5, | ||
1026 | ARRAY_SIZE(twl4030_vibradirmode_texts), | ||
1027 | twl4030_vibradirmode_texts); | ||
1028 | |||
1029 | /* Vibra H-bridge direction */ | ||
1030 | static const char *twl4030_vibradir_texts[] = { | ||
1031 | "Positive polarity", "Negative polarity", | ||
1032 | }; | ||
1033 | |||
1034 | static const struct soc_enum twl4030_vibradir_enum = | ||
1035 | SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 1, | ||
1036 | ARRAY_SIZE(twl4030_vibradir_texts), | ||
1037 | twl4030_vibradir_texts); | ||
1038 | |||
867 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { | 1039 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { |
1040 | /* Codec operation mode control */ | ||
1041 | SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum, | ||
1042 | snd_soc_get_enum_double, | ||
1043 | snd_soc_put_twl4030_opmode_enum_double), | ||
1044 | |||
868 | /* Common playback gain controls */ | 1045 | /* Common playback gain controls */ |
869 | SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", | 1046 | SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", |
870 | TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA, | 1047 | TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA, |
@@ -893,6 +1070,16 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { | |||
893 | TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL, | 1070 | TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL, |
894 | 1, 1, 0), | 1071 | 1, 1, 0), |
895 | 1072 | ||
1073 | /* Common voice downlink gain controls */ | ||
1074 | SOC_SINGLE_TLV("DAC Voice Digital Downlink Volume", | ||
1075 | TWL4030_REG_VRXPGA, 0, 0x31, 0, digital_voice_downlink_tlv), | ||
1076 | |||
1077 | SOC_SINGLE_TLV("DAC Voice Analog Downlink Volume", | ||
1078 | TWL4030_REG_VDL_APGA_CTL, 3, 0x12, 1, analog_tlv), | ||
1079 | |||
1080 | SOC_SINGLE("DAC Voice Analog Downlink Switch", | ||
1081 | TWL4030_REG_VDL_APGA_CTL, 1, 1, 0), | ||
1082 | |||
896 | /* Separate output gain controls */ | 1083 | /* Separate output gain controls */ |
897 | SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume", | 1084 | SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume", |
898 | TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL, | 1085 | TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL, |
@@ -920,6 +1107,9 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { | |||
920 | 0, 3, 5, 0, input_gain_tlv), | 1107 | 0, 3, 5, 0, input_gain_tlv), |
921 | 1108 | ||
922 | SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), | 1109 | SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), |
1110 | |||
1111 | SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum), | ||
1112 | SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum), | ||
923 | }; | 1113 | }; |
924 | 1114 | ||
925 | static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | 1115 | static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { |
@@ -947,26 +1137,19 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
947 | SND_SOC_DAPM_OUTPUT("CARKITR"), | 1137 | SND_SOC_DAPM_OUTPUT("CARKITR"), |
948 | SND_SOC_DAPM_OUTPUT("HFL"), | 1138 | SND_SOC_DAPM_OUTPUT("HFL"), |
949 | SND_SOC_DAPM_OUTPUT("HFR"), | 1139 | SND_SOC_DAPM_OUTPUT("HFR"), |
1140 | SND_SOC_DAPM_OUTPUT("VIBRA"), | ||
950 | 1141 | ||
951 | /* DACs */ | 1142 | /* DACs */ |
952 | SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback", | 1143 | SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback", |
953 | SND_SOC_NOPM, 0, 0), | 1144 | SND_SOC_NOPM, 0, 0), |
954 | SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback", | 1145 | SND_SOC_DAPM_DAC("DAC Left1", "Left Front HiFi Playback", |
955 | SND_SOC_NOPM, 0, 0), | 1146 | SND_SOC_NOPM, 0, 0), |
956 | SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback", | 1147 | SND_SOC_DAPM_DAC("DAC Right2", "Right Rear HiFi Playback", |
957 | SND_SOC_NOPM, 0, 0), | 1148 | SND_SOC_NOPM, 0, 0), |
958 | SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback", | 1149 | SND_SOC_DAPM_DAC("DAC Left2", "Left Rear HiFi Playback", |
1150 | SND_SOC_NOPM, 0, 0), | ||
1151 | SND_SOC_DAPM_DAC("DAC Voice", "Voice Playback", | ||
959 | SND_SOC_NOPM, 0, 0), | 1152 | SND_SOC_NOPM, 0, 0), |
960 | |||
961 | /* Analog PGAs */ | ||
962 | SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL, | ||
963 | 0, 0, NULL, 0), | ||
964 | SND_SOC_DAPM_PGA("ARXL1_APGA", TWL4030_REG_ARXL1_APGA_CTL, | ||
965 | 0, 0, NULL, 0), | ||
966 | SND_SOC_DAPM_PGA("ARXR2_APGA", TWL4030_REG_ARXR2_APGA_CTL, | ||
967 | 0, 0, NULL, 0), | ||
968 | SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL, | ||
969 | 0, 0, NULL, 0), | ||
970 | 1153 | ||
971 | /* Analog bypasses */ | 1154 | /* Analog bypasses */ |
972 | SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, | 1155 | SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, |
@@ -981,6 +1164,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
981 | SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, | 1164 | SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, |
982 | &twl4030_dapm_abypassl2_control, | 1165 | &twl4030_dapm_abypassl2_control, |
983 | bypass_event, SND_SOC_DAPM_POST_REG), | 1166 | bypass_event, SND_SOC_DAPM_POST_REG), |
1167 | SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, | ||
1168 | &twl4030_dapm_abypassv_control, | ||
1169 | bypass_event, SND_SOC_DAPM_POST_REG), | ||
984 | 1170 | ||
985 | /* Digital bypasses */ | 1171 | /* Digital bypasses */ |
986 | SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0, | 1172 | SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0, |
@@ -989,43 +1175,88 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
989 | SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0, | 1175 | SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0, |
990 | &twl4030_dapm_dbypassr_control, bypass_event, | 1176 | &twl4030_dapm_dbypassr_control, bypass_event, |
991 | SND_SOC_DAPM_POST_REG), | 1177 | SND_SOC_DAPM_POST_REG), |
1178 | SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, | ||
1179 | &twl4030_dapm_dbypassv_control, bypass_event, | ||
1180 | SND_SOC_DAPM_POST_REG), | ||
992 | 1181 | ||
993 | SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL, | 1182 | /* Digital mixers, power control for the physical DACs */ |
994 | 0, 0, NULL, 0), | 1183 | SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer", |
995 | SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL, | 1184 | TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0), |
996 | 1, 0, NULL, 0), | 1185 | SND_SOC_DAPM_MIXER("Digital L1 Playback Mixer", |
997 | SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL, | 1186 | TWL4030_REG_AVDAC_CTL, 1, 0, NULL, 0), |
998 | 2, 0, NULL, 0), | 1187 | SND_SOC_DAPM_MIXER("Digital R2 Playback Mixer", |
999 | SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL, | 1188 | TWL4030_REG_AVDAC_CTL, 2, 0, NULL, 0), |
1000 | 3, 0, NULL, 0), | 1189 | SND_SOC_DAPM_MIXER("Digital L2 Playback Mixer", |
1001 | 1190 | TWL4030_REG_AVDAC_CTL, 3, 0, NULL, 0), | |
1002 | /* Output MUX controls */ | 1191 | SND_SOC_DAPM_MIXER("Digital Voice Playback Mixer", |
1192 | TWL4030_REG_AVDAC_CTL, 4, 0, NULL, 0), | ||
1193 | |||
1194 | /* Analog mixers, power control for the physical PGAs */ | ||
1195 | SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", | ||
1196 | TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0), | ||
1197 | SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", | ||
1198 | TWL4030_REG_ARXL1_APGA_CTL, 0, 0, NULL, 0), | ||
1199 | SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", | ||
1200 | TWL4030_REG_ARXR2_APGA_CTL, 0, 0, NULL, 0), | ||
1201 | SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", | ||
1202 | TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0), | ||
1203 | SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer", | ||
1204 | TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0), | ||
1205 | |||
1206 | /* Output MIXER controls */ | ||
1003 | /* Earpiece */ | 1207 | /* Earpiece */ |
1004 | SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0, | 1208 | SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, |
1005 | &twl4030_dapm_earpiece_control), | 1209 | &twl4030_dapm_earpiece_controls[0], |
1210 | ARRAY_SIZE(twl4030_dapm_earpiece_controls)), | ||
1006 | /* PreDrivL/R */ | 1211 | /* PreDrivL/R */ |
1007 | SND_SOC_DAPM_VALUE_MUX("PredriveL Mux", SND_SOC_NOPM, 0, 0, | 1212 | SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0, |
1008 | &twl4030_dapm_predrivel_control), | 1213 | &twl4030_dapm_predrivel_controls[0], |
1009 | SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0, | 1214 | ARRAY_SIZE(twl4030_dapm_predrivel_controls)), |
1010 | &twl4030_dapm_predriver_control), | 1215 | SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0, |
1216 | &twl4030_dapm_predriver_controls[0], | ||
1217 | ARRAY_SIZE(twl4030_dapm_predriver_controls)), | ||
1011 | /* HeadsetL/R */ | 1218 | /* HeadsetL/R */ |
1012 | SND_SOC_DAPM_MUX_E("HeadsetL Mux", SND_SOC_NOPM, 0, 0, | 1219 | SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0, |
1013 | &twl4030_dapm_hsol_control, headsetl_event, | 1220 | &twl4030_dapm_hsol_controls[0], |
1014 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 1221 | ARRAY_SIZE(twl4030_dapm_hsol_controls)), |
1015 | SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0, | 1222 | SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM, |
1016 | &twl4030_dapm_hsor_control), | 1223 | 0, 0, NULL, 0, headsetlpga_event, |
1224 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1225 | SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0, | ||
1226 | &twl4030_dapm_hsor_controls[0], | ||
1227 | ARRAY_SIZE(twl4030_dapm_hsor_controls)), | ||
1228 | SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM, | ||
1229 | 0, 0, NULL, 0, headsetrpga_event, | ||
1230 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1017 | /* CarkitL/R */ | 1231 | /* CarkitL/R */ |
1018 | SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0, | 1232 | SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0, |
1019 | &twl4030_dapm_carkitl_control), | 1233 | &twl4030_dapm_carkitl_controls[0], |
1020 | SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0, | 1234 | ARRAY_SIZE(twl4030_dapm_carkitl_controls)), |
1021 | &twl4030_dapm_carkitr_control), | 1235 | SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0, |
1236 | &twl4030_dapm_carkitr_controls[0], | ||
1237 | ARRAY_SIZE(twl4030_dapm_carkitr_controls)), | ||
1238 | |||
1239 | /* Output MUX controls */ | ||
1022 | /* HandsfreeL/R */ | 1240 | /* HandsfreeL/R */ |
1023 | SND_SOC_DAPM_MUX_E("HandsfreeL Mux", TWL4030_REG_HFL_CTL, 5, 0, | 1241 | SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0, |
1024 | &twl4030_dapm_handsfreel_control, handsfree_event, | 1242 | &twl4030_dapm_handsfreel_control), |
1025 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 1243 | SND_SOC_DAPM_SWITCH("HandsfreeL Switch", SND_SOC_NOPM, 0, 0, |
1026 | SND_SOC_DAPM_MUX_E("HandsfreeR Mux", TWL4030_REG_HFR_CTL, 5, 0, | 1244 | &twl4030_dapm_handsfreelmute_control), |
1027 | &twl4030_dapm_handsfreer_control, handsfree_event, | 1245 | SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM, |
1028 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 1246 | 0, 0, NULL, 0, handsfreelpga_event, |
1247 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1248 | SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0, | ||
1249 | &twl4030_dapm_handsfreer_control), | ||
1250 | SND_SOC_DAPM_SWITCH("HandsfreeR Switch", SND_SOC_NOPM, 0, 0, | ||
1251 | &twl4030_dapm_handsfreermute_control), | ||
1252 | SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM, | ||
1253 | 0, 0, NULL, 0, handsfreerpga_event, | ||
1254 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1255 | /* Vibra */ | ||
1256 | SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, | ||
1257 | &twl4030_dapm_vibra_control), | ||
1258 | SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0, | ||
1259 | &twl4030_dapm_vibrapath_control), | ||
1029 | 1260 | ||
1030 | /* Introducing four virtual ADC, since TWL4030 have four channel for | 1261 | /* Introducing four virtual ADC, since TWL4030 have four channel for |
1031 | capture */ | 1262 | capture */ |
@@ -1050,11 +1281,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1050 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| | 1281 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| |
1051 | SND_SOC_DAPM_POST_REG), | 1282 | SND_SOC_DAPM_POST_REG), |
1052 | 1283 | ||
1053 | /* Analog input muxes with switch for the capture amplifiers */ | 1284 | /* Analog input mixers for the capture amplifiers */ |
1054 | SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route", | 1285 | SND_SOC_DAPM_MIXER("Analog Left Capture Route", |
1055 | TWL4030_REG_ANAMICL, 4, 0, &twl4030_dapm_analoglmic_control), | 1286 | TWL4030_REG_ANAMICL, 4, 0, |
1056 | SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route", | 1287 | &twl4030_dapm_analoglmic_controls[0], |
1057 | TWL4030_REG_ANAMICR, 4, 0, &twl4030_dapm_analogrmic_control), | 1288 | ARRAY_SIZE(twl4030_dapm_analoglmic_controls)), |
1289 | SND_SOC_DAPM_MIXER("Analog Right Capture Route", | ||
1290 | TWL4030_REG_ANAMICR, 4, 0, | ||
1291 | &twl4030_dapm_analogrmic_controls[0], | ||
1292 | ARRAY_SIZE(twl4030_dapm_analogrmic_controls)), | ||
1058 | 1293 | ||
1059 | SND_SOC_DAPM_PGA("ADC Physical Left", | 1294 | SND_SOC_DAPM_PGA("ADC Physical Left", |
1060 | TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0), | 1295 | TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0), |
@@ -1073,62 +1308,86 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1073 | }; | 1308 | }; |
1074 | 1309 | ||
1075 | static const struct snd_soc_dapm_route intercon[] = { | 1310 | static const struct snd_soc_dapm_route intercon[] = { |
1076 | {"Analog L1 Playback Mixer", NULL, "DAC Left1"}, | 1311 | {"Digital L1 Playback Mixer", NULL, "DAC Left1"}, |
1077 | {"Analog R1 Playback Mixer", NULL, "DAC Right1"}, | 1312 | {"Digital R1 Playback Mixer", NULL, "DAC Right1"}, |
1078 | {"Analog L2 Playback Mixer", NULL, "DAC Left2"}, | 1313 | {"Digital L2 Playback Mixer", NULL, "DAC Left2"}, |
1079 | {"Analog R2 Playback Mixer", NULL, "DAC Right2"}, | 1314 | {"Digital R2 Playback Mixer", NULL, "DAC Right2"}, |
1080 | 1315 | {"Digital Voice Playback Mixer", NULL, "DAC Voice"}, | |
1081 | {"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"}, | 1316 | |
1082 | {"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"}, | 1317 | {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, |
1083 | {"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"}, | 1318 | {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, |
1084 | {"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"}, | 1319 | {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, |
1320 | {"Analog R2 Playback Mixer", NULL, "Digital R2 Playback Mixer"}, | ||
1321 | {"Analog Voice Playback Mixer", NULL, "Digital Voice Playback Mixer"}, | ||
1085 | 1322 | ||
1086 | /* Internal playback routings */ | 1323 | /* Internal playback routings */ |
1087 | /* Earpiece */ | 1324 | /* Earpiece */ |
1088 | {"Earpiece Mux", "DACL1", "ARXL1_APGA"}, | 1325 | {"Earpiece Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1089 | {"Earpiece Mux", "DACL2", "ARXL2_APGA"}, | 1326 | {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
1090 | {"Earpiece Mux", "DACR1", "ARXR1_APGA"}, | 1327 | {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"}, |
1328 | {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | ||
1091 | /* PreDrivL */ | 1329 | /* PreDrivL */ |
1092 | {"PredriveL Mux", "DACL1", "ARXL1_APGA"}, | 1330 | {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1093 | {"PredriveL Mux", "DACL2", "ARXL2_APGA"}, | 1331 | {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
1094 | {"PredriveL Mux", "DACR2", "ARXR2_APGA"}, | 1332 | {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, |
1333 | {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | ||
1095 | /* PreDrivR */ | 1334 | /* PreDrivR */ |
1096 | {"PredriveR Mux", "DACR1", "ARXR1_APGA"}, | 1335 | {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1097 | {"PredriveR Mux", "DACR2", "ARXR2_APGA"}, | 1336 | {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, |
1098 | {"PredriveR Mux", "DACL2", "ARXL2_APGA"}, | 1337 | {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, |
1338 | {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | ||
1099 | /* HeadsetL */ | 1339 | /* HeadsetL */ |
1100 | {"HeadsetL Mux", "DACL1", "ARXL1_APGA"}, | 1340 | {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1101 | {"HeadsetL Mux", "DACL2", "ARXL2_APGA"}, | 1341 | {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
1342 | {"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | ||
1343 | {"HeadsetL PGA", NULL, "HeadsetL Mixer"}, | ||
1102 | /* HeadsetR */ | 1344 | /* HeadsetR */ |
1103 | {"HeadsetR Mux", "DACR1", "ARXR1_APGA"}, | 1345 | {"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1104 | {"HeadsetR Mux", "DACR2", "ARXR2_APGA"}, | 1346 | {"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, |
1347 | {"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | ||
1348 | {"HeadsetR PGA", NULL, "HeadsetR Mixer"}, | ||
1105 | /* CarkitL */ | 1349 | /* CarkitL */ |
1106 | {"CarkitL Mux", "DACL1", "ARXL1_APGA"}, | 1350 | {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1107 | {"CarkitL Mux", "DACL2", "ARXL2_APGA"}, | 1351 | {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
1352 | {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | ||
1108 | /* CarkitR */ | 1353 | /* CarkitR */ |
1109 | {"CarkitR Mux", "DACR1", "ARXR1_APGA"}, | 1354 | {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1110 | {"CarkitR Mux", "DACR2", "ARXR2_APGA"}, | 1355 | {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, |
1356 | {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | ||
1111 | /* HandsfreeL */ | 1357 | /* HandsfreeL */ |
1112 | {"HandsfreeL Mux", "DACL1", "ARXL1_APGA"}, | 1358 | {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"}, |
1113 | {"HandsfreeL Mux", "DACL2", "ARXL2_APGA"}, | 1359 | {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"}, |
1114 | {"HandsfreeL Mux", "DACR2", "ARXR2_APGA"}, | 1360 | {"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"}, |
1361 | {"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"}, | ||
1362 | {"HandsfreeL Switch", "Switch", "HandsfreeL Mux"}, | ||
1363 | {"HandsfreeL PGA", NULL, "HandsfreeL Switch"}, | ||
1115 | /* HandsfreeR */ | 1364 | /* HandsfreeR */ |
1116 | {"HandsfreeR Mux", "DACR1", "ARXR1_APGA"}, | 1365 | {"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"}, |
1117 | {"HandsfreeR Mux", "DACR2", "ARXR2_APGA"}, | 1366 | {"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"}, |
1118 | {"HandsfreeR Mux", "DACL2", "ARXL2_APGA"}, | 1367 | {"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"}, |
1368 | {"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"}, | ||
1369 | {"HandsfreeR Switch", "Switch", "HandsfreeR Mux"}, | ||
1370 | {"HandsfreeR PGA", NULL, "HandsfreeR Switch"}, | ||
1371 | /* Vibra */ | ||
1372 | {"Vibra Mux", "AudioL1", "DAC Left1"}, | ||
1373 | {"Vibra Mux", "AudioR1", "DAC Right1"}, | ||
1374 | {"Vibra Mux", "AudioL2", "DAC Left2"}, | ||
1375 | {"Vibra Mux", "AudioR2", "DAC Right2"}, | ||
1119 | 1376 | ||
1120 | /* outputs */ | 1377 | /* outputs */ |
1121 | {"OUTL", NULL, "ARXL2_APGA"}, | 1378 | {"OUTL", NULL, "Analog L2 Playback Mixer"}, |
1122 | {"OUTR", NULL, "ARXR2_APGA"}, | 1379 | {"OUTR", NULL, "Analog R2 Playback Mixer"}, |
1123 | {"EARPIECE", NULL, "Earpiece Mux"}, | 1380 | {"EARPIECE", NULL, "Earpiece Mixer"}, |
1124 | {"PREDRIVEL", NULL, "PredriveL Mux"}, | 1381 | {"PREDRIVEL", NULL, "PredriveL Mixer"}, |
1125 | {"PREDRIVER", NULL, "PredriveR Mux"}, | 1382 | {"PREDRIVER", NULL, "PredriveR Mixer"}, |
1126 | {"HSOL", NULL, "HeadsetL Mux"}, | 1383 | {"HSOL", NULL, "HeadsetL PGA"}, |
1127 | {"HSOR", NULL, "HeadsetR Mux"}, | 1384 | {"HSOR", NULL, "HeadsetR PGA"}, |
1128 | {"CARKITL", NULL, "CarkitL Mux"}, | 1385 | {"CARKITL", NULL, "CarkitL Mixer"}, |
1129 | {"CARKITR", NULL, "CarkitR Mux"}, | 1386 | {"CARKITR", NULL, "CarkitR Mixer"}, |
1130 | {"HFL", NULL, "HandsfreeL Mux"}, | 1387 | {"HFL", NULL, "HandsfreeL PGA"}, |
1131 | {"HFR", NULL, "HandsfreeR Mux"}, | 1388 | {"HFR", NULL, "HandsfreeR PGA"}, |
1389 | {"Vibra Route", "Audio", "Vibra Mux"}, | ||
1390 | {"VIBRA", NULL, "Vibra Route"}, | ||
1132 | 1391 | ||
1133 | /* Capture path */ | 1392 | /* Capture path */ |
1134 | {"Analog Left Capture Route", "Main mic", "MAINMIC"}, | 1393 | {"Analog Left Capture Route", "Main mic", "MAINMIC"}, |
@@ -1168,18 +1427,22 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1168 | {"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"}, | 1427 | {"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"}, |
1169 | {"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"}, | 1428 | {"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"}, |
1170 | {"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"}, | 1429 | {"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"}, |
1430 | {"Voice Analog Loopback", "Switch", "Analog Left Capture Route"}, | ||
1171 | 1431 | ||
1172 | {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, | 1432 | {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, |
1173 | {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, | 1433 | {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, |
1174 | {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, | 1434 | {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, |
1175 | {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"}, | 1435 | {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"}, |
1436 | {"Analog Voice Playback Mixer", NULL, "Voice Analog Loopback"}, | ||
1176 | 1437 | ||
1177 | /* Digital bypass routes */ | 1438 | /* Digital bypass routes */ |
1178 | {"Right Digital Loopback", "Volume", "TX1 Capture Route"}, | 1439 | {"Right Digital Loopback", "Volume", "TX1 Capture Route"}, |
1179 | {"Left Digital Loopback", "Volume", "TX1 Capture Route"}, | 1440 | {"Left Digital Loopback", "Volume", "TX1 Capture Route"}, |
1441 | {"Voice Digital Loopback", "Volume", "TX2 Capture Route"}, | ||
1180 | 1442 | ||
1181 | {"Analog R2 Playback Mixer", NULL, "Right Digital Loopback"}, | 1443 | {"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"}, |
1182 | {"Analog L2 Playback Mixer", NULL, "Left Digital Loopback"}, | 1444 | {"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"}, |
1445 | {"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"}, | ||
1183 | 1446 | ||
1184 | }; | 1447 | }; |
1185 | 1448 | ||
@@ -1226,6 +1489,58 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, | |||
1226 | return 0; | 1489 | return 0; |
1227 | } | 1490 | } |
1228 | 1491 | ||
1492 | static void twl4030_constraints(struct twl4030_priv *twl4030, | ||
1493 | struct snd_pcm_substream *mst_substream) | ||
1494 | { | ||
1495 | struct snd_pcm_substream *slv_substream; | ||
1496 | |||
1497 | /* Pick the stream, which need to be constrained */ | ||
1498 | if (mst_substream == twl4030->master_substream) | ||
1499 | slv_substream = twl4030->slave_substream; | ||
1500 | else if (mst_substream == twl4030->slave_substream) | ||
1501 | slv_substream = twl4030->master_substream; | ||
1502 | else /* This should not happen.. */ | ||
1503 | return; | ||
1504 | |||
1505 | /* Set the constraints according to the already configured stream */ | ||
1506 | snd_pcm_hw_constraint_minmax(slv_substream->runtime, | ||
1507 | SNDRV_PCM_HW_PARAM_RATE, | ||
1508 | twl4030->rate, | ||
1509 | twl4030->rate); | ||
1510 | |||
1511 | snd_pcm_hw_constraint_minmax(slv_substream->runtime, | ||
1512 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
1513 | twl4030->sample_bits, | ||
1514 | twl4030->sample_bits); | ||
1515 | |||
1516 | snd_pcm_hw_constraint_minmax(slv_substream->runtime, | ||
1517 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
1518 | twl4030->channels, | ||
1519 | twl4030->channels); | ||
1520 | } | ||
1521 | |||
1522 | /* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for | ||
1523 | * capture has to be enabled/disabled. */ | ||
1524 | static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction, | ||
1525 | int enable) | ||
1526 | { | ||
1527 | u8 reg, mask; | ||
1528 | |||
1529 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION); | ||
1530 | |||
1531 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | ||
1532 | mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN; | ||
1533 | else | ||
1534 | mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN; | ||
1535 | |||
1536 | if (enable) | ||
1537 | reg |= mask; | ||
1538 | else | ||
1539 | reg &= ~mask; | ||
1540 | |||
1541 | twl4030_write(codec, TWL4030_REG_OPTION, reg); | ||
1542 | } | ||
1543 | |||
1229 | static int twl4030_startup(struct snd_pcm_substream *substream, | 1544 | static int twl4030_startup(struct snd_pcm_substream *substream, |
1230 | struct snd_soc_dai *dai) | 1545 | struct snd_soc_dai *dai) |
1231 | { | 1546 | { |
@@ -1234,26 +1549,25 @@ static int twl4030_startup(struct snd_pcm_substream *substream, | |||
1234 | struct snd_soc_codec *codec = socdev->card->codec; | 1549 | struct snd_soc_codec *codec = socdev->card->codec; |
1235 | struct twl4030_priv *twl4030 = codec->private_data; | 1550 | struct twl4030_priv *twl4030 = codec->private_data; |
1236 | 1551 | ||
1237 | /* If we already have a playback or capture going then constrain | ||
1238 | * this substream to match it. | ||
1239 | */ | ||
1240 | if (twl4030->master_substream) { | 1552 | if (twl4030->master_substream) { |
1241 | struct snd_pcm_runtime *master_runtime; | ||
1242 | master_runtime = twl4030->master_substream->runtime; | ||
1243 | |||
1244 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
1245 | SNDRV_PCM_HW_PARAM_RATE, | ||
1246 | master_runtime->rate, | ||
1247 | master_runtime->rate); | ||
1248 | |||
1249 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
1250 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
1251 | master_runtime->sample_bits, | ||
1252 | master_runtime->sample_bits); | ||
1253 | |||
1254 | twl4030->slave_substream = substream; | 1553 | twl4030->slave_substream = substream; |
1255 | } else | 1554 | /* The DAI has one configuration for playback and capture, so |
1555 | * if the DAI has been already configured then constrain this | ||
1556 | * substream to match it. */ | ||
1557 | if (twl4030->configured) | ||
1558 | twl4030_constraints(twl4030, twl4030->master_substream); | ||
1559 | } else { | ||
1560 | if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & | ||
1561 | TWL4030_OPTION_1)) { | ||
1562 | /* In option2 4 channel is not supported, set the | ||
1563 | * constraint for the first stream for channels, the | ||
1564 | * second stream will 'inherit' this cosntraint */ | ||
1565 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
1566 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
1567 | 2, 2); | ||
1568 | } | ||
1256 | twl4030->master_substream = substream; | 1569 | twl4030->master_substream = substream; |
1570 | } | ||
1257 | 1571 | ||
1258 | return 0; | 1572 | return 0; |
1259 | } | 1573 | } |
@@ -1270,6 +1584,17 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream, | |||
1270 | twl4030->master_substream = twl4030->slave_substream; | 1584 | twl4030->master_substream = twl4030->slave_substream; |
1271 | 1585 | ||
1272 | twl4030->slave_substream = NULL; | 1586 | twl4030->slave_substream = NULL; |
1587 | |||
1588 | /* If all streams are closed, or the remaining stream has not yet | ||
1589 | * been configured than set the DAI as not configured. */ | ||
1590 | if (!twl4030->master_substream) | ||
1591 | twl4030->configured = 0; | ||
1592 | else if (!twl4030->master_substream->runtime->channels) | ||
1593 | twl4030->configured = 0; | ||
1594 | |||
1595 | /* If the closing substream had 4 channel, do the necessary cleanup */ | ||
1596 | if (substream->runtime->channels == 4) | ||
1597 | twl4030_tdm_enable(codec, substream->stream, 0); | ||
1273 | } | 1598 | } |
1274 | 1599 | ||
1275 | static int twl4030_hw_params(struct snd_pcm_substream *substream, | 1600 | static int twl4030_hw_params(struct snd_pcm_substream *substream, |
@@ -1282,8 +1607,24 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1282 | struct twl4030_priv *twl4030 = codec->private_data; | 1607 | struct twl4030_priv *twl4030 = codec->private_data; |
1283 | u8 mode, old_mode, format, old_format; | 1608 | u8 mode, old_mode, format, old_format; |
1284 | 1609 | ||
1285 | if (substream == twl4030->slave_substream) | 1610 | /* If the substream has 4 channel, do the necessary setup */ |
1286 | /* Ignoring hw_params for slave substream */ | 1611 | if (params_channels(params) == 4) { |
1612 | u8 format, mode; | ||
1613 | |||
1614 | format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | ||
1615 | mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); | ||
1616 | |||
1617 | /* Safety check: are we in the correct operating mode and | ||
1618 | * the interface is in TDM mode? */ | ||
1619 | if ((mode & TWL4030_OPTION_1) && | ||
1620 | ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM)) | ||
1621 | twl4030_tdm_enable(codec, substream->stream, 1); | ||
1622 | else | ||
1623 | return -EINVAL; | ||
1624 | } | ||
1625 | |||
1626 | if (twl4030->configured) | ||
1627 | /* Ignoring hw_params for already configured DAI */ | ||
1287 | return 0; | 1628 | return 0; |
1288 | 1629 | ||
1289 | /* bit rate */ | 1630 | /* bit rate */ |
@@ -1363,6 +1704,21 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1363 | /* set CODECPDZ afterwards */ | 1704 | /* set CODECPDZ afterwards */ |
1364 | twl4030_codec_enable(codec, 1); | 1705 | twl4030_codec_enable(codec, 1); |
1365 | } | 1706 | } |
1707 | |||
1708 | /* Store the important parameters for the DAI configuration and set | ||
1709 | * the DAI as configured */ | ||
1710 | twl4030->configured = 1; | ||
1711 | twl4030->rate = params_rate(params); | ||
1712 | twl4030->sample_bits = hw_param_interval(params, | ||
1713 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; | ||
1714 | twl4030->channels = params_channels(params); | ||
1715 | |||
1716 | /* If both playback and capture streams are open, and one of them | ||
1717 | * is setting the hw parameters right now (since we are here), set | ||
1718 | * constraints to the other stream to match the current one. */ | ||
1719 | if (twl4030->slave_substream) | ||
1720 | twl4030_constraints(twl4030, substream); | ||
1721 | |||
1366 | return 0; | 1722 | return 0; |
1367 | } | 1723 | } |
1368 | 1724 | ||
@@ -1370,17 +1726,21 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
1370 | int clk_id, unsigned int freq, int dir) | 1726 | int clk_id, unsigned int freq, int dir) |
1371 | { | 1727 | { |
1372 | struct snd_soc_codec *codec = codec_dai->codec; | 1728 | struct snd_soc_codec *codec = codec_dai->codec; |
1729 | struct twl4030_priv *twl4030 = codec->private_data; | ||
1373 | u8 infreq; | 1730 | u8 infreq; |
1374 | 1731 | ||
1375 | switch (freq) { | 1732 | switch (freq) { |
1376 | case 19200000: | 1733 | case 19200000: |
1377 | infreq = TWL4030_APLL_INFREQ_19200KHZ; | 1734 | infreq = TWL4030_APLL_INFREQ_19200KHZ; |
1735 | twl4030->sysclk = 19200; | ||
1378 | break; | 1736 | break; |
1379 | case 26000000: | 1737 | case 26000000: |
1380 | infreq = TWL4030_APLL_INFREQ_26000KHZ; | 1738 | infreq = TWL4030_APLL_INFREQ_26000KHZ; |
1739 | twl4030->sysclk = 26000; | ||
1381 | break; | 1740 | break; |
1382 | case 38400000: | 1741 | case 38400000: |
1383 | infreq = TWL4030_APLL_INFREQ_38400KHZ; | 1742 | infreq = TWL4030_APLL_INFREQ_38400KHZ; |
1743 | twl4030->sysclk = 38400; | ||
1384 | break; | 1744 | break; |
1385 | default: | 1745 | default: |
1386 | printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n", | 1746 | printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n", |
@@ -1424,6 +1784,9 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1424 | case SND_SOC_DAIFMT_I2S: | 1784 | case SND_SOC_DAIFMT_I2S: |
1425 | format |= TWL4030_AIF_FORMAT_CODEC; | 1785 | format |= TWL4030_AIF_FORMAT_CODEC; |
1426 | break; | 1786 | break; |
1787 | case SND_SOC_DAIFMT_DSP_A: | ||
1788 | format |= TWL4030_AIF_FORMAT_TDM; | ||
1789 | break; | ||
1427 | default: | 1790 | default: |
1428 | return -EINVAL; | 1791 | return -EINVAL; |
1429 | } | 1792 | } |
@@ -1443,6 +1806,180 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1443 | return 0; | 1806 | return 0; |
1444 | } | 1807 | } |
1445 | 1808 | ||
1809 | /* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R | ||
1810 | * (VTXL, VTXR) for uplink has to be enabled/disabled. */ | ||
1811 | static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction, | ||
1812 | int enable) | ||
1813 | { | ||
1814 | u8 reg, mask; | ||
1815 | |||
1816 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION); | ||
1817 | |||
1818 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | ||
1819 | mask = TWL4030_ARXL1_VRX_EN; | ||
1820 | else | ||
1821 | mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN; | ||
1822 | |||
1823 | if (enable) | ||
1824 | reg |= mask; | ||
1825 | else | ||
1826 | reg &= ~mask; | ||
1827 | |||
1828 | twl4030_write(codec, TWL4030_REG_OPTION, reg); | ||
1829 | } | ||
1830 | |||
1831 | static int twl4030_voice_startup(struct snd_pcm_substream *substream, | ||
1832 | struct snd_soc_dai *dai) | ||
1833 | { | ||
1834 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1835 | struct snd_soc_device *socdev = rtd->socdev; | ||
1836 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1837 | u8 infreq; | ||
1838 | u8 mode; | ||
1839 | |||
1840 | /* If the system master clock is not 26MHz, the voice PCM interface is | ||
1841 | * not avilable. | ||
1842 | */ | ||
1843 | infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL) | ||
1844 | & TWL4030_APLL_INFREQ; | ||
1845 | |||
1846 | if (infreq != TWL4030_APLL_INFREQ_26000KHZ) { | ||
1847 | printk(KERN_ERR "TWL4030 voice startup: " | ||
1848 | "MCLK is not 26MHz, call set_sysclk() on init\n"); | ||
1849 | return -EINVAL; | ||
1850 | } | ||
1851 | |||
1852 | /* If the codec mode is not option2, the voice PCM interface is not | ||
1853 | * avilable. | ||
1854 | */ | ||
1855 | mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) | ||
1856 | & TWL4030_OPT_MODE; | ||
1857 | |||
1858 | if (mode != TWL4030_OPTION_2) { | ||
1859 | printk(KERN_ERR "TWL4030 voice startup: " | ||
1860 | "the codec mode is not option2\n"); | ||
1861 | return -EINVAL; | ||
1862 | } | ||
1863 | |||
1864 | return 0; | ||
1865 | } | ||
1866 | |||
1867 | static void twl4030_voice_shutdown(struct snd_pcm_substream *substream, | ||
1868 | struct snd_soc_dai *dai) | ||
1869 | { | ||
1870 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1871 | struct snd_soc_device *socdev = rtd->socdev; | ||
1872 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1873 | |||
1874 | /* Enable voice digital filters */ | ||
1875 | twl4030_voice_enable(codec, substream->stream, 0); | ||
1876 | } | ||
1877 | |||
1878 | static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | ||
1879 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | ||
1880 | { | ||
1881 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1882 | struct snd_soc_device *socdev = rtd->socdev; | ||
1883 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1884 | u8 old_mode, mode; | ||
1885 | |||
1886 | /* Enable voice digital filters */ | ||
1887 | twl4030_voice_enable(codec, substream->stream, 1); | ||
1888 | |||
1889 | /* bit rate */ | ||
1890 | old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) | ||
1891 | & ~(TWL4030_CODECPDZ); | ||
1892 | mode = old_mode; | ||
1893 | |||
1894 | switch (params_rate(params)) { | ||
1895 | case 8000: | ||
1896 | mode &= ~(TWL4030_SEL_16K); | ||
1897 | break; | ||
1898 | case 16000: | ||
1899 | mode |= TWL4030_SEL_16K; | ||
1900 | break; | ||
1901 | default: | ||
1902 | printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n", | ||
1903 | params_rate(params)); | ||
1904 | return -EINVAL; | ||
1905 | } | ||
1906 | |||
1907 | if (mode != old_mode) { | ||
1908 | /* change rate and set CODECPDZ */ | ||
1909 | twl4030_codec_enable(codec, 0); | ||
1910 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
1911 | twl4030_codec_enable(codec, 1); | ||
1912 | } | ||
1913 | |||
1914 | return 0; | ||
1915 | } | ||
1916 | |||
1917 | static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
1918 | int clk_id, unsigned int freq, int dir) | ||
1919 | { | ||
1920 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1921 | u8 infreq; | ||
1922 | |||
1923 | switch (freq) { | ||
1924 | case 26000000: | ||
1925 | infreq = TWL4030_APLL_INFREQ_26000KHZ; | ||
1926 | break; | ||
1927 | default: | ||
1928 | printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n", | ||
1929 | freq); | ||
1930 | return -EINVAL; | ||
1931 | } | ||
1932 | |||
1933 | infreq |= TWL4030_APLL_EN; | ||
1934 | twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); | ||
1935 | |||
1936 | return 0; | ||
1937 | } | ||
1938 | |||
1939 | static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
1940 | unsigned int fmt) | ||
1941 | { | ||
1942 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1943 | u8 old_format, format; | ||
1944 | |||
1945 | /* get format */ | ||
1946 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF); | ||
1947 | format = old_format; | ||
1948 | |||
1949 | /* set master/slave audio interface */ | ||
1950 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
1951 | case SND_SOC_DAIFMT_CBS_CFM: | ||
1952 | format &= ~(TWL4030_VIF_SLAVE_EN); | ||
1953 | break; | ||
1954 | case SND_SOC_DAIFMT_CBS_CFS: | ||
1955 | format |= TWL4030_VIF_SLAVE_EN; | ||
1956 | break; | ||
1957 | default: | ||
1958 | return -EINVAL; | ||
1959 | } | ||
1960 | |||
1961 | /* clock inversion */ | ||
1962 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
1963 | case SND_SOC_DAIFMT_IB_NF: | ||
1964 | format &= ~(TWL4030_VIF_FORMAT); | ||
1965 | break; | ||
1966 | case SND_SOC_DAIFMT_NB_IF: | ||
1967 | format |= TWL4030_VIF_FORMAT; | ||
1968 | break; | ||
1969 | default: | ||
1970 | return -EINVAL; | ||
1971 | } | ||
1972 | |||
1973 | if (format != old_format) { | ||
1974 | /* change format and set CODECPDZ */ | ||
1975 | twl4030_codec_enable(codec, 0); | ||
1976 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
1977 | twl4030_codec_enable(codec, 1); | ||
1978 | } | ||
1979 | |||
1980 | return 0; | ||
1981 | } | ||
1982 | |||
1446 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) | 1983 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) |
1447 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) | 1984 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) |
1448 | 1985 | ||
@@ -1454,21 +1991,47 @@ static struct snd_soc_dai_ops twl4030_dai_ops = { | |||
1454 | .set_fmt = twl4030_set_dai_fmt, | 1991 | .set_fmt = twl4030_set_dai_fmt, |
1455 | }; | 1992 | }; |
1456 | 1993 | ||
1457 | struct snd_soc_dai twl4030_dai = { | 1994 | static struct snd_soc_dai_ops twl4030_dai_voice_ops = { |
1995 | .startup = twl4030_voice_startup, | ||
1996 | .shutdown = twl4030_voice_shutdown, | ||
1997 | .hw_params = twl4030_voice_hw_params, | ||
1998 | .set_sysclk = twl4030_voice_set_dai_sysclk, | ||
1999 | .set_fmt = twl4030_voice_set_dai_fmt, | ||
2000 | }; | ||
2001 | |||
2002 | struct snd_soc_dai twl4030_dai[] = { | ||
2003 | { | ||
1458 | .name = "twl4030", | 2004 | .name = "twl4030", |
1459 | .playback = { | 2005 | .playback = { |
1460 | .stream_name = "Playback", | 2006 | .stream_name = "HiFi Playback", |
1461 | .channels_min = 2, | 2007 | .channels_min = 2, |
1462 | .channels_max = 2, | 2008 | .channels_max = 4, |
1463 | .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000, | 2009 | .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000, |
1464 | .formats = TWL4030_FORMATS,}, | 2010 | .formats = TWL4030_FORMATS,}, |
1465 | .capture = { | 2011 | .capture = { |
1466 | .stream_name = "Capture", | 2012 | .stream_name = "Capture", |
1467 | .channels_min = 2, | 2013 | .channels_min = 2, |
1468 | .channels_max = 2, | 2014 | .channels_max = 4, |
1469 | .rates = TWL4030_RATES, | 2015 | .rates = TWL4030_RATES, |
1470 | .formats = TWL4030_FORMATS,}, | 2016 | .formats = TWL4030_FORMATS,}, |
1471 | .ops = &twl4030_dai_ops, | 2017 | .ops = &twl4030_dai_ops, |
2018 | }, | ||
2019 | { | ||
2020 | .name = "twl4030 Voice", | ||
2021 | .playback = { | ||
2022 | .stream_name = "Voice Playback", | ||
2023 | .channels_min = 1, | ||
2024 | .channels_max = 1, | ||
2025 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | ||
2026 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
2027 | .capture = { | ||
2028 | .stream_name = "Capture", | ||
2029 | .channels_min = 1, | ||
2030 | .channels_max = 2, | ||
2031 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | ||
2032 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
2033 | .ops = &twl4030_dai_voice_ops, | ||
2034 | }, | ||
1472 | }; | 2035 | }; |
1473 | EXPORT_SYMBOL_GPL(twl4030_dai); | 2036 | EXPORT_SYMBOL_GPL(twl4030_dai); |
1474 | 2037 | ||
@@ -1500,6 +2063,8 @@ static int twl4030_resume(struct platform_device *pdev) | |||
1500 | static int twl4030_init(struct snd_soc_device *socdev) | 2063 | static int twl4030_init(struct snd_soc_device *socdev) |
1501 | { | 2064 | { |
1502 | struct snd_soc_codec *codec = socdev->card->codec; | 2065 | struct snd_soc_codec *codec = socdev->card->codec; |
2066 | struct twl4030_setup_data *setup = socdev->codec_data; | ||
2067 | struct twl4030_priv *twl4030 = codec->private_data; | ||
1503 | int ret = 0; | 2068 | int ret = 0; |
1504 | 2069 | ||
1505 | printk(KERN_INFO "TWL4030 Audio Codec init \n"); | 2070 | printk(KERN_INFO "TWL4030 Audio Codec init \n"); |
@@ -1509,14 +2074,31 @@ static int twl4030_init(struct snd_soc_device *socdev) | |||
1509 | codec->read = twl4030_read_reg_cache; | 2074 | codec->read = twl4030_read_reg_cache; |
1510 | codec->write = twl4030_write; | 2075 | codec->write = twl4030_write; |
1511 | codec->set_bias_level = twl4030_set_bias_level; | 2076 | codec->set_bias_level = twl4030_set_bias_level; |
1512 | codec->dai = &twl4030_dai; | 2077 | codec->dai = twl4030_dai; |
1513 | codec->num_dai = 1; | 2078 | codec->num_dai = ARRAY_SIZE(twl4030_dai), |
1514 | codec->reg_cache_size = sizeof(twl4030_reg); | 2079 | codec->reg_cache_size = sizeof(twl4030_reg); |
1515 | codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), | 2080 | codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), |
1516 | GFP_KERNEL); | 2081 | GFP_KERNEL); |
1517 | if (codec->reg_cache == NULL) | 2082 | if (codec->reg_cache == NULL) |
1518 | return -ENOMEM; | 2083 | return -ENOMEM; |
1519 | 2084 | ||
2085 | /* Configuration for headset ramp delay from setup data */ | ||
2086 | if (setup) { | ||
2087 | unsigned char hs_pop; | ||
2088 | |||
2089 | if (setup->sysclk) | ||
2090 | twl4030->sysclk = setup->sysclk; | ||
2091 | else | ||
2092 | twl4030->sysclk = 26000; | ||
2093 | |||
2094 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
2095 | hs_pop &= ~TWL4030_RAMP_DELAY; | ||
2096 | hs_pop |= (setup->ramp_delay_value << 2); | ||
2097 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
2098 | } else { | ||
2099 | twl4030->sysclk = 26000; | ||
2100 | } | ||
2101 | |||
1520 | /* register pcms */ | 2102 | /* register pcms */ |
1521 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 2103 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
1522 | if (ret < 0) { | 2104 | if (ret < 0) { |
@@ -1604,13 +2186,13 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); | |||
1604 | 2186 | ||
1605 | static int __init twl4030_modinit(void) | 2187 | static int __init twl4030_modinit(void) |
1606 | { | 2188 | { |
1607 | return snd_soc_register_dai(&twl4030_dai); | 2189 | return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); |
1608 | } | 2190 | } |
1609 | module_init(twl4030_modinit); | 2191 | module_init(twl4030_modinit); |
1610 | 2192 | ||
1611 | static void __exit twl4030_exit(void) | 2193 | static void __exit twl4030_exit(void) |
1612 | { | 2194 | { |
1613 | snd_soc_unregister_dai(&twl4030_dai); | 2195 | snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); |
1614 | } | 2196 | } |
1615 | module_exit(twl4030_exit); | 2197 | module_exit(twl4030_exit); |
1616 | 2198 | ||