[alsa-devel] [PATCH 7/9] ASoC: da7210: Add support for ALC and Noise suppression
This patch adds controls to set following ALC parameters, - Max gain, Min gain, Noise gain, Attack rate, Release rate and delay
It also adds a switch to enable/disable noise suppression.
As per DA7210 data sheet, ALC and noise suppression can be enabled only if certain conditions are met. This condition checks are handled by simply using "_EXT" version of controls to capture change events.
Tested on Samsung SMDK6410 board with DA7210 evaluation board.
Signed-off-by: Ashish Chavan ashish.chavan@kpitcummins.com Signed-off-by: David Dajun Chen dchen@diasemi.com
--- sound/soc/codecs/da7210.c | 104 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 104 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index eb12c82..9cab62a 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -26,6 +26,7 @@ #include <sound/tlv.h>
/* DA7210 register space */ +#define DA7210_CONTROL 0x01 #define DA7210_STATUS 0x02 #define DA7210_STARTUP1 0x03 #define DA7210_MIC_L 0x07 @@ -65,6 +66,12 @@ #define DA7210_PLL_DIV2 0x2A #define DA7210_PLL_DIV3 0x2B #define DA7210_PLL 0x2C +#define DA7210_ALC_MAX 0x83 +#define DA7210_ALC_MIN 0x84 +#define DA7210_ALC_NOIS 0x85 +#define DA7210_ALC_ATT 0x86 +#define DA7210_ALC_REL 0x87 +#define DA7210_ALC_DEL 0x88 #define DA7210_A_HID_UNLOCK 0x8A #define DA7210_A_TEST_UNLOCK 0x8B #define DA7210_A_PLL1 0x90 @@ -87,6 +94,7 @@ #define DA7210_IN_R_EN (1 << 7)
/* ADC bit fields */ +#define DA7210_ADC_ALC_EN (1 << 0) #define DA7210_ADC_L_EN (1 << 3) #define DA7210_ADC_R_EN (1 << 7)
@@ -165,9 +173,11 @@ #define DA7210_OUT2_EN (1 << 7)
/* AUX1_L bit fields */ +#define DA7210_AUX1_L_VOL (0x3F << 0) #define DA7210_AUX1_L_EN (1 << 7)
/* AUX1_R bit fields */ +#define DA7210_AUX1_R_VOL (0x3F << 0) #define DA7210_AUX1_R_EN (1 << 7)
/* AUX2 bit fields */ @@ -176,6 +186,19 @@ /* SOFTMUTE bit fields */ #define DA7210_RAMP_EN (1 << 6)
+/* CONTROL bit fields */ +#define DA7210_NOISE_SUP_EN (1 << 3) + +/* IN_GAIN bit fields */ +#define DA7210_INPGA_L_VOL (0x0F << 0) +#define DA7210_INPGA_R_VOL (0xF0 << 0) + +/* ZERO_CROSS bit fields */ +#define DA7210_AUX1_L_ZC (1 << 0) +#define DA7210_AUX1_R_ZC (1 << 1) +#define DA7210_HP_L_ZC (1 << 6) +#define DA7210_HP_R_ZC (1 << 7) + /* Default gain/vol values */ #define DA7210_DFLT_DAC_GAIN 0x10 /* 0dB */ #define DA7210_DFLT_OUT1_VOL 0x35 /* 0dB */ @@ -185,6 +208,10 @@ #define DA7210_DFLT_AUX2_VOL 0x02 /* 6dB */ #define DA7210_DFLT_INPGA_VOL 0x07 /* 6dB */
+/* Minimum INPGA and AUX1 volume to enable noise suppression */ +#define DA7210_INPGA_MIN_VOL_NS 0x0A /* 10.5dB */ +#define DA7210_AUX1_MIN_VOL_NS 0x35 /* 6dB */ + #define DA7210_VERSION "0.0.1"
/* @@ -247,6 +274,70 @@ static const char *da7210_hp_mode_txt[] = { static const struct soc_enum da7210_hp_mode_sel = SOC_ENUM_SINGLE(DA7210_HP_CFG, 0, 2, da7210_hp_mode_txt);
+/* ALC can be enabled only if noise suppression is disabled */ +static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u8 *reg_cache = codec->reg_cache; + + if (ucontrol->value.integer.value[0]) { + /* Check if noise suppression is enabled */ + if (reg_cache[DA7210_CONTROL] & DA7210_NOISE_SUP_EN) { + dev_dbg(codec->dev, "Failed to enable ALC\n"); + return -EPERM; + } + } + /* If all conditions are met or we are actually disabling ALC */ + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* Noise suppression can be enabled only if following conditions are met + * ALC disabled + * ZC enabled for HP and AUX1 PGA + * INPGA_L_VOL and INPGA_R_VOL >= 10.5 dB + * AUX1_L_VOL and AUX1_R_VOL >= 6 dB + */ +static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u8 *reg_cache = codec->reg_cache; + u8 val; + + if (ucontrol->value.integer.value[0]) { + /* Check if ALC is enabled */ + if (reg_cache[DA7210_ADC] & DA7210_ADC_ALC_EN) + goto err; + + /* Check ZC for HP and AUX1 PGA */ + if ((reg_cache[DA7210_ZERO_CROSS] & + (DA7210_AUX1_L_ZC | DA7210_AUX1_R_ZC | DA7210_HP_L_ZC | + DA7210_HP_R_ZC)) != 0xc3) + goto err; + + /* Check INPGA_L_VOL and INPGA_R_VOL */ + val = reg_cache[DA7210_IN_GAIN]; + if (((val & DA7210_INPGA_L_VOL) < DA7210_INPGA_MIN_VOL_NS) || + (((val & DA7210_INPGA_R_VOL) >> 4) < + DA7210_INPGA_MIN_VOL_NS)) + goto err; + + /* Check AUX1_L_VOL and AUX1_R_VOL */ + if (((reg_cache[DA7210_AUX1_L] & DA7210_AUX1_L_VOL) < + DA7210_AUX1_MIN_VOL_NS) || + ((reg_cache[DA7210_AUX1_R] & DA7210_AUX1_R_VOL) < + DA7210_AUX1_MIN_VOL_NS)) + goto err; + } + /* If all conditions are met or we are actually disabling Noise sup */ + return snd_soc_put_volsw(kcontrol, ucontrol); + +err: + dev_dbg(codec->dev, "Failed to enable Noise suppression\n"); + return -EPERM; +} + static const struct snd_kcontrol_new da7210_snd_controls[] = {
SOC_DOUBLE_R_TLV("HeadPhone Playback Volume", @@ -322,6 +413,19 @@ static const struct snd_kcontrol_new da7210_snd_controls[] = { SOC_DOUBLE("Headphone ZC Switch", DA7210_ZERO_CROSS, 6, 7, 1, 0),
SOC_ENUM("Headphone Class", da7210_hp_mode_sel), + + /* ALC controls */ + SOC_SINGLE_EXT("ALC Enable Switch", DA7210_ADC, 0, 1, 0, + snd_soc_get_volsw, da7210_put_alc_sw), + SOC_SINGLE("ALC Capture Max Gain", DA7210_ALC_MAX, 0, 0x3F, 0), + SOC_SINGLE("ALC Capture Min Gain", DA7210_ALC_MIN, 0, 0x3F, 0), + SOC_SINGLE("ALC Capture Noise Gain", DA7210_ALC_NOIS, 0, 0x3F, 0), + SOC_SINGLE("ALC Capture Attack Rate", DA7210_ALC_ATT, 0, 0xFF, 0), + SOC_SINGLE("ALC Capture Release Rate", DA7210_ALC_REL, 0, 0xFF, 0), + SOC_SINGLE("ALC Capture Release Delay", DA7210_ALC_DEL, 0, 0xFF, 0), + + SOC_SINGLE_EXT("Noise Suppression Enable Switch", DA7210_CONTROL, 3, 1, + 0, snd_soc_get_volsw, da7210_put_noise_sup_sw), };
/* Codec private data */
On Wed, Oct 12, 2011 at 08:37:50PM +0530, Ashish Chavan wrote:
- if (ucontrol->value.integer.value[0]) {
/* Check if noise suppression is enabled */
if (reg_cache[DA7210_CONTROL] & DA7210_NOISE_SUP_EN) {
No, don't go peering directly into the register cache. This is going to explode if the board configures a different cache style and means you can't change the cache type later. Just read the register and trust that the cache will do something sane.
dev_dbg(codec->dev, "Failed to enable ALC\n");
return -EPERM;
}
Either drop the debug print or make it say what the problem is.
similar problems in the rest of the patch.
- SOC_SINGLE("ALC Capture Max Gain", DA7210_ALC_MAX, 0, 0x3F, 0),
- SOC_SINGLE("ALC Capture Min Gain", DA7210_ALC_MIN, 0, 0x3F, 0),
- SOC_SINGLE("ALC Capture Noise Gain", DA7210_ALC_NOIS, 0, 0x3F, 0),
Volume.
On Wed, 2011-10-12 at 16:04 +0100, Mark Brown wrote:
On Wed, Oct 12, 2011 at 08:37:50PM +0530, Ashish Chavan wrote:
- if (ucontrol->value.integer.value[0]) {
/* Check if noise suppression is enabled */
if (reg_cache[DA7210_CONTROL] & DA7210_NOISE_SUP_EN) {
No, don't go peering directly into the register cache. This is going to explode if the board configures a different cache style and means you can't change the cache type later. Just read the register and trust that the cache will do something sane.
Will update the code to directly read the register. I thought of saving some io bandwidth and if I remember correctly, I saw few exiting drivers doing this.
dev_dbg(codec->dev, "Failed to enable ALC\n");
return -EPERM;
}
Either drop the debug print or make it say what the problem is.
Will add more specific info about error.
similar problems in the rest of the patch.
- SOC_SINGLE("ALC Capture Max Gain", DA7210_ALC_MAX, 0, 0x3F, 0),
- SOC_SINGLE("ALC Capture Min Gain", DA7210_ALC_MIN, 0, 0x3F, 0),
- SOC_SINGLE("ALC Capture Noise Gain", DA7210_ALC_NOIS, 0, 0x3F, 0),
Volume.
On Thu, Oct 13, 2011 at 02:45:45PM +0530, Ashish Chavan wrote:
On Wed, 2011-10-12 at 16:04 +0100, Mark Brown wrote:
No, don't go peering directly into the register cache. This is going to explode if the board configures a different cache style and means you can't change the cache type later. Just read the register and trust that the cache will do something sane.
Will update the code to directly read the register. I thought of saving some io bandwidth and if I remember correctly, I saw few exiting drivers doing this.
The whole point of having the register cache is that reads don't result in any actual I/O, if there's any I/O happening we need to fix the register cache code.
participants (2)
-
Ashish Chavan
-
Mark Brown