[alsa-devel] [PATCH 0/9] ASoC: TWL4030: Add mixer controls for outputs
Hello,
I have tried to clean up the changes that I have at the momnent. After the series you will have 'ultimate' control on how the audio is routed to the outputs of TWL. One can select the possible inputs (for the output) on all cahnnels. Also I have added gain (volume) controls, wherever it is possible, but they are working for both channels in case of stereo outputs.
I tried to name the controls to match with the output's name, so you can easily tell which pin(s) the control will touch.
The capture part is not touched, yet.
I have not included the changes regarding to the SND_SOC_DAPM_OUTPUT() things. So you have one (actually two) outputs and you can use the mixer interface to select where the audio actually routed.
The next step is to make correct dapm mapping for the TWL, but that will take some time.
Please comment, if I'm doing something that I should not.
--- Peter Ujfalusi (9): ASoC: TWL4030: Change the Master volume control to TLV ASoC: TWL4030: Add CGAIN volume control ASoC: TWL4030: Add helper function for mux handling. ASoC: TWL4030: Add helper function for output gain controls ASoC: TWL4030: Add PreDriv outupt mux and volume controls ASoC: TWL4030: Add Headset outupt mux and volume controls ASoC: TWL4030: Add Carkit outupt mux and volume controls ASoC: TWL4030: Add Hands-free outupt mux control ASoC: TWL4030: Add Earpiece outupt mux and volume controls
sound/soc/codecs/twl4030.c | 298 +++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 296 insertions(+), 2 deletions(-)
TWL4030 FGAIN volume control has a range: -62 to 0 dB in 1 dB steps, 0 in the FGAIN means mute.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 11 +++++++++-- 1 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 49c7da3..498c42f 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -33,6 +33,7 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> +#include <sound/tlv.h>
#include "twl4030.h"
@@ -189,10 +190,16 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
}
+/* + * FGAIN volume control: + * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) + */ +static DECLARE_TLV_DB_SCALE(master_tlv, -6300, 100, 1); + static const struct snd_kcontrol_new twl4030_snd_controls[] = { - SOC_DOUBLE_R("Master Playback Volume", + SOC_DOUBLE_R_TLV("Master Playback Volume", TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, - 0, 0x3f, 0), + 0, 0x3f, 0, master_tlv), SOC_DOUBLE_R("Capture Volume", TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, 0, 0x1f, 0),
Add CGAIN (Coarse gain control) to TWL4030 codec. The range of the CGAIN is: 0 dB to 12 dB in 6 dB steps.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 498c42f..91effd3 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -196,10 +196,20 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) */ static DECLARE_TLV_DB_SCALE(master_tlv, -6300, 100, 1);
+/* + * CGAIN volume control: + * 0 dB to 12 dB in 6 dB steps + * value 2 and 3 means 12 dB + */ +static DECLARE_TLV_DB_SCALE(master_coarse_tlv, 0, 600, 0); + static const struct snd_kcontrol_new twl4030_snd_controls[] = { SOC_DOUBLE_R_TLV("Master Playback Volume", TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, 0, 0x3f, 0, master_tlv), + SOC_DOUBLE_R_TLV("Master PCM Playback Volume", + TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, + 6, 0x2, 0, master_coarse_tlv), SOC_DOUBLE_R("Capture Volume", TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, 0, 0x1f, 0),
Some of TWL4030's input selection are in bitfield, which needs special helper function to deal with. For example: PREDL_CTL register: bit 0 (0x1): voice_en bit 1 (0x2): Audio L1 enable (DACL1) bit 2 (0x4): Audio L2 enable (DACL2) bit 3 (0x8): Audio R2 enable (DACR2)
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 51 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 91effd3..17f2b51 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -191,6 +191,57 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) }
/* + * Some of TWL's input selection are in bitfield. + * Dealing with those need special function + * For example: PREDL_CTL register: + * bit 0 (0x1): voice_en + * bit 1 (0x2): Audio L1 enable (DACL1) + * bit 2 (0x4): Audio L2 enable (DACL2) + * bit 3 (0x8): Audio R2 enable (DACR2) + */ +static int snd_soc_get_enum_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int val_mask = 0; + int i; + int val = twl4030_read_reg_cache(codec, e->reg); + + for (i = 0; i < e->max - 1; i++) + val_mask |= (1 << i); + + val = (val >> e->shift_l) & val_mask; + i = 0; + while (val && i++ < e->max) + val >>= 1; + + ucontrol->value.integer.value[0] = i; + return 0; +} + +static int snd_soc_put_enum_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int val_mask = 0; + int val = 0; + int i; + + for (i = 0; i < e->max - 1; i++) + val_mask |= (1 << i); + val_mask <<= e->shift_l; + + if (ucontrol->value.integer.value[0]) { + val = (1 << (ucontrol->value.integer.value[0] - 1)); + val <<= e->shift_l; + } + + return snd_soc_update_bits(codec, e->reg, val_mask, val); +} + +/* * FGAIN volume control: * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) */
Some of the gain controls in TWL (mostly those which are associated with the outputs) are implemented in an interesting way: 0x0 : Power down (mute) 0x1 : 6dB 0x2 : 0 dB 0x3 : -6 dB Inverting not going to help with these. Custom volsw and volsw_2r get/put functions to handle these gains.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 157 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 157 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 17f2b51..b0975a9 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -242,6 +242,163 @@ static int snd_soc_put_enum_twl4030(struct snd_kcontrol *kcontrol, }
/* + * Some of the gain controls in TWL (mostly those which are associated with + * the outputs) are implemented in an interesting way: + * 0x0 : Power down (mute) + * 0x1 : 6dB + * 0x2 : 0 dB + * 0x3 : -6 dB + * Inverting not going to help with these. + * Custom volsw and volsw_2r get/put functions to handle these gain bits. + */ +#define SOC_DOUBLE_TLV_TWL4030(xname, xreg, shift_left, shift_right, xmax,\ + xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw_twl4030, \ + .put = snd_soc_put_volsw_twl4030, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = shift_left, .rshift = shift_right,\ + .max = xmax, .invert = xinvert} } +#define SOC_DOUBLE_R_TLV_TWL4030(xname, reg_left, reg_right, xshift, xmax,\ + xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_2r, \ + .get = snd_soc_get_volsw_r2_twl4030,\ + .put = snd_soc_put_volsw_r2_twl4030, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ + .max = xmax, .invert = xinvert} } +#define SOC_SINGLE_TLV_TWL4030(xname, xreg, xshift, xmax, xinvert, tlv_array) \ + SOC_DOUBLE_TLV_TWL4030(xname, xreg, xshift, xshift, xmax, \ + xinvert, tlv_array) + +static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int mask = (1 << fls(max)) - 1; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, reg) >> shift) & mask; + if (ucontrol->value.integer.value[0]) + ucontrol->value.integer.value[0] = + max + 1 - ucontrol->value.integer.value[0]; + + if (shift != rshift) { + ucontrol->value.integer.value[1] = + (snd_soc_read(codec, reg) >> rshift) & mask; + if (ucontrol->value.integer.value[1]) + ucontrol->value.integer.value[1] = + max + 1 - ucontrol->value.integer.value[1]; + } + + return 0; +} + +static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int mask = (1 << fls(max)) - 1; + unsigned short val, val2, val_mask; + + val = (ucontrol->value.integer.value[0] & mask); + + val_mask = mask << shift; + if (val) + val = max + 1 - val; + val = val << shift; + if (shift != rshift) { + val2 = (ucontrol->value.integer.value[1] & mask); + val_mask |= mask << rshift; + if (val2) + val2 = max + 1 - val2; + val |= val2 << rshift; + } + return snd_soc_update_bits(codec, reg, val_mask, val); +} + +static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + int max = mc->max; + int mask = (1<<fls(max))-1; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, reg) >> shift) & mask; + ucontrol->value.integer.value[1] = + (snd_soc_read(codec, reg2) >> shift) & mask; + + if (ucontrol->value.integer.value[0]) + ucontrol->value.integer.value[0] = + max + 1 - ucontrol->value.integer.value[0]; + if (ucontrol->value.integer.value[1]) + ucontrol->value.integer.value[1] = + max + 1 - ucontrol->value.integer.value[1]; + + return 0; +} + +static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + int max = mc->max; + int mask = (1 << fls(max)) - 1; + int err; + unsigned short val, val2, val_mask; + + val_mask = mask << shift; + val = (ucontrol->value.integer.value[0] & mask); + val2 = (ucontrol->value.integer.value[1] & mask); + + if (val) + val = max + 1 - val; + if (val2) + val2 = max + 1 - val2; + + val = val << shift; + val2 = val2 << shift; + + err = snd_soc_update_bits(codec, reg, val_mask, val); + if (err < 0) + return err; + + err = snd_soc_update_bits(codec, reg2, val_mask, val2); + return err; +} + +/* * FGAIN volume control: * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) */
This patch adds the routing selection for the PreDriv output and PreDriv gain control.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 26 ++++++++++++++++++++++++++ 1 files changed, 26 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index b0975a9..cee52ab 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -190,6 +190,16 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
}
+static const char *twl4030_predrivl_mix[] = {"Off", "Voice", "DACL1", + "DACL2", "DACR2"}; +static const char *twl4030_predrivr_mix[] = {"Off", "Voice", "DACR1", + "DACR2", "DACL2"}; + + +static const struct soc_enum twl4030_enum[] = { + SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 0, 5, twl4030_predrivl_mix), + SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 0, 5, twl4030_predrivr_mix), +}; /* * Some of TWL's input selection are in bitfield. * Dealing with those need special function @@ -411,6 +421,12 @@ static DECLARE_TLV_DB_SCALE(master_tlv, -6300, 100, 1); */ static DECLARE_TLV_DB_SCALE(master_coarse_tlv, 0, 600, 0);
+/* + * Output gain controls + * -6 dB to 6 dB in 6 dB steps (mute instead of -12) + */ +static DECLARE_TLV_DB_SCALE(gainv1_tvl, -1200, 600, 1); + static const struct snd_kcontrol_new twl4030_snd_controls[] = { SOC_DOUBLE_R_TLV("Master Playback Volume", TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, @@ -421,6 +437,16 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { SOC_DOUBLE_R("Capture Volume", TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, 0, 0x1f, 0), + + /* PreDrive output controls*/ + SOC_ENUM_EXT("PreDriveL Playback Mux", twl4030_enum[0], + snd_soc_get_enum_twl4030, snd_soc_put_enum_twl4030), + SOC_ENUM_EXT("PreDriveR Playback Mux", twl4030_enum[1], + snd_soc_get_enum_twl4030, snd_soc_put_enum_twl4030), + SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume", + TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL, + 4, 3, 0, gainv1_tvl), + };
/* add non dapm controls */
This patch adds the routing selection for the headset output and Headset gain control.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index cee52ab..b3cd498 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -194,11 +194,15 @@ static const char *twl4030_predrivl_mix[] = {"Off", "Voice", "DACL1", "DACL2", "DACR2"}; static const char *twl4030_predrivr_mix[] = {"Off", "Voice", "DACR1", "DACR2", "DACL2"}; +static const char *twl4030_hsoutl_mix[] = {"Off", "Voice", "DACL1", "DACL2"}; +static const char *twl4030_hsoutr_mix[] = {"Off", "Voice", "DACR1", "DACR2"};
static const struct soc_enum twl4030_enum[] = { SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 0, 5, twl4030_predrivl_mix), SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 0, 5, twl4030_predrivr_mix), + SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 0, 4, twl4030_hsoutl_mix), + SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 3, 4, twl4030_hsoutr_mix), }; /* * Some of TWL's input selection are in bitfield. @@ -447,6 +451,14 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL, 4, 3, 0, gainv1_tvl),
+ /* Headset output controls*/ + SOC_ENUM_EXT("HeadsetL Playback Mux", twl4030_enum[2], + snd_soc_get_enum_twl4030, snd_soc_put_enum_twl4030), + SOC_ENUM_EXT("HeadsetR Playback Mux", twl4030_enum[3], + snd_soc_get_enum_twl4030, snd_soc_put_enum_twl4030), + SOC_DOUBLE_TLV_TWL4030("Headset Playback Volume", + TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, gainv1_tvl), + };
/* add non dapm controls */
This patch adds the routing selection for the Carkit output and Carkit gain control.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index b3cd498..03b0431 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -196,6 +196,8 @@ static const char *twl4030_predrivr_mix[] = {"Off", "Voice", "DACR1", "DACR2", "DACL2"}; static const char *twl4030_hsoutl_mix[] = {"Off", "Voice", "DACL1", "DACL2"}; static const char *twl4030_hsoutr_mix[] = {"Off", "Voice", "DACR1", "DACR2"}; +static const char *twl4030_carkitl_mix[] = {"Off", "Voice", "DACL1", "DACL2"}; +static const char *twl4030_carkitr_mix[] = {"Off", "Voice", "DACR1", "DACR2"};
static const struct soc_enum twl4030_enum[] = { @@ -203,6 +205,8 @@ static const struct soc_enum twl4030_enum[] = { SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 0, 5, twl4030_predrivr_mix), SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 0, 4, twl4030_hsoutl_mix), SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 3, 4, twl4030_hsoutr_mix), + SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 0, 4, twl4030_carkitl_mix), + SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 0, 4, twl4030_carkitr_mix), }; /* * Some of TWL's input selection are in bitfield. @@ -459,6 +463,14 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { SOC_DOUBLE_TLV_TWL4030("Headset Playback Volume", TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, gainv1_tvl),
+ /* Carkit output controls*/ + SOC_ENUM_EXT("CarkitL Playback Mux", twl4030_enum[4], + snd_soc_get_enum_twl4030, snd_soc_put_enum_twl4030), + SOC_ENUM_EXT("CarkitR Playback Mux", twl4030_enum[5], + snd_soc_get_enum_twl4030, snd_soc_put_enum_twl4030), + SOC_DOUBLE_R_TLV_TWL4030("Carkit Playback Volume", + TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL, + 4, 3, 0, gainv1_tvl), };
/* add non dapm controls */
This patch adds the routing selection for the Hands-free output.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 03b0431..88f5cbe 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -198,6 +198,10 @@ static const char *twl4030_hsoutl_mix[] = {"Off", "Voice", "DACL1", "DACL2"}; static const char *twl4030_hsoutr_mix[] = {"Off", "Voice", "DACR1", "DACR2"}; static const char *twl4030_carkitl_mix[] = {"Off", "Voice", "DACL1", "DACL2"}; static const char *twl4030_carkitr_mix[] = {"Off", "Voice", "DACR1", "DACR2"}; +static const char *twl4030_handsfreel_mix[] = {"Voice", "DACL1", + "DACL2", "DACR2"}; +static const char *twl4030_handsfreer_mix[] = {"Voice", "DACR1", + "DACR2", "DACL2"};
static const struct soc_enum twl4030_enum[] = { @@ -207,6 +211,8 @@ static const struct soc_enum twl4030_enum[] = { SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 3, 4, twl4030_hsoutr_mix), SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 0, 4, twl4030_carkitl_mix), SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 0, 4, twl4030_carkitr_mix), + SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0, 4, twl4030_handsfreel_mix), + SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0, 4, twl4030_handsfreer_mix), }; /* * Some of TWL's input selection are in bitfield. @@ -471,6 +477,10 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { SOC_DOUBLE_R_TLV_TWL4030("Carkit Playback Volume", TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL, 4, 3, 0, gainv1_tvl), + + /* Hand-free output controls*/ + SOC_ENUM("HandsfreeL Playback Mux", twl4030_enum[6]), + SOC_ENUM("HandsfreeR Playback Mux", twl4030_enum[7]), };
/* add non dapm controls */
This patch adds the routing selection for the Earpiece output and Earpiece gain control.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 88f5cbe..98d6bb0 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -202,6 +202,8 @@ static const char *twl4030_handsfreel_mix[] = {"Voice", "DACL1", "DACL2", "DACR2"}; static const char *twl4030_handsfreer_mix[] = {"Voice", "DACR1", "DACR2", "DACL2"}; +static const char *twl4030_earpiece_mix[] = {"Off", "Voice", "DACL1", + "DACL2", "DACR1"};
static const struct soc_enum twl4030_enum[] = { @@ -213,6 +215,7 @@ static const struct soc_enum twl4030_enum[] = { SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 0, 4, twl4030_carkitr_mix), SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0, 4, twl4030_handsfreel_mix), SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0, 4, twl4030_handsfreer_mix), + SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 0, 5, twl4030_earpiece_mix), }; /* * Some of TWL's input selection are in bitfield. @@ -481,6 +484,12 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { /* Hand-free output controls*/ SOC_ENUM("HandsfreeL Playback Mux", twl4030_enum[6]), SOC_ENUM("HandsfreeR Playback Mux", twl4030_enum[7]), + + /* Earpiece output controls */ + SOC_ENUM_EXT("Earpiece Playback Mux", twl4030_enum[8], + snd_soc_get_enum_twl4030, snd_soc_put_enum_twl4030), + SOC_SINGLE_TLV_TWL4030("Earpiece Playback Volume", + TWL4030_REG_EAR_CTL, 4, 3, 0, gainv1_tvl), };
/* add non dapm controls */
On Mon, Nov 24, 2008 at 01:49:39PM +0200, Peter Ujfalusi wrote:
+static const char *twl4030_predrivl_mix[] = {"Off", "Voice", "DACL1",
"DACL2", "DACR2"};
+static const char *twl4030_predrivr_mix[] = {"Off", "Voice", "DACR1",
"DACR2", "DACL2"};
+static const struct soc_enum twl4030_enum[] = {
I know quite a few existing drivers use an array of enums but please don't do this for new code - it doesn't help legibility to have to find the enums in the table.
- SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 0, 5, twl4030_predrivl_mix),
- SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 0, 5, twl4030_predrivr_mix),
+};
Is this really an enum? The fact that it's described as a mixer and it's got a series of individual bits for selecting the inputs make it sound like it should be possible to enable multiple inputs at once in which case this should be a series of switches - see SND_SOC_DAPM_MIXERs in other drivers for examples. If it's really an enum then calling it a mux is probably better.
Another issue is that it might be better to add these as DAPM controls now rather than have to move them to be DAPM controls later. If you use SND_SOC_NOPM for the power bit then DAPM won't actually do any power control, it'll just expose the control.
These comments apply to the other patches in the series.
Hello,
On Monday 24 November 2008 15:48:09 ext Mark Brown wrote:
On Mon, Nov 24, 2008 at 01:49:39PM +0200, Peter Ujfalusi wrote:
+static const char *twl4030_predrivl_mix[] = {"Off", "Voice", "DACL1",
"DACL2", "DACR2"};
+static const char *twl4030_predrivr_mix[] = {"Off", "Voice", "DACR1",
"DACR2", "DACL2"};
+static const struct soc_enum twl4030_enum[] = {
I know quite a few existing drivers use an array of enums but please don't do this for new code - it doesn't help legibility to have to find the enums in the table.
I have seen this approach in several codec code, so I have also implemented in a same way. I will change it. Do you have a pointer, where should I look for existing code?
- SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 0, 5, twl4030_predrivl_mix),
- SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 0, 5, twl4030_predrivr_mix),
+};
Is this really an enum? The fact that it's described as a mixer and it's got a series of individual bits for selecting the inputs make it sound like it should be possible to enable multiple inputs at once in which case this should be a series of switches - see SND_SOC_DAPM_MIXERs in other drivers for examples. If it's really an enum then calling it a mux is probably better.
Hmmm, the situation is kind of both... The selection is done in a bitfield. Let's take the PREDL, as I used that in the helper function comment: bit 0 (0x1): voice_en bit 1 (0x2): Audio L1 enable (DACL1) bit 2 (0x4): Audio L2 enable (DACL2) bit 3 (0x8): Audio R2 enable (DACR2) The voice path can be enabled/disabled independently from the digital paths, but only _one_ of the DACL1, DACL2 or DACR2 can be selected at the time. On top of that the mute can be performed by setting all four bits to 0. I don't know actually what that means, but probably than the PreDriv path will be effectively turned off. On the side not: the voice path at the moment not in use -> the codec has to be in different mode to have the voice path enabled. But then lot's of things will behave differently. Initially I did not wanted to spread these controls - to make it simple for myself, but I might separate the voice and DAC mux selections (separated enable for voice and mux the DACL/Rs selection).
Another issue is that it might be better to add these as DAPM controls now rather than have to move them to be DAPM controls later. If you use SND_SOC_NOPM for the power bit then DAPM won't actually do any power control, it'll just expose the control.
These comments apply to the other patches in the series.
OK, I'll try to move the controls to DAPM, as you suggested.
Thanks, Péter
On Mon, Nov 24, 2008 at 04:19:18PM +0200, Peter Ujfalusi wrote:
On Monday 24 November 2008 15:48:09 ext Mark Brown wrote:
I know quite a few existing drivers use an array of enums but please don't do this for new code - it doesn't help legibility to have to find the enums in the table.
I have seen this approach in several codec code, so I have also implemented in a same way. I will change it. Do you have a pointer, where should I look for existing code?
At least WM8990 and WM8903 do this with separate controls. Most of the drivers should be fine to look at for examples, it's just that this was done for some of the older drivers and it's never been worth changing them.
Is this really an enum? The fact that it's described as a mixer and
...
in other drivers for examples. If it's really an enum then calling it a mux is probably better.
Hmmm, the situation is kind of both... The selection is done in a bitfield. Let's take the PREDL, as I used that in the helper function comment: bit 0 (0x1): voice_en bit 1 (0x2): Audio L1 enable (DACL1) bit 2 (0x4): Audio L2 enable (DACL2) bit 3 (0x8): Audio R2 enable (DACR2) The voice path can be enabled/disabled independently from the digital paths, but only _one_ of the DACL1, DACL2 or DACR2 can be selected at the time.
Oh, ick. There's a couple of ways I can think of to represent that to DAPM. One would be to have a simple mixer with switches for everything and an extended event on the mixer which returns an error if multiple DAC inputs are enabled. The other is to have the DAC selection be a custom mux like you've got and feed that into the mixer unconditionally. The voice input should certainly be a separate control here.
On top of that the mute can be performed by setting all four bits to 0. I don't know actually what that means, but probably than the PreDriv path will be effectively turned off.
That's just equivalent to turning off all the inputs so sounds like it shouldn't need any special handling.
On the side not: the voice path at the moment not in use -> the codec has to be in different mode to have the voice path enabled. But then lot's of things will behave differently.
Could you go into more detail about these modes? Sounds like it could get tricky...
Initially I did not wanted to spread these controls - to make it simple for myself, but I might separate the voice and DAC mux selections (separated enable for voice and mux the DACL/Rs selection).
Yes, that does sound like the best approach.
On Monday 24 November 2008 16:50:31 ext Mark Brown wrote:
On Mon, Nov 24, 2008 at 04:19:18PM +0200, Peter Ujfalusi wrote:
On Monday 24 November 2008 15:48:09 ext Mark Brown wrote:
Do you have a pointer, where should I look for existing code?
At least WM8990 and WM8903 do this with separate controls. Most of the drivers should be fine to look at for examples, it's just that this was done for some of the older drivers and it's never been worth changing them.
I'll take a look.
Oh, ick. There's a couple of ways I can think of to represent that to DAPM. One would be to have a simple mixer with switches for everything and an extended event on the mixer which returns an error if multiple DAC inputs are enabled. The other is to have the DAC selection be a custom mux like you've got and feed that into the mixer unconditionally. The voice input should certainly be a separate control here.
Not sure that I understand these, but I'll try to get the 'other' way implemented.
On the side not: the voice path at the moment not in use -> the codec has to be in different mode to have the voice path enabled. But then lot's of things will behave differently.
Could you go into more detail about these modes? Sounds like it could get tricky...
Let's see... I might be the wrong person for this, but in short. TWL can operate in two modes: Option 1: two RX and TX stereo paths (four channel RX and four channel TX) Option 2: Voice uplink (sterreo) and downlink (mono) + one RX and TX stereo paths (two channel RX and two channel TX).
At the moment the driver supports the Option 1. Adding the support for Option 2 is tricky, since I have not seen a HW which can support it. The SW support can be added, but at least I can not verify, that it is working... On the Option 1 side: in order to use all four channel the TDM support is needed for twl4030 and for omap-mcbsp. Actually this is my long term plan... This is one of the reasons to have as flexible routing and control for TWL as possible. When the Option 1 is ready, I think in theory it should not be that hard to add support for the Option 2. Couple of new DAC, mixers, muxes, interconnects..
Hmm, after reading it back: probably this will just cause more confusion ;)
Initially I did not wanted to spread these controls - to make it simple for myself, but I might separate the voice and DAC mux selections (separated enable for voice and mux the DACL/Rs selection).
Yes, that does sound like the best approach.
On Mon, Nov 24, 2008 at 01:49:38PM +0200, Peter Ujfalusi wrote:
Some of the gain controls in TWL (mostly those which are associated with the outputs) are implemented in an interesting way: 0x0 : Power down (mute) 0x1 : 6dB 0x2 : 0 dB 0x3 : -6 dB Inverting not going to help with these. Custom volsw and volsw_2r get/put functions to handle these gains.
Applied.
participants (2)
-
Mark Brown
-
Peter Ujfalusi