/***************************************************************************
 *
 * 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_addr_map.h"
#include CHIP_SPECIFIC_HDR(reg_codec)
#include "hal_codec.h"
#include "hal_cmu.h"
#include "hal_psc.h"
#include "hal_aud.h"
#include "hal_trace.h"
#include "hal_timer.h"
#include "analog.h"
#include "cmsis.h"
#include "string.h"
#include "tgt_hardware.h"
#ifdef RTOS
#include "cmsis_os.h"
#endif
#include "hal_chipid.h"

#define NO_DAC_RESET

#define SDM_MUTE_NOISE_SUPPRESSION

#ifndef CODEC_OUTPUT_DEV
#define CODEC_OUTPUT_DEV                    CFG_HW_AUD_OUTPUT_PATH_SPEAKER_DEV
#endif
#if ((CODEC_OUTPUT_DEV & CFG_HW_AUD_OUTPUT_PATH_SPEAKER_DEV) == 0)
#ifndef AUDIO_OUTPUT_SWAP
#define AUDIO_OUTPUT_SWAP
#endif
#endif

#if (defined(__TWS__) || defined(IBRT)) && defined(ANC_APP)
//#define CODEC_TIMER
#endif

#ifdef CODEC_DSD
#ifdef ANC_APP
#error "ANC_APP conflicts with CODEC_DSD"
#endif
#ifdef AUDIO_ANC_FB_MC
#error "AUDIO_ANC_FB_MC conflicts with CODEC_DSD"
#endif
#ifdef __AUDIO_RESAMPLE__
#error "__AUDIO_RESAMPLE__ conflicts with CODEC_DSD"
#endif
#endif

#define RS_CLOCK_FACTOR                     200

// Trigger DMA request when TX-FIFO *empty* count > threshold
#define HAL_CODEC_TX_FIFO_TRIGGER_LEVEL     (3)
// Trigger DMA request when RX-FIFO count >= threshold
#define HAL_CODEC_RX_FIFO_TRIGGER_LEVEL     (4)

#define MAX_DIG_DBVAL                       (50)
#define ZERODB_DIG_DBVAL                    (0)
#define MIN_DIG_DBVAL                       (-99)

#define MAX_SIDETONE_DBVAL                  (30)
#define MIN_SIDETONE_DBVAL                  (-30)
#define SIDETONE_DBVAL_STEP                 (-2)

#define MAX_SIDETONE_REGVAL                 (0)
#define MIN_SIDETONE_REGVAL                 (30)
#define MUTE_SIDETONE_REGVAL                (31)

#ifndef MC_DELAY_COMMON
#define MC_DELAY_COMMON                     28
#endif

#ifndef CODEC_DIGMIC_PHASE
#define CODEC_DIGMIC_PHASE                  7
#endif

#define ADC_IIR_CH_NUM                      2

#define MAX_DIG_MIC_CH_NUM                  5

#define NORMAL_ADC_CH_NUM                   5
// Echo cancel ADC channel number
#define EC_ADC_CH_NUM                       2
#define MAX_ADC_CH_NUM                      (NORMAL_ADC_CH_NUM + EC_ADC_CH_NUM)

#define MAX_DAC_CH_NUM                      2

#ifdef CODEC_DSD
#define NORMAL_MIC_MAP                      (AUD_CHANNEL_MAP_NORMAL_ALL & ~(AUD_CHANNEL_MAP_CH5 | AUD_CHANNEL_MAP_CH6 | AUD_CHANNEL_MAP_CH7 | \
                                                AUD_CHANNEL_MAP_DIGMIC_CH5 | AUD_CHANNEL_MAP_DIGMIC_CH6 | AUD_CHANNEL_MAP_DIGMIC_CH7 | \
                                                AUD_CHANNEL_MAP_DIGMIC_CH2 | AUD_CHANNEL_MAP_DIGMIC_CH3))
#else
#define NORMAL_MIC_MAP                      (AUD_CHANNEL_MAP_NORMAL_ALL & ~(AUD_CHANNEL_MAP_CH5 | AUD_CHANNEL_MAP_CH6 | AUD_CHANNEL_MAP_CH7 | \
                                                AUD_CHANNEL_MAP_DIGMIC_CH5 | AUD_CHANNEL_MAP_DIGMIC_CH6 | AUD_CHANNEL_MAP_DIGMIC_CH7))
#endif
#define NORMAL_ADC_MAP                      (AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH1 | AUD_CHANNEL_MAP_CH2 | AUD_CHANNEL_MAP_CH3 | AUD_CHANNEL_MAP_CH4)

#define EC_MIC_MAP                          (AUD_CHANNEL_MAP_ECMIC_CH0 | AUD_CHANNEL_MAP_ECMIC_CH1)
#define EC_ADC_MAP                          (AUD_CHANNEL_MAP_CH5 | AUD_CHANNEL_MAP_CH6)

#define VALID_MIC_MAP                       (NORMAL_MIC_MAP | EC_MIC_MAP)
#define VALID_ADC_MAP                       (NORMAL_ADC_MAP | EC_ADC_MAP)

#define VALID_SPK_MAP                       (AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH1)
#define VALID_DAC_MAP                       VALID_SPK_MAP

#if (CFG_HW_AUD_OUTPUT_PATH_SPEAKER_DEV & ~VALID_SPK_MAP)
#error "Invalid CFG_HW_AUD_OUTPUT_PATH_SPEAKER_DEV"
#endif
#if (CODEC_OUTPUT_DEV & ~VALID_SPK_MAP)
#error "Invalid CODEC_OUTPUT_DEV"
#endif

#define RSTN_ADC_FREE_RUNNING_CLK           CODEC_SOFT_RSTN_ADC(1 << NORMAL_ADC_CH_NUM)

#if defined(SPEECH_SIDETONE) && \
        (defined(CFG_HW_AUD_SIDETONE_MIC_DEV) && (CFG_HW_AUD_SIDETONE_MIC_DEV)) && \
        defined(CFG_HW_AUD_SIDETONE_GAIN_DBVAL)
#define SIDETONE_ENABLE
#if (CFG_HW_AUD_SIDETONE_GAIN_DBVAL > MAX_SIDETONE_DBVAL) || \
        (CFG_HW_AUD_SIDETONE_GAIN_DBVAL < MIN_SIDETONE_DBVAL) || \
        defined(CFG_HW_AUD_SIDETONE_IIR_INDEX) || \
        defined(CFG_HW_AUD_SIDETONE_GAIN_RAMP)
#define SIDETONE_DEDICATED_ADC_CHAN
#define SIDETONE_RESERVED_ADC_CHAN
#endif
#endif

enum CODEC_ADC_EN_REQ_T {
    CODEC_ADC_EN_REQ_STREAM,
    CODEC_ADC_EN_REQ_MC,
    CODEC_ADC_EN_REQ_DSD,

    CODEC_ADC_EN_REQ_QTY,
};

enum CODEC_IRQ_TYPE_T {
    CODEC_IRQ_TYPE_BT_TRIGGER,
    CODEC_IRQ_TYPE_VAD,
    CODEC_IRQ_TYPE_ANC_FB_CHECK,

    CODEC_IRQ_TYPE_QTY,
};

struct CODEC_DAC_DRE_CFG_T {
    uint8_t dre_delay;
    uint8_t thd_db_offset;
    uint8_t step_mode;
    uint32_t dre_win;
    uint16_t amp_high;
};

struct CODEC_DAC_SAMPLE_RATE_T {
    enum AUD_SAMPRATE_T sample_rate;
    uint32_t codec_freq;
    uint8_t codec_div;
    uint8_t cmu_div;
    uint8_t dac_up;
    uint8_t bypass_cnt;
    uint8_t mc_delay;
};

static const struct CODEC_DAC_SAMPLE_RATE_T codec_dac_sample_rate[] = {
#ifdef __AUDIO_RESAMPLE__
    {AUD_SAMPRATE_8463,      CODEC_FREQ_CRYSTAL,               1,             1, 6, 0, MC_DELAY_COMMON + 160},
    {AUD_SAMPRATE_16927,     CODEC_FREQ_CRYSTAL,               1,             1, 3, 0, MC_DELAY_COMMON +  85},
    {AUD_SAMPRATE_50781,     CODEC_FREQ_CRYSTAL,               1,             1, 1, 0, MC_DELAY_COMMON +  29},
#endif
    {AUD_SAMPRATE_7350,   CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 6, 0, MC_DELAY_COMMON + 174},
    {AUD_SAMPRATE_8000,   CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 6, 0, MC_DELAY_COMMON + 168}, // T
    {AUD_SAMPRATE_14700,  CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 3, 0, MC_DELAY_COMMON +  71},
    {AUD_SAMPRATE_16000,  CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 3, 0, MC_DELAY_COMMON +  88}, // T
    {AUD_SAMPRATE_22050,  CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 2, 0, MC_DELAY_COMMON +  60},
    {AUD_SAMPRATE_24000,  CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 2, 0, MC_DELAY_COMMON +  58},
    {AUD_SAMPRATE_44100,  CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 0, MC_DELAY_COMMON +  31}, // T
    {AUD_SAMPRATE_48000,  CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 0, MC_DELAY_COMMON +  30}, // T
    {AUD_SAMPRATE_88200,  CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 1, MC_DELAY_COMMON +  12},
    {AUD_SAMPRATE_96000,  CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 1, MC_DELAY_COMMON +  12}, // T
    {AUD_SAMPRATE_176400, CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 2, MC_DELAY_COMMON +   5},
    {AUD_SAMPRATE_192000, CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 2, MC_DELAY_COMMON +   5},
    {AUD_SAMPRATE_352800, CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 3, MC_DELAY_COMMON +   2},
    {AUD_SAMPRATE_384000, CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 3, MC_DELAY_COMMON +   2},
};

struct CODEC_ADC_SAMPLE_RATE_T {
    enum AUD_SAMPRATE_T sample_rate;
    uint32_t codec_freq;
    uint8_t codec_div;
    uint8_t cmu_div;
    uint8_t adc_down;
    uint8_t bypass_cnt;
};

static const struct CODEC_ADC_SAMPLE_RATE_T codec_adc_sample_rate[] = {
#ifdef __AUDIO_RESAMPLE__
    {AUD_SAMPRATE_8463,      CODEC_FREQ_CRYSTAL,               1,             1, 6, 0},
    {AUD_SAMPRATE_16927,     CODEC_FREQ_CRYSTAL,               1,             1, 3, 0},
    {AUD_SAMPRATE_50781,     CODEC_FREQ_CRYSTAL,               1,             1, 1, 0},
#endif
    {AUD_SAMPRATE_7350,   CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 6, 0},
    {AUD_SAMPRATE_8000,   CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 6, 0},
    {AUD_SAMPRATE_14700,  CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 3, 0},
    {AUD_SAMPRATE_16000,  CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 3, 0},
    {AUD_SAMPRATE_44100,  CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 0},
    {AUD_SAMPRATE_48000,  CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 0},
    {AUD_SAMPRATE_88200,  CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 1},
    {AUD_SAMPRATE_96000,  CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 1},
    {AUD_SAMPRATE_176400, CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 2},
    {AUD_SAMPRATE_192000, CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 2},
    {AUD_SAMPRATE_352800, CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 3},
    {AUD_SAMPRATE_384000, CODEC_FREQ_48K_SERIES,   CODEC_PLL_DIV, CODEC_CMU_DIV, 1, 3},
};

const CODEC_ADC_VOL_T WEAK codec_adc_vol[TGT_ADC_VOL_LEVEL_QTY] = {
    -99, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28,
};

static struct CODEC_T * const codec = (struct CODEC_T *)CODEC_BASE;

#define CODEC_FIR_CH0_BASE                              (CODEC_BASE + 0x9000)
#define CODEC_FIR_CH1_BASE                              (CODEC_BASE + 0xB000)
#define CODEC_FIR_CH2_BASE                              (CODEC_BASE + 0xD000)
#define CODEC_FIR_CH3_BASE                              (CODEC_BASE + 0xF000)

#define CODEC_FIR_HISTORY_CH0_BASE                      (CODEC_FIR_CH0_BASE - 0x1000)
#define CODEC_FIR_HISTORY_CH1_BASE                      (CODEC_FIR_CH1_BASE - 0x1000)
#define CODEC_FIR_HISTORY_CH2_BASE                      (CODEC_FIR_CH2_BASE - 0x1000)
#define CODEC_FIR_HISTORY_CH3_BASE                      (CODEC_FIR_CH3_BASE - 0x1000)

#ifdef CODEC_MIN_PHASE
static volatile int32_t * const codec_fir_ch0 = (int32_t *)CODEC_FIR_CH0_BASE;
static volatile int32_t * const codec_fir_ch1 = (int32_t *)CODEC_FIR_CH1_BASE;
static volatile int32_t * const codec_fir_ch2 = (int32_t *)CODEC_FIR_CH2_BASE;
static volatile int32_t * const codec_fir_ch3 = (int32_t *)CODEC_FIR_CH3_BASE;
static volatile int32_t * const codec_fir_history0 = (int32_t *)CODEC_FIR_HISTORY_CH0_BASE;
static volatile int32_t * const codec_fir_history1 = (int32_t *)CODEC_FIR_HISTORY_CH1_BASE;
static volatile int32_t * const codec_fir_history2 = (int32_t *)CODEC_FIR_HISTORY_CH2_BASE;
static volatile int32_t * const codec_fir_history3 = (int32_t *)CODEC_FIR_HISTORY_CH3_BASE;

#define MAX_FIR_ORDERS (512)

#define FIR_LOW_FREQ

#ifdef FIR_LOW_FREQ
#define FIR_DAC_ORDERS (220)
#define FIR_ADC_ORDERS (220)
#else
#define FIR_DAC_ORDERS (512)
#define FIR_ADC_ORDERS (512)
#endif

STATIC_ASSERT(FIR_DAC_ORDERS <= MAX_FIR_ORDERS, "FIR_DAC_ORDERS too large");
STATIC_ASSERT(FIR_ADC_ORDERS <= MAX_FIR_ORDERS, "FIR_ADC_ORDERS too large");

const static int32_t POSSIBLY_UNUSED fir_coef_linear[FIR_DAC_ORDERS] = {
    524287,
};

const static int32_t POSSIBLY_UNUSED fir_coef_minimun[FIR_DAC_ORDERS] = {
#ifdef FIR_LOW_FREQ
    171,1077,4203,12582,31473,68652,133899,237244,385800,579602,807418,1044189,1251727,1383990,1396843,1260694,972640,563948,
    99101,-335381,-650923,-780973,-701927,-443652,-84578,268808,512951,579186,455906,192655,-116310,-364122,-468309,-399443,
    -190845,74646,296174,392908,334154,149860,-82526,-269615,-339392,-269328,-94001,110604,259377,293401,203556,32780,-143605,
    -250723,-245944,-136029,27365,171442,234497,192404,67901,-80955,-187545,-206431,-132784,-3152,122291,187500,165597,69954,
    -53253,-147114,-170196,-114838,-9444,95806,152719,137136,59154,-42785,-120684,-139533,-93024,-5397,80987,126029,110487,
    44131,-39952,-101741,-113290,-71331,2185,71419,104055,86354,28846,-39643,-86369,-90279,-51459,9830,63615,84824,64943,15296,
    -39213,-72539,-69912,-34308,15781,55890,67480,46479,4496,-37461,-59466,-52097,-20389,19303,47689,51882,31200,-3160,-34103,
    -47104,-36994,-9893,20334,39133,38235,19235,-7752,-29414,-35781,-24769,-2693,19264,30682,26817,10499,-9713,-23968,-25906,
    -15440,1619,16733,22882,17788,4668,-9693,-18412,-17788,-8810,3652,13454,16181,11101,1226,-8416,-13305,-11533,-4481,4101,
    10062,10834,6508,-446,-6545,-9016,-7036,-1934,3627,7021,6880,3613,-961,-4595,-5701,-4025,-619,2769,4591,4179,1965,-839,
    -2887,-3327,-2140,-46,1903,2850,2488,1144,-458,-1565,-1740,-1020,170,1245,1754,1547,815,-53,-656,-761,-376,288,927,1290,
    1257,878,307,-256,-663,-840,-809,-642,-427,-237,-103,-33,
#else
    358,2134,7870,22383,53324,110934,206479,349044,541000,773159,1021370,1246556,1399740,1432299,1309745,1025462,609633,129002,
    -324731,-656532,-794417,-712592,-443054,-70120,292346,535048,587990,443750,160328,-158614,-400055,-481834,-382027,
    -146176,130807,341515,407598,309695,93804,-148511,-318068,-348626,-233320,-26063,182493,304084,290262,152119,-45991,
    -215703,-283797,-223953,-67174,113414,236243,248077,147280,-16729,-167204,-235765,-193530,-63427,91650,198950,209932,
    122330,-19887,-148250,-202614,-159367,-41765,92191,178176,176391,90055,-37106,-142792,-176569,-123965,-12686,101744,
    163572,143971,54527,-59287,-141093,-151499,-87091,18628,112527,148481,110037,17958,-80830,-137074,-123736,-49016,48438,
    119432,129027,73750,-17278,-97589,-127045,-91900,-11220,73360,119064,103602,36056,-48313,-106407,-109292,-56617,23738,
    90354,109596,72600,-665,-72105,-105274,-83967,-20135,52732,97141,90877,38119,-33174,-86035,-93649,-52950,14213,72765,
    92702,64465,3511,-58099,-88538,-72655,-19509,42731,81690,77624,33421,-27283,-72714,-79582,-45018,12285,62151,78801,54174,
    1818,-50528,-75615,-60867,-14676,38325,70381,65146,26015,-25988,-63481,-67140,-35653,13901,55293,67020,43473,-2402,-46194,
    -65008,-49433,-8237,36534,61346,53542,17793,-26648,-56304,-55870,-26107,16829,50150,56518,33065,-7346,-43163,-55631,-38609,
    -1582,35606,53375,42719,9762,-27735,-49938,-45423,-17053,19783,45514,46773,23343,-11967,-40312,-46863,-28563,4471,34530,45798,
    32673,2539,-28370,-43713,-35673,-8936,22017,40746,37582,14610,-15648,-37053,-38453,-19489,9418,32783,38351,23519,-3470,-28096,
    -37370,-26681,-2079,23137,35604,28971,7128,-18054,-33170,-30413,-11603,12974,30179,31045,15443,-8023,-26756,-30924,-18617,3302,
    23015,30118,21105,1094,-19077,-28707,-22913,-5093,15046,26776,24054,8635,-11031,-24419,-24565,-11680,7118,21726,24486,14198,-3394,
    -18792,-23876,-16181,-76,15704,22793,17629,3230,-12551,-21309,-18559,-6028,9408,19490,18994,8433,-6352,-17414,-18974,-10431,3440,
    15149,18537,12008,-731,-12769,-17737,-13172,-1735,10336,16622,13932,3920,-7916,-15252,-14311,-5805,5559,13680,14335,7371,-3319,-11967,
    -14040,-8615,1232,10161,13463,9538,664,-8320,-12648,-10154,-2348,6485,11634,10474,3797,-4703,-10470,-10527,-5006,3007,9196,10334,5966,
    -1433,-7856,-9930,-6686,1,6486,9343,7168,1266,-5126,-8610,-7432,-2359,3803,7762,7491,3267,-2551,-6835,-7369,-3991,1387,5858,7087,4531,
    -335,-4863,-6673,-4898,-597,3875,6149,5100,1395,-2920,-5545,-5154,-2058,2015,4882,5073,2582,-1181,-4187,-4878,-2974,427,3479,4586,3237,
    234,-2781,-4219,-3383,-799,2107,3793,3420,1262,-1476,-3331,-3363,-1628,894,2845,3224,1895,-375,-2357,-3021,-2074,-79,1875,2764,2167,459,
    -1416,-2470,-2186,-771,987,2151,2139,1009,-599,-1823,-2040,-1182,254,1492,1894,1290,40,-1172,-1717,-1342,-286,869,1516,1343,480,-592,-1303,
    -1303,-626,342,1084,1226,725,-127,-870,-1124,-784,-55,664,1001,803,200,-475,-869,-794,-313,303,729,756,392,-154,-592,-701,-443,28,458,630,468,74,
    -337,-553,-473,-155,226,471,462,215,-130,-394,-443,-263,45,319,420,302,31,-253,-404,-349,-115,181,394,418,237,-74,-375,-536,-472,-196,214,626,935,
    1062,1013,824,581,341,166,55,
#endif
};

static uint8_t min_phase_cfg;
#endif

static bool codec_init = false;
static bool codec_opened = false;

static int8_t digdac_gain[MAX_DAC_CH_NUM];
static int8_t digadc_gain[NORMAL_ADC_CH_NUM];

static bool codec_mute[AUD_STREAM_NUM];

#ifdef AUDIO_OUTPUT_SWAP
static bool output_swap = true;
#endif

#ifdef ANC_APP
static float anc_boost_gain_attn;
static int8_t anc_adc_gain_offset[NORMAL_ADC_CH_NUM];
static enum AUD_CHANNEL_MAP_T anc_adc_gain_offset_map;
#endif
#if defined(NOISE_GATING) && defined(NOISE_REDUCTION)
static bool codec_nr_enabled;
static int8_t digdac_gain_offset_nr;
#endif
#ifdef AUDIO_OUTPUT_DC_CALIB
static int32_t dac_dc_l;
static int32_t dac_dc_r;
static float dac_dc_gain_attn;
#endif
#ifdef __AUDIO_RESAMPLE__
static uint8_t rs_clk_map;
STATIC_ASSERT(sizeof(rs_clk_map) * 8 >= AUD_STREAM_NUM, "rs_clk_map size too small");

static uint32_t resample_clk_freq;
static uint8_t resample_rate_idx[AUD_STREAM_NUM];
#endif
#ifdef CODEC_TIMER
static uint32_t cur_codec_freq;
#endif
// EC
static uint8_t codec_rate_idx[AUD_STREAM_NUM];

//static HAL_CODEC_DAC_RESET_CALLBACK dac_reset_callback;

static uint8_t codec_irq_map;
STATIC_ASSERT(sizeof(codec_irq_map) * 8 >= CODEC_IRQ_TYPE_QTY, "codec_irq_map size too small");
static HAL_CODEC_IRQ_CALLBACK codec_irq_callback[CODEC_IRQ_TYPE_QTY];

static enum AUD_CHANNEL_MAP_T codec_dac_ch_map;
static enum AUD_CHANNEL_MAP_T codec_adc_ch_map;
static enum AUD_CHANNEL_MAP_T anc_adc_ch_map;
static enum AUD_CHANNEL_MAP_T codec_mic_ch_map;
static enum AUD_CHANNEL_MAP_T anc_mic_ch_map;
#ifdef SIDETONE_DEDICATED_ADC_CHAN
static enum AUD_CHANNEL_MAP_T sidetone_adc_ch_map;
static int8_t sidetone_adc_gain;
static int8_t sidetone_gain_offset;
#ifdef CFG_HW_AUD_SIDETONE_GAIN_RAMP
static float sidetone_ded_chan_coef;
#endif
#endif

static uint8_t dac_delay_ms;

#ifdef ANC_PROD_TEST
#define OPT_TYPE
#else
#define OPT_TYPE                        const
#endif

static OPT_TYPE uint8_t codec_digmic_phase = CODEC_DIGMIC_PHASE;

#if defined(AUDIO_ANC_FB_MC) && defined(__AUDIO_RESAMPLE__)
#error "Music cancel cannot work with audio resample"
#endif
#ifdef AUDIO_ANC_FB_MC
static bool mc_enabled;
static bool mc_dual_chan;
static bool mc_16bit;
#endif

#ifdef CODEC_DSD
static bool dsd_enabled;
static uint8_t dsd_rate_idx;
#endif

#if defined(AUDIO_ANC_FB_MC) || defined(CODEC_DSD)
static uint8_t adc_en_map;
STATIC_ASSERT(sizeof(adc_en_map) * 8 >= CODEC_ADC_EN_REQ_QTY, "adc_en_map size too small");
#endif

#ifdef PERF_TEST_POWER_KEY
static enum HAL_CODEC_PERF_TEST_POWER_T cur_perft_power;
#endif

#ifdef AUDIO_OUTPUT_SW_GAIN
static int8_t swdac_gain;
static HAL_CODEC_SW_OUTPUT_COEF_CALLBACK sw_output_coef_callback;
#endif

static HAL_CODEC_BT_TRIGGER_CALLBACK bt_trigger_callback = NULL;

#ifdef VOICE_DETECTOR_EN
#define CODEC_VAD_BUF_ADDR 0x40304000
#define CODEC_VAD_BUF_SIZE 0x4000
static enum AUD_VAD_TYPE_T vad_type;
static AUD_VAD_CALLBACK vad_handler;
static bool vad_enabled;
static uint32_t vad_data_cnt;
static uint32_t vad_addr_cnt;
#endif

#if defined(DAC_CLASSG_ENABLE)
static struct dac_classg_cfg _dac_classg_cfg = {
    .thd2 = 0xC0,
    .thd1 = 0x10,
    .thd0 = 0x10,
    .lr = 1,
    .step_4_3n = 0,
    .quick_down = 1,
    .wind_width = 0x400,
};
#endif

#ifdef DAC_DRE_ENABLE
static const struct CODEC_DAC_DRE_CFG_T dac_dre_cfg = {
    .dre_delay = 8,
    .thd_db_offset = 0xF, //5,
    .step_mode = 0,
    .dre_win = 0x6000,
    .amp_high = 2, //0x10,
};
#endif

static void hal_codec_set_dig_adc_gain(enum AUD_CHANNEL_MAP_T map, int32_t gain);
static void hal_codec_set_dig_dac_gain(enum AUD_CHANNEL_MAP_T map, int32_t gain);
static void hal_codec_restore_dig_dac_gain(void);
static void hal_codec_set_dac_gain_value(enum AUD_CHANNEL_MAP_T map, uint32_t val);
static int hal_codec_set_adc_down(enum AUD_CHANNEL_MAP_T map, uint32_t val);
static int hal_codec_set_adc_hbf_bypass_cnt(enum AUD_CHANNEL_MAP_T map, uint32_t cnt);
static uint32_t hal_codec_get_adc_chan(enum AUD_CHANNEL_MAP_T mic_map);
#ifdef AUDIO_OUTPUT_SW_GAIN
static void hal_codec_set_sw_gain(int32_t gain);
#endif
#ifdef __AUDIO_RESAMPLE__
static float get_playback_resample_phase(void);
static float get_capture_resample_phase(void);
static uint32_t resample_phase_float_to_value(float phase);
static float resample_phase_value_to_float(uint32_t value);
#endif

static void hal_codec_reg_update_delay(void)
{
    hal_sys_timer_delay_us(2);
}

#if defined(DAC_CLASSG_ENABLE)
void hal_codec_classg_config(const struct dac_classg_cfg *cfg)
{
    _dac_classg_cfg = *cfg;
}

static void hal_codec_classg_enable(bool en)
{
    struct dac_classg_cfg *config;

    if (en) {
        config = &_dac_classg_cfg;

        codec->REG_0B4 = SET_BITFIELD(codec->REG_0B4, CODEC_CODEC_CLASSG_THD2, config->thd2);
        codec->REG_0B4 = SET_BITFIELD(codec->REG_0B4, CODEC_CODEC_CLASSG_THD1, config->thd1);
        codec->REG_0B4 = SET_BITFIELD(codec->REG_0B4, CODEC_CODEC_CLASSG_THD0, config->thd0);

        // Make class-g set the lowest gain after several samples.
        // Class-g gain will have impact on dc.
        codec->REG_0B0 = SET_BITFIELD(codec->REG_0B0, CODEC_CODEC_CLASSG_WINDOW, 6);

        if (config->lr)
            codec->REG_0B0 |= CODEC_CODEC_CLASSG_LR;
        else
            codec->REG_0B0 &= ~CODEC_CODEC_CLASSG_LR;

        if (config->step_4_3n)
            codec->REG_0B0 |= CODEC_CODEC_CLASSG_STEP_3_4N;
        else
            codec->REG_0B0 &= ~CODEC_CODEC_CLASSG_STEP_3_4N;

        if (config->quick_down)
            codec->REG_0B0 |= CODEC_CODEC_CLASSG_QUICK_DOWN;
        else
            codec->REG_0B0 &= ~CODEC_CODEC_CLASSG_QUICK_DOWN;

        codec->REG_0B0 |= CODEC_CODEC_CLASSG_EN;

        // Restore class-g window after the gain has been updated
        hal_codec_reg_update_delay();
        codec->REG_0B0 = SET_BITFIELD(codec->REG_0B0, CODEC_CODEC_CLASSG_WINDOW, config->wind_width);
    } else {
        codec->REG_0B0 &= ~CODEC_CODEC_CLASSG_QUICK_DOWN;
    }
}
#endif

#if defined(AUDIO_OUTPUT_DC_CALIB) || defined(SDM_MUTE_NOISE_SUPPRESSION)
static void hal_codec_dac_dc_offset_enable(int32_t dc_l, int32_t dc_r)
{
    codec->REG_0E0 &= CODEC_CODEC_DAC_DC_UPDATE_CH0;
    hal_codec_reg_update_delay();
    codec->REG_0E8 = SET_BITFIELD(codec->REG_0E8, CODEC_CODEC_DAC_DC_CH1, dc_r);
    codec->REG_0E0 = SET_BITFIELD(codec->REG_0E0, CODEC_CODEC_DAC_DC_CH0, dc_l) | CODEC_CODEC_DAC_DC_UPDATE_CH0;
}
#endif

#ifdef CODEC_MIN_PHASE
static void hal_codec_min_phase_init(void)
{
    int i;

    // SYS clock should be 52M or above

    // DAC
    codec->REG_108 &= ~(CODEC_STREAM0_FIR1_CH0);
    codec->REG_108 = SET_BITFIELD(codec->REG_108,CODEC_FIR_MODE_CH0,0);
    codec->REG_108 = SET_BITFIELD(codec->REG_108,CODEC_FIR_ORDER_CH0,FIR_DAC_ORDERS);

    codec->REG_10C = SET_BITFIELD(codec->REG_10C,CODEC_FIR_BURST_LENGTH_CH0,4);
    codec->REG_10C = SET_BITFIELD(codec->REG_10C,CODEC_FIR_GAIN_SEL_CH0,4);

    codec->REG_110 &= ~(CODEC_STREAM0_FIR1_CH1);
    codec->REG_110 = SET_BITFIELD(codec->REG_110,CODEC_FIR_MODE_CH1,0);
    codec->REG_110 = SET_BITFIELD(codec->REG_110,CODEC_FIR_ORDER_CH1,FIR_DAC_ORDERS);

    codec->REG_114 = SET_BITFIELD(codec->REG_114,CODEC_FIR_BURST_LENGTH_CH1,4);
    codec->REG_114 = SET_BITFIELD(codec->REG_114,CODEC_FIR_GAIN_SEL_CH1,4);

    // ADC
    codec->REG_118 &= ~(CODEC_STREAM0_FIR1_CH2);
    codec->REG_118 = SET_BITFIELD(codec->REG_118,CODEC_FIR_MODE_CH2,0);
    codec->REG_118 = SET_BITFIELD(codec->REG_118,CODEC_FIR_ORDER_CH2,FIR_ADC_ORDERS);

    //codec->REG_11C = SET_BITFIELD(codec->REG_11C,CODEC_FIR_BURST_LENGTH_CH2,4);
    codec->REG_11C = SET_BITFIELD(codec->REG_11C,CODEC_FIR_GAIN_SEL_CH2,6);

    codec->REG_120 &= ~(CODEC_STREAM0_FIR1_CH3);
    codec->REG_120 = SET_BITFIELD(codec->REG_120,CODEC_FIR_MODE_CH3,0);
    codec->REG_120 = SET_BITFIELD(codec->REG_120,CODEC_FIR_ORDER_CH3,FIR_ADC_ORDERS);

    //codec->REG_124 = SET_BITFIELD(codec->REG_124,CODEC_FIR_BURST_LENGTH_CH3,4);
    codec->REG_124 = SET_BITFIELD(codec->REG_124,CODEC_FIR_GAIN_SEL_CH3,6);

    // DAC
    codec->REG_100 |= (CODEC_FIR_UPSAMPLE_CH0 | CODEC_FIR_UPSAMPLE_CH1);

    for (i = 0; i < FIR_DAC_ORDERS; i++) {
        codec_fir_ch0[i] = fir_coef_minimun[i];
        codec_fir_ch1[i] = fir_coef_minimun[i];
    }

    // ADC
    codec->REG_100 &= ~(CODEC_FIR_UPSAMPLE_CH2 | CODEC_FIR_UPSAMPLE_CH3);

    for (i = 0; i < FIR_DAC_ORDERS; i++) {
        codec_fir_ch2[i] = fir_coef_minimun[i];
        codec_fir_ch3[i] = fir_coef_minimun[i];
    }

    // Init history buffer
    for (i=0; i < MAX_FIR_ORDERS; i++) {
        codec_fir_history0[i] = 0;
        codec_fir_history1[i] = 0;
        codec_fir_history2[i] = 0;
        codec_fir_history3[i] = 0;
    }
}

static void hal_codec_min_phase_term(void)
{
    // Release SYS clock request
}
#endif

int hal_codec_open(enum HAL_CODEC_ID_T id)
{
    int i;
    bool first_open;

#ifdef CODEC_POWER_DOWN
    first_open = true;
#else
    first_open = !codec_init;
#endif

    analog_aud_pll_open(ANA_AUD_PLL_USER_CODEC);

    if (!codec_init) {
        for (i = 0; i < CFG_HW_AUD_INPUT_PATH_NUM; i++) {
            if (cfg_audio_input_path_cfg[i].cfg & AUD_CHANNEL_MAP_ALL & ~VALID_MIC_MAP) {
                ASSERT(false, "Invalid input path cfg: i=%d io_path=%d cfg=0x%X",
                    i, cfg_audio_input_path_cfg[i].io_path, cfg_audio_input_path_cfg[i].cfg);
            }
        }
#ifdef ANC_APP
        anc_boost_gain_attn = 1.0f;
#endif
        codec_init = true;
    }
    if (first_open) {
        // Codec will be powered down first
        hal_psc_codec_enable();
    }
    hal_cmu_codec_clock_enable();
    hal_cmu_codec_reset_clear();

    codec_opened = true;

    codec->REG_060 |= CODEC_EN_CLK_ADC_MASK | CODEC_EN_CLK_ADC_ANA_MASK | CODEC_POL_ADC_ANA_MASK | CODEC_POL_DAC_OUT;
    codec->REG_064 |= CODEC_SOFT_RSTN_32K | CODEC_SOFT_RSTN_IIR | CODEC_SOFT_RSTN_RS |
        CODEC_SOFT_RSTN_DAC | CODEC_SOFT_RSTN_ADC_MASK | CODEC_SOFT_RSTN_ADC_ANA_MASK;
    codec->REG_000 = 0;
    codec->REG_04C &= ~CODEC_MC_ENABLE;
    codec->REG_004 = ~0UL;
    hal_codec_reg_update_delay();
    codec->REG_004 = 0;
    codec->REG_000 |= CODEC_CODEC_IF_EN;

    codec->REG_054 |= CODEC_FAULT_MUTE_DAC_ENABLE;

#ifdef AUDIO_OUTPUT_SWAP
    if (output_swap) {
        codec->REG_0A0 |= CODEC_CODEC_DAC_OUT_SWAP;
    } else {
        codec->REG_0A0 &= ~CODEC_CODEC_DAC_OUT_SWAP;
    }
#endif

    if (first_open) {
#ifdef AUDIO_ANC_FB_MC
        codec->REG_04C = CODEC_DMA_CTRL_MC;
        codec->REG_230 |= CODEC_MC_EN_SEL|CODEC_MC_RATE_SRC_SEL;
#endif

        // ANC zero-crossing
        codec->REG_0D4 |= CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FF_CH0 | CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FF_CH1;
        codec->REG_0D8 |= CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FB_CH0 | CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FB_CH1;

        // Enable ADC zero-crossing gain adjustment
        for (i = 0; i < NORMAL_ADC_CH_NUM; i++) {
            *(&codec->REG_084 + i) |= CODEC_CODEC_ADC_GAIN_SEL_CH0;
        }

        // DRE ini gain and offset
        uint8_t max_gain, ini_gain, dre_offset;
        max_gain = analog_aud_get_max_dre_gain();
        if (max_gain < 0xF) {
            ini_gain = 0xF - max_gain;
        } else {
            ini_gain = 0;
        }
        if (max_gain > 0xF) {
            dre_offset = max_gain - 0xF;
        } else {
            dre_offset = 0;
        }
        codec->REG_0C0 = CODEC_CODEC_DRE_INI_ANA_GAIN_CH0(ini_gain) | CODEC_CODEC_DRE_GAIN_OFFSET_CH0(dre_offset);
        codec->REG_0C8 = CODEC_CODEC_DRE_INI_ANA_GAIN_CH1(ini_gain) | CODEC_CODEC_DRE_GAIN_OFFSET_CH1(dre_offset);
        codec->REG_0E0 = CODEC_CODEC_DAC_ANA_GAIN_UPDATE_DELAY_CH0(0);
        codec->REG_0E8 = CODEC_CODEC_DAC_ANA_GAIN_UPDATE_DELAY_CH1(0);

#ifdef ANC_PROD_TEST
#ifdef AUDIO_ANC_FB_MC
        // Enable ADC + music cancel.
        codec->REG_130 |= CODEC_CODEC_FB_CHECK_KEEP_CH0;
        codec->REG_134 |= CODEC_CODEC_FB_CHECK_KEEP_CH1;
#elif defined(AUDIO_ANC_FB_MC_HW)
        // Enable ADC + music cancel.
        codec->REG_130 |= CODEC_CODEC_FB_CHECK_KEEP_CH0;
#endif
#endif

#if defined(FIXED_CODEC_ADC_VOL) && defined(SINGLE_CODEC_ADC_VOL)
        const CODEC_ADC_VOL_T *adc_gain_db;

        adc_gain_db = hal_codec_get_adc_volume(CODEC_SADC_VOL);
        if (adc_gain_db) {
            hal_codec_set_dig_adc_gain(NORMAL_ADC_MAP, *adc_gain_db);
#ifdef SIDETONE_DEDICATED_ADC_CHAN
            sidetone_adc_gain = *adc_gain_db;
#endif
        }
#endif

#ifdef AUDIO_OUTPUT_DC_CALIB
        hal_codec_dac_dc_offset_enable(dac_dc_l, dac_dc_r);
#elif defined(SDM_MUTE_NOISE_SUPPRESSION)
        hal_codec_dac_dc_offset_enable(1, 1);
#endif

#ifdef AUDIO_OUTPUT_SW_GAIN
        const struct CODEC_DAC_VOL_T *vol_tab_ptr;

        // Init gain settings
        vol_tab_ptr = hal_codec_get_dac_volume(0);
        if (vol_tab_ptr) {
            analog_aud_set_dac_gain(vol_tab_ptr->tx_pa_gain);
            hal_codec_set_dig_dac_gain(VALID_DAC_MAP, ZERODB_DIG_DBVAL);
        }
#else
        // Enable DAC zero-crossing gain adjustment
        codec->REG_09C |= CODEC_CODEC_DAC_GAIN_SEL_CH0;
        codec->REG_0A0 |= CODEC_CODEC_DAC_GAIN_SEL_CH1;
#endif

#ifdef AUDIO_OUTPUT_DC_CALIB_ANA
        // Reset SDM
        hal_codec_set_dac_gain_value(VALID_DAC_MAP, 0);
        codec->REG_098 |= CODEC_CODEC_DAC_SDM_CLOSE;
#endif

#ifdef SDM_MUTE_NOISE_SUPPRESSION
        codec->REG_098 = SET_BITFIELD(codec->REG_098, CODEC_CODEC_DAC_DITHER_GAIN, 0x10);
#endif

#ifdef __AUDIO_RESAMPLE__
        codec->REG_0E4 &= ~(CODEC_CODEC_RESAMPLE_DAC_ENABLE | CODEC_CODEC_RESAMPLE_ADC_ENABLE |
            CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE | CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE);
#endif

#ifdef CODEC_DSD
        for(i = 0; i < ARRAY_SIZE(codec_adc_sample_rate); i++) {
            if(codec_adc_sample_rate[i].sample_rate == AUD_SAMPRATE_44100) {
                break;
            }
        }
        hal_codec_set_adc_down((AUD_CHANNEL_MAP_CH2 | AUD_CHANNEL_MAP_CH3), codec_adc_sample_rate[i].adc_down);
        hal_codec_set_adc_hbf_bypass_cnt((AUD_CHANNEL_MAP_CH2 | AUD_CHANNEL_MAP_CH3), codec_adc_sample_rate[i].bypass_cnt);
#endif

        // Mute DAC when cpu fault occurs
        hal_cmu_codec_set_fault_mask(0x3F);

#ifdef CODEC_TIMER
        // Disable sync stamp auto clear to avoid impacting codec timer capture
        codec->REG_054 &= ~CODEC_STAMP_CLR_USED;
#else
        // Enable sync stamp auto clear
        codec->REG_054 |= CODEC_STAMP_CLR_USED;
#endif
    }

#ifdef CODEC_MIN_PHASE
    if (min_phase_cfg) {
        hal_codec_min_phase_init();
    }
#endif

    return 0;
}

int hal_codec_close(enum HAL_CODEC_ID_T id)
{
#ifdef CODEC_MIN_PHASE
    if (min_phase_cfg) {
        hal_codec_min_phase_term();
    }
#endif

    codec->REG_054 &= ~CODEC_FAULT_MUTE_DAC_ENABLE;

    codec->REG_000 &= ~CODEC_CODEC_IF_EN;
    codec->REG_064 &= ~(CODEC_SOFT_RSTN_32K | CODEC_SOFT_RSTN_IIR | CODEC_SOFT_RSTN_RS |
        CODEC_SOFT_RSTN_DAC | CODEC_SOFT_RSTN_ADC_MASK | CODEC_SOFT_RSTN_ADC_ANA_MASK);
    codec->REG_060 &= ~(CODEC_EN_CLK_ADC_MASK | CODEC_EN_CLK_ADC_ANA_MASK);

    codec_opened = false;

#ifdef CODEC_POWER_DOWN
    hal_cmu_codec_reset_set();
    hal_cmu_codec_clock_disable();
    hal_psc_codec_disable();
#else
    // NEVER reset or power down CODEC registers, for the CODEC driver expects that last configurations
    // still exist in the next stream setup
    hal_cmu_codec_clock_disable();
#endif

    analog_aud_pll_close(ANA_AUD_PLL_USER_CODEC);

    return 0;
}

void hal_codec_crash_mute(void)
{
    if (codec_opened) {
        codec->REG_000 &= ~CODEC_CODEC_IF_EN;
    }
}

#ifdef DAC_DRE_ENABLE
static void hal_codec_dac_dre_enable(void)
{
    codec->REG_0C0 = (codec->REG_0C0 & ~(CODEC_CODEC_DRE_THD_DB_OFFSET_SIGN_CH0 | CODEC_CODEC_DRE_DELAY_CH0_MASK |
            CODEC_CODEC_DRE_INI_ANA_GAIN_CH0_MASK | CODEC_CODEC_DRE_THD_DB_OFFSET_CH0_MASK | CODEC_CODEC_DRE_STEP_MODE_CH0_MASK)) |
        CODEC_CODEC_DRE_DELAY_CH0(dac_dre_cfg.dre_delay) |
        CODEC_CODEC_DRE_INI_ANA_GAIN_CH0(0xF) | CODEC_CODEC_DRE_THD_DB_OFFSET_CH0(dac_dre_cfg.thd_db_offset) |
        CODEC_CODEC_DRE_THD_DB_OFFSET_CH0(dac_dre_cfg.step_mode) | CODEC_CODEC_DRE_ENABLE_CH0;
    codec->REG_0C4 = (codec->REG_0C4 & ~(CODEC_CODEC_DRE_WINDOW_CH0_MASK | CODEC_CODEC_DRE_AMP_HIGH_CH0_MASK)) |
        CODEC_CODEC_DRE_WINDOW_CH0(dac_dre_cfg.dre_win) | CODEC_CODEC_DRE_AMP_HIGH_CH0(dac_dre_cfg.amp_high);

    codec->REG_0C8 = (codec->REG_0C8 & ~(CODEC_CODEC_DRE_THD_DB_OFFSET_SIGN_CH1 | CODEC_CODEC_DRE_DELAY_CH1_MASK |
            CODEC_CODEC_DRE_INI_ANA_GAIN_CH1_MASK | CODEC_CODEC_DRE_THD_DB_OFFSET_CH1_MASK | CODEC_CODEC_DRE_STEP_MODE_CH1_MASK)) |
        CODEC_CODEC_DRE_DELAY_CH1(dac_dre_cfg.dre_delay) |
        CODEC_CODEC_DRE_INI_ANA_GAIN_CH1(0xF) | CODEC_CODEC_DRE_THD_DB_OFFSET_CH1(dac_dre_cfg.thd_db_offset) |
        CODEC_CODEC_DRE_THD_DB_OFFSET_CH1(dac_dre_cfg.step_mode) | CODEC_CODEC_DRE_ENABLE_CH1;
    codec->REG_0CC = (codec->REG_0CC & ~(CODEC_CODEC_DRE_WINDOW_CH1_MASK | CODEC_CODEC_DRE_AMP_HIGH_CH1_MASK)) |
        CODEC_CODEC_DRE_WINDOW_CH1(dac_dre_cfg.dre_win) | CODEC_CODEC_DRE_AMP_HIGH_CH1(dac_dre_cfg.amp_high);
}

static void hal_codec_dac_dre_disable(void)
{
    codec->REG_0C0 &= ~CODEC_CODEC_DRE_ENABLE_CH0;
    codec->REG_0C8 &= ~CODEC_CODEC_DRE_ENABLE_CH1;
}
#endif

#ifdef PERF_TEST_POWER_KEY
static void hal_codec_update_perf_test_power(void)
{
    int32_t nominal_vol;
    uint32_t ini_ana_gain;
    int32_t dac_vol;

    if (!codec_opened) {
        return;
    }

    dac_vol = 0;
    if (cur_perft_power == HAL_CODEC_PERF_TEST_30MW) {
        nominal_vol = 0;
        ini_ana_gain = 0;
    } else if (cur_perft_power == HAL_CODEC_PERF_TEST_10MW) {
        nominal_vol = -5;
        ini_ana_gain = 6;
    } else if (cur_perft_power == HAL_CODEC_PERF_TEST_5MW) {
        nominal_vol = -8;
        ini_ana_gain = 0xA;
    } else if (cur_perft_power == HAL_CODEC_PERF_TEST_M60DB) {
        nominal_vol = -60;
        ini_ana_gain = 0xF; // about -11 dB
        dac_vol = -49;
    } else {
        return;
    }

    if (codec->REG_0C0 & CODEC_CODEC_DRE_ENABLE_CH0) {
        dac_vol = nominal_vol;
    } else {
        codec->REG_0C0 = SET_BITFIELD(codec->REG_0C0, CODEC_CODEC_DRE_INI_ANA_GAIN_CH0, ini_ana_gain);
        codec->REG_0C8 = SET_BITFIELD(codec->REG_0C8, CODEC_CODEC_DRE_INI_ANA_GAIN_CH1, ini_ana_gain);
    }

#ifdef AUDIO_OUTPUT_SW_GAIN
    hal_codec_set_sw_gain(dac_vol);
#else
    hal_codec_set_dig_dac_gain(VALID_DAC_MAP, dac_vol);
#endif

#if defined(NOISE_GATING) && defined(NOISE_REDUCTION)
    if (codec_nr_enabled) {
        codec_nr_enabled = false;
        hal_codec_set_noise_reduction(true);
    }
#endif
}

void hal_codec_dac_gain_m60db_check(enum HAL_CODEC_PERF_TEST_POWER_T type)
{
    cur_perft_power = type;

    if (!codec_opened || (codec->REG_098 & CODEC_CODEC_DAC_EN) == 0) {
        return;
    }

    hal_codec_update_perf_test_power();
}
#endif

#if defined(NOISE_GATING) && defined(NOISE_REDUCTION)
void hal_codec_set_noise_reduction(bool enable)
{
    uint32_t ini_ana_gain;

    if (codec_nr_enabled == enable) {
        // Avoid corrupting digdac_gain_offset_nr or using an invalid one
        return;
    }

    codec_nr_enabled = enable;

    if (!codec_opened) {
        return;
    }

    // ini_ana_gain=0   -->   0dB
    // ini_ana_gain=0xF --> -11dB
    if (enable) {
        ini_ana_gain = GET_BITFIELD(codec->REG_0C0, CODEC_CODEC_DRE_INI_ANA_GAIN_CH0);
        digdac_gain_offset_nr = ((0xF - ini_ana_gain) * 11 + 0xF / 2) / 0xF;
        ini_ana_gain = 0xF;
    } else {
        ini_ana_gain = 0xF - (digdac_gain_offset_nr * 0xF + 11 / 2) / 11;
        digdac_gain_offset_nr = 0;
    }

    codec->REG_0C0 = SET_BITFIELD(codec->REG_0C0, CODEC_CODEC_DRE_INI_ANA_GAIN_CH0, ini_ana_gain);
    codec->REG_0C8 = SET_BITFIELD(codec->REG_0C8, CODEC_CODEC_DRE_INI_ANA_GAIN_CH1, ini_ana_gain);

#ifdef AUDIO_OUTPUT_SW_GAIN
    hal_codec_set_sw_gain(swdac_gain);
#else
    hal_codec_restore_dig_dac_gain();
#endif
}
#endif

void hal_codec_stop_playback_stream(enum HAL_CODEC_ID_T id)
{
#if (defined(AUDIO_OUTPUT_DC_CALIB_ANA) || defined(AUDIO_OUTPUT_DC_CALIB)) && (!(defined(__TWS__) || defined(IBRT)) || defined(ANC_APP))
    // Disable PA
    analog_aud_codec_speaker_enable(false);
#endif

    codec->REG_098 &= ~(CODEC_CODEC_DAC_EN | CODEC_CODEC_DAC_EN_CH0 | CODEC_CODEC_DAC_EN_CH1);

#ifdef CODEC_TIMER
    // Reset codec timer
    codec->REG_054 &= ~CODEC_EVENT_FOR_CAPTURE;
#endif

#ifdef DAC_DRE_ENABLE
    hal_codec_dac_dre_disable();
#endif

#if defined(DAC_CLASSG_ENABLE)
    hal_codec_classg_enable(false);
#endif

#ifndef NO_DAC_RESET
    // Reset DAC
    // Avoid DAC outputing noise after it is disabled
    codec->REG_064 &= ~CODEC_SOFT_RSTN_DAC;
    codec->REG_064 |= CODEC_SOFT_RSTN_DAC;
#endif
    codec->REG_060 &= ~CODEC_EN_CLK_DAC;
}

void hal_codec_start_playback_stream(enum HAL_CODEC_ID_T id)
{
    codec->REG_060 |= CODEC_EN_CLK_DAC;
#ifndef NO_DAC_RESET
    // Reset DAC
    codec->REG_064 &= ~CODEC_SOFT_RSTN_DAC;
    codec->REG_064 |= CODEC_SOFT_RSTN_DAC;
#endif

#ifdef DAC_DRE_ENABLE
    if (
            //(codec->REG_044 & CODEC_MODE_16BIT_DAC) == 0 &&
#ifdef ANC_APP
            anc_adc_ch_map == 0 &&
#endif
            1
            )
    {
        hal_codec_dac_dre_enable();
    }
#endif

#ifdef PERF_TEST_POWER_KEY
    hal_codec_update_perf_test_power();
#endif

#if defined(DAC_CLASSG_ENABLE)
    hal_codec_classg_enable(true);
#endif

#ifdef CODEC_TIMER
    // Enable codec timer and record time by bt event instead of gpio event
    codec->REG_054 = (codec->REG_054 & ~CODEC_EVENT_SEL) | CODEC_EVENT_FOR_CAPTURE;
#endif

    if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH0) {
        codec->REG_098 |= CODEC_CODEC_DAC_EN_CH0;
    } else {
        codec->REG_098 &= ~CODEC_CODEC_DAC_EN_CH0;
    }
    if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH1) {
        codec->REG_098 |= CODEC_CODEC_DAC_EN_CH1;
    } else {
        codec->REG_098 &= ~CODEC_CODEC_DAC_EN_CH1;
    }

#if (defined(AUDIO_OUTPUT_DC_CALIB_ANA) || defined(AUDIO_OUTPUT_DC_CALIB)) && (!(defined(__TWS__) || defined(IBRT)) || defined(ANC_APP))
#if 0
    uint32_t cfg_en;
    uint32_t anc_ff_gain, anc_fb_gain;

    cfg_en = codec->REG_000 & CODEC_DAC_ENABLE;
    anc_ff_gain = codec->REG_0D4;
    anc_fb_gain = codec->REG_0D8;

    if (cfg_en) {
        codec->REG_000 &= ~cfg_en;
    }
    if (anc_ff_gain) {
        codec->REG_0D4 = CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FF_CH0 | CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FF_CH1;
        anc_ff_gain |= CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FF_CH0 | CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FF_CH1;
    }
    if (anc_fb_gain) {
        codec->REG_0D8 = CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FB_CH0 | CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FB_CH1;
        anc_fb_gain = CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FB_CH0 | CODEC_CODEC_ANC_MUTE_GAIN_PASS0_FB_CH1;
    }
    osDelay(1);
#endif
#endif

    codec->REG_098 |= CODEC_CODEC_DAC_EN;

#if (defined(AUDIO_OUTPUT_DC_CALIB_ANA) || defined(AUDIO_OUTPUT_DC_CALIB)) && (!(defined(__TWS__) || defined(IBRT)) || defined(ANC_APP))
#ifdef AUDIO_OUTPUT_DC_CALIB
    // At least delay 4ms for 8K-sample-rate mute data to arrive at DAC PA
    osDelay(5);
#endif

#if 0
    if (cfg_en) {
        codec->REG_000 |= cfg_en;
    }
    if (anc_ff_gain) {
        codec->REG_0D4 = anc_ff_gain;
    }
    if (anc_fb_gain) {
        codec->REG_0D8 = anc_fb_gain;
    }
#endif

    // Enable PA
    analog_aud_codec_speaker_enable(true);

#ifdef AUDIO_ANC_FB_MC
    if (mc_enabled) {
        uint32_t lock;
        lock = int_lock();
        // MC FIFO and DAC FIFO must be started at the same time
        codec->REG_04C |= CODEC_MC_ENABLE;
        codec->REG_000 |= CODEC_DAC_ENABLE;
        int_unlock(lock);
    }
#endif
#endif
}

#ifdef AF_ADC_I2S_SYNC
static bool _hal_codec_capture_enable_delay = false;

void hal_codec_capture_enable_delay(void)
{
    _hal_codec_capture_enable_delay = true;
}

void hal_codec_capture_enable(void)
{
	codec->REG_080 |= CODEC_CODEC_ADC_EN;
}
#endif

int hal_codec_start_stream(enum HAL_CODEC_ID_T id, enum AUD_STREAM_T stream)
{
    if (stream == AUD_STREAM_PLAYBACK) {
        // Reset and start DAC
        hal_codec_start_playback_stream(id);
    } else {
#if defined(AUDIO_ANC_FB_MC) || defined(CODEC_DSD)
        adc_en_map |= (1 << CODEC_ADC_EN_REQ_STREAM);
        if (adc_en_map == (1 << CODEC_ADC_EN_REQ_STREAM))
#endif
        {
            // Reset ADC ANA
            codec->REG_064 &= ~CODEC_SOFT_RSTN_ADC_ANA_MASK;
            codec->REG_064 |= CODEC_SOFT_RSTN_ADC_ANA_MASK;

#ifdef AF_ADC_I2S_SYNC
            if (_hal_codec_capture_enable_delay)
            {
                _hal_codec_capture_enable_delay = false;
            }
            else
            {
                hal_codec_capture_enable();
            }
#else
            codec->REG_080 |= CODEC_CODEC_ADC_EN;
#endif
        }
    }

    return 0;
}

int hal_codec_stop_stream(enum HAL_CODEC_ID_T id, enum AUD_STREAM_T stream)
{
    if (stream == AUD_STREAM_PLAYBACK) {
        // Stop and reset DAC
        hal_codec_stop_playback_stream(id);
    } else {
#if defined(AUDIO_ANC_FB_MC) || defined(CODEC_DSD)
        adc_en_map &= ~(1 << CODEC_ADC_EN_REQ_STREAM);
        if (adc_en_map == 0)
#endif
        {
            codec->REG_080 &= ~CODEC_CODEC_ADC_EN;
#ifdef AF_ADC_I2S_SYNC
            _hal_codec_capture_enable_delay = false;
#endif
        }
    }

    return 0;
}

#ifdef CODEC_DSD
void hal_codec_dsd_enable(void)
{
    dsd_enabled = true;
}

void hal_codec_dsd_disable(void)
{
    dsd_enabled = false;
}

static void hal_codec_dsd_cfg_start(void)
{
#if !(defined(FIXED_CODEC_ADC_VOL) && defined(SINGLE_CODEC_ADC_VOL))
    uint32_t vol;
    const CODEC_ADC_VOL_T *adc_gain_db;

    vol = hal_codec_get_mic_chan_volume_level(AUD_CHANNEL_MAP_DIGMIC_CH2);
    adc_gain_db = hal_codec_get_adc_volume(vol);
    if (adc_gain_db) {
        hal_codec_set_dig_adc_gain((AUD_CHANNEL_MAP_CH2 | AUD_CHANNEL_MAP_CH3), *adc_gain_db);
    }
#endif

    codec->REG_004 |= CODEC_DSD_RX_FIFO_FLUSH | CODEC_DSD_TX_FIFO_FLUSH;
    hal_codec_reg_update_delay();
    codec->REG_004 &= ~(CODEC_DSD_RX_FIFO_FLUSH | CODEC_DSD_TX_FIFO_FLUSH);

    codec->REG_0B8 = CODEC_CODEC_DSD_ENABLE_L | CODEC_CODEC_DSD_ENABLE_R | CODEC_CODEC_DSD_SAMPLE_RATE(dsd_rate_idx);
    codec->REG_048 = CODEC_DSD_IF_EN | CODEC_DSD_ENABLE | CODEC_DSD_DUAL_CHANNEL | CODEC_MODE_24BIT_DSD |
        /* CODEC_DMA_CTRL_RX_DSD | */ CODEC_DMA_CTRL_TX_DSD | CODEC_DSD_IN_16BIT;

    codec->REG_080 = (codec->REG_080 & ~(CODEC_CODEC_LOOP_SEL_L_MASK | CODEC_CODEC_LOOP_SEL_R_MASK)) |
        CODEC_CODEC_ADC_LOOP | CODEC_CODEC_LOOP_SEL_L(2) | CODEC_CODEC_LOOP_SEL_R(3);

    codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH2, 2);
    codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH2;
    codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH3, 3);
    codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH3;

    codec->REG_064 &= ~(CODEC_SOFT_RSTN_ADC(1 << 2) | CODEC_SOFT_RSTN_ADC(1 << 3));
    codec->REG_064 |= CODEC_SOFT_RSTN_ADC(1 << 2) | CODEC_SOFT_RSTN_ADC(1 << 3);
    codec->REG_080 |= CODEC_CODEC_ADC_EN_CH2 | CODEC_CODEC_ADC_EN_CH3;

    if (adc_en_map == 0) {
        // Reset ADC free running clock and ADC ANA
        codec->REG_064 &= ~(RSTN_ADC_FREE_RUNNING_CLK | CODEC_SOFT_RSTN_ADC_ANA_MASK);
        codec->REG_064 |= (RSTN_ADC_FREE_RUNNING_CLK | CODEC_SOFT_RSTN_ADC_ANA_MASK);
        codec->REG_080 |= CODEC_CODEC_ADC_EN;
    }
    adc_en_map |= (1 << CODEC_ADC_EN_REQ_DSD);
}

