From 8fa8359b89fcbe1db12ccc917e2bf166d8f5519c Mon Sep 17 00:00:00 2001 From: Kristoffer KARLSSON Date: Mon, 10 Oct 2011 10:39:07 +0200 Subject: Ux500 ASoC: Add ANC functionality This patch adds support for control of the ANC block in the ab8500 chipset. ST-Ericsson Linux next: NA ST-Ericsson ID: 325656 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Icf57820d32b218f16f13d96d195b2cba6b31f494 Signed-off-by: Kristoffer KARLSSON Change-Id: Ic841ecb0ae13939c84e8d959ab3cb74986e3b72b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32988 Reviewed-by: Roger NILSSON1 Reviewed-by: QATOOLS --- sound/soc/codecs/ab8500_audio.c | 166 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/ab8500_audio.h | 25 ++++-- sound/soc/ux500/ux500_ab8500.c | 56 ++++++++++++++ 3 files changed, 241 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ab8500_audio.c b/sound/soc/codecs/ab8500_audio.c index ad2fb60c6ab..8d719e7950a 100644 --- a/sound/soc/codecs/ab8500_audio.c +++ b/sound/soc/codecs/ab8500_audio.c @@ -55,6 +55,23 @@ #define AB8500_GPIO_DIR5_REG 0x1014 #define AB8500_GPIO_OUT5_REG 0x1024 +/* Nr of FIR/IIR-coeff banks in ANC-block */ +#define AB8500_NR_OF_ANC_COEFF_BANKS 2 + +/* Macros to simplify implementation of register write sequences and error handling */ +#define AB8500_SET_BIT_LOCKED(xreg, xbit, xerr, xerr_hdl) { \ + xerr = snd_soc_update_bits_locked(ab8500_codec, xreg, REG_MASK_NONE, BMASK(xbit)); \ + if (xerr < 0) \ + goto xerr_hdl; } +#define AB8500_CLEAR_BIT_LOCKED(xreg, xbit, xerr, xerr_hdl) { \ + xerr = snd_soc_update_bits_locked(ab8500_codec, xreg, BMASK(xbit), REG_MASK_NONE); \ + if (xerr < 0) \ + goto xerr_hdl; } +#define AB8500_WRITE(xreg, xvalue, xerr, xerr_hdl) { \ + xerr = snd_soc_write(ab8500_codec, xreg, xvalue); \ + if (xerr < 0) \ + goto xerr_hdl; } + /* * AB8500 register cache & default register settings */ @@ -184,6 +201,26 @@ struct soc_smra_control { long *values; }; +/* ANC FIR- & IIR-coeff caches */ +static int ab8500_anc_fir_coeff_cache[REG_ANC_FIR_COEFFS]; +static int ab8500_anc_iir_coeff_cache[REG_ANC_IIR_COEFFS]; + +/* ANC states */ +enum anc_states { + ANC_UNCONFIGURED = 0, + ANC_CONFIGURE_FIR_IIR = 1, + ANC_FIR_IIR_CONFIGURED = 2, + ANC_CONFIGURE_FIR = 3, + ANC_FIR_CONFIGURED = 4, + ANC_CONFIGURE_IIR = 5, + ANC_IIR_CONFIGURED = 6, + ANC_ERROR = 7 +}; +static int ab8500_anc_status = ANC_UNCONFIGURED; + +/* ANC configuration lock */ +static DEFINE_MUTEX(ab8500_anc_conf_lock); + /* Reads an arbitrary register from the ab8500 chip. */ static int ab8500_codec_read_reg(struct snd_soc_codec *codec, unsigned int bank, @@ -1933,6 +1970,38 @@ static struct snd_kcontrol_new ab8500_snd_controls[] = { REG_SIDFIRADR_ADDRESS_SHIFT, REG_SIDFIRADR_ADDRESS_MAX, NORMAL), + + /* ANC */ + SOC_SINGLE_S1R("ANC Warp Delay Shift", + REG_ANCCONF2, + REG_ANCCONF2_VALUE_MIN, + REG_ANCCONF2_VALUE_MAX, + NORMAL), + SOC_SINGLE_S1R("ANC FIR Output Shift", + REG_ANCCONF3, + REG_ANCCONF3_VALUE_MIN, + REG_ANCCONF3_VALUE_MAX, + NORMAL), + SOC_SINGLE_S1R("ANC IIR Output Shift", + REG_ANCCONF4, + REG_ANCCONF4_VALUE_MIN, + REG_ANCCONF4_VALUE_MAX, + NORMAL), + SOC_SINGLE_S2R("ANC Warp Delay", + REG_ANCCONF9, REG_ANCCONF10, + REG_ANC_WARP_DELAY_MIN, + REG_ANC_WARP_DELAY_MAX, + NORMAL), + SOC_MULTIPLE_SA("ANC FIR Coefficients", + ab8500_anc_fir_coeff_cache, + REG_ANC_FIR_COEFF_MIN, + REG_ANC_FIR_COEFF_MAX, + NORMAL), + SOC_MULTIPLE_SA("ANC IIR Coefficients", + ab8500_anc_iir_coeff_cache, + REG_ANC_IIR_COEFF_MIN, + REG_ANC_IIR_COEFF_MAX, + NORMAL), }; static int ab8500_codec_set_format_if1(struct snd_soc_codec *codec, unsigned int fmt) @@ -2236,6 +2305,103 @@ int ab8500_audio_setup_if1(struct snd_soc_codec *codec, return 0; } +/* ANC block current configuration status */ +unsigned int ab8500_audio_anc_status(void) +{ + return ab8500_anc_status; +} + +/* ANC IIR-/FIR-coefficients configuration sequence */ +int ab8500_audio_anc_configure(unsigned int req_state) +{ + bool configure_fir = req_state == ANC_CONFIGURE_FIR || req_state == ANC_CONFIGURE_FIR_IIR; + bool configure_iir = req_state == ANC_CONFIGURE_IIR || req_state == ANC_CONFIGURE_FIR_IIR; + unsigned int bank, param; + int ret; + + if (req_state == ANC_UNCONFIGURED + || req_state == ANC_FIR_IIR_CONFIGURED + || req_state == ANC_FIR_CONFIGURED + || req_state == ANC_IIR_CONFIGURED + || req_state == ANC_ERROR) + return -1; + + mutex_lock(&ab8500_anc_conf_lock); + + if (configure_fir) + AB8500_CLEAR_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ENANC, ret, cleanup) + + AB8500_SET_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ENANC, ret, cleanup) + + if (configure_fir) { + for (bank = 0; bank < AB8500_NR_OF_ANC_COEFF_BANKS; bank++) { + for (param = 0; param < REG_ANC_FIR_COEFFS; param++) { + if (param == 0 && bank == 0) + AB8500_SET_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ANCFIRUPDATE, ret, cleanup) + + AB8500_WRITE(REG_ANCCONF5, ab8500_anc_fir_coeff_cache[param] >> 8 & REG_MASK_ALL, ret, cleanup) + AB8500_WRITE(REG_ANCCONF6, ab8500_anc_fir_coeff_cache[param] & REG_MASK_ALL, ret, cleanup) + + if (param == REG_ANC_FIR_COEFFS - 1 && bank == 1) + AB8500_CLEAR_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ANCFIRUPDATE, ret, cleanup) + } + } + if (ab8500_anc_status == ANC_IIR_CONFIGURED) + ab8500_anc_status = ANC_FIR_IIR_CONFIGURED; + else if (ab8500_anc_status != ANC_FIR_IIR_CONFIGURED) + ab8500_anc_status = ANC_FIR_CONFIGURED; + } + + if (configure_iir) { + for (bank = 0; bank < AB8500_NR_OF_ANC_COEFF_BANKS; bank++) { + for (param = 0; param < REG_ANC_IIR_COEFFS; param++) { + if (param == 0) { + if (bank == 0) { + AB8500_SET_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ANCIIRINIT, ret, cleanup) + udelay(2000); + AB8500_CLEAR_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ANCIIRINIT, ret, cleanup) + udelay(2000); + } else { + AB8500_SET_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ANCIIRUPDATE, ret, cleanup) + } + } else if (param > 3) { + AB8500_WRITE(REG_ANCCONF7, REG_MASK_NONE, ret, cleanup) + AB8500_WRITE(REG_ANCCONF8, ab8500_anc_iir_coeff_cache[param] >> 16 & REG_MASK_ALL, ret, cleanup) + } + + AB8500_WRITE(REG_ANCCONF7, ab8500_anc_iir_coeff_cache[param] >> 8 & REG_MASK_ALL, ret, cleanup) + AB8500_WRITE(REG_ANCCONF8, ab8500_anc_iir_coeff_cache[param] & REG_MASK_ALL, ret, cleanup) + + if (param == REG_ANC_IIR_COEFFS - 1 && bank == 1) + AB8500_CLEAR_BIT_LOCKED(REG_ANCCONF1, REG_ANCCONF1_ANCIIRUPDATE, ret, cleanup) + } + } + if (ab8500_anc_status == ANC_FIR_CONFIGURED) + ab8500_anc_status = ANC_FIR_IIR_CONFIGURED; + else if (ab8500_anc_status != ANC_FIR_IIR_CONFIGURED) + ab8500_anc_status = ANC_IIR_CONFIGURED; + } + + mutex_unlock(&ab8500_anc_conf_lock); + + return 0; + +cleanup: + ret |= snd_soc_update_bits_locked(ab8500_codec + , REG_ANCCONF1 + , BMASK(REG_ANCCONF1_ENANC) + | BMASK(REG_ANCCONF1_ANCIIRINIT) + | BMASK(REG_ANCCONF1_ANCIIRUPDATE) + | BMASK(REG_ANCCONF1_ANCFIRUPDATE) + , REG_MASK_NONE); + + ab8500_anc_status = ANC_ERROR; + + mutex_unlock(&ab8500_anc_conf_lock); + + return ret; +} + bool ab8500_audio_dapm_path_active(enum ab8500_audio_dapm_path dapm_path) { int reg, reg_mask; diff --git a/sound/soc/codecs/ab8500_audio.h b/sound/soc/codecs/ab8500_audio.h index 2d659c58746..2b2f69e44c2 100644 --- a/sound/soc/codecs/ab8500_audio.h +++ b/sound/soc/codecs/ab8500_audio.h @@ -31,6 +31,8 @@ int ab8500_audio_setup_if1(struct snd_soc_codec *codec, unsigned int fmt, unsigned int wl, unsigned int delay); +unsigned int ab8500_audio_anc_status(); +int ab8500_audio_anc_configure(unsigned int req_state); enum ab8500_audio_dapm_path { AB8500_AUDIO_DAPM_PATH_DMIC, @@ -588,14 +590,25 @@ bool ab8500_audio_dapm_path_active(enum ab8500_audio_dapm_path dapm_path); #define REG_ANCCONF1_ANCFIRUPDATE 0 /* REG_ANCCONF2 */ +#define REG_ANCCONF2_VALUE_MIN -0x10 +#define REG_ANCCONF2_VALUE_MAX 0x0F /* REG_ANCCONF3 */ +#define REG_ANCCONF3_VALUE_MIN -0x10 +#define REG_ANCCONF3_VALUE_MAX 0x0F /* REG_ANCCONF4 */ -/* REG_ANCCONF5 */ -/* REG_ANCCONF6 */ -/* REG_ANCCONF7 */ -/* REG_ANCCONF8 */ -/* REG_ANCCONF9 */ -/* REG_ANCCONF10 */ +#define REG_ANCCONF4_VALUE_MIN -0x10 +#define REG_ANCCONF4_VALUE_MAX 0x0F +/* REG_ANC_FIR_COEFFS */ +#define REG_ANC_FIR_COEFF_MIN -0x8000 +#define REG_ANC_FIR_COEFF_MAX 0x7FFF +#define REG_ANC_FIR_COEFFS 0xF +/* REG_ANC_IIR_COEFFS */ +#define REG_ANC_IIR_COEFF_MIN -0x800000 +#define REG_ANC_IIR_COEFF_MAX 0x7FFFFF +#define REG_ANC_IIR_COEFFS 0x18 +/* REG_ANC_WARP_DELAY */ +#define REG_ANC_WARP_DELAY_MIN 0x0000 +#define REG_ANC_WARP_DELAY_MAX 0xFFFF /* REG_ANCCONF11 */ /* REG_ANCCONF12 */ /* REG_ANCCONF13 */ diff --git a/sound/soc/ux500/ux500_ab8500.c b/sound/soc/ux500/ux500_ab8500.c index d45436a8a03..34c6aa0eef4 100644 --- a/sound/soc/ux500/ux500_ab8500.c +++ b/sound/soc/ux500/ux500_ab8500.c @@ -59,6 +59,19 @@ static struct clk *clk_ptr_sysclk; static struct clk *clk_ptr_ulpclk; static struct clk *clk_ptr_gpio1; +/* ANC States */ +static const char *enum_anc_state[] = { + "Unconfigured", + "Configure FIR+IIR", + "FIR+IIR Configured", + "Configure FIR", + "FIR Configured", + "Configure IIR", + "IIR Configured", + "Error" +}; +static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state); + /* Regulators */ enum regulator_idx { REGULATOR_AUDIO, @@ -285,6 +298,48 @@ static const struct snd_kcontrol_new mclk_input_control = { .private_value = 1 /* ULPCLK */ }; +static int anc_status_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = ab8500_audio_anc_status(); + + return 0; +} + +static int anc_status_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + int req_state = ucontrol->value.integer.value[0]; + + ret = ux500_ab8500_power_control_inc(); + if (ret) + goto cleanup; + + ret = ab8500_audio_anc_configure(req_state); + + ux500_ab8500_power_control_dec(); + +cleanup: + if (ret) { + pr_err("%s: Unable to configure ANC! (ret = %d)\n", + __func__, ret); + return 0; + } + + return 1; +} + +static const struct snd_kcontrol_new anc_status_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ANC Status", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_enum_ext, + .get = anc_status_control_get, .put = anc_status_control_put, + .private_value = (unsigned long) &soc_enum_ancstate +}; + /* DAPM-events */ static int dapm_audioreg_event(struct snd_soc_dapm_widget *w, @@ -573,6 +628,7 @@ int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd) /* Add controls */ snd_ctl_add(codec->card->snd_card, snd_ctl_new1(&mclk_input_control, codec)); + snd_ctl_add(codec->card->snd_card, snd_ctl_new1(&anc_status_control, codec)); /* Get references to clock-nodes */ clk_ptr_sysclk = NULL; -- cgit v1.2.3