/***************************************************************************
 *
 * 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