static void hal_codec_dsd_cfg_stop(void)
{
    adc_en_map &= ~(1 << CODEC_ADC_EN_REQ_DSD);
    if (adc_en_map == 0) {
        codec->REG_080 &= ~CODEC_CODEC_ADC_EN;
    }

    codec->REG_080 &= ~(CODEC_CODEC_ADC_EN_CH2 | CODEC_CODEC_ADC_EN_CH3);
    codec->REG_0A4 &= ~(CODEC_CODEC_PDM_ADC_SEL_CH2 | CODEC_CODEC_PDM_ADC_SEL_CH3);
    codec->REG_048 = 0;
    codec->REG_0B8 = 0;

    codec->REG_080 &= ~CODEC_CODEC_ADC_LOOP;
}
#endif

#ifdef __AUDIO_RESAMPLE__
void hal_codec_resample_clock_enable(enum AUD_STREAM_T stream)
{
    uint32_t clk;
    bool set = false;

    // 192K-24BIT requires 52M clock, and 384K-24BIT requires 104M clock
    if (stream == AUD_STREAM_PLAYBACK) {
        clk = codec_dac_sample_rate[resample_rate_idx[AUD_STREAM_PLAYBACK]].sample_rate * RS_CLOCK_FACTOR;
    } else {
        clk = codec_adc_sample_rate[resample_rate_idx[AUD_STREAM_CAPTURE]].sample_rate * RS_CLOCK_FACTOR;
    }

    if (rs_clk_map == 0) {
        set = true;
    } else {
        if (resample_clk_freq < clk) {
            set = true;
        }
    }

    if (set) {
        resample_clk_freq = clk;
        hal_cmu_codec_rs_enable(clk);
    }

    rs_clk_map |= (1 << stream);
}

