diff options
author | Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com> | 2018-07-19 09:20:26 +0300 |
---|---|---|
committer | Leonard Crestez <leonard.crestez@nxp.com> | 2018-08-24 12:41:33 +0300 |
commit | 06495dd0cb0d0e2de225e32251b78edafa473406 (patch) | |
tree | 2d41a881bb4ce1aae542f22ca6e74151c042a3b3 /sound | |
parent | 4cf9ddee84c995e02931be9de5448cb162130862 (diff) |
MLK-18655-2 sound: asoc: add micfil hardware voice active detection
Hardware Voice Activity Detector (HWVAD) is a block responsible for
detect voice activity in a channel selected by the user and it can be
configured in Envelope-based or Energy-based mode. Optionally, a
Zero-Crossing Detector can be enabled to improve the voice detection.
To enable hwvad from userspace there is a interface and you should write
the number of channels in /sys/devices/platform/30080000.micfil/hwvad/enable
to enable the hardware voice activity detection for micfil or 0
to disable it.
When voice activity is detected, an udev event will be generated:
"EVENT=PDM_VOICE_DETECT" and hwvad will be disabled afterwards.
Signed-off-by: Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/fsl/fsl_micfil.c | 857 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_micfil.h | 167 |
2 files changed, 981 insertions, 43 deletions
diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index cf622369745f..b19a785f8016 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -9,8 +9,11 @@ * http://www.gnu.org/copyleft/gpl.html */ +#include <linux/atomic.h> #include <linux/clk.h> +#include <linux/device.h> #include <linux/interrupt.h> +#include <linux/kobject.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> @@ -18,9 +21,11 @@ #include <linux/of_platform.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/sysfs.h> +#include <linux/types.h> #include <sound/dmaengine_pcm.h> -#include <sound/soc.h> #include <sound/pcm.h> +#include <sound/soc.h> #include "fsl_micfil.h" #include "imx-pcm.h" @@ -34,11 +39,16 @@ struct fsl_micfil { const struct fsl_micfil_soc_data *soc; struct clk *mclk; struct snd_dmaengine_dai_dma_data dma_params_rx; + struct kobject *hwvad_kobject; + unsigned int channels; unsigned int dataline; - bool slave_mode; char name[32]; unsigned int mclk_streams; int quality; /*QUALITY 2-0 bits */ + bool slave_mode; + atomic_t state; + atomic_t voice_detected; + atomic_t init_hwvad_done; }; struct fsl_micfil_soc_data { @@ -48,6 +58,11 @@ struct fsl_micfil_soc_data { bool imx; }; +static char *envp[] = { + "EVENT=PDM_VOICE_DETECT", + NULL, + }; + static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { .imx = true, .fifos = 8, @@ -139,6 +154,8 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { snd_soc_get_enum_double, set_quality), }; +static int disable_hwvad(struct device *dev); + static inline unsigned int get_pdm_clk(struct fsl_micfil *micfil, unsigned int rate); @@ -220,8 +237,7 @@ static int fsl_micfil_bsy(struct device *dev) } if (stat & MICFIL_STAT_BSY_FIL_MASK) - usleep_range(MICFIL_SLEEP_MIN, - MICFIL_SLEEP_MAX); + usleep_range(MICFIL_SLEEP_MIN, MICFIL_SLEEP_MAX); else return 0; } @@ -260,11 +276,513 @@ static int fsl_micfil_reset(struct device *dev) return 0; } +/* enable/disable hwvad interrupts */ +static int configure_hwvad_interrupts(struct device *dev, + int enable) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret; + u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0; + u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0; + + /* Voice Activity Detector Interruption Enable */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_IE_MASK, + vadie_reg); + if (ret) { + dev_err(dev, + "Failed to set/clear VADIE in CTRL1_VAD0 [%d]\n", + ret); + return ret; + } + + /* Voice Activity Detector Error Interruption Enable */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_ERIE_MASK, + vaderie_reg); + if (ret) { + dev_err(dev, + "Failed to set/clear VADERIE in CTRL1_VAD0 [%d]\n", + ret); + return ret; + } + + return 0; +} + +static int init_hwvad_internal_filters(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret; + + /* Voice Activity Detector Internal Filters Initialization*/ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_ST10_MASK, + MICFIL_VAD0_CTRL1_ST10); + if (ret) { + dev_err(dev, + "Failed to set VADST10 in CTRL1_VAD0 [%d]\n", + ret); + return ret; + } + + /* sleep for 100ms - it should be enough for bit to stay + * pulsed for more than 2 cycles + */ + usleep_range(MICFIL_SLEEP_MIN, MICFIL_SLEEP_MAX); + + /* Voice Activity Detector Enabled */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_ST10_MASK, + 0); + if (ret) { + dev_err(dev, + "Failed to clear VADST10 in CTRL1_VAD0 [%d]\n", + ret); + return ret; + } + return 0; +} + +/* Zero-Crossing Detector Initialization + * Optionally a Zero-Crossing Detection block (ZCD) could + * be enabled to avoid low energy voiced speech be missed, + * improving the voice detection performance. + * See Section 8.4.3 + */ +static int __maybe_unused init_zcd(struct device *dev, + int threshold, + int mode) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret; + + if (mode == MICFIL_HWVAD_ZCD_AUTO) { + /* enable auto-threshold */ + auto_threshold = MICFIL_VAD0_ZCD_ZCDAUT; + + /* Zero-Crossing Detector Adjustment */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_ZCD, + MICFIL_VAD0_ZCD_ZCDADJ_MASK, + MICFIL_HWVAD_ZCDADJ); + if (ret) { + dev_err(dev, + "Failed to set ZCDADJ in ZCD_VAD0 [%d]\n", + ret); + return ret; + } + } + + /* Zero-Crossing Detector Threshold */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_ZCD, + MICFIL_VAD0_ZCD_ZCDTH_MASK, + MICFIL_VAD0_ZCD_ZCDTH(threshold)); + if (ret) { + dev_err(dev, "Failed to set ZCDTH in ZCD_VAD0 [%d]\n", ret); + return ret; + } + + /* Zero-Crossing Detector AND Behavior */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_ZCD, + MICFIL_VAD0_ZCD_ZCDAND_MASK, + MICFIL_HWVAD_ZCDAND); + if (ret) { + dev_err(dev, "Failed to set ZCDAND in ZCD_VAD0 [%d]\n", ret); + return ret; + } + + /* Zero-Crossing Detector Automatic Threshold */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_ZCD, + MICFIL_VAD0_ZCD_ZCDAUT_MASK, + auto_threshold); + if (ret) { + dev_err(dev, + "Failed to set/clear ZCDAUT in ZCD_VAD0 [%d]\n", + ret); + return ret; + } + + /* Zero-Crossing Detector Enable */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_ZCD, + MICFIL_VAD0_ZCD_ZCDEN_MASK, + MICFIL_VAD0_ZCD_ZCDEN); + if (ret) { + dev_err(dev, "Failed to set ZCDEN in ZCD_VAD0 [%d]\n", ret); + return ret; + } + + return 0; +} + +/* Configuration done only in energy-based initialization mode */ +static int init_hwvad_energy_mode(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret, i; + u32 stat; + u32 flag; + + dev_info(dev, "Energy-based mode initialization\n"); + + /* Voice Activity Detector Reset */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_RST_SHIFT, + MICFIL_VAD0_CTRL1_RST); + if (ret) { + dev_err(dev, "Failed to set VADRST in CTRL1_VAD0 [%d]\n", ret); + return ret; + } + + /* Voice Activity Detector Enabled */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_EN_MASK, + MICFIL_VAD0_CTRL1_EN); + if (ret) { + dev_err(dev, "Failed to set VADEN in CTRL1_VAD0 [%d]\n", ret); + return ret; + } + + /* it would be a good idea to wait some time before VADEN + * is set + */ + usleep_range(5 * MICFIL_SLEEP_MIN, 5 * MICFIL_SLEEP_MAX); + + /* Enable Interrupts */ + ret = configure_hwvad_interrupts(dev, 1); + + /* Initialize Zero Crossing Detector */ + ret = init_zcd(dev); + if (ret) + return ret; + + /* Enable MICFIL module */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN_MASK, + MICFIL_CTRL1_PDMIEN); + if (ret) { + dev_err(dev, "failed to enable the module\n"); + return ret; + } + + /* Wait for INITF to be asserted */ + for (i = 0; i < MICFIL_MAX_RETRY; i++) { + ret = regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &stat); + if (ret) { + dev_err(dev, "failed to read register %d\n", + REG_MICFIL_VAD0_STAT); + return ret; + } + + flag = (stat & MICFIL_VAD0_STAT_INITF_MASK); + if (flag == 0) + break; + + usleep_range(MICFIL_SLEEP_MIN, MICFIL_SLEEP_MAX); + } + + if (i == MICFIL_MAX_RETRY) { + dev_err(dev, "initf not asserted. Failed to init hwvad\n"); + return -EBUSY; + } + + /* Initialize Internal Filters */ + ret = init_hwvad_internal_filters(dev); + if (ret) + return ret; + + return ret; +} + +/* Configuration done only in envelope-based initialization mode */ +static int init_hwvad_envelope_mode(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret, i; + u32 stat; + u32 flag; + + dev_info(dev, "Envelope-based mode initialization\n"); + + /* Frame energy disable */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_FRENDIS_MASK, + MICFIL_VAD0_CTRL2_FRENDIS); + if (ret) { + dev_err(dev, "Failed to set FRENDIS in CTRL2_VAD0 [%d]\n", ret); + return ret; + } + + /* Enable pre-filter Noise & Signal */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_PREFEN_MASK, + MICFIL_VAD0_CTRL2_PREFEN); + if (ret) { + dev_err(dev, "Failed to set PREFEN in CTRL2_VAD0 [%d]\n", ret); + return ret; + } + + /* Enable Signal Filter */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SFILEN_MASK, + MICFIL_VAD0_SCONFIG_SFILEN); + if (ret) { + dev_err(dev, + "Failed to set SFILEN in SCONFIG_VAD0 [%d]\n", + ret); + return ret; + } + + /* Signal Maximum Enable */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SMAXEN_MASK, + MICFIL_VAD0_SCONFIG_SMAXEN); + if (ret) { + dev_err(dev, + "Failed to set SMAXEN in SCONFIG_VAD0 [%d]\n", + ret); + return ret; + } + + /* Allways enable noise filter, not based on voice activity + * information + */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NFILAUT_MASK, + 0); + if (ret) { + dev_err(dev, + "Failed to set NFILAUT in NCONFIG_VAD0 [%d]\n", + ret); + return ret; + } + + /* Noise Minimum Enable */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NMINEN_MASK, + MICFIL_VAD0_NCONFIG_NMINEN); + if (ret) { + dev_err(dev, + "Failed to set NMINEN in NCONFIG_VAD0 [%d]\n", + ret); + return ret; + } + + /* Noise Decimation Enable */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NDECEN_MASK, + MICFIL_VAD0_NCONFIG_NDECEN); + if (ret) { + dev_err(dev, + "Failed to set NDECEN in NCONFIG_VAD0 [%d]\n", + ret); + return ret; + } + + /* Voice Activity Detector Reset */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_RST_SHIFT, + MICFIL_VAD0_CTRL1_RST); + if (ret) { + dev_err(dev, "Failed to set VADRST in CTRL1_VAD0 [%d]\n", ret); + return ret; + } + + /* Initialize Zero Crossing Detector */ + ret = init_zcd(dev); + if (ret) + return ret; + + /* Voice Activity Detector Enabled */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_EN_MASK, + MICFIL_VAD0_CTRL1_EN); + if (ret) { + dev_err(dev, "Failed to set VADEN in CTRL1_VAD0 [%d]\n", ret); + return ret; + } + + /* Enable MICFIL module */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN_MASK, + MICFIL_CTRL1_PDMIEN); + if (ret) { + dev_err(dev, "failed to enable the module\n"); + return ret; + } + + /* it would be a good idea to wait some time before VADEN + * is set + */ + usleep_range(3 * MICFIL_SLEEP_MIN, 3 * MICFIL_SLEEP_MAX); + + /* Wait for INITF to be asserted */ + for (i = 0; i < MICFIL_MAX_RETRY; i++) { + ret = regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &stat); + if (ret) { + dev_err(dev, "failed to read register %d\n", + REG_MICFIL_VAD0_STAT); + return ret; + } + + flag = (stat & MICFIL_VAD0_STAT_INITF_MASK); + if (flag == 0) + break; + + usleep_range(MICFIL_SLEEP_MIN, MICFIL_SLEEP_MAX); + } + + if (i == MICFIL_MAX_RETRY) { + dev_err(dev, "initf not asserted. Failed to init hwvad\n"); + return -EBUSY; + } + + /* Initialize Internal Filters */ + ret = init_hwvad_internal_filters(dev); + if (ret) + return ret; + + /* Enable interrupts */ + ret = configure_hwvad_interrupts(dev, 1); + if (ret) + return ret; + + return ret; +} + +/* Hardware Voice Active Detection: The HWVAD takes data from the input + * of a selected PDM microphone to detect if there is any + * voice activity. When a voice activity is detected, an interrupt could + * be delivered to the system. Initialization in section 8.4: + * Can work in two modes: + * -> Eneveope-based mode (section 8.4.1) + * -> Energy-based mode (section 8.4.2) + * + * It is important to remark that the HWVAD detector could be enabled + * or reset only when the MICFIL isn't running i.e. when the BSY_FIL + * bit in STAT register is cleared + */ +static int __maybe_unused init_hwvad(struct device *dev, int mode) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret; + + ret = fsl_micfil_bsy(dev); + if (ret) { + dev_err(dev, + "Hardware Voice Active Detection initialization fail. BSY_FIL flag is set [%d]\n", + ret); + return ret; + } + + /* configure CIC OSR in VADCICOSR */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_CICOSR_MASK, + MICFIL_CTRL2_OSR_DEFAULT); + if (ret) { + dev_err(dev, "Failed to set CICOSR in CTRL1_VAD0i [%d]\n", ret); + return ret; + } + + /* configure source channel in VADCHSEL */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_CHSEL_MASK, + MICFIL_VAD0_CTRL1_CHSEL(micfil->channels)); + if (ret) { + dev_err(dev, "Failed to set CHSEL in CTRL1_VAD0 [%d]\n", ret); + return ret; + } + + /* configure detector frame time VADFRAMET */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_FRAMET_MASK, + MICFIL_VAD0_CTRL2_FRAMET(MICFIL_HWVAD_FRAMET_DEFAULT)); + if (ret) { + dev_err(dev, "Failed to set FRAMET in CTRL2_VAD0 [%d]\n", ret); + return ret; + } + + /* configure initialization time in VADINITT */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_INITT_MASK, + MICFIL_VAD0_CTRL1_INITT(MICFIL_HWVAD_INIT_FRAMES)); + if (ret) { + dev_err(dev, "Failed to set INITT in CTRL1_VAD0 [%d]\n", ret); + return ret; + } + + /* configure input gain in VADINPGAIN */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_INPGAIN_MASK, + MICFIL_VAD0_CTRL2_INPGAIN(MICFIL_HWVAD_INPGAIN)); + if (ret) { + dev_err(dev, "Failed to set INPGAIN in CTRL2_VAD0 [%d]\n", ret); + return ret; + } + + /* configure sound gain in SGAIN */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SGAIN_MASK, + MICFIL_VAD0_SCONFIG_SGAIN(MICFIL_HWVAD_SGAIN)); + if (ret) { + dev_err(dev, "Failed to set SGAIN in SCONFIG_VAD0 [%d]\n", ret); + return ret; + } + + /* configure sound gain in SGAIN */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NGAIN_MASK, + MICFIL_VAD0_NCONFIG_NGAIN(MICFIL_HWVAD_NGAIN)); + if (ret) { + dev_err(dev, "Failed to set NGAIN in NCONFIG_VAD0 [%d]\n", ret); + return ret; + } + + /* configure or clear the VADNFILADJ based on mode */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NFILADJ_MASK, + MICFIL_VAD0_NCONFIG_NFILADJ(MICFIL_HWVAD_NFILADJ)); + if (ret) { + dev_err(dev, + "Failed to set VADNFILADJ in NCONFIG_VAD0 [%d]\n", + ret); + return ret; + } + + /* enable the high-pass filter in VADHPF */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_HPF_MASK, + MICFIL_VAD0_CTRL2_HPF(MICFIL_HWVAD_HPF_BYPASS)); + if (ret) { + dev_err(dev, "Failed to set HPF in CTRL2_VAD0 [%d]\n", ret); + return ret; + } + + /* envelope-based specific initialization */ + if (mode == MICFIL_HWVAD_ENVELOPE_MODE) { + ret = init_hwvad_envelope_mode(dev); + if (ret) + return ret; + } else { + ret = init_hwvad_energy_mode(dev); + if (ret) + return ret; + } + + return 0; +} + static int fsl_micfil_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); struct device *dev = &micfil->pdev->dev; + int state; + + state = atomic_read(&micfil->state); + if (state != RECORDING_OFF_HWVAD_OFF) { + dev_err(dev, "Cannot record while recording or hwvad is on\n"); + return -EPERM; + } if (!micfil) { dev_err(dev, "micfil dai priv_data not set\n"); @@ -280,11 +798,24 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); struct device *dev = &micfil->pdev->dev; int ret; + int old_state; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* mark as stream open. Not allowed: + * - two paralel recordings + * - hwvad enabled while recording + */ + old_state = atomic_cmpxchg(&micfil->state, + RECORDING_OFF_HWVAD_OFF, + RECORDING_ON_HWVAD_OFF); + if (old_state != RECORDING_OFF_HWVAD_OFF) { + dev_err(dev, "Another record or hwvad is on\n"); + return -EBUSY; + } + ret = fsl_micfil_reset(dev); if (ret) { dev_err(dev, "failed to soft reset\n"); @@ -313,6 +844,7 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dev, "failed to enable the module\n"); return ret; } + break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: @@ -333,6 +865,9 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dev, "failed to update DISEL bits\n"); return ret; } + + /* clear the stream open flag */ + atomic_set(&micfil->state, RECORDING_OFF_HWVAD_OFF); break; default: return -EINVAL; @@ -340,6 +875,43 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int fsl_set_clock_params(struct device *dev, unsigned int rate) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + unsigned int clk_div; + int ret; + + if (fsl_micfil_bsy(dev)) + return -EBUSY; + + /* set CICOSR */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_CICOSR_MASK, + MICFIL_CTRL2_OSR_DEFAULT); + if (ret) { + dev_err(dev, "failed to set CICOSR in reg 0x%X\n", + REG_MICFIL_CTRL2); + return ret; + } + + /* set CLK_DIV */ + clk_div = get_clk_div(micfil, rate); + + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_CLKDIV_MASK, clk_div); + if (ret) { + dev_err(dev, "failed to set CLKDIV in reg 0x%X\n", + REG_MICFIL_CTRL2); + return ret; + } + + ret = clk_prepare_enable(micfil->mclk); + if (ret) + return ret; + + return 0; +} + static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -348,7 +920,6 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); struct device *dev = &micfil->pdev->dev; - unsigned int clk_div; int ret; /* 1. Disable the module */ @@ -368,41 +939,13 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, return ret; } - /* The following bits must not change - * when BSY_FIL is asserted - */ - if (!fsl_micfil_bsy(dev)) { - /* set CICOSR */ - ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, - MICFIL_CTRL2_CICOSR_MASK, - MICFIL_CTRL2_OSR_DEFAULT); - if (ret) { - dev_err(dev, "failed to set CICOSR in reg 0x%X\n", - REG_MICFIL_CTRL2); - return ret; - } - - /* Do not enable the clock if it is already enabled */ - if (!(micfil->mclk_streams & BIT(substream->stream))) { - ret = clk_prepare_enable(micfil->mclk); - if (ret) - return ret; - micfil->mclk_streams |= BIT(substream->stream); - } - - /* set CLK_DIV */ - clk_div = get_clk_div(micfil, rate); - ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, - MICFIL_CTRL2_CLKDIV_MASK, clk_div); - if (ret) { - dev_err(dev, "failed to set CLKDIV in reg 0x%X\n", - REG_MICFIL_CTRL2); - return ret; - } - } + ret = fsl_set_clock_params(dev, rate); + if (ret) + return ret; micfil->dma_params_rx.fifo_num = channels; micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; + micfil->channels = channels; return 0; } @@ -592,7 +1135,7 @@ static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) switch (reg) { case REG_MICFIL_CTRL1: case REG_MICFIL_CTRL2: - case REG_MICFIL_STAT: + case REG_MICFIL_STAT: /* Write 1 to Clear */ case REG_MICFIL_FIFO_CTRL: case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ case REG_MICFIL_DC_CTRL: @@ -614,6 +1157,7 @@ static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case REG_MICFIL_CTRL1: + case REG_MICFIL_CTRL2: case REG_MICFIL_STAT: case REG_MICFIL_DATACH0: case REG_MICFIL_DATACH1: @@ -623,7 +1167,13 @@ static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) case REG_MICFIL_DATACH5: case REG_MICFIL_DATACH6: case REG_MICFIL_DATACH7: + case REG_MICFIL_VAD0_CTRL1: + case REG_MICFIL_VAD0_CTRL2: + case REG_MICFIL_VAD0_STAT: + case REG_MICFIL_VAD0_SCONFIG: + case REG_MICFIL_VAD0_NCONFIG: case REG_MICFIL_VAD0_NDATA: + case REG_MICFIL_VAD0_ZCD: return true; default: return false; @@ -646,19 +1196,77 @@ static const struct regmap_config fsl_micfil_regmap_config = { /* END OF REGMAP */ +static void voice_detected_irq(struct fsl_micfil *micfil) +{ + struct device *dev = &micfil->pdev->dev; + int ret; + + /* disable hwvad */ + ret = disable_hwvad(dev); + if (ret) + dev_err(dev, "Failed to disable HWVAD module\n"); + + /* disable hwvad interrupts */ + ret = configure_hwvad_interrupts(dev, 0); + if (ret) + dev_err(dev, "Failed to disable interrupts\n"); + + /* notify userspace that voice was detected */ + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); +} + +static irqreturn_t hwvad_isr(int irq, void *devid) +{ + struct fsl_micfil *micfil = (struct fsl_micfil *)devid; + struct device *dev = &micfil->pdev->dev; + u32 vad0_reg; + int old_flag; + + regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); + + old_flag = atomic_cmpxchg(&micfil->voice_detected, 0, 1); + if ((vad0_reg & MICFIL_VAD0_STAT_IF_MASK) && !old_flag) { + dev_info(dev, "Detected voice\n"); + + /* Write 1 to clear */ + regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_STAT, + MICFIL_VAD0_STAT_IF_MASK, + MICFIL_VAD0_STAT_IF); + + voice_detected_irq(micfil); + } + + return IRQ_HANDLED; +} + static irqreturn_t micfil_isr(int irq, void *devid) { struct fsl_micfil *micfil = (struct fsl_micfil *)devid; struct platform_device *pdev = micfil->pdev; + irqreturn_t ret = IRQ_HANDLED; u32 stat_reg; u32 ctrl1_reg; + u32 vad0_reg; bool dma_enabled; int i; regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); + regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); dma_enabled = MICFIL_DMA_ENABLED(ctrl1_reg); + if (vad0_reg & MICFIL_VAD0_STAT_IF_MASK) + ret = IRQ_WAKE_THREAD; + + if (vad0_reg & MICFIL_VAD0_STAT_EF_MASK) + dev_dbg(&pdev->dev, "isr: voice activity event detected\n"); + + if (vad0_reg & MICFIL_VAD0_STAT_INSATF_MASK) + dev_dbg(&pdev->dev, "isr: voice activity input overflow/underflow detected\n"); + + if (vad0_reg & MICFIL_VAD0_STAT_INITF_MASK) + dev_dbg(&pdev->dev, "isr: voice activity dectector is initializing\n"); + if (stat_reg & MICFIL_STAT_BSY_FIL_MASK) dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); @@ -686,9 +1294,159 @@ static irqreturn_t micfil_isr(int irq, void *devid) 1); } - return IRQ_HANDLED; + return ret; } +static int fsl_set_clock_params(struct device *, unsigned int); + +static int enable_hwvad(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret; + int old_state; + + /* go further with enablement only if both recording and + * hwvad are not on + */ + old_state = atomic_cmpxchg(&micfil->state, + RECORDING_OFF_HWVAD_OFF, + RECORDING_OFF_HWVAD_ON); + if (old_state != RECORDING_OFF_HWVAD_OFF) { + dev_err(dev, "Another record or hwvad is on\n"); + return -EBUSY; + } + + /* This is required because if an arecord was done, + * suspend function will mark regmap as cache only + * and reads/writes in volatile regs will fail + */ + regcache_mark_dirty(micfil->regmap); + regcache_sync(micfil->regmap); + regcache_cache_only(micfil->regmap, false); + + /* clear voice detected flag */ + atomic_set(&micfil->voice_detected, 0); + + ret = fsl_set_clock_params(dev, MICFIL_DEFAULT_RATE); + if (ret) + goto enable_err; + + ret = fsl_micfil_reset(dev); + if (ret) + return ret; + + /* Initialize Hardware Voice Activity */ + ret = init_hwvad(dev, MICFIL_HWVAD_ENVELOPE_MODE); + if (ret) + goto enable_err; + + return 0; + +enable_err: + atomic_cmpxchg(&micfil->state, RECORDING_OFF_HWVAD_ON, RECORDING_OFF_HWVAD_OFF); + return ret; +} + +static int disable_hwvad(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret = 0; + int old_state; + + old_state = atomic_read(&micfil->state); + + if (old_state == RECORDING_OFF_HWVAD_ON) { + /* Disable MICFIL module */ + ret |= regmap_update_bits(micfil->regmap, + REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN_MASK, + 0); + + /* Voice Activity Detector Reset */ + ret |= regmap_update_bits(micfil->regmap, + REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_RST_SHIFT, + MICFIL_VAD0_CTRL1_RST); + + /* Disable HWVAD */ + ret |= regmap_update_bits(micfil->regmap, + REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_EN_MASK, + 0); + + /* Disable Signal Filter */ + ret |= regmap_update_bits(micfil->regmap, + REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SFILEN_MASK, + 0); + + /* Signal Maximum Enable */ + ret |= regmap_update_bits(micfil->regmap, + REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SMAXEN_MASK, + 0); + + /* Enable pre-filter Noise & Signal */ + ret |= regmap_update_bits(micfil->regmap, + REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_PREFEN_MASK, + 0); + + /* Noise Decimation Enable */ + ret |= regmap_update_bits(micfil->regmap, + REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NDECEN_MASK, + 0); + + /* disable the clock */ + clk_disable_unprepare(micfil->mclk); + + atomic_set(&micfil->state, RECORDING_OFF_HWVAD_OFF); + } else { + ret = -EPERM; + dev_err(dev, "HWVAD is not enabled %d\n", ret); + } + + return ret; +} + +static ssize_t micfil_hwvad_handler(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + struct kobject *nand_kobj = kobj->parent; + struct device *dev = container_of(nand_kobj, struct device, kobj); + struct fsl_micfil *micfil = dev_get_drvdata(dev); + unsigned long enabled_channels; + int ret; + + ret = kstrtoul(buf, 16, &enabled_channels); + if (ret < 0) + return -EINVAL; + + if (enabled_channels > 0 && enabled_channels <= 8) { + dev_info(dev, + "enabling hwvad with %lu channels at rate %d\n", + enabled_channels, MICFIL_DEFAULT_RATE); + micfil->channels = enabled_channels; + ret = enable_hwvad(dev); + } else if (!enabled_channels) { + dev_info(dev, "disabling hwvad\n"); + micfil->channels = 0; + ret = disable_hwvad(dev); + } else { + dev_err(dev, "Unsupported number of channels. Try 0,1..8\n"); + ret = -EINVAL; + } + if (ret) + return ret; + + return count; +} + +static struct kobj_attribute hwvad_enable_attribute = __ATTR(enable, 0660, NULL, micfil_hwvad_handler); + static int fsl_micfil_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -759,11 +1517,12 @@ static int fsl_micfil_probe(struct platform_device *pdev) return irq; } - if (of_property_read_bool(np, "shared-interrupt")) + if (of_property_read_bool(np, "fsl,shared-interrupt")) irqflag = IRQF_SHARED; - ret = devm_request_irq(&pdev->dev, irq, micfil_isr, irqflag, - micfil->name, micfil); + ret = devm_request_threaded_irq(&pdev->dev, irq, micfil_isr, + hwvad_isr, irqflag, + micfil->name, micfil); if (ret) { dev_err(&pdev->dev, "failed to claim irq %u\n", irq); return ret; @@ -797,6 +1556,20 @@ static int fsl_micfil_probe(struct platform_device *pdev) return ret; } + /* create sysfs entry used to enable hwvad from userspace */ + micfil->hwvad_kobject = kobject_create_and_add("hwvad", + &pdev->dev.kobj); + if (!micfil->hwvad_kobject) + return -ENOMEM; + + ret = sysfs_create_file(micfil->hwvad_kobject, + &hwvad_enable_attribute.attr); + if (ret) { + dev_err(&pdev->dev, "failed to create file for hwvad_enable\n"); + kobject_put(micfil->hwvad_kobject); + return -ENOMEM; + } + return 0; } diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h index 3252db75fc0f..50a06b5f40d5 100644 --- a/sound/soc/fsl/fsl_micfil.h +++ b/sound/soc/fsl/fsl_micfil.h @@ -116,6 +116,165 @@ #define MICFIL_FIFO_CTRL_FIFOWMK(v) (((v) << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT) \ & MICFIL_FIFO_CTRL_FIFOWMK_MASK) +/* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/ +#define MICFIL_VAD0_CTRL1_CHSEL_SHIFT 24 +#define MICFIL_VAD0_CTRL1_CHSEL_WIDTH 3 +#define MICFIL_VAD0_CTRL1_CHSEL_MASK ((BIT(MICFIL_VAD0_CTRL1_CHSEL_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) +#define MICFIL_VAD0_CTRL1_CHSEL(v) (((v) << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) \ + & MICFIL_VAD0_CTRL1_CHSEL_MASK) +#define MICFIL_VAD0_CTRL1_CICOSR_SHIFT 16 +#define MICFIL_VAD0_CTRL1_CICOSR_WIDTH 4 +#define MICFIL_VAD0_CTRL1_CICOSR_MASK ((BIT(MICFIL_VAD0_CTRL1_CICOSR_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) +#define MICFIL_VAD0_CTRL1_CICOSR(v) (((v) << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) \ + & MICFIL_VAD0_CTRL1_CICOSR_MASK) +#define MICFIL_VAD0_CTRL1_INITT_SHIFT 8 +#define MICFIL_VAD0_CTRL1_INITT_WIDTH 5 +#define MICFIL_VAD0_CTRL1_INITT_MASK ((BIT(MICFIL_VAD0_CTRL1_INITT_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL1_INITT_SHIFT) +#define MICFIL_VAD0_CTRL1_INITT(v) (((v) << MICFIL_VAD0_CTRL1_INITT_SHIFT) \ + & MICFIL_VAD0_CTRL1_INITT_MASK) +#define MICFIL_VAD0_CTRL1_ST10_SHIFT 4 +#define MICFIL_VAD0_CTRL1_ST10_MASK BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT) +#define MICFIL_VAD0_CTRL1_ST10 BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT) +#define MICFIL_VAD0_CTRL1_ERIE_SHIFT 3 +#define MICFIL_VAD0_CTRL1_ERIE_MASK BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT) +#define MICFIL_VAD0_CTRL1_ERIE BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT) +#define MICFIL_VAD0_CTRL1_IE_SHIFT 2 +#define MICFIL_VAD0_CTRL1_IE_MASK BIT(MICFIL_VAD0_CTRL1_IE_SHIFT) +#define MICFIL_VAD0_CTRL1_IE BIT(MICFIL_VAD0_CTRL1_IE_SHIFT) +#define MICFIL_VAD0_CTRL1_RST_SHIFT 1 +#define MICFIL_VAD0_CTRL1_RST BIT(MICFIL_VAD0_CTRL1_RST_SHIFT) +#define MICFIL_VAD0_CTRL1_EN_SHIFT 0 +#define MICFIL_VAD0_CTRL1_EN_MASK BIT(MICFIL_VAD0_CTRL1_EN_SHIFT) +#define MICFIL_VAD0_CTRL1_EN BIT(MICFIL_VAD0_CTRL1_EN_SHIFT) + +/* MICFIL HWVAD0 Control 2 Register -- REG_MICFIL_VAD0_CTRL2*/ +#define MICFIL_VAD0_CTRL2_FRENDIS_SHIFT 31 +#define MICFIL_VAD0_CTRL2_FRENDIS_MASK BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_FRENDIS BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_PREFEN_SHIFT 30 +#define MICFIL_VAD0_CTRL2_PREFEN_MASK BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT) +#define MICFIL_VAD0_CTRL2_PREFEN BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT) +#define MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT 28 +#define MICFIL_VAD0_CTRL2_FOUTDIS_MASK BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_FOUTDIS BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_FRAMET_SHIFT 16 +#define MICFIL_VAD0_CTRL2_FRAMET_WIDTH 6 +#define MICFIL_VAD0_CTRL2_FRAMET_MASK ((BIT(MICFIL_VAD0_CTRL2_FRAMET_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) +#define MICFIL_VAD0_CTRL2_FRAMET(v) (((v) << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) \ + & MICFIL_VAD0_CTRL2_FRAMET_MASK) +#define MICFIL_VAD0_CTRL2_INPGAIN_SHIFT 8 +#define MICFIL_VAD0_CTRL2_INPGAIN_WIDTH 4 +#define MICFIL_VAD0_CTRL2_INPGAIN_MASK ((BIT(MICFIL_VAD0_CTRL2_INPGAIN_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) +#define MICFIL_VAD0_CTRL2_INPGAIN(v) (((v) << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) \ + & MICFIL_VAD0_CTRL2_INPGAIN_MASK) +#define MICFIL_VAD0_CTRL2_HPF_SHIFT 0 +#define MICFIL_VAD0_CTRL2_HPF_WIDTH 2 +#define MICFIL_VAD0_CTRL2_HPF_MASK ((BIT(MICFIL_VAD0_CTRL2_HPF_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL2_HPF_SHIFT) +#define MICFIL_VAD0_CTRL2_HPF(v) (((v) << MICFIL_VAD0_CTRL2_HPF_SHIFT) \ + & MICFIL_VAD0_CTRL2_HPF_MASK) + +/* MICFIL HWVAD0 Signal CONFIG Register -- REG_MICFIL_VAD0_SCONFIG */ +#define MICFIL_VAD0_SCONFIG_SFILEN_SHIFT 31 +#define MICFIL_VAD0_SCONFIG_SFILEN_MASK BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SFILEN BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT 30 +#define MICFIL_VAD0_SCONFIG_SMAXEN_MASK BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SMAXEN BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SGAIN_SHIFT 0 +#define MICFIL_VAD0_SCONFIG_SGAIN_WIDTH 4 +#define MICFIL_VAD0_SCONFIG_SGAIN_MASK ((BIT(MICFIL_VAD0_SCONFIG_SGAIN_WIDTH) - 1) \ + << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SGAIN(v) (((v) << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) \ + & MICFIL_VAD0_SCONFIG_SGAIN_MASK) + +/* MICFIL HWVAD0 Noise CONFIG Register -- REG_MICFIL_VAD0_NCONFIG */ +#define MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT 31 +#define MICFIL_VAD0_NCONFIG_NFILAUT_MASK BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT) +#define MICFIL_VAD0_NCONFIG_NFILAUT BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT) +#define MICFIL_VAD0_NCONFIG_NMINEN_SHIFT 30 +#define MICFIL_VAD0_NCONFIG_NMINEN_MASK BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NMINEN BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NDECEN_SHIFT 29 +#define MICFIL_VAD0_NCONFIG_NDECEN_MASK BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NDECEN BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NOREN_SHIFT 28 +#define MICFIL_VAD0_NCONFIG_NOREN BIT(MICFIL_VAD0_NCONFIG_NOREN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT 8 +#define MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH 5 +#define MICFIL_VAD0_NCONFIG_NFILADJ_MASK ((BIT(MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH) - 1) \ + << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) +#define MICFIL_VAD0_NCONFIG_NFILADJ(v) (((v) << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) \ + & MICFIL_VAD0_NCONFIG_NFILADJ_MASK) +#define MICFIL_VAD0_NCONFIG_NGAIN_SHIFT 0 +#define MICFIL_VAD0_NCONFIG_NGAIN_WIDTH 4 +#define MICFIL_VAD0_NCONFIG_NGAIN_MASK ((BIT(MICFIL_VAD0_NCONFIG_NGAIN_WIDTH) - 1) \ + << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NGAIN(v) (((v) << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) \ + & MICFIL_VAD0_NCONFIG_NGAIN_MASK) + +/* MICFIL HWVAD0 Zero-Crossing Detector - REG_MICFIL_VAD0_ZCD */ +#define MICFIL_VAD0_ZCD_ZCDTH_SHIFT 16 +#define MICFIL_VAD0_ZCD_ZCDTH_WIDTH 10 +#define MICFIL_VAD0_ZCD_ZCDTH_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDTH_WIDTH) - 1) \ + << MICFIL_VAD0_ZCD_ZCDTH_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDTH(v) (((v) << MICFIL_VAD0_ZCD_ZCDTH_SHIFT)\ + & MICFIL_VAD0_ZCD_ZCDTH_MASK) +#define MICFIL_VAD0_ZCD_ZCDADJ_SHIFT 8 +#define MICFIL_VAD0_ZCD_ZCDADJ_WIDTH 4 +#define MICFIL_VAD0_ZCD_ZCDADJ_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDADJ_WIDTH) - 1)\ + << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDADJ(v) (((v) << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT)\ + & MICFIL_VAD0_ZCD_ZCDADJ_MASK) +#define MICFIL_VAD0_ZCD_ZCDAND_SHIFT 4 +#define MICFIL_VAD0_ZCD_ZCDAND_MASK BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDAND BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDAUT_SHIFT 2 +#define MICFIL_VAD0_ZCD_ZCDAUT_MASK BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDAUT BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDEN_SHIFT 0 +#define MICFIL_VAD0_ZCD_ZCDEN_MASK BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDEN BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT) + +/* MICFIL HWVAD0 Status Register - REG_MICFIL_VAD0_STAT */ +#define MICFIL_VAD0_STAT_INITF_SHIFT 31 +#define MICFIL_VAD0_STAT_INITF_MASK BIT(MICFIL_VAD0_STAT_INITF_SHIFT) +#define MICFIL_VAD0_STAT_INITF BIT(MICFIL_VAD0_STAT_INITF_SHIFT) +#define MICFIL_VAD0_STAT_INSATF_SHIFT 16 +#define MICFIL_VAD0_STAT_INSATF_MASK BIT(MICFIL_VAD0_STAT_INSATF_SHIFT) +#define MICFIL_VAD0_STAT_INSATF BIT(MICFIL_VAD0_STAT_INSATF_SHIFT) +#define MICFIL_VAD0_STAT_EF_SHIFT 15 +#define MICFIL_VAD0_STAT_EF_MASK BIT(MICFIL_VAD0_STAT_EF_SHIFT) +#define MICFIL_VAD0_STAT_EF BIT(MICFIL_VAD0_STAT_EF_SHIFT) +#define MICFIL_VAD0_STAT_IF_SHIFT 0 +#define MICFIL_VAD0_STAT_IF_MASK BIT(MICFIL_VAD0_STAT_IF_SHIFT) +#define MICFIL_VAD0_STAT_IF BIT(MICFIL_VAD0_STAT_IF_SHIFT) + +/* HWVAD Constants */ +#define MICFIL_HWVAD_ENVELOPE_MODE 0 +#define MICFIL_HWVAD_ENERGY_MODE 1 +#define MICFIL_HWVAD_INIT_FRAMES 10 +#define MICFIL_HWVAD_INPGAIN 0 +#define MICFIL_HWVAD_SGAIN 6 +#define MICFIL_HWVAD_NGAIN 3 +#define MICFIL_HWVAD_NFILADJ 0 +#define MICFIL_HWVAD_ZCDADJ (1 << (MICFIL_VAD0_ZCD_ZCDADJ_WIDTH - 2)) +#define MICFIL_HWVAD_ZCDTH 10 /* initial threshold value */ +#define MICFIL_HWVAD_ZCDOR 0 +#define MICFIL_HWVAD_ZCDAND 1 +#define MICFIL_HWVAD_ZCD_MANUAL 0 +#define MICFIL_HWVAD_ZCD_AUTO 1 +#define MICFIL_HWVAD_HPF_BYPASS 0 +#define MICFIL_HWVAD_HPF_1750HZ 1 +#define MICFIL_HWVAD_HPF_215HZ 2 +#define MICFIL_HWVAD_HPF_102HZ 3 +#define MICFIL_HWVAD_FRAMET_DEFAULT 10 + /* MICFIL Output Control Register */ #define MICFIL_OUTGAIN_CHX_SHIFT(v) (4 * (v)) @@ -130,10 +289,16 @@ #define FIFO_PTRWID 3 #define FIFO_LEN BIT(FIFO_PTRWID) -#define MICFIL_MAX_RETRY 10 +#define MICFIL_MAX_RETRY 25 #define MICFIL_SLEEP_MIN 90000 /* in us */ #define MICFIL_SLEEP_MAX 100000 /* in us */ #define MICFIL_DMA_MAXBURST_RX 6 #define MICFIL_CTRL2_OSR_DEFAULT (0 << MICFIL_CTRL2_CICOSR_SHIFT) +#define MICFIL_DEFAULT_RATE 48000 + +/* States of micfil */ +#define RECORDING_OFF_HWVAD_OFF 0 +#define RECORDING_OFF_HWVAD_ON 1 +#define RECORDING_ON_HWVAD_OFF 2 #endif /* _FSL_MICFIL_H */ |