summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorCosmin-Gabriel Samoila <cosmin.samoila@nxp.com>2018-07-19 09:20:26 +0300
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commit06495dd0cb0d0e2de225e32251b78edafa473406 (patch)
tree2d41a881bb4ce1aae542f22ca6e74151c042a3b3 /sound
parent4cf9ddee84c995e02931be9de5448cb162130862 (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.c857
-rw-r--r--sound/soc/fsl/fsl_micfil.h167
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 */