void hal_codec_resample_clock_disable(enum AUD_STREAM_T stream)
{
    if (rs_clk_map == 0) {
        return;
    }
    rs_clk_map &= ~(1 << stream);
    if (rs_clk_map == 0) {
        hal_cmu_codec_rs_disable();
    }
}
#endif

static void hal_codec_enable_dig_mic(enum AUD_CHANNEL_MAP_T mic_map)
{
    uint32_t phase = 0;
    uint32_t line_map = 0;

    phase = codec->REG_0A8;
    for (int i = 0; i < MAX_DIG_MIC_CH_NUM; i++) {
        if (mic_map & (AUD_CHANNEL_MAP_DIGMIC_CH0 << i)) {
            line_map |= (1 << (i / 2));
        }
        phase = (phase & ~(CODEC_CODEC_PDM_CAP_PHASE_CH0_MASK << (i * 2))) |
            (CODEC_CODEC_PDM_CAP_PHASE_CH0(codec_digmic_phase) << (i * 2));
    }
    codec->REG_0A8 = phase;
    codec->REG_0A4 |= CODEC_CODEC_PDM_ENABLE;
    hal_iomux_set_dig_mic(line_map);
}

static void hal_codec_disable_dig_mic(void)
{
    codec->REG_0A4 &= ~CODEC_CODEC_PDM_ENABLE;
}

static void hal_codec_set_ec_down_sel(bool dac_rate_valid)
{
    uint8_t dac_factor;
    uint8_t adc_factor;
    uint8_t d, a;
    uint8_t val;
    uint8_t sel = 0;
    bool err = false;

    if (dac_rate_valid) {
        d = codec_rate_idx[AUD_STREAM_PLAYBACK];
        if (codec_dac_sample_rate[d].sample_rate < AUD_SAMPRATE_44100) {
            dac_factor = (6 / codec_dac_sample_rate[d].dac_up) * (codec_dac_sample_rate[d].bypass_cnt + 1);
        } else {
            // SINC to 48K/44.1K
            dac_factor = (6 / 1) * (0 + 1);
        }
        a = codec_rate_idx[AUD_STREAM_CAPTURE];
        adc_factor = (6 / codec_adc_sample_rate[a].adc_down) * (codec_adc_sample_rate[a].bypass_cnt + 1);

        val = dac_factor / adc_factor;
        if (val * adc_factor == dac_factor) {
            if (val == 3) {
                sel = 0;
            } else if (val == 6) {
                sel = 1;
            } else if (val == 1) {
                sel = 2;
            } else if (val == 2) {
                sel = 3;
            } else {
                err = true;
            }
        } else {
            err = true;
        }

        ASSERT(!err, "%s: Invalid EC sample rate: play=%u cap=%u", __FUNCTION__,
            codec_dac_sample_rate[d].sample_rate, codec_adc_sample_rate[a].sample_rate);
    } else {
        sel = 0;
    }

    uint32_t ec_mask, ec_val;

    ec_mask = 0;
    ec_val = 0;
    if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH5) {
        ec_mask |= CODEC_CODEC_DOWN_SEL_MC_CH0_MASK;
        ec_val |= CODEC_CODEC_DOWN_SEL_MC_CH0(sel);
    }
    if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH6) {
        ec_mask |= CODEC_CODEC_DOWN_SEL_MC_CH1_MASK;
        ec_val |= CODEC_CODEC_DOWN_SEL_MC_CH1(sel);
    }
    codec->REG_228 = (codec->REG_228 & ~ec_mask) | ec_val;
}

static void hal_codec_ec_enable(void)
{
    uint32_t ec_val;
    bool dac_rate_valid;
    uint8_t a;

    dac_rate_valid = !!(codec->REG_000 & CODEC_DAC_ENABLE);

    hal_codec_set_ec_down_sel(dac_rate_valid);

    // If no normal ADC chan, ADC0 must be enabled
    if ((codec_adc_ch_map & ~EC_ADC_MAP) == 0) {
        a = codec_rate_idx[AUD_STREAM_CAPTURE];
        hal_codec_set_adc_down(AUD_CHANNEL_MAP_CH0, codec_adc_sample_rate[a].adc_down);
        hal_codec_set_adc_hbf_bypass_cnt(AUD_CHANNEL_MAP_CH0, codec_adc_sample_rate[a].bypass_cnt);
        codec->REG_080 |= CODEC_CODEC_ADC_EN_CH0;
    }

    ec_val = 0;
    if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH5) {
        ec_val |= CODEC_CODEC_MC_ENABLE_CH0;
    }
    if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH6) {
        ec_val |= CODEC_CODEC_MC_ENABLE_CH1;
    }
    if (codec->REG_0E4 & CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE) {
        ec_val |= CODEC_CODEC_RESAMPLE_MC_ENABLE;
        if ((codec_adc_ch_map & EC_ADC_MAP) == EC_ADC_MAP) {
            ec_val |= CODEC_CODEC_RESAMPLE_MC_DUAL_CH;
        }
    }
    codec->REG_228 |= ec_val;
}

static void hal_codec_ec_disable(void)
{
    codec->REG_228 &= ~(CODEC_CODEC_MC_ENABLE_CH0 | CODEC_CODEC_MC_ENABLE_CH1 |
        CODEC_CODEC_RESAMPLE_MC_ENABLE | CODEC_CODEC_RESAMPLE_MC_DUAL_CH);
    if ((codec_adc_ch_map & ~EC_ADC_MAP) == 0 && (anc_adc_ch_map & AUD_CHANNEL_MAP_CH0) == 0) {
        codec->REG_080 &= ~CODEC_CODEC_ADC_EN_CH0;
    }
}

int hal_codec_start_interface(enum HAL_CODEC_ID_T id, enum AUD_STREAM_T stream, int dma)
{
    uint32_t fifo_flush = 0;

    if (stream == AUD_STREAM_PLAYBACK) {
#ifdef CODEC_DSD
        if (dsd_enabled) {
            hal_codec_dsd_cfg_start();
        }
#endif
#ifdef CODEC_MIN_PHASE
        if (min_phase_cfg & (1 << AUD_STREAM_PLAYBACK)) {
            if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH0) {
                codec->REG_100 |= CODEC_FIR_STREAM_ENABLE_CH0;
                codec->REG_098 |= CODEC_CODEC_DAC_L_FIR_UPSAMPLE;
            }
            if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH1) {
                codec->REG_100 |= CODEC_FIR_STREAM_ENABLE_CH1;
                codec->REG_098 |= CODEC_CODEC_DAC_R_FIR_UPSAMPLE;
            }
        }
#endif
#ifdef __AUDIO_RESAMPLE__
        if (codec->REG_0E4 & CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE) {
            hal_codec_resample_clock_enable(stream);
#if (defined(__TWS__) || defined(IBRT)) && defined(ANC_APP)
            enum HAL_CODEC_SYNC_TYPE_T sync_type;

            sync_type = GET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_DAC_TRIGGER_SEL);
            if (sync_type != HAL_CODEC_SYNC_TYPE_NONE) {
                codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_DAC_TRIGGER_SEL, HAL_CODEC_SYNC_TYPE_NONE);
                codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
                hal_codec_reg_update_delay();
                codec->REG_0F4 = resample_phase_float_to_value(1.0f);
                hal_codec_reg_update_delay();
                codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
                hal_codec_reg_update_delay();
                codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
                codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_DAC_TRIGGER_SEL, sync_type);
                hal_codec_reg_update_delay();
                codec->REG_0F4 = resample_phase_float_to_value(get_playback_resample_phase());
                codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
            }
#endif
            codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_DAC_ENABLE;
        }
#endif
        if ((codec->REG_000 & CODEC_ADC_ENABLE) && (codec_adc_ch_map & EC_ADC_MAP)) {
            hal_codec_set_ec_down_sel(true);
        }
#ifdef AUDIO_ANC_FB_MC
        fifo_flush |= CODEC_MC_FIFO_FLUSH;
#endif
        fifo_flush |= CODEC_TX_FIFO_FLUSH;
        codec->REG_004 |= fifo_flush;
        hal_codec_reg_update_delay();
        codec->REG_004 &= ~fifo_flush;
        if (dma) {
            codec->REG_008 = SET_BITFIELD(codec->REG_008, CODEC_CODEC_TX_THRESHOLD, HAL_CODEC_TX_FIFO_TRIGGER_LEVEL);
            codec->REG_000 |= CODEC_DMACTRL_TX;
            // Delay a little time for DMA to fill the TX FIFO before sending
            for (volatile int i = 0; i < 50; i++);
        }
