aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
authorGrazvydas Ignotas <notasas@gmail.com>2008-12-02 13:48:58 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2008-12-03 07:40:28 -0500
commit5920b45303291057fef827f5bdafe04001c1bbae (patch)
tree1d63ec7971c8f00dee0d380b93c25c5844e007a6 /sound/soc/codecs
parent87689d567a45f80416feea0a2aa6d3a2a6b8963a (diff)
ASoC: TWL4030: Add input selection and gain controls
The TWL4030 codec device has two ADCs. Both of them can have several inputs routed to them, but TRM says that only one source can be selected for every ADC, even though every source has a dedicated bit in the registers. This patch adds input source controls. It modifies default register values to have no inputs selected and ADCs disabled. When some input is selected, control handlers enable apropriate input amplifier and ADC. If a microphone is selected, bias power is automatically enabled. When some input is deselected, unused chip parts are disabled. Microphone and line input recording tested on OMAP3 pandora board. Signed-off-by: Grazvydas Ignotas <notasas@gmail.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/twl4030.c177
-rw-r--r--sound/soc/codecs/twl4030.h16
2 files changed, 190 insertions, 3 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index ffd5120697a2..3c9fdf2c6c7b 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -46,9 +46,9 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
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) */
49 0x24, /* REG_ANAMICL (0x5) */ 49 0x20, /* REG_ANAMICL (0x5) */
50 0x04, /* REG_ANAMICR (0x6) */ 50 0x00, /* REG_ANAMICR (0x6) */
51 0x0a, /* REG_AVADC_CTL (0x7) */ 51 0x00, /* REG_AVADC_CTL (0x7) */
52 0x00, /* REG_ADCMICSEL (0x8) */ 52 0x00, /* REG_ADCMICSEL (0x8) */
53 0x00, /* REG_DIGMIXING (0x9) */ 53 0x00, /* REG_DIGMIXING (0x9) */
54 0x0c, /* REG_ATXL1PGA (0xA) */ 54 0x0c, /* REG_ATXL1PGA (0xA) */
@@ -347,6 +347,162 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
347 return err; 347 return err;
348} 348}
349 349
350static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
351 struct snd_ctl_elem_value *ucontrol)
352{
353 struct snd_soc_codec *codec = kcontrol->private_data;
354 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
355 int result = 0;
356
357 /* one bit must be set a time */
358 reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
359 | TWL4030_MAINMIC_EN;
360 if (reg != 0) {
361 result++;
362 while ((reg & 1) == 0) {
363 result++;
364 reg >>= 1;
365 }
366 }
367
368 ucontrol->value.integer.value[0] = result;
369 return 0;
370}
371
372static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
373 struct snd_ctl_elem_value *ucontrol)
374{
375 struct snd_soc_codec *codec = kcontrol->private_data;
376 int value = ucontrol->value.integer.value[0];
377 u8 anamicl, micbias, avadc_ctl;
378
379 anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
380 anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
381 | TWL4030_MAINMIC_EN);
382 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
383 micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
384 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
385
386 switch (value) {
387 case 1:
388 anamicl |= TWL4030_MAINMIC_EN;
389 micbias |= TWL4030_MICBIAS1_EN;
390 break;
391 case 2:
392 anamicl |= TWL4030_HSMIC_EN;
393 micbias |= TWL4030_HSMICBIAS_EN;
394 break;
395 case 3:
396 anamicl |= TWL4030_AUXL_EN;
397 break;
398 case 4:
399 anamicl |= TWL4030_CKMIC_EN;
400 break;
401 default:
402 break;
403 }
404
405 /* If some input is selected, enable amp and ADC */
406 if (value != 0) {
407 anamicl |= TWL4030_MICAMPL_EN;
408 avadc_ctl |= TWL4030_ADCL_EN;
409 } else {
410 anamicl &= ~TWL4030_MICAMPL_EN;
411 avadc_ctl &= ~TWL4030_ADCL_EN;
412 }
413
414 twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
415 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
416 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
417
418 return 1;
419}
420
421static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
422 struct snd_ctl_elem_value *ucontrol)
423{
424 struct snd_soc_codec *codec = kcontrol->private_data;
425 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
426 int value = 0;
427
428 reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
429 switch (reg) {
430 case TWL4030_SUBMIC_EN:
431 value = 1;
432 break;
433 case TWL4030_AUXR_EN:
434 value = 2;
435 break;
436 default:
437 break;
438 }
439
440 ucontrol->value.integer.value[0] = value;
441 return 0;
442}
443
444static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
445 struct snd_ctl_elem_value *ucontrol)
446{
447 struct snd_soc_codec *codec = kcontrol->private_data;
448 int value = ucontrol->value.integer.value[0];
449 u8 anamicr, micbias, avadc_ctl;
450
451 anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
452 anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
453 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
454 micbias &= ~TWL4030_MICBIAS2_EN;
455 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
456
457 switch (value) {
458 case 1:
459 anamicr |= TWL4030_SUBMIC_EN;
460 micbias |= TWL4030_MICBIAS2_EN;
461 break;
462 case 2:
463 anamicr |= TWL4030_AUXR_EN;
464 break;
465 default:
466 break;
467 }
468
469 if (value != 0) {
470 anamicr |= TWL4030_MICAMPR_EN;
471 avadc_ctl |= TWL4030_ADCR_EN;
472 } else {
473 anamicr &= ~TWL4030_MICAMPR_EN;
474 avadc_ctl &= ~TWL4030_ADCR_EN;
475 }
476
477 twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
478 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
479 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
480
481 return 1;
482}
483
484static const char *twl4030_left_in_sel[] = {
485 "None",
486 "Main Mic",
487 "Headset Mic",
488 "Line In",
489 "Carkit Mic",
490};
491
492static const char *twl4030_right_in_sel[] = {
493 "None",
494 "Sub Mic",
495 "Line In",
496};
497
498static const struct soc_enum twl4030_left_input_mux =
499 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
500 twl4030_left_in_sel);
501
502static const struct soc_enum twl4030_right_input_mux =
503 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
504 twl4030_right_in_sel);
505
350/* 506/*
351 * FGAIN volume control: 507 * FGAIN volume control:
352 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) 508 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
@@ -378,6 +534,12 @@ static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
378 */ 534 */
379static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); 535static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
380 536
537/*
538 * Gain control for input amplifiers
539 * 0 dB to 30 dB in 6 dB steps
540 */
541static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
542
381static const struct snd_kcontrol_new twl4030_snd_controls[] = { 543static const struct snd_kcontrol_new twl4030_snd_controls[] = {
382 /* Common playback gain controls */ 544 /* Common playback gain controls */
383 SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", 545 SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
@@ -420,6 +582,15 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
420 SOC_DOUBLE_R_TLV("Capture Volume", 582 SOC_DOUBLE_R_TLV("Capture Volume",
421 TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, 583 TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
422 0, 0x1f, 0, digital_capture_tlv), 584 0, 0x1f, 0, digital_capture_tlv),
585
586 SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN,
587 0, 3, 5, 0, input_gain_tlv),
588
589 /* Input source controls */
590 SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
591 twl4030_get_left_input, twl4030_put_left_input),
592 SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
593 twl4030_get_right_input, twl4030_put_right_input),
423}; 594};
424 595
425/* add non dapm controls */ 596/* add non dapm controls */
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index 09865d9f5203..a2065d417c2e 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -113,7 +113,16 @@
113#define TWL4030_CODECPDZ 0x02 113#define TWL4030_CODECPDZ 0x02
114#define TWL4030_OPT_MODE 0x01 114#define TWL4030_OPT_MODE 0x01
115 115
116/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
117
118#define TWL4030_MICBIAS2_CTL 0x40
119#define TWL4030_MICBIAS1_CTL 0x20
120#define TWL4030_HSMICBIAS_EN 0x04
121#define TWL4030_MICBIAS2_EN 0x02
122#define TWL4030_MICBIAS1_EN 0x01
123
116/* ANAMICL (0x05) Fields */ 124/* ANAMICL (0x05) Fields */
125
117#define TWL4030_CNCL_OFFSET_START 0x80 126#define TWL4030_CNCL_OFFSET_START 0x80
118#define TWL4030_OFFSET_CNCL_SEL 0x60 127#define TWL4030_OFFSET_CNCL_SEL 0x60
119#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00 128#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
@@ -127,10 +136,17 @@
127#define TWL4030_MAINMIC_EN 0x01 136#define TWL4030_MAINMIC_EN 0x01
128 137
129/* ANAMICR (0x06) Fields */ 138/* ANAMICR (0x06) Fields */
139
130#define TWL4030_MICAMPR_EN 0x10 140#define TWL4030_MICAMPR_EN 0x10
131#define TWL4030_AUXR_EN 0x04 141#define TWL4030_AUXR_EN 0x04
132#define TWL4030_SUBMIC_EN 0x01 142#define TWL4030_SUBMIC_EN 0x01
133 143
144/* AVADC_CTL (0x07) Fields */
145
146#define TWL4030_ADCL_EN 0x08
147#define TWL4030_AVADC_CLK_PRIORITY 0x04
148#define TWL4030_ADCR_EN 0x02
149
134/* AUDIO_IF (0x0E) Fields */ 150/* AUDIO_IF (0x0E) Fields */
135 151
136#define TWL4030_AIF_SLAVE_EN 0x80 152#define TWL4030_AIF_SLAVE_EN 0x80