pinebuds/platform/drivers/codec/best2300p/codec_best2300p.c
2022-08-15 17:20:27 +08:00

688 lines
21 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#include "plat_types.h"
#include "codec_int.h"
#include "hal_codec.h"
#include "hal_trace.h"
#include "hal_sleep.h"
#include "stdbool.h"
#include "analog.h"
#include "tgt_hardware.h"
#define CODEC_TRACE(n, s, ...) TRACE(n, s, ##__VA_ARGS__)
#define CODEC_INT_INST HAL_CODEC_ID_0
#ifndef CODEC_OUTPUT_DEV
#define CODEC_OUTPUT_DEV CFG_HW_AUD_OUTPUT_PATH_SPEAKER_DEV
#endif
#ifdef __CODEC_ASYNC_CLOSE__
#include "cmsis_os.h"
#define CODEC_ASYNC_CLOSE_DELAY (5000)
static void codec_timehandler(void const *param);
osTimerDef (CODEC_TIMER, codec_timehandler);
static osTimerId codec_timer;
static CODEC_CLOSE_HANDLER close_hdlr;
enum CODEC_HW_STATE_T {
CODEC_HW_STATE_CLOSED,
CODEC_HW_STATE_CLOSE_PENDING,
CODEC_HW_STATE_OPENED,
};
enum CODEC_HW_STATE_T codec_hw_state = CODEC_HW_STATE_CLOSED;
#endif
enum CODEC_USER_T {
CODEC_USER_STREAM = (1 << 0),
CODEC_USER_ANC = (1 << 1),
CODEC_USER_VAD = (1 << 2),
};
struct CODEC_CONFIG_T {
enum CODEC_USER_T user_map;
bool resample_en;
bool mute_state[AUD_STREAM_NUM];
bool chan_vol_set[AUD_STREAM_NUM];
struct STREAM_CONFIG_T {
bool opened;
bool started;
struct HAL_CODEC_CONFIG_T codec_cfg;
} stream_cfg[AUD_STREAM_NUM];
};
static struct CODEC_CONFIG_T codec_int_cfg = {
.user_map = 0,
.resample_en = false,
.mute_state = { false, false, },
.chan_vol_set = { false, false, },
//playback
.stream_cfg[AUD_STREAM_PLAYBACK] = {
.opened = false,
.started = false,
.codec_cfg = {
.sample_rate = AUD_SAMPRATE_NULL,
}
},
//capture
.stream_cfg[AUD_STREAM_CAPTURE] = {
.opened = false,
.started = false,
.codec_cfg = {
.sample_rate = AUD_SAMPRATE_NULL,
}
}
};
static bool anc_ff_enabled;
static bool anc_fb_enabled;
static enum AUD_SAMPRATE_T codec_anc_samp_rate;
static CODEC_ANC_HANDLER codec_anc_handler;
#ifdef VOICE_DETECTOR_EN
static enum AUD_VAD_TYPE_T vad_type;
#endif
#ifdef CODEC_ANC_BOOST
static CODEC_ANC_BOOST_DELAY_FUNC boost_delay;
void codec_set_anc_boost_delay_func(CODEC_ANC_BOOST_DELAY_FUNC delay_func)
{
boost_delay = delay_func;
}
#endif
uint32_t codec_int_stream_open(enum AUD_STREAM_T stream)
{
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
codec_int_cfg.stream_cfg[stream].opened = true;
return 0;
}
uint32_t codec_int_stream_setup(enum AUD_STREAM_T stream, struct HAL_CODEC_CONFIG_T *cfg)
{
enum AUD_CHANNEL_MAP_T ch_map;
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
if (codec_int_cfg.stream_cfg[stream].codec_cfg.sample_rate == AUD_SAMPRATE_NULL) {
// Codec uninitialized -- all config items should be set
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag = HAL_CODEC_CONFIG_ALL;
} else {
// Codec initialized before -- only different config items need to be set
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag = HAL_CODEC_CONFIG_NULL;
}
// Always config sample rate, for the pll setting might have been changed by the other stream
CODEC_TRACE(2,"[sample_rate]old=%d new=%d", codec_int_cfg.stream_cfg[stream].codec_cfg.sample_rate, cfg->sample_rate);
if (codec_int_cfg.user_map & CODEC_USER_ANC) {
// Check ANC sample rate
if (codec_anc_handler) {
enum AUD_SAMPRATE_T cfg_rate;
enum AUD_SAMPRATE_T old_rate;
cfg_rate = hal_codec_anc_convert_rate(cfg->sample_rate);
if (cfg_rate != codec_anc_samp_rate) {
old_rate = codec_anc_samp_rate;
codec_anc_handler(stream, cfg_rate, NULL, NULL);
codec_anc_samp_rate = cfg_rate;
TRACE(5,"%s: ANC sample rate changes from %u to %u due to stream=%d samp_rate=%u",
__func__, old_rate, codec_anc_samp_rate, stream, cfg->sample_rate);
}
}
}
codec_int_cfg.stream_cfg[stream].codec_cfg.sample_rate = cfg->sample_rate;
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_SAMPLE_RATE;
if(codec_int_cfg.stream_cfg[stream].codec_cfg.bits != cfg->bits)
{
CODEC_TRACE(2,"[bits]old=%d new=%d", codec_int_cfg.stream_cfg[stream].codec_cfg.bits, cfg->bits);
codec_int_cfg.stream_cfg[stream].codec_cfg.bits = cfg->bits;
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_BITS;
}
if(codec_int_cfg.stream_cfg[stream].codec_cfg.channel_num != cfg->channel_num)
{
CODEC_TRACE(2,"[channel_num]old=%d new=%d", codec_int_cfg.stream_cfg[stream].codec_cfg.channel_num, cfg->channel_num);
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_num = cfg->channel_num;
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_CHANNEL_NUM;
}
ch_map = cfg->channel_map;
if (ch_map == 0) {
if (stream == AUD_STREAM_PLAYBACK) {
ch_map = (enum AUD_CHANNEL_MAP_T)CODEC_OUTPUT_DEV;
} else {
ch_map = (enum AUD_CHANNEL_MAP_T)hal_codec_get_input_path_cfg(cfg->io_path);
}
ch_map &= AUD_CHANNEL_MAP_ALL;
}
if(codec_int_cfg.stream_cfg[stream].codec_cfg.channel_map != ch_map)
{
CODEC_TRACE(2,"[channel_map]old=0x%x new=0x%x", codec_int_cfg.stream_cfg[stream].codec_cfg.channel_map, ch_map);
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_map = ch_map;
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_CHANNEL_MAP | HAL_CODEC_CONFIG_VOL | HAL_CODEC_CONFIG_BITS;
}
if(codec_int_cfg.stream_cfg[stream].codec_cfg.use_dma != cfg->use_dma)
{
CODEC_TRACE(2,"[use_dma]old=%d new=%d", codec_int_cfg.stream_cfg[stream].codec_cfg.use_dma, cfg->use_dma);
codec_int_cfg.stream_cfg[stream].codec_cfg.use_dma = cfg->use_dma;
}
if(codec_int_cfg.stream_cfg[stream].codec_cfg.vol != cfg->vol)
{
CODEC_TRACE(3,"[vol]old=%d new=%d chan_vol_set=%d", codec_int_cfg.stream_cfg[stream].codec_cfg.vol, cfg->vol, codec_int_cfg.chan_vol_set[stream]);
codec_int_cfg.stream_cfg[stream].codec_cfg.vol = cfg->vol;
if (!codec_int_cfg.chan_vol_set[stream]) {
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_VOL;
}
}
if(codec_int_cfg.stream_cfg[stream].codec_cfg.io_path != cfg->io_path)
{
CODEC_TRACE(2,"[io_path]old=%d new=%d", codec_int_cfg.stream_cfg[stream].codec_cfg.io_path, cfg->io_path);
codec_int_cfg.stream_cfg[stream].codec_cfg.io_path = cfg->io_path;
}
CODEC_TRACE(3,"[%s]stream=%d set_flag=0x%x",__func__,stream,codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag);
hal_codec_setup_stream(CODEC_INT_INST, stream, &(codec_int_cfg.stream_cfg[stream].codec_cfg));
return 0;
}
void codec_int_stream_mute(enum AUD_STREAM_T stream, bool mute)
{
bool anc_on;
CODEC_TRACE(3,"%s: stream=%d mute=%d", __func__, stream, mute);
if (mute == codec_int_cfg.mute_state[stream]) {
CODEC_TRACE(2,"[%s] Codec already in mute status: %d", __func__, mute);
return;
}
anc_on = !!(codec_int_cfg.user_map & CODEC_USER_ANC);
if (stream == AUD_STREAM_PLAYBACK) {
if (mute) {
if (!anc_on) {
analog_aud_codec_mute();
}
hal_codec_dac_mute(true);
} else {
hal_codec_dac_mute(false);
if (!anc_on) {
analog_aud_codec_nomute();
}
}
} else {
hal_codec_adc_mute(mute);
}
codec_int_cfg.mute_state[stream] = mute;
}
void codec_int_stream_set_chan_vol(enum AUD_STREAM_T stream, enum AUD_CHANNEL_MAP_T ch_map, uint8_t vol)
{
CODEC_TRACE(4,"%s: stream=%d ch_map=0x%X vol=%u", __func__, stream, ch_map, vol);
codec_int_cfg.chan_vol_set[stream] = true;
hal_codec_set_chan_vol(stream, ch_map, vol);
}
void codec_int_stream_restore_chan_vol(enum AUD_STREAM_T stream)
{
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
if (codec_int_cfg.chan_vol_set[stream]) {
codec_int_cfg.chan_vol_set[stream] = false;
// Restore normal volume
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag = HAL_CODEC_CONFIG_VOL;
hal_codec_setup_stream(CODEC_INT_INST, stream, &(codec_int_cfg.stream_cfg[stream].codec_cfg));
}
}
static void codec_hw_start(enum AUD_STREAM_T stream)
{
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
if (stream == AUD_STREAM_PLAYBACK) {
// Enable DAC before starting stream (specifically before enabling PA)
analog_aud_codec_dac_enable(true);
}
hal_codec_start_stream(CODEC_INT_INST, stream);
}
static void codec_hw_stop(enum AUD_STREAM_T stream)
{
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
hal_codec_stop_stream(CODEC_INT_INST, stream);
if (stream == AUD_STREAM_PLAYBACK) {
// Disable DAC after stopping stream (specifically after disabling PA)
analog_aud_codec_dac_enable(false);
}
}
uint32_t codec_int_stream_start(enum AUD_STREAM_T stream)
{
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
codec_int_cfg.stream_cfg[stream].started = true;
if (stream == AUD_STREAM_CAPTURE) {
analog_aud_codec_adc_enable(codec_int_cfg.stream_cfg[stream].codec_cfg.io_path,
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_map, true);
}
hal_codec_start_interface(CODEC_INT_INST, stream,
codec_int_cfg.stream_cfg[stream].codec_cfg.use_dma);
if ((codec_int_cfg.user_map & CODEC_USER_ANC) == 0) {
codec_hw_start(stream);
}
return 0;
}
uint32_t codec_int_stream_stop(enum AUD_STREAM_T stream)
{
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
hal_codec_stop_interface(CODEC_INT_INST, stream);
if ((codec_int_cfg.user_map & CODEC_USER_ANC) == 0) {
codec_hw_stop(stream);
}
if (stream == AUD_STREAM_CAPTURE) {
analog_aud_codec_adc_enable(codec_int_cfg.stream_cfg[stream].codec_cfg.io_path,
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_map, false);
}
codec_int_cfg.stream_cfg[stream].started = false;
return 0;
}
uint32_t codec_int_stream_close(enum AUD_STREAM_T stream)
{
//close all stream
CODEC_TRACE(2,"%s: stream=%d", __func__, stream);
if (codec_int_cfg.stream_cfg[stream].started) {
codec_int_stream_stop(stream);
}
codec_int_stream_restore_chan_vol(stream);
codec_int_cfg.stream_cfg[stream].opened = false;
return 0;
}
static void codec_hw_open(enum CODEC_USER_T user)
{
enum CODEC_USER_T old_map;
#ifdef __AUDIO_RESAMPLE__
bool resample_en = !!hal_cmu_get_audio_resample_status();
#endif
old_map = codec_int_cfg.user_map;
codec_int_cfg.user_map |= user;
if (old_map) {
#ifdef __AUDIO_RESAMPLE__
ASSERT(codec_int_cfg.resample_en == resample_en,
"%s: Bad resamp status %d for user 0x%X (old_map=0x%X)", __func__, resample_en, user, old_map);
#endif
return;
}
#ifdef __AUDIO_RESAMPLE__
codec_int_cfg.resample_en = resample_en;
#endif
CODEC_TRACE(1,"%s", __func__);
#ifdef __CODEC_ASYNC_CLOSE__
if (codec_timer == NULL) {
codec_timer = osTimerCreate (osTimer(CODEC_TIMER), osTimerOnce, NULL);
}
osTimerStop(codec_timer);
#endif
// Audio resample: Might have different clock source, so must be reconfigured here
hal_codec_open(CODEC_INT_INST);
#ifdef __CODEC_ASYNC_CLOSE__
CODEC_TRACE(2,"%s: codec_hw_state=%d", __func__, codec_hw_state);
if (codec_hw_state == CODEC_HW_STATE_CLOSED) {
codec_hw_state = CODEC_HW_STATE_OPENED;
// Continue to open the codec hardware
} else if (codec_hw_state == CODEC_HW_STATE_CLOSE_PENDING) {
// Still opened
codec_hw_state = CODEC_HW_STATE_OPENED;
return;
} else {
// Already opened
return;
}
#endif
analog_aud_codec_open();
}
static void codec_hw_close(enum CODEC_CLOSE_TYPE_T type, enum CODEC_USER_T user)
{
codec_int_cfg.user_map &= ~user;
if (type == CODEC_CLOSE_NORMAL) {
if (codec_int_cfg.user_map) {
return;
}
}
#ifdef __CODEC_ASYNC_CLOSE__
CODEC_TRACE(3,"%s: type=%d codec_hw_state=%d", __func__, type, codec_hw_state);
#else
CODEC_TRACE(2,"%s: type=%d", __func__, type);
#endif
if (type == CODEC_CLOSE_NORMAL) {
// Audio resample: Might have different clock source, so close now and reconfigure when open
hal_codec_close(CODEC_INT_INST);
#ifdef CODEC_POWER_DOWN
memset(&codec_int_cfg, 0, sizeof(codec_int_cfg));
#endif
#ifdef __CODEC_ASYNC_CLOSE__
ASSERT(codec_hw_state == CODEC_HW_STATE_OPENED, "%s: (type=%d) Bad codec_hw_state=%d", __func__, type, codec_hw_state);
// Start a timer to close the codec hardware
codec_hw_state = CODEC_HW_STATE_CLOSE_PENDING;
osTimerStop(codec_timer);
osTimerStart(codec_timer, CODEC_ASYNC_CLOSE_DELAY);
return;
} else if (type == CODEC_CLOSE_ASYNC_REAL) {
if (codec_hw_state != CODEC_HW_STATE_CLOSE_PENDING) {
// Already closed or reopened
return;
}
codec_hw_state = CODEC_HW_STATE_CLOSED;
#endif
} else if (type == CODEC_CLOSE_FORCED) {
hal_codec_crash_mute();
}
analog_aud_codec_close();
}
uint32_t codec_int_open(void)
{
CODEC_TRACE(2,"%s: user_map=0x%X", __func__, codec_int_cfg.user_map);
codec_hw_open(CODEC_USER_STREAM);
return 0;
}
uint32_t codec_int_close(enum CODEC_CLOSE_TYPE_T type)
{
CODEC_TRACE(3,"%s: type=%d user_map=0x%X", __func__, type, codec_int_cfg.user_map);
if (type == CODEC_CLOSE_NORMAL) {
if (codec_int_cfg.stream_cfg[AUD_STREAM_PLAYBACK].opened == false &&
codec_int_cfg.stream_cfg[AUD_STREAM_CAPTURE].opened == false){
codec_hw_close(type, CODEC_USER_STREAM);
}
} else {
codec_hw_close(type, CODEC_USER_STREAM);
}
return 0;
}
#ifdef __CODEC_ASYNC_CLOSE__
void codec_int_set_close_handler(CODEC_CLOSE_HANDLER hdlr)
{
close_hdlr = hdlr;
}
static void codec_timehandler(void const *param)
{
if (close_hdlr) {
close_hdlr();
}
}
#endif
int codec_anc_open(enum ANC_TYPE_T type, enum AUD_SAMPRATE_T dac_rate, enum AUD_SAMPRATE_T adc_rate, CODEC_ANC_HANDLER hdlr)
{
bool anc_running = false;
CODEC_TRACE(4,"%s: type=%d dac_rate=%d adc_rate=%d", __func__, type, dac_rate, adc_rate);
dac_rate = hal_codec_anc_convert_rate(dac_rate);
adc_rate = hal_codec_anc_convert_rate(adc_rate);
ASSERT(dac_rate == adc_rate, "%s: Unmatched rates: dac_rate=%u adc_rate=%u", __func__, dac_rate, adc_rate);
if (hdlr && (codec_int_cfg.user_map & CODEC_USER_STREAM)) {
enum AUD_SAMPRATE_T cfg_dac_rate, cfg_adc_rate;
if (codec_int_cfg.stream_cfg[AUD_STREAM_PLAYBACK].opened) {
cfg_dac_rate = hal_codec_anc_convert_rate(codec_int_cfg.stream_cfg[AUD_STREAM_PLAYBACK].codec_cfg.sample_rate);
} else {
cfg_dac_rate = dac_rate;
}
if (codec_int_cfg.stream_cfg[AUD_STREAM_CAPTURE].opened) {
cfg_adc_rate = hal_codec_anc_convert_rate(codec_int_cfg.stream_cfg[AUD_STREAM_CAPTURE].codec_cfg.sample_rate);
} else {
cfg_adc_rate = adc_rate;
}
if (codec_int_cfg.stream_cfg[AUD_STREAM_PLAYBACK].opened && codec_int_cfg.stream_cfg[AUD_STREAM_CAPTURE].opened) {
ASSERT(cfg_dac_rate == cfg_adc_rate, "%s: Unmatched cfg rates: dac_rate=%u adc_rate=%u", __func__, cfg_dac_rate, cfg_adc_rate);
} else if (codec_int_cfg.stream_cfg[AUD_STREAM_CAPTURE].opened) {
cfg_dac_rate = cfg_adc_rate;
}
if (dac_rate != cfg_dac_rate) {
hdlr(AUD_STREAM_PLAYBACK, cfg_dac_rate, NULL, NULL);
TRACE(3,"%s: ANC sample rate changes from %u to %u", __func__, dac_rate, cfg_dac_rate);
dac_rate = cfg_dac_rate;
}
}
codec_anc_samp_rate = dac_rate;
codec_anc_handler = hdlr;
if (anc_ff_enabled || anc_fb_enabled) {
anc_running = true;
}
if (type == ANC_FEEDFORWARD) {
anc_ff_enabled = true;
} else if (type == ANC_FEEDBACK) {
anc_fb_enabled = true;
}
if (!anc_running) {
hal_chip_wake_lock(HAL_CHIP_WAKE_LOCK_USER_ANC);
hal_cmu_anc_enable(HAL_CMU_ANC_CLK_USER_ANC);
enum AUD_STREAM_T stream;
enum AUD_CHANNEL_NUM_T play_chan_num;
codec_hw_open(CODEC_USER_ANC);
if (CODEC_OUTPUT_DEV == (AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH1)) {
play_chan_num = AUD_CHANNEL_NUM_2;
} else {
play_chan_num = AUD_CHANNEL_NUM_1;
}
for (stream = AUD_STREAM_PLAYBACK; stream <= AUD_STREAM_CAPTURE; stream++) {
if (codec_int_cfg.stream_cfg[stream].opened) {
if (stream == AUD_STREAM_PLAYBACK) {
/* ASSERT(codec_int_cfg.stream_cfg[stream].codec_cfg.channel_num == play_chan_num,
"Invalid existing ANC channel num %d != %d for stream %d",
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_num,
play_chan_num,
stream);*/
}
} else {
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag = 0;
codec_int_cfg.stream_cfg[stream].codec_cfg.sample_rate = codec_anc_samp_rate;
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_SAMPLE_RATE;
if (stream == AUD_STREAM_PLAYBACK) {
codec_int_cfg.stream_cfg[stream].codec_cfg.vol = TGT_VOLUME_LEVEL_10;
} else {
codec_int_cfg.stream_cfg[stream].codec_cfg.vol = CODEC_SADC_VOL;
}
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_VOL;
if (stream == AUD_STREAM_PLAYBACK) {
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_num = play_chan_num;
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_map = CODEC_OUTPUT_DEV;
} else {
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_num = 0;
codec_int_cfg.stream_cfg[stream].codec_cfg.channel_map = 0;
}
codec_int_cfg.stream_cfg[stream].codec_cfg.set_flag |= HAL_CODEC_CONFIG_CHANNEL_NUM;
hal_codec_setup_stream(CODEC_INT_INST, stream, &(codec_int_cfg.stream_cfg[stream].codec_cfg));
}
}
// Start play first, then start capture last
for (stream = AUD_STREAM_PLAYBACK; stream <= AUD_STREAM_CAPTURE; stream++) {
if (!codec_int_cfg.stream_cfg[stream].started) {
codec_hw_start(stream);
}
}
}
hal_codec_anc_adc_enable(type);
analog_aud_codec_anc_enable(type, true);
if (!anc_running) {
// Enable pa if dac muted before
if (codec_int_cfg.mute_state[AUD_STREAM_PLAYBACK]) {
analog_aud_codec_nomute();
}
#ifdef CODEC_ANC_BOOST
analog_aud_codec_anc_boost(true, (ANALOG_ANC_BOOST_DELAY_FUNC)boost_delay);
#endif
}
return 0;
}
int codec_anc_close(enum ANC_TYPE_T type)
{
CODEC_TRACE(2,"%s: type=%d", __func__, type);
if (type == ANC_FEEDFORWARD) {
anc_ff_enabled = false;
} else if (type == ANC_FEEDBACK) {
anc_fb_enabled = false;
}
hal_codec_anc_adc_disable(type);
analog_aud_codec_anc_enable(type, false);
if (anc_ff_enabled || anc_fb_enabled) {
return 0;
}
#ifdef CODEC_ANC_BOOST
analog_aud_codec_anc_boost(false, (ANALOG_ANC_BOOST_DELAY_FUNC)boost_delay);
#endif
enum AUD_STREAM_T stream;
// Stop capture first, then stop play last
for (stream = AUD_STREAM_CAPTURE; stream >= AUD_STREAM_PLAYBACK && stream <= AUD_STREAM_CAPTURE; stream--) {
if (!codec_int_cfg.stream_cfg[stream].started) {
codec_hw_stop(stream);
}
}
codec_hw_close(CODEC_CLOSE_NORMAL, CODEC_USER_ANC);
hal_cmu_anc_disable(HAL_CMU_ANC_CLK_USER_ANC);
hal_chip_wake_unlock(HAL_CHIP_WAKE_LOCK_USER_ANC);
// Disable pa if dac muted before
if (codec_int_cfg.mute_state[AUD_STREAM_PLAYBACK]) {
analog_aud_codec_mute();
}
codec_anc_handler = NULL;
return 0;
}
#ifdef VOICE_DETECTOR_EN
int codec_vad_open(const struct AUD_VAD_CONFIG_T *cfg)
{
if (cfg->type == AUD_VAD_TYPE_NONE) {
return codec_vad_close();
}
if (vad_type == cfg->type) {
return 0;
}
vad_type = cfg->type;
if (codec_int_cfg.user_map == 0) {
codec_hw_open(CODEC_USER_VAD);
}
hal_codec_vad_open(cfg);
codec_int_cfg.user_map |= (1 << CODEC_USER_VAD);
return 0;
}
int codec_vad_close(void)
{
if (vad_type == AUD_VAD_TYPE_NONE) {
return 0;
}
vad_type = AUD_VAD_TYPE_NONE;
codec_int_cfg.user_map &= ~(1 << CODEC_USER_VAD);
if (codec_int_cfg.user_map == 0) {
codec_hw_close(CODEC_CLOSE_NORMAL, CODEC_USER_VAD);
}
return 0;
}
#endif