#ifdef AUDIO_ANC_FB_MC
        if (mc_dual_chan) {
            codec->REG_04C |= CODEC_DUAL_CHANNEL_MC;
        } else {
            codec->REG_04C &= ~CODEC_DUAL_CHANNEL_MC;
        }
        if (mc_16bit) {
            codec->REG_04C |= CODEC_MODE_16BIT_MC;
        } else {
            codec->REG_04C &= ~CODEC_MODE_16BIT_MC;
        }
        if (adc_en_map == 0) {
            // Reset ADC free running clock and ADC ANA
            codec->REG_064 &= ~(RSTN_ADC_FREE_RUNNING_CLK | CODEC_SOFT_RSTN_ADC_ANA_MASK);
            codec->REG_064 |= (RSTN_ADC_FREE_RUNNING_CLK | CODEC_SOFT_RSTN_ADC_ANA_MASK);
            codec->REG_080 |= CODEC_CODEC_ADC_EN;
        }
        adc_en_map |= (1 << CODEC_ADC_EN_REQ_MC);
        // If codec function has been enabled, start FIFOs directly;
        // otherwise, start FIFOs after PA is enabled
        if (codec->REG_098 & CODEC_CODEC_DAC_EN) {
            uint32_t lock;
            lock = int_lock();
            // MC FIFO and DAC FIFO must be started at the same time
            codec->REG_04C |= CODEC_MC_ENABLE;
            codec->REG_000 |= CODEC_DAC_ENABLE;
            int_unlock(lock);
        }
        mc_enabled = true;
#else
        codec->REG_000 |= CODEC_DAC_ENABLE;
#endif
    } else {
#ifdef VOICE_DETECTOR_EN
        if ((codec_adc_ch_map & AUD_CHANNEL_MAP_CH4) &&
                (vad_type == AUD_VAD_TYPE_MIX || vad_type == AUD_VAD_TYPE_DIG)) {
            // Stop vad buffering
            hal_codec_vad_stop();
        }
#endif
#ifdef CODEC_MIN_PHASE
        if (min_phase_cfg & (1 << AUD_STREAM_CAPTURE)) {
            if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH2) {
                codec->REG_100 |= CODEC_FIR_STREAM_ENABLE_CH2;
                codec->REG_0D0 |= CODEC_CODEC_ADC_FIR_DS_EN_CH2;
            }
            if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH3) {
                codec->REG_100 |= CODEC_FIR_STREAM_ENABLE_CH3;
                codec->REG_0D0 |= CODEC_CODEC_ADC_FIR_DS_EN_CH3;
            }
        }
#endif
#ifdef __AUDIO_RESAMPLE__
        if (codec->REG_0E4 & CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE) {
            hal_codec_resample_clock_enable(stream);
#if (defined(__TWS__) || defined(IBRT)) && defined(ANC_APP)
            enum HAL_CODEC_SYNC_TYPE_T sync_type;

            sync_type = GET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_ADC_TRIGGER_SEL);
            if (sync_type != HAL_CODEC_SYNC_TYPE_NONE) {
                codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_ADC_TRIGGER_SEL, HAL_CODEC_SYNC_TYPE_NONE);
                codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
                hal_codec_reg_update_delay();
                codec->REG_0F8 = resample_phase_float_to_value(1.0f);
                hal_codec_reg_update_delay();
                codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
                hal_codec_reg_update_delay();
                codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
                codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_ADC_TRIGGER_SEL, sync_type);
                hal_codec_reg_update_delay();
                codec->REG_0F8 = resample_phase_float_to_value(get_capture_resample_phase());
                codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
            }
#endif
            codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_ADC_ENABLE;
        }
#endif
        if (codec_mic_ch_map & AUD_CHANNEL_MAP_DIGMIC_ALL) {
            hal_codec_enable_dig_mic(codec_mic_ch_map);
        }
        if (codec_adc_ch_map & EC_ADC_MAP) {
            hal_codec_ec_enable();
        }
        for (int i = 0; i < MAX_ADC_CH_NUM; i++) {
            if (codec_adc_ch_map & (AUD_CHANNEL_MAP_CH0 << i)) {
                if (i < NORMAL_ADC_CH_NUM &&
                        (codec->REG_080 & (CODEC_CODEC_ADC_EN_CH0 << i)) == 0) {
                    // Reset ADC channel
                    codec->REG_064 &= ~CODEC_SOFT_RSTN_ADC(1 << i);
                    codec->REG_064 |= CODEC_SOFT_RSTN_ADC(1 << i);
                    codec->REG_080 |= (CODEC_CODEC_ADC_EN_CH0 << i);
                }
                codec->REG_000 |= (CODEC_ADC_ENABLE_CH0 << i);
            }
        }
        fifo_flush = CODEC_RX_FIFO_FLUSH_CH0 | CODEC_RX_FIFO_FLUSH_CH1 | CODEC_RX_FIFO_FLUSH_CH2 |
            CODEC_RX_FIFO_FLUSH_CH3 | CODEC_RX_FIFO_FLUSH_CH4 | CODEC_RX_FIFO_FLUSH_CH5 | CODEC_RX_FIFO_FLUSH_CH6;
        codec->REG_004 |= fifo_flush;
        hal_codec_reg_update_delay();
        codec->REG_004 &= ~fifo_flush;
        if (dma) {
            codec->REG_008 = SET_BITFIELD(codec->REG_008, CODEC_CODEC_RX_THRESHOLD, HAL_CODEC_RX_FIFO_TRIGGER_LEVEL);
            codec->REG_000 |= CODEC_DMACTRL_RX;
        }
        codec->REG_000 |= CODEC_ADC_ENABLE;
    }

    return 0;
}

int hal_codec_stop_interface(enum HAL_CODEC_ID_T id, enum AUD_STREAM_T stream)
{
    uint32_t fifo_flush = 0;

    if (stream == AUD_STREAM_PLAYBACK) {
        codec->REG_000 &= ~CODEC_DAC_ENABLE;
        codec->REG_000 &= ~CODEC_DMACTRL_TX;
#ifdef __AUDIO_RESAMPLE__
        codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_DAC_ENABLE;
        hal_codec_resample_clock_disable(stream);
#endif
#ifdef CODEC_MIN_PHASE
        if (min_phase_cfg & (1 << AUD_STREAM_PLAYBACK)) {
            if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH0) {
                codec->REG_100 &= ~CODEC_FIR_STREAM_ENABLE_CH0;
                codec->REG_098 &= ~CODEC_CODEC_DAC_L_FIR_UPSAMPLE;
            }
            if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH1) {
                codec->REG_100 &= ~CODEC_FIR_STREAM_ENABLE_CH1;
                codec->REG_098 &= ~CODEC_CODEC_DAC_R_FIR_UPSAMPLE;
            }
        }
#endif
#ifdef CODEC_DSD
        hal_codec_dsd_cfg_stop();
        dsd_enabled = false;
#endif
#ifdef AUDIO_ANC_FB_MC
        mc_enabled = false;
        codec->REG_04C &= ~CODEC_MC_ENABLE;
        adc_en_map &= ~(1 << CODEC_ADC_EN_REQ_MC);
        if (adc_en_map == 0) {
            codec->REG_080 &= ~CODEC_CODEC_ADC_EN;
        }
        fifo_flush |= CODEC_MC_FIFO_FLUSH;
#endif
        fifo_flush |= CODEC_TX_FIFO_FLUSH;
        codec->REG_004 |= fifo_flush;
        hal_codec_reg_update_delay();
        codec->REG_004 &= ~fifo_flush;
        // Cancel dac sync request
        hal_codec_sync_dac_disable();
        hal_codec_sync_dac_resample_rate_disable();
        hal_codec_sync_dac_gain_disable();
#ifdef NO_DAC_RESET
        // Clean up DAC intermediate states
        osDelay(dac_delay_ms);
#endif
    } else {
        codec->REG_000 &= ~(CODEC_ADC_ENABLE | CODEC_ADC_ENABLE_CH0 | CODEC_ADC_ENABLE_CH1 | CODEC_ADC_ENABLE_CH2 |
            CODEC_ADC_ENABLE_CH3 | CODEC_ADC_ENABLE_CH4 | CODEC_ADC_ENABLE_CH5 | CODEC_ADC_ENABLE_CH6);
        codec->REG_000 &= ~CODEC_DMACTRL_RX;
        for (int i = 0; i < MAX_ADC_CH_NUM; i++) {
            if (i < NORMAL_ADC_CH_NUM &&
                    (codec_adc_ch_map & (AUD_CHANNEL_MAP_CH0 << i)) &&
                    (anc_adc_ch_map & (AUD_CHANNEL_MAP_CH0 << i)) == 0) {
                codec->REG_080 &= ~(CODEC_CODEC_ADC_EN_CH0 << i);
            }
        }
        if (codec_adc_ch_map & EC_ADC_MAP) {
            hal_codec_ec_disable();
        }
        if ((codec_mic_ch_map & AUD_CHANNEL_MAP_DIGMIC_ALL) &&
                (anc_mic_ch_map & AUD_CHANNEL_MAP_DIGMIC_ALL) == 0) {
            hal_codec_disable_dig_mic();
        }
#ifdef __AUDIO_RESAMPLE__
        codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_ADC_ENABLE;
        hal_codec_resample_clock_disable(stream);
#endif
#ifdef CODEC_MIN_PHASE
        if (min_phase_cfg & (1 << AUD_STREAM_CAPTURE)) {
            if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH2) {
                codec->REG_100 &= ~CODEC_FIR_STREAM_ENABLE_CH2;
                codec->REG_0D0 &= ~CODEC_CODEC_ADC_FIR_DS_EN_CH2;
            }
            if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH3) {
                codec->REG_100 &= ~CODEC_FIR_STREAM_ENABLE_CH3;
                codec->REG_0D0 &= ~CODEC_CODEC_ADC_FIR_DS_EN_CH3;
            }
        }
#endif
        fifo_flush = CODEC_RX_FIFO_FLUSH_CH0 | CODEC_RX_FIFO_FLUSH_CH1 | CODEC_RX_FIFO_FLUSH_CH2 |
            CODEC_RX_FIFO_FLUSH_CH3 | CODEC_RX_FIFO_FLUSH_CH4 | CODEC_RX_FIFO_FLUSH_CH5 | CODEC_RX_FIFO_FLUSH_CH6;
        codec->REG_004 |= fifo_flush;
        hal_codec_reg_update_delay();
        codec->REG_004 &= ~fifo_flush;
        // Cancel adc sync request
        hal_codec_sync_adc_disable();
        hal_codec_sync_adc_resample_rate_disable();
        hal_codec_sync_adc_gain_disable();
    }

    return 0;
}

static void hal_codec_set_dac_gain_value(enum AUD_CHANNEL_MAP_T map, uint32_t val)
{
    codec->REG_09C &= ~CODEC_CODEC_DAC_GAIN_UPDATE;
    hal_codec_reg_update_delay();
    if (map & AUD_CHANNEL_MAP_CH0) {
        codec->REG_09C = SET_BITFIELD(codec->REG_09C, CODEC_CODEC_DAC_GAIN_CH0, val);
    }
    if (map & AUD_CHANNEL_MAP_CH1) {
        codec->REG_0A0 = SET_BITFIELD(codec->REG_0A0, CODEC_CODEC_DAC_GAIN_CH1, val);
    }
    codec->REG_09C |= CODEC_CODEC_DAC_GAIN_UPDATE;
}

void hal_codec_get_dac_gain(float *left_gain, float *right_gain)
{
    struct DAC_GAIN_T {
        int32_t v : 20;
    };

    struct DAC_GAIN_T left;
    struct DAC_GAIN_T right;

    left.v  = GET_BITFIELD(codec->REG_09C, CODEC_CODEC_DAC_GAIN_CH0);
    right.v = GET_BITFIELD(codec->REG_0A0, CODEC_CODEC_DAC_GAIN_CH1);

    *left_gain = left.v;
    *right_gain = right.v;

    // Gain format: 6.14
    *left_gain /= (1 << 14);
    *right_gain /= (1 << 14);
}

void hal_codec_dac_mute(bool mute)
{
    codec_mute[AUD_STREAM_PLAYBACK] = mute;

#ifdef AUDIO_OUTPUT_SW_GAIN
    hal_codec_set_sw_gain(swdac_gain);
#else
    if (mute) {
        hal_codec_set_dac_gain_value(VALID_DAC_MAP, 0);
    } else {
        hal_codec_restore_dig_dac_gain();
    }
#endif
}

static float db_to_amplitude_ratio(int32_t db)
{
    float coef;

    if (db == ZERODB_DIG_DBVAL) {
        coef = 1;
    } else if (db <= MIN_DIG_DBVAL) {
        coef = 0;
    } else {
        if (db > MAX_DIG_DBVAL) {
            db = MAX_DIG_DBVAL;
        }
        coef = db_to_float(db);
    }

    return coef;
}

static float digdac_gain_to_float(int32_t gain)
{
    float coef;

#if defined(NOISE_GATING) && defined(NOISE_REDUCTION)
    gain += digdac_gain_offset_nr;
#endif

    coef = db_to_amplitude_ratio(gain);

#ifdef AUDIO_OUTPUT_DC_CALIB
    coef *= dac_dc_gain_attn;
#endif

#ifdef ANC_APP
    coef *= anc_boost_gain_attn;
#endif

#if 0
    static const float thd_attn = 0.982878873; // -0.15dB

    // Ensure that THD is good at max gain
    if (coef > thd_attn) {
        coef = thd_attn;
    }
#endif

    return coef;
}

static void hal_codec_set_dig_dac_gain(enum AUD_CHANNEL_MAP_T map, int32_t gain)
{
    uint32_t val;
    float coef;
    bool mute;

    if (map & AUD_CHANNEL_MAP_CH0) {
        digdac_gain[0] = gain;
    }
    if (map & AUD_CHANNEL_MAP_CH1) {
        digdac_gain[1] = gain;
    }

#ifdef AUDIO_OUTPUT_SW_GAIN
    mute = false;
#else
    mute = codec_mute[AUD_STREAM_PLAYBACK];
#endif

#ifdef AUDIO_OUTPUT_DC_CALIB_ANA
    if (codec->REG_098 & CODEC_CODEC_DAC_SDM_CLOSE) {
        mute = true;
    }
#endif

    if (mute) {
        val = 0;
    } else {
        coef = digdac_gain_to_float(gain);

        // Gain format: 6.14
        int32_t s_val = (int32_t)(coef * (1 << 14));
        val = __SSAT(s_val, 20);
    }

    hal_codec_set_dac_gain_value(map, val);
}

static void hal_codec_restore_dig_dac_gain(void)
{
    if (digdac_gain[0] == digdac_gain[1]) {
        hal_codec_set_dig_dac_gain(VALID_DAC_MAP, digdac_gain[0]);
    } else {
        hal_codec_set_dig_dac_gain(AUD_CHANNEL_MAP_CH0, digdac_gain[0]);
        hal_codec_set_dig_dac_gain(AUD_CHANNEL_MAP_CH1, digdac_gain[1]);
    }
}

#ifdef AUDIO_OUTPUT_SW_GAIN
static void hal_codec_set_sw_gain(int32_t gain)
{
    float coef;
    bool mute;

    swdac_gain = gain;

    mute = codec_mute[AUD_STREAM_PLAYBACK];

    if (mute) {
        coef = 0;
    } else {
        coef = digdac_gain_to_float(gain);
    }

    if (sw_output_coef_callback) {
        sw_output_coef_callback(coef);
    }
}

void hal_codec_set_sw_output_coef_callback(HAL_CODEC_SW_OUTPUT_COEF_CALLBACK callback)
{
    sw_output_coef_callback = callback;
}
#endif

static void hal_codec_set_adc_gain_value(enum AUD_CHANNEL_MAP_T map, uint32_t val)
{
    uint32_t gain_update = 0;

    for (int i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if (map & (AUD_CHANNEL_MAP_CH0 << i)) {
            *(&codec->REG_084 + i) = SET_BITFIELD(*(&codec->REG_084 + i), CODEC_CODEC_ADC_GAIN_CH0, val);
            gain_update |= (CODEC_CODEC_ADC_GAIN_UPDATE_CH0 << i);
        }
    }
    codec->REG_09C &= ~gain_update;
    hal_codec_reg_update_delay();
    codec->REG_09C |= gain_update;
}

static void hal_codec_set_dig_adc_gain(enum AUD_CHANNEL_MAP_T map, int32_t gain)
{
    uint32_t val;
    float coef;
    bool mute;
    int i;
    int32_t s_val;

    for (i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if (map & (1 << i)) {
            digadc_gain[i] = gain;
        }
    }

    mute = codec_mute[AUD_STREAM_CAPTURE];

    if (mute) {
        val = 0;
    } else {
#ifdef ANC_APP
        enum AUD_CHANNEL_MAP_T adj_map;
        int32_t anc_gain;

        adj_map = map & anc_adc_gain_offset_map;
        while (adj_map) {
            i = get_msb_pos(adj_map);
            adj_map &= ~(1 << i);
            anc_gain = gain + anc_adc_gain_offset[i];
            coef = db_to_amplitude_ratio(anc_gain);
            coef /= anc_boost_gain_attn;
            // Gain format: 8.12
            s_val = (int32_t)(coef * (1 << 12));
            val = __SSAT(s_val, 20);
            hal_codec_set_adc_gain_value((1 << i), val);
        }

        map &= ~anc_adc_gain_offset_map;
#endif

        if (map) {
            coef = db_to_amplitude_ratio(gain);
#ifdef ANC_APP
            coef /= anc_boost_gain_attn;
#endif
            // Gain format: 8.12
            s_val = (int32_t)(coef * (1 << 12));
            val = __SSAT(s_val, 20);
        } else {
            val = 0;
        }
    }

    hal_codec_set_adc_gain_value(map, val);
}

static void hal_codec_restore_dig_adc_gain(void)
{
    int i;

    for (i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        hal_codec_set_dig_adc_gain((1 << i), digadc_gain[i]);
    }
}

static void POSSIBLY_UNUSED hal_codec_get_adc_gain(enum AUD_CHANNEL_MAP_T map, float *gain)
{
    struct ADC_GAIN_T {
        int32_t v : 20;
    };

    struct ADC_GAIN_T adc_val;

    for (int i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if (map & (AUD_CHANNEL_MAP_CH0 << i)) {
            adc_val.v = GET_BITFIELD(*(&codec->REG_084 + i), CODEC_CODEC_ADC_GAIN_CH0);

            *gain = adc_val.v;
            // Gain format: 8.12
            *gain /= (1 << 12);
            return;
        }
    }

    *gain = 0;
}

void hal_codec_adc_mute(bool mute)
{
    codec_mute[AUD_STREAM_CAPTURE] = mute;

    if (mute) {
        hal_codec_set_adc_gain_value(NORMAL_ADC_MAP, 0);
    } else {
        hal_codec_restore_dig_adc_gain();
    }
}

int hal_codec_set_chan_vol(enum AUD_STREAM_T stream, enum AUD_CHANNEL_MAP_T ch_map, uint8_t vol)
{
    if (stream == AUD_STREAM_PLAYBACK) {
#ifdef AUDIO_OUTPUT_SW_GAIN
        ASSERT(false, "%s: Cannot set play chan vol with AUDIO_OUTPUT_SW_GAIN", __func__);
#else
#ifdef SINGLE_CODEC_DAC_VOL
        ASSERT(false, "%s: Cannot set play chan vol with SINGLE_CODEC_DAC_VOL", __func__);
#else
        const struct CODEC_DAC_VOL_T *vol_tab_ptr;

        vol_tab_ptr = hal_codec_get_dac_volume(vol);
        if (vol_tab_ptr) {
            if (ch_map & AUD_CHANNEL_MAP_CH0) {
                hal_codec_set_dig_dac_gain(AUD_CHANNEL_MAP_CH0, vol_tab_ptr->sdac_volume);
            }
            if (ch_map & AUD_CHANNEL_MAP_CH1) {
                hal_codec_set_dig_dac_gain(AUD_CHANNEL_MAP_CH1, vol_tab_ptr->sdac_volume);
            }
        }
#endif
#endif
    } else {
#ifdef SINGLE_CODEC_ADC_VOL
        ASSERT(false, "%s: Cannot set cap chan vol with SINGLE_CODEC_ADC_VOL", __func__);
#else
        uint8_t mic_ch, adc_ch;
        enum AUD_CHANNEL_MAP_T map;
        const CODEC_ADC_VOL_T *adc_gain_db;

        adc_gain_db = hal_codec_get_adc_volume(vol);
        if (adc_gain_db) {
            map = ch_map & ~EC_MIC_MAP;
            while (map) {
                mic_ch = get_lsb_pos(map);
                map &= ~(1 << mic_ch);
                adc_ch = hal_codec_get_adc_chan(1 << mic_ch);
                ASSERT(adc_ch < NORMAL_ADC_CH_NUM, "%s: Bad cap ch_map=0x%X (ch=%u)", __func__, ch_map, mic_ch);
                hal_codec_set_dig_adc_gain((1 << adc_ch), *adc_gain_db);
            }
        }
#endif
    }

    return 0;
}

static int hal_codec_set_dac_hbf_bypass_cnt(uint32_t cnt)
{
    uint32_t bypass = 0;
    uint32_t bypass_mask = CODEC_CODEC_DAC_HBF1_BYPASS | CODEC_CODEC_DAC_HBF2_BYPASS | CODEC_CODEC_DAC_HBF3_BYPASS;

    if (cnt == 0) {
    } else if (cnt == 1) {
        bypass = CODEC_CODEC_DAC_HBF3_BYPASS;
    } else if (cnt == 2) {
        bypass = CODEC_CODEC_DAC_HBF2_BYPASS | CODEC_CODEC_DAC_HBF3_BYPASS;
    } else if (cnt == 3) {
        bypass = CODEC_CODEC_DAC_HBF1_BYPASS | CODEC_CODEC_DAC_HBF2_BYPASS | CODEC_CODEC_DAC_HBF3_BYPASS;
    } else {
        ASSERT(false, "%s: Invalid dac bypass cnt: %u", __FUNCTION__, cnt);
    }

    // OSR is fixed to 512
    //codec->REG_098 = SET_BITFIELD(codec->REG_098, CODEC_CODEC_DAC_OSR_SEL, 2);

    codec->REG_098 = (codec->REG_098 & ~bypass_mask) | bypass;
    return 0;
}

static int hal_codec_set_dac_up(uint32_t val)
{
    uint32_t sel = 0;

    if (val == 2) {
        sel = 0;
    } else if (val == 3) {
        sel = 1;
    } else if (val == 4) {
        sel = 2;
    } else if (val == 6) {
        sel = 3;
    } else if (val == 1) {
        sel = 4;
    } else {
        ASSERT(false, "%s: Invalid dac up: %u", __FUNCTION__, val);
    }
    codec->REG_098 = SET_BITFIELD(codec->REG_098, CODEC_CODEC_DAC_UP_SEL, sel);
    return 0;
}

static uint32_t POSSIBLY_UNUSED hal_codec_get_dac_up(void)
{
    uint32_t sel;

    sel = GET_BITFIELD(codec->REG_098, CODEC_CODEC_DAC_UP_SEL);
    if (sel == 0) {
        return 2;
    } else if (sel == 1) {
        return 3;
    } else if (sel == 2) {
        return 4;
    } else if (sel == 3) {
        return 6;
    } else {
        return 1;
    }
}

