[alsa-devel] [PATCH 0/8] ASoC: TWL4030: DAPM implementation for outputs (playback)
Hello,
This series of patches adds DAPM implementation for all outputs found on TWL.
The design allows one to configure all outputs independently from each other, since the internal routing of TWL actually treats them individually.
The pre-DAC routings can be configured in the board specific files of needed. I have sent a patch which has added this to the codec driver.
Based on the output routing selections all the corresponding DAC and analog PGA on the chain will be powered on.
--- Peter Ujfalusi (8): ASoC: TWL4030: Correct DAPM_DAC with power control ASoC: TWL4030: Add Analog PGA control switch to DAPM ASoC: TWL4030: Add DAPM event handler for output MUX selection ASoC: TWL4030: DAPM mapping of the Earpiece output ASoC: TWL4030: DAPM mapping of the PreDriv outputs ASoC: TWL4030: DAPM mapping of the Headset outputs ASoC: TWL4030: DAPM mapping of the Carkit outputs ASoC: TWL4030: DAPM mapping of the Handsfree outputs
sound/soc/codecs/twl4030.c | 248 +++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 243 insertions(+), 5 deletions(-)
Add all four DACs to dapm_widgets with power switch.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 15 +++++++++++---- 1 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 3c9fdf2..3543bf6 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -616,8 +616,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"),
- SND_SOC_DAPM_DAC("DACL", "Left Playback", SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_DAC("DACR", "Right Playback", SND_SOC_NOPM, 0, 0), + /* DACs */ + SND_SOC_DAPM_DAC("DACR1", "Right Front Playback", + TWL4030_REG_AVDAC_CTL, 0, 0), + SND_SOC_DAPM_DAC("DACL1", "Left Front Playback", + TWL4030_REG_AVDAC_CTL, 1, 0), + SND_SOC_DAPM_DAC("DACR2", "Right Rear Playback", + TWL4030_REG_AVDAC_CTL, 2, 0), + SND_SOC_DAPM_DAC("DACL2", "Left Rear Playback", + TWL4030_REG_AVDAC_CTL, 3, 0),
SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), @@ -625,8 +632,8 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
static const struct snd_soc_dapm_route intercon[] = { /* outputs */ - {"OUTL", NULL, "DACL"}, - {"OUTR", NULL, "DACR"}, + {"OUTL", NULL, "DACL2"}, + {"OUTR", NULL, "DACR2"},
/* inputs */ {"ADCL", NULL, "INL"},
Add all four APGA switch to DAPM routing and widgets.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 22 ++++++++++++++++++++-- 1 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 3543bf6..11a6610 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -626,14 +626,32 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACL2", "Left Rear Playback", TWL4030_REG_AVDAC_CTL, 3, 0),
+ /* Analog PGAs + * bit 0: APGA enable + * bit 1: Digital to analog path enable + */ + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXR1_APGA", + TWL4030_REG_ARXR1_APGA_CTL, 0, 0x3, 0x3, 0x0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXL1_APGA", + TWL4030_REG_ARXL1_APGA_CTL, 0, 0x3, 0x3, 0x0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXR2_APGA", + TWL4030_REG_ARXR2_APGA_CTL, 0, 0x3, 0x3, 0x0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXL2_APGA", + TWL4030_REG_ARXL2_APGA_CTL, 0, 0x3, 0x3, 0x0), + SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), };
static const struct snd_soc_dapm_route intercon[] = { + {"ARXL1_APGA", NULL, "DACL1"}, + {"ARXR1_APGA", NULL, "DACR1"}, + {"ARXL2_APGA", NULL, "DACL2"}, + {"ARXR2_APGA", NULL, "DACR2"}, + /* outputs */ - {"OUTL", NULL, "DACL2"}, - {"OUTR", NULL, "DACR2"}, + {"OUTL", NULL, "ARXL2_APGA"}, + {"OUTR", NULL, "ARXR2_APGA"},
/* inputs */ {"ADCL", NULL, "INL"},
DAPM event handler is set to filter out invalid MUX settings for certain outputs. Earpiece: - 0 = Off - 1 = DACL1 - 2 = DACL2 - 3 = *** Invalid *** - 4 = DACR1
PreDriveL/R: - 0 = Off/Off - 1 = DACL1/DACR1 - 2 = DACL2/DACR2 - 3 = *** Invalid/Invalid *** - 4 = DACR2/DACL2
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 24 ++++++++++++++++++++++++ 1 files changed, 24 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 11a6610..6e7b5fc 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -190,6 +190,30 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
}
+static int outmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int ret = 0; + int val; + + switch (e->reg) { + case TWL4030_REG_PREDL_CTL: + case TWL4030_REG_PREDR_CTL: + case TWL4030_REG_EAR_CTL: + val = w->value >> e->shift_l; + if (val == 3) { + printk(KERN_WARNING + "Invalid MUX setting for register 0x%02x (%d)\n", + e->reg, val); + ret = -1; + } + break; + } + + return ret; +} + /* * Some of the gain controls in TWL (mostly those which are associated with * the outputs) are implemented in an interesting way:
Adds DAPM muxing, routing for the Earpiece output.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 27 +++++++++++++++++++++++++++ 1 files changed, 27 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 6e7b5fc..83eaaee 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -190,6 +190,19 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
}
+/* Earpiece */ +static const char *twl4030_earpiece_texts[] = + {"Off", "DACL1 Switch", "DACL2 Switch", "Invalid", + "DACR1 Switch"}; + +static const struct soc_enum twl4030_earpiece_enum = + SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, + ARRAY_SIZE(twl4030_earpiece_texts), + twl4030_earpiece_texts); + +static const struct snd_kcontrol_new twl4030_dapm_erapiece_controls = +SOC_DAPM_ENUM("Route", twl4030_earpiece_enum); + static int outmixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -639,6 +652,7 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"), + SND_SOC_DAPM_OUTPUT("EARPIECE"),
/* DACs */ SND_SOC_DAPM_DAC("DACR1", "Right Front Playback", @@ -663,6 +677,12 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL, 0, 0x3, 0x3, 0x0),
+ /* Output MUX controls */ + /* Earpiece */ + SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_erapiece_controls, outmixer_event, + SND_SOC_DAPM_PRE_REG), + SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), }; @@ -673,9 +693,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"ARXL2_APGA", NULL, "DACL2"}, {"ARXR2_APGA", NULL, "DACR2"},
+ /* Internal playback routings */ + /* Earpiece */ + {"Earpiece Mux", "DACL1 Switch", "ARXL1_APGA"}, + {"Earpiece Mux", "DACL2 Switch", "ARXL2_APGA"}, + {"Earpiece Mux", "DACR1 Switch", "ARXR1_APGA"}, + /* outputs */ {"OUTL", NULL, "ARXL2_APGA"}, {"OUTR", NULL, "ARXR2_APGA"}, + {"EARPIECE", NULL, "Earpiece Mux"},
/* inputs */ {"ADCL", NULL, "INL"},
Adds DAPM muxing, routing for the PreDrive outputs.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 83eaaee..c31f328 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -203,6 +203,32 @@ static const struct soc_enum twl4030_earpiece_enum = static const struct snd_kcontrol_new twl4030_dapm_erapiece_controls = SOC_DAPM_ENUM("Route", twl4030_earpiece_enum);
+/* PreDrive Left */ +static const char *twl4030_predrivel_texts[] = + {"Off", "DACL1 Switch", "DACL2 Switch", "Invalid", + "DACR2 Switch"}; + +static const struct soc_enum twl4030_predrivel_enum = + SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, + ARRAY_SIZE(twl4030_predrivel_texts), + twl4030_predrivel_texts); + +static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls = +SOC_DAPM_ENUM("Route", twl4030_predrivel_enum); + +/* PreDrive Right */ +static const char *twl4030_predriver_texts[] = + {"Off", "DACR1 Switch", "DACR2 Switch", "Invalid", + "DACL2 Switch"}; + +static const struct soc_enum twl4030_predriver_enum = + SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, + ARRAY_SIZE(twl4030_predriver_texts), + twl4030_predriver_texts); + +static const struct snd_kcontrol_new twl4030_dapm_predriver_controls = +SOC_DAPM_ENUM("Route", twl4030_predriver_enum); + static int outmixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -653,6 +679,8 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"), SND_SOC_DAPM_OUTPUT("EARPIECE"), + SND_SOC_DAPM_OUTPUT("PREDRIVEL"), + SND_SOC_DAPM_OUTPUT("PREDRIVER"),
/* DACs */ SND_SOC_DAPM_DAC("DACR1", "Right Front Playback", @@ -682,6 +710,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_erapiece_controls, outmixer_event, SND_SOC_DAPM_PRE_REG), + /* PreDrivL/R */ + SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_predrivel_controls, outmixer_event, + SND_SOC_DAPM_PRE_REG), + SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_predriver_controls, outmixer_event, + SND_SOC_DAPM_PRE_REG),
SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), @@ -698,11 +733,21 @@ static const struct snd_soc_dapm_route intercon[] = { {"Earpiece Mux", "DACL1 Switch", "ARXL1_APGA"}, {"Earpiece Mux", "DACL2 Switch", "ARXL2_APGA"}, {"Earpiece Mux", "DACR1 Switch", "ARXR1_APGA"}, + /* PreDrivL */ + {"PredriveL Mux", "DACL1 Switch", "ARXL1_APGA"}, + {"PredriveL Mux", "DACL2 Switch", "ARXL2_APGA"}, + {"PredriveL Mux", "DACR2 Switch", "ARXR2_APGA"}, + /* PreDrivR */ + {"PredriveR Mux", "DACR1 Switch", "ARXR1_APGA"}, + {"PredriveR Mux", "DACR2 Switch", "ARXR2_APGA"}, + {"PredriveR Mux", "DACL2 Switch", "ARXL2_APGA"},
/* outputs */ {"OUTL", NULL, "ARXL2_APGA"}, {"OUTR", NULL, "ARXR2_APGA"}, {"EARPIECE", NULL, "Earpiece Mux"}, + {"PREDRIVEL", NULL, "PredriveL Mux"}, + {"PREDRIVER", NULL, "PredriveR Mux"},
/* inputs */ {"ADCL", NULL, "INL"},
Adds DAPM muxing, routing for the Headset outputs.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 39 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index c31f328..4aab5a2 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -229,6 +229,30 @@ static const struct soc_enum twl4030_predriver_enum = static const struct snd_kcontrol_new twl4030_dapm_predriver_controls = SOC_DAPM_ENUM("Route", twl4030_predriver_enum);
+/* Headset Left */ +static const char *twl4030_hsol_texts[] = + {"Off", "DACL1 Switch", "DACL2 Switch"}; + +static const struct soc_enum twl4030_hsol_enum = + SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1, + ARRAY_SIZE(twl4030_hsol_texts), + twl4030_hsol_texts); + +static const struct snd_kcontrol_new twl4030_dapm_hsol_controls = +SOC_DAPM_ENUM("Route", twl4030_hsol_enum); + +/* Headset Right */ +static const char *twl4030_hsor_texts[] = + {"Off", "DACR1 Switch", "DACR2 Switch"}; + +static const struct soc_enum twl4030_hsor_enum = + SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4, + ARRAY_SIZE(twl4030_hsor_texts), + twl4030_hsor_texts); + +static const struct snd_kcontrol_new twl4030_dapm_hsor_controls = +SOC_DAPM_ENUM("Route", twl4030_hsor_enum); + static int outmixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -681,6 +705,8 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("EARPIECE"), SND_SOC_DAPM_OUTPUT("PREDRIVEL"), SND_SOC_DAPM_OUTPUT("PREDRIVER"), + SND_SOC_DAPM_OUTPUT("HSOL"), + SND_SOC_DAPM_OUTPUT("HSOR"),
/* DACs */ SND_SOC_DAPM_DAC("DACR1", "Right Front Playback", @@ -717,6 +743,11 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_predriver_controls, outmixer_event, SND_SOC_DAPM_PRE_REG), + /* HeadsetL/R */ + SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_hsol_controls), + SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_hsor_controls),
SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), @@ -741,6 +772,12 @@ static const struct snd_soc_dapm_route intercon[] = { {"PredriveR Mux", "DACR1 Switch", "ARXR1_APGA"}, {"PredriveR Mux", "DACR2 Switch", "ARXR2_APGA"}, {"PredriveR Mux", "DACL2 Switch", "ARXL2_APGA"}, + /* HeadsetL */ + {"HeadsetL Mux", "DACL1 Switch", "ARXL1_APGA"}, + {"HeadsetL Mux", "DACL2 Switch", "ARXL2_APGA"}, + /* HeadsetR */ + {"HeadsetR Mux", "DACR1 Switch", "ARXR1_APGA"}, + {"HeadsetR Mux", "DACR2 Switch", "ARXR2_APGA"},
/* outputs */ {"OUTL", NULL, "ARXL2_APGA"}, @@ -748,6 +785,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"EARPIECE", NULL, "Earpiece Mux"}, {"PREDRIVEL", NULL, "PredriveL Mux"}, {"PREDRIVER", NULL, "PredriveR Mux"}, + {"HSOL", NULL, "HeadsetL Mux"}, + {"HSOR", NULL, "HeadsetR Mux"},
/* inputs */ {"ADCL", NULL, "INL"},
Adds DAPM muxing, routing for the Carkit outputs.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 37 +++++++++++++++++++++++++++++++++++++ 1 files changed, 37 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 4aab5a2..4fb16ec 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -253,6 +253,30 @@ static const struct soc_enum twl4030_hsor_enum = static const struct snd_kcontrol_new twl4030_dapm_hsor_controls = SOC_DAPM_ENUM("Route", twl4030_hsor_enum);
+/* Carkit Left */ +static const char *twl4030_carkitl_texts[] = + {"Off", "DACL1 Switch", "DACL2 Switch"}; + +static const struct soc_enum twl4030_carkitl_enum = + SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1, + ARRAY_SIZE(twl4030_carkitl_texts), + twl4030_carkitl_texts); + +static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls = +SOC_DAPM_ENUM("Route", twl4030_carkitl_enum); + +/* Carkit Right */ +static const char *twl4030_carkitr_texts[] = + {"Off", "DACR1 Switch", "DACR2 Switch"}; + +static const struct soc_enum twl4030_carkitr_enum = + SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1, + ARRAY_SIZE(twl4030_carkitr_texts), + twl4030_carkitr_texts); + +static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls = +SOC_DAPM_ENUM("Route", twl4030_carkitr_enum); + static int outmixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -748,6 +772,11 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { &twl4030_dapm_hsol_controls), SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_hsor_controls), + /* CarkitL/R */ + SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_carkitl_controls), + SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_carkitr_controls),
SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), @@ -778,6 +807,12 @@ static const struct snd_soc_dapm_route intercon[] = { /* HeadsetR */ {"HeadsetR Mux", "DACR1 Switch", "ARXR1_APGA"}, {"HeadsetR Mux", "DACR2 Switch", "ARXR2_APGA"}, + /* CarkitL */ + {"CarkitL Mux", "DACL1 Switch", "ARXL1_APGA"}, + {"CarkitL Mux", "DACL2 Switch", "ARXL2_APGA"}, + /* CarkitR */ + {"CarkitR Mux", "DACR1 Switch", "ARXR1_APGA"}, + {"CarkitR Mux", "DACR2 Switch", "ARXR2_APGA"},
/* outputs */ {"OUTL", NULL, "ARXL2_APGA"}, @@ -787,6 +822,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PREDRIVER", NULL, "PredriveR Mux"}, {"HSOL", NULL, "HeadsetL Mux"}, {"HSOR", NULL, "HeadsetR Mux"}, + {"CARKITL", NULL, "CarkitL Mux"}, + {"CARKITR", NULL, "CarkitR Mux"},
/* inputs */ {"ADCL", NULL, "INL"},
Adds DAPM muxing, routing for the Handsfree outputs.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 41 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 4fb16ec..334f625 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -277,6 +277,30 @@ static const struct soc_enum twl4030_carkitr_enum = static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls = SOC_DAPM_ENUM("Route", twl4030_carkitr_enum);
+/* Handsfree Left */ +static const char *twl4030_handsfreel_texts[] = + {"Voice", "DACL1 Switch", "DACL2 Switch", "DACR2 Switch"}; + +static const struct soc_enum twl4030_handsfreel_enum = + SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0, + ARRAY_SIZE(twl4030_handsfreel_texts), + twl4030_handsfreel_texts); + +static const struct snd_kcontrol_new twl4030_dapm_handsfreel_controls = +SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum); + +/* Handsfree Right */ +static const char *twl4030_handsfreer_texts[] = + {"Voice", "DACR1 Switch", "DACR2 Switch", "DACL2 Switch"}; + +static const struct soc_enum twl4030_handsfreer_enum = + SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0, + ARRAY_SIZE(twl4030_handsfreer_texts), + twl4030_handsfreer_texts); + +static const struct snd_kcontrol_new twl4030_dapm_handsfreer_controls = +SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); + static int outmixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -731,6 +755,8 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("PREDRIVER"), SND_SOC_DAPM_OUTPUT("HSOL"), SND_SOC_DAPM_OUTPUT("HSOR"), + SND_SOC_DAPM_OUTPUT("HFL"), + SND_SOC_DAPM_OUTPUT("HFR"),
/* DACs */ SND_SOC_DAPM_DAC("DACR1", "Right Front Playback", @@ -777,6 +803,11 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { &twl4030_dapm_carkitl_controls), SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_carkitr_controls), + /* HandsfreeL/R */ + SND_SOC_DAPM_MUX("HandsfreeL Mux", TWL4030_REG_HFL_CTL, 5, 0, + &twl4030_dapm_handsfreel_controls), + SND_SOC_DAPM_MUX("HandsfreeR Mux", TWL4030_REG_HFR_CTL, 5, 0, + &twl4030_dapm_handsfreer_controls),
SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), @@ -813,6 +844,14 @@ static const struct snd_soc_dapm_route intercon[] = { /* CarkitR */ {"CarkitR Mux", "DACR1 Switch", "ARXR1_APGA"}, {"CarkitR Mux", "DACR2 Switch", "ARXR2_APGA"}, + /* HandsfreeL */ + {"HandsfreeL Mux", "DACL1 Switch", "ARXL1_APGA"}, + {"HandsfreeL Mux", "DACL2 Switch", "ARXL2_APGA"}, + {"HandsfreeL Mux", "DACR2 Switch", "ARXR2_APGA"}, + /* HandsfreeR */ + {"HandsfreeR Mux", "DACR1 Switch", "ARXR1_APGA"}, + {"HandsfreeR Mux", "DACR2 Switch", "ARXR2_APGA"}, + {"HandsfreeR Mux", "DACL2 Switch", "ARXL2_APGA"},
/* outputs */ {"OUTL", NULL, "ARXL2_APGA"}, @@ -824,6 +863,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HSOR", NULL, "HeadsetR Mux"}, {"CARKITL", NULL, "CarkitL Mux"}, {"CARKITR", NULL, "CarkitR Mux"}, + {"HFL", NULL, "HandsfreeL Mux"}, + {"HFR", NULL, "HandsfreeR Mux"},
/* inputs */ {"ADCL", NULL, "INL"},
On Mon, Dec 08, 2008 at 10:09:05AM +0200, Peter Ujfalusi wrote:
+static const char *twl4030_predrivel_texts[] =
{"Off", "DACL1 Switch", "DACL2 Switch", "Invalid",
"DACR2 Switch"};
Why the "Switch" in the option names - I'd expect to see it omitted. Does the "Invalid" option work well in applications like alsamixer?
Hello,
On Monday 08 December 2008 13:28:48 ext Mark Brown wrote:
On Mon, Dec 08, 2008 at 10:09:05AM +0200, Peter Ujfalusi wrote:
+static const char *twl4030_predrivel_texts[] =
{"Off", "DACL1 Switch", "DACL2 Switch", "Invalid",
"DACR2 Switch"};
Why the "Switch" in the option names - I'd expect to see it omitted.
They will be gone.
Does the "Invalid" option work well in applications like alsamixer?
Not quite well, the outmixer_event will just plain forbits the Invalid combination (returns -1, which should have been -EINVAL). In alsamixer you can not really select the "DACR2". With amixer you can. I have been wondering about this for a while but could not find a better solution, yet.
On Mon, Dec 08, 2008 at 01:40:05PM +0200, Peter Ujfalusi wrote:
On Monday 08 December 2008 13:28:48 ext Mark Brown wrote:
Does the "Invalid" option work well in applications like alsamixer?
Not quite well, the outmixer_event will just plain forbits the Invalid combination (returns -1, which should have been -EINVAL). In alsamixer you can not really select the "DACR2". With amixer you can.
Eew, ick. This isn't going to work nicely at all without custom controls or support in the core for masking out mux values.
I have been wondering about this for a while but could not find a better solution, yet.
Silently rewriting the values isn't nice but should work.
Given the nature of the control I suspect your current solution will be OK for now - I'd not expect it to be updated interactively too often.
On Mon, Dec 08, 2008 at 10:09:04AM +0200, Peter Ujfalusi wrote:
+static const struct snd_kcontrol_new twl4030_dapm_erapiece_controls = +SOC_DAPM_ENUM("Route", twl4030_earpiece_enum);
erapiece? :)
On Mon, Dec 08, 2008 at 10:09:02AM +0200, Peter Ujfalusi wrote:
- /* Analog PGAs
* bit 0: APGA enable
* bit 1: Digital to analog path enable
*/
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXR1_APGA",
TWL4030_REG_ARXR1_APGA_CTL, 0, 0x3, 0x3, 0x0),
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXL1_APGA",
TWL4030_REG_ARXL1_APGA_CTL, 0, 0x3, 0x3, 0x0),
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXR2_APGA",
TWL4030_REG_ARXR2_APGA_CTL, 0, 0x3, 0x3, 0x0),
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXL2_APGA",
TWL4030_REG_ARXL2_APGA_CTL, 0, 0x3, 0x3, 0x0),
The PGA should be standard SND_SOC_DAPM_PGA()s with the digital to analogue switches used to implement mute controls for them.
On Monday 08 December 2008 13:23:37 ext Mark Brown wrote:
On Mon, Dec 08, 2008 at 10:09:02AM +0200, Peter Ujfalusi wrote:
- /* Analog PGAs
* bit 0: APGA enable
* bit 1: Digital to analog path enable
*/
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXR1_APGA",
TWL4030_REG_ARXR1_APGA_CTL, 0, 0x3, 0x3, 0x0),
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXL1_APGA",
TWL4030_REG_ARXL1_APGA_CTL, 0, 0x3, 0x3, 0x0),
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXR2_APGA",
TWL4030_REG_ARXR2_APGA_CTL, 0, 0x3, 0x3, 0x0),
- SND_SOC_DAPM_REG(snd_soc_dapm_pga, "ARXL2_APGA",
TWL4030_REG_ARXL2_APGA_CTL, 0, 0x3, 0x3, 0x0),
The PGA should be standard SND_SOC_DAPM_PGA()s with the digital to analogue switches used to implement mute controls for them.
Should I 'chain' these switches, like this:
/* Digital to analog enable/disable */ SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL, 1, 0, NULL, 0), /* APGA enable/disable */ SND_SOC_DAPM_PGA("ARXR1_APGA Enable", TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0), ... {"ARXR1_APGA Enable", NULL, "DACR1"}, {"ARXR1_APGA", NULL, "ARXR1_APGA Enable"},
Or should I use the wcontrols, wncontrols to implement the DA enable/disable:
static const struct snd_kcontrol_new arxr1_apga_controls[] = { SOC_DAPM_SINGLE("ARXR1_APGA Switch", TWL4030_REG_ARXR1_APGA_CTL, 1, 1, 0), }; ... SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL, 0, 0, &arxr1_apga_controls[0], ARRAY_SIZE(arxr1_apga_controls)),
If the later is preferred, than probably it would be better to move the "DAC1 Analog Playback Volume" and "DAC2 Analog Playback Volume" from the twl4030_snd_controls to twl4030_dapm_widgets, separate the four paths and use SOC_DAPM_SINGLE_TLV("ARXR1_APGA Playback Volume" ? Hmm, in this case the SOC_DAPM_SINGLE_TLV can be also part of the arxr1_apga_controls...
On Mon, Dec 08, 2008 at 02:06:08PM +0200, Peter Ujfalusi wrote:
On Monday 08 December 2008 13:23:37 ext Mark Brown wrote:
The PGA should be standard SND_SOC_DAPM_PGA()s with the digital to analogue switches used to implement mute controls for them.
Should I 'chain' these switches, like this:
/* Digital to analog enable/disable */ SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL, 1, 0, NULL, 0), /* APGA enable/disable */ SND_SOC_DAPM_PGA("ARXR1_APGA Enable", TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0),
I'd expect to see user control here...
Or should I use the wcontrols, wncontrols to implement the DA enable/disable:
You can do that, or just do it as a normal control (which allows you to make the control a stereo control). This lets the user control the switch which is generally more useful.
If the later is preferred, than probably it would be better to move the "DAC1 Analog Playback Volume" and "DAC2 Analog Playback Volume" from the twl4030_snd_controls to twl4030_dapm_widgets, separate the four paths and use SOC_DAPM_SINGLE_TLV("ARXR1_APGA Playback Volume" ? Hmm, in this case the SOC_DAPM_SINGLE_TLV can be also part of the arxr1_apga_controls...
Indeed, and if you do that DAPM will try to ramp up and down the volume using the first control after and before changing the power. This feature is rarely used since it's not normally needed for performance.
On Mon, Dec 08, 2008 at 10:09:00AM +0200, Peter Ujfalusi wrote:
Peter Ujfalusi (8): ASoC: TWL4030: Correct DAPM_DAC with power control ASoC: TWL4030: Add Analog PGA control switch to DAPM ASoC: TWL4030: Add DAPM event handler for output MUX selection ASoC: TWL4030: DAPM mapping of the Earpiece output ASoC: TWL4030: DAPM mapping of the PreDriv outputs ASoC: TWL4030: DAPM mapping of the Headset outputs ASoC: TWL4030: DAPM mapping of the Carkit outputs ASoC: TWL4030: DAPM mapping of the Handsfree outputs
This series looks basically good - I've commented on a few issues in the individual patches and I'd also note that you seem to be using "controls" to refer to the single control for muxes in most of the patches, which is a little confusing.
On Monday 08 December 2008 15:20:41 ext Mark Brown wrote:
On Mon, Dec 08, 2008 at 10:09:00AM +0200, Peter Ujfalusi wrote:
Based on the output routing selections all the corresponding DAC and analog PGA on the chain will be powered on.
One other question: how does this all interact with the existing power control in set_bias_level()?
This is a good question. As I see: when the twl4030_power_down is called from the twl4030_set_bias_level (SND_SOC_BIAS_STANDBY, SND_SOC_BIAS_OFF) it sets the codec to power-off state (CODEC_MODE:CODECPDZ = 0), which effectively turns off the digital filter, analog section, so basically everything. When the CODECPDZ is 0, APGA, DAC, analog output settings have no effect (they are powered off)
On Tue, Dec 09, 2008 at 08:51:58AM +0200, Peter Ujfalusi wrote:
As I see: when the twl4030_power_down is called from the twl4030_set_bias_level (SND_SOC_BIAS_STANDBY, SND_SOC_BIAS_OFF) it sets the codec to power-off state (CODEC_MODE:CODECPDZ = 0), which effectively turns off the digital filter, analog section, so basically everything. When the CODECPDZ is 0, APGA, DAC, analog output settings have no effect (they are powered off)
Right, that's what I thought. You'll need to fix this in order to use bypass paths since it will turn off the analogue.
On Tuesday 09 December 2008 11:47:01 ext Mark Brown wrote:
On Tue, Dec 09, 2008 at 08:51:58AM +0200, Peter Ujfalusi wrote:
As I see: when the twl4030_power_down is called from the twl4030_set_bias_level (SND_SOC_BIAS_STANDBY, SND_SOC_BIAS_OFF) it sets the codec to power-off state (CODEC_MODE:CODECPDZ = 0), which effectively turns off the digital filter, analog section, so basically everything. When the CODECPDZ is 0, APGA, DAC, analog output settings have no effect (they are powered off)
Right, that's what I thought. You'll need to fix this in order to use bypass paths since it will turn off the analogue.
Yes, that is true.
My plan is (yes I do have one): 1. Proper (at least as close to) implementation for the playback path(s) 2. Proper capture implementation (DAPM, etc) 3. Adding the bypass routes 4. The driver is kind of feature complete
participants (2)
-
Mark Brown
-
Peter Ujfalusi