summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCHLin <CHLIN56@nuvoton.com>2018-02-01 13:19:07 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-02-28 15:21:13 -0800
commit5e614b1c98b73579d7f7975299a8be215f6ffd6d (patch)
tree27e8c34980a2d53bb600def03945c6913e40ef63
parent2f1e99bcef7165c051b08bc365ef6e1f689843e0 (diff)
downloadchrome-ec-5e614b1c98b73579d7f7975299a8be215f6ffd6d.tar.gz
npcx7: WoV: Add support for Wake-on-Voice (WoV) module
This CL adds the driver support for the WoV module which inludes the following files: - wov.c - wov_chip.h - apm.c - apm_chip.h It also supports the console commad "wov" which can test different configuration and audio quality by entering different parameters. The detail description of WoV console command is listed below: ------------------------------------------------------------------------ [Note]: Before changing any of settings, please make sure the operation mode is on the "OFF" state. (ie. run the command wov cfgmod off first) . > wov init Initialize WoV interface, including pin mux and interrupt registration etc. > wov mute <enable / disable > mute enable / disable. > wov cfgsrc <mono | stereo | left | right> set audio source, ex: wov cfgsrc left, means audio source from left MIC. > wov cfgbis <16|18|20|24> set audio resolution, ex: wov cfgbit 16 means audio resolution are 16bits. > wov cfgsfs <8000|12000|16000|24000|32000|48000> set audio sampling frequency rate, ex: wov cfgsfs 48000 means audio sampling rate are 48Khz. > wov cfgbck <32fs|48fs|64fs|128fs|256fs> set I2S bit clock rate, ex: wov cfgsfs 48000 and wov cfgbck 32fs means audio sampling rate are 1536Khz (32*48000). > wov cfgfmt <i2s|right|left|pcma|pcmb|tdm> set I2S but format, ex: wov cfgfmt right means audio I2S format are Right-Justify. > wov cfgmod <off|vad|ram|i2s|rami2s> set audio operation mode ,ex: wov cfgmod i2s means audio output via I2S bus. > wov cfgtdm <0~496 0~496 0~3> set TDM time slot, the first values is left channel delay counter, the second is right channel, and the 3rd is startup counting condition. (chosen LRCK raising or falling edge) . [Note: this command is just working on cfgmod equal to tdm] > wov cfgget retrieve above settings. > wov vadsens (currently not support, reserve for next version) > wov gain (0~31) set audio data gain value, ex: wov gain 10 means setting audio digital gain are 10dB. > wov cfgdck <1.0 | 2.4 | 3.0 > set digital MIC PDM clock rate. ex: wov cfgdck 2.4 means PDM clock are 2.4Mhz. ----------------------------------------------------------------------- This CL also adds the chip ID (0x24) for npcx7m7w. So the console command "version" can show the chip is npcx7m7w. BRANCH=none BUG=none TEST=No build errors for make buildall. TEST="BOARD=npcx7_evb make"; Flash the image on EVB; Test WoV function with console commands described above. Change-Id: Ief2b3e89edbd3e6d2a9d82d317a93c9f0b7a20cd Signed-off-by: Dror Goldstein <dror.goldstein@nuvoton.com> Signed-off-by: Simon Liang <CMLiang@nuvoton.com> Signed-off-by: CHLin <CHLIN56@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/897314 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: CH Lin <chlin56@nuvoton.com> Reviewed-by: Scott Collyer <scollyer@chromium.org>
-rw-r--r--board/npcx7_evb/gpio.inc6
-rw-r--r--chip/npcx/apm.c620
-rw-r--r--chip/npcx/apm_chip.h418
-rw-r--r--chip/npcx/build.mk1
-rw-r--r--chip/npcx/gpio_chip-npcx7.h22
-rw-r--r--chip/npcx/registers.h192
-rw-r--r--chip/npcx/system.c6
-rw-r--r--chip/npcx/wov.c1848
-rw-r--r--chip/npcx/wov_chip.h652
-rw-r--r--include/common.h2
-rw-r--r--include/console_channel.inc3
-rw-r--r--include/module_id.h1
-rwxr-xr-xutil/flash_ec2
13 files changed, 3768 insertions, 5 deletions
diff --git a/board/npcx7_evb/gpio.inc b/board/npcx7_evb/gpio.inc
index 2fe60c5b71..2acec02f09 100644
--- a/board/npcx7_evb/gpio.inc
+++ b/board/npcx7_evb/gpio.inc
@@ -100,3 +100,9 @@ ALTERNATE(PIN_MASK(3, 0x03), 0, MODULE_KEYBOARD_SCAN, 0)
ALTERNATE(PIN_MASK(D, 0x04), 1, MODULE_PMU, 0) /* PSL_IN1 GPIOD2 */
ALTERNATE(PIN_MASK(0, 0x07), 1, MODULE_PMU, 0) /* PSL_IN2/3/4 GPIO00/01/02 */
#endif
+
+#ifdef CONFIG_WAKE_ON_VOICE
+ALTERNATE(PIN_MASK(A, 0xA0), 1, MODULE_WOV, 0) /* I2S_SYNC/I2S_SCLK GPIOA5/A7 */
+ALTERNATE(PIN_MASK(B, 0x01), 1, MODULE_WOV, 0) /* I2S_SDAT GPIOB0 */
+ALTERNATE(PIN_MASK(9, 0x90), 1, MODULE_WOV, 0) /* DMIC_CLK/DMIC_IN GPIO94/97 */
+#endif
diff --git a/chip/npcx/apm.c b/chip/npcx/apm.c
new file mode 100644
index 0000000000..a394d0a06a
--- /dev/null
+++ b/chip/npcx/apm.c
@@ -0,0 +1,620 @@
+/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* NPCX-specific APM module for Chrome EC */
+
+#include "apm_chip.h"
+#include "common.h"
+#include "registers.h"
+#include "util.h"
+#include "wov_chip.h"
+
+static struct apm_config apm_conf;
+static struct apm_auto_gain_config apm_gain_conf;
+
+
+static uint32_t apm_indirect_reg[][3] = {
+ {(NPCX_APM_BASE_ADDR + 0x034), (NPCX_APM_BASE_ADDR + 0x038)},
+ {(NPCX_APM_BASE_ADDR + 0x04C), (NPCX_APM_BASE_ADDR + 0x050)},
+ {(NPCX_APM_BASE_ADDR + 0x05C), (NPCX_APM_BASE_ADDR + 0x060)}
+};
+
+#define APM_CNTRL_REG 0
+#define APM_DATA_REG 1
+
+/**
+ * Reads data indirect register.
+ *
+ * @param reg_offset - Indirect register APM_MIX_REG, APM_ADC_AGC_REG or
+ * APM_VAD_REG.
+ * @param indirect_addr - Indirect access address.
+ * @return The read data.
+ */
+static uint8_t apm_read_indirect_data(enum apm_indirect_reg_offset reg_offset,
+ uint8_t indirect_addr)
+{
+ /* Set the indirect access address. */
+ SET_FIELD(REG8(apm_indirect_reg[reg_offset][APM_CNTRL_REG]),
+ NPCX_APM_CONTROL_ADD, indirect_addr);
+
+ /* Read command. */
+ CLEAR_BIT(REG8(apm_indirect_reg[reg_offset][APM_CNTRL_REG]),
+ NPCX_APM_CONTROL_LOAD);
+
+ /* Get the data. */
+ return REG8(apm_indirect_reg[reg_offset][APM_DATA_REG]);
+}
+
+/**
+ * Writes data indirect register.
+ *
+ * @param reg_offset - Indirect register APM_MIX_REG, APM_ADC_AGC_REG or
+ * APM_VAD_REG.
+ * @param indirect_addr - Indirect access address.
+ * @param value - Written value.
+ * @return None
+ */
+static void apm_write_indirect_data(enum apm_indirect_reg_offset reg_offset,
+ uint8_t indirect_addr, uint8_t value)
+{
+ /* Set the data. */
+ REG8(apm_indirect_reg[reg_offset][APM_DATA_REG]) = value;
+
+ /* Set the indirect access address. */
+ SET_FIELD(REG8(apm_indirect_reg[reg_offset][APM_CNTRL_REG]),
+ NPCX_APM_CONTROL_ADD, indirect_addr);
+
+ /* Write command. */
+ SET_BIT(REG8(apm_indirect_reg[reg_offset][APM_CNTRL_REG]),
+ NPCX_APM_CONTROL_LOAD);
+ CLEAR_BIT(REG8(apm_indirect_reg[reg_offset][APM_CNTRL_REG]),
+ NPCX_APM_CONTROL_LOAD);
+}
+
+/**
+ * Sets the ADC DMIC rate.
+ *
+ * @param rate - ADC digital microphone rate
+ * @return None
+ */
+void apm_set_adc_dmic_config_l(enum apm_dmic_rate rate)
+{
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_RATE, rate);
+}
+
+/**
+ * Sets VAD DMIC rate.
+ *
+ * @param rate - VAD DMIC rate
+ *
+ * @return None
+ */
+void apm_set_vad_dmic_rate_l(enum apm_dmic_rate rate)
+{
+ uint8_t vad_data;
+
+ vad_data = apm_read_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_0_REG);
+
+ /* Set VAD_0 register. */
+ SET_FIELD(vad_data, NPCX_VAD_0_VAD_DMIC_FREQ, rate);
+
+ apm_write_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_0_REG, vad_data);
+}
+
+/**
+ * Translates from ADC real value to frequency code
+ *
+ * @param adc_freq_val - ADC frequency.
+ * @return ADC frequency code, 0xFFFF in case of wrong value.
+ */
+static enum apm_adc_frequency apm_adc_freq_val_2_code(uint32_t adc_freq_val)
+{
+ enum apm_adc_frequency freq_code;
+
+ switch (adc_freq_val) {
+ case 8000:
+ freq_code = APM_ADC_FREQ_8_000_KHZ;
+ break;
+ case 12000:
+ freq_code = APM_ADC_FREQ_12_000_KHZ;
+ break;
+ case 16000:
+ freq_code = APM_ADC_FREQ_16_000_KHZ;
+ break;
+ case 24000:
+ freq_code = APM_ADC_FREQ_24_000_KHZ;
+ break;
+ case 32000:
+ freq_code = APM_ADC_FREQ_32_000_KHZ;
+ break;
+ case 48000:
+ freq_code = APM_ADC_FREQ_48_000_KHZ;
+ break;
+ default:
+ freq_code = APM_ADC_FREQ_UNSUPPORTED;
+ break;
+ }
+
+ return freq_code;
+}
+
+/*****************************************************************************/
+/* IC specific low-level driver */
+
+/**
+ * Initiate APM module local parameters..
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void apm_init(void)
+{
+ apm_conf.adc_dmic_rate = APM_DMIC_RATE_3_0;
+ apm_conf.gain_coupling = APM_ADC_CHAN_GAINS_INDEPENDENT;
+ apm_conf.left_chan_gain = 0;
+ apm_conf.right_chan_gain = 0;
+
+ apm_gain_conf.stereo_enable = 0;
+ apm_gain_conf.agc_target = APM_ADC_MAX_TARGET_LEVEL_19_5;
+ apm_gain_conf.nois_gate_en = 0;
+ apm_gain_conf.nois_gate_thold = APM_MIN_NOISE_GET_THRESHOLD;
+ apm_gain_conf.hold_time = APM_HOLD_TIME_128;
+ apm_gain_conf.attack_time = APM_GAIN_RAMP_TIME_160;
+ apm_gain_conf.decay_time = APM_GAIN_RAMP_TIME_160;
+ apm_gain_conf.gain_max = APM_GAIN_VALUE_42_5;
+ apm_gain_conf.gain_min = APM_GAIN_VALUE_0_0;
+}
+
+/**
+ * Enables/Disables APM module.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void apm_enable(int enable)
+{
+ if (enable) {
+ CLEAR_BIT(NPCX_APM_CR_APM, NPCX_APM_CR_APM_PD);
+
+ /* Work around that enable the AGC. */
+ SET_FIELD(NPCX_APM_CR_APM, NPCX_APM_CR_APM_AGC_DIS, 0x00);
+
+ } else
+ SET_BIT(NPCX_APM_CR_APM, NPCX_APM_CR_APM_PD);
+}
+
+/**
+ * Enables/Disables voice activity detected interrupt.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return APM interrupt mode.
+ */
+void apm_enable_vad_interrupt(int enable)
+{
+ wov_interrupt_enable(WOV_VAD_INT_INDX, enable);
+ wov_interrupt_enable(WOV_VAD_WAKE_INDX, enable);
+ if (enable)
+ CLEAR_BIT(NPCX_APM_IMR, NPCX_APM_IMR_VAD_DTC_MASK);
+ else
+ SET_BIT(NPCX_APM_IMR, NPCX_APM_IMR_VAD_DTC_MASK);
+}
+
+/**
+ * Enables/Disables ADC.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void apm_adc_enable(int enable)
+{
+ if (enable) {
+ CLEAR_BIT(NPCX_APM_AICR_ADC, NPCX_APM_AICR_ADC_PD_AICR_ADC);
+ SET_FIELD(NPCX_APM_AICR_ADC,
+ NPCX_APM_AICR_ADC_ADC_AUDIOIF, 0x00);
+ } else {
+ SET_BIT(NPCX_APM_AICR_ADC, NPCX_APM_AICR_ADC_PD_AICR_ADC);
+ SET_FIELD(NPCX_APM_AICR_ADC,
+ NPCX_APM_AICR_ADC_ADC_AUDIOIF, 0x03);
+ }
+}
+
+/**
+ * sets the ADC frequency.
+ *
+ * @param adc_freq - ADC frequency.
+ * @return None
+ */
+void apm_adc_set_freq(enum apm_adc_frequency adc_freq)
+{
+ SET_FIELD(NPCX_APM_FCR_ADC, NPCX_APM_FCR_ADC_ADC_FREQ, adc_freq);
+}
+
+/**
+ * Configures the ADC.
+ *
+ * @param hpf_enable - High pass filter enabled flag, 1 means enable
+ * @param filter_mode - ADC wind noise filter mode.
+ * @param adc_freq - ADC frequency.
+ * @return None
+ */
+void apm_adc_config(int hpf_enable,
+ enum apm_adc_wind_noise_filter_mode filter_mode,
+ enum apm_adc_frequency adc_freq)
+{
+ if (hpf_enable)
+ SET_BIT(NPCX_APM_FCR_ADC, NPCX_APM_FCR_ADC_ADC_HPF);
+ else
+ CLEAR_BIT(NPCX_APM_FCR_ADC, NPCX_APM_FCR_ADC_ADC_HPF);
+
+ SET_FIELD(NPCX_APM_FCR_ADC, NPCX_APM_FCR_ADC_ADC_WNF, filter_mode);
+
+ SET_FIELD(NPCX_APM_FCR_ADC, NPCX_APM_FCR_ADC_ADC_FREQ, adc_freq);
+}
+
+/**
+ * Enables/Disables Digital Microphone.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void apm_dmic_enable(int enable)
+{
+ if (enable)
+ CLEAR_BIT(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_PD_DMIC);
+ else
+ SET_BIT(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_PD_DMIC);
+}
+
+/**
+ * Sets the ADC DMIC rate.
+ *
+ * @param rate - ADC digital microphone rate
+ * @return None
+ */
+void apm_set_adc_dmic_config(enum apm_dmic_rate rate)
+{
+ apm_conf.adc_dmic_rate = rate;
+}
+
+/**
+ * Configures Digital Mixer
+ *
+ * @param mix_left - Mixer left channel output selection on ADC path.
+ * @param mix_right - Mixer right channel output selection on ADC path.
+ * @return None
+ */
+void apm_digital_mixer_config(enum apm_dig_mix mix_left,
+ enum apm_dig_mix mix_right)
+{
+ uint8_t mix_2 = 0;
+
+ SET_FIELD(mix_2, NPCX_APM_MIX_2_AIADCL_SEL, mix_left);
+ SET_FIELD(mix_2, NPCX_APM_MIX_2_AIADCR_SEL, mix_right);
+
+ apm_write_indirect_data(APM_MIX_REG, APM_INDIRECT_MIX_2_REG, mix_2);
+}
+
+/**
+ * Enables/Disables the VAD functionality.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void apm_vad_enable(int enable)
+{
+ if (enable)
+ SET_BIT(NPCX_APM_CR_VAD, NPCX_APM_CR_VAD_VAD_EN);
+ else
+ CLEAR_BIT(NPCX_APM_CR_VAD, NPCX_APM_CR_VAD_VAD_EN);
+}
+
+/**
+ * Enables/Disables VAD ADC wakeup
+ *
+ * @param enable - 1 enable, 0 disable.
+ *
+ * @return None
+ */
+void apm_vad_adc_wakeup_enable(int enable)
+{
+ uint8_t vad_data;
+
+ vad_data = apm_read_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_0_REG);
+
+ if (enable)
+ SET_BIT(vad_data, NPCX_VAD_0_VAD_ADC_WAKEUP);
+ else
+ CLEAR_BIT(vad_data, NPCX_VAD_0_VAD_ADC_WAKEUP);
+
+ apm_write_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_0_REG, vad_data);
+}
+
+/**
+ * Sets VAD DMIC rate.
+ *
+ * @param rate - VAD DMIC rate
+ *
+ * @return None
+ */
+void apm_set_vad_dmic_rate(enum apm_dmic_rate rate)
+{
+ apm_conf.vad_dmic_rate = rate;
+}
+
+/**
+ * Sets VAD Input chanel.
+ *
+ * @param chan_src - Processed digital microphone channel
+ * selection.
+ * @return None
+ */
+void apm_set_vad_input_channel(enum apm_vad_in_channel_src chan_src)
+{
+ uint8_t vad_data;
+
+ vad_data = apm_read_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_0_REG);
+
+ SET_FIELD(vad_data, NPCX_VAD_0_VAD_INSEL, chan_src);
+
+ apm_write_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_0_REG, vad_data);
+}
+
+/**
+ * Sets VAD sensitivity.
+ *
+ * @param sensitivity_db - VAD sensitivity in db.
+ * @return None
+ */
+void apm_set_vad_sensitivity(uint8_t sensitivity_db)
+{
+ uint8_t vad_data;
+
+ vad_data = apm_read_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_1_REG);
+
+ SET_FIELD(vad_data, NPCX_VAD_1_VAD_POWER_SENS, sensitivity_db);
+
+ apm_write_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_1_REG, vad_data);
+}
+
+/**
+ * Gets VAD sensitivity.
+ *
+ * @param None.
+ * @return VAD sensitivity in db
+ */
+uint8_t apm_get_vad_sensitivity(void)
+{
+ uint8_t vad_data;
+
+ vad_data = apm_read_indirect_data(APM_VAD_REG, APM_INDIRECT_VAD_1_REG);
+
+ return GET_FIELD(vad_data, NPCX_VAD_1_VAD_POWER_SENS);
+}
+
+/**
+ * Restarts VAD functionality.
+ *
+ * @param None
+ * @return None
+ */
+void apm_vad_restart(void)
+{
+ SET_BIT(NPCX_APM_CR_VAD_CMD, NPCX_APM_CR_VAD_CMD_VAD_RESTART);
+}
+
+/**
+ * Restarts VAD functionality.
+ *
+ * @param gain_coupling - ADC digital gain coupling (independent or
+ * rigth tracks left).
+ * @param left_chan_gain - Left channel ADC digital gain programming value.
+ * @param right_chan_gain - Right channel ADC digital gain programming value.
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list apm_adc_gain_config(enum apm_adc_gain_coupling gain_coupling,
+ uint8_t left_chan_gain, uint8_t right_chan_gain)
+{
+ /* Check parameters validity. */
+ if ((left_chan_gain > 0x2B) || (right_chan_gain > 0x2B))
+ return EC_ERROR_INVAL;
+
+ /*
+ * Store the parameters in order to use them in case the function
+ * was called prioe calling to wov_set_mode.
+ */
+ apm_conf.gain_coupling = gain_coupling;
+ apm_conf.left_chan_gain = left_chan_gain;
+ apm_conf.right_chan_gain = right_chan_gain;
+
+ /* Set gain coupling.*/
+ if (gain_coupling == APM_ADC_CHAN_GAINS_INDEPENDENT)
+ CLEAR_BIT(NPCX_APM_GCR_ADCL, NPCX_APM_GCR_ADCL_LRGID);
+ else
+ SET_BIT(NPCX_APM_GCR_ADCL, NPCX_APM_GCR_ADCL_LRGID);
+
+ /* set channels gains. */
+ SET_FIELD(NPCX_APM_GCR_ADCL, NPCX_APM_GCR_ADCL_GIDL, left_chan_gain);
+ SET_FIELD(NPCX_APM_GCR_ADCR, NPCX_APM_GCR_ADCR_GIDR, right_chan_gain);
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Enables/Disables the automatic gain.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void apm_auto_gain_cntrl_enable(int enable)
+{
+ if (enable)
+ SET_BIT(NPCX_APM_CR_ADC_AGC, NPCX_APM_CR_ADC_AGC_ADC_AGC_EN);
+ else
+ CLEAR_BIT(NPCX_APM_CR_ADC_AGC, NPCX_APM_CR_ADC_AGC_ADC_AGC_EN);
+}
+
+/**
+ * Enables/Disables the automatic gain.
+ *
+ * @param gain_cfg - struct of apm auto gain config
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list apm_adc_auto_gain_config(
+ struct apm_auto_gain_config *gain_cfg)
+{
+ uint8_t gain_data = 0;
+
+ /* Check parameters validity. */
+
+ if (gain_cfg->gain_min > gain_cfg->gain_max)
+ return EC_ERROR_INVAL;
+
+ /*
+ * Store the parameters in order to use them in case the function
+ * was called prioe calling to wov_set_mode.
+ */
+ apm_gain_conf.stereo_enable = gain_cfg->stereo_enable;
+ apm_gain_conf.agc_target = gain_cfg->agc_target;
+ apm_gain_conf.nois_gate_en = gain_cfg->nois_gate_en;
+ apm_gain_conf.nois_gate_thold = gain_cfg->nois_gate_thold;
+ apm_gain_conf.hold_time = gain_cfg->hold_time;
+ apm_gain_conf.attack_time = gain_cfg->attack_time;
+ apm_gain_conf.decay_time = gain_cfg->decay_time;
+ apm_gain_conf.gain_max = gain_cfg->gain_max;
+ apm_gain_conf.gain_min = gain_cfg->gain_min;
+
+ /* Set the parameters. */
+
+ if (gain_cfg->stereo_enable)
+ CLEAR_BIT(gain_data, NPCX_ADC_AGC_0_AGC_STEREO);
+ else
+ SET_BIT(gain_data, NPCX_ADC_AGC_0_AGC_STEREO);
+
+ SET_FIELD(gain_data, NPCX_ADC_AGC_0_AGC_TARGET, gain_cfg->agc_target);
+
+ apm_write_indirect_data(APM_ADC_AGC_REG, APM_INDIRECT_ADC_AGC_0_REG,
+ gain_data);
+
+ gain_data = 0;
+
+ if (gain_cfg->stereo_enable)
+ SET_BIT(gain_data, NPCX_ADC_AGC_1_NG_EN);
+ else
+ CLEAR_BIT(gain_data, NPCX_ADC_AGC_1_NG_EN);
+ SET_FIELD(gain_data, NPCX_ADC_AGC_1_NG_THR, gain_cfg->nois_gate_thold);
+ SET_FIELD(gain_data, NPCX_ADC_AGC_1_HOLD, gain_cfg->hold_time);
+
+ apm_write_indirect_data(APM_ADC_AGC_REG, APM_INDIRECT_ADC_AGC_1_REG,
+ gain_data);
+
+ gain_data = 0;
+
+ SET_FIELD(gain_data, NPCX_ADC_AGC_2_ATK, gain_cfg->attack_time);
+ SET_FIELD(gain_data, NPCX_ADC_AGC_2_DCY, gain_cfg->decay_time);
+
+ apm_write_indirect_data(APM_ADC_AGC_REG, APM_INDIRECT_ADC_AGC_2_REG,
+ gain_data);
+
+ gain_data = 0;
+
+ SET_FIELD(gain_data, NPCX_ADC_AGC_3_AGC_MAX, gain_cfg->gain_max);
+
+ apm_write_indirect_data(APM_ADC_AGC_REG, APM_INDIRECT_ADC_AGC_3_REG,
+ gain_data);
+
+ gain_data = 0;
+
+ SET_FIELD(gain_data, NPCX_ADC_AGC_4_AGC_MIN, gain_cfg->gain_min);
+
+ apm_write_indirect_data(APM_ADC_AGC_REG, APM_INDIRECT_ADC_AGC_4_REG,
+ gain_data);
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Sets APM mode (enables & disables APN sub modules accordingly
+ * to the APM mode).
+ *
+ * @param apm_mode - APM mode, DEFAULT, DETECTION, RECORD or INDEPENDENT modes.
+ * @return None
+ */
+void apm_set_mode(enum wov_modes wov_mode)
+{
+ apm_enable(0);
+
+ switch (wov_mode) {
+ case WOV_MODE_OFF:
+ apm_vad_enable(0);
+ apm_enable_vad_interrupt(0);
+ apm_dmic_enable(0);
+ apm_adc_enable(0);
+ apm_vad_adc_wakeup_enable(0);
+ wov_apm_active(0);
+ break;
+
+ case WOV_MODE_VAD:
+ apm_clear_vad_detected_bit();
+ wov_apm_active(1);
+ apm_vad_restart();
+ apm_vad_enable(1);
+ apm_enable_vad_interrupt(1);
+ apm_set_vad_sensitivity(wov_conf.sensitivity_db);
+ apm_set_vad_dmic_rate_l(apm_conf.vad_dmic_rate);
+ apm_dmic_enable(1);
+ apm_adc_enable(0);
+ apm_vad_adc_wakeup_enable(1);
+ break;
+
+ case WOV_MODE_RAM:
+ case WOV_MODE_I2S:
+ case WOV_MODE_RAM_AND_I2S:
+ wov_apm_active(1);
+ apm_vad_enable(0);
+ apm_enable_vad_interrupt(0);
+ apm_set_adc_dmic_config_l(apm_conf.adc_dmic_rate);
+ apm_dmic_enable(1);
+ apm_adc_enable(1);
+ apm_vad_adc_wakeup_enable(0);
+ break;
+
+ default:
+ apm_set_vad_dmic_rate_l(APM_DMIC_RATE_1_0);
+ apm_set_adc_dmic_config_l(APM_DMIC_RATE_1_0);
+ apm_vad_enable(0);
+ apm_enable_vad_interrupt(0);
+ apm_dmic_enable(0);
+ apm_adc_enable(0);
+ apm_vad_adc_wakeup_enable(0);
+ wov_apm_active(0);
+ break;
+ }
+
+ apm_adc_gain_config(apm_conf.gain_coupling,
+ apm_conf.left_chan_gain,
+ apm_conf.right_chan_gain);
+
+ apm_adc_auto_gain_config(&apm_gain_conf);
+
+ apm_adc_set_freq(apm_adc_freq_val_2_code(wov_conf.sample_per_sec));
+
+ if (wov_mode != WOV_MODE_OFF)
+ apm_enable(1);
+}
+
+/**
+ * Clears VAD detected bit in IFR register.
+ *
+ * @param None
+ * @return None.
+ */
+void apm_clear_vad_detected_bit(void)
+{
+ apm_vad_enable(0);
+
+ APM_CLEAR_VAD_INTERRUPT;
+
+ apm_vad_enable(1);
+}
diff --git a/chip/npcx/apm_chip.h b/chip/npcx/apm_chip.h
new file mode 100644
index 0000000000..95e69aba28
--- /dev/null
+++ b/chip/npcx/apm_chip.h
@@ -0,0 +1,418 @@
+/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* NPCX-specific APM module for Chrome EC */
+
+#ifndef __CROS_EC_APM_CHIP_H
+#define __CROS_EC_APM_CHIP_H
+
+#include "common.h"
+
+/* MIX indirect registers. */
+#define APM_INDIRECT_MIX_2_REG 0x02
+
+/* ADC_AGC indirect registers. */
+#define APM_INDIRECT_ADC_AGC_0_REG 0x00
+#define APM_INDIRECT_ADC_AGC_1_REG 0x01
+#define APM_INDIRECT_ADC_AGC_2_REG 0x02
+#define APM_INDIRECT_ADC_AGC_3_REG 0x03
+#define APM_INDIRECT_ADC_AGC_4_REG 0x04
+
+/* APM_VAD_REG indirect registers. */
+#define APM_INDIRECT_VAD_0_REG 0x00
+#define APM_INDIRECT_VAD_1_REG 0x01
+
+/* APM macros. */
+#define APM_IS_IRQ_PENDING IS_BIT_SET(NPCX_APM_SR, NPCX_APM_SR_IRQ_PEND)
+#define APM_IS_VOICE_ACTIVITY_DETECTED \
+ IS_BIT_SET(NPCX_APM_IFR, NPCX_APM_IFR_VAD_DTC)
+#define APM_CLEAR_VAD_INTERRUPT SET_BIT(NPCX_APM_IFR, NPCX_APM_IFR_VAD_DTC)
+
+/* Indirect registers. */
+enum apm_indirect_reg_offset {
+ APM_MIX_REG = 0,
+ APM_ADC_AGC_REG,
+ APM_VAD_REG
+};
+
+/* ADC wind noise filter modes. */
+enum apm_adc_wind_noise_filter_mode {
+ APM_ADC_WIND_NOISE_FILTER_INACTIVE = 0,
+ APM_ADC_WIND_NOISE_FILTER_MODE_1_ACTIVE,
+ APM_ADC_WIND_NOISE_FILTER_MODE_2_ACTIVE,
+ APM_ADC_WIND_NOISE_FILTER_MODE_3_ACTIVE,
+};
+
+/* ADC frequency. */
+enum apm_adc_frequency {
+ APM_ADC_FREQ_8_000_KHZ = 0x00,
+ APM_ADC_FREQ_11_025_KHZ,
+ APM_ADC_FREQ_12_000_KHZ,
+ APM_ADC_FREQ_16_000_KHZ,
+ APM_ADC_FREQ_22_050_KHZ,
+ APM_ADC_FREQ_24_000_KHZ,
+ APM_ADC_FREQ_32_000_KHZ,
+ APM_ADC_FREQ_44_100_KHZ,
+ APM_ADC_FREQ_48_000_KHZ,
+ APM_ADC_FREQ_UNSUPPORTED = 0x0F
+};
+
+/* DMIC source. */
+enum apm_dmic_src {
+ APM_CURRENT_DMIC_CHANNEL = 0x01, /* Current channel, left or rigth. */
+ APM_AVERAGE_DMIC_CHANNEL = 0x02 /* Average between left & right. */
+};
+
+/* ADC digital microphone rate. */
+enum apm_dmic_rate {
+ APM_DMIC_RATE_3_0 = 0, /* 3.0 -3.25 MHz (default). */
+ APM_DMIC_RATE_2_4, /* 2.4 -2.6 MHz. */
+ APM_DMIC_RATE_1_0 /* 1.0 -1.08 MHz. */
+};
+
+/* Digitla mixer output. */
+enum apm_dig_mix {
+ APM_OUT_MIX_NORMAL_INPUT = 0, /* Default. */
+ APM_OUT_MIX_CROSS_INPUT,
+ APM_OUT_MIX_MIXED_INPUT,
+ APM_OUT_MIX_NO_INPUT
+};
+
+/* VAD Input Channel Selection */
+enum apm_vad_in_channel_src {
+ APM_IN_LEFT = 0,
+ APM_IN_RIGHT,
+ APM_IN_AVERAGE_LEFT_RIGHT,
+ APM_IN_RESERVED
+};
+
+/* ADC digital gain coupling. */
+enum apm_adc_gain_coupling {
+ APM_ADC_CHAN_GAINS_INDEPENDENT = 0,
+ APM_ADC_RIGHT_CHAN_GAIN_TRACKS_LEFT
+};
+
+/* ADC target output level. */
+enum apm_adc_target_out_level {
+ APM_ADC_MAX_TARGET_LEVEL = 0,
+ APM_ADC_MAX_TARGET_LEVEL_1_5,
+ APM_ADC_MAX_TARGET_LEVEL_3_0,
+ APM_ADC_MAX_TARGET_LEVEL_4_5,
+ APM_ADC_MAX_TARGET_LEVEL_6_0,
+ APM_ADC_MAX_TARGET_LEVEL_7_5,
+ APM_ADC_MAX_TARGET_LEVEL_9_0,
+ APM_ADC_MAX_TARGET_LEVEL_10_5,
+ APM_ADC_MAX_TARGET_LEVEL_12_0,
+ APM_ADC_MAX_TARGET_LEVEL_13_5,
+ APM_ADC_MAX_TARGET_LEVEL_15_0,
+ APM_ADC_MAX_TARGET_LEVEL_16_5,
+ APM_ADC_MAX_TARGET_LEVEL_18_0,
+ APM_ADC_MAX_TARGET_LEVEL_19_5, /* Default. */
+ APM_ADC_MAX_TARGET_LEVEL_21_0,
+ APM_ADC_MAX_TARGET_LEVEL_22_5
+};
+
+/* Noise gate threshold values. */
+enum apm_noise_gate_threshold {
+ APM_MIN_NOISE_GET_THRESHOLD = 0,
+ APM_MIN_NOISE_GET_THRESHOLD_6,
+ APM_MIN_NOISE_GET_THRESHOLD_12,
+ APM_MIN_NOISE_GET_THRESHOLD_18,
+ APM_MIN_NOISE_GET_THRESHOLD_24,
+ APM_MIN_NOISE_GET_THRESHOLD_30,
+ APM_MIN_NOISE_GET_THRESHOLD_36,
+ APM_MIN_NOISE_GET_THRESHOLD_42
+};
+
+/* Hold time in msec before starting AGC adjustment to the TARGET value. */
+enum apm_agc_adj_hold_time {
+ APM_HOLD_TIME_0 = 0,
+ APM_HOLD_TIME_2,
+ APM_HOLD_TIME_4,
+ APM_HOLD_TIME_8,
+ APM_HOLD_TIME_16,
+ APM_HOLD_TIME_32,
+ APM_HOLD_TIME_64,
+ APM_HOLD_TIME_128, /* Default. */
+ APM_HOLD_TIME_256,
+ APM_HOLD_TIME_512,
+ APM_HOLD_TIME_1024,
+ APM_HOLD_TIME_2048,
+ APM_HOLD_TIME_4096,
+ APM_HOLD_TIME_8192,
+ APM_HOLD_TIME_16384,
+ APM_HOLD_TIME_32768
+};
+
+/* Attack time in msec - gain ramp down. */
+enum apm_gain_ramp_time {
+ APM_GAIN_RAMP_TIME_32 = 0,
+ APM_GAIN_RAMP_TIME_64,
+ APM_GAIN_RAMP_TIME_96,
+ APM_GAIN_RAMP_TIME_128,
+ APM_GAIN_RAMP_TIME_160, /* Default. */
+ APM_GAIN_RAMP_TIME_192,
+ APM_GAIN_RAMP_TIME_224,
+ APM_GAIN_RAMP_TIME_256,
+ APM_GAIN_RAMP_TIME_288,
+ APM_GAIN_RAMP_TIME_320,
+ APM_GAIN_RAMP_TIME_352,
+ APM_GAIN_RAMP_TIME_384,
+ APM_GAIN_RAMP_TIME_416,
+ APM_GAIN_RAMP_TIME_448,
+ APM_GAIN_RAMP_TIME_480,
+ APM_GAIN_RAMP_TIME_512
+};
+
+/* Minimum and Maximum gain values. */
+enum apm_gain_values {
+ APM_GAIN_VALUE_0_0 = 0,
+ APM_GAIN_VALUE_1_5,
+ APM_GAIN_VALUE_3_0,
+ APM_GAIN_VALUE_4_5,
+ APM_GAIN_VALUE_6_0,
+ APM_GAIN_VALUE_7_5,
+ APM_GAIN_VALUE_9_0,
+ APM_GAIN_VALUE_10_5,
+ APM_GAIN_VALUE_12_0,
+ APM_GAIN_VALUE_13_5,
+ APM_GAIN_VALUE_15_0,
+ APM_GAIN_VALUE_16_5,
+ APM_GAIN_VALUE_18_0,
+ APM_GAIN_VALUE_19_5,
+ APM_GAIN_VALUE_21_0,
+ APM_GAIN_VALUE_22_5,
+ APM_GAIN_VALUE_23_0_1ST,
+ APM_GAIN_VALUE_23_0_2ND,
+ APM_GAIN_VALUE_23_0_3RD,
+ APM_GAIN_VALUE_24_5,
+ APM_GAIN_VALUE_26_0,
+ APM_GAIN_VALUE_27_5,
+ APM_GAIN_VALUE_29_0,
+ APM_GAIN_VALUE_30_5,
+ APM_GAIN_VALUE_32_0,
+ APM_GAIN_VALUE_33_5,
+ APM_GAIN_VALUE_35_0,
+ APM_GAIN_VALUE_36_5,
+ APM_GAIN_VALUE_38_0,
+ APM_GAIN_VALUE_39_5,
+ APM_GAIN_VALUE_41_0,
+ APM_GAIN_VALUE_42_5
+};
+
+/* ADC Audio Data Word Length */
+enum apm_adc_data_length {
+ APM_ADC_DATA_LEN_16_BITS = 0x00,
+ APM_ADC_DATA_LEN_18_BITS,
+ APM_ADC_DATA_LEN_20_BITS,
+ APM_ADC_DATA_LEN_24_BITS
+};
+
+struct apm_config {
+ enum apm_dmic_rate vad_dmic_rate;
+ enum apm_dmic_rate adc_dmic_rate;
+ enum apm_adc_gain_coupling gain_coupling;
+ uint8_t left_chan_gain;
+ uint8_t right_chan_gain;
+};
+struct apm_auto_gain_config {
+ int stereo_enable;
+ enum apm_adc_target_out_level agc_target;
+ int nois_gate_en;
+ enum apm_noise_gate_threshold nois_gate_thold;
+ enum apm_agc_adj_hold_time hold_time;
+ enum apm_gain_ramp_time attack_time;
+ enum apm_gain_ramp_time decay_time;
+ enum apm_gain_values gain_max;
+ enum apm_gain_values gain_min;
+};
+
+/**
+ * Sets the ADC DMIC rate.
+ *
+ * @param rate - ADC digital microphone rate
+ * @return None
+ */
+void apm_set_adc_dmic_config(enum apm_dmic_rate rate);
+
+/**
+ * Sets VAD DMIC rate.
+ *
+ * @param rate - VAD DMIC rate
+ *
+ * @return None
+ */
+void apm_set_vad_dmic_rate(enum apm_dmic_rate rate);
+
+/*****************************************************************************/
+/* IC specific low-level driver */
+enum wov_modes;
+/**
+ * Initiate APM module local parameters..
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void apm_init(void);
+
+/**
+ * Enables/Disables APM module.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return None
+ */
+void apm_enable(int enable);
+
+/**
+ * Enables/Disables voice activity detected interrupt.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return APM interrupt mode.
+ */
+void apm_enable_vad_interrupt(int enable);
+
+/**
+ * Enables/Disables ADC.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return None
+ */
+void apm_adc_enable(int enable);
+
+/**
+ * sets the ADC frequency.
+ *
+ * @param adc_freq - ADC frequency.
+ * @return None
+ */
+void apm_adc_set_freq(enum apm_adc_frequency adc_freq);
+
+/**
+ * Configures the ADC.
+ *
+ * @param hpf_enable - High pass filter enabled flag, true means enable
+ * @param filter_mode - ADC wind noise filter mode.
+ * @param adc_freq - ADC frequency.
+ * @return None
+ */
+void apm_adc_config(int hpf_enable,
+ enum apm_adc_wind_noise_filter_mode filter_mode,
+ enum apm_adc_frequency adc_freq);
+
+/**
+ * Enables/Disables Digital Microphone.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return None
+ */
+void apm_dmic_enable(int enable);
+
+/**
+ * Configures Digital Microphone.
+ *
+ * @param mix_left - Mixer left channel output selection on ADC path.
+ * @param mix_right - Mixer right channel output selection on ADC path.
+ * @return None
+ */
+void apm_digital_mixer_config(enum apm_dig_mix mix_left,
+ enum apm_dig_mix mix_right);
+
+/**
+ * Enables/Disables the VAD functionality.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return None
+ */
+void apm_vad_enable(int enable);
+
+/**
+ * Enables/Disables VAD ADC wakeup
+ *
+ * @param enable - true enable, false disable.
+ *
+ * @return None
+ */
+void apm_vad_adc_wakeup_enable(int enable);
+
+/**
+ * Sets VAD Input chanel.
+ *
+ * @param chan_src - Processed digital microphone channel
+ * selection.
+ * @return None
+ */
+void apm_set_vad_input_channel(enum apm_vad_in_channel_src chan_src);
+
+/**
+ * Sets VAD sensitivity.
+ *
+ * @param sensitivity_db - VAD sensitivity in db.
+ * @return None
+ */
+void apm_set_vad_sensitivity(uint8_t sensitivity_db);
+
+/**
+ * Gets VAD sensitivity.
+ *
+ * @param None.
+ * @return VAD sensitivity in db
+ */
+uint8_t apm_get_vad_sensitivity(void);
+
+/**
+ * Restarts VAD functionality.
+ *
+ * @param None
+ * @return None
+ */
+void apm_vad_restart(void);
+
+/**
+ * Restarts VAD functionality.
+ *
+ * @param gain_coupling - ADC digital gain coupling (independent or
+ * rigth tracks left).
+ * @param left_chan_gain - Left channel ADC digital gain programming value.
+ * @param right_chan_gain - Right channel ADC digital gain programming value.
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list apm_adc_gain_config(enum apm_adc_gain_coupling gain_coupling,
+ uint8_t left_chan_gain, uint8_t right_chan_gain);
+
+/**
+ * Enables/Disables the automatic gain.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return None
+ */
+void apm_auto_gain_cntrl_enable(int enable);
+
+/**
+ * Enables/Disables the automatic gain.
+ *
+ * @param gain_cfg - struct of apm auto gain config
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list apm_adc_auto_gain_config(
+ struct apm_auto_gain_config *gain_cfg);
+
+/**
+ * Sets APM mode (enables & disables APN sub modules accordingly
+ * to the APM mode).
+ *
+ * @param apm_mode - APM mode, DEFAULT, DETECTION, RECORD or INDEPENDENT modes.
+ * @return None
+ */
+void apm_set_mode(enum wov_modes wov_mode);
+
+/**
+ * Clears VAD detected bit in IFR register.
+ *
+ * @param None
+ * @return None.
+ */
+void apm_clear_vad_detected_bit(void);
+
+#endif /* __CROS_EC_APM_CHIP_H */
diff --git a/chip/npcx/build.mk b/chip/npcx/build.mk
index 2cd4d087c2..13c4a60015 100644
--- a/chip/npcx/build.mk
+++ b/chip/npcx/build.mk
@@ -34,6 +34,7 @@ chip-$(CONFIG_PWM)+=pwm.o
chip-$(CONFIG_SPI)+=spi.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
+chip-$(CONFIG_WAKE_ON_VOICE)+=apm.o
chip-$(CONFIG_WAKE_ON_VOICE)+=wov.o
# spi monitor program fw for openocd and UUT(UART Update Tool)
diff --git a/chip/npcx/gpio_chip-npcx7.h b/chip/npcx/gpio_chip-npcx7.h
index 05bc3eb170..23f18b8700 100644
--- a/chip/npcx/gpio_chip-npcx7.h
+++ b/chip/npcx/gpio_chip-npcx7.h
@@ -313,6 +313,20 @@
#define NPCX_ALT_PSL_IN4 /* NO PSL in NPCX7mnG series */
#endif
+/* WOV module */
+#ifdef NPCX_WOV_SUPPORT
+#define NPCX_ALT_I2S_SYNC ALT(A, 5, NPCX_ALT(E, I2S_SL)) /* I2S_SYNC */
+#define NPCX_ALT_I2S_SCLK ALT(A, 7, NPCX_ALT(E, I2S_SL)) /* I2S_SCLK */
+#define NPCX_ALT_I2S_SDAT ALT(B, 0, NPCX_ALT(E, I2S_SL)) /* I2S_DATA */
+#define NPCX_ALT_DMIC_CLK ALT(9, 4, NPCX_ALT(E, WOV_SL)) /* DMIC_CLK */
+#define NPCX_ALT_DMIC_IN ALT(9, 7, NPCX_ALT(E, WOV_SL)) /* DMIC_IN */
+#else
+#define NPCX_ALT_I2S_SYNC
+#define NPCX_ALT_I2S_SCLK
+#define NPCX_ALT_I2S_SDAT
+#define NPCX_ALT_DMIC_CLK
+#define NPCX_ALT_DMIC_IN
+#endif
#define NPCX_ALT_TABLE { \
NPCX_ALT_I2C0SDA0 \
NPCX_ALT_I2C0SCL0 \
@@ -393,7 +407,13 @@
NPCX_ALT_PSL_IN1 \
NPCX_ALT_PSL_IN2 \
NPCX_ALT_PSL_IN3 \
- NPCX_ALT_PSL_IN4 }
+ NPCX_ALT_PSL_IN4 \
+ NPCX_ALT_I2S_SYNC \
+ NPCX_ALT_I2S_SCLK \
+ NPCX_ALT_I2S_SDAT \
+ NPCX_ALT_DMIC_CLK \
+ NPCX_ALT_DMIC_IN \
+}
/*****************************************************************************/
/* Macro functions for Low-Voltage mapping table */
diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h
index 9bca32e82f..e04a8a8797 100644
--- a/chip/npcx/registers.h
+++ b/chip/npcx/registers.h
@@ -64,6 +64,7 @@
#define DEBUG_CLK 0
#define DEBUG_LPC 0
#define DEBUG_ESPI 0
+#define DEBUG_WOV 0
/* Modules Map */
#define NPCX_ESPI_BASE_ADDR 0x4000A000
@@ -75,10 +76,13 @@
#define NPCX_GDMA_BASE_ADDR 0x40011000
#define NPCX_FIU_BASE_ADDR 0x40020000
#define NPCX_KBSCAN_REGS_BASE 0x400A3000
+#define NPCX_WOV_BASE_ADDR 0x400A4000
+#define NPCX_APM_BASE_ADDR 0x400A4800
#define NPCX_GLUE_REGS_BASE 0x400A5000
#define NPCX_BBRAM_BASE_ADDR 0x400AF000
#define NPCX_HFCG_BASE_ADDR 0x400B5000
#define NPCX_LFCG_BASE_ADDR 0x400B5100
+#define NPCX_FMUL2_BASE_ADDR 0x400B5200
#define NPCX_MTC_BASE_ADDR 0x400B7000
#define NPCX_MSWC_BASE_ADDR 0x400C1000
#define NPCX_SCFG_BASE_ADDR 0x400C3000
@@ -202,7 +206,7 @@
#define NPCX_IRQ_SMB5 NPCX_IRQ_19
#define NPCX_IRQ_SMB6 NPCX_IRQ_20
#define NPCX_IRQ_PS2 NPCX_IRQ_21
-#define NPCX_IRQ22_NOUSED NPCX_IRQ_22
+#define NPCX_IRQ_WOV NPCX_IRQ_22
#define NPCX_IRQ_MFT_2 NPCX_IRQ_23
#define NPCX_IRQ_SHM NPCX_IRQ_24
#define NPCX_IRQ_KBC_IBF NPCX_IRQ_25
@@ -689,6 +693,12 @@ enum {
#define NPCX_DEVALTF_SHI_NEW 7
#endif
+/* pin-mux for WoV */
+#ifdef NPCX_WOV_SUPPORT
+#define NPCX_DEVALTE_WOV_SL 0
+#define NPCX_DEVALTE_I2S_SL 1
+#endif
+
/* Others bit definitions */
#define NPCX_LFCGCALCNT_LPREG_CTL_EN 1
@@ -890,6 +900,7 @@ enum {
#define NPCX_PWDWN_CTL7_SMB5_PD 0
#define NPCX_PWDWN_CTL7_SMB6_PD 1
#define NPCX_PWDWN_CTL7_SMB7_PD 2
+#define NPCX_PWDWN_CTL7_WOV_PD 7
#endif
/*
@@ -912,6 +923,9 @@ enum {
#if defined(CHIP_FAMILY_NPCX7)
CGC_OFFSET_I2C2 = 6,
#endif
+#ifdef NPCX_WOV_SUPPORT
+ CGC_OFFSET_WOV = 6,
+#endif
};
enum NPCX_PMC_PWDWN_CTL_T {
@@ -948,6 +962,9 @@ enum NPCX_PMC_PWDWN_CTL_T {
(1 << NPCX_PWDWN_CTL7_SMB6_PD) | \
(1 << NPCX_PWDWN_CTL7_SMB7_PD))
#endif
+#ifdef NPCX_WOV_SUPPORT
+#define CGC_WOV_MASK (1 << NPCX_PWDWN_CTL7_WOV_PD)
+#endif
#define CGC_ADC_MASK (1 << NPCX_PWDWN_CTL4_ADC_PD)
#define CGC_PECI_MASK (1 << NPCX_PWDWN_CTL4_PECI_PD)
#define CGC_SPI_MASK (1 << NPCX_PWDWN_CTL4_SPIP_PD)
@@ -1824,6 +1841,179 @@ enum {
| MASK(A_SIZE))
/******************************************************************************/
+/* APM (Audio Processing Module) Registers */
+#define NPCX_APM_SR REG8(NPCX_APM_BASE_ADDR + 0x000)
+#define NPCX_APM_SR2 REG8(NPCX_APM_BASE_ADDR + 0x004)
+#define NPCX_APM_ICR REG8(NPCX_APM_BASE_ADDR + 0x008)
+#define NPCX_APM_IMR REG8(NPCX_APM_BASE_ADDR + 0x00C)
+#define NPCX_APM_IFR REG8(NPCX_APM_BASE_ADDR + 0x010)
+#define NPCX_APM_CR_APM REG8(NPCX_APM_BASE_ADDR + 0x014)
+#define NPCX_APM_CR_CK REG8(NPCX_APM_BASE_ADDR + 0x018)
+#define NPCX_APM_AICR_ADC REG8(NPCX_APM_BASE_ADDR + 0x01C)
+#define NPCX_APM_FCR_ADC REG8(NPCX_APM_BASE_ADDR + 0x020)
+#define NPCX_APM_CR_DMIC REG8(NPCX_APM_BASE_ADDR + 0x02C)
+#define NPCX_APM_CR_ADC REG8(NPCX_APM_BASE_ADDR + 0x030)
+#define NPCX_APM_CR_MIX REG8(NPCX_APM_BASE_ADDR + 0x034)
+#define NPCX_APM_DR_MIX REG8(NPCX_APM_BASE_ADDR + 0x038)
+#define NPCX_APM_GCR_ADCL REG8(NPCX_APM_BASE_ADDR + 0x03C)
+#define NPCX_APM_GCR_ADCR REG8(NPCX_APM_BASE_ADDR + 0x040)
+#define NPCX_APM_GCR_MIXADCL REG8(NPCX_APM_BASE_ADDR + 0x044)
+#define NPCX_APM_GCR_MIXADCR REG8(NPCX_APM_BASE_ADDR + 0x048)
+#define NPCX_APM_CR_ADC_AGC REG8(NPCX_APM_BASE_ADDR + 0x04C)
+#define NPCX_APM_DR_ADC_AGC REG8(NPCX_APM_BASE_ADDR + 0x050)
+#define NPCX_APM_SR_ADC_AGCDGL REG8(NPCX_APM_BASE_ADDR + 0x054)
+#define NPCX_APM_SR_ADC_AGCDGR REG8(NPCX_APM_BASE_ADDR + 0x058)
+#define NPCX_APM_CR_VAD REG8(NPCX_APM_BASE_ADDR + 0x05C)
+#define NPCX_APM_DR_VAD REG8(NPCX_APM_BASE_ADDR + 0x060)
+#define NPCX_APM_CR_VAD_CMD REG8(NPCX_APM_BASE_ADDR + 0x064)
+#define NPCX_APM_CR_TR REG8(NPCX_APM_BASE_ADDR + 0x068)
+#define NPCX_APM_DR_TR REG8(NPCX_APM_BASE_ADDR + 0x06C)
+#define NPCX_APM_SR_TR1 REG8(NPCX_APM_BASE_ADDR + 0x070)
+#define NPCX_APM_SR_TR_SRCADC REG8(NPCX_APM_BASE_ADDR + 0x074)
+
+/******************************************************************************/
+/* APM register fields */
+#define NPCX_APM_SR_IRQ_PEND 6
+#define NPCX_APM_SR2_SMUTEIP 6
+#define NPCX_APM_ICR_INTR_MODE FIELD(6, 2)
+#define NPCX_APM_IMR_VAD_DTC_MASK 6
+#define NPCX_APM_IFR_VAD_DTC 6
+#define NPCX_APM_CR_APM_PD 0
+#define NPCX_APM_CR_APM_AGC_DIS FIELD(1, 2)
+#define NPCX_APM_CR_CK_MCLK_FREQ FIELD(0, 2)
+#define NPCX_APM_AICR_ADC_ADC_AUDIOIF FIELD(0, 2)
+#define NPCX_APM_AICR_ADC_PD_AICR_ADC 4
+#define NPCX_APM_AICR_ADC_ADC_ADWL FIELD(6, 2)
+#define NPCX_APM_FCR_ADC_ADC_FREQ FIELD(0, 4)
+#define NPCX_APM_FCR_ADC_ADC_WNF FIELD(4, 2)
+#define NPCX_APM_FCR_ADC_ADC_HPF 6
+#define NPCX_APM_CR_DMIC_ADC_DMIC_SEL_RIGHT FIELD(0, 2)
+#define NPCX_APM_CR_DMIC_ADC_DMIC_SEL_LEFT FIELD(2, 2)
+#define NPCX_APM_CR_DMIC_ADC_DMIC_RATE FIELD(4, 3)
+#define NPCX_APM_CR_DMIC_PD_DMIC 7
+#define NPCX_APM_CR_ADC_ADC_SOFT_MUTE 7
+#define NPCX_APM_CR_MIX_MIX_ADD FIELD(0, 6)
+#define NPCX_APM_CR_MIX_MIX_LOAD 6
+#define NPCX_APM_DR_MIX_MIX_DATA FIELD(0, 8)
+#define NPCX_APM_MIX_2_AIADCR_SEL FIELD(4, 2)
+#define NPCX_APM_MIX_2_AIADCL_SEL FIELD(6, 2)
+#define NPCX_APM_GCR_ADCL_GIDL FIELD(0, 6)
+#define NPCX_APM_GCR_ADCL_LRGID 7
+#define NPCX_APM_GCR_ADCR_GIDR FIELD(0, 6)
+#define NPCX_APM_GCR_MIXADCL_GIMIXL FIELD(0, 6)
+#define NPCX_APM_GCR_MIXADCR_GIMIXR FIELD(0, 6)
+#define NPCX_APM_CR_ADC_AGC_ADC_AGC_ADD FIELD(0, 6)
+#define NPCX_APM_CR_ADC_AGC_ADC_AGC_LOAD 6
+#define NPCX_APM_CR_ADC_AGC_ADC_AGC_EN 7
+#define NPCX_APM_DR_ADC_AGC_ADC_AGC_DATA FIELD(0, 8)
+#define NPCX_ADC_AGC_0_AGC_TARGET FIELD(2, 4)
+#define NPCX_ADC_AGC_0_AGC_STEREO 6
+#define NPCX_ADC_AGC_1_HOLD FIELD(0, 4)
+#define NPCX_ADC_AGC_1_NG_THR FIELD(4, 3)
+#define NPCX_ADC_AGC_1_NG_EN 7
+#define NPCX_ADC_AGC_2_DCY FIELD(0, 4)
+#define NPCX_ADC_AGC_2_ATK FIELD(4, 4)
+#define NPCX_ADC_AGC_3_AGC_MAX FIELD(0, 5)
+#define NPCX_ADC_AGC_4_AGC_MIN FIELD(0, 5)
+#define NPCX_APM_CR_VAD_VAD_ADD FIELD(0, 6)
+#define NPCX_APM_CR_VAD_VAD_LOAD 6
+#define NPCX_APM_CR_VAD_VAD_EN 7
+#define NPCX_APM_DR_VAD_VAD_DATA FIELD(0, 8)
+#define NPCX_APM_CR_VAD_CMD_VAD_RESTART 0
+#define NPCX_APM_CR_TR_FAST_ON 7
+#define NPCX_VAD_0_VAD_INSEL FIELD(0, 2)
+#define NPCX_VAD_0_VAD_DMIC_FREQ FIELD(2, 3)
+#define NPCX_VAD_0_VAD_ADC_WAKEUP 5
+#define NPCX_VAD_0_ZCD_EN 6
+#define NPCX_VAD_1_VAD_POWER_SENS FIELD(0, 5)
+#define NPCX_APM_CONTROL_ADD FIELD(0, 6)
+#define NPCX_APM_CONTROL_LOAD 6
+
+/******************************************************************************/
+/* FMUL2 (Frequency Multiplier Module 2) Registers */
+#define NPCX_FMUL2_FM2CTRL REG8(NPCX_FMUL2_BASE_ADDR + 0x000)
+#define NPCX_FMUL2_FM2ML REG8(NPCX_FMUL2_BASE_ADDR + 0x002)
+#define NPCX_FMUL2_FM2MH REG8(NPCX_FMUL2_BASE_ADDR + 0x004)
+#define NPCX_FMUL2_FM2N REG8(NPCX_FMUL2_BASE_ADDR + 0x006)
+#define NPCX_FMUL2_FM2P REG8(NPCX_FMUL2_BASE_ADDR + 0x008)
+#define NPCX_FMUL2_FM2_VER REG8(NPCX_FMUL2_BASE_ADDR + 0x00A)
+
+/******************************************************************************/
+/* FMUL2 register fields */
+#define NPCX_FMUL2_FM2CTRL_LOAD2 0
+#define NPCX_FMUL2_FM2CTRL_LOCK2 2
+#define NPCX_FMUL2_FM2CTRL_FMUL2_DIS 5
+#define NPCX_FMUL2_FM2CTRL_TUNE_DIS 6
+#define NPCX_FMUL2_FM2CTRL_CLK2_CHNG 7
+#define NPCX_FMUL2_FM2N_FM2N FIELD(0, 6)
+#define NPCX_FMUL2_FM2P_WFPRED FIELD(4, 4)
+
+/******************************************************************************/
+/* WOV (Wake-on-Voice) Registers */
+#define NPCX_WOV_CLOCK_CNTL REG32(NPCX_WOV_BASE_ADDR + 0x000)
+#define NPCX_WOV_PLL_CNTL1 REG32(NPCX_WOV_BASE_ADDR + 0x004)
+#define NPCX_WOV_PLL_CNTL2 REG32(NPCX_WOV_BASE_ADDR + 0x008)
+#define NPCX_WOV_FIFO_CNT REG32(NPCX_WOV_BASE_ADDR + 0x00C)
+#define NPCX_WOV_FIFO_OUT REG32(NPCX_WOV_BASE_ADDR + 0x010)
+#define NPCX_WOV_STATUS REG32(NPCX_WOV_BASE_ADDR + 0x014)
+#define NPCX_WOV_WOV_INTEN REG32(NPCX_WOV_BASE_ADDR + 0x018)
+#define NPCX_WOV_APM_CTRL REG32(NPCX_WOV_BASE_ADDR + 0x01C)
+#define NPCX_WOV_I2S_CNTL(n) REG32(NPCX_WOV_BASE_ADDR + 0x020 + (4*n))
+#define NPCX_WOV_VERSION REG32(NPCX_WOV_BASE_ADDR + 0x030)
+
+/******************************************************************************/
+/* WOV register fields */
+#define NPCX_WOV_CLOCK_CNT_CLK_SEL 0
+#define NPCX_WOV_CLOCK_CNT_DMIC_EN 3
+#define NPCX_WOV_CLOCK_CNT_PLL_EDIV_SEL 7
+#define NPCX_WOV_CLOCK_CNT_PLL_EDIV FIELD(8, 7)
+#define NPCX_WOV_CLOCK_CNT_PLL_EDIV_DC FIELD(16, 7)
+#define NPCX_WOV_CLOCK_CNT_DMIC_CKDIV_EN 24
+#define NPCX_WOV_CLOCK_CNT_DMIC_CKDIV_SEL 25
+#define NPCX_WOV_FIFO_CNT_FIFO_ITHRSH FIELD(0, 6)
+#define NPCX_WOV_FIFO_CNT_FIFO_WTHRSH FIELD(6, 6)
+#define NPCX_WOV_FIFO_CNT_I2S_FFRST 13
+#define NPCX_WOV_FIFO_CNT_CORE_FFRST 14
+#define NPCX_WOV_FIFO_CNT_CFIFO_ISEL FIELD(16, 3)
+#define NPCX_WOV_STATUS_CFIFO_CNT FIELD(0, 8)
+#define NPCX_WOV_STATUS_CFIFO_NE 8
+#define NPCX_WOV_STATUS_CFIFO_OIT 9
+#define NPCX_WOV_STATUS_CFIFO_OWT 10
+#define NPCX_WOV_STATUS_CFIFO_OVRN 11
+#define NPCX_WOV_STATUS_I2S_FIFO_OVRN 12
+#define NPCX_WOV_STATUS_I2S_FIFO_UNDRN 13
+#define NPCX_WOV_STATUS_BITS FIELD(9, 6)
+#define NPCX_WOV_INTEN_VAD_INTEN 0
+#define NPCX_WOV_INTEN_VAD_WKEN 1
+#define NPCX_WOV_INTEN_CFIFO_NE_IE 8
+#define NPCX_WOV_INTEN_CFIFO_OIT_IE 9
+#define NPCX_WOV_INTEN_CFIFO_OWT_WE 10
+#define NPCX_WOV_INTEN_CFIFO_OVRN_IE 11
+#define NPCX_WOV_INTEN_I2S_FIFO_OVRN_IE 12
+#define NPCX_WOV_INTEN_I2S_FIFO_UNDRN_IE 13
+#define NPCX_WOV_APM_CTRL_APM_RST 0
+#define NPCX_WOV_PLL_CNTL1_PLL_PWDEN 0
+#define NPCX_WOV_PLL_CNTL1_PLL_OTDV1 FIELD(4, 4)
+#define NPCX_WOV_PLL_CNTL1_PLL_OTDV2 FIELD(8, 4)
+#define NPCX_WOV_PLL_CNTL1_PLL_LOCKI 15
+#define NPCX_WOV_PLL_CNTL2_PLL_FBDV FIELD(0, 12)
+#define NPCX_WOV_PLL_CNTL2_PLL_INDV FIELD(12, 4)
+#define NPCX_WOV_I2S_CNTL_I2S_BCNT FIELD(0, 5)
+#define NPCX_WOV_I2S_CNTL_I2S_TRIG 5
+#define NPCX_WOV_I2S_CNTL_I2S_LBHIZ 6
+#define NPCX_WOV_I2S_CNTL_I2S_ST_DEL FIELD(7, 9)
+#define NPCX_WOV_I2S_CNTL_I2S_CHAN FIELD(0, 16)
+#define NPCX_WOV_I2S_CNTL0_I2S_HIZD 16
+#define NPCX_WOV_I2S_CNTL0_I2S_HIZ 17
+#define NPCX_WOV_I2S_CNTL0_I2S_SCLK_INV 18
+#define NPCX_WOV_I2S_CNTL0_I2S_OPS 19
+#define NPCX_WOV_I2S_CNTL0_I2S_OPE 20
+#define NPCX_WOV_I2S_CNTL0_I2S_IPS 21
+#define NPCX_WOV_I2S_CNTL0_I2S_IPE 22
+#define NPCX_WOV_I2S_CNTL0_I2S_TST 23
+#define NPCX_WOV_I2S_CNTL1_I2S_CHN1_DIS 24
+
+/******************************************************************************/
/* UART registers and functions */
#if NPCX_UART_MODULE2
diff --git a/chip/npcx/system.c b/chip/npcx/system.c
index ffbe490c52..649ec25a89 100644
--- a/chip/npcx/system.c
+++ b/chip/npcx/system.c
@@ -695,8 +695,12 @@ void system_pre_init(void)
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6) = pwdwn6;
#if defined(CHIP_FAMILY_NPCX7)
+#if defined(CHIP_VARIANT_NPCX7M7W) || defined(CHIP_VARIANT_NPCX7M6XB)
+ NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0xE7;
+#else
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0x07;
#endif
+#endif
/* Following modules can be powered down automatically in npcx7 */
#if defined(CHIP_FAMILY_NPCX5)
@@ -794,6 +798,8 @@ const char *system_get_chip_name(void)
#elif defined(CHIP_FAMILY_NPCX7)
case 0x21:
return "NPCX796F";
+ case 0x24:
+ return "NPCX797W";
#endif
default:
*p = system_to_hex((chip_id & 0xF0) >> 4);
diff --git a/chip/npcx/wov.c b/chip/npcx/wov.c
index 6ea921e076..a8631ac893 100644
--- a/chip/npcx/wov.c
+++ b/chip/npcx/wov.c
@@ -1,10 +1,1854 @@
-/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "common.h"
+/* NPCX-specific WOV module for Chrome EC */
+
+#include "apm_chip.h"
+#include "clock.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "wov_chip.h"
#ifndef NPCX_WOV_SUPPORT
#error "Do not enable CONFIG_WAKE_ON_VOICE if npcx ec doesn't support WOV !"
#endif
+
+/* Console output macros */
+#if !(DEBUG_WOV)
+#define CPUTS(...)
+#define CPRINTS(...)
+#else
+#define CPUTS(outstr) cputs(CC_WOV, outstr)
+#define CPRINTS(format, args...) cprints(CC_WOV, format, ## args)
+#endif
+
+/* WOV FIFO status. */
+#define WOV_STATUS_OFFSET NPCX_WOV_STATUS_CFIFO_OIT
+#define WOV_IS_CFIFO_INT_THRESHOLD(sts) \
+ IS_BIT_SET(sts, (NPCX_WOV_STATUS_CFIFO_OIT - WOV_STATUS_OFFSET))
+#define WOV_IS_CFIFO_WAKE_THRESHOLD(sts) \
+ IS_BIT_SET(sts, (NPCX_WOV_STATUS_CFIFO_OWT - WOV_STATUS_OFFSET))
+#define WOV_IS_CFIFO_OVERRUN(sts) \
+ IS_BIT_SET(sts, (NPCX_WOV_STATUS_CFIFO_OVRN - WOV_STATUS_OFFSET))
+#define WOV_IS_I2S_FIFO_OVERRUN(sts) \
+ IS_BIT_SET(sts, (NPCX_WOV_STATUS_I2S_FIFO_OVRN - WOV_STATUS_OFFSET))
+#define WOV_IS_I2S_FIFO_UNDERRUN(sts) \
+ IS_BIT_SET(sts, (NPCX_WOV_STATUS_I2S_FIFO_UNDRN - WOV_STATUS_OFFSET))
+
+/* Core FIFO threshold. */
+#define WOV_SET_FIFO_WAKE_THRESHOLD(n) \
+ SET_FIELD(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_FIFO_WTHRSH, n)
+
+#define WOV_SET_FIFO_INT_THRESHOLD(n) \
+ SET_FIELD(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_FIFO_ITHRSH, n)
+#define WOV_GET_FIFO_INT_THRESHOLD \
+ GET_FIELD(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_FIFO_ITHRSH)
+
+#define WOV_PLL_IS_NOT_LOCK \
+ (!IS_BIT_SET(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_LOCKI))
+
+/* mask definitions that clear reserved fields for WOV registers.*/
+#define WOV_CLK_CTRL_REG_RESERVED_MASK 0x037F7FFF
+
+#define WOV_GET_FIFO_WAKE_THRESHOLD \
+ GET_FIELD(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_FIFO_WTHRSH)
+
+/* Wait time 4ms for FMUL2 enabled and for configuration tuning sequence. */
+#define WOV_FMUL2_CLK_TUNING_DELAY_TIME (4 * 1000)
+
+/* The size of RAM buffer to store the voice data */
+#define VOICE_BUF_SIZE 16000
+
+/* PLL setting options. */
+struct wov_pll_set_options_val {
+ uint8_t pll_indv; /* Input Divider */
+ uint16_t pll_fbdv; /* Feedback Divider */
+ uint8_t pll_otdv1; /* Output devide 1. */
+ uint8_t pll_otdv2; /* Output devide 2. */
+ uint32_t pll_ext_div; /* Index for the table pll_ext_div */
+};
+
+/* PLL External Divider Load Values. */
+struct wov_pll_ext_div_val {
+ uint8_t pll_ediv; /* Required PLL external divider */
+ uint8_t pll_ediv_dc; /* Required PLL external divider DC */
+};
+
+
+static const struct wov_pll_ext_div_val pll_ext_div[] = {
+ {0x2F, 0x78}, /* 12 */
+ {0x57, 0x7C}, /* 13 */
+ {0x2B, 0x7C}, /* 14 */
+ {0x55, 0x7E}, /* 15 */
+ {0x2A, 0x7E}, /* 16 */
+ {0x15, 0x7F}, /* 17 */
+ {0x4A, 0x7F}, /* 18 */
+ {0x65, 0x3F}, /* 19 */
+ {0x32, 0x3F}, /* 20 */
+ {0x19, 0x5F}, /* 21 */
+ {0x4C, 0x5F}, /* 22 */
+ {0x66, 0x2F}, /* 23 */
+ {0x73, 0x2F}, /* 24 */
+ {0x39, 0x57}, /* 25 */
+ {0x5C, 0x57}, /* 26 */
+ {0x6E, 0x2B}, /* 27 */
+ {0x77, 0x2B}, /* 28 */
+ {0x3B, 0x55}, /* 29 */
+ {0x5D, 0x55}, /* 30 */
+ {0x2E, 0x2A}, /* 31 */
+ {0x17, 0x2A}, /* 32 */
+ {0x4B, 0x15}, /* 33 */
+ {0x25, 0x15}, /* 34 */
+ {0x52, 0x4A}, /* 35 */
+ {0x69, 0x4A}, /* 36 */
+ {0x34, 0x65}, /* 37 */
+ {0x1A, 0x65}, /* 38 */
+ {0x0D, 0x32}, /* 39 */
+ {0x46, 0x32}, /* 40 */
+ {0x63, 0x19}, /* 41 */
+ {0x31, 0x19}, /* 42 */
+ {0x58, 0x4C}, /* 43 */
+ {0x6C, 0x4C}, /* 44 */
+ {0x76, 0x66}, /* 45 */
+ {0x7B, 0x66}, /* 46 */
+ {0x3D, 0x73}, /* 47 */
+ {0x5E, 0x73}, /* 48 */
+ {0x6F, 0x39}, /* 49 */
+ {0x37, 0x39}, /* 50 */
+ {0x5B, 0x5C}, /* 51 */
+ {0x2D, 0x5C}, /* 52 */
+ {0x56, 0x6E}, /* 53 */
+ {0x6B, 0x6E}, /* 54 */
+ {0x35, 0x77}, /* 55 */
+ {0x5A, 0x77}, /* 56 */
+ {0x6D, 0x3B}, /* 57 */
+ {0x36, 0x3B}, /* 58 */
+ {0x1B, 0x5D}, /* 59 */
+ {0x4D, 0x5D}, /* 60 */
+ {0x26, 0x2E}, /* 61 */
+ {0x13, 0x2E}, /* 62 */
+ {0x49, 0x17}, /* 63 */
+ {0x24, 0x17}, /* 64 */
+ {0x12, 0x4B}, /* 65 */
+ {0x09, 0x4B}, /* 66 */
+ {0x44, 0x25} /* 67 */
+};
+
+/* WOV interrupts */
+static const uint8_t wov_interupts[] = {
+ 0, /* VAD_INTEN */
+ 1, /* VAD_WKEN */
+ 8, /* CFIFO_NE_IE */
+ 9, /* CFIFO_OIT_IE */
+ 10, /* CFIFO_OWT_WE */
+ 11, /* CFIFO_OVRN_IE */
+ 12, /* I2S_FIFO_OVRN_IE */
+ 13 /* I2S_FIFO_UNDRN_IE */
+};
+
+
+struct wov_ppl_divider {
+ uint16_t pll_frame_len; /* PLL frame length. */
+ uint16_t pll_fbdv; /* PLL feedback divider. */
+ uint8_t pll_indv; /* PLL Input Divider. */
+ uint8_t pll_otdv1; /* PLL Output Divider 1. */
+ uint8_t pll_otdv2; /* PLL Output Divider 2. */
+ uint8_t pll_ediv; /* PLL External Divide Factor. */
+};
+
+struct wov_cfifo_buf {
+ uint32_t *buf; /* Pointer to a buffer. */
+ int size; /* Buffer size in words. */
+};
+
+struct wov_config wov_conf;
+
+static struct wov_cfifo_buf cfifo_buf;
+static wov_call_back_t callback_fun;
+
+const uint32_t voice_buffer[VOICE_BUF_SIZE] = {0};
+
+#define WOV_CALLBACK(event) \
+ { \
+ if (callback_fun != NULL) \
+ callback_fun(event); \
+ }
+
+#define CONFIG_WOV_FIFO_THRESH_WORDS WOV_FIFO_THRESHOLD_80_DATA_WORDS
+
+/**
+ * Reads data from the core fifo.
+ *
+ * @param num_elements - Number of elements (Dword) to read.
+ *
+ * @return None
+ */
+void wov_cfifo_read_handler_l(uint32_t num_elements)
+{
+ uint32_t index;
+
+ for (index = 0; index < num_elements; index++)
+ cfifo_buf.buf[index] = NPCX_WOV_FIFO_OUT;
+
+ cfifo_buf.buf = &cfifo_buf.buf[index];
+ cfifo_buf.size -= num_elements;
+}
+
+static enum ec_error_list wov_calc_pll_div_s(int32_t d_in,
+ int32_t total_div, int32_t vco_freq,
+ struct wov_ppl_divider *pll_div)
+{
+ int32_t d_1, d_2, d_e;
+
+ /*
+ * Please see comments in wov_calc_pll_div_l function below.
+ */
+ for (d_e = 4; d_e < 75; d_e++) {
+ for (d_2 = 1; d_2 < 7; d_2++) {
+ for (d_1 = 1; d_1 < 7; d_1++) {
+ if ((vco_freq / (d_1 * d_2)) > 900)
+ continue;
+
+ if (total_div == (d_in * d_e * d_1 * d_2)) {
+ pll_div->pll_indv = d_in;
+ pll_div->pll_otdv1 = d_1;
+ pll_div->pll_otdv2 = d_2;
+ pll_div->pll_ediv = d_e;
+ return EC_SUCCESS;
+ }
+ }
+ }
+ }
+ return EC_ERROR_INVAL;
+}
+
+/**
+ * Gets the PLL divider value accordingly to the i2S clock frequency.
+ *
+ * @param i2s_clk_freq - i2S clock frequency
+ * @param sample_rate - Sample rate in KHz (16KHz or 48KHz)
+ * @param pll_div - PLL dividers.
+ *
+ * @return None
+ */
+static enum ec_error_list wov_calc_pll_div_l(uint32_t i2s_clk_freq,
+ uint32_t sample_rate, struct wov_ppl_divider *pll_div)
+{
+ int32_t d_f;
+ int32_t total_div;
+ int32_t d_in;
+ int32_t n;
+ int32_t vco_freq;
+ int32_t i2s_clk_freq_khz;
+
+ n = i2s_clk_freq / sample_rate;
+ if (i2s_clk_freq != (sample_rate * n))
+ return EC_ERROR_INVAL;
+
+ if ((n < 32) || (n >= 257))
+ return EC_ERROR_INVAL;
+
+ pll_div->pll_frame_len = n;
+
+ i2s_clk_freq_khz = i2s_clk_freq / 1000;
+
+ /*
+ * The code below implemented the “PLL setting option” table as
+ * describe in the NPCX7m7w specification document.
+ * - Total_div is VCO frequency in MHz / 12 MHz
+ * - d_f is the Feedback Divider
+ * - d_in is the Input Divider (PLL_INDV)
+ * - d_e is the PLL Ext Divider
+ * - d_2 is the Output Divide 2 (PLL_OTDV2)
+ * - d_1 is the Output Divide 1 (PLL_OTDV1)
+ * It is preferred that d_f will be as smaller as possible, after that
+ * the d_in will be as smaller as possible and so on, this is the
+ * reason that d_f (calculated from total_div) is in the external loop
+ * and d-1 is in the internal loop (as it may contain the bigger value).
+ * The “PLL setting option” code divided to 2 function in order to
+ * fulfil the coding style indentation rule.
+ */
+
+ /* total div is min_vco/12 400/12=33. */
+ for (total_div = 33; total_div < 1500; total_div++) {
+ d_f = (total_div * 12000) / i2s_clk_freq_khz;
+ if ((total_div * 12000) == (d_f * i2s_clk_freq_khz)) {
+ for (d_in = 1; d_in < 10; d_in++) {
+ if (((i2s_clk_freq / 1000) / d_in) <= 500)
+ continue;
+
+ vco_freq = total_div * 12 / d_in;
+ if ((vco_freq < 500) || (vco_freq > 1600))
+ continue;
+ if (wov_calc_pll_div_s(d_in, total_div,
+ vco_freq, pll_div) ==
+ EC_SUCCESS) {
+ pll_div->pll_fbdv = d_f;
+ return EC_SUCCESS;
+ }
+
+ }
+ }
+ }
+
+ return EC_ERROR_INVAL;
+}
+
+/**
+ * Check if PLL is locked.
+ *
+ * @param None
+ *
+ * @return EC_SUCCESS if PLL is locked, EC_ERROR_UNKNOWN otherwise .
+ */
+enum ec_error_list wov_wait_for_pll_lock_l(void)
+{
+ uint32_t index;
+
+ for (index = 0; WOV_PLL_IS_NOT_LOCK; index++) {
+ /* PLL doesn't reach to lock state. */
+ if (index > 0xFFFF)
+ return EC_ERROR_UNKNOWN;
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Configure I2S bus. (Parameters determined via common config functions.)
+ *
+ * @param None.
+ *
+ * @return none.
+ */
+static enum ec_error_list wov_set_i2s_config_l(void)
+{
+ struct wov_ppl_divider pll_div;
+ enum ec_error_list ret_code;
+ enum wov_i2s_chan_trigger trigger_0, trigger_1;
+ int32_t start_delay_0, start_delay_1;
+
+ ret_code = wov_calc_pll_div_l(wov_conf.i2s_clock,
+ wov_conf.sample_per_sec, &pll_div);
+ if (ret_code == EC_SUCCESS) {
+ /* Configure the PLL. */
+ ret_code = wov_pll_clk_div_config(
+ pll_div.pll_otdv1, pll_div.pll_otdv2, pll_div.pll_fbdv,
+ pll_div.pll_indv);
+ if (ret_code != EC_SUCCESS)
+ return ret_code;
+
+ ret_code = wov_pll_clk_ext_div_config(
+ (enum wov_pll_ext_div_sel)(pll_div.pll_ediv > 15),
+ pll_div.pll_ediv);
+ if (ret_code != EC_SUCCESS)
+ return ret_code;
+
+ wov_i2s_global_config(
+ (enum wov_floating_mode)(wov_conf.dai_format ==
+ WOV_DAI_FMT_PCM_TDM),
+ WOV_FLOATING_DRIVEN, WOV_CLK_NORMAL, 0, WOV_PULL_DOWN,
+ 0, WOV_PULL_DOWN, WOV_NORMAL_MODE);
+
+ /* Configure DAI format. */
+ switch (wov_conf.dai_format) {
+ case WOV_DAI_FMT_I2S:
+ trigger_0 = WOV_I2S_SAMPLED_0_AFTER_1;
+ trigger_1 = WOV_I2S_SAMPLED_1_AFTER_0;
+ start_delay_0 = 1;
+ start_delay_1 = 1;
+ break;
+
+ case WOV_DAI_FMT_RIGHT_J:
+ trigger_0 = WOV_I2S_SAMPLED_1_AFTER_0;
+ trigger_1 = WOV_I2S_SAMPLED_0_AFTER_1;
+ start_delay_0 = (pll_div.pll_frame_len / 2) -
+ wov_conf.bit_depth;
+ start_delay_1 = (pll_div.pll_frame_len / 2) -
+ wov_conf.bit_depth;
+ break;
+
+ case WOV_DAI_FMT_LEFT_J:
+ trigger_0 = WOV_I2S_SAMPLED_1_AFTER_0;
+ trigger_1 = WOV_I2S_SAMPLED_0_AFTER_1;
+ start_delay_0 = 0;
+ start_delay_1 = 0;
+ break;
+
+ case WOV_DAI_FMT_PCM_A:
+ trigger_0 = WOV_I2S_SAMPLED_1_AFTER_0;
+ trigger_1 = WOV_I2S_SAMPLED_1_AFTER_0;
+ start_delay_0 = 1;
+ start_delay_1 = wov_conf.bit_depth + 1;
+ break;
+
+ case WOV_DAI_FMT_PCM_B:
+ trigger_0 = WOV_I2S_SAMPLED_1_AFTER_0;
+ trigger_1 = WOV_I2S_SAMPLED_1_AFTER_0;
+ start_delay_0 = 0;
+ start_delay_1 = wov_conf.bit_depth;
+ break;
+
+ case WOV_DAI_FMT_PCM_TDM:
+ trigger_0 = WOV_I2S_SAMPLED_1_AFTER_0;
+ trigger_1 = WOV_I2S_SAMPLED_1_AFTER_0;
+ start_delay_0 = wov_conf.i2s_start_delay_0;
+ start_delay_1 = wov_conf.i2s_start_delay_1;
+ break;
+
+ default:
+ return EC_ERROR_INVALID_CONFIG;
+ }
+
+ udelay(100);
+
+ ret_code = wov_i2s_channel_config(0, wov_conf.bit_depth,
+ trigger_0, start_delay_0);
+
+ ret_code = wov_i2s_channel_config(1, wov_conf.bit_depth,
+ trigger_1, start_delay_1);
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Sets microphone source.
+ *
+ * | Left | Right | Mono | Stereo
+ *------------------|-----------|---------------|---------------|--------------
+ *FIFO_CNT. |0x0 or 0x2 | 0x0 or 0x2 | 0x1 or 0x3 |0x1 or 0x3
+ *CFIFI_ISEL | (left) |(left) |(left & Right) |(left & right)
+ *------------------|-----------|---------------|---------------|--------------
+ *CR_DMIC. | 0x1 | 0x1 | 0x2 | 0x1
+ *ADC_DMIC_SEL_LEFT | (left) | (left) | (average) | (left)
+ *------------------|-----------|---------------|---------------|--------------
+ *CR_DMIC. | 0x1 | 0x1 | 0x2 | 0x1
+ *ADC_DMIC_SEL_RIGHT| (right) | (right) | (average) | (right)
+ *------------------|-----------|---------------|---------------|--------------
+ *MIX_2. | 0x0 | 0x1 | 0x0 | 0x0
+ *AIADCL_SEL | (normal) |(cross inputs) | (normal) | (normal)
+ *------------------|-----------|---------------|---------------|--------------
+ *MIX_2. | 0x3 | 0x3 | 0x0 | 0x0
+ *AIADCR_SEL |(no input) | (no input) | (normal) | (normal)
+ *------------------|-----------|---------------|---------------|--------------
+ *VAD_0. | 0x0 | 0x1 | 0x2 | Not
+ *VAD_INSEL | (left) | (right) | (average) | applicable
+ *
+ * @param None.
+ * @return return EC_SUCCESS if mic source valid othewise return error code.
+ */
+static enum ec_error_list wov_set_mic_source_l(void)
+{
+ switch (wov_conf.mic_src) {
+ case WOV_SRC_LEFT:
+ if (wov_conf.bit_depth == 16)
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x00);
+ else
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x02);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_LEFT,
+ 0x01);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_RIGHT,
+ 0x01);
+ apm_digital_mixer_config(APM_OUT_MIX_NORMAL_INPUT,
+ APM_OUT_MIX_NO_INPUT);
+ apm_set_vad_input_channel(APM_IN_LEFT);
+ break;
+
+ case WOV_SRC_RIGHT:
+ if (wov_conf.bit_depth == 16)
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x00);
+ else
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x02);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_LEFT,
+ 0x01);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_RIGHT,
+ 0x01);
+ apm_digital_mixer_config(APM_OUT_MIX_CROSS_INPUT,
+ APM_OUT_MIX_NO_INPUT);
+ apm_set_vad_input_channel(APM_IN_RIGHT);
+ break;
+
+ case WOV_SRC_MONO:
+ if (wov_conf.bit_depth == 16)
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x01);
+ else
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x03);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_LEFT,
+ 0x02);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_RIGHT,
+ 0x02);
+ apm_digital_mixer_config(APM_OUT_MIX_NORMAL_INPUT,
+ APM_OUT_MIX_NORMAL_INPUT);
+ apm_set_vad_input_channel(APM_IN_AVERAGE_LEFT_RIGHT);
+ break;
+
+ case WOV_SRC_STEREO:
+ if (wov_conf.bit_depth == 16)
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x01);
+ else
+ SET_FIELD(NPCX_WOV_FIFO_CNT,
+ NPCX_WOV_FIFO_CNT_CFIFO_ISEL, 0x03);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_LEFT,
+ 0x01);
+ SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_SEL_RIGHT,
+ 0x01);
+ apm_digital_mixer_config(APM_OUT_MIX_NORMAL_INPUT,
+ APM_OUT_MIX_NORMAL_INPUT);
+ apm_set_vad_input_channel(APM_IN_RESERVED);
+ break;
+
+ default:
+ return EC_ERROR_INVAL;
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * WoV interrupt handler.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_interrupt_handler(void)
+{
+ uint32_t wov_status;
+ uint32_t wov_inten;
+
+ wov_inten = GET_FIELD(NPCX_WOV_WOV_INTEN, NPCX_WOV_STATUS_BITS);
+ wov_status = wov_inten &
+ GET_FIELD(NPCX_WOV_STATUS, NPCX_WOV_STATUS_BITS);
+
+ /*
+ * Voice activity detected.
+ */
+ if (APM_IS_VOICE_ACTIVITY_DETECTED) {
+ APM_CLEAR_VAD_INTERRUPT;
+ apm_vad_enable(0);
+ apm_enable_vad_interrupt(0);
+ WOV_CALLBACK(WOV_EVENT_VAD);
+ }
+
+ /* Core FIFO is overrun. Reset the Core FIFO and inform the FW */
+ if (WOV_IS_CFIFO_OVERRUN(wov_status)) {
+ WOV_CALLBACK(WOV_EVENT_ERROR_CORE_FIFO_OVERRUN);
+ wov_core_fifo_reset();
+ } else if (WOV_IS_CFIFO_INT_THRESHOLD(wov_status) &&
+ (cfifo_buf.buf != NULL)) {
+ /*
+ * Core FIFO threshold or FIFO not empty event occurred.
+ * - Read data from core FIFO to the buffer.
+ * - In case data ready or no space for data, inform the FW.
+ */
+
+ /* Copy data from CFIFO to RAM. */
+ wov_cfifo_read_handler_l((WOV_GET_CORE_FIFO_THRESHOLD * 2));
+
+ if (cfifo_buf.size < (WOV_GET_CORE_FIFO_THRESHOLD * 2)) {
+ cfifo_buf.buf = NULL;
+ cfifo_buf.size = 0;
+ WOV_CALLBACK(WOV_EVENT_DATA_READY);
+ }
+ }
+
+ /* I2S FIFO is overrun. Reset the I2S FIFO and inform the FW. */
+ if (WOV_IS_I2S_FIFO_OVERRUN(wov_status)) {
+ WOV_CALLBACK(WOV_EVENT_ERROR_I2S_FIFO_OVERRUN);
+ wov_i2s_fifo_reset();
+ }
+
+ /* I2S FIFO is underrun. Reset the I2S FIFO and inform the FW. */
+ if (WOV_IS_I2S_FIFO_UNDERRUN(wov_status))
+ WOV_CALLBACK(WOV_EVENT_ERROR_I2S_FIFO_UNDERRUN);
+
+ /* Clear the WoV status register. */
+ SET_FIELD(NPCX_WOV_STATUS, NPCX_WOV_STATUS_BITS, wov_status);
+}
+
+DECLARE_IRQ(NPCX_IRQ_WOV, wov_interrupt_handler, 3);
+
+/**
+ * Enable FMUL2.
+ *
+ * @param enable - enabled flag, true for enable
+ * @return None
+ */
+static void wov_fmul2_enable(int enable)
+{
+ if (enable) {
+ /* Enable clock tuning. */
+ CLEAR_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_TUNE_DIS);
+ /* Enable clock. */
+ CLEAR_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_FMUL2_DIS);
+ } else
+ SET_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_FMUL2_DIS);
+
+ udelay(WOV_FMUL2_CLK_TUNING_DELAY_TIME);
+
+ /* Disable clock tuning. */
+ SET_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_TUNE_DIS);
+}
+
+#define WOV_FMUL2_MAX_RETRIES 0x000FFFFF
+
+/* FMUL2 clock multipliers values. */
+struct wov_fmul2_multiplier_setting_val {
+ uint8_t fm2mh;
+ uint8_t fm2ml;
+ uint8_t fm2n;
+};
+
+
+/**
+ * Configure FMUL2 clock tunning.
+ *
+ * @param None
+ * @return None
+ */
+void wov_fmul2_conf_tuning(void)
+{
+ /* Check if FMUL2 is enabled, then do nothing. */
+ if (IS_BIT_SET(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_FMUL2_DIS) ==
+ 0x00)
+ return;
+
+ /* Enable clock tuning. */
+ CLEAR_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_TUNE_DIS);
+
+ udelay(WOV_FMUL2_CLK_TUNING_DELAY_TIME);
+
+ /* Disable clock tuning. */
+ SET_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_TUNE_DIS);
+}
+
+static int wov_get_cfifo_threshold_l(void)
+{
+ int fifo_threshold;
+
+ fifo_threshold = WOV_GET_FIFO_INT_THRESHOLD;
+
+ if (fifo_threshold == 0)
+ return 1;
+ else
+ return (fifo_threshold * 2);
+}
+
+/***************************************************************************
+ *
+ * Exported function.
+ *
+ **************************************************************************/
+
+
+/**
+ * Set FMUL2 clock divider.
+ *
+ * @param None
+ * @return None
+ */
+void wov_fmul2_set_clk_divider(enum fmul2_clk_divider clk_div)
+{
+ SET_FIELD(NPCX_FMUL2_FM2P, NPCX_FMUL2_FM2P_WFPRED, clk_div);
+}
+
+/**
+ * Configure DMIC clock.
+ *
+ * @param enable - DMIC enabled , 1 means enable
+ * @param clk_div - DMIC clock division factor (disable, divide by 2
+ * divide by 4)
+ * @return None
+ */
+void wov_dmic_clk_config(int enable, enum wov_dmic_clk_div_sel clk_div)
+{
+ /* If DMIC enabled then configured its clock.*/
+ if (enable) {
+ if (clk_div != WOV_DMIC_DIV_DISABLE) {
+ SET_BIT(NPCX_WOV_CLOCK_CNTL,
+ NPCX_WOV_CLOCK_CNT_DMIC_CKDIV_EN);
+ if (clk_div == WOV_DMIC_DIV_BY_2)
+ CLEAR_BIT(NPCX_WOV_CLOCK_CNTL,
+ NPCX_WOV_CLOCK_CNT_DMIC_CKDIV_SEL);
+ else
+ SET_BIT(NPCX_WOV_CLOCK_CNTL,
+ NPCX_WOV_CLOCK_CNT_DMIC_CKDIV_SEL);
+ } else
+ CLEAR_BIT(NPCX_WOV_CLOCK_CNTL,
+ NPCX_WOV_CLOCK_CNT_DMIC_CKDIV_EN);
+
+ SET_BIT(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_DMIC_EN);
+ } else
+ CLEAR_BIT(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_DMIC_EN);
+}
+
+/**
+ * Sets WoV mode
+ *
+ * @param wov_mode - WoV mode
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_set_mode(enum wov_modes wov_mode)
+{
+ enum ec_error_list ret_code;
+
+ if ((wov_mode == WOV_MODE_I2S) || (wov_mode == WOV_MODE_RAM_AND_I2S)) {
+ wov_pll_enable(1);
+ wov_set_clk_selection(WOV_PLL_CLK_SRC);
+ } else {
+ wov_pll_enable(0);
+ wov_set_clk_selection(WOV_FMUL2_CLK_SRC);
+ wov_stop_i2s_capture();
+ }
+
+ apm_set_mode(wov_mode);
+
+ ret_code = wov_set_mic_source_l();
+ if (ret_code != EC_SUCCESS)
+ return ret_code;
+
+ switch (wov_mode) {
+ case WOV_MODE_OFF:
+ wov_dmic_clk_config(0, WOV_DMIC_DIV_DISABLE);
+ wov_mute(1);
+ wov_fmul2_enable(0);
+ break;
+ case WOV_MODE_VAD:
+ wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE);
+ wov_fmul2_enable(1);
+ wov_mute(0);
+ break;
+ case WOV_MODE_RAM:
+ if ((wov_conf.bit_depth != 16) && (wov_conf.bit_depth != 24))
+ return EC_ERROR_INVAL;
+ wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE);
+ wov_fmul2_enable(1);
+ wov_mute(0);
+ break;
+ case WOV_MODE_I2S:
+ wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE);
+ wov_fmul2_enable(0);
+ wov_set_i2s_config_l();
+ wov_start_i2s_capture();
+ wov_mute(0);
+ break;
+ case WOV_MODE_RAM_AND_I2S:
+ if ((wov_conf.bit_depth != 16) && (wov_conf.bit_depth != 24))
+ return EC_ERROR_INVAL;
+ wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE);
+ wov_fmul2_enable(0);
+ wov_set_i2s_config_l();
+ wov_start_i2s_capture();
+ wov_mute(0);
+ break;
+ default:
+ wov_dmic_clk_config(0, WOV_DMIC_DIV_DISABLE);
+ wov_fmul2_enable(0);
+ wov_mute(1);
+ return EC_ERROR_INVAL;
+ }
+
+ wov_conf.mode = wov_mode;
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Gets WoV mode
+ *
+ * @param None
+ * @return WoV mode
+ */
+enum wov_modes wov_get_mode(void)
+{
+ return wov_conf.mode;
+}
+
+/**
+ * Initiates WoV.
+ *
+ * @param callback - Pointer to callback function.
+ *
+ * @return None
+ */
+void wov_init(void)
+{
+ apm_init();
+
+ wov_apm_active(1);
+ wov_mute(1);
+
+ wov_conf.mode = WOV_MODE_OFF;
+ wov_conf.sample_per_sec = 16000;
+ wov_conf.bit_depth = 16;
+ wov_conf.mic_src = WOV_SRC_LEFT;
+ wov_conf.left_chan_gain = 0;
+ wov_conf.rigth_chan_gain = 0;
+ wov_conf.i2s_start_delay_0 = 0;
+ wov_conf.i2s_start_delay_1 = 0;
+ wov_conf.i2s_clock = 0;
+ wov_conf.dai_format = WOV_DAI_FMT_I2S;
+ wov_conf.sensitivity_db = 5;
+
+ callback_fun = wov_handle_event;
+
+ wov_cfifo_config(WOV_CFIFO_IN_LEFT_CHAN_2_CONS_16_BITS,
+ WOV_FIFO_THRESHOLD_80_DATA_WORDS);
+
+ apm_set_vad_dmic_rate(APM_DMIC_RATE_1_0);
+ apm_set_adc_dmic_config(APM_DMIC_RATE_2_4);
+}
+
+/**
+ * Select clock source FMUL2 or PLL.
+ *
+ * @param clk_src - select between FMUL2 (WOV_FMUL2_CLK_SRC) and
+ * PLL (WOV_PLL_CLK_SRC)
+ *
+ * NOTE: THIS FUNCTION RESETS THE APM and RETURN ITS REGISSTERS TO THEIR
+ * DEFAULT VALUES !!!!!!!
+ *
+ * @return None
+ */
+void wov_set_clk_selection(enum wov_clk_src_sel clk_src)
+{
+ int is_apm_disable;
+
+ is_apm_disable = IS_BIT_SET(NPCX_APM_CR_APM, NPCX_APM_CR_APM_PD);
+
+ apm_enable(0);
+
+ if (clk_src == WOV_FMUL2_CLK_SRC)
+ CLEAR_BIT(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_CLK_SEL);
+ else if (wov_wait_for_pll_lock_l() == EC_SUCCESS)
+ SET_BIT(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_CLK_SEL);
+
+ udelay(1);
+
+ if (!is_apm_disable)
+ apm_enable(1);
+}
+
+/**
+ * Configure PLL clock.
+ *
+ * @param ext_div_sel - PLL external divider selector.
+ * @param div_factor - When ext_div_sel is WOV_PLL_EXT_DIV_BIN_CNT
+ * then it is the 4 LSBits of this field,
+ * otherwise this field is an index to
+ * PLL External Divider Load Values table.
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_pll_clk_ext_div_config(
+ enum wov_pll_ext_div_sel ext_div_sel,
+ uint32_t div_factor)
+{
+ /* Sets the clock division factor for the PLL external divider.
+ * The divide factor should be in the range of 2 to 67.
+ * When ext_div_sel is WOV_PLL_EXT_DIV_BIN_CNT, then the 4 least
+ * significant bits of div_factor are used to set the divide
+ * ratio.
+ * In this case the divide ration legal values are from 2 to 15
+ * For WOV_PLL_EXT_DIV_LFSR, this parameter is used as index for
+ * pll_ext_div table.
+ */
+ if (ext_div_sel == WOV_PLL_EXT_DIV_BIN_CNT)
+ CLEAR_BIT(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_PLL_EDIV_SEL);
+ else
+ SET_BIT(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_PLL_EDIV_SEL);
+
+ if (ext_div_sel == WOV_PLL_EXT_DIV_BIN_CNT) {
+ if ((div_factor > 15) || (div_factor < 2))
+ return EC_ERROR_INVAL;
+
+ SET_FIELD(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_PLL_EDIV,
+ (div_factor));
+ } else {
+ if ((div_factor > 67) || (div_factor < 12))
+ return EC_ERROR_INVAL;
+
+ SET_FIELD(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_PLL_EDIV,
+ pll_ext_div[div_factor - 12].pll_ediv);
+
+ SET_FIELD(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_PLL_EDIV_DC,
+ pll_ext_div[div_factor - 12].pll_ediv_dc);
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * PLL power down.
+ *
+ * @param enable - 1 enable the PLL or 0 PLL disable
+ * @return None
+ */
+void wov_pll_enable(int enable)
+{
+ if (enable)
+ CLEAR_BIT(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_PWDEN);
+ else
+ SET_BIT(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_PWDEN);
+
+ udelay(1);
+}
+
+/**
+ * Configures PLL clock dividers..
+ *
+ * @param out_div_1 - PLL output divider #1, valid values 1-7
+ * @param out_div_2 - PLL output divider #2, valid values 1-7
+ * @param feedback_div - PLL feadback divider (Default is 375 decimal)
+ * @param in_div - PLL input divider
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_pll_clk_div_config(uint32_t out_div_1,
+ uint32_t out_div_2,
+ uint32_t feedback_div,
+ uint32_t in_div)
+{
+ /* Parameter check. */
+ if ((out_div_1 < 1) || (out_div_1 > 7) ||
+ (out_div_2 < 1) || (out_div_2 > 7))
+ return EC_ERROR_INVAL;
+
+ /*
+ * PLL configuration sequence:
+ * 1. Set PLL_PWDEN bit to 1.
+ * 2. Set PLL divider values.
+ * 3. Wait 1usec.
+ * 4. Clear PLL_PWDEN bit to 0 while not changing other PLL parameters.
+ */
+ SET_BIT(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_PWDEN);
+
+ SET_FIELD(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_OTDV1, out_div_1);
+ SET_FIELD(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_OTDV2, out_div_2);
+
+ SET_FIELD(NPCX_WOV_PLL_CNTL2, NPCX_WOV_PLL_CNTL2_PLL_FBDV,
+ feedback_div);
+
+ SET_FIELD(NPCX_WOV_PLL_CNTL2, NPCX_WOV_PLL_CNTL2_PLL_INDV, in_div);
+
+ udelay(1);
+
+ CLEAR_BIT(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_PWDEN);
+
+ udelay(1);
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Enables/Disables WoV interrupt.
+ *
+ * @param int_index - Interrupt ID.
+ * @param enable - enabled flag, 1 means enable
+ *
+ * @return None.
+ */
+void wov_interrupt_enable(enum wov_interrupt_index int_index, int enable)
+{
+ if (enable)
+ SET_BIT(NPCX_WOV_WOV_INTEN, wov_interupts[int_index]);
+ else
+ CLEAR_BIT(NPCX_WOV_WOV_INTEN, wov_interupts[int_index]);
+}
+
+/**
+ * Sets core FIFO threshold.
+ *
+ * @param in_sel - Core FIFO input select
+ * @param threshold - Core FIFO threshold
+ *
+ * @return None
+ */
+void wov_cfifo_config(enum wov_core_fifo_in_sel in_sel,
+ enum wov_fifo_threshold threshold)
+{
+ /* Set core FIFO input selection. */
+ SET_FIELD(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_CFIFO_ISEL, in_sel);
+
+ /* Set wake & interrupt core FIFO threshold. */
+ WOV_SET_FIFO_WAKE_THRESHOLD(threshold);
+ WOV_SET_FIFO_INT_THRESHOLD(threshold);
+}
+
+/**
+ * Start the actual capturing of the Voice data to the RAM.
+ * Note that the pointer to the RAM buffer must be precisely
+ * set by calling wov_set_buffer();
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_start_ram_capture(void)
+{
+ /* Clear the CFIFO status bits in WoV status register. */
+ SET_FIELD(NPCX_WOV_STATUS, NPCX_WOV_STATUS_BITS, 0x27);
+
+ CLEAR_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_CORE_FFRST);
+
+ wov_interrupt_enable(WOV_CFIFO_OVERRUN_INT_INDX, 1);
+ wov_interrupt_enable(WOV_CFIFO_THRESHOLD_INT_INDX, 1);
+ wov_interrupt_enable(WOV_CFIFO_THRESHOLD_WAKE_INDX, 1);
+}
+
+/**
+ * Stop the capturing of the Voice data to the RAM.
+ *
+ * @param none
+ *
+ * @return None
+ */
+void wov_stop_ram_capture(void)
+{
+ SET_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_CORE_FFRST);
+
+ wov_interrupt_enable(WOV_CFIFO_OVERRUN_INT_INDX, 0);
+ wov_interrupt_enable(WOV_CFIFO_THRESHOLD_INT_INDX, 0);
+ wov_interrupt_enable(WOV_CFIFO_THRESHOLD_WAKE_INDX, 0);
+
+ udelay(10);
+}
+
+/**
+ * Rests the Core FIFO.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_core_fifo_reset(void)
+{
+ SET_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_CORE_FFRST);
+
+ udelay(10);
+
+ CLEAR_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_CORE_FFRST);
+}
+
+/**
+ * Rests the I2S FIFO.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_i2s_fifo_reset(void)
+{
+ SET_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST);
+
+ udelay(10);
+
+ CLEAR_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST);
+}
+
+/**
+ * Start the capturing of the Voice data via I2S.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_start_i2s_capture(void)
+{
+ /* Clear the I2S status bits in WoV status register. */
+ SET_FIELD(NPCX_WOV_STATUS, NPCX_WOV_STATUS_BITS, 0x18);
+
+ CLEAR_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST);
+
+ wov_interrupt_enable(WOV_I2SFIFO_OVERRUN_INT_INDX, 1);
+ wov_interrupt_enable(WOV_I2SFIFO_UNDERRUN_INT_INDX, 1);
+}
+
+/**
+ * Stop the capturing of the Voice data via I2S.
+ *
+ * @param none
+ *
+ * @return None
+ */
+void wov_stop_i2s_capture(void)
+{
+ SET_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST);
+
+ wov_interrupt_enable(WOV_I2SFIFO_OVERRUN_INT_INDX, 0);
+ wov_interrupt_enable(WOV_I2SFIFO_UNDERRUN_INT_INDX, 0);
+
+ udelay(10);
+}
+
+/**
+ * Sets data buffer for reading from core FIFO
+ *
+ * @param buff - Pointer to the read buffer, buffer must be 32 bits
+ * aligned.
+ * @param size_in_words - Size must be a multiple of CONFIG_WOV_THRESHOLD_WORDS
+ * (defaulte = 80 words)
+ *
+ * @return None
+ *
+ * Note - When the data buffer will be full the FW will be notifyed
+ * about it, and the FW will need to recall to this function.
+ */
+int wov_set_buffer(uint32_t *buf, int size_in_words)
+{
+ int cfifo_threshold;
+
+ cfifo_threshold = wov_get_cfifo_threshold_l();
+ if (size_in_words !=
+ ((size_in_words / cfifo_threshold) * cfifo_threshold))
+ return EC_ERROR_INVAL;
+
+ cfifo_buf.buf = buf;
+ cfifo_buf.size = size_in_words;
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Resets the APM.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void wov_apm_active(int enable)
+{
+ /* For APM it is negativ logic. */
+ if (enable)
+ CLEAR_BIT(NPCX_WOV_APM_CTRL, NPCX_WOV_APM_CTRL_APM_RST);
+ else
+ SET_BIT(NPCX_WOV_APM_CTRL, NPCX_WOV_APM_CTRL_APM_RST);
+}
+
+/**
+ * I2S golobal configuration
+ *
+ * @param i2s_hiz_data - Defines when the I2S data output is floating.
+ * @param i2s_hiz - Defines if the I2S data output is always floating.
+ * @param clk_invert - Defines the I2S bit clock edge sensitivity
+ * @param out_pull_en - Enable a pull-up or a pull-down resistor on
+ * I2S output
+ * @param out_pull_mode - Select a pull-up or a pull-down resistor on
+ * I2S output
+ * @param in_pull_en - Enable a pull-up or a pull-down resistor on
+ * I2S input
+ * @param in_pull_mode - Select a pull-up or a pull-down resistor on
+ * I2S intput
+ * @param test_mode - Selects I2S test mode
+ *
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_i2s_global_config(
+ enum wov_floating_mode i2s_hiz_data,
+ enum wov_floating_mode i2s_hiz,
+ enum wov_clk_inverted_mode clk_invert,
+ int out_pull_en,
+ enum wov_pull_upd_down_sel out_pull_mode,
+ int in_pull_en,
+ enum wov_pull_upd_down_sel in_pull_mode,
+ enum wov_test_mode test_mode)
+{
+ /* Check the parameters correctness. */
+ if ((i2s_hiz_data == WOV_FLOATING) &&
+ ((GET_FIELD(NPCX_WOV_I2S_CNTL(0),
+ NPCX_WOV_I2S_CNTL_I2S_ST_DEL) == 0) ||
+ (GET_FIELD(NPCX_WOV_I2S_CNTL(1),
+ NPCX_WOV_I2S_CNTL_I2S_ST_DEL) == 0)))
+ return EC_ERROR_INVAL;
+
+ /* Set the parameters. */
+ if (i2s_hiz_data == WOV_FLOATING_DRIVEN)
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_HIZD);
+ else
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_HIZD);
+
+ if (i2s_hiz == WOV_FLOATING_DRIVEN)
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_HIZ);
+ else
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_HIZ);
+
+ if (clk_invert == WOV_CLK_NORMAL)
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0),
+ NPCX_WOV_I2S_CNTL0_I2S_SCLK_INV);
+ else
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_SCLK_INV);
+
+ if (out_pull_en)
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_OPE);
+ else
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_OPE);
+
+ if (out_pull_mode == WOV_PULL_DOWN)
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_OPS);
+ else
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_OPS);
+
+ if (in_pull_en)
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_IPE);
+ else
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_IPE);
+
+ if (in_pull_mode == WOV_PULL_DOWN)
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_IPS);
+ else
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_IPS);
+
+ if (test_mode == WOV_NORMAL_MODE)
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_TST);
+ else
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL0_I2S_TST);
+
+ /* I2S should be reset in order I2S interface to function correctly. */
+ wov_i2s_fifo_reset();
+
+ return EC_SUCCESS;
+}
+
+/**
+ * I2S channel configuration
+ *
+ * @param channel_num - I2S channel number, 0 or 1.
+ * @param bit_count - I2S channel bit count.
+ * @param trigger - Define the I2S chanel trigger 1->0 or 0->1
+ * @param start_delay - Defines the delay from the trigger defined for
+ * the channel till the first bit (MSB) of the data.
+ *
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_i2s_channel_config(uint32_t channel_num,
+ uint32_t bit_count,
+ enum wov_i2s_chan_trigger trigger,
+ int32_t start_delay)
+{
+ /* Check the parameters correctnes. */
+ if ((channel_num != 0) && (channel_num != 1))
+ return EC_ERROR_INVAL;
+
+ if ((start_delay < 0) || (start_delay > 496))
+ return EC_ERROR_INVAL;
+
+ if ((bit_count != 16) && (bit_count != 18) && (bit_count != 20) &&
+ (bit_count != 24))
+ return EC_ERROR_INVAL;
+
+ /* Set the parameters. */
+ SET_FIELD(NPCX_WOV_I2S_CNTL(channel_num), NPCX_WOV_I2S_CNTL_I2S_BCNT,
+ (bit_count - 1));
+
+ if (trigger == WOV_I2S_SAMPLED_1_AFTER_0)
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(channel_num),
+ NPCX_WOV_I2S_CNTL_I2S_TRIG);
+ else
+ SET_BIT(NPCX_WOV_I2S_CNTL(channel_num),
+ NPCX_WOV_I2S_CNTL_I2S_TRIG);
+
+ SET_FIELD(NPCX_WOV_I2S_CNTL(channel_num), NPCX_WOV_I2S_CNTL_I2S_ST_DEL,
+ start_delay);
+
+ /* I2S should be reset in order I2S interface to function correctly. */
+ wov_i2s_fifo_reset();
+
+ return EC_SUCCESS;
+}
+
+/**
+ * wov_i2s_channel1_disable
+ *
+ * @param disable - disabled flag, 1 means disable
+ *
+ * @return None
+ */
+void wov_i2s_channel1_disable(int disable)
+{
+ if (disable)
+ SET_BIT(NPCX_WOV_I2S_CNTL(1), NPCX_WOV_I2S_CNTL1_I2S_CHN1_DIS);
+ else
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(1),
+ NPCX_WOV_I2S_CNTL1_I2S_CHN1_DIS);
+}
+
+/**
+ * Sets sampling rate.
+ *
+ * @param samples_per_second - Valid sample rate.
+ * @return In case sample rate is valid return EC_SUCCESS othewise return
+ * error code.
+ */
+int wov_set_sample_rate(uint32_t samples_per_second)
+{
+ if (wov_conf.mode != WOV_MODE_OFF)
+ return EC_ERROR_INVALID_CONFIG;
+
+ switch (samples_per_second) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ wov_conf.sample_per_sec = samples_per_second;
+ return EC_SUCCESS;
+ default:
+ return EC_ERROR_INVAL;
+ }
+}
+
+/**
+ * Gets sampling rate.
+ *
+ * @param None
+ * @return the current sampling rate.
+ */
+uint32_t wov_get_sample_rate(void)
+{
+ return wov_conf.sample_per_sec;
+}
+
+/**
+ * Sets sampling depth.
+ *
+ * @param bits_num - Valid sample depth in bits.
+ * @return In case sample depth is valid return EC_SUCCESS othewise return
+ * error code.
+ */
+int wov_set_sample_depth(int bits_num)
+{
+ if (wov_conf.mode != WOV_MODE_OFF)
+ return EC_ERROR_INVALID_CONFIG;
+
+ if ((bits_num != 16) && (bits_num != 18) &&
+ (bits_num != 20) && (bits_num != 24))
+ return EC_ERROR_INVAL;
+
+ wov_conf.bit_depth = bits_num;
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Gets sampling depth.
+ *
+ * @param None.
+ * @return sample depth in bits.
+ */
+int wov_get_sample_depth(void)
+{
+ return wov_conf.bit_depth;
+}
+
+/**
+ * Sets microphone source.
+ *
+ * @param mic_src - Valid microphone source
+ * @return return EC_SUCCESS if mic source valid othewise return error code.
+ */
+int wov_set_mic_source(enum wov_mic_source mic_src)
+{
+ wov_conf.mic_src = mic_src;
+
+ return wov_set_mic_source_l();
+}
+
+/**
+ * Gets microphone source.
+ *
+ * @param None.
+ * @return sample depth in bits.
+ */
+enum wov_mic_source wov_get_mic_source(void)
+{
+ return wov_conf.mic_src;
+}
+
+/**
+ * Mutes the WoV.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void wov_mute(int enable)
+{
+ if (enable)
+ SET_BIT(NPCX_APM_CR_ADC, NPCX_APM_CR_ADC_ADC_SOFT_MUTE);
+ else
+ CLEAR_BIT(NPCX_APM_CR_ADC, NPCX_APM_CR_ADC_ADC_SOFT_MUTE);
+}
+
+/**
+ * Sets gain
+ *
+ * @param left_chan_gain - Left channel gain.
+ * @param right_chan_gain - Right channel gain
+ * @return None
+ */
+void wov_set_gain(int left_chan_gain, int right_chan_gain)
+{
+ (void) apm_adc_gain_config(APM_ADC_CHAN_GAINS_INDEPENDENT,
+ left_chan_gain, right_chan_gain);
+}
+
+/**
+ * Enables/Disables ADC.
+ *
+ * @param enable - enabled flag, 1 means enable
+ * @return None
+ */
+void wov_enable_agc(int enable)
+{
+ apm_auto_gain_cntrl_enable(enable);
+}
+
+/**
+ * Enables/Disables the automatic gain.
+ *
+ * @param stereo - Stereo enabled flag, 1 means enable.
+ * @param target - Target output level of the ADC.
+ * @param noise_gate_threshold - Noise Gate system select. 1 means enable.
+ * @param hold_time - Hold time before starting AGC adjustment to
+ * the TARGET value.
+ * @param attack_time - Attack time - gain ramp down.
+ * @param decay_time - Decay time - gain ramp up.
+ * @param max_applied_gain - Maximum Gain Value to apply to the ADC path.
+ * @param min_applied_gain - Minimum Gain Value to apply to the ADC path.
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_set_agc_config(int stereo, float target,
+ int noise_gate_threshold, uint8_t hold_time,
+ uint16_t attack_time, uint16_t decay_time,
+ float max_applied_gain, float min_applied_gain)
+{
+ int target_code;
+ int ngth_code;
+ int attack_time_code;
+ int decay_time_code;
+ int max_applied_gain_code;
+ int min_applied_gain_code;
+ enum ec_error_list ret_code;
+ struct apm_auto_gain_config gain_cfg;
+
+ for (target_code = 0; target_code < 16; target_code++) {
+ if (((float)target_code * (-1.5)) == target)
+ break;
+ }
+ if (target_code == 16)
+ return EC_ERROR_INVAL;
+
+ if (noise_gate_threshold == 0)
+ ngth_code = 0;
+ else {
+ for (ngth_code = 0; ngth_code <= 0x07; ngth_code++) {
+ if ((-68 + ngth_code * 6) == noise_gate_threshold)
+ break;
+ }
+ if (ngth_code * 6 > 42)
+ return EC_ERROR_INVAL;
+ }
+
+ if (hold_time > 15)
+ return EC_ERROR_INVAL;
+
+ for (attack_time_code = 0; attack_time_code <= 0x0F;
+ attack_time_code++) {
+ if (((attack_time_code + 1) * 32) == attack_time)
+ break;
+ }
+ if (attack_time_code > 0x0F)
+ return EC_ERROR_INVAL;
+
+ for (decay_time_code = 0; decay_time_code <= 0x0F; decay_time_code++) {
+ if (((decay_time_code + 1) * 32) == decay_time)
+ break;
+ }
+ if (decay_time_code > 0x0F)
+ return EC_ERROR_INVAL;
+
+ for (max_applied_gain_code = 0; max_applied_gain_code < 16;
+ max_applied_gain_code++) {
+ if ((max_applied_gain_code * 1.5) == max_applied_gain)
+ break;
+ }
+ if (max_applied_gain_code == 16) {
+ for (max_applied_gain_code = 18; max_applied_gain_code < 32;
+ max_applied_gain_code++) {
+ if (((max_applied_gain_code * 1.5) - 4) ==
+ max_applied_gain)
+ break;
+ }
+ }
+ if (max_applied_gain_code > 32)
+ return EC_ERROR_INVAL;
+
+ for (min_applied_gain_code = 0; min_applied_gain_code < 16;
+ min_applied_gain_code++) {
+ if ((min_applied_gain_code * 1.5) == min_applied_gain)
+ break;
+ }
+ if (min_applied_gain_code == 16) {
+ for (min_applied_gain_code = 18; min_applied_gain_code < 32;
+ min_applied_gain_code++) {
+ if (((min_applied_gain_code * 1.5) - 4) ==
+ min_applied_gain)
+ break;
+ }
+ }
+ if (min_applied_gain_code > 32)
+ return EC_ERROR_INVAL;
+
+ gain_cfg.stereo_enable = stereo,
+ gain_cfg.agc_target = (enum apm_adc_target_out_level) target_code;
+ gain_cfg.nois_gate_en = (noise_gate_threshold != 0);
+ gain_cfg.nois_gate_thold = (enum apm_noise_gate_threshold) ngth_code;
+ gain_cfg.hold_time = (enum apm_agc_adj_hold_time) hold_time;
+ gain_cfg.attack_time = (enum apm_gain_ramp_time) attack_time_code;
+ gain_cfg.decay_time = (enum apm_gain_ramp_time) decay_time_code;
+ gain_cfg.gain_max = (enum apm_gain_values) max_applied_gain_code;
+ gain_cfg.gain_min = (enum apm_gain_values) min_applied_gain_code;
+
+ ret_code = apm_adc_auto_gain_config(&gain_cfg);
+
+ return ret_code;
+}
+
+/**
+ * Sets VAD sensitivity.
+ *
+ * @param sensitivity_db - VAD sensitivity in db.
+ * @return None
+ */
+int wov_set_vad_sensitivity(int sensitivity_db)
+{
+
+ if ((sensitivity_db < 0) || (sensitivity_db > 31))
+ return EC_ERROR_INVAL;
+
+ wov_conf.sensitivity_db = sensitivity_db;
+
+ apm_set_vad_sensitivity(sensitivity_db);
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Gets VAD sensitivity.
+ *
+ * @param None.
+ * @return VAD sensitivity in db
+ */
+int wov_get_vad_sensitivity(void)
+{
+ return wov_conf.sensitivity_db;
+}
+
+/**
+ * Configure I2S bus. (Sample rate and size are determined via common
+ * config functions.)
+ *
+ * @param i2s_clock - I2S clock frequency in Hz (needed in order to
+ * configure the internal PLL for 12MHz)
+ * @param format - one of the following: I2S mode, Right Justified mode,
+ * Left Justified mode, PCM A Audio, PCM B Audio and
+ * Time Division Multiplexing
+ * @return EC error code.
+ */
+void wov_set_i2s_config(uint32_t i2s_clock, enum wov_dai_format format)
+{
+ if (wov_conf.mode != WOV_MODE_OFF)
+ return;
+
+ wov_conf.i2s_clock = i2s_clock;
+ wov_conf.dai_format = format;
+}
+
+/**
+ * Configure I2S bus. (Sample rate and size are determined via common
+ * config functions.)
+ *
+ * @param ch0_delay - 0 to 496. Defines the delay from the SYNC till the
+ * first bit (MSB) of channel 0 (left channel)
+ * @param ch1_delay - -1 to 496. Defines the delay from the SYNC till the
+ * first bit (MSB) of channel 1 (right channel).
+ * If channel 1 is not used set this field to -1.
+ *
+ * @param flags - WOV_TDM_ADJACENT_TO_CH0 = (1 << 0). There is a
+ * channel adjacent to channel 0, so float SDAT when
+ * driving the last bit (LSB) of the channel during the
+ * second half of the clock cycle to avoid bus contention.
+ *
+ * WOV_TDM_ADJACENT_TO_CH1 = (1 << 1). There is a channel
+ * adjacent to channel 1.
+ *
+ * @return EC error code.
+ */
+enum ec_error_list wov_set_i2s_tdm_config(int ch0_delay, int ch1_delay,
+ uint32_t flags)
+{
+ if (wov_conf.mode != WOV_MODE_OFF)
+ return EC_ERROR_INVALID_CONFIG;
+
+ if ((ch0_delay < 0) || (ch0_delay > 496) ||
+ (ch1_delay < -1) || (ch1_delay > 496))
+ return EC_ERROR_INVAL;
+
+ wov_conf.i2s_start_delay_0 = ch0_delay;
+ wov_conf.i2s_start_delay_1 = ch1_delay;
+
+ SET_FIELD(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL_I2S_ST_DEL,
+ ch0_delay);
+
+ if (ch1_delay == -1)
+ wov_i2s_channel1_disable(1);
+ else {
+ wov_i2s_channel1_disable(0);
+ SET_FIELD(NPCX_WOV_I2S_CNTL(1), NPCX_WOV_I2S_CNTL_I2S_ST_DEL,
+ ch1_delay);
+ }
+
+ if (flags & 0x0001)
+ SET_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL_I2S_LBHIZ);
+ else
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(0), NPCX_WOV_I2S_CNTL_I2S_LBHIZ);
+
+ if (flags & 0x0002)
+ SET_BIT(NPCX_WOV_I2S_CNTL(1), NPCX_WOV_I2S_CNTL_I2S_LBHIZ);
+ else
+ CLEAR_BIT(NPCX_WOV_I2S_CNTL(1), NPCX_WOV_I2S_CNTL_I2S_LBHIZ);
+
+ /* I2S should be reset in order I2S interface to function correctly. */
+ wov_i2s_fifo_reset();
+
+ return EC_SUCCESS;
+}
+
+static void wov_system_init(void)
+{
+ /* Set WoV module to be operational. */
+ clock_enable_peripheral(CGC_OFFSET_WOV, CGC_WOV_MASK,
+ CGC_MODE_RUN | CGC_MODE_SLEEP);
+ /* Configure pins from GPIOs to WOV */
+ gpio_config_module(MODULE_WOV, 1);
+ wov_init();
+
+ task_enable_irq(NPCX_IRQ_WOV);
+
+ CPRINTS("WoV init done");
+}
+DECLARE_HOOK(HOOK_INIT, wov_system_init, HOOK_PRIO_DEFAULT);
+
+void wov_handle_event(enum wov_events event)
+{
+ enum wov_modes mode;
+
+ mode = wov_get_mode();
+ if (event == WOV_EVENT_DATA_READY) {
+ CPRINTS("ram data ready");
+ if (mode == WOV_MODE_RAM) {
+ wov_set_mode(WOV_MODE_OFF);
+ } else if (mode == WOV_MODE_RAM_AND_I2S) {
+ /* just capture one times on RAM*/
+ wov_stop_ram_capture();
+ }
+ }
+ if (event == WOV_EVENT_VAD)
+ CPRINTS("got vad");
+ if (event == WOV_EVENT_ERROR_CORE_FIFO_OVERRUN)
+ CPRINTS("error: cfifo overrun");
+}
+/* voice data 16Khz 2ch 16bit 1s */
+static int command_wov(int argc, char **argv)
+{
+ static int bit_clk;
+ static enum wov_dai_format i2s_fmt;
+
+ if (argc == 2) {
+ if (strcasecmp(argv[1], "init") == 0) {
+ wov_system_init();
+ return EC_SUCCESS;
+ }
+ if (strcasecmp(argv[1], "cfgget") == 0) {
+ CPRINTS("mode:%d", wov_get_mode());
+ CPRINTS("sample rate:%d", wov_get_sample_rate());
+ CPRINTS("sample bits:%d", wov_get_sample_depth());
+ CPRINTS("mic source:%d", wov_get_mic_source());
+ CPRINTS("vad sensitivity :%d",
+ wov_get_vad_sensitivity());
+ return EC_SUCCESS;
+ }
+ } else if (argc == 3) {
+ if (strcasecmp(argv[1], "cfgsrc") == 0) {
+ if (strcasecmp(argv[2], "mono") == 0)
+ wov_set_mic_source(WOV_SRC_MONO);
+ else if (strcasecmp(argv[2], "stereo") == 0)
+ wov_set_mic_source(WOV_SRC_STEREO);
+ else if (strcasecmp(argv[2], "left") == 0)
+ wov_set_mic_source(WOV_SRC_LEFT);
+ else if (strcasecmp(argv[2], "right") == 0)
+ wov_set_mic_source(WOV_SRC_RIGHT);
+ else
+ return EC_ERROR_INVAL;
+
+ wov_i2s_fifo_reset();
+ return EC_SUCCESS;
+ }
+ if (strcasecmp(argv[1], "cfgbit") == 0) {
+ int bits;
+
+ bits = atoi(argv[2]);
+ if ((bits == 16) || (bits == 18) || (bits == 20) ||
+ (bits == 24)) {
+ return wov_set_sample_depth(bits);
+ }
+ }
+ if (strcasecmp(argv[1], "cfgsfs") == 0) {
+ int fs;
+
+ fs = atoi(argv[2]);
+ return wov_set_sample_rate(fs);
+ }
+ if (strcasecmp(argv[1], "cfgbck") == 0) {
+ int fs;
+
+ fs = wov_get_sample_rate();
+ if (strcasecmp(argv[2], "32fs") == 0)
+ bit_clk = fs * 32;
+ else if (strcasecmp(argv[2], "48fs") == 0)
+ bit_clk = fs * 48;
+ else if (strcasecmp(argv[2], "64fs") == 0)
+ bit_clk = fs * 64;
+ else if (strcasecmp(argv[2], "128fs") == 0)
+ bit_clk = fs * 128;
+ else if (strcasecmp(argv[2], "256fs") == 0)
+ bit_clk = fs * 256;
+ else
+ return EC_ERROR_INVAL;
+
+ wov_set_i2s_config(bit_clk, i2s_fmt);
+ return EC_SUCCESS;
+ }
+ if (strcasecmp(argv[1], "cfgfmt") == 0) {
+ if (strcasecmp(argv[2], "i2s") == 0)
+ i2s_fmt = WOV_DAI_FMT_I2S;
+ else if (strcasecmp(argv[2], "right") == 0)
+ i2s_fmt = WOV_DAI_FMT_RIGHT_J;
+ else if (strcasecmp(argv[2], "left") == 0)
+ i2s_fmt = WOV_DAI_FMT_LEFT_J;
+ else if (strcasecmp(argv[2], "pcma") == 0)
+ i2s_fmt = WOV_DAI_FMT_PCM_A;
+ else if (strcasecmp(argv[2], "pcmb") == 0)
+ i2s_fmt = WOV_DAI_FMT_PCM_B;
+ else if (strcasecmp(argv[2], "tdm") == 0)
+ i2s_fmt = WOV_DAI_FMT_PCM_TDM;
+ else
+ return EC_ERROR_INVAL;
+
+ wov_set_i2s_config(bit_clk, i2s_fmt);
+ return EC_SUCCESS;
+ }
+ if (strcasecmp(argv[1], "cfgdck") == 0) {
+ if (strcasecmp(argv[2], "1.0") == 0)
+ apm_set_adc_dmic_config(APM_DMIC_RATE_1_0);
+ else if (strcasecmp(argv[2], "2.4") == 0)
+ apm_set_adc_dmic_config(APM_DMIC_RATE_2_4);
+ else if (strcasecmp(argv[2], "3.0") == 0)
+ apm_set_adc_dmic_config(APM_DMIC_RATE_3_0);
+ else
+ return EC_ERROR_INVAL;
+ return EC_SUCCESS;
+ }
+ if (strcasecmp(argv[1], "cfgmod") == 0) {
+ if (strcasecmp(argv[2], "off") == 0) {
+ wov_set_mode(WOV_MODE_OFF);
+ wov_stop_ram_capture();
+ } else if (strcasecmp(argv[2], "vad") == 0) {
+ wov_set_mode(WOV_MODE_VAD);
+ } else if (strcasecmp(argv[2], "ram") == 0) {
+ if (wov_set_buffer((uint32_t *)voice_buffer,
+ sizeof(voice_buffer) / sizeof(uint32_t))
+ != EC_SUCCESS)
+ CPRINTS("Init fail: voice buf size");
+ wov_set_mode(WOV_MODE_RAM);
+ wov_start_ram_capture();
+ } else if (strcasecmp(argv[2], "i2s") == 0) {
+ wov_set_mode(WOV_MODE_I2S);
+ wov_stop_ram_capture();
+ } else if (strcasecmp(argv[2], "rami2s") == 0) {
+ if (wov_set_buffer((uint32_t *)voice_buffer,
+ sizeof(voice_buffer) / sizeof(uint32_t))
+ != EC_SUCCESS)
+ CPRINTS("Init fail: voice buf size");
+ wov_set_mode(WOV_MODE_RAM_AND_I2S);
+ wov_start_ram_capture();
+ } else {
+ return EC_ERROR_INVAL;
+ }
+ wov_i2s_fifo_reset();
+ return EC_SUCCESS;
+ }
+ if (strcasecmp(argv[1], "mute") == 0) {
+ if (strcasecmp(argv[2], "enable") == 0) {
+ wov_mute(1);
+ return EC_SUCCESS;
+ }
+ if (strcasecmp(argv[2], "disable") == 0) {
+ wov_mute(0);
+ return EC_SUCCESS;
+ }
+ }
+ if (strcasecmp(argv[1], "vadsens") == 0)
+ return wov_set_vad_sensitivity(atoi(argv[2]));
+
+ if (strcasecmp(argv[1], "gain") == 0) {
+ wov_set_gain(atoi(argv[2]), atoi(argv[2]));
+ return EC_SUCCESS;
+ }
+ } else if (argc == 5) {
+ if (strcasecmp(argv[1], "cfgtdm") == 0) {
+ int delay0, delay1;
+ uint32_t flags;
+
+ delay0 = atoi(argv[2]);
+ delay1 = atoi(argv[3]);
+ flags = atoi(argv[4]);
+ if ((delay0 > 496) || (delay1 > 496) || (flags > 3) ||
+ (delay0 < 0) || (delay1 < 0)) {
+ return EC_ERROR_INVAL;
+ }
+ wov_set_i2s_tdm_config(delay0, delay1, flags);
+ return EC_SUCCESS;
+ }
+ }
+
+ return EC_ERROR_INVAL;
+}
+
+DECLARE_CONSOLE_COMMAND(wov, command_wov,
+ "init\n"
+ "mute <enable|disable>\n"
+ "cfgsrc <mono|stereo|left|right>\n"
+ "cfgbit <16|18|20|24>\n"
+ "cfgsfs <8000|12000|16000|24000|32000|48000>\n"
+ "cfgbck <32fs|48fs|64fs|128fs|256fs>\n"
+ "cfgfmt <i2s|right|left|pcma|pcmb|tdm>\n"
+ "cfgmod <off|vad|ram|i2s|rami2s>\n"
+ "cfgtdm [0~496 0~496 0~3]>\n"
+ "cfgdck <1.0|2.4|3.0>\n"
+ "cfgget\n"
+ "vadsens <0~31>\n"
+ "gain <0~31>",
+ "wov configuration");
diff --git a/chip/npcx/wov_chip.h b/chip/npcx/wov_chip.h
new file mode 100644
index 0000000000..7e2fb520cc
--- /dev/null
+++ b/chip/npcx/wov_chip.h
@@ -0,0 +1,652 @@
+/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* NPCX-specific WOV module for Chrome EC */
+
+#ifndef __CROS_EC_WOV_CHIP_H
+#define __CROS_EC_WOV_CHIP_H
+
+#include "common.h"
+
+/* FMUL2 clock Frequency. */
+enum fmul2_clk_freq {
+ FMUL2_48_MHZ = 0, /* Default */
+ FMUL2_24_MHZ
+};
+
+enum fmul2_clk_divider {
+ FMUL2_CLK_NO_DIVIDER = 0x00,
+ FMUL2_CLK_DIVIDER_BY_2 = 0x01,
+ FMUL2_CLK_DIVIDER_BY_4 = 0x03, /* Default */
+ FMUL2_CLK_DIVIDER_BY_8 = 0x07
+};
+
+/* Microphone source. */
+enum wov_mic_source {
+ /* Only data from left mic. */
+ WOV_SRC_LEFT = 0,
+ /* Only data from right mic. */
+ WOV_SRC_RIGHT,
+ /* Both channels have the same data (average of left & right */
+ WOV_SRC_MONO,
+ /* Each channel has its own data. */
+ WOV_SRC_STEREO
+};
+
+/* Clock source for APM. */
+enum wov_clk_src_sel {
+ WOV_FMUL2_CLK_SRC = 0,
+ WOV_PLL_CLK_SRC = 1
+};
+
+/* FMUL clock division factore. */
+enum wov_fmul_div {
+ WOV_NODIV = 0,
+ WOV_DIV_BY_2,
+ WOV_DIV_BY_4, /* Default value */
+ WOV_DIV_BY_8
+};
+
+/* Lock state. */
+enum wov_lock_state {
+ WOV_UNLOCK = 0,
+ WOV_LOCK = 1
+};
+
+/* Reference clock source select. */
+enum wov_ref_clk_src_sel {
+ WOV_FREE_RUN_OSCILLATOR = 0,
+ WOV_CRYSTAL_OSCILLATOR = 1
+};
+
+/* PLL external divider select. */
+enum wov_ext_div_sel {
+ WOV_EXT_DIV_BINARY_CNT = 0,
+ WOV_EXT_DIV_LFSR_DIV = 1
+};
+
+/* FMUL output frequency. */
+enum wov_fmul_out_freq {
+ WOV_FMUL_OUT_FREQ_48_MHZ = 0,
+ WOV_FMUL_OUT_FREQ_49_MHZ = 1
+};
+
+/* Digital microphone clock divider select. */
+enum wov_dmic_clk_div_sel {
+ WOV_DMIC_DIV_DISABLE = 1,
+ WOV_DMIC_DIV_BY_2 = 2,
+ WOV_DMIC_DIV_BY_4 = 4
+};
+
+/* FIFO threshold. */
+enum wov_fifo_threshold {
+ WOV_FIFO_THRESHOLD_1_DATA_WORD = 0,
+ WOV_FIFO_THRESHOLD_2_DATA_WORDS = 1,
+ WOV_FIFO_THRESHOLD_4_DATA_WORDS = 2,
+ WOV_FIFO_THRESHOLD_8_DATA_WORDS = 4,
+ WOV_FIFO_THRESHOLD_16_DATA_WORDS = 8,
+ WOV_FIFO_THRESHOLD_32_DATA_WORDS = 16,
+ WOV_FIFO_THRESHOLD_40_DATA_WORDS = 20,
+ WOV_FIFO_THRESHOLD_64_DATA_WORDS = 32,
+ WOV_FIFO_THRESHOLD_80_DATA_WORDS = 40,
+ WOV_FIFO_THRESHOLD_96_DATA_WORDS = 48
+};
+
+/* FIFO DMA request select. */
+enum wov_fifo_dma_req_sel {
+ WOV_FIFO_DMA_DFLT_DMA_REQ_CONN = 0,
+ WOV_FIFO_DMA_DMA_REQ_CON_FIFO
+};
+
+/* FIFO operational state. */
+enum wov_fifo_oper_state {
+ WOV_FIFO_OPERATIONAL = 0,
+ WOV_FIFO_RESET, /* Default */
+};
+
+/* WoV interrupt index. */
+enum wov_interrupt_index {
+ WOV_VAD_INT_INDX,
+ WOV_VAD_WAKE_INDX,
+ WOV_CFIFO_NOT_EMPTY_INDX,
+ WOV_CFIFO_THRESHOLD_INT_INDX,
+ WOV_CFIFO_THRESHOLD_WAKE_INDX,
+ WOV_CFIFO_OVERRUN_INT_INDX,
+ WOV_I2SFIFO_OVERRUN_INT_INDX,
+ WOV_I2SFIFO_UNDERRUN_INT_INDX
+};
+
+/* FIFO DMA request selection. */
+enum wov_dma_req_sel {
+ WOV_DFLT_ESPI_DMA_REQ = 0,
+ WOV_FROM_FIFO_DMA_REQUEST
+};
+
+/* Core FIFO input select. */
+enum wov_core_fifo_in_sel {
+ WOV_CFIFO_IN_LEFT_CHAN_2_CONS_16_BITS = 0, /* Default */
+ WOV_CFIFO_IN_LEFT_RIGHT_CHAN_16_BITS,
+ WOV_CFIFO_IN_LEFT_CHAN_24_BITS,
+ WOV_CFIFO_IN_LEFT_RIGHT_CHAN_24_BITS
+};
+
+/* PLL external divider selector. */
+enum wov_pll_ext_div_sel {
+ WOV_PLL_EXT_DIV_BIN_CNT = 0,
+ WOV_PLL_EXT_DIV_LFSR
+};
+
+/* Code for events for call back function. */
+enum wov_events {
+ WOV_NO_EVENT = 0,
+ /*
+ * Data is ready.
+ * need to call to wov_set_buffer to update the buffer * pointer
+ */
+ WOV_EVENT_DATA_READY = 1,
+ WOV_EVENT_VAD, /* Voice activity detected */
+
+ WOV_EVENT_ERROR_FIRST = 128,
+ WOV_EVENT_ERROR_CORE_FIFO_OVERRUN = 128,
+ WOV_EVENT_ERROR_I2S_FIFO_UNDERRUN = 129,
+ WOV_EVENT_ERROR_I2S_FIFO_OVERRUN = 130,
+ WOV_EVENT_ERROR_LAST = 255,
+
+};
+
+/* WoV FIFO errors. */
+enum wov_fifo_errors {
+ WOV_FIFO_NO_ERROR = 0,
+ WOV_CORE_FIFO_OVERRUN = 1, /* 2 : I2S FIFO is underrun. */
+ WOV_I2S_FIFO_OVERRUN = 2, /* 3 : I2S FIFO is overrun. */
+ WOV_I2S_FIFO_UNDERRUN = 3 /* 4 : I2S FIFO is underrun. */
+
+};
+
+/* Selects I2S test mode. */
+enum wov_test_mode { WOV_NORMAL_MODE = 0, WOV_TEST_MODE };
+
+/* PULL_UP/PULL_DOWN selection. */
+enum wov_pull_upd_down_sel { WOV_PULL_DOWN = 0, WOV_PULL_UP };
+
+/* I2S output data floating mode. */
+enum wov_floating_mode { WOV_FLOATING_DRIVEN = 0, WOV_FLOATING };
+
+/* Clock inverted mode. */
+enum wov_clk_inverted_mode { WOV_CLK_NORMAL = 0, WOV_CLK_INVERTED };
+
+enum wov_i2s_chan_trigger {
+ WOV_I2S_SAMPLED_1_AFTER_0 = 0,
+ WOV_I2S_SAMPLED_0_AFTER_1 = 1
+};
+
+/* APM modes. */
+enum wov_modes {
+ WOV_MODE_OFF = 1,
+ WOV_MODE_VAD,
+ WOV_MODE_RAM,
+ WOV_MODE_I2S,
+ WOV_MODE_RAM_AND_I2S
+};
+
+/* DAI format. */
+enum wov_dai_format {
+ WOV_DAI_FMT_I2S, /* I2S mode */
+ WOV_DAI_FMT_RIGHT_J, /* Right Justified mode */
+ WOV_DAI_FMT_LEFT_J, /* Left Justified mode */
+ WOV_DAI_FMT_PCM_A, /* PCM A Audio */
+ WOV_DAI_FMT_PCM_B, /* PCM B Audio */
+ WOV_DAI_FMT_PCM_TDM /* Time Division Multiplexing */
+};
+
+struct wov_config {
+ enum wov_modes mode;
+ uint32_t sample_per_sec;
+ int bit_depth;
+ enum wov_mic_source mic_src;
+ int left_chan_gain;
+ int rigth_chan_gain;
+ uint16_t i2s_start_delay_0;
+ uint16_t i2s_start_delay_1;
+ uint32_t i2s_clock;
+ enum wov_dai_format dai_format;
+ int sensitivity_db;
+};
+
+extern struct wov_config wov_conf;
+
+/**
+ * Set FMUL2 clock divider.
+ *
+ * @param None
+ * @return None
+ */
+void wov_fmul2_set_clk_divider(enum fmul2_clk_divider clk_div);
+
+/**
+ * WoV Call back function decleration.
+ *
+ * @param event - the event that cause the call to the callback
+ * function.
+ *
+ * @return None
+ */
+typedef void (*wov_call_back_t)(enum wov_events);
+
+/*
+ * WoV macros.
+ */
+
+/* MACROs that set fields of the Clock Control Register structure. */
+#define WOV_APM_CLK_SRC_FMUL2(reg_val) reg_val.clk_sel = 0
+#define WOV_APM_CLK_SRC_PLL(reg_val) reg_val.clk_sel = 1
+#define WOV_APM_GET_CLK_SRC(reg_val) (reg_val.clk_sel)
+
+/* Core FIFO threshold. */
+#define WOV_GET_CORE_FIFO_THRESHOLD WOV_GET_FIFO_INT_THRESHOLD
+
+/******************************************************************************
+ *
+ * WoV APIs
+ *
+ ******************************************************************************/
+
+/**
+ * Initiates WoV.
+ *
+ * @param callback - Pointer to callback function.
+ *
+ * @return None
+ */
+void wov_init(void);
+
+/**
+ * Sets WoV stage
+ *
+ * @param wov_mode - WoV stage (Table 38)
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_set_mode(enum wov_modes wov_mode);
+
+/**
+ * Gets WoV mode
+ *
+ * @param None
+ * @return WoV mode
+ */
+enum wov_modes wov_get_mode(void);
+
+/**
+ * Configure WoV.
+ *
+ * @param samples_per_second - Valid sample rate.
+ * @return In case sample rate is valid return EC_SUCCESS othewise return
+ * error code.
+ */
+int wov_set_sample_rate(uint32_t samples_per_second);
+
+/**
+ * Gets sampling rate.
+ *
+ * @param None
+ * @return the current sampling rate.
+ */
+uint32_t wov_get_sample_rate(void);
+
+/**
+ * Sets sampling depth.
+ *
+ * @param bits_num - Valid sample depth in bits.
+ * @return In case sample depth is valid return EC_SUCCESS othewise return
+ * error code.
+ */
+int wov_set_sample_depth(int bits_num);
+
+/**
+ * Gets sampling depth.
+ *
+ * @param None.
+ * @return sample depth in bits.
+ */
+int wov_get_sample_depth(void);
+
+/**
+ * Sets microphone source.
+ *
+ * @param mic_src - Valid microphone source
+ * @return return EC_SUCCESS if mic source valid othewise
+ * return error code.
+ */
+int wov_set_mic_source(enum wov_mic_source mic_src);
+
+/**
+ * Gets microphone source.
+ *
+ * @param None.
+ * @return sample depth in bits.
+ */
+enum wov_mic_source wov_get_mic_source(void);
+
+/**
+ * Mutes the WoV.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return None
+ */
+void wov_mute(int enable);
+
+/**
+ * Sets gain
+ *
+ * @param left_chan_gain - Left channel gain.
+ * @param right_chan_gain - Right channel gain
+ * @return None
+ */
+void wov_set_gain(int left_chan_gain, int right_chan_gain);
+
+/**
+ * Enables/Disables ADC.
+ *
+ * @param enable - enabled flag, true means enable
+ * @return None
+ */
+void wov_enable_agc(int enable);
+
+/**
+ * Enables/Disables the automatic gain.
+ *
+ * @param stereo - Stereo enabled flag, 1 means enable.
+ * @param target - Target output level of the ADC.
+ * @param noise_gate_threshold - Noise Gate system select. 1 means enable.
+ * @param hold_time - Hold time before starting AGC adjustment to
+ * the TARGET value.
+ * @param attack_time - Attack time - gain ramp down.
+ * @param decay_time - Decay time - gain ramp up.
+ * @param max_applied_gain - Maximum Gain Value to apply to the ADC path.
+ * @param min_applied_gain - Minimum Gain Value to apply to the ADC path.
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_set_agc_config(int stereo, float target,
+ int noise_gate_threshold, uint8_t hold_time,
+ uint16_t attack_time, uint16_t decay_time,
+ float max_applied_gain, float min_applied_gain);
+
+/**
+ * Sets VAD sensitivity.
+ *
+ * @param sensitivity_db - VAD sensitivity in db.
+ * @return None
+ */
+int wov_set_vad_sensitivity(int sensitivity_db);
+
+/**
+ * Gets VAD sensitivity.
+ *
+ * @param None.
+ * @return VAD sensitivity in db
+ */
+int wov_get_vad_sensitivity(void);
+
+/**
+ * Configure I2S bus. (Sample rate and size are determined via common
+ * config functions.)
+ *
+ * @param i2s_clock - I2S clock frequency in Hz (needed in order to
+ * configure the internal PLL for 12MHz)
+ * @param format - one of the following: I2S mode, Right Justified mode,
+ * Left Justified mode, PCM A Audio, PCM B Audio and
+ * Time Division Multiplexing
+ * @return EC error code.
+ */
+void wov_set_i2s_config(uint32_t i2s_clock, enum wov_dai_format format);
+
+/**
+ * Configure I2S bus. (Sample rate and size are determined via common
+ * config functions.)
+ *
+ * @param ch0_delay - 0 to 496. Defines the delay from the SYNC till the
+ * first bit (MSB) of channel 0 (left channel)
+ * @param ch1_delay - -1 to 496. Defines the delay from the SYNC till the
+ * first bit (MSB) of channel 1 (right channel).
+ * If channel 1 is not used set this field to -1.
+ *
+ * @param flags - WOV_TDM_ADJACENT_TO_CH0 = (1 << 0). There is a
+ * channel adjacent to channel 0, so float SDAT when
+ * driving the last bit (LSB) of the channel during the
+ * second half of the clock cycle to avoid bus contention.
+ *
+ * WOV_TDM_ADJACENT_TO_CH1 = (1 << 1). There is a channel
+ * adjacent to channel 1.
+ *
+ * @return EC error code.
+ */
+enum ec_error_list wov_set_i2s_tdm_config(int ch0_delay, int ch1_delay,
+ uint32_t flags);
+
+/**
+ * Configure FMUL2 clock tunning.
+ *
+ * @param None
+ * @return None
+ */
+void wov_fmul2_conf_tuning(void);
+
+/**
+ * Configure DMIC clock.
+ *
+ * @param enable - DMIC enabled , true means enable
+ * @param clk_div - DMIC clock division factor (disable, divide by 2
+ * divide by 4)
+ * @return None
+ */
+void wov_dmic_clk_config(int enable, enum wov_dmic_clk_div_sel clk_div);
+
+/**
+ * FMUL2 clock control configuration.
+ *
+ * @param clk_src - select between FMUL2 (WOV_FMUL2_CLK_SRC) and
+ * PLL (WOV_PLL_CLK_SRC)
+ * @return None
+ */
+extern void wov_set_clk_selection(enum wov_clk_src_sel clk_src);
+
+/**
+ * Configure PLL clock.
+ *
+ * @param ext_div_sel - PLL external divider selector.
+ * @param div_factor - When ext_div_sel is WOV_PLL_EXT_DIV_BIN_CNT
+ * then it is the 4 LSBits of this field,
+ * otherwise this field is an index to
+ * PLL External Divider Load Values table.
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_pll_clk_ext_div_config(
+ enum wov_pll_ext_div_sel ext_div_sel, uint32_t div_factor);
+
+/**
+ * PLL power down.
+ *
+ * @param enable - true power down the PLL or false PLL operating
+ * @return None
+ */
+void wov_pll_enable(int enable);
+
+/**
+ * Configures PLL clock dividers..
+ *
+ * @param out_div_1 - PLL output divider #1, valid values 1-7
+ * @param out_div_2 - PLL output divider #2, valid values 1-7
+ * @param feedback_div - PLL feadback divider (Default is 375 decimal)
+ * @param in_div - PLL input divider
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_pll_clk_div_config(uint32_t out_div_1,
+ uint32_t out_div_2,
+ uint32_t feedback_div,
+ uint32_t in_div);
+
+/**
+ * Enables/Disables WoV interrupt.
+ *
+ * @param int_index - Interrupt ID.
+ * @param enable - enabled flag, 1 means enable
+ *
+ * @return None.
+ */
+void wov_interrupt_enable(enum wov_interrupt_index int_index, int enable);
+
+/**
+ * Sets core FIFO threshold.
+ *
+ * @param in_sel - Core FIFO input select
+ * @param threshold - Core FIFO threshold
+ *
+ * @return None
+ */
+void wov_cfifo_config(enum wov_core_fifo_in_sel in_sel,
+ enum wov_fifo_threshold threshold);
+
+/**
+ * Start the actual capturing of the Voice data to the RAM.
+ * Note that the pointer to the RAM buffer must be precisely
+ * set by calling wov_set_buffer();
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_start_ram_capture(void);
+
+/**
+ * Stop the capturing of the Voice data to the RAM.
+ *
+ * @param none
+ *
+ * @return None
+ */
+void wov_stop_ram_capture(void);
+
+/**
+ * Rests the Core FIFO.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_core_fifo_reset(void);
+
+/**
+ * Rests the I2S FIFO.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_i2s_fifo_reset(void);
+
+/**
+ * Start the capturing of the Voice data via I2S.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_start_i2s_capture(void);
+
+/**
+ * Stop the capturing of the Voice data via I2S.
+ *
+ * @param none
+ *
+ * @return None
+ */
+void wov_stop_i2s_capture(void);
+
+/**
+ * Reads data from the core fifo.
+ *
+ * @param num_elements - Number of elements (Dword) to read.
+ *
+ * @return None
+ */
+void wov_cfifo_read_handler(uint32_t num_elements);
+
+/**
+ * WoV interrupt handler.
+ *
+ * @param None
+ *
+ * @return None
+ */
+void wov_interrupt_handler(void);
+
+/**
+ * Sets data buffer for reading from core FIFO
+ *
+ * @param buff - Pointer to the read buffer, buffer must be 32 bits
+ * aligned.
+ * @param size_in_words - Size must be a multiple of CONFIG_WOV_THRESHOLD_WORDS
+ * (defaulte = 80 words)
+ *
+ * @return None
+ *
+ * Note - When the data buffer will be full the FW will be notifyed
+ * about it, and the FW will need to recall to this function.
+ */
+int wov_set_buffer(uint32_t *buff, int size_in_words);
+
+/**
+ * Resets the APM.
+ *
+ * @param enable - enabled flag, true or false
+ * @return None
+ */
+void wov_apm_active(int enable);
+
+void wov_handle_event(enum wov_events event);
+
+/**
+ * I2S golobal configuration
+ *
+ * @param i2s_hiz_data - Defines when the I2S data output is floating.
+ * @param i2s_hiz - Defines if the I2S data output is always floating.
+ * @param clk_invert - Defines the I2S bit clock edge sensitivity
+ * @param out_pull_en - Enable a pull-up or a pull-down resistor on
+ * I2S output
+ * @param out_pull_mode - Select a pull-up or a pull-down resistor on
+ * I2S output
+ * @param in_pull_en - Enable a pull-up or a pull-down resistor on
+ * I2S input
+ * @param in_pull_mode - Select a pull-up or a pull-down resistor on
+ * I2S intput
+ * @param test_mode - Selects I2S test mode
+ *
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_i2s_global_config(
+ enum wov_floating_mode i2s_hiz_data,
+ enum wov_floating_mode i2s_hiz,
+ enum wov_clk_inverted_mode clk_invert,
+ int out_pull_en, enum wov_pull_upd_down_sel out_pull_mode,
+ int in_pull_en,
+ enum wov_pull_upd_down_sel in_pull_mode,
+ enum wov_test_mode test_mode);
+
+/**
+ * I2S channel configuration
+ *
+ * @param channel_num - I2S channel number, 0 or 1.
+ * @param bit_count - I2S channel bit count.
+ * @param trigger - Define the I2S chanel trigger 1->0 or 0->1
+ * @param start_delay - Defines the delay from the trigger defined for
+ * the channel till the first bit (MSB) of the data.
+ *
+ * @return EC_ERROR_INVAL or EC_SUCCESS
+ */
+enum ec_error_list wov_i2s_channel_config(uint32_t channel_num,
+ uint32_t bit_count, enum wov_i2s_chan_trigger trigger,
+ int32_t start_delay);
+
+#endif /* __CROS_EC_WOV_CHIP_H */
diff --git a/include/common.h b/include/common.h
index fff9415240..334b7a6111 100644
--- a/include/common.h
+++ b/include/common.h
@@ -145,6 +145,8 @@ enum ec_error_list {
EC_ERROR_UNCHANGED = 22,
/* Memory allocation */
EC_ERROR_MEMORY_ALLOCATION = 23,
+ /* Invalid to configure in the current module mode/stage */
+ EC_ERROR_INVALID_CONFIG = 24,
/* Verified boot errors */
EC_ERROR_VBOOT_SIGNATURE = 0x1000, /* 4096 */
diff --git a/include/console_channel.inc b/include/console_channel.inc
index 0d3f7a07f9..b8412cf335 100644
--- a/include/console_channel.inc
+++ b/include/console_channel.inc
@@ -89,4 +89,7 @@ CONSOLE_CHANNEL(CC_USBCHARGE, "usbcharge")
CONSOLE_CHANNEL(CC_USBPD, "usbpd")
#endif
CONSOLE_CHANNEL(CC_VBOOT, "vboot")
+#ifdef CONFIG_WAKE_ON_VOICE
+CONSOLE_CHANNEL(CC_WOV, "wov")
+#endif
CONSOLE_CHANNEL(CC_HOOK, "hook")
diff --git a/include/module_id.h b/include/module_id.h
index 3e73f3c4a2..aac2cb6186 100644
--- a/include/module_id.h
+++ b/include/module_id.h
@@ -51,6 +51,7 @@ enum module_id {
MODULE_VBOOT,
MODULE_MCO,
MODULE_TFDP,
+ MODULE_WOV,
/* Module count; not an actual module */
MODULE_COUNT
diff --git a/util/flash_ec b/util/flash_ec
index 211a089be3..e7b178979c 100755
--- a/util/flash_ec
+++ b/util/flash_ec
@@ -105,10 +105,10 @@ BOARDS_NPCX_5M6G_JTAG=(
)
BOARDS_NPCX_7M6X_JTAG=(
- npcx7_evb
)
BOARDS_NPCX_7M7X_JTAG=(
+ npcx7_evb
)
BOARDS_NPCX_SPI=(