static int hal_codec_set_adc_down(enum AUD_CHANNEL_MAP_T map, uint32_t val)
{
    uint32_t sel = 0;

    if (val == 3) {
        sel = 0;
    } else if (val == 6) {
        sel = 1;
    } else if (val == 1) {
        sel = 2;
    } else {
        ASSERT(false, "%s: Invalid adc down: %u", __FUNCTION__, val);
    }
    for (int i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if (map & (AUD_CHANNEL_MAP_CH0 << i)) {
            *(&codec->REG_084 + i) = SET_BITFIELD(*(&codec->REG_084 + i), CODEC_CODEC_ADC_DOWN_SEL_CH0, sel);
        }
    }
    return 0;
}

static int hal_codec_set_adc_hbf_bypass_cnt(enum AUD_CHANNEL_MAP_T map, uint32_t cnt)
{
    uint32_t bypass = 0;
    uint32_t bypass_mask = CODEC_CODEC_ADC_HBF1_BYPASS_CH0 | CODEC_CODEC_ADC_HBF2_BYPASS_CH0 | CODEC_CODEC_ADC_HBF3_BYPASS_CH0;

    if (cnt == 0) {
    } else if (cnt == 1) {
        bypass = CODEC_CODEC_ADC_HBF3_BYPASS_CH0;
    } else if (cnt == 2) {
        bypass = CODEC_CODEC_ADC_HBF2_BYPASS_CH0 | CODEC_CODEC_ADC_HBF3_BYPASS_CH0;
    } else if (cnt == 3) {
        bypass = CODEC_CODEC_ADC_HBF1_BYPASS_CH0 | CODEC_CODEC_ADC_HBF2_BYPASS_CH0 | CODEC_CODEC_ADC_HBF3_BYPASS_CH0;
    } else {
        ASSERT(false, "%s: Invalid bypass cnt: %u", __FUNCTION__, cnt);
    }
    for (int i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if (map & (AUD_CHANNEL_MAP_CH0 << i)) {
            *(&codec->REG_084 + i) = (*(&codec->REG_084 + i) & ~bypass_mask) | bypass;
        }
    }
    return 0;
}

#ifdef __AUDIO_RESAMPLE__
static float get_playback_resample_phase(void)
{
    return (float)codec_dac_sample_rate[resample_rate_idx[AUD_STREAM_PLAYBACK]].codec_freq / hal_cmu_get_crystal_freq();
}

static float get_capture_resample_phase(void)
{
    return (float)hal_cmu_get_crystal_freq() / codec_adc_sample_rate[resample_rate_idx[AUD_STREAM_CAPTURE]].codec_freq;
}

static uint32_t resample_phase_float_to_value(float phase)
{
    if (phase >= 4.0) {
        return (uint32_t)-1;
    } else {
        // Phase format: 2.30
        return (uint32_t)(phase * (1 << 30));
    }
}

static float POSSIBLY_UNUSED resample_phase_value_to_float(uint32_t value)
{
    // Phase format: 2.30
    return (float)value / (1 << 30);
}
#endif

#ifdef SIDETONE_ENABLE
static void hal_codec_set_sidetone_adc_chan(enum AUD_CHANNEL_MAP_T chan_map)
{
    if (chan_map == AUD_CHANNEL_MAP_CH0) {
        codec->REG_080 &= ~CODEC_CODEC_SIDE_TONE_MIC_SEL;
        codec->REG_078 &= ~CODEC_CODEC_SIDE_TONE_CH_SEL;
    } else if (chan_map == AUD_CHANNEL_MAP_CH2) {
        codec->REG_080 &= ~CODEC_CODEC_SIDE_TONE_MIC_SEL;
        codec->REG_078 |= CODEC_CODEC_SIDE_TONE_CH_SEL;
    } else if (chan_map == AUD_CHANNEL_MAP_CH4) {
        codec->REG_080 |= CODEC_CODEC_SIDE_TONE_MIC_SEL;
    }
}
#endif

int hal_codec_setup_stream(enum HAL_CODEC_ID_T id, enum AUD_STREAM_T stream, const struct HAL_CODEC_CONFIG_T *cfg)
{
    int i;
    int rate_idx;
    uint32_t ana_dig_div;
    enum AUD_SAMPRATE_T sample_rate;

    if (stream == AUD_STREAM_PLAYBACK) {
        if ((HAL_CODEC_CONFIG_CHANNEL_MAP | HAL_CODEC_CONFIG_CHANNEL_NUM) & cfg->set_flag) {
            if (cfg->channel_num == AUD_CHANNEL_NUM_2) {
                if (cfg->channel_map != (AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH1)) {
                    TRACE(2,"\n!!! WARNING:%s: Bad play stereo ch map: 0x%X\n", __func__, cfg->channel_map);
                }
                codec->REG_044 |= CODEC_DUAL_CHANNEL_DAC;
            } else {
                ASSERT(cfg->channel_num == AUD_CHANNEL_NUM_1, "%s: Bad play ch num: %u", __func__, cfg->channel_num);
                // Allow to DMA one channel but output 2 channels
                ASSERT((cfg->channel_map & ~(AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH1)) == 0,
                    "%s: Bad play mono ch map: 0x%X", __func__, cfg->channel_map);
                codec->REG_044 &= ~CODEC_DUAL_CHANNEL_DAC;
            }
            codec_dac_ch_map = AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH1;
        }

        if (HAL_CODEC_CONFIG_BITS & cfg->set_flag) {
            if (cfg->bits == AUD_BITS_16) {
                codec->REG_044 = (codec->REG_044 & ~CODEC_MODE_32BIT_DAC) | CODEC_MODE_16BIT_DAC;
                codec->REG_04C = (codec->REG_04C & ~CODEC_MODE_32BIT_MC) | CODEC_MODE_16BIT_MC;
            } else if (cfg->bits == AUD_BITS_24) {
                codec->REG_044 &= ~(CODEC_MODE_16BIT_DAC | CODEC_MODE_32BIT_DAC);
                codec->REG_04C &= ~(CODEC_MODE_16BIT_MC | CODEC_MODE_32BIT_MC);
            } else if (cfg->bits == AUD_BITS_32) {
                codec->REG_044 = (codec->REG_044 & ~CODEC_MODE_16BIT_DAC) | CODEC_MODE_32BIT_DAC;
                codec->REG_04C = (codec->REG_04C & ~CODEC_MODE_16BIT_MC) | CODEC_MODE_32BIT_MC;
            } else {
                ASSERT(false, "%s: Bad play bits: %u", __func__, cfg->bits);
            }
        }

        if (HAL_CODEC_CONFIG_SAMPLE_RATE & cfg->set_flag) {
            sample_rate = cfg->sample_rate;
#ifdef CODEC_DSD
            if (dsd_enabled) {
                if (sample_rate == AUD_SAMPRATE_176400) {
                    dsd_rate_idx = 0;
                } else if (sample_rate == AUD_SAMPRATE_352800) {
                    dsd_rate_idx = 1;
                } else if (sample_rate == AUD_SAMPRATE_705600) {
                    dsd_rate_idx = 2;
                } else {
                    ASSERT(false, "%s: Bad DSD sample rate: %u", __func__, sample_rate);
                }
                sample_rate = AUD_SAMPRATE_44100;
            }
#endif

            for (i = 0; i < ARRAY_SIZE(codec_dac_sample_rate); i++) {
                if (codec_dac_sample_rate[i].sample_rate == sample_rate) {
                    break;
                }
            }
            ASSERT(i < ARRAY_SIZE(codec_dac_sample_rate), "%s: Invalid playback sample rate: %u", __func__, sample_rate);
            rate_idx = i;
            ana_dig_div = codec_dac_sample_rate[rate_idx].codec_div / codec_dac_sample_rate[rate_idx].cmu_div;
            ASSERT(ana_dig_div * codec_dac_sample_rate[rate_idx].cmu_div == codec_dac_sample_rate[rate_idx].codec_div,
                "%s: Invalid playback div for rate %u: codec_div=%u cmu_div=%u", __func__, sample_rate,
                codec_dac_sample_rate[rate_idx].codec_div, codec_dac_sample_rate[rate_idx].cmu_div);

            TRACE(2,"[%s] playback sample_rate=%d", __func__, sample_rate);

#ifdef CODEC_TIMER
            cur_codec_freq = codec_dac_sample_rate[rate_idx].codec_freq;
#endif

            codec_rate_idx[AUD_STREAM_PLAYBACK] = rate_idx;

#ifdef __AUDIO_RESAMPLE__
            uint32_t mask, val;

            if (hal_cmu_get_audio_resample_status() && codec_dac_sample_rate[rate_idx].codec_freq != CODEC_FREQ_CRYSTAL) {
#ifdef CODEC_TIMER
                cur_codec_freq = CODEC_FREQ_CRYSTAL;
#endif
                if ((codec->REG_0E4 & CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE) == 0 ||
                        resample_rate_idx[AUD_STREAM_PLAYBACK] != rate_idx) {
                    resample_rate_idx[AUD_STREAM_PLAYBACK] = rate_idx;
                    codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
                    hal_codec_reg_update_delay();
                    codec->REG_0F4 = resample_phase_float_to_value(get_playback_resample_phase());
                    hal_codec_reg_update_delay();
                    codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
                }

                mask = CODEC_CODEC_RESAMPLE_DAC_L_ENABLE | CODEC_CODEC_RESAMPLE_DAC_R_ENABLE;
                val = 0;
                if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH0) {
                    val |= CODEC_CODEC_RESAMPLE_DAC_L_ENABLE;
                }
                if (codec_dac_ch_map & AUD_CHANNEL_MAP_CH1) {
                    val |= CODEC_CODEC_RESAMPLE_DAC_R_ENABLE;
                }
            } else {
                mask = CODEC_CODEC_RESAMPLE_DAC_L_ENABLE | CODEC_CODEC_RESAMPLE_DAC_R_ENABLE |
                    CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
                val = 0;
            }
            codec->REG_0E4 = (codec->REG_0E4 & ~mask) | val;
#endif

            // 8K -> 4ms, 16K -> 2ms, ...
            dac_delay_ms = 4 / ((sample_rate + AUD_SAMPRATE_8000 / 2) / AUD_SAMPRATE_8000);
            if (dac_delay_ms < 2) {
                dac_delay_ms = 2;
            }

#ifdef __AUDIO_RESAMPLE__
            if (!hal_cmu_get_audio_resample_status())
#endif
            {
#ifdef __AUDIO_RESAMPLE__
                ASSERT(codec_dac_sample_rate[rate_idx].codec_freq != CODEC_FREQ_CRYSTAL,
                    "%s: playback sample rate %u is for resample only", __func__, sample_rate);
#endif
                analog_aud_freq_pll_config(codec_dac_sample_rate[rate_idx].codec_freq, codec_dac_sample_rate[rate_idx].codec_div);
                hal_cmu_codec_dac_set_div(codec_dac_sample_rate[rate_idx].cmu_div * CODEC_FREQ_EXTRA_DIV);
            }
            hal_codec_set_dac_up(codec_dac_sample_rate[rate_idx].dac_up);
            hal_codec_set_dac_hbf_bypass_cnt(codec_dac_sample_rate[rate_idx].bypass_cnt);
#ifdef AUDIO_ANC_FB_MC
            codec->REG_04C = SET_BITFIELD(codec->REG_04C, CODEC_MC_DELAY, codec_dac_sample_rate[rate_idx].mc_delay);
#endif
        }

        if (HAL_CODEC_CONFIG_VOL & cfg->set_flag) {
            const struct CODEC_DAC_VOL_T *vol_tab_ptr;

            vol_tab_ptr = hal_codec_get_dac_volume(cfg->vol);
            if (vol_tab_ptr) {
#ifdef AUDIO_OUTPUT_SW_GAIN
                hal_codec_set_sw_gain(vol_tab_ptr->sdac_volume);
#else
                analog_aud_set_dac_gain(vol_tab_ptr->tx_pa_gain);
                hal_codec_set_dig_dac_gain(VALID_DAC_MAP, vol_tab_ptr->sdac_volume);
#endif
#ifdef PERF_TEST_POWER_KEY
                // Update performance test power after applying new dac volume
                hal_codec_update_perf_test_power();
#endif
            }
        }
    } else {
        enum AUD_CHANNEL_MAP_T mic_map;
        enum AUD_CHANNEL_MAP_T reserv_map;
        uint8_t cnt;
        uint8_t ch_idx;
        uint32_t cfg_set_mask;
        uint32_t cfg_clr_mask;
#ifdef VOICE_DETECTOR_EN
        uint32_t adc_channel_en = 0;
#endif

        mic_map = 0;
        if ((HAL_CODEC_CONFIG_CHANNEL_MAP | HAL_CODEC_CONFIG_CHANNEL_NUM) & cfg->set_flag) {
            codec_adc_ch_map = 0;
            codec_mic_ch_map = 0;
            mic_map = cfg->channel_map;
        }

        if (mic_map) {
            codec_mic_ch_map = mic_map;
            reserv_map = 0;

#ifdef ANC_APP
#if defined(ANC_FF_MIC_CH_L) || defined(ANC_FF_MIC_CH_R)
#ifdef ANC_PROD_TEST
            if ((ANC_FF_MIC_CH_L & ~NORMAL_MIC_MAP) || (ANC_FF_MIC_CH_L & (ANC_FF_MIC_CH_L - 1))) {
                ASSERT(false, "Invalid ANC_FF_MIC_CH_L: 0x%04X", ANC_FF_MIC_CH_L);
            }
            if ((ANC_FF_MIC_CH_R & ~NORMAL_MIC_MAP) || (ANC_FF_MIC_CH_R & (ANC_FF_MIC_CH_R - 1))) {
                ASSERT(false, "Invalid ANC_FF_MIC_CH_R: 0x%04X", ANC_FF_MIC_CH_R);
            }
            if (ANC_FF_MIC_CH_L & ANC_FF_MIC_CH_R) {
                ASSERT(false, "Conflicted ANC_FF_MIC_CH_L (0x%04X) and ANC_FF_MIC_CH_R (0x%04X)", ANC_FF_MIC_CH_L, ANC_FF_MIC_CH_R);
            }
#if defined(ANC_FB_MIC_CH_L) || defined(ANC_FB_MIC_CH_R)
            if ((ANC_FF_MIC_CH_L & ANC_FB_MIC_CH_L) || (ANC_FF_MIC_CH_L & ANC_FB_MIC_CH_R) ||
                    (ANC_FF_MIC_CH_R & ANC_FB_MIC_CH_L) || (ANC_FF_MIC_CH_R & ANC_FB_MIC_CH_R)) {
                ASSERT(false, "Conflicted FF MIC (0x%04X/0x%04X) and FB MIC (0x%04X/0x%04X)",
                    ANC_FF_MIC_CH_L, ANC_FF_MIC_CH_R, ANC_FB_MIC_CH_L, ANC_FB_MIC_CH_R);
            }
#endif
#ifdef VOICE_DETECTOR_EN
            if (ANC_FF_MIC_CH_L & AUD_CHANNEL_MAP_CH4) {
                ASSERT(false, "Conflicted ANC_FF_MIC_CH_L and VAD MIC");
            }
            if (ANC_FF_MIC_CH_R & AUD_CHANNEL_MAP_CH4) {
                ASSERT(false, "Conflicted ANC_FF_MIC_CH_R and VAD MIC");
            }
#endif
#else // !ANC_PROD_TEST
#if (ANC_FF_MIC_CH_L & ~NORMAL_MIC_MAP) || (ANC_FF_MIC_CH_L & (ANC_FF_MIC_CH_L - 1))
#error "Invalid ANC_FF_MIC_CH_L"
#endif
#if (ANC_FF_MIC_CH_R & ~NORMAL_MIC_MAP) || (ANC_FF_MIC_CH_R & (ANC_FF_MIC_CH_R - 1))
#error "Invalid ANC_FF_MIC_CH_R"
#endif
#if (ANC_FF_MIC_CH_L & ANC_FF_MIC_CH_R)
#error "Conflicted ANC_FF_MIC_CH_L and ANC_FF_MIC_CH_R"
#endif
#if defined(ANC_FB_MIC_CH_L) || defined(ANC_FB_MIC_CH_R)
#if (ANC_FF_MIC_CH_L & ANC_FB_MIC_CH_L) || (ANC_FF_MIC_CH_L & ANC_FB_MIC_CH_R) || \
        (ANC_FF_MIC_CH_R & ANC_FB_MIC_CH_L) || (ANC_FF_MIC_CH_R & ANC_FB_MIC_CH_R)
#error "Conflicted ANC_FF_MIC_CH_L and ANC_FF_MIC_CH_R, ANC_FB_MIC_CH_L, ANC_FB_MIC_CH_R"
#endif
#endif
#ifdef VOICE_DETECTOR_EN
#if (ANC_FF_MIC_CH_L & AUD_CHANNEL_MAP_CH4)
#error "Conflicted ANC_FF_MIC_CH_L and VAD MIC"
#endif
#if (ANC_FF_MIC_CH_R & AUD_CHANNEL_MAP_CH4)
#error "Conflicted ANC_FF_MIC_CH_R and VAD MIC"
#endif
#endif
#endif // !ANC_PROD_TEST
            if (mic_map & ANC_FF_MIC_CH_L) {
                codec_adc_ch_map |= AUD_CHANNEL_MAP_CH0;
                mic_map &= ~ANC_FF_MIC_CH_L;
                ch_idx = get_msb_pos(ANC_FF_MIC_CH_L);
                if (ANC_FF_MIC_CH_L & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                    ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                    codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH0, ch_idx);
                    codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH0;
                } else {
                    codec->REG_084 = SET_BITFIELD(codec->REG_084, CODEC_CODEC_ADC_IN_SEL_CH0, ch_idx);
                    codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH0;
                }
            } else if (ANC_FF_MIC_CH_L & AUD_CHANNEL_MAP_ALL) {
                reserv_map |= AUD_CHANNEL_MAP_CH0;
            }
            if (mic_map & ANC_FF_MIC_CH_R) {
                codec_adc_ch_map |= AUD_CHANNEL_MAP_CH1;
                mic_map &= ~ANC_FF_MIC_CH_R;
                ch_idx = get_msb_pos(ANC_FF_MIC_CH_R);
                if (ANC_FF_MIC_CH_R & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                    ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                    codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH1, ch_idx);
                    codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH1;
                } else {
                    codec->REG_088 = SET_BITFIELD(codec->REG_088, CODEC_CODEC_ADC_IN_SEL_CH1, ch_idx);
                    codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH1;
                }
            } else if (ANC_FF_MIC_CH_R & AUD_CHANNEL_MAP_ALL) {
                reserv_map |= AUD_CHANNEL_MAP_CH1;
            }
#if defined(SIDETONE_ENABLE) && !defined(SIDETONE_DEDICATED_ADC_CHAN)
            if (CFG_HW_AUD_SIDETONE_MIC_DEV == ANC_FF_MIC_CH_L) {
                hal_codec_set_sidetone_adc_chan(AUD_CHANNEL_MAP_CH0);
            }
#ifdef ANC_PROD_TEST
            if (CFG_HW_AUD_SIDETONE_MIC_DEV == ANC_FF_MIC_CH_R) {
                ASSERT(false, "SIDETONE MIC cannot be ANC_FF_MIC_CH_R: 0x%X", ANC_FF_MIC_CH_R);
            }
#elif (CFG_HW_AUD_SIDETONE_MIC_DEV == ANC_FF_MIC_CH_R)
#error "SIDETONE MIC cannot be ANC_FF_MIC_CH_R"
#endif
#endif
#endif

#if defined(ANC_FB_MIC_CH_L) || defined(ANC_FB_MIC_CH_R)
#ifdef ANC_PROD_TEST
            if ((ANC_FB_MIC_CH_L & ~NORMAL_MIC_MAP) || (ANC_FB_MIC_CH_L & (ANC_FB_MIC_CH_L - 1))) {
                ASSERT(false, "Invalid ANC_FB_MIC_CH_L: 0x%04X", ANC_FB_MIC_CH_L);
            }
            if ((ANC_FB_MIC_CH_R & ~NORMAL_MIC_MAP) || (ANC_FB_MIC_CH_R & (ANC_FB_MIC_CH_R - 1))) {
                ASSERT(false, "Invalid ANC_FB_MIC_CH_R: 0x%04X", ANC_FB_MIC_CH_R);
            }
            if (ANC_FB_MIC_CH_L & ANC_FB_MIC_CH_R) {
                ASSERT(false, "Conflicted ANC_FB_MIC_CH_L (0x%04X) and ANC_FB_MIC_CH_R (0x%04X)", ANC_FB_MIC_CH_L, ANC_FB_MIC_CH_R);
            }
#ifdef VOICE_DETECTOR_EN
            if (ANC_FB_MIC_CH_L & AUD_CHANNEL_MAP_CH4) {
                ASSERT(false, "Conflicted ANC_FB_MIC_CH_L and VAD MIC");
            }
            if (ANC_FB_MIC_CH_R & AUD_CHANNEL_MAP_CH4) {
                ASSERT(false, "Conflicted ANC_FB_MIC_CH_R and VAD MIC");
            }
#endif
#else // !ANC_PROD_TEST
#if (ANC_FB_MIC_CH_L & ~NORMAL_MIC_MAP) || (ANC_FB_MIC_CH_L & (ANC_FB_MIC_CH_L - 1))
#error "Invalid ANC_FB_MIC_CH_L"
#endif
#if (ANC_FB_MIC_CH_R & ~NORMAL_MIC_MAP) || (ANC_FB_MIC_CH_R & (ANC_FB_MIC_CH_R - 1))
#error "Invalid ANC_FB_MIC_CH_R"
#endif
#if (ANC_FB_MIC_CH_L & ANC_FB_MIC_CH_R)
#error "Conflicted ANC_FB_MIC_CH_L and ANC_FB_MIC_CH_R"
#endif
#ifdef VOICE_DETECTOR_EN
#if (ANC_FB_MIC_CH_L & AUD_CHANNEL_MAP_CH4)
#error "Conflicted ANC_FB_MIC_CH_L and VAD MIC"
#endif
#if (ANC_FB_MIC_CH_R & AUD_CHANNEL_MAP_CH4)
#error "Conflicted ANC_FB_MIC_CH_R and VAD MIC"
#endif
#endif
#endif // !ANC_PROD_TEST
            if (mic_map & ANC_FB_MIC_CH_L) {
                codec_adc_ch_map |= AUD_CHANNEL_MAP_CH2;
                mic_map &= ~ANC_FB_MIC_CH_L;
                ch_idx = get_msb_pos(ANC_FB_MIC_CH_L);
                if (ANC_FB_MIC_CH_L & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                    ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                    codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH2, ch_idx);
                    codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH2;
                } else {
                    codec->REG_08C = SET_BITFIELD(codec->REG_08C, CODEC_CODEC_ADC_IN_SEL_CH2, ch_idx);
                    codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH2;
                }
            } else if (ANC_FB_MIC_CH_L & AUD_CHANNEL_MAP_ALL) {
                reserv_map |= AUD_CHANNEL_MAP_CH2;
            }
            if (mic_map & ANC_FB_MIC_CH_R) {
                codec_adc_ch_map |= AUD_CHANNEL_MAP_CH3;
                mic_map &= ~ANC_FB_MIC_CH_R;
                ch_idx = get_msb_pos(ANC_FB_MIC_CH_R);
                if (ANC_FB_MIC_CH_R & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                    ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                    codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH3, ch_idx);
                    codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH3;
                } else {
                    codec->REG_090 = SET_BITFIELD(codec->REG_090, CODEC_CODEC_ADC_IN_SEL_CH3, ch_idx);
                    codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH3;
                }
            } else if (ANC_FB_MIC_CH_R & AUD_CHANNEL_MAP_ALL) {
                reserv_map |= AUD_CHANNEL_MAP_CH3;
            }
