diff options
author | CHLin <CHLIN56@nuvoton.com> | 2018-03-01 16:00:08 +0800 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2018-09-18 15:38:04 +0000 |
commit | 9ff66b6e3c9adae9eed838053a81a67f4dc62f9b (patch) | |
tree | c87489b18fb17f3acf34c168a41250ef4802d377 | |
parent | 2cfc7b163ff0b917097d2c360d2ffc23cc632d92 (diff) | |
download | chrome-ec-9ff66b6e3c9adae9eed838053a81a67f4dc62f9b.tar.gz |
npcx: WoV: enhance the WoV driver and fix bugs
Firmware bugs fix:
----------------------------------------------------------------------
1. wov mode change is malfunctional.
2. wov_set_mic_source set in VAD_0.VAD_INSEL field 0x3 when requested
wov_mic_source is WOV_SRC_STEREO.
3. wov_set_mic_source doesn't disable I2S channel 1 when using only
left or right channel.
4. wov_start/stop_ram_capture is called when needed by the driver,
it is used in wov_set_mode.
5. Never activate Automatic wake-up enabled. VAD_0.VAD_ADC_WAKEUP
remain '0' forever.
6. Set DMIC clock signal output to use fast transitions.
(set DEVALTE.DMCLK_FAST to be '1').
7. for VAD and RAM modes, DMIC clock setting should be 750 KHz.
8. for I2S mode (and RAM and I2S mode), DMIC clock should be 3 MHz.
9. fix issue "WoV driver may cause unsynchronized data in the I2S output"
10. fMUL2 clock tuning to LFCLK reference remain enabled when FMUL2 is enabled.
11. core-FIFO status bits in WOV_STATUS register are cleared when FIFO is reset.
12. i2S-FIFO status bits in WOV_STATUS register are cleared when FIFO is reset.
13. reset I2S FIFO when FIFO is underrun.
14. increase delay to 1msec when resets the FIFOs.
15. set MIC source prior start capturing data.
16. fix issue "enables ADC path in VAD mode when it isn't needed"
17. Increase delay in all places from 10Usec to 100Usec
Firmware enhancement:
----------------------------------------------------------------------
1. Add support for DMIC clock rate of 750 KHz and 1.2 MHz.
2. Add console command to enable/disable fmul2 tunning.
> wov fmul2 <enable|disable>
3. Originally, the console command "wov cfgmod ram" will tie the
function wov_set_mode(WOV_MODE_RAM) and start RAM capture together.
In the CL, we split it into two console commands:
> wov cfgmod ram
> wov capram
4. Add APIs to set DMIC clk rate for different mode (VAD/RAM/I2S) and
thier related console commands.
> cfgdckV <0.75|1.0|1.2|2.4|3.0>
> cfgdckR <0.75|1.0|1.2|2.4|3.0>
> cfgdckI <0.75|1.0|1.2|2.4|3.0>
This change allows to modify setting (ex: fmul2 tunning on/off) after
the wov mode is set to RAM and before the voice capture to RAM starts.
BRANCH=none
BUG=b:74600211, b:74617334, b:72213375
TEST=No build errors for make buildall.
TEST=Test bugs described above are fixed.
TEST=Test enhancement described above is well functional.
Change-Id: Id97b51fbd3e6e495d48aedf000a427538d91adf7
Signed-off-by: Dror Goldstein <dror.goldstein@nuvoton.com>
Signed-off-by: Simon Liang <CMLiang@nuvoton.com>
Signed-off-by: CHLin <CHLIN56@nuvoton.com>
Reviewed-on: https://chromium-review.googlesource.com/942286
Commit-Ready: CH Lin <chlin56@nuvoton.corp-partner.google.com>
Tested-by: CH Lin <chlin56@nuvoton.corp-partner.google.com>
Tested-by: Scott Collyer <scollyer@chromium.org>
Reviewed-by: Scott Collyer <scollyer@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1230974
Reviewed-by: Martin Roth <martinroth@chromium.org>
Commit-Queue: Martin Roth <martinroth@chromium.org>
Tested-by: Martin Roth <martinroth@chromium.org>
-rw-r--r-- | chip/npcx/apm.c | 130 | ||||
-rw-r--r-- | chip/npcx/apm_chip.h | 67 | ||||
-rw-r--r-- | chip/npcx/registers.h | 1 | ||||
-rw-r--r-- | chip/npcx/wov.c | 347 | ||||
-rw-r--r-- | chip/npcx/wov_chip.h | 13 |
5 files changed, 401 insertions, 157 deletions
diff --git a/chip/npcx/apm.c b/chip/npcx/apm.c index a394d0a06a..4ab64774c1 100644 --- a/chip/npcx/apm.c +++ b/chip/npcx/apm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved. +/* Copyright 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. */ @@ -81,7 +81,15 @@ static void apm_write_indirect_data(enum apm_indirect_reg_offset reg_offset, */ 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); + if (rate == APM_DMIC_RATE_0_75) + SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_RATE, + APM_DMIC_RATE_3_0); + else if (rate == APM_DMIC_RATE_1_2) + SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_RATE, + APM_DMIC_RATE_2_4); + else + SET_FIELD(NPCX_APM_CR_DMIC, NPCX_APM_CR_DMIC_ADC_DMIC_RATE, + rate); } /** @@ -98,11 +106,21 @@ void apm_set_vad_dmic_rate_l(enum apm_dmic_rate rate) 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); + if (rate == APM_DMIC_RATE_0_75) + SET_FIELD(vad_data, NPCX_VAD_0_VAD_DMIC_FREQ, + APM_DMIC_RATE_3_0); + else if (rate == APM_DMIC_RATE_1_2) + SET_FIELD(vad_data, NPCX_VAD_0_VAD_DMIC_FREQ, + APM_DMIC_RATE_2_4); + else + 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); } +/*****************************************************************************/ +/* IC specific low-level driver */ + /** * Translates from ADC real value to frequency code * @@ -140,9 +158,6 @@ static enum apm_adc_frequency apm_adc_freq_val_2_code(uint32_t adc_freq_val) return freq_code; } -/*****************************************************************************/ -/* IC specific low-level driver */ - /** * Initiate APM module local parameters.. * @@ -151,7 +166,8 @@ static enum apm_adc_frequency apm_adc_freq_val_2_code(uint32_t adc_freq_val) */ void apm_init(void) { - apm_conf.adc_dmic_rate = APM_DMIC_RATE_3_0; + apm_conf.adc_ram_dmic_rate = APM_DMIC_RATE_0_75; + apm_conf.adc_i2s_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; @@ -202,6 +218,23 @@ void apm_enable_vad_interrupt(int enable) } /** + * Enable/Disable the WoV in the ADC. + * + * @param enable - enabled flag, 1 means enable + * @return None + */ +void apm_adc_wov_enable(int enable) +{ + if (enable) { + SET_FIELD(NPCX_APM_AICR_ADC, + NPCX_APM_AICR_ADC_ADC_AUDIOIF, 0x00); + } else { + SET_FIELD(NPCX_APM_AICR_ADC, + NPCX_APM_AICR_ADC_ADC_AUDIOIF, 0x03); + } +} + +/** * Enables/Disables ADC. * * @param enable - enabled flag, 1 means enable @@ -268,17 +301,49 @@ void apm_dmic_enable(int enable) } /** - * Sets the ADC DMIC rate. + * Sets the RAM ADC DMIC rate. + * + * @param rate - ADC digital microphone rate + * @return None + */ +void apm_set_adc_ram_dmic_config(enum apm_dmic_rate rate) +{ + apm_conf.adc_ram_dmic_rate = rate; +} + +/** + * Gets the RAM ADC DMIC rate. + * + * @param None + * @return ADC digital microphone rate code. + */ +enum apm_dmic_rate apm_get_adc_ram_dmic_rate(void) +{ + return apm_conf.adc_ram_dmic_rate; +} + +/** + * Sets the ADC I2S DMIC rate. * * @param rate - ADC digital microphone rate * @return None */ -void apm_set_adc_dmic_config(enum apm_dmic_rate rate) +void apm_set_adc_i2s_dmic_config(enum apm_dmic_rate rate) { - apm_conf.adc_dmic_rate = rate; + apm_conf.adc_i2s_dmic_rate = rate; } /** + * Gets the ADC I2S DMIC rate. + * + * @param None + * @return ADC digital microphone rate code. + */ +enum apm_dmic_rate apm_get_adc_i2s_dmic_rate(void) +{ + return apm_conf.adc_i2s_dmic_rate; +} +/** * Configures Digital Mixer * * @param mix_left - Mixer left channel output selection on ADC path. @@ -305,9 +370,9 @@ void apm_digital_mixer_config(enum apm_dig_mix mix_left, void apm_vad_enable(int enable) { if (enable) - SET_BIT(NPCX_APM_CR_VAD, NPCX_APM_CR_VAD_VAD_EN); + NPCX_APM_CR_VAD = 0x80; else - CLEAR_BIT(NPCX_APM_CR_VAD, NPCX_APM_CR_VAD_VAD_EN); + NPCX_APM_CR_VAD = 0x00; } /** @@ -344,6 +409,18 @@ void apm_set_vad_dmic_rate(enum apm_dmic_rate rate) } /** + * Gets VAD DMIC rate. + * + * @param None + * + * @return ADC digital microphone rate code. + */ +enum apm_dmic_rate apm_get_vad_dmic_rate(void) +{ + return apm_conf.vad_dmic_rate; +} + +/** * Sets VAD Input chanel. * * @param chan_src - Processed digital microphone channel @@ -450,9 +527,9 @@ enum ec_error_list apm_adc_gain_config(enum apm_adc_gain_coupling gain_coupling, 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); + NPCX_APM_CR_ADC_AGC = 0x80; else - CLEAR_BIT(NPCX_APM_CR_ADC_AGC, NPCX_APM_CR_ADC_AGC_ADC_AGC_EN); + NPCX_APM_CR_ADC_AGC = 0x00; } /** @@ -499,7 +576,7 @@ enum ec_error_list apm_adc_auto_gain_config( gain_data = 0; - if (gain_cfg->stereo_enable) + if (gain_cfg->nois_gate_en) SET_BIT(gain_data, NPCX_ADC_AGC_1_NG_EN); else CLEAR_BIT(gain_data, NPCX_ADC_AGC_1_NG_EN); @@ -547,25 +624,23 @@ void apm_set_mode(enum wov_modes wov_mode) 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); + apm_vad_enable(0); wov_apm_active(0); break; case WOV_MODE_VAD: apm_clear_vad_detected_bit(); wov_apm_active(1); + apm_dmic_enable(1); + apm_adc_wov_enable(1); + apm_set_vad_dmic_rate_l(apm_conf.vad_dmic_rate); + apm_set_vad_sensitivity(wov_conf.sensitivity_db); + apm_enable_vad_interrupt(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: @@ -574,10 +649,12 @@ void apm_set_mode(enum wov_modes wov_mode) wov_apm_active(1); apm_vad_enable(0); apm_enable_vad_interrupt(0); - apm_set_adc_dmic_config_l(apm_conf.adc_dmic_rate); + if (wov_mode == WOV_MODE_RAM) + apm_set_adc_dmic_config_l(apm_conf.adc_ram_dmic_rate); + else + apm_set_adc_dmic_config_l(apm_conf.adc_i2s_dmic_rate); apm_dmic_enable(1); apm_adc_enable(1); - apm_vad_adc_wakeup_enable(0); break; default: @@ -587,7 +664,6 @@ void apm_set_mode(enum wov_modes wov_mode) apm_enable_vad_interrupt(0); apm_dmic_enable(0); apm_adc_enable(0); - apm_vad_adc_wakeup_enable(0); wov_apm_active(0); break; } @@ -615,6 +691,4 @@ 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 index 95e69aba28..ad62538374 100644 --- a/chip/npcx/apm_chip.h +++ b/chip/npcx/apm_chip.h @@ -1,10 +1,8 @@ -/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved. +/* Copyright 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 @@ -67,9 +65,12 @@ enum apm_dmic_src { /* 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. */ + /* 3.0, 2.4 & 1.0 must be 0, 1 & 2 respectively */ + 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. */ + APM_DMIC_RATE_1_2, /* 1.2 MHz. */ + APM_DMIC_RATE_0_75 /* 750 KHz. */ }; /* Digitla mixer output. */ @@ -212,7 +213,8 @@ enum apm_adc_data_length { struct apm_config { enum apm_dmic_rate vad_dmic_rate; - enum apm_dmic_rate adc_dmic_rate; + enum apm_dmic_rate adc_ram_dmic_rate; + enum apm_dmic_rate adc_i2s_dmic_rate; enum apm_adc_gain_coupling gain_coupling; uint8_t left_chan_gain; uint8_t right_chan_gain; @@ -229,13 +231,40 @@ struct apm_auto_gain_config { enum apm_gain_values gain_min; }; +/*****************************************************************************/ +/* IC specific low-level driver */ +enum wov_modes; /** - * Sets the ADC DMIC rate. + * Sets the RAM ADC DMIC rate. * * @param rate - ADC digital microphone rate * @return None */ -void apm_set_adc_dmic_config(enum apm_dmic_rate rate); +void apm_set_adc_ram_dmic_config(enum apm_dmic_rate rate); + +/** + * Gets the RAM ADC DMIC rate. + * + * @param None + * @return ADC digital microphone rate code. + */ +enum apm_dmic_rate apm_get_adc_ram_dmic_rate(void); + +/** + * Sets the ADC I2S DMIC rate. + * + * @param rate - ADC digital microphone rate + * @return None + */ +void apm_set_adc_i2s_dmic_config(enum apm_dmic_rate rate); + +/** + * Gets the ADC I2S DMIC rate. + * + * @param None + * @return ADC digital microphone rate code. + */ +enum apm_dmic_rate apm_get_adc_i2s_dmic_rate(void); /** * Sets VAD DMIC rate. @@ -246,9 +275,23 @@ void apm_set_adc_dmic_config(enum apm_dmic_rate rate); */ void apm_set_vad_dmic_rate(enum apm_dmic_rate rate); -/*****************************************************************************/ -/* IC specific low-level driver */ -enum wov_modes; +/** + * Gets VAD DMIC rate. + * + * @param None + * + * @return ADC digital microphone rate code. + */ +enum apm_dmic_rate apm_get_vad_dmic_rate(void); + +/** + * Gets the ADC DMIC rate. + * + * @param None + * @return ADC digital microphone rate code. + */ +enum apm_dmic_rate apm_get_adc_dmic_rate(void); + /** * Initiate APM module local parameters.. * diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h index c126647941..48b6b21c9b 100644 --- a/chip/npcx/registers.h +++ b/chip/npcx/registers.h @@ -747,6 +747,7 @@ enum { #ifdef NPCX_WOV_SUPPORT #define NPCX_DEVALTE_WOV_SL 0 #define NPCX_DEVALTE_I2S_SL 1 +#define NPCX_DEVALTE_DMCLK_FAST 2 #endif /* Others bit definitions */ diff --git a/chip/npcx/wov.c b/chip/npcx/wov.c index 090a66ebdf..57302fd01b 100644 --- a/chip/npcx/wov.c +++ b/chip/npcx/wov.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved. +/* Copyright 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. */ @@ -81,7 +81,6 @@ struct wov_pll_ext_div_val { 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 */ @@ -153,7 +152,6 @@ static const uint8_t wov_interupts[] = { 13 /* I2S_FIFO_UNDRN_IE */ }; - struct wov_ppl_divider { uint16_t pll_frame_len; /* PLL frame length. */ uint16_t pll_fbdv; /* PLL feedback divider. */ @@ -310,7 +308,7 @@ static enum ec_error_list wov_calc_pll_div_l(uint32_t i2s_clk_freq, */ enum ec_error_list wov_wait_for_pll_lock_l(void) { - uint32_t index; + volatile uint32_t index; for (index = 0; WOV_PLL_IS_NOT_LOCK; index++) { /* PLL doesn't reach to lock state. */ @@ -420,6 +418,22 @@ static enum ec_error_list wov_set_i2s_config_l(void) } /** + * wov_i2s_channel1_disable + * + * @param disable - disabled flag, 1 means disable + * + * @return None + */ +static 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 microphone source. * * | Left | Right | Mono | Stereo @@ -462,6 +476,7 @@ static enum ec_error_list wov_set_mic_source_l(void) apm_digital_mixer_config(APM_OUT_MIX_NORMAL_INPUT, APM_OUT_MIX_NO_INPUT); apm_set_vad_input_channel(APM_IN_LEFT); + wov_i2s_channel1_disable(1); break; case WOV_SRC_RIGHT: @@ -478,6 +493,7 @@ static enum ec_error_list wov_set_mic_source_l(void) apm_digital_mixer_config(APM_OUT_MIX_CROSS_INPUT, APM_OUT_MIX_NO_INPUT); apm_set_vad_input_channel(APM_IN_RIGHT); + wov_i2s_channel1_disable(1); break; case WOV_SRC_MONO: @@ -494,6 +510,7 @@ static enum ec_error_list wov_set_mic_source_l(void) apm_digital_mixer_config(APM_OUT_MIX_NORMAL_INPUT, APM_OUT_MIX_NORMAL_INPUT); apm_set_vad_input_channel(APM_IN_AVERAGE_LEFT_RIGHT); + wov_i2s_channel1_disable(0); break; case WOV_SRC_STEREO: @@ -509,7 +526,7 @@ static enum ec_error_list wov_set_mic_source_l(void) 0x01); apm_digital_mixer_config(APM_OUT_MIX_NORMAL_INPUT, APM_OUT_MIX_NORMAL_INPUT); - apm_set_vad_input_channel(APM_IN_RESERVED); + wov_i2s_channel1_disable(0); break; default: @@ -539,9 +556,8 @@ void wov_interrupt_handler(void) * Voice activity detected. */ if (APM_IS_VOICE_ACTIVITY_DETECTED) { - APM_CLEAR_VAD_INTERRUPT; - apm_vad_enable(0); apm_enable_vad_interrupt(0); + APM_CLEAR_VAD_INTERRUPT; WOV_CALLBACK(WOV_EVENT_VAD); } @@ -574,8 +590,11 @@ void wov_interrupt_handler(void) } /* I2S FIFO is underrun. Reset the I2S FIFO and inform the FW. */ - if (WOV_IS_I2S_FIFO_UNDERRUN(wov_status)) + if (WOV_IS_I2S_FIFO_UNDERRUN(wov_status)) { WOV_CALLBACK(WOV_EVENT_ERROR_I2S_FIFO_UNDERRUN); + wov_i2s_fifo_reset(); + } + /* Clear the WoV status register. */ SET_FIELD(NPCX_WOV_STATUS, NPCX_WOV_STATUS_BITS, wov_status); @@ -592,17 +611,22 @@ DECLARE_IRQ(NPCX_IRQ_WOV, wov_interrupt_handler, 4); 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); + /* If clock disabled, then enable it. */ + if (IS_BIT_SET(NPCX_FMUL2_FM2CTRL, + NPCX_FMUL2_FM2CTRL_FMUL2_DIS)) { + /* 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); - /* Disable clock tuning. */ - SET_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_TUNE_DIS); + udelay(WOV_FMUL2_CLK_TUNING_DELAY_TIME); + + } + } else + SET_BIT(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_FMUL2_DIS); } #define WOV_FMUL2_MAX_RETRIES 0x000FFFFF @@ -614,7 +638,6 @@ struct wov_fmul2_multiplier_setting_val { uint8_t fm2n; }; - /** * Configure FMUL2 clock tunning. * @@ -649,13 +672,30 @@ static int wov_get_cfifo_threshold_l(void) return (fifo_threshold * 2); } +/** + * Gets clock source FMUL2 or PLL. + * + * @param None. + * + * NOTE: + * + * @return The clock source FMUL2 (WOV_FMUL2_CLK_SRC) and + * PLL (WOV_PLL_CLK_SRC) + */ +static enum wov_clk_src_sel wov_get_clk_selection(void) +{ + if (IS_BIT_SET(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_CLK_SEL)) + return WOV_PLL_CLK_SRC; + else + return WOV_FMUL2_CLK_SRC; +} + /*************************************************************************** * * Exported function. * **************************************************************************/ - /** * Set FMUL2 clock divider. * @@ -706,55 +746,79 @@ void wov_dmic_clk_config(int enable, enum wov_dmic_clk_div_sel clk_div) enum ec_error_list wov_set_mode(enum wov_modes wov_mode) { enum ec_error_list ret_code; + enum wov_clk_src_sel prev_clock; - 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); + /* If mode is OFF, then power down and exit. */ + if (wov_mode == WOV_MODE_OFF) { 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_stop_ram_capture(); + wov_set_clk_selection(WOV_FMUL2_CLK_SRC); wov_dmic_clk_config(0, WOV_DMIC_DIV_DISABLE); wov_mute(1); + apm_set_mode(WOV_MODE_OFF); wov_fmul2_enable(0); - break; + wov_conf.mode = WOV_MODE_OFF; + return EC_SUCCESS; + } + + switch (wov_mode) { case WOV_MODE_VAD: - wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE); - wov_fmul2_enable(1); - wov_mute(0); + if (apm_get_vad_dmic_rate() == APM_DMIC_RATE_0_75) + wov_dmic_clk_config(1, WOV_DMIC_DIV_BY_4); + else if (apm_get_vad_dmic_rate() == APM_DMIC_RATE_1_2) + wov_dmic_clk_config(1, WOV_DMIC_DIV_BY_2); + else + wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE); + wov_stop_i2s_capture(); + wov_stop_ram_capture(); + wov_set_clk_selection(WOV_FMUL2_CLK_SRC); + apm_set_mode(wov_mode); + ret_code = wov_set_mic_source_l(); + if (ret_code != EC_SUCCESS) + return ret_code; 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); + + if (apm_get_adc_ram_dmic_rate() == APM_DMIC_RATE_0_75) + wov_dmic_clk_config(1, WOV_DMIC_DIV_BY_4); + else if (apm_get_adc_ram_dmic_rate() == APM_DMIC_RATE_1_2) + wov_dmic_clk_config(1, WOV_DMIC_DIV_BY_2); + else + wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE); + wov_stop_i2s_capture(); + wov_set_clk_selection(WOV_FMUL2_CLK_SRC); + apm_set_mode(wov_mode); + ret_code = wov_set_mic_source_l(); + if (ret_code != EC_SUCCESS) + return ret_code; + wov_start_ram_capture(); 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(); + case WOV_MODE_I2S: + if (apm_get_adc_i2s_dmic_rate() == APM_DMIC_RATE_0_75) + wov_dmic_clk_config(1, WOV_DMIC_DIV_BY_4); + else if (apm_get_adc_i2s_dmic_rate() == APM_DMIC_RATE_1_2) + wov_dmic_clk_config(1, WOV_DMIC_DIV_BY_2); + else + wov_dmic_clk_config(1, WOV_DMIC_DIV_DISABLE); + prev_clock = wov_get_clk_selection(); + if (prev_clock != WOV_PLL_CLK_SRC) { + wov_set_i2s_config_l(); + wov_set_clk_selection(WOV_PLL_CLK_SRC); + } + apm_set_mode(wov_mode); + ret_code = wov_set_mic_source_l(); + if (ret_code != EC_SUCCESS) + return ret_code; wov_start_i2s_capture(); - wov_mute(0); + if (wov_mode == WOV_MODE_RAM_AND_I2S) + wov_start_ram_capture(); + else + wov_stop_ram_capture(); break; default: wov_dmic_clk_config(0, WOV_DMIC_DIV_DISABLE); @@ -763,6 +827,8 @@ enum ec_error_list wov_set_mode(enum wov_modes wov_mode) return EC_ERROR_INVAL; } + wov_mute(0); + wov_conf.mode = wov_mode; return EC_SUCCESS; @@ -805,13 +871,17 @@ void wov_init(void) wov_conf.dai_format = WOV_DAI_FMT_I2S; wov_conf.sensitivity_db = 5; + /* Set DMIC clock signal output to use fast transitions. */ + SET_BIT(NPCX_DEVALT(0xE), NPCX_DEVALTE_DMCLK_FAST); + 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); + apm_set_vad_dmic_rate(APM_DMIC_RATE_0_75); + apm_set_adc_ram_dmic_config(APM_DMIC_RATE_0_75); + apm_set_adc_i2s_dmic_config(APM_DMIC_RATE_3_0); } /** @@ -829,6 +899,16 @@ void wov_set_clk_selection(enum wov_clk_src_sel clk_src) { int is_apm_disable; + /* + * Be sure that both clocks are active, as both of them need to + * be active when modify the CLK_SEL bit. + */ + if (IS_BIT_SET(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_PWDEN)) + wov_pll_enable(1); + + if (IS_BIT_SET(NPCX_FMUL2_FM2CTRL, NPCX_FMUL2_FM2CTRL_FMUL2_DIS)) + wov_fmul2_enable(1); + is_apm_disable = IS_BIT_SET(NPCX_APM_CR_APM, NPCX_APM_CR_APM_PD); apm_enable(0); @@ -838,10 +918,17 @@ void wov_set_clk_selection(enum wov_clk_src_sel clk_src) else if (wov_wait_for_pll_lock_l() == EC_SUCCESS) SET_BIT(NPCX_WOV_CLOCK_CNTL, NPCX_WOV_CLOCK_CNT_CLK_SEL); - udelay(1); + udelay(100); if (!is_apm_disable) apm_enable(1); + + /* Disable the unneeded clock. */ + if (clk_src == WOV_PLL_CLK_SRC) + wov_fmul2_enable(0); + else + wov_pll_enable(0); + } /** @@ -905,7 +992,7 @@ void wov_pll_enable(int enable) else SET_BIT(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_PWDEN); - udelay(1); + udelay(100); } /** @@ -944,11 +1031,11 @@ enum ec_error_list wov_pll_clk_div_config(uint32_t out_div_1, SET_FIELD(NPCX_WOV_PLL_CNTL2, NPCX_WOV_PLL_CNTL2_PLL_INDV, in_div); - udelay(1); + udelay(100); CLEAR_BIT(NPCX_WOV_PLL_CNTL1, NPCX_WOV_PLL_CNTL1_PLL_PWDEN); - udelay(1); + udelay(100); return EC_SUCCESS; } @@ -1024,7 +1111,7 @@ void wov_stop_ram_capture(void) wov_interrupt_enable(WOV_CFIFO_THRESHOLD_INT_INDX, 0); wov_interrupt_enable(WOV_CFIFO_THRESHOLD_WAKE_INDX, 0); - udelay(10); + udelay(100); } /** @@ -1038,7 +1125,10 @@ void wov_core_fifo_reset(void) { SET_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_CORE_FFRST); - udelay(10); + udelay(1000); + + /* 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); } @@ -1052,11 +1142,19 @@ void wov_core_fifo_reset(void) */ void wov_i2s_fifo_reset(void) { + int disable; + + disable = IS_BIT_SET(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST); + SET_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST); - udelay(10); + udelay(1000); - CLEAR_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST); + /* Clear the I2S status bits in WoV status register. */ + SET_FIELD(NPCX_WOV_STATUS, NPCX_WOV_STATUS_BITS, 0x18); + + if (!disable) + CLEAR_BIT(NPCX_WOV_FIFO_CNT, NPCX_WOV_FIFO_CNT_I2S_FFRST); } /** @@ -1091,7 +1189,7 @@ void wov_stop_i2s_capture(void) wov_interrupt_enable(WOV_I2SFIFO_OVERRUN_INT_INDX, 0); wov_interrupt_enable(WOV_I2SFIFO_UNDERRUN_INT_INDX, 0); - udelay(10); + udelay(100); } /** @@ -1269,22 +1367,6 @@ enum ec_error_list wov_i2s_channel_config(uint32_t channel_num, } /** - * 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. @@ -1492,7 +1574,7 @@ enum ec_error_list wov_set_agc_config(int stereo, float target, break; } } - if (max_applied_gain_code > 32) + if (max_applied_gain_code >= 32) return EC_ERROR_INVAL; for (min_applied_gain_code = 0; min_applied_gain_code < 16; @@ -1653,17 +1735,10 @@ 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(); - } + CPRINTS("ram data ready and stop ram capture"); + /* just capture one times on RAM*/ + wov_stop_ram_capture(); } if (event == WOV_EVENT_VAD) CPRINTS("got vad"); @@ -1690,6 +1765,18 @@ static int command_wov(int argc, char **argv) wov_get_vad_sensitivity()); return EC_SUCCESS; } + /* Start to capature voice data and store in RAM buffer */ + if (strcasecmp(argv[1], "capram") == 0) { + if (wov_set_buffer((uint32_t *)voice_buffer, + sizeof(voice_buffer) / sizeof(uint32_t)) + == EC_SUCCESS) { + CPRINTS("Start RAM Catpure..."); + wov_start_ram_capture(); + return EC_SUCCESS; + } + CPRINTS("Init fail: voice buffer size"); + return EC_ERROR_INVAL; + } } else if (argc == 3) { if (strcasecmp(argv[1], "cfgsrc") == 0) { if (strcasecmp(argv[2], "mono") == 0) @@ -1760,17 +1847,52 @@ static int command_wov(int argc, char **argv) wov_set_i2s_config(bit_clk, i2s_fmt); return EC_SUCCESS; } - if (strcasecmp(argv[1], "cfgdck") == 0) { + if (strcasecmp(argv[1], "cfgdckV") == 0) { if (strcasecmp(argv[2], "1.0") == 0) - apm_set_adc_dmic_config(APM_DMIC_RATE_1_0); + apm_set_vad_dmic_rate(APM_DMIC_RATE_1_0); + else if (strcasecmp(argv[2], "1.2") == 0) + apm_set_vad_dmic_rate(APM_DMIC_RATE_1_2); else if (strcasecmp(argv[2], "2.4") == 0) - apm_set_adc_dmic_config(APM_DMIC_RATE_2_4); + apm_set_vad_dmic_rate(APM_DMIC_RATE_2_4); else if (strcasecmp(argv[2], "3.0") == 0) - apm_set_adc_dmic_config(APM_DMIC_RATE_3_0); + apm_set_vad_dmic_rate(APM_DMIC_RATE_3_0); + else if (strcasecmp(argv[2], "0.75") == 0) + apm_set_vad_dmic_rate(APM_DMIC_RATE_0_75); else return EC_ERROR_INVAL; return EC_SUCCESS; } + if (strcasecmp(argv[1], "cfgdckR") == 0) { + if (strcasecmp(argv[2], "1.0") == 0) + apm_set_adc_ram_dmic_config(APM_DMIC_RATE_1_0); + else if (strcasecmp(argv[2], "1.2") == 0) + apm_set_adc_ram_dmic_config(APM_DMIC_RATE_1_2); + else if (strcasecmp(argv[2], "2.4") == 0) + apm_set_adc_ram_dmic_config(APM_DMIC_RATE_2_4); + else if (strcasecmp(argv[2], "3.0") == 0) + apm_set_adc_ram_dmic_config(APM_DMIC_RATE_3_0); + else if (strcasecmp(argv[2], "0.75") == 0) + apm_set_adc_ram_dmic_config(APM_DMIC_RATE_0_75); + else + return EC_ERROR_INVAL; + return EC_SUCCESS; + } + if (strcasecmp(argv[1], "cfgdckI") == 0) { + if (strcasecmp(argv[2], "1.0") == 0) + apm_set_adc_i2s_dmic_config(APM_DMIC_RATE_1_0); + else if (strcasecmp(argv[2], "1.2") == 0) + apm_set_adc_i2s_dmic_config(APM_DMIC_RATE_1_2); + else if (strcasecmp(argv[2], "2.4") == 0) + apm_set_adc_i2s_dmic_config(APM_DMIC_RATE_2_4); + else if (strcasecmp(argv[2], "3.0") == 0) + apm_set_adc_i2s_dmic_config(APM_DMIC_RATE_3_0); + else if (strcasecmp(argv[2], "0.75") == 0) + apm_set_adc_i2s_dmic_config(APM_DMIC_RATE_0_75); + 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); @@ -1780,20 +1902,19 @@ static int command_wov(int argc, char **argv) } 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(); + == EC_SUCCESS) + wov_set_mode(WOV_MODE_RAM); + else + return EC_ERROR_INVAL; } 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(); + == EC_SUCCESS) + wov_set_mode(WOV_MODE_RAM_AND_I2S); + else + return EC_ERROR_INVAL; } else { return EC_ERROR_INVAL; } @@ -1810,6 +1931,18 @@ static int command_wov(int argc, char **argv) return EC_SUCCESS; } } + if (strcasecmp(argv[1], "fmul2") == 0) { + if (strcasecmp(argv[2], "enable") == 0) { + CLEAR_BIT(NPCX_FMUL2_FM2CTRL, + NPCX_FMUL2_FM2CTRL_TUNE_DIS); + return EC_SUCCESS; + } + if (strcasecmp(argv[2], "disable") == 0) { + SET_BIT(NPCX_FMUL2_FM2CTRL, + NPCX_FMUL2_FM2CTRL_TUNE_DIS); + return EC_SUCCESS; + } + } if (strcasecmp(argv[1], "vadsens") == 0) return wov_set_vad_sensitivity(atoi(argv[2])); @@ -1840,6 +1973,7 @@ static int command_wov(int argc, char **argv) DECLARE_CONSOLE_COMMAND(wov, command_wov, "init\n" "mute <enable|disable>\n" + "capram\n" "cfgsrc <mono|stereo|left|right>\n" "cfgbit <16|18|20|24>\n" "cfgsfs <8000|12000|16000|24000|32000|48000>\n" @@ -1847,8 +1981,11 @@ DECLARE_CONSOLE_COMMAND(wov, command_wov, "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" + "cfgdckV <0.75|1.0|1.2|2.4|3.0>\n" + "cfgdckR <0.75|1.0|1.2|2.4|3.0>\n" + "cfgdckI <0.75|1.0|1.2|2.4|3.0>\n" "cfgget\n" + "fmul2 <enable|disable>\n" "vadsens <0~31>\n" "gain <0~31>", "wov configuration"); diff --git a/chip/npcx/wov_chip.h b/chip/npcx/wov_chip.h index 7e2fb520cc..bdccb768ff 100644 --- a/chip/npcx/wov_chip.h +++ b/chip/npcx/wov_chip.h @@ -1,10 +1,8 @@ -/* Copyright (c) 2018 The Chromium OS Authors. All rights reserved. +/* Copyright 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 @@ -574,15 +572,6 @@ void wov_stop_i2s_capture(void); 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 |