diff options
-rw-r--r-- | board/npcx7_evb/gpio.inc | 6 | ||||
-rw-r--r-- | chip/npcx/apm.c | 620 | ||||
-rw-r--r-- | chip/npcx/apm_chip.h | 418 | ||||
-rw-r--r-- | chip/npcx/build.mk | 1 | ||||
-rw-r--r-- | chip/npcx/gpio_chip-npcx7.h | 22 | ||||
-rw-r--r-- | chip/npcx/registers.h | 192 | ||||
-rw-r--r-- | chip/npcx/system.c | 6 | ||||
-rw-r--r-- | chip/npcx/wov.c | 1848 | ||||
-rw-r--r-- | chip/npcx/wov_chip.h | 652 | ||||
-rw-r--r-- | include/common.h | 2 | ||||
-rw-r--r-- | include/console_channel.inc | 3 | ||||
-rw-r--r-- | include/module_id.h | 1 | ||||
-rwxr-xr-x | util/flash_ec | 2 |
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=( |