#if defined(SIDETONE_ENABLE) && !defined(SIDETONE_DEDICATED_ADC_CHAN)
            if (CFG_HW_AUD_SIDETONE_MIC_DEV == ANC_FB_MIC_CH_L) {
                hal_codec_set_sidetone_adc_chan(AUD_CHANNEL_MAP_CH2);
            }
#ifdef ANC_PROD_TEST
            if (CFG_HW_AUD_SIDETONE_MIC_DEV == ANC_FB_MIC_CH_R) {
                ASSERT(false, "SIDETONE MIC cannot be ANC_FB_MIC_CH_R: 0x%X", ANC_FB_MIC_CH_R);
            }
#elif (CFG_HW_AUD_SIDETONE_MIC_DEV == ANC_FB_MIC_CH_R)
#error "SIDETONE MIC cannot be ANC_FB_MIC_CH_R"
#endif
#endif
#endif
#endif // ANC_APP

#ifdef CODEC_DSD
            reserv_map |= AUD_CHANNEL_MAP_CH2 | AUD_CHANNEL_MAP_CH3;
#endif

            if (mic_map & AUD_CHANNEL_MAP_CH4) {
                codec_adc_ch_map |= AUD_CHANNEL_MAP_CH4;
                mic_map &= ~AUD_CHANNEL_MAP_CH4;
                codec->REG_094 = SET_BITFIELD(codec->REG_094, CODEC_CODEC_ADC_IN_SEL_CH4, 4);
                codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH4;
#if defined(SIDETONE_ENABLE) && !defined(SIDETONE_DEDICATED_ADC_CHAN)
                if (CFG_HW_AUD_SIDETONE_MIC_DEV == AUD_CHANNEL_MAP_CH4) {
                    hal_codec_set_sidetone_adc_chan(AUD_CHANNEL_MAP_CH4);
                }
#endif
            }
            if (mic_map & AUD_CHANNEL_MAP_ECMIC_CH0) {
                codec_adc_ch_map |= AUD_CHANNEL_MAP_CH5;
                mic_map &= ~AUD_CHANNEL_MAP_ECMIC_CH0;
                codec->REG_228 &= ~CODEC_CODEC_MC_SEL_CH0;
            }
            if (mic_map & AUD_CHANNEL_MAP_ECMIC_CH1) {
                codec_adc_ch_map |= AUD_CHANNEL_MAP_CH6;
                mic_map &= ~AUD_CHANNEL_MAP_ECMIC_CH1;
                codec->REG_228 |= CODEC_CODEC_MC_SEL_CH1;
            }

            reserv_map |= codec_adc_ch_map;

#ifdef CODEC_MIN_PHASE
            if (min_phase_cfg & (1 << AUD_STREAM_CAPTURE)) {
                if (mic_map && (reserv_map & AUD_CHANNEL_MAP_CH2) == 0) {
                    codec_adc_ch_map |= AUD_CHANNEL_MAP_CH2;
                    reserv_map |= codec_adc_ch_map;
                    ch_idx = get_lsb_pos(mic_map);
                    mic_map &= ~(1 << ch_idx);
                    if ((1 << ch_idx) & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                        ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                        codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH2, ch_idx);
                        codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH2;
                    } else {
                        codec->REG_08C = SET_BITFIELD(codec->REG_08C, CODEC_CODEC_ADC_IN_SEL_CH2, ch_idx);
                        codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH2;
                    }
                }
                if (mic_map && (reserv_map & AUD_CHANNEL_MAP_CH3) == 0) {
                    codec_adc_ch_map |= AUD_CHANNEL_MAP_CH3;
                    reserv_map |= codec_adc_ch_map;
                    ch_idx = get_lsb_pos(mic_map);
                    mic_map &= ~(1 << ch_idx);
                    if ((1 << ch_idx) & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                        ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                        codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH3, ch_idx);
                        codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH3;
                    } else {
                        codec->REG_090 = SET_BITFIELD(codec->REG_090, CODEC_CODEC_ADC_IN_SEL_CH3, ch_idx);
                        codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH3;
                    }
                }
            }
#endif

#ifdef SIDETONE_ENABLE
#if defined(SIDETONE_DEDICATED_ADC_CHAN) || defined(SIDETONE_RESERVED_ADC_CHAN)
            if (mic_map & CFG_HW_AUD_SIDETONE_MIC_DEV) {
                enum AUD_CHANNEL_MAP_T st_map = 0;

                // Alloc sidetone adc chan
                if ((reserv_map & AUD_CHANNEL_MAP_CH0) == 0) {
                    st_map = AUD_CHANNEL_MAP_CH0;
                } else if ((reserv_map & AUD_CHANNEL_MAP_CH2) == 0) {
                    st_map = AUD_CHANNEL_MAP_CH2;
                } else if ((reserv_map & AUD_CHANNEL_MAP_CH4) == 0) {
                    st_map = AUD_CHANNEL_MAP_CH4;
                } else {
                    ASSERT(false, "%s: Cannot alloc dedicated sidetone adc: reserv_map=0x%X", __func__, reserv_map);
                }
                // Associate mic and sidetone adc
                hal_codec_set_sidetone_adc_chan(st_map);
                ch_idx = get_lsb_pos(CFG_HW_AUD_SIDETONE_MIC_DEV);
                i = get_lsb_pos(st_map);
                if ((1 << ch_idx) & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                    ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                    codec->REG_0A8 = (codec->REG_0A8 & ~(CODEC_CODEC_PDM_MUX_CH0_MASK << (3 * i))) |
                        (CODEC_CODEC_PDM_MUX_CH0(ch_idx) << (3 * i));
                    codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH0 << i;
                } else {
                    *(&codec->REG_084 + i) = SET_BITFIELD(*(&codec->REG_084 + i), CODEC_CODEC_ADC_IN_SEL_CH0, ch_idx);
                    codec->REG_0A4 &= ~(CODEC_CODEC_PDM_ADC_SEL_CH0 << i);
                }
#ifdef SIDETONE_DEDICATED_ADC_CHAN
                sidetone_adc_ch_map = st_map;
#else
                mic_map &= ~(1 << ch_idx);
                codec_adc_ch_map |= st_map;
#endif
                // Mark sidetone adc as used
                reserv_map |= st_map;
            }
#endif
#endif

            i = 0;
            while (mic_map && i < NORMAL_ADC_CH_NUM) {
                ASSERT(i < MAX_ANA_MIC_CH_NUM || (mic_map & AUD_CHANNEL_MAP_DIGMIC_ALL),
                    "%s: Not enough ana cap chan: mic_map=0x%X adc_map=0x%X reserv_map=0x%X",
                    __func__, mic_map, codec_adc_ch_map, reserv_map);
                ch_idx = get_lsb_pos(mic_map);
                mic_map &= ~(1 << ch_idx);
                while ((reserv_map & (AUD_CHANNEL_MAP_CH0 << i)) && i < NORMAL_ADC_CH_NUM) {
                    i++;
                }
#if defined(SIDETONE_ENABLE) && !(defined(SIDETONE_DEDICATED_ADC_CHAN) || defined(SIDETONE_RESERVED_ADC_CHAN))
                if (CFG_HW_AUD_SIDETONE_MIC_DEV == (1 << ch_idx)) {
                    if ((reserv_map & AUD_CHANNEL_MAP_CH0) == 0) {
                        i = 0;
                    } else if ((reserv_map & AUD_CHANNEL_MAP_CH2) == 0) {
                        i = 2;
                    } else if ((reserv_map & AUD_CHANNEL_MAP_CH4) == 0) {
                        i = 4;
                    } else {
                        ASSERT(false, "%s: No sidetone adc: reserv_map=0x%X. Try SIDETONE_RESERVED_ADC_CHAN", __func__, reserv_map);
                    }
                    hal_codec_set_sidetone_adc_chan((1 << i));
                }
#endif
                if (i < NORMAL_ADC_CH_NUM) {
                    codec_adc_ch_map |= (AUD_CHANNEL_MAP_CH0 << i);
                    reserv_map |= codec_adc_ch_map;
                    if ((1 << ch_idx) & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                        ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                        codec->REG_0A8 = (codec->REG_0A8 & ~(CODEC_CODEC_PDM_MUX_CH0_MASK << (3 * i))) |
                            (CODEC_CODEC_PDM_MUX_CH0(ch_idx) << (3 * i));
                        codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH0 << i;
                    } else {
                        *(&codec->REG_084 + i) = SET_BITFIELD(*(&codec->REG_084 + i), CODEC_CODEC_ADC_IN_SEL_CH0, ch_idx);
                        codec->REG_0A4 &= ~(CODEC_CODEC_PDM_ADC_SEL_CH0 << i);
                    }
                    i++;
                }
            }

#if defined(SIDETONE_ENABLE) && !(defined(SIDETONE_DEDICATED_ADC_CHAN) || defined(SIDETONE_RESERVED_ADC_CHAN))
            if (mic_map) {
                if (reserv_map + 1 < (1 << NORMAL_ADC_CH_NUM)) {
                    ASSERT(false, "%s: No adc due to sidetone mic_map=0x%X reserv_map=0x%X. Try SIDETONE_RESERVED_ADC_CHAN",
                        __func__, mic_map, reserv_map);
                }
            }
#endif
            ASSERT(mic_map == 0, "%s: Bad cap chan map: 0x%X reserv_map=0x%X", __func__, mic_map, reserv_map);
        }

        if (HAL_CODEC_CONFIG_BITS & cfg->set_flag) {
            cfg_set_mask = 0;
            cfg_clr_mask = CODEC_MODE_16BIT_ADC_CH0 | CODEC_MODE_16BIT_ADC_CH1 | CODEC_MODE_16BIT_ADC_CH2 |
                CODEC_MODE_16BIT_ADC_CH3 | CODEC_MODE_16BIT_ADC_CH4 | CODEC_MODE_16BIT_ADC_CH5 | CODEC_MODE_16BIT_ADC_CH6 |
                CODEC_MODE_24BIT_ADC | CODEC_MODE_32BIT_ADC;
            if (cfg->bits == AUD_BITS_16) {
                cfg_set_mask |= CODEC_MODE_16BIT_ADC_CH0 | CODEC_MODE_16BIT_ADC_CH1 | CODEC_MODE_16BIT_ADC_CH2 |
                CODEC_MODE_16BIT_ADC_CH3 | CODEC_MODE_16BIT_ADC_CH4 | CODEC_MODE_16BIT_ADC_CH5 | CODEC_MODE_16BIT_ADC_CH6;
            } else if (cfg->bits == AUD_BITS_24) {
                cfg_set_mask |= CODEC_MODE_24BIT_ADC;
            } else if (cfg->bits == AUD_BITS_32) {
                cfg_set_mask |= CODEC_MODE_32BIT_ADC;
            } else {
                ASSERT(false, "%s: Bad cap bits: %d", __func__, cfg->bits);
            }
#ifdef VOICE_DETECTOR_EN
            for (int i = 0; i < MAX_ADC_CH_NUM; i++){
                adc_channel_en |= (CODEC_ADC_ENABLE_CH0 << i);
            }

            if(((codec->REG_000 & adc_channel_en) != 0) && ((codec->REG_040 & cfg_set_mask) == 0)){
                ASSERT(false, "%s: Cap bits conflict: %d", __func__, cfg->bits);
            }else
#endif
            codec->REG_040 = (codec->REG_040 & ~cfg_clr_mask) | cfg_set_mask;
        }

        cnt = 0;
        for (i = 0; i < MAX_ADC_CH_NUM; i++) {
            if (codec_adc_ch_map & (AUD_CHANNEL_MAP_CH0 << i)) {
                cnt++;
            }
        }
        ASSERT(cnt == cfg->channel_num, "%s: Invalid capture stream chan cfg: map=0x%X num=%u",
            __func__, codec_adc_ch_map, cfg->channel_num);

        if (HAL_CODEC_CONFIG_SAMPLE_RATE & cfg->set_flag) {
            sample_rate = cfg->sample_rate;

            for(i = 0; i < ARRAY_SIZE(codec_adc_sample_rate); i++) {
                if(codec_adc_sample_rate[i].sample_rate == sample_rate) {
                    break;
                }
            }
            ASSERT(i < ARRAY_SIZE(codec_adc_sample_rate), "%s: Invalid capture sample rate: %d", __func__, sample_rate);
            rate_idx = i;
            ana_dig_div = codec_adc_sample_rate[rate_idx].codec_div / codec_adc_sample_rate[rate_idx].cmu_div;
            ASSERT(ana_dig_div * codec_adc_sample_rate[rate_idx].cmu_div == codec_adc_sample_rate[rate_idx].codec_div,
                "%s: Invalid catpure div for rate %u: codec_div=%u cmu_div=%u", __func__, sample_rate,
                codec_adc_sample_rate[rate_idx].codec_div, codec_adc_sample_rate[rate_idx].cmu_div);

            TRACE(2,"[%s] capture sample_rate=%d", __func__, sample_rate);

#ifdef CODEC_TIMER
            cur_codec_freq = codec_adc_sample_rate[rate_idx].codec_freq;
#endif

            codec_rate_idx[AUD_STREAM_CAPTURE] = rate_idx;

            if (codec_adc_ch_map & EC_ADC_MAP) {
                // If EC enabled, init resample-adc-ch0 to adc0
                codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_ADC_CH0_SEL, 0);
            }

            uint32_t normal_chan_num;

            normal_chan_num = cfg->channel_num;
            if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH5) {
                normal_chan_num--;
            }
            if (codec_adc_ch_map & AUD_CHANNEL_MAP_CH6) {
                normal_chan_num--;
            }

#ifdef __AUDIO_RESAMPLE__
            uint32_t mask, val;

            if (hal_cmu_get_audio_resample_status() && codec_adc_sample_rate[rate_idx].codec_freq != CODEC_FREQ_CRYSTAL) {
                ASSERT(normal_chan_num <= AUD_CHANNEL_NUM_2,
                    "%s: Invalid capture resample chan num: %d/%d map=0x%X", __func__, normal_chan_num, cfg->channel_num, cfg->channel_map);
#ifdef CODEC_TIMER
                cur_codec_freq = CODEC_FREQ_CRYSTAL;
#endif
                if ((codec->REG_0E4 & CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE) == 0 ||
                        resample_rate_idx[AUD_STREAM_CAPTURE] != rate_idx) {
                    resample_rate_idx[AUD_STREAM_CAPTURE] = rate_idx;
                    codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
                    hal_codec_reg_update_delay();
                    codec->REG_0F8 = resample_phase_float_to_value(get_capture_resample_phase());
                    hal_codec_reg_update_delay();
                    codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
                }

                mask = CODEC_CODEC_RESAMPLE_ADC_DUAL_CH |
                    CODEC_CODEC_RESAMPLE_ADC_CH0_SEL_MASK | CODEC_CODEC_RESAMPLE_ADC_CH1_SEL_MASK;
                val = 0;
                cnt = 0;
                for (i = 0; i < NORMAL_ADC_CH_NUM; i++) {
                    if (codec_adc_ch_map & (AUD_CHANNEL_MAP_CH0 << i)) {
                        if (cnt == 0) {
                            val |= CODEC_CODEC_RESAMPLE_ADC_CH0_SEL(i);
                        } else {
                            val |= CODEC_CODEC_RESAMPLE_ADC_CH1_SEL(i);
                        }
                        cnt++;
                    }
                }
                if (normal_chan_num == AUD_CHANNEL_NUM_2) {
                    val |= CODEC_CODEC_RESAMPLE_ADC_DUAL_CH;
                }
            } else {
                mask = CODEC_CODEC_RESAMPLE_ADC_DUAL_CH |
                    CODEC_CODEC_RESAMPLE_ADC_CH0_SEL_MASK | CODEC_CODEC_RESAMPLE_ADC_CH1_SEL_MASK |
                    CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
                val = 0;
            }
            codec->REG_0E4 = (codec->REG_0E4 & ~mask) | val;
#endif

            // Echo cancel channels will check the enable signal of resample ADC CH0, even when resample is disabled
            if (codec_adc_ch_map & EC_ADC_MAP) {
                if (normal_chan_num && (codec->REG_0E4 & CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE) == 0) {
                    for (i = 0; i < NORMAL_ADC_CH_NUM; i++) {
                        if (codec_adc_ch_map & (AUD_CHANNEL_MAP_CH0 << i)) {
                            codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_ADC_CH0_SEL, i);
                            break;
                        }
                    }
                }
            }

#ifdef __AUDIO_RESAMPLE__
            if (!hal_cmu_get_audio_resample_status())
#endif
            {
#ifdef __AUDIO_RESAMPLE__
                ASSERT(codec_adc_sample_rate[rate_idx].codec_freq != CODEC_FREQ_CRYSTAL,
                    "%s: capture sample rate %u is for resample only", __func__, sample_rate);
#endif
                analog_aud_freq_pll_config(codec_adc_sample_rate[rate_idx].codec_freq, codec_adc_sample_rate[rate_idx].codec_div);
                hal_cmu_codec_adc_set_div(codec_adc_sample_rate[rate_idx].cmu_div * CODEC_FREQ_EXTRA_DIV);
            }
            hal_codec_set_adc_down(codec_adc_ch_map, codec_adc_sample_rate[rate_idx].adc_down);
            hal_codec_set_adc_hbf_bypass_cnt(codec_adc_ch_map, codec_adc_sample_rate[rate_idx].bypass_cnt);
        }

#if !(defined(FIXED_CODEC_ADC_VOL) && defined(SINGLE_CODEC_ADC_VOL))
        if (HAL_CODEC_CONFIG_VOL & cfg->set_flag) {
#ifdef SINGLE_CODEC_ADC_VOL
            const CODEC_ADC_VOL_T *adc_gain_db;
            adc_gain_db = hal_codec_get_adc_volume(cfg->vol);
            if (adc_gain_db) {
                hal_codec_set_dig_adc_gain(NORMAL_ADC_MAP, *adc_gain_db);
#ifdef SIDETONE_DEDICATED_ADC_CHAN
                sidetone_adc_gain = *adc_gain_db;
                hal_codec_set_dig_adc_gain(sidetone_adc_ch_map, sidetone_adc_gain + sidetone_gain_offset);
#endif
            }
#else // !SINGLE_CODEC_ADC_VOL
            uint32_t vol;

            mic_map = codec_mic_ch_map;
            while (mic_map) {
                ch_idx = get_lsb_pos(mic_map);
                mic_map &= ~(1 << ch_idx);
                vol = hal_codec_get_mic_chan_volume_level(1 << ch_idx);
                hal_codec_set_chan_vol(AUD_STREAM_CAPTURE, (1 << ch_idx), vol);
            }
#ifdef SIDETONE_DEDICATED_ADC_CHAN
            if (codec_mic_ch_map & CFG_HW_AUD_SIDETONE_MIC_DEV) {
                const CODEC_ADC_VOL_T *adc_gain_db;

                vol = hal_codec_get_mic_chan_volume_level(CFG_HW_AUD_SIDETONE_MIC_DEV);
                adc_gain_db = hal_codec_get_adc_volume(vol);
                if (adc_gain_db) {
                    sidetone_adc_gain = *adc_gain_db;
                    hal_codec_set_dig_adc_gain(sidetone_adc_ch_map, sidetone_adc_gain + sidetone_gain_offset);
                }
            }
#endif
#endif // !SINGLE_CODEC_ADC_VOL
        }
#endif
    }

    return 0;
}

int hal_codec_anc_adc_enable(enum ANC_TYPE_T type)
{
#ifdef ANC_APP
    enum AUD_CHANNEL_MAP_T map;
    enum AUD_CHANNEL_MAP_T mic_map;
    uint8_t ch_idx;

    map = 0;
    mic_map = 0;
    if (type == ANC_FEEDFORWARD) {
#if defined(ANC_FF_MIC_CH_L) || defined(ANC_FF_MIC_CH_R)
        if (ANC_FF_MIC_CH_L) {
            ch_idx = get_msb_pos(ANC_FF_MIC_CH_L);
            if (ANC_FF_MIC_CH_L & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH0, ch_idx);
                codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH0;
            } else {
                codec->REG_084 = SET_BITFIELD(codec->REG_084, CODEC_CODEC_ADC_IN_SEL_CH0, ch_idx);
                codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH0;
            }
            map |= AUD_CHANNEL_MAP_CH0;
            mic_map |= ANC_FF_MIC_CH_L;
        }
        if (ANC_FF_MIC_CH_R) {
            ch_idx = get_msb_pos(ANC_FF_MIC_CH_R);
            if (ANC_FF_MIC_CH_R & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH1, ch_idx);
                codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH1;
            } else {
                codec->REG_088 = SET_BITFIELD(codec->REG_088, CODEC_CODEC_ADC_IN_SEL_CH1, ch_idx);
                codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH1;
            }
            map |= AUD_CHANNEL_MAP_CH1;
            mic_map |= ANC_FF_MIC_CH_R;
        }
#else
        ASSERT(false, "No ana adc ff ch defined");
#endif
    } else if (type == ANC_FEEDBACK) {
#if defined(ANC_FB_MIC_CH_L) || defined(ANC_FB_MIC_CH_R)
        if (ANC_FB_MIC_CH_L) {
            ch_idx = get_msb_pos(ANC_FB_MIC_CH_L);
            if (ANC_FB_MIC_CH_L & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH2, ch_idx);
                codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH2;
            } else {
                codec->REG_08C = SET_BITFIELD(codec->REG_08C, CODEC_CODEC_ADC_IN_SEL_CH2, ch_idx);
                codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH2;
            }
            map |= AUD_CHANNEL_MAP_CH2;
            mic_map |= ANC_FB_MIC_CH_L;
        }
        if (ANC_FB_MIC_CH_R) {
            ch_idx = get_msb_pos(ANC_FB_MIC_CH_R);
            if (ANC_FB_MIC_CH_R & AUD_CHANNEL_MAP_DIGMIC_ALL) {
                ch_idx = hal_codec_get_digmic_hw_index(ch_idx);
                codec->REG_0A8 = SET_BITFIELD(codec->REG_0A8, CODEC_CODEC_PDM_MUX_CH3, ch_idx);
                codec->REG_0A4 |= CODEC_CODEC_PDM_ADC_SEL_CH3;
            } else {
                codec->REG_090 = SET_BITFIELD(codec->REG_090, CODEC_CODEC_ADC_IN_SEL_CH3, ch_idx);
                codec->REG_0A4 &= ~CODEC_CODEC_PDM_ADC_SEL_CH3;
            }
            map |= AUD_CHANNEL_MAP_CH3;
            mic_map |= ANC_FB_MIC_CH_R;
        }
#else
        ASSERT(false, "No ana adc fb ch defined");
#endif
    }
    anc_adc_ch_map |= map;
    anc_mic_ch_map |= mic_map;

    if (anc_mic_ch_map & AUD_CHANNEL_MAP_DIGMIC_ALL) {
        hal_codec_enable_dig_mic(anc_mic_ch_map);
    }

    for (int i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if (map & (AUD_CHANNEL_MAP_CH0 << i)) {
            if ((codec->REG_080 & (CODEC_CODEC_ADC_EN_CH0 << i)) == 0) {
                // Reset ADC channel
                codec->REG_064 &= ~CODEC_SOFT_RSTN_ADC(1 << i);
                codec->REG_064 |= CODEC_SOFT_RSTN_ADC(1 << i);
                codec->REG_080 |= (CODEC_CODEC_ADC_EN_CH0 << i);
            }
        }
    }

#ifdef DAC_DRE_ENABLE
    if (anc_adc_ch_map && (codec->REG_098 & CODEC_CODEC_DAC_EN)) {
        hal_codec_dac_dre_disable();
    }
#endif
#endif

    return 0;
}

int hal_codec_anc_adc_disable(enum ANC_TYPE_T type)
{
#ifdef ANC_APP
    enum AUD_CHANNEL_MAP_T map;
    enum AUD_CHANNEL_MAP_T mic_map;

    map = 0;
    mic_map = 0;
    if (type == ANC_FEEDFORWARD) {
#if defined(ANC_FF_MIC_CH_L) || defined(ANC_FF_MIC_CH_R)
        if (ANC_FF_MIC_CH_L) {
            map |= AUD_CHANNEL_MAP_CH0;
            mic_map |= ANC_FF_MIC_CH_L;
        }
        if (ANC_FF_MIC_CH_R) {
            map |= AUD_CHANNEL_MAP_CH1;
            mic_map |= ANC_FF_MIC_CH_R;
        }
#endif
    } else if (type == ANC_FEEDBACK) {
#if defined(ANC_FB_MIC_CH_L) || defined(ANC_FB_MIC_CH_R)
        if (ANC_FB_MIC_CH_L) {
            map |= AUD_CHANNEL_MAP_CH2;
            mic_map |= ANC_FB_MIC_CH_L;
        }
        if (ANC_FB_MIC_CH_R) {
            map |= AUD_CHANNEL_MAP_CH3;
            mic_map |= ANC_FB_MIC_CH_R;
        }
#endif
    }
    anc_adc_ch_map &= ~map;
    anc_mic_ch_map &= ~mic_map;

    if ((anc_mic_ch_map & AUD_CHANNEL_MAP_DIGMIC_ALL) == 0 &&
            ((codec_mic_ch_map & AUD_CHANNEL_MAP_DIGMIC_ALL) == 0 || (codec->REG_000 & CODEC_ADC_ENABLE) == 0)) {
        hal_codec_disable_dig_mic();
    }

    for (int i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if ((map & (AUD_CHANNEL_MAP_CH0 << i)) == 0) {
            continue;
        }
        if (codec->REG_000 & CODEC_ADC_ENABLE) {
            if (codec_adc_ch_map & (AUD_CHANNEL_MAP_CH0 << i)) {
                continue;
            }
            if (i == 0 &&
                    (codec->REG_228 & (CODEC_CODEC_MC_ENABLE_CH0 | CODEC_CODEC_MC_ENABLE_CH1)) &&
                    (codec_adc_ch_map & ~EC_ADC_MAP) == 0) {
                continue;
            }
        }
        codec->REG_080 &= ~(CODEC_CODEC_ADC_EN_CH0 << i);
    }

#ifdef DAC_DRE_ENABLE
    if (anc_adc_ch_map == 0 && (codec->REG_098 & CODEC_CODEC_DAC_EN) &&
            //(codec->REG_044 & CODEC_MODE_16BIT_DAC) == 0 &&
            1) {
        hal_codec_dac_dre_enable();
    }
#endif
#endif

    return 0;
}

enum AUD_SAMPRATE_T hal_codec_anc_convert_rate(enum AUD_SAMPRATE_T rate)
{
    if (hal_cmu_get_audio_resample_status()) {
        return AUD_SAMPRATE_50781;
    } else if (CODEC_FREQ_48K_SERIES / rate * rate == CODEC_FREQ_48K_SERIES) {
        return AUD_SAMPRATE_48000;
    } else /* if (CODEC_FREQ_44_1K_SERIES / rate * rate == CODEC_FREQ_44_1K_SERIES) */ {
        return AUD_SAMPRATE_44100;
    }
}

int hal_codec_anc_dma_enable(enum HAL_CODEC_ID_T id)
{
    return 0;
}

int hal_codec_anc_dma_disable(enum HAL_CODEC_ID_T id)
{
    return 0;
}

int hal_codec_aux_mic_dma_enable(enum HAL_CODEC_ID_T id)
{
    return 0;
}

int hal_codec_aux_mic_dma_disable(enum HAL_CODEC_ID_T id)
{
    return 0;
}

uint32_t hal_codec_get_alg_dac_shift(void)
{
    return 0;
}

#ifdef ANC_APP
void hal_codec_set_anc_boost_gain_attn(float attn)
{
    anc_boost_gain_attn = attn;

#ifdef AUDIO_OUTPUT_SW_GAIN
    hal_codec_set_sw_gain(swdac_gain);
#else
    hal_codec_restore_dig_dac_gain();
#endif
    hal_codec_restore_dig_adc_gain();
}

void hal_codec_apply_anc_adc_gain_offset(enum ANC_TYPE_T type, int8_t offset_l, int8_t offset_r)
{
    enum AUD_CHANNEL_MAP_T map_l, map_r;
    enum AUD_CHANNEL_MAP_T ch_map;
    uint8_t ch_idx;

    if (analog_debug_get_anc_calib_mode()) {
        return;
    }

    map_l = 0;
    map_r = 0;

#if defined(ANC_FF_MIC_CH_L) || defined(ANC_FF_MIC_CH_R)
    if (type & ANC_FEEDFORWARD) {
        if (ANC_FF_MIC_CH_L) {
            map_l |= AUD_CHANNEL_MAP_CH0;
        }
        if (ANC_FF_MIC_CH_R) {
            map_r |= AUD_CHANNEL_MAP_CH1;
        }
    }
#endif
#if defined(ANC_FB_MIC_CH_L) || defined(ANC_FB_MIC_CH_R)
    if (type & ANC_FEEDBACK) {
        if (ANC_FB_MIC_CH_L) {
            map_l |= AUD_CHANNEL_MAP_CH2;
        }
        if (ANC_FB_MIC_CH_R) {
            map_r |= AUD_CHANNEL_MAP_CH3;
        }
    }
#endif

    if (map_l) {
        ch_map = map_l;
        while (ch_map) {
            ch_idx = get_msb_pos(ch_map);
            ch_map &= ~(1 << ch_idx);
            anc_adc_gain_offset[ch_idx] = offset_l;
        }
        if (offset_l) {
            anc_adc_gain_offset_map |= map_l;
        } else {
            anc_adc_gain_offset_map &= ~map_l;
        }
    }
    if (map_r) {
        ch_map = map_r;
        while (ch_map) {
            ch_idx = get_msb_pos(ch_map);
            ch_map &= ~(1 << ch_idx);
            anc_adc_gain_offset[ch_idx] = offset_r;
        }
        if (offset_r) {
            anc_adc_gain_offset_map |= map_r;
        } else {
            anc_adc_gain_offset_map &= ~map_r;
        }
    }
    if (map_l || map_r) {
        hal_codec_restore_dig_adc_gain();
    }
}
#endif

#ifdef AUDIO_OUTPUT_DC_CALIB
void hal_codec_set_dac_dc_gain_attn(float attn)
{
    dac_dc_gain_attn = attn;
}

void hal_codec_set_dac_dc_offset(int16_t dc_l, int16_t dc_r)
{
    // DC calib values are based on 16-bit, but hardware compensation is based on 24-bit
    dac_dc_l = dc_l << 8;
    dac_dc_r = dc_r << 8;
#ifdef SDM_MUTE_NOISE_SUPPRESSION
    if (dac_dc_l == 0) {
        dac_dc_l = 1;
    }
    if (dac_dc_r == 0) {
        dac_dc_r = 1;
    }
#endif
}
#endif

void hal_codec_set_dac_reset_callback(HAL_CODEC_DAC_RESET_CALLBACK callback)
{
    //dac_reset_callback = callback;
}

static uint32_t POSSIBLY_UNUSED hal_codec_get_adc_chan(enum AUD_CHANNEL_MAP_T mic_map)
{
    uint8_t adc_ch;
    uint8_t mic_ch;
    uint8_t digmic_ch0;
    uint8_t en_ch;
    bool digmic;
    int i;

    adc_ch = MAX_ADC_CH_NUM;

    mic_ch = get_lsb_pos(mic_map);

    if (((1 << mic_ch) & codec_mic_ch_map) == 0) {
        return adc_ch;
    }

    digmic_ch0 = get_lsb_pos(AUD_CHANNEL_MAP_DIGMIC_CH0);

    if (mic_ch >= digmic_ch0) {
        mic_ch -= digmic_ch0;
        digmic = true;
    } else {
        digmic = false;
    }

    for (i = 0; i < NORMAL_ADC_CH_NUM; i++) {
        if (codec_adc_ch_map & (1 << i)) {
            if (digmic ^ !!(codec->REG_0A4 & (CODEC_CODEC_PDM_ADC_SEL_CH0 << i))) {
                continue;
            }
            if (digmic) {
                en_ch = (codec->REG_0A8 & (CODEC_CODEC_PDM_MUX_CH0_MASK << (3 * i))) >> (CODEC_CODEC_PDM_MUX_CH0_SHIFT + 3 * i);
            } else {
                en_ch = GET_BITFIELD(*(&codec->REG_084 + i), CODEC_CODEC_ADC_IN_SEL_CH0);
            }
            if (mic_ch == en_ch) {
                adc_ch = i;
                break;
            }
        }
    }

    return adc_ch;
}

void hal_codec_sidetone_enable(void)
{
#ifdef SIDETONE_ENABLE
#if (CFG_HW_AUD_SIDETONE_MIC_DEV & (CFG_HW_AUD_SIDETONE_MIC_DEV - 1))
#error "Invalid CFG_HW_AUD_SIDETONE_MIC_DEV: only 1 mic can be defined"
#endif
#if (CFG_HW_AUD_SIDETONE_MIC_DEV == 0) || (CFG_HW_AUD_SIDETONE_MIC_DEV & ~NORMAL_MIC_MAP)
#error "Invalid CFG_HW_AUD_SIDETONE_MIC_DEV: bad mic channel"
#endif
    int gain = CFG_HW_AUD_SIDETONE_GAIN_DBVAL;
    uint32_t val;

#ifdef SIDETONE_DEDICATED_ADC_CHAN
    sidetone_gain_offset = 0;
    if (gain > MAX_SIDETONE_DBVAL) {
        sidetone_gain_offset = gain - MAX_SIDETONE_DBVAL;
    } else if (gain < MIN_SIDETONE_DBVAL) {
        sidetone_gain_offset = gain - MIN_SIDETONE_DBVAL;
    }
#endif

    if (gain > MAX_SIDETONE_DBVAL) {
        gain = MAX_SIDETONE_DBVAL;
    } else if (gain < MIN_SIDETONE_DBVAL) {
        gain = MIN_SIDETONE_DBVAL;
    }

    val = MIN_SIDETONE_REGVAL + (gain - MIN_SIDETONE_DBVAL) / SIDETONE_DBVAL_STEP;

    codec->REG_080 = SET_BITFIELD(codec->REG_080, CODEC_CODEC_SIDE_TONE_GAIN, val);

#ifdef SIDETONE_DEDICATED_ADC_CHAN
    uint8_t adc_ch;

    adc_ch = get_lsb_pos(sidetone_adc_ch_map);
    if (adc_ch >= NORMAL_ADC_CH_NUM) {
        return;
    }

    hal_codec_set_dig_adc_gain(sidetone_adc_ch_map, sidetone_adc_gain + sidetone_gain_offset);
#ifdef CFG_HW_AUD_SIDETONE_GAIN_RAMP
    hal_codec_get_adc_gain(sidetone_adc_ch_map, &sidetone_ded_chan_coef);
    hal_codec_set_dig_adc_gain(sidetone_adc_ch_map, MIN_DIG_DBVAL);
#endif
    codec->REG_080 |= (CODEC_CODEC_ADC_EN_CH0 << adc_ch);

#ifdef CFG_HW_AUD_SIDETONE_IIR_INDEX
#if (CFG_HW_AUD_SIDETONE_IIR_INDEX >= ADC_IIR_CH_NUM + 0UL)
#error "Invalid CFG_HW_AUD_SIDETONE_IIR_INDEX"
#endif
    uint32_t mask;

    if (CFG_HW_AUD_SIDETONE_IIR_INDEX == 0) {
        mask = CODEC_CODEC_ADC_IIR_CH0_SEL_MASK;
        val = CODEC_CODEC_ADC_IIR_CH0_SEL(adc_ch);
    } else {
        mask = CODEC_CODEC_ADC_IIR_CH1_SEL_MASK;
        val = CODEC_CODEC_ADC_IIR_CH1_SEL(adc_ch);
    }
    codec->REG_078 = (codec->REG_078 & ~mask) | val;
#endif
#endif
#endif
}

void hal_codec_sidetone_disable(void)
{
#ifdef SIDETONE_ENABLE
    codec->REG_080 = SET_BITFIELD(codec->REG_080, CODEC_CODEC_SIDE_TONE_GAIN, MUTE_SIDETONE_REGVAL);
#ifdef SIDETONE_DEDICATED_ADC_CHAN
    if (sidetone_adc_ch_map) {
        uint8_t adc_ch;

        adc_ch = get_lsb_pos(sidetone_adc_ch_map);
        codec->REG_080 &= ~(CODEC_CODEC_ADC_EN_CH0 << adc_ch);
    }
#endif
#endif
}

int hal_codec_sidetone_gain_ramp_up(float step)
{
    int ret = 0;
#ifdef CFG_HW_AUD_SIDETONE_GAIN_RAMP
    float coef;
    uint32_t val;

    hal_codec_get_adc_gain(sidetone_adc_ch_map, &coef);
    coef += step;
    if (coef >= sidetone_ded_chan_coef) {
        coef = sidetone_ded_chan_coef;
        ret = 1;
    }
    // Gain format: 8.12
    int32_t s_val = (int32_t)(coef * (1 << 12));
    val = __SSAT(s_val, 20);
    hal_codec_set_adc_gain_value(sidetone_adc_ch_map, val);

#endif
    return ret;
}

int hal_codec_sidetone_gain_ramp_down(float step)
{
    int ret = 0;
#ifdef CFG_HW_AUD_SIDETONE_GAIN_RAMP
    float coef;
    uint32_t val;

    hal_codec_get_adc_gain(sidetone_adc_ch_map, &coef);
    coef -= step;
    if (coef <= 0) {
        coef = 0;
        ret = 1;
    }

    // Gain format: 8.12
    int32_t s_val = (int32_t)(coef * (1 << 12));
    val = __SSAT(s_val, 20);
    hal_codec_set_adc_gain_value(sidetone_adc_ch_map, val);
#endif
    return ret;
}

void hal_codec_select_adc_iir_mic(uint32_t index, enum AUD_CHANNEL_MAP_T mic_map)
{
    uint32_t mask, val;
    uint8_t adc_ch;

    ASSERT(index < ADC_IIR_CH_NUM, "%s: Bad index=%u", __func__, index);
    ASSERT(mic_map && (mic_map & (mic_map - 1)) == 0, "%s: Bad mic_map=0x%X", __func__, mic_map);
#ifdef CFG_HW_AUD_SIDETONE_IIR_INDEX
    ASSERT(index != CFG_HW_AUD_SIDETONE_IIR_INDEX, "%s: Adc iir index conflicts with sidetone", __func__);
#endif

    adc_ch = hal_codec_get_adc_chan(mic_map);
    if (index == 0) {
        mask = CODEC_CODEC_ADC_IIR_CH0_SEL_MASK;
        val = CODEC_CODEC_ADC_IIR_CH0_SEL(adc_ch);
    } else {
        mask = CODEC_CODEC_ADC_IIR_CH1_SEL_MASK;
        val = CODEC_CODEC_ADC_IIR_CH1_SEL(adc_ch);
    }
    codec->REG_078 = (codec->REG_078 & ~mask) | val;
}

void hal_codec_min_phase_mode_enable(enum AUD_STREAM_T stream)
{
#ifdef CODEC_MIN_PHASE
    if (min_phase_cfg == 0 && codec_opened) {
        hal_codec_min_phase_init();
    }

    min_phase_cfg |= (1 << stream);
#endif
}

void hal_codec_min_phase_mode_disable(enum AUD_STREAM_T stream)
{
#ifdef CODEC_MIN_PHASE
    min_phase_cfg &= ~(1 << stream);

    if (min_phase_cfg == 0 && codec_opened) {
        hal_codec_min_phase_term();
    }
#endif
}

void hal_codec_sync_dac_enable(enum HAL_CODEC_SYNC_TYPE_T type)
{
#if defined(ANC_APP)
    //hal_codec_sync_dac_resample_rate_enable(type);
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_DAC_ENABLE_SEL, type);
#else
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_CODEC_DAC_ENABLE_SEL, type);
#endif
}

void hal_codec_sync_dac_disable(void)
{
#if defined(ANC_APP)
    //hal_codec_sync_dac_resample_rate_disable();
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_DAC_ENABLE_SEL, HAL_CODEC_SYNC_TYPE_NONE);
#else
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_CODEC_DAC_ENABLE_SEL, HAL_CODEC_SYNC_TYPE_NONE);
#endif
}

void hal_codec_sync_adc_enable(enum HAL_CODEC_SYNC_TYPE_T type)
{
#if defined(ANC_APP)
    //hal_codec_sync_adc_resample_rate_enable(type);
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_ADC_ENABLE_SEL, type);
#else
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_CODEC_ADC_ENABLE_SEL, type);
#endif
}

void hal_codec_sync_adc_disable(void)
{
#if defined(ANC_APP)
    //hal_codec_sync_adc_resample_rate_disable();
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_ADC_ENABLE_SEL, HAL_CODEC_SYNC_TYPE_NONE);
#else
    codec->REG_054 = SET_BITFIELD(codec->REG_054, CODEC_CODEC_ADC_ENABLE_SEL, HAL_CODEC_SYNC_TYPE_NONE);
#endif
}

void hal_codec_sync_dac_resample_rate_enable(enum HAL_CODEC_SYNC_TYPE_T type)
{
    codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_DAC_TRIGGER_SEL, type);
}

void hal_codec_sync_dac_resample_rate_disable(void)
{
    codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_DAC_TRIGGER_SEL, HAL_CODEC_SYNC_TYPE_NONE);
}

void hal_codec_sync_adc_resample_rate_enable(enum HAL_CODEC_SYNC_TYPE_T type)
{
    codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_ADC_TRIGGER_SEL, type);
}

void hal_codec_sync_adc_resample_rate_disable(void)
{
    codec->REG_0E4 = SET_BITFIELD(codec->REG_0E4, CODEC_CODEC_RESAMPLE_ADC_TRIGGER_SEL, HAL_CODEC_SYNC_TYPE_NONE);
}

void hal_codec_sync_dac_gain_enable(enum HAL_CODEC_SYNC_TYPE_T type)
{
    codec->REG_09C = SET_BITFIELD(codec->REG_09C, CODEC_CODEC_DAC_GAIN_TRIGGER_SEL, type);
}

void hal_codec_sync_dac_gain_disable(void)
{
    codec->REG_09C = SET_BITFIELD(codec->REG_09C, CODEC_CODEC_DAC_GAIN_TRIGGER_SEL, HAL_CODEC_SYNC_TYPE_NONE);
}

void hal_codec_sync_adc_gain_enable(enum HAL_CODEC_SYNC_TYPE_T type)
{
}

void hal_codec_sync_adc_gain_disable(void)
{
}

void hal_codec_gpio_trigger_debounce_enable(void)
{
    if (codec_opened) {
        codec->REG_054 |= CODEC_GPIO_TRIGGER_DB_ENABLE;
    }
}

void hal_codec_gpio_trigger_debounce_disable(void)
{
    if (codec_opened) {
        codec->REG_054 &= ~CODEC_GPIO_TRIGGER_DB_ENABLE;
    }
}

#ifdef CODEC_TIMER
uint32_t hal_codec_timer_get(void)
{
    if (codec_opened) {
        return codec->REG_050;
    }

    return 0;
}

uint32_t hal_codec_timer_ticks_to_us(uint32_t ticks)
{
    uint32_t timer_freq;

    timer_freq = cur_codec_freq / 4 / CODEC_FREQ_EXTRA_DIV;

    return (uint32_t)((float)ticks * 1000000 / timer_freq);
}

void hal_codec_timer_trigger_read(void)
{
    if (codec_opened) {
        codec->REG_078 ^= CODEC_GET_CNT_TRIG;
        hal_codec_reg_update_delay();
    }
}
#endif

#ifdef AUDIO_OUTPUT_DC_CALIB_ANA
int hal_codec_dac_sdm_reset_set(void)
{
    if (codec_opened) {
        hal_codec_set_dac_gain_value(VALID_DAC_MAP, 0);
        if (codec->REG_098 & CODEC_CODEC_DAC_EN) {
            osDelay(dac_delay_ms);
        }
        for (int i = 0x200; i >= 0; i -= 0x100) {
            hal_codec_dac_dc_offset_enable(i, i);
            osDelay(1);
        }
        codec->REG_098 |= CODEC_CODEC_DAC_SDM_CLOSE;
        osDelay(1);
    }

    return 0;
}

int hal_codec_dac_sdm_reset_clear(void)
{
    if (codec_opened) {
        osDelay(1);
        codec->REG_098 &= ~CODEC_CODEC_DAC_SDM_CLOSE;
        for (int i = 0x100; i <= 0x300; i += 0x100) {
            hal_codec_dac_dc_offset_enable(i, i);
            osDelay(1);
        }
        hal_codec_restore_dig_dac_gain();
    }

    return 0;
}
#endif

void hal_codec_tune_resample_rate(enum AUD_STREAM_T stream, float ratio)
{
#ifdef __AUDIO_RESAMPLE__
    uint32_t val;

    if (!codec_opened) {
        return;
    }

    if (stream == AUD_STREAM_PLAYBACK) {
        if (codec->REG_0E4 & CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE) {
            codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
            hal_codec_reg_update_delay();
            val = resample_phase_float_to_value(get_playback_resample_phase());
            val += (int)(val * ratio);
            codec->REG_0F4 = val;
            hal_codec_reg_update_delay();
            codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
        }
    } else {
        if (codec->REG_0E4 & CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE) {
            codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
            hal_codec_reg_update_delay();
            val = resample_phase_float_to_value(get_capture_resample_phase());
            val -= (int)(val * ratio);
            codec->REG_0F8 = val;
            hal_codec_reg_update_delay();
            codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
        }
    }
#endif
}

void hal_codec_tune_both_resample_rate(float ratio)
{
#ifdef __AUDIO_RESAMPLE__
    bool update[2];
    uint32_t val[2];
    uint32_t lock;

    if (!codec_opened) {
        return;
    }

    update[0] = !!(codec->REG_0E4 & CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE);
    update[1] = !!(codec->REG_0E4 & CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE);

    val[0] = val[1] = 0;

    if (update[0]) {
        codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
        val[0] = resample_phase_float_to_value(get_playback_resample_phase());
        val[0] += (int)(val[0] * ratio);
    }
    if (update[1]) {
        codec->REG_0E4 &= ~CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
        val[1] = resample_phase_float_to_value(get_capture_resample_phase());
        val[1] -= (int)(val[1] * ratio);
    }

    hal_codec_reg_update_delay();

    if (update[0]) {
        codec->REG_0F4 = val[0];
    }
    if (update[1]) {
        codec->REG_0F8 = val[1];
    }

    hal_codec_reg_update_delay();

    lock = int_lock();
    if (update[0]) {
        codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_DAC_PHASE_UPDATE;
    }
    if (update[1]) {
        codec->REG_0E4 |= CODEC_CODEC_RESAMPLE_ADC_PHASE_UPDATE;
    }
    int_unlock(lock);
#endif
}

int hal_codec_select_clock_out(uint32_t cfg)
{
    uint32_t lock;
    int ret = 1;

    lock = int_lock();

    if (codec_opened) {
        codec->REG_060 = SET_BITFIELD(codec->REG_060, CODEC_CFG_CLK_OUT, cfg);
        ret = 0;
    }

    int_unlock(lock);

    return ret;
}

#ifdef AUDIO_ANC_FB_MC
void hal_codec_setup_mc(enum AUD_CHANNEL_NUM_T channel_num, enum AUD_BITS_T bits)
{
    if (channel_num == AUD_CHANNEL_NUM_2) {
        mc_dual_chan = true;
    } else {
        mc_dual_chan = false;
    }

    if (bits <= AUD_BITS_16) {
        mc_16bit = true;
    } else {
        mc_16bit = false;
    }
}
#endif

void hal_codec_swap_output(bool swap)
{
#ifdef AUDIO_OUTPUT_SWAP
    output_swap = swap;

    if (codec_opened) {
        if (output_swap) {
            codec->REG_0A0 |= CODEC_CODEC_DAC_OUT_SWAP;
        } else {
            codec->REG_0A0 &= ~CODEC_CODEC_DAC_OUT_SWAP;
        }
    }
#endif
}

int hal_codec_config_digmic_phase(uint8_t phase)
{
#ifdef ANC_PROD_TEST
    codec_digmic_phase = phase;
#endif
    return 0;
}

static void hal_codec_general_irq_handler(void)
{
    uint32_t status;

    status = codec->REG_00C;
    codec->REG_00C = status;

    status &= codec->REG_010;

    for (int i = 0; i < CODEC_IRQ_TYPE_QTY; i++) {
        if (codec_irq_callback[i]) {
            codec_irq_callback[i](status);
        }
    }
}

static void hal_codec_set_irq_handler(enum CODEC_IRQ_TYPE_T type, HAL_CODEC_IRQ_CALLBACK cb)
{
    uint32_t lock;

    ASSERT(type < CODEC_IRQ_TYPE_QTY, "%s: Bad type=%d", __func__, type);

    lock = int_lock();

    codec_irq_callback[type] = cb;

    if (cb) {
        if (codec_irq_map == 0) {
            NVIC_SetVector(CODEC_IRQn, (uint32_t)hal_codec_general_irq_handler);
            NVIC_SetPriority(CODEC_IRQn, IRQ_PRIORITY_HIGHPLUSPLUS);
            NVIC_ClearPendingIRQ(CODEC_IRQn);
            NVIC_EnableIRQ(CODEC_IRQn);
        }
        codec_irq_map |= (1 << type);
    } else {
        codec_irq_map &= ~(1 << type);
        if (codec_irq_map == 0) {
            NVIC_DisableIRQ(CODEC_IRQn);
            NVIC_ClearPendingIRQ(CODEC_IRQn);
        }
    }

    int_unlock(lock);
}

void hal_codec_anc_fb_check_set_irq_handler(HAL_CODEC_IRQ_CALLBACK cb)
{
    hal_codec_set_irq_handler(CODEC_IRQ_TYPE_ANC_FB_CHECK, cb);
}

/* AUDIO CODEC VOICE ACTIVE DETECTION DRIVER */
#ifdef VOICE_DETECTOR_EN

//#define CODEC_VAD_DEBUG

static inline void hal_codec_vad_set_udc(int v)
{
    codec->REG_14C &= ~CODEC_VAD_U_DC(0xf);
    codec->REG_14C |= CODEC_VAD_U_DC(v);
}

static inline void hal_codec_vad_set_upre(int v)
{
    codec->REG_14C &= ~CODEC_VAD_U_PRE(0x7);
    codec->REG_14C |= CODEC_VAD_U_PRE(v);
}

static inline void hal_codec_vad_set_frame_len(int v)
{
    codec->REG_14C &= ~CODEC_VAD_FRAME_LEN(0xff);
    codec->REG_14C |= CODEC_VAD_FRAME_LEN(v);
}

static inline void hal_codec_vad_set_mvad(int v)
{
    codec->REG_14C &= ~CODEC_VAD_MVAD(0xf);
    codec->REG_14C |= CODEC_VAD_MVAD(v);
}

static inline void hal_codec_vad_set_pre_gain(int v)
{
    codec->REG_14C &= ~CODEC_VAD_PRE_GAIN(0x3f);
    codec->REG_14C |= CODEC_VAD_PRE_GAIN(v);
}

static inline void hal_codec_vad_set_sth(int v)
{
    codec->REG_14C &= ~CODEC_VAD_STH(0x3f);
    codec->REG_14C |= CODEC_VAD_STH(v);
}

static inline void hal_codec_vad_set_frame_th1(int v)
{
    codec->REG_150 &= ~CODEC_VAD_FRAME_TH1(0xff);
    codec->REG_150 |= CODEC_VAD_FRAME_TH1(v);
}

static inline void hal_codec_vad_set_frame_th2(int v)
{
    codec->REG_150 &= ~CODEC_VAD_FRAME_TH2(0x3ff);
    codec->REG_150 |= CODEC_VAD_FRAME_TH2(v);
}

static inline void hal_codec_vad_set_frame_th3(int v)
{
    codec->REG_150 &= ~CODEC_VAD_FRAME_TH3(0x3fff);
    codec->REG_150 |= CODEC_VAD_FRAME_TH3(v);
}

static inline void hal_codec_vad_set_range1(int v)
{
    codec->REG_154 &= ~CODEC_VAD_RANGE1(0x1f);
    codec->REG_154 |= CODEC_VAD_RANGE1(v);
}

static inline void hal_codec_vad_set_range2(int v)
{
    codec->REG_154 &= ~CODEC_VAD_RANGE2(0x7f);
    codec->REG_154 |= CODEC_VAD_RANGE2(v);
}

static inline void hal_codec_vad_set_range3(int v)
{
    codec->REG_154 &= ~CODEC_VAD_RANGE3(0x1ff);
    codec->REG_154 |= CODEC_VAD_RANGE3(v);
}

static inline void hal_codec_vad_set_range4(int v)
{
    codec->REG_154 &= ~CODEC_VAD_RANGE4(0x3ff);
    codec->REG_154 |= CODEC_VAD_RANGE4(v);
}

static inline void hal_codec_vad_set_psd_th1(int v)
{
    codec->REG_158 &= ~CODEC_VAD_PSD_TH1(0x7ffffff);
    codec->REG_158 |= CODEC_VAD_PSD_TH1(v);
}

static inline void hal_codec_vad_set_psd_th2(int v)
{
    codec->REG_15C &= ~CODEC_VAD_PSD_TH2(0x7ffffff);
    codec->REG_15C |= CODEC_VAD_PSD_TH2(v);
}

static inline void hal_codec_vad_en(int enable)
{
    if (enable) {
        codec->REG_148 |= CODEC_VAD_EN; //enable vad
    } else {
        codec->REG_148 &= ~CODEC_VAD_EN; //disable vad
        codec->REG_148 |= CODEC_VAD_FINISH;
    }
}

static inline void hal_codec_vad_bypass_ds(int bypass)
{
    if (bypass)
        codec->REG_148 |= CODEC_VAD_DS_BYPASS; //bypass ds
    else
        codec->REG_148 &= ~CODEC_VAD_DS_BYPASS; //not bypass ds
}

static inline void hal_codec_vad_bypass_dc(int bypass)
{
    if (bypass)
        codec->REG_148 |= CODEC_VAD_DC_CANCEL_BYPASS; // bypass dc
    else
        codec->REG_148 &= ~CODEC_VAD_DC_CANCEL_BYPASS; //not bypass dc
}

static inline void hal_codec_vad_bypass_pre(int bypass)
{
    if (bypass)
        codec->REG_148 |= CODEC_VAD_PRE_BYPASS; //bypass pre
    else
        codec->REG_148 &= ~CODEC_VAD_PRE_BYPASS; //not bypass pre
}

static inline void hal_codec_vad_dig_mode(int enable)
{
    if (enable)
        codec->REG_148 |= CODEC_VAD_DIG_MODE; //digital mode
    else
        codec->REG_148 &= ~CODEC_VAD_DIG_MODE; //not digital mode
}

static inline void hal_codec_vad_adc_en(int enable)
{
    if (enable) {
        codec->REG_080 |= (CODEC_CODEC_ADC_EN | CODEC_CODEC_ADC_EN_CH4);
    } else {
        uint32_t val;

        val = codec->REG_080;
        val &= ~CODEC_CODEC_ADC_EN_CH4;
        if ((val & (CODEC_CODEC_ADC_EN_CH0 | CODEC_CODEC_ADC_EN_CH1 |
                CODEC_CODEC_ADC_EN_CH2 | CODEC_CODEC_ADC_EN_CH3)) == 0) {
            val &= ~CODEC_CODEC_ADC_EN;
        }
        codec->REG_080 = val;
    }
}

static inline void hal_codec_vad_irq_en(int enable)
{
    if (enable){
        codec->REG_010 |= (CODEC_VAD_FIND_MSK | CODEC_VAD_NOT_FIND_MSK);
    }
    else{
        codec->REG_010 &= ~(CODEC_VAD_FIND_MSK | CODEC_VAD_NOT_FIND_MSK);
    }

    codec->REG_00C = CODEC_VAD_FIND | CODEC_VAD_NOT_FIND;
}

static inline void hal_codec_vad_adc_if_en(int enable)
{
    if (enable) {
        codec->REG_000 |= (CODEC_DMACTRL_RX | CODEC_ADC_ENABLE_CH4 | CODEC_ADC_ENABLE);
    } else {
        codec->REG_000 &= ~(CODEC_DMACTRL_RX | CODEC_ADC_ENABLE_CH4 | CODEC_ADC_ENABLE);
    }
}

static inline void hal_codec_vad_adc_down(int v)
{
    unsigned int regval = codec->REG_094;

    regval &= ~CODEC_CODEC_ADC_DOWN_SEL_CH4(0x3);
    regval |= CODEC_CODEC_ADC_DOWN_SEL_CH4(v);
    codec->REG_094 = regval;
}

#ifdef CODEC_VAD_DEBUG
void hal_codec_vad_reg_dump(void)
{
    TRACE(1,"codec base = %8x\n", (int)&(codec->REG_000));
    TRACE(1,"codec->REG_000 = %x\n", codec->REG_000);
    TRACE(1,"codec->REG_00C = %x\n", codec->REG_00C);
    TRACE(1,"codec->REG_010 = %x\n", codec->REG_010);
    TRACE(1,"codec->REG_060 = %x\n", codec->REG_060);
    TRACE(1,"codec->REG_064 = %x\n", codec->REG_064);
    TRACE(1,"codec->REG_080 = %x\n", codec->REG_080);
    TRACE(1,"codec->REG_094 = %x\n", codec->REG_094);
    TRACE(1,"codec->REG_148 = %x\n", codec->REG_148);
    TRACE(1,"codec->REG_14C = %x\n", codec->REG_14C);
    TRACE(1,"codec->REG_150 = %x\n", codec->REG_150);
    TRACE(1,"codec->REG_154 = %x\n", codec->REG_154);
    TRACE(1,"codec->REG_158 = %x\n", codec->REG_158);
    TRACE(1,"codec->REG_15C = %x\n", codec->REG_15C);
}
#endif

static inline void hal_codec_vad_data_info(uint32_t *data_cnt, uint32_t *addr_cnt)
{
    uint32_t regval = codec->REG_160;

    *data_cnt = GET_BITFIELD(regval, CODEC_VAD_MEM_DATA_CNT) * 2;
    if (*data_cnt >= ((CODEC_VAD_MEM_DATA_CNT_MASK >> CODEC_VAD_MEM_DATA_CNT_SHIFT) - 1) * 2) {
        *data_cnt = ((CODEC_VAD_MEM_DATA_CNT_MASK >> CODEC_VAD_MEM_DATA_CNT_SHIFT) + 1) * 2;
    }
    *addr_cnt = GET_BITFIELD(regval, CODEC_VAD_MEM_ADDR_CNT) * 2;
}

uint32_t hal_codec_vad_recv_data(uint8_t *dst, uint32_t dst_size)
{
    uint8_t *src = (uint8_t *)CODEC_VAD_BUF_ADDR;
    const uint32_t src_size = CODEC_VAD_BUF_SIZE;
    uint32_t len;
    uint32_t start_pos;

    TRACE(5,"%s, dst=%x, dst_size=%d, vad_data_cnt=%d, vad_addr_cnt=%d",
        __func__, (uint32_t)dst, dst_size, vad_data_cnt, vad_addr_cnt);

    if (vad_data_cnt > src_size || vad_addr_cnt >= src_size) {
        return 0;
    }

    if (dst == NULL) {
        return vad_data_cnt;
    }

    if (vad_addr_cnt >= vad_data_cnt) {
        start_pos = vad_addr_cnt - vad_data_cnt;
    } else {
        // In this case (src_size == vad_data_cnt)
        start_pos = vad_addr_cnt + src_size - vad_data_cnt;
    }

    len = MIN(dst_size, vad_data_cnt);

    if (start_pos + len <= src_size) {
        memcpy(dst, src + start_pos, len);
    } else {
        uint32_t len1, len2;
        len1 = src_size - start_pos;
        len2 = len - len1;
        memcpy(dst, src + start_pos, len1);
        memcpy(dst + len1, src, len2);
    }

    TRACE(2,"%s, len=%d", __func__, len);
    return len;
}

void hal_codec_get_vad_data_info(struct CODEC_VAD_BUF_INFO_T* vad_buf_info)
{
    vad_buf_info->base_addr = CODEC_VAD_BUF_ADDR;
    vad_buf_info->buf_size = CODEC_VAD_BUF_SIZE;
    vad_buf_info->data_count = vad_data_cnt;
    vad_buf_info->addr_count = vad_addr_cnt;
}

static void hal_codec_vad_isr(uint32_t irq_status)
{
    if ((irq_status & (CODEC_VAD_FIND | CODEC_VAD_NOT_FIND)) == 0) {
        return;
    }

    TRACE(2,"%s VAD_FIND=%d", __func__, !!(irq_status & CODEC_VAD_FIND));

    if (vad_handler) {
        vad_handler(!!(irq_status & CODEC_VAD_FIND));
    }
}

int hal_codec_vad_config(const struct AUD_VAD_CONFIG_T *conf)
{
    unsigned int adc_channel_en = 0;
    unsigned int  cfg_set_mask = 0;
    unsigned int  cfg_clr_mask = 0;

    if (!conf)
        return -1;

    vad_handler = conf->handler;

    hal_codec_vad_en(0);
    hal_codec_vad_irq_en(0);

    hal_codec_vad_set_udc(conf->udc);
    hal_codec_vad_set_upre(conf->upre);
    hal_codec_vad_set_frame_len(conf->frame_len);
    hal_codec_vad_set_mvad(conf->mvad);
    hal_codec_vad_set_pre_gain(conf->pre_gain);
    hal_codec_vad_set_sth(conf->sth);
    hal_codec_vad_set_frame_th1(conf->frame_th[0]);
    hal_codec_vad_set_frame_th2(conf->frame_th[1]);
    hal_codec_vad_set_frame_th3(conf->frame_th[2]);
    hal_codec_vad_set_range1(conf->range[0]);
    hal_codec_vad_set_range2(conf->range[1]);
    hal_codec_vad_set_range3(conf->range[2]);
    hal_codec_vad_set_range4(conf->range[3]);
    hal_codec_vad_set_psd_th1(conf->psd_th[0]);
    hal_codec_vad_set_psd_th2(conf->psd_th[1]);
    hal_codec_vad_dig_mode(0);
    hal_codec_vad_bypass_dc(0);
    hal_codec_vad_bypass_pre(0);

    if (conf->sample_rate == AUD_SAMPRATE_8000) {
        // select adc down 8KHz
        hal_codec_vad_adc_down(1);
        hal_codec_vad_bypass_ds(1);
    } else if (conf->sample_rate == AUD_SAMPRATE_16000) {
        // select adc down 16KHz
        hal_codec_vad_adc_down(0);
        hal_codec_vad_bypass_ds(0);
    } else {
        ASSERT(false, "%s: Bad sample rate: %u", __func__, conf->sample_rate);
    }

    cfg_clr_mask = CODEC_MODE_16BIT_ADC_CH0 | CODEC_MODE_16BIT_ADC_CH1 | CODEC_MODE_16BIT_ADC_CH2 |
        CODEC_MODE_16BIT_ADC_CH3 | CODEC_MODE_16BIT_ADC_CH4 | CODEC_MODE_16BIT_ADC_CH5 | CODEC_MODE_16BIT_ADC_CH6 |
        CODEC_MODE_24BIT_ADC | CODEC_MODE_32BIT_ADC;

    if (conf->bits == AUD_BITS_16) {
        cfg_set_mask |= CODEC_MODE_16BIT_ADC_CH0 | CODEC_MODE_16BIT_ADC_CH1 | CODEC_MODE_16BIT_ADC_CH2 |
            CODEC_MODE_16BIT_ADC_CH3 | CODEC_MODE_16BIT_ADC_CH4 | CODEC_MODE_16BIT_ADC_CH5 | CODEC_MODE_16BIT_ADC_CH6;
    } else if (conf->bits == AUD_BITS_24) {
        cfg_set_mask |= CODEC_MODE_24BIT_ADC;
    } else if (conf->bits == AUD_BITS_32) {
        cfg_set_mask |= CODEC_MODE_32BIT_ADC;
    } else {
        ASSERT(false, "%s: Bad cap bits: %d", __func__, conf->bits);
    }

    for (int i = 0; i < MAX_ADC_CH_NUM; i++){
        adc_channel_en |= (CODEC_ADC_ENABLE_CH0 << i);
    }

    if(((codec->REG_000 & adc_channel_en) != 0) && ((codec->REG_040 & cfg_set_mask) == 0)){
        ASSERT(false, "%s: Cap bits conflict: %d", __func__, conf->bits);
    }else{
        codec->REG_040 = (codec->REG_040 & ~cfg_clr_mask) | cfg_set_mask;
    }

    codec->REG_220 = 320;
    codec->REG_224 = 32000*3;//vad timeout value
#ifdef I2C_VAD
    codec->REG_230 |= CODEC_VAD_EXT_EN | CODEC_VAD_SRC_SEL;
#endif

#if !(defined(FIXED_CODEC_ADC_VOL) && defined(SINGLE_CODEC_ADC_VOL))
    const CODEC_ADC_VOL_T *adc_gain_db;

#ifdef SINGLE_CODEC_ADC_VOL
    adc_gain_db = hal_codec_get_adc_volume(CODEC_SADC_VOL);
#else
    adc_gain_db = hal_codec_get_adc_volume(hal_codec_get_mic_chan_volume_level(AUD_CHANNEL_MAP_CH4));
#endif
    if (adc_gain_db) {
        hal_codec_set_dig_adc_gain(AUD_CHANNEL_MAP_CH4, *adc_gain_db);
    }
#endif

    return 0;
}

int hal_codec_vad_open(const struct AUD_VAD_CONFIG_T *conf)
{
    vad_type = conf->type;

    // open analog vad
    analog_aud_vad_adc_enable(true);

    // enable vad clock
    hal_cmu_codec_vad_clock_enable(1);

    hal_codec_vad_config(conf);

    return 0;
}

int hal_codec_vad_close(void)
{
#ifdef I2C_VAD
    codec->REG_230 &= ~(CODEC_VAD_EXT_EN | CODEC_VAD_SRC_SEL);
#endif

    // disable vad clock
    hal_cmu_codec_vad_clock_enable(0);

    // close analog vad
    analog_aud_vad_adc_enable(false);

    vad_type = AUD_VAD_TYPE_NONE;

    return 0;
}

int hal_codec_vad_start(void)
{
    if (vad_enabled) {
        return 0;
    }
    vad_enabled = true;
    vad_data_cnt = 0;
    vad_addr_cnt = 0;

    hal_codec_vad_irq_en(1);
    hal_codec_set_irq_handler(CODEC_IRQ_TYPE_VAD, hal_codec_vad_isr);

    if (vad_type == AUD_VAD_TYPE_MIX || vad_type == AUD_VAD_TYPE_DIG) {
        // digital vad
        hal_codec_vad_en(1);
        // enable adc if
        hal_codec_vad_adc_if_en(1);
        // enable adc
        hal_codec_vad_adc_en(1);
    }

    analog_aud_vad_enable(vad_type, true);

    return 0;
}

int hal_codec_vad_stop(void)
{
    if (!vad_enabled) {
        return 0;
    }
    vad_enabled = false;
    hal_codec_vad_data_info(&vad_data_cnt, &vad_addr_cnt);

    analog_aud_vad_enable(vad_type, false);

    hal_codec_vad_irq_en(0);
    hal_codec_set_irq_handler(CODEC_IRQ_TYPE_VAD, NULL);

    if (vad_type == AUD_VAD_TYPE_MIX || vad_type == AUD_VAD_TYPE_DIG) {
        hal_codec_vad_en(0);
        hal_codec_vad_adc_if_en(0);
        hal_codec_vad_adc_en(0);
    }

    return 0;
}

#endif

//********************BT trigger functions: START********************
static void hal_codec_bt_trigger_isr(uint32_t irq_status)
{
    if ((irq_status & CODEC_BT_TRIGGER) == 0) {
        return;
    }

    if (bt_trigger_callback) {
        TRACE(1,"[%s] bt_trigger_callback Start...", __func__);
        bt_trigger_callback();
    } else {
        TRACE(1,"[%s] bt_trigger_callback = NULL", __func__);
    }
}

static inline void hal_codec_bt_trigger_irq_en(int enable)
{
    if (enable)
        codec->REG_010 |= CODEC_BT_TRIGGER_MSK;
    else
        codec->REG_010 &= ~CODEC_BT_TRIGGER_MSK;

    codec->REG_00C = CODEC_BT_TRIGGER;
}

void hal_codec_set_bt_trigger_callback(HAL_CODEC_BT_TRIGGER_CALLBACK callback)
{
    bt_trigger_callback = callback;
}

int hal_codec_bt_trigger_start(void)
{
    uint32_t lock;

    TRACE(1,"[%s] Start", __func__);

    lock = int_lock();

    hal_codec_set_irq_handler(CODEC_IRQ_TYPE_BT_TRIGGER, hal_codec_bt_trigger_isr);
    hal_codec_bt_trigger_irq_en(1);

    int_unlock(lock);

    return 0;
}

int hal_codec_bt_trigger_stop(void)
{
    uint32_t lock;

    TRACE(1,"[%s] Stop", __func__);

    lock = int_lock();

    hal_codec_bt_trigger_irq_en(0);
    hal_codec_set_irq_handler(CODEC_IRQ_TYPE_BT_TRIGGER, NULL);

    int_unlock(lock);

    return 0;
}
//********************BT trigger functions: END********************

