/***************************************************************************
 *
 * 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 "audioflinger.h"
#include "hal_dma.h"
#include "hal_i2s.h"
#include "hal_tdm.h"
#include "hal_codec.h"
#include "hal_spdif.h"
#include "hal_btpcm.h"
#include "codec_tlv32aic32.h"
#include "codec_int.h"

#include "string.h"
#include "hal_trace.h"
#include "hal_cmu.h"
#include "hal_timer.h"
#include "pmu.h"
#include "analog.h"
#include "tgt_hardware.h"

#include "cmsis.h"

#ifdef __RAND_FROM_MIC__
#include "randfrommic.h"
#endif

#ifdef TWS_PROMPT_SYNC
extern void tws_playback_ticks_check_for_mix_prompt(void);
#endif


#ifdef RTOS
#include "cmsis_os.h"
#else
#include "hal_sleep.h"
#endif

#ifdef AUDIO_OUTPUT_SW_LIMITER
#include "floatlimiter.h"
#endif

#if defined(CHIP_BEST2001) && defined(FPGA)
#undef FPGA
#endif

#define AF_TRACE_DEBUG()   // TRACE(2,"%s:%d\n", __func__, __LINE__)

//#define AF_STREAM_ID_0_PLAYBACK_FADEOUT

//#define CORRECT_SAMPLE_VALUE

#ifdef FPGA
#define AF_DEVICE_EXT_CODEC
#endif
#ifdef AUDIO_OUTPUT_DC_CALIB
#ifdef CHIP_BEST1000
#define AUDIO_OUTPUT_DC_CALIB_SW
#define AUDIO_OUTPUT_PA_ON_FADE_IN
#if !(defined(__TWS__) || defined(IBRT))
#define AUDIO_OUTPUT_PA_OFF_FADE_OUT
#endif
#elif (defined(__TWS__) || defined(IBRT)) && !defined(ANC_APP)
#define AUDIO_OUTPUT_PA_ON_FADE_IN
#endif // !CHIP_BEST1000 && __TWS__
#elif defined(AUDIO_OUTPUT_DC_CALIB_ANA) && (defined(__TWS__) || defined(IBRT)) && !defined(ANC_APP)
#define AUDIO_OUTPUT_PA_ON_FADE_IN
#endif // !AUDIO_OUTPUT_DC_CALIB && AUDIO_OUTPUT_DC_CALIB_ANA && __TWS__

#define AF_FADE_OUT_SIGNAL_ID           15
#define AF_FADE_IN_SIGNAL_ID            14

/* config params */
#define AF_CODEC_INST HAL_CODEC_ID_0
#define AF_SPDIF_INST HAL_SPDIF_ID_0
#define AF_BTPCM_INST HAL_BTPCM_ID_0

#define AF_CPU_WAKE_USER                HAL_CPU_WAKE_LOCK_USER_AUDIOFLINGER

#define AF_CODEC_RIGHT_CHAN_ATTN        0.968277856 // -0.28 dB

#define AF_CODEC_VOL_UPDATE_STEP        0.00002

#define AF_CODEC_DC_MAX_SCALE           32767

#define AF_CODEC_DC_STABLE_INTERVAL     MS_TO_TICKS(4)
#ifdef AUDIO_OUTPUT_PA_OFF_FADE_OUT
#define AF_CODEC_PA_RESTART_INTERVAL    MS_TO_TICKS(160)
#else
#define AF_CODEC_PA_RESTART_INTERVAL    MS_TO_TICKS(8)
#endif

// The following might have been defined in tgt_hardware.h
#ifndef AF_CODEC_FADE_IN_MS
#define AF_CODEC_FADE_IN_MS             20
#endif
#ifndef AF_CODEC_FADE_OUT_MS
#define AF_CODEC_FADE_OUT_MS            20
#endif
#define AF_CODEC_FADE_MIN_SAMPLE_CNT    200

#define PP_PINGPANG(v)                  (v == PP_PING ? PP_PANG : PP_PING)

#ifdef AUDIO_ANC_FB_ADJ_MC
#define AF_STACK_SIZE                   (1024*10)
#else
#ifndef AF_STACK_SIZE
#define AF_STACK_SIZE                   (1024*8)
#endif
#endif

#if defined(AUDIO_ANC_FB_MC) && defined(ANC_APP)
#define DYNAMIC_AUDIO_BUFFER_COUNT
#endif

#ifdef DYNAMIC_AUDIO_BUFFER_COUNT
#define MAX_AUDIO_BUFFER_COUNT          16
#define MIN_AUDIO_BUFFER_COUNT          2
#define AUDIO_BUFFER_COUNT              (role->dma_desc_cnt)
#else
#define MAX_AUDIO_BUFFER_COUNT          4
#define AUDIO_BUFFER_COUNT              MAX_AUDIO_BUFFER_COUNT
#if (AUDIO_BUFFER_COUNT & 0x1)
#error "AUDIO_BUFFER_COUNT must be an even number"
#endif
#endif

/* internal use */
enum AF_BOOL_T{
    AF_FALSE = 0,
    AF_TRUE = 1
};

enum AF_RESULT_T{
    AF_RES_SUCCESS = 0,
    AF_RES_FAILD = 1
};

enum AF_DAC_PA_STATE_T {
    AF_DAC_PA_NULL,
    AF_DAC_PA_ON_TRIGGER,
    AF_DAC_PA_ON_SOFT_START,
    AF_DAC_PA_OFF_TRIGGER,
    AF_DAC_PA_OFF_SOFT_END,
    AF_DAC_PA_OFF_SOFT_END_DONE,
    AF_DAC_PA_OFF_DC_START,
};

//status machine
enum AF_STATUS_T{
    AF_STATUS_NULL = 0x00,
    AF_STATUS_OPEN_CLOSE = 0x01,
    AF_STATUS_STREAM_OPEN_CLOSE = 0x02,
    AF_STATUS_STREAM_START_STOP = 0x04,
    AF_STATUS_STREAM_PAUSE_RESTART = 0x08,
    AF_STATUS_MASK = 0x0F,
};

struct af_stream_ctl_t{
    enum AF_PP_T pp_index;      //pingpong operate
    uint8_t pp_cnt;             //use to count the lost signals
    uint8_t status;             //status machine
    enum AUD_STREAM_USE_DEVICE_T use_device;
};

struct af_stream_cfg_t {
    //used inside
    struct af_stream_ctl_t ctl;

    //dma buf parameters, RAM can be alloced in different way
    uint8_t *dma_buf_ptr;
    uint32_t dma_buf_size;

    //store stream cfg parameters
    struct AF_STREAM_CONFIG_T cfg;

    //dma cfg parameters
#ifdef DYNAMIC_AUDIO_BUFFER_COUNT
    uint8_t dma_desc_cnt;
#endif
    struct HAL_DMA_DESC_T dma_desc[MAX_AUDIO_BUFFER_COUNT];
    struct HAL_DMA_CH_CFG_T dma_cfg;

    //callback function
    AF_STREAM_HANDLER_T handler;
};

static struct af_stream_cfg_t af_stream[AUD_STREAM_ID_NUM][AUD_STREAM_NUM];
static uint8_t af_sig_lost_cnt[AUD_STREAM_ID_NUM][AUD_STREAM_NUM];

static AF_CODEC_PLAYBACK_POST_HANDLER_T codec_play_post_hdlr;

#ifdef AUDIO_OUTPUT_SW_GAIN
volatile static float saved_output_coef;
#ifdef AUDIO_OUTPUT_SW_LIMITER
static FloatLimiterPtr FloatLimiterP;
#else
typedef struct {
    float coefs_b[3];
    float coefs_a[3];    
    float history_x[2];
    float history_y[2];    
}SW_GAIN_IIR_T;
SW_GAIN_IIR_T sw_gain_iir=
    {
        .coefs_b={0.00000042780818597736f,   0.00000085561637195472f,   0.00000042780818597736f},
        .coefs_a={1.00000000000000000000f, -1.99738371810092970000f,   0.99738542933367369000f},
        .history_x={0,0},
        .history_y={0,0} ,       
    };
#endif
#endif

#ifdef AUDIO_OUTPUT_DC_CALIB
static bool dac_dc_valid;
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
static int16_t dac_dc[2];
#endif
#endif

#if defined(AUDIO_OUTPUT_PA_ON_FADE_IN) || defined(AUDIO_OUTPUT_PA_OFF_FADE_OUT)
static volatile enum AF_DAC_PA_STATE_T dac_pa_state;
static uint32_t dac_dc_start_time;
static uint32_t dac_pa_stop_time;
#endif

#if defined(AUDIO_OUTPUT_PA_OFF_FADE_OUT) && defined(RTOS)
static osThreadId fade_thread_id;
#ifdef AF_STREAM_ID_0_PLAYBACK_FADEOUT
#error "Cannot enable 2 kinds of fade out codes at the same time"
#endif
#endif

#ifdef RTOS

#ifdef AF_STREAM_ID_0_PLAYBACK_FADEOUT
struct af_stream_fade_out_t{
    bool stop_on_process;
    uint8_t stop_process_cnt;
    osThreadId stop_request_tid;
    uint32_t need_fadeout_len;
    uint32_t need_fadeout_len_processed;
};

static struct af_stream_fade_out_t af_stream_fade_out ={
                                                .stop_on_process = false,
                                                .stop_process_cnt = 0,
                                                .stop_request_tid = NULL,
                                                .need_fadeout_len = 0,
                                                .need_fadeout_len_processed = 0,
};
#endif

static osThreadId af_thread_tid;

static void af_thread(void const *argument);
osThreadDef(af_thread, osPriorityAboveNormal, 1, AF_STACK_SIZE, "audio_flinger");
static int af_default_priority;

osMutexId audioflinger_mutex_id = NULL;
osMutexDef(audioflinger_mutex);

static AF_IRQ_NOTIFICATION_T irq_notif;

#else // !RTOS

static volatile uint32_t af_flag_lock;

#endif // RTOS

#ifdef CODEC_DSD
static bool af_dsd_enabled;
#endif

void af_lock_thread(void)
{
    void * POSSIBLY_UNUSED lr = __builtin_return_address(0);
#ifdef RTOS
    osMutexWait(audioflinger_mutex_id, osWaitForever);
#else
    static void * POSSIBLY_UNUSED locked_lr;
    ASSERT(af_flag_lock == 0, "audioflinger has been locked by %p. LR=%p", (void *)locked_lr, (void *)lr);
    af_flag_lock = 1;
    locked_lr = lr;
#endif
}

void af_unlock_thread(void)
{
    void * POSSIBLY_UNUSED lr = __builtin_return_address(0);
#ifdef RTOS
    osMutexRelease(audioflinger_mutex_id);
#else
    static void * POSSIBLY_UNUSED unlocked_lr;
    ASSERT(af_flag_lock == 1, "audioflinger not locked before (lastly unlocked by %p). LR=%p", (void *)unlocked_lr, (void *)lr);
    af_flag_lock = 0;
    unlocked_lr = lr;
#endif
}

#if defined(RTOS) && defined(AF_STREAM_ID_0_PLAYBACK_FADEOUT)
int af_stream_fadeout_start(uint32_t sample)
{
    TRACE(1,"fadein_config sample:%d", sample);
    af_stream_fade_out.need_fadeout_len = sample;
    af_stream_fade_out.need_fadeout_len_processed = sample;
    return 0;
}

int af_stream_fadeout_stop(void)
{
    af_stream_fade_out.stop_process_cnt = 0;
    af_stream_fade_out.stop_on_process = false;
    return 0;
}

uint32_t af_stream_fadeout(int16_t *buf, uint32_t len, enum AUD_CHANNEL_NUM_T num)
{
    uint32_t i;
    uint32_t j = 0;
    uint32_t start;
    uint32_t end;

    start = af_stream_fade_out.need_fadeout_len_processed;
    end = af_stream_fade_out.need_fadeout_len_processed  > len ?
           af_stream_fade_out.need_fadeout_len_processed - len :  0;

    if (start <= end){
//        TRACE(0,"skip fadeout");
        memset(buf, 0, len*2);
        return len;
    }
//    TRACE(3,"fadeout l:%d start:%d end:%d", len, start, end);
//    DUMP16("%05d ", buf, 10);
//    DUMP16("%05d ", buf+len-10, 10);


    if (num == AUD_CHANNEL_NUM_1){
        for (i = start; i > end; i--){
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
        }
    }else if (num == AUD_CHANNEL_NUM_2){
        for (i = start; i > end; i-=2){
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
        }
    }else if (num == AUD_CHANNEL_NUM_4){
        for (i = start; i > end; i-=4){
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
        }
    }else if (num == AUD_CHANNEL_NUM_8){
        for (i = start; i > end; i-=8){
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
            *(buf+j) = *(buf+j)*i/af_stream_fade_out.need_fadeout_len;
            j++;
        }

    }
    af_stream_fade_out.need_fadeout_len_processed -= j;

//    TRACE(3,"out i:%d process:%d x:%d", i, af_stream_fade_out.need_fadeout_len_processed, end+((start-end)/AUD_CHANNEL_NUM_2));
//    DUMP16("%05d ", buf, 10);
//    DUMP16("%05d ", buf+len-10, 10);

    return len;
}

void af_stream_stop_wait_finish()
{
    af_stream_fade_out.stop_on_process = true;
    af_stream_fade_out.stop_request_tid = osThreadGetId();
    osSignalClear(af_stream_fade_out.stop_request_tid, (1 << AF_FADE_OUT_SIGNAL_ID));
    af_unlock_thread();
    osSignalWait((1 << AF_FADE_OUT_SIGNAL_ID), 300);
    af_lock_thread();
}

void af_stream_stop_process(struct af_stream_cfg_t *af_cfg, uint8_t *buf, uint32_t len)
{
    af_lock_thread();
    if (af_stream_fade_out.stop_on_process){
//        TRACE(5,"%s num:%d size:%d len:%d cnt:%d", __func__, af_cfg->cfg.channel_num, af_cfg->cfg.data_size, len,  af_stream_fade_out.stop_process_cnt);
        af_stream_fadeout((int16_t *)buf, len/2, af_cfg->cfg.channel_num);

//        TRACE(3,"process ret:%d %d %d", *(int16_t *)(buf+len-2-2-2), *(int16_t *)(buf+len-2-2), *(int16_t *)(buf+len-2));
        if (af_stream_fade_out.stop_process_cnt++>3){
            TRACE(0,"stop_process end");
            osSignalSet(af_stream_fade_out.stop_request_tid, (1 << AF_FADE_OUT_SIGNAL_ID));
        }
    }
    af_unlock_thread();
}
#endif // RTOS && AF_STREAM_ID_0_PLAYBACK_FADEOUT

//used by dma irq and af_thread
static inline struct af_stream_cfg_t *af_get_stream_role(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    ASSERT(id < AUD_STREAM_ID_NUM, "[%s] Bad id=%d", __func__, id);
    ASSERT(stream < AUD_STREAM_NUM, "[%s] Bad stream=%d", __func__, stream);

    return &af_stream[id][stream];
}

static void af_set_status(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream, enum AF_STATUS_T status)
{
    struct af_stream_cfg_t *role = NULL;

    role = af_get_stream_role(id, stream);

    role->ctl.status |= status;
}

static void af_clear_status(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream, enum AF_STATUS_T status)
{
    struct af_stream_cfg_t *role = NULL;

    role = af_get_stream_role(id, stream);

    role->ctl.status &= ~status;
}

//get current stream config parameters
uint32_t af_stream_get_cfg(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream, struct AF_STREAM_CONFIG_T **cfg, bool needlock)
{
    AF_TRACE_DEBUG();

    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;

    if (needlock) {
        af_lock_thread();
    }
    role = af_get_stream_role(id, stream);
    //check stream is open
    if (role->ctl.status & AF_STATUS_STREAM_OPEN_CLOSE) {
        *cfg = &(role->cfg);
        ret = AF_RES_SUCCESS;
    } else {
        ret = AF_RES_FAILD;
    }
    if (needlock) {
        af_unlock_thread();
    }
    return ret;
}

#if 0
static void af_dump_cfg()
{
    struct af_stream_cfg_t *role = NULL;

    TRACE(0,"dump cfg.........start");
    //initial parameter
    for(uint8_t id=0; id< AUD_STREAM_ID_NUM; id++)
    {
        for(uint8_t stream=0; stream < AUD_STREAM_NUM; stream++)
        {
            role = af_get_stream_role((enum AUD_STREAM_ID_T)id, (enum AUD_STREAM_T)stream);

            TRACE(2,"id = %d, stream = %d:", id, stream);
            TRACE(1,"ctl.use_device = %d", role->ctl.use_device);
            TRACE(1,"cfg.device = %d", role->cfg.device);
            TRACE(1,"dma_cfg.ch = %d", role->dma_cfg.ch);
        }
    }
    TRACE(0,"dump cfg.........end");
}
#endif

#ifdef AUDIO_OUTPUT_SW_GAIN
static void af_codec_output_gain_changed(float coef)
{
    saved_output_coef = coef;
}
#ifndef AUDIO_OUTPUT_SW_LIMITER
static float af_codec_sw_gain_filter(float gain_in)
{
    float gain_out = saved_output_coef*sw_gain_iir.coefs_b[0]
                            +sw_gain_iir.history_x[0]*sw_gain_iir.coefs_b[1]
                            +sw_gain_iir.history_x[1]*sw_gain_iir.coefs_b[2]
                            -sw_gain_iir.history_y[0]*sw_gain_iir.coefs_a[1]
                            -sw_gain_iir.history_y[1]*sw_gain_iir.coefs_a[2];

    sw_gain_iir.history_y[1]=sw_gain_iir.history_y[0];
    sw_gain_iir.history_y[0]=gain_out;    
    sw_gain_iir.history_x[1]=sw_gain_iir.history_x[0];
    sw_gain_iir.history_x[0]=saved_output_coef;    

    return gain_out;
}
static void af_codec_sw_gain_process(uint8_t *buf, uint32_t size, enum AUD_BITS_T bits, enum AUD_CHANNEL_NUM_T chans)
{
    uint32_t i,pcm_len;
    int32_t pcm_out;
    float output_gain=1.0f;

    //TRACE(1,"af_codec_sw_gain_process:%d",(int32_t)(saved_output_coef*1000));
    
    if(chans==AUD_CHANNEL_NUM_1)
    {
        if (bits <= AUD_BITS_16)
        {
            pcm_len = size / sizeof(int16_t);
            int16_t *pcm_buf = (int16_t *)buf;
            for (i = 0; i < pcm_len; i++)
            {
                output_gain=af_codec_sw_gain_filter(saved_output_coef);

                pcm_out = (int32_t)(pcm_buf[i] * output_gain);
                pcm_buf[i] = __SSAT(pcm_out, 16);
            }
        }
        else
        {
            pcm_len = size / sizeof(int32_t);
            int32_t *pcm_buf = (int32_t *)buf;
            for (i = 0; i < pcm_len; i++)
            {
                output_gain=af_codec_sw_gain_filter(saved_output_coef);
                
                pcm_out = (int32_t)(pcm_buf[i] * output_gain);
                pcm_buf[i] = __SSAT(pcm_out, 24);
            }
        }
    }
    else if(chans==AUD_CHANNEL_NUM_2)
    {
        if (bits <= AUD_BITS_16)
        {
            pcm_len = size / sizeof(int16_t);
            int16_t *pcm_buf = (int16_t *)buf;
            for (i = 0; i < pcm_len; i=i+2)
            {
                output_gain=af_codec_sw_gain_filter(saved_output_coef);
                
                pcm_out = (int32_t)(pcm_buf[i] * output_gain);
                pcm_buf[i] = __SSAT(pcm_out, 16);
                pcm_out = (int32_t)(pcm_buf[i+1] * output_gain);                
                pcm_buf[i+1] = __SSAT(pcm_out, 16);
            }
        }
        else
        {
            pcm_len = size / sizeof(int32_t);
            int32_t *pcm_buf = (int32_t *)buf;
            for (i = 0; i < pcm_len; i=i+2)
            {
                output_gain=af_codec_sw_gain_filter(saved_output_coef);

                pcm_out = (int32_t)(pcm_buf[i] * output_gain);
                pcm_buf[i] = __SSAT(pcm_out, 24);
                pcm_out = (int32_t)(pcm_buf[i+1] * output_gain);
                pcm_buf[i+1] = __SSAT(pcm_out, 24);
            }
        }
    }
}
#endif
#endif // AUDIO_OUTPUT_SW_GAIN

static uint32_t POSSIBLY_UNUSED af_codec_get_sample_count(uint32_t size, enum AUD_BITS_T bits, enum AUD_CHANNEL_NUM_T chans)
{
    uint32_t div;

    if (bits <= AUD_BITS_16) {
        div = 2;
    } else {
        div = 4;
    }
    div *= chans;
    return size / div;
}

static void POSSIBLY_UNUSED af_zero_mem(void *dst, unsigned int size)
{
    uint32_t *d = dst;
    uint32_t count = size / 4;

    while (count--) {
        *d++ = 0;
    }
}

static bool af_codec_playback_pre_handler(uint8_t *buf, uint32_t len, const struct af_stream_cfg_t *role)
{
    uint32_t POSSIBLY_UNUSED time;

    if (0) {

#ifdef AUDIO_OUTPUT_PA_ON_FADE_IN
    } else if (dac_pa_state == AF_DAC_PA_ON_TRIGGER &&
            (time = hal_sys_timer_get()) - dac_dc_start_time >= AF_CODEC_DC_STABLE_INTERVAL &&
            time - dac_pa_stop_time >= AF_CODEC_PA_RESTART_INTERVAL) {
        analog_aud_codec_speaker_enable(true);
        dac_pa_state = AF_DAC_PA_NULL;
#endif // AUDIO_OUTPUT_PA_ON_FADE_IN

#ifdef AUDIO_OUTPUT_PA_OFF_FADE_OUT
    } else if (dac_pa_state == AF_DAC_PA_OFF_TRIGGER) {
        dac_dc_start_time = hal_sys_timer_get();
        dac_pa_state = AF_DAC_PA_OFF_DC_START;
    } else if (dac_pa_state == AF_DAC_PA_OFF_DC_START) {
        time = hal_sys_timer_get();
        if (time - dac_dc_start_time >= AF_CODEC_DC_STABLE_INTERVAL) {
            dac_pa_state = AF_DAC_PA_NULL;
            analog_aud_codec_speaker_enable(false);
            dac_pa_stop_time = time;
#ifdef RTOS
            osSignalSet(fade_thread_id, (1 << AF_FADE_OUT_SIGNAL_ID));
#endif
        }
#endif // AUDIO_OUTPUT_PA_OFF_FADE_OUT

    }

#if defined(AUDIO_OUTPUT_PA_ON_FADE_IN) || defined(AUDIO_OUTPUT_PA_OFF_FADE_OUT)
    if (dac_pa_state == AF_DAC_PA_ON_TRIGGER || dac_pa_state == AF_DAC_PA_OFF_DC_START) {
        af_zero_mem(buf, len);
        return true;
    }
#endif

    return false;
}

#ifdef AUDIO_OUTPUT_DC_CALIB_SW
static void af_codec_playback_sw_dc_calib(uint8_t *buf, uint32_t len, enum AUD_BITS_T bits, enum AUD_CHANNEL_NUM_T chans)
{
    uint32_t cnt;

    if (bits <= AUD_BITS_16) {
        int16_t *ptr16 = (int16_t *)buf;
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
        int16_t dc_l = dac_dc[0];
        int16_t dc_r = dac_dc[1];
#endif
        if (chans == AUD_CHANNEL_NUM_1) {
            cnt = len / sizeof(int16_t);
            while (cnt-- > 0) {
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
                *ptr16 = __SSAT(*ptr16 + dc_l, 16);
#endif
                ptr16++;
            }
        } else {
            cnt = len / sizeof(int16_t) / 2;
            while (cnt-- > 0) {
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
                *ptr16 = __SSAT(*ptr16 + dc_l, 16);
                *(ptr16 + 1) = __SSAT(*(ptr16 + 1) + dc_r, 16);
#endif
                ptr16 += 2;
            }
        }
    } else {
        int32_t *ptr32 = (int32_t *)buf;
        int32_t dac_bits =
#ifdef CHIP_BEST1000
            CODEC_PLAYBACK_BIT_DEPTH;
#else
            24;
#endif
        int32_t val_shift;

        if (dac_bits < 24) {
            val_shift = 24 - dac_bits;
        } else {
            val_shift = 0;
        }

#ifdef AUDIO_OUTPUT_DC_CALIB_SW
        int32_t dc_l = dac_dc[0] << 8;
        int32_t dc_r = dac_dc[1] << 8;
#endif
        if (chans == AUD_CHANNEL_NUM_1) {
            cnt = len / sizeof(int32_t);
            while (cnt-- > 0) {
#ifdef CORRECT_SAMPLE_VALUE
                *ptr32 = ((*ptr32) << (32 - dac_bits)) >> (32 - dac_bits);
#endif
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
                *ptr32 = __SSAT((*ptr32 + dc_l) >> val_shift, dac_bits);
#elif defined(CORRECT_SAMPLE_VALUE)
                *ptr32 = __SSAT(*ptr32 >> val_shift, dac_bits);
#else
                *ptr32 = *ptr32 >> val_shift;
#endif
                ptr32++;
            }
        } else {
            cnt = len / sizeof(int32_t) / 2;
            while (cnt-- > 0) {
#ifdef CORRECT_SAMPLE_VALUE
                *ptr32 = ((*ptr32) << (32 - dac_bits)) >> (32 - dac_bits);
                *(ptr32 + 1) = ((*(ptr32 + 1)) << (32 - dac_bits)) >> (32 - dac_bits);
#endif
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
                *ptr32 = __SSAT((*ptr32 + dc_l) >> val_shift, dac_bits);
                *(ptr32 + 1) = __SSAT((*(ptr32 + 1) + dc_r) >> val_shift, dac_bits);
#elif defined(CORRECT_SAMPLE_VALUE)
                *ptr32 = __SSAT(*ptr32 >> val_shift, dac_bits);
                *(ptr32 + 1) = __SSAT(*(ptr32 + 1) >> val_shift, dac_bits);
#else
                *ptr32 = *ptr32 >> val_shift;
                *(ptr32 + 1) = *(ptr32 + 1) >> val_shift;
#endif
                ptr32 += 2;
            }
        }
    }
}
#endif // AUDIO_OUTPUT_DC_CALIB_SW

static void af_codec_playback_post_handler(uint8_t *buf, uint32_t len, const struct af_stream_cfg_t *role)
{
    POSSIBLY_UNUSED enum AUD_BITS_T bits;
    POSSIBLY_UNUSED enum AUD_CHANNEL_NUM_T chans;
    POSSIBLY_UNUSED uint32_t cnt;

    bits = role->cfg.bits;
    chans = role->cfg.channel_num;

#ifdef AUDIO_OUTPUT_SW_GAIN
#ifdef AUDIO_OUTPUT_SW_LIMITER
    if (bits <= AUD_BITS_16) {
        cnt = len / sizeof(int16_t);
    } else {
        cnt = len / sizeof(int32_t);
    }
    ApplyFloatLimiter(FloatLimiterP, buf, saved_output_coef, cnt);
#else
    af_codec_sw_gain_process(buf,len,bits,chans);
#endif
#endif

    if (codec_play_post_hdlr) {
        codec_play_post_hdlr(buf, len, (void *)&role->cfg);
    }

#if defined(AUDIO_OUTPUT_INVERT_RIGHT_CHANNEL)
    if (chans == AUD_CHANNEL_NUM_2) {
        if (bits == AUD_BITS_16) {
            int16_t *buf16 = (int16_t *)buf;
            for (int i = 1; i < len / sizeof(int16_t); i += 2) {
                int32_t tmp = -buf16[i];
                buf16[i] = __SSAT(tmp, 16);
            }
        } else {
            int32_t *buf32 = (int32_t *)buf;
            for (int i = 1; i < len / sizeof(int32_t); i += 2) {
                int32_t tmp = -buf32[i];
                buf32[i] = __SSAT(tmp, 24);
            }
        }
    }
#endif

#if (defined(CHIP_BEST1000) || defined(CHIP_BEST2000)) && defined(AUDIO_OUTPUT_CALIB_GAIN_MISSMATCH)
    // gain must less than 1.0
    float gain = 0.9441f;
    if (chans == AUD_CHANNEL_NUM_2) {
        if (bits == AUD_BITS_16) {
            int16_t *buf16 = (int16_t *)buf;
            for (int i = 1; i < len / sizeof(int16_t); i += 2) {
                int32_t tmp = (int)(buf16[i + 1] * gain);
                buf16[i + 1] = __SSAT(tmp, 16);
            }
        } else {
            int32_t *buf32 = (int32_t *)buf;
            for (int i = 1; i < len / sizeof(int32_t); i += 2) {
                int32_t tmp = (int)(buf32[i + 1] * gain);
                buf32[i + 1] = __SSAT(tmp, 24);
            }
        }
    }
#endif

#if defined(AUDIO_OUTPUT_DC_CALIB_SW)
    af_codec_playback_sw_dc_calib(buf, len, bits, chans);
#elif defined(CHIP_BEST1000)
    if (bits == AUD_BITS_24 || bits == AUD_BITS_32) {
        int32_t *ptr32 = (int32_t *)buf;
        int32_t val_shift;

        if (bits == AUD_BITS_24) {
            val_shift = 24 - CODEC_PLAYBACK_BIT_DEPTH;
        } else {
            val_shift = 32 - CODEC_PLAYBACK_BIT_DEPTH;
        }

        cnt = len / sizeof(int32_t);
        while (cnt-- > 0) {
            *ptr32 >>= val_shift;
            ptr32++;
        }
    }
#endif

#if defined(CHIP_BEST1000) && defined(AUDIO_OUTPUT_GAIN_M60DB_CHECK)
    hal_codec_dac_gain_m60db_check(HAL_CODEC_PERF_TEST_M60DB);
#endif
}

static inline void af_thread_stream_handler(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    uint32_t lock;
    struct af_stream_cfg_t *role;
    uint32_t dma_addr, hw_pos;
    uint8_t *buf;
    uint32_t len;
    uint32_t pp_cnt;
    bool codec_playback;
    bool skip_handler;
    enum AF_PP_T pp_index;

    role = af_get_stream_role(id, stream);

    af_lock_thread();

    if (role->handler && (role->ctl.status & AF_STATUS_STREAM_START_STOP)) {
        lock = int_lock();
        pp_cnt = role->ctl.pp_cnt;
        role->ctl.pp_cnt = 0;
        int_unlock(lock);

        af_sig_lost_cnt[id][stream] = pp_cnt - 1;
        if (af_sig_lost_cnt[id][stream]) {
            TRACE(3,"af_thread:WARNING: id=%d stream=%d lost %u signals", id, stream, af_sig_lost_cnt[id][stream]);
        }

        pp_index = PP_PINGPANG(role->ctl.pp_index);

        // Get PP index from accurate DMA pos
        dma_addr = af_stream_get_cur_dma_addr(id, stream);
        hw_pos = dma_addr - (uint32_t)role->dma_buf_ptr;
        if (hw_pos > role->dma_buf_size) {
            TRACE(3,"af_thread: Failed to get valid dma addr for id=%d stream=%d: 0x%08x", id, stream, dma_addr);
        }
#ifndef CHIP_BEST1000
        if (role->cfg.chan_sep_buf && role->cfg.channel_num > AUD_CHANNEL_NUM_1) {
            uint32_t chan_size;

            chan_size = role->dma_buf_size / role->cfg.channel_num;

            if (hw_pos <= role->dma_buf_size) {
                hw_pos = hw_pos % chan_size;
                if (hw_pos < chan_size / 2) {
                    pp_index = PP_PANG;
                } else if (hw_pos < chan_size) {
                    pp_index = PP_PING;
                }
            }
            if (pp_index == PP_PING) {
                buf = role->dma_buf_ptr;
            } else {
                buf = role->dma_buf_ptr + chan_size / 2;
            }
        } else
#endif
        {
            if (hw_pos < role->dma_buf_size / 2) {
                pp_index = PP_PANG;
            } else if (hw_pos < role->dma_buf_size) {
                pp_index = PP_PING;
            }
            if (pp_index == PP_PING) {
                buf = role->dma_buf_ptr;
            } else {
                buf = role->dma_buf_ptr + role->dma_buf_size / 2;
            }
        }

        if (stream == AUD_STREAM_PLAYBACK && role->ctl.use_device == AUD_STREAM_USE_INT_CODEC) {
            codec_playback = true;
        } else {
            codec_playback = false;
        }

        skip_handler = false;
        len = role->dma_buf_size / 2;

        if (codec_playback) {
            skip_handler = af_codec_playback_pre_handler(buf, len, role);
        }

        if (!skip_handler) {
#ifdef __RAND_FROM_MIC__
            if ((AUD_STREAM_CAPTURE == stream) && (AUD_STREAM_USE_INT_CODEC == role->cfg.device))
            {
                random_data_process(buf, role->dma_buf_size / 2, role->cfg.bits, role->cfg.channel_num);
            }
#endif
            role->handler(buf, len);
        }

        if (codec_playback) {
            af_codec_playback_post_handler(buf, len, role);
        }

#if defined(RTOS) && defined(AF_STREAM_ID_0_PLAYBACK_FADEOUT)
        af_stream_stop_process(role, buf, len);
#endif

        if (role->ctl.pp_cnt) {
            TRACE(3,"af_thread:WARNING: id=%d stream=%d hdlr ran too long (pp_cnt=%u)", id, stream, role->ctl.pp_cnt);
        }
    }

    af_unlock_thread();
}

#ifdef RTOS

static void af_thread(void const *argument)
{
    osEvent evt;
    uint32_t signals = 0;
    enum AUD_STREAM_ID_T id;
    enum AUD_STREAM_T stream;

    while(1)
    {
        //wait any signal
        evt = osSignalWait(0x0, osWaitForever);
        signals = evt.value.signals;
//        TRACE(3,"[%s] status = %x, signals = %d", __func__, evt.status, evt.value.signals);

        if(evt.status == osEventSignal)
        {
            for(uint8_t i=0; i<AUD_STREAM_ID_NUM * AUD_STREAM_NUM; i++)
            {
                if(signals & (1 << i))
                {
                    id = (enum AUD_STREAM_ID_T)(i >> 1);
                    stream = (enum AUD_STREAM_T)(i & 1);

                    af_thread_stream_handler(id, stream);

                }
            }
        }
        else
        {
            TRACE(2,"[%s] ERROR: evt.status = %d", __func__, evt.status);
            continue;
        }
    }
}

#else // !RTOS

#include "cmsis.h"
static volatile uint32_t af_flag_open;
static volatile uint32_t af_flag_signal;

static void af_set_flag(volatile uint32_t *flag, uint32_t set)
{
    uint32_t lock;

    lock = int_lock();
    *flag |= set;
    int_unlock(lock);
}

static void af_clear_flag(volatile uint32_t *flag, uint32_t clear)
{
    uint32_t lock;

    lock = int_lock();
    *flag &= ~clear;
    int_unlock(lock);
}

static bool af_flag_active(volatile uint32_t *flag, uint32_t bits)
{
    return !!(*flag & bits);
}

void af_thread(void const *argument)
{
    uint32_t lock;
    uint32_t i;
    enum AUD_STREAM_ID_T id;
    enum AUD_STREAM_T stream;

    if (af_flag_open == 0) {
        return;
    }

    for (i = 0; i < AUD_STREAM_ID_NUM * AUD_STREAM_NUM; i++) {
        if (!af_flag_active(&af_flag_signal, 1 << i)) {
            continue;
        }
        af_clear_flag(&af_flag_signal, 1 << i);

        id = (enum AUD_STREAM_ID_T)(i >> 1);
        stream = (enum AUD_STREAM_T)(i & 1);

        af_thread_stream_handler(id, stream);
    }

    lock = int_lock();
    if (af_flag_signal == 0) {
        hal_cpu_wake_unlock(AF_CPU_WAKE_USER);
    }
    int_unlock(lock);
}

#endif // !RTOS

static void af_dma_irq_handler(uint8_t ch, uint32_t remain_dst_tsize, uint32_t error, struct HAL_DMA_DESC_T *lli)
{
    struct af_stream_cfg_t *role = NULL;

    //initial parameter
    for(uint8_t id=0; id< AUD_STREAM_ID_NUM; id++)
    {
        for(uint8_t stream=0; stream < AUD_STREAM_NUM; stream++)
        {
            role = af_get_stream_role((enum AUD_STREAM_ID_T)id, (enum AUD_STREAM_T)stream);

            if(role->dma_cfg.ch == ch)
            {
                role->ctl.pp_index = PP_PINGPANG(role->ctl.pp_index);
                role->ctl.pp_cnt++;
                //TRACE(4,"[%s] id = %d, stream = %d, ch = %d", __func__, id, stream, ch);
                //TRACE(2,"[%s] PLAYBACK pp_cnt = %d", __func__, role->ctl.pp_cnt);
#ifdef RTOS
                if (irq_notif) {
                    irq_notif(id, stream);
                }
                osSignalSet(af_thread_tid, 0x01 << (id * 2 + stream));
#else
                af_set_flag(&af_flag_signal, 0x01 << (id * 2 + stream));
                hal_cpu_wake_lock(AF_CPU_WAKE_USER);
#endif
                return;
            }
        }
    }

    //invalid dma irq
    ASSERT(0, "[%s] ERROR: channel id = %d", __func__, ch);
}

#ifdef __CODEC_ASYNC_CLOSE__
static void af_codec_async_close(void)
{
    af_lock_thread();
    codec_int_close(CODEC_CLOSE_ASYNC_REAL);
    af_unlock_thread();
}
#endif

void *af_thread_tid_get(void)
{
#ifdef RTOS
    return (void *)af_thread_tid;
#else
    return NULL;
#endif
}

#ifdef CODEC_ANC_BOOST
static void af_codec_anc_boost_delay(uint32_t ms)
{
    af_unlock_thread();

    osDelay(ms);

    af_lock_thread();
}
#endif

uint32_t af_open(void)
{
    AF_TRACE_DEBUG();
    struct af_stream_cfg_t *role = NULL;

#ifdef RTOS
    if (audioflinger_mutex_id == NULL)
    {
        audioflinger_mutex_id = osMutexCreate((osMutex(audioflinger_mutex)));
    }
#endif

    af_lock_thread();

#ifdef AUDIO_OUTPUT_DC_CALIB
    if (!dac_dc_valid) {
        int16_t dc_l, dc_r;
        uint16_t max_dc;
        float attn;

        dac_dc_valid = true;
        analog_aud_get_dc_calib_value(&dc_l, &dc_r);
        if (ABS(dc_l) >= ABS(dc_r)) {
            max_dc = ABS(dc_l);
        } else {
            max_dc = ABS(dc_r);
        }
        ASSERT(max_dc + 1 < AF_CODEC_DC_MAX_SCALE, "Bad dc values: (%d, %d)", dc_l, dc_r);
        if (max_dc) {
            attn = 1 - (float)(max_dc + 1) / AF_CODEC_DC_MAX_SCALE;
        } else {
            attn = 1;
        }
        hal_codec_set_dac_dc_gain_attn(attn);
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
        dac_dc[0] = dc_l;
        dac_dc[1] = dc_r;
#else
        hal_codec_set_dac_dc_offset(dc_l, dc_r);
#endif
    }
#endif

#ifdef AUDIO_OUTPUT_SW_GAIN
    hal_codec_set_sw_output_coef_callback(af_codec_output_gain_changed);
#endif

#ifdef __CODEC_ASYNC_CLOSE__
    codec_int_set_close_handler(af_codec_async_close);
#endif

#ifdef CODEC_ANC_BOOST
    codec_set_anc_boost_delay_func(af_codec_anc_boost_delay);
#endif

    //initial parameters
    for(uint8_t id=0; id< AUD_STREAM_ID_NUM; id++)
    {
        for(uint8_t stream=0; stream < AUD_STREAM_NUM; stream++)
        {
            role = af_get_stream_role((enum AUD_STREAM_ID_T)id, (enum AUD_STREAM_T)stream);

            if(role->ctl.status == AF_STATUS_NULL)
            {
                role->dma_buf_ptr = NULL;
                role->dma_buf_size = 0;
                role->ctl.pp_index = PP_PING;
                role->ctl.status = AF_STATUS_OPEN_CLOSE;
                role->ctl.use_device = AUD_STREAM_USE_DEVICE_NULL;
                role->dma_cfg.ch = HAL_DMA_CHAN_NONE;
            }
            else
            {
                ASSERT(0, "[%s] ERROR: id = %d, stream = %d", __func__, id, stream);
            }
        }
    }

#ifdef RTOS
    af_thread_tid = osThreadCreate(osThread(af_thread), NULL);
    af_default_priority = af_get_priority();
    osSignalSet(af_thread_tid, 0x0);
#endif
#ifdef __RAND_FROM_MIC__
    randInit();
#endif
    af_unlock_thread();

    return AF_RES_SUCCESS;
}

static void af_stream_update_dma_buffer(enum AUD_STREAM_T stream, struct af_stream_cfg_t *role, const struct AF_STREAM_CONFIG_T *cfg)
{
    int i;
    enum HAL_DMA_RET_T dma_ret;
    struct HAL_DMA_DESC_T *dma_desc, *next_desc;
    struct HAL_DMA_CH_CFG_T *dma_cfg;
    int irq;
    uint32_t desc_xfer_size;
    uint32_t align;
    uint8_t samp_size;
    enum HAL_DMA_WDITH_T width;
#ifndef CHIP_BEST1000
    bool dma_2d_en;
    uint32_t chan_desc_xfer_size = 0;
#endif

    dma_desc = &role->dma_desc[0];
    dma_cfg = &role->dma_cfg;

#ifdef CODEC_DSD
    if (stream == AUD_STREAM_PLAYBACK && role->cfg.device == AUD_STREAM_USE_INT_CODEC) {
        if (dma_cfg->dst_periph == HAL_AUDMA_DSD_TX) {
            dma_cfg->dst_bsize = HAL_DMA_BSIZE_1;
        } else {
            dma_cfg->dst_bsize = HAL_DMA_BSIZE_4;
        }
    }
#endif


    if(role->cfg.device == AUD_STREAM_USE_BT_PCM)
     {
        dma_cfg->dst_bsize = HAL_DMA_BSIZE_1;
        dma_cfg->src_bsize = HAL_DMA_BSIZE_1;
        dma_cfg->try_burst = 0;
    }

    role->dma_buf_ptr = cfg->data_ptr;
    role->dma_buf_size = cfg->data_size;

    if (cfg->bits == AUD_BITS_24 || cfg->bits == AUD_BITS_32) {
        width = HAL_DMA_WIDTH_WORD;
        samp_size = 4;
    } else if (cfg->bits == AUD_BITS_16) {
        width = HAL_DMA_WIDTH_HALFWORD;
        samp_size = 2;
    } else {
        ASSERT(false, "%s: Invalid stream config bits=%d", __func__, cfg->bits);
        width = HAL_DMA_WIDTH_BYTE;
        samp_size = 1;
    }

#ifdef DYNAMIC_AUDIO_BUFFER_COUNT
    uint32_t desc_cnt;

    desc_cnt = (cfg->data_size / samp_size + HAL_DMA_MAX_DESC_XFER_SIZE - 1) / HAL_DMA_MAX_DESC_XFER_SIZE;
    if (desc_cnt < 2) {
        desc_cnt = 2;
    } else if (desc_cnt & (desc_cnt - 1)) {
        desc_cnt = 1 << (31 - __CLZ(desc_cnt) + 1);
    }
    TRACE(4,"%s: desc_cnt=%u data_size=%u samp_size=%u", __func__, desc_cnt, cfg->data_size, samp_size);
    ASSERT(MIN_AUDIO_BUFFER_COUNT <= desc_cnt && desc_cnt <= MAX_AUDIO_BUFFER_COUNT, "%s: Bad desc_cnt=%u", __func__, desc_cnt);
    role->dma_desc_cnt = desc_cnt;
#endif

    desc_xfer_size = cfg->data_size / AUDIO_BUFFER_COUNT;

#ifndef CHIP_BEST1000
    if (cfg->chan_sep_buf && cfg->channel_num > AUD_CHANNEL_NUM_1) {
        dma_2d_en = true;
    } else {
        dma_2d_en = false;
    }

    if (dma_2d_en) {
        enum HAL_DMA_BSIZE_T bsize;
        uint8_t burst_size;

        chan_desc_xfer_size = desc_xfer_size / cfg->channel_num;

        if (stream == AUD_STREAM_PLAYBACK) {
            bsize = dma_cfg->src_bsize;
        } else {
            bsize = dma_cfg->dst_bsize;
        }
        if (bsize == HAL_DMA_BSIZE_1) {
            burst_size = 1;
        } else if (bsize == HAL_DMA_BSIZE_4) {
            burst_size = 4;
        } else {
            burst_size = 8;
        }
        align = burst_size * samp_size * cfg->channel_num;
        // Ensure word-aligned too
        if (align & 0x1) {
            align *= 4;
        } else if (align & 0x2) {
            align *= 2;
        }
    } else
#endif
    {
        align = 4;
    }
    ASSERT(desc_xfer_size * AUDIO_BUFFER_COUNT == cfg->data_size && (desc_xfer_size % align) == 0,
        "%s: Dma data size is not aligned: data_size=%u AUDIO_BUFFER_COUNT=%u align=%u",
        __func__, cfg->data_size, AUDIO_BUFFER_COUNT, align);

    dma_cfg->dst_width = width;
    dma_cfg->src_width = width;
    dma_cfg->src_tsize = desc_xfer_size / samp_size;

    for (i = 0; i < AUDIO_BUFFER_COUNT; i++) {
        if (i == AUDIO_BUFFER_COUNT - 1) {
            next_desc = &dma_desc[0];
            irq = 1;
        } else {
            next_desc = &dma_desc[i + 1];
            if (i == AUDIO_BUFFER_COUNT / 2 - 1) {
                irq = 1;
            } else {
                irq = 0;
            }
        }

        if (stream == AUD_STREAM_PLAYBACK) {
#ifndef CHIP_BEST1000
            if (dma_2d_en) {
                dma_cfg->src = (uint32_t)(role->dma_buf_ptr + chan_desc_xfer_size * i);
            } else
#endif
            {
                dma_cfg->src = (uint32_t)(role->dma_buf_ptr + desc_xfer_size * i);
            }
        } else {
#ifndef CHIP_BEST1000
            if (dma_2d_en) {
                dma_cfg->dst = (uint32_t)(role->dma_buf_ptr + chan_desc_xfer_size * i);
            } else
#endif
            {
                dma_cfg->dst = (uint32_t)(role->dma_buf_ptr + desc_xfer_size * i);
            }
        }

        dma_ret = hal_audma_init_desc(&dma_desc[i], dma_cfg, next_desc, irq);
        ASSERT(dma_ret == HAL_DMA_OK, "[%s] Failed to init dma desc for stream %d: ret=%d", __func__, stream, dma_ret);
    }
}

// Support memory<-->peripheral
// Note: Do not support peripheral <--> peripheral
uint32_t af_stream_open(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream, const struct AF_STREAM_CONFIG_T *cfg)
{
    AF_TRACE_DEBUG();
    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;
    struct HAL_DMA_CH_CFG_T *dma_cfg = NULL;

#ifdef __RAND_FROM_MIC__
    random_status_sync();
#endif

    role = af_get_stream_role(id, stream);
    TRACE(3,"[%s] id = %d, stream = %d", __func__, id, stream);

    ASSERT(cfg->data_ptr != NULL, "[%s] ERROR: data_ptr == NULL!!!", __func__);
    ASSERT(((uint32_t)cfg->data_ptr) % 4 == 0, "[%s] ERROR: data_ptr(%p) is not align!!!", __func__, cfg->data_ptr);
    ASSERT(cfg->data_size != 0, "[%s] ERROR: data_size == 0!!!", __func__);
#ifndef CHIP_BEST1000
    if (cfg->chan_sep_buf && cfg->channel_num > AUD_CHANNEL_NUM_1) {
        ASSERT(cfg->device == AUD_STREAM_USE_INT_CODEC ||
               cfg->device == AUD_STREAM_USE_I2S0_MASTER || cfg->device == AUD_STREAM_USE_I2S0_SLAVE ||
               cfg->device == AUD_STREAM_USE_I2S1_MASTER || cfg->device == AUD_STREAM_USE_I2S1_SLAVE ||
               cfg->device == AUD_STREAM_USE_TDM0_MASTER || cfg->device == AUD_STREAM_USE_TDM0_SLAVE ||
               cfg->device == AUD_STREAM_USE_TDM1_MASTER || cfg->device == AUD_STREAM_USE_TDM1_SLAVE,
                "[%s] ERROR: Unsupport chan_sep_buf for device %d", __func__, cfg->device);
    }
#endif

    ret = AF_RES_FAILD;

    af_lock_thread();

    //check af is open
    if(role->ctl.status != AF_STATUS_OPEN_CLOSE)
    {
        TRACE(2,"[%s] ERROR: status = %d",__func__, role->ctl.status);
        goto _exit;
    }

    role->cfg = *cfg;

    device = cfg->device;
    role->ctl.use_device = device;

    dma_cfg = &role->dma_cfg;
    memset(dma_cfg, 0, sizeof(*dma_cfg));

    if(device == AUD_STREAM_USE_BT_PCM)
    {
        dma_cfg->dst_bsize = HAL_DMA_BSIZE_1;
        dma_cfg->src_bsize = HAL_DMA_BSIZE_1;
        dma_cfg->try_burst = 0;
    }
    else
    {
        dma_cfg->dst_bsize = HAL_DMA_BSIZE_4;

#ifndef CHIP_BEST1000
        // If channel num > 1, burst size should be set to 1 for:
        // 1) all playback streams with 2D DMA enabled; or
        // 2) internal codec capture streams with 2D DMA disabled.
        if (cfg->channel_num > AUD_CHANNEL_NUM_1 &&
                ((stream == AUD_STREAM_PLAYBACK && cfg->chan_sep_buf) ||
                 (device == AUD_STREAM_USE_INT_CODEC && stream == AUD_STREAM_CAPTURE && !cfg->chan_sep_buf))) {
            dma_cfg->src_bsize = HAL_DMA_BSIZE_1;
        } else
#endif
        {
            dma_cfg->src_bsize = HAL_DMA_BSIZE_4;
        }
        dma_cfg->try_burst = 1;
    }

    dma_cfg->handler = af_dma_irq_handler;

    if (stream == AUD_STREAM_PLAYBACK)
    {
        AF_TRACE_DEBUG();
        dma_cfg->src_periph = (enum HAL_DMA_PERIPH_T)0;
        dma_cfg->type = HAL_DMA_FLOW_M2P_DMA;

        //open device and stream
        if(0)
        {

        }
#ifdef AF_DEVICE_EXT_CODEC
        else if(device == AUD_STREAM_USE_EXT_CODEC)
        {
            AF_TRACE_DEBUG();
            tlv32aic32_open();
            tlv32aic32_stream_open(stream);

            dma_cfg->dst_periph = HAL_AUDMA_I2S0_TX;
        }
#endif
#ifdef AF_DEVICE_I2S
        else if(device == AUD_STREAM_USE_I2S0_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->dst_periph = HAL_AUDMA_I2S0_TX;
        }
        else if(device == AUD_STREAM_USE_I2S0_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->dst_periph = HAL_AUDMA_I2S0_TX;
        }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
        else if(device == AUD_STREAM_USE_I2S1_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->dst_periph = HAL_AUDMA_I2S1_TX;
        }
        else if(device == AUD_STREAM_USE_I2S1_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->dst_periph = HAL_AUDMA_I2S1_TX;
        }
#endif
#endif
#ifdef AF_DEVICE_TDM
        else if(device == AUD_STREAM_USE_TDM0_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->dst_periph = HAL_AUDMA_I2S0_TX;
        }
        else if(device == AUD_STREAM_USE_TDM0_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->dst_periph = HAL_AUDMA_I2S0_TX;
        }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
        else if(device == AUD_STREAM_USE_TDM1_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->dst_periph = HAL_AUDMA_I2S1_TX;
        }
        else if(device == AUD_STREAM_USE_TDM1_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->dst_periph = HAL_AUDMA_I2S1_TX;
        }
#endif
#endif
#ifdef AF_DEVICE_INT_CODEC
        else if(device == AUD_STREAM_USE_INT_CODEC)
        {
            AF_TRACE_DEBUG();
            codec_int_open();
            codec_int_stream_open(stream);

#ifdef AUDIO_OUTPUT_SW_GAIN
#ifdef AUDIO_OUTPUT_SW_LIMITER
            FLOATLIMITER_ERROR limiter_err;

            if (FloatLimiterP == NULL) {
                FloatLimiterP = CreateFloatLimiter(FL_ATTACK_DEFAULT_MS, FL_RELEASE_DEFAULT_MS, FL_THRESHOLD, MAX_CHANEL, MAX_SAMPLERATE, MAX_SAMPLEBIT);
            }

            limiter_err = SetFloatLimiterNChannels(FloatLimiterP, cfg->channel_num);
            if (limiter_err != FLOATLIMIT_OK) {
                goto _exit;
            }
            limiter_err = SetFloatLimiterSampleRate(FloatLimiterP, cfg->sample_rate);
            if (limiter_err != FLOATLIMIT_OK) {
                goto _exit;
            }
            limiter_err = SetFloatLimiterSampleBit(FloatLimiterP, cfg->bits);
            if (limiter_err != FLOATLIMIT_OK) {
                goto _exit;
            }
#else
            sw_gain_iir.history_x[0]=0.0f;
            sw_gain_iir.history_x[1]=0.0f;
            sw_gain_iir.history_y[0]=0.0f;
            sw_gain_iir.history_y[1]=0.0f;    
#endif
#endif 


#ifdef CODEC_DSD
            if (af_dsd_enabled) {
                dma_cfg->dst_periph = HAL_AUDMA_DSD_TX;
                dma_cfg->dst_bsize = HAL_DMA_BSIZE_1;
            } else
#endif
            {
                dma_cfg->dst_periph = HAL_AUDMA_CODEC_TX;
            }
        }
#endif
#ifdef AF_DEVICE_SPDIF
        else if(device == AUD_STREAM_USE_INT_SPDIF)
        {
            AF_TRACE_DEBUG();
            hal_spdif_open(AF_SPDIF_INST, stream);
            dma_cfg->dst_periph = HAL_AUDMA_SPDIF0_TX;
        }
#endif
#ifdef AF_DEVICE_BT_PCM
        else if(device == AUD_STREAM_USE_BT_PCM)
        {
            AF_TRACE_DEBUG();
            hal_btpcm_open(AF_BTPCM_INST, stream);
            dma_cfg->dst_periph = HAL_AUDMA_BTPCM_TX;
        }
#endif
#ifdef AUDIO_ANC_FB_MC
        else if(device == AUD_STREAM_USE_MC)
        {
            AF_TRACE_DEBUG();
            dma_cfg->dst_periph = HAL_AUDMA_MC_RX;
        }
#endif
        else
        {
            ASSERT(0, "[%s] ERROR: device %d is not defined!", __func__, device);
        }

        dma_cfg->ch = hal_audma_get_chan(dma_cfg->dst_periph, HAL_DMA_HIGH_PRIO);
    }
    else
    {
        AF_TRACE_DEBUG();
        dma_cfg->dst_periph = (enum HAL_DMA_PERIPH_T)0;
        dma_cfg->type = HAL_DMA_FLOW_P2M_DMA;

        //open device and stream
        if(0)
        {
        }
#ifdef AF_DEVICE_EXT_CODEC
        else if(device == AUD_STREAM_USE_EXT_CODEC)
        {
            AF_TRACE_DEBUG();
            tlv32aic32_open();
            tlv32aic32_stream_open(stream);

            dma_cfg->src_periph = HAL_AUDMA_I2S0_RX;
        }
#endif
#ifdef AF_DEVICE_I2S
        else if(device == AUD_STREAM_USE_I2S0_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->src_periph = HAL_AUDMA_I2S0_RX;
        }
        else if(device == AUD_STREAM_USE_I2S0_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->src_periph = HAL_AUDMA_I2S0_RX;
        }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
        else if(device == AUD_STREAM_USE_I2S1_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->src_periph = HAL_AUDMA_I2S1_RX;
        }
        else if(device == AUD_STREAM_USE_I2S1_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_i2s_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->src_periph = HAL_AUDMA_I2S1_RX;
        }
#endif
#endif
#ifdef AF_DEVICE_TDM
        else if(device == AUD_STREAM_USE_TDM0_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->src_periph = HAL_AUDMA_I2S0_RX;
            AF_TRACE_DEBUG();
        }
        else if(device == AUD_STREAM_USE_TDM0_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_0, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->src_periph = HAL_AUDMA_I2S0_RX;
        }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
        else if(device == AUD_STREAM_USE_TDM1_MASTER)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_MASTER);
            dma_cfg->src_periph = HAL_AUDMA_I2S1_RX;
        }
        else if(device == AUD_STREAM_USE_TDM1_SLAVE)
        {
            AF_TRACE_DEBUG();
            hal_tdm_open(HAL_I2S_ID_1, stream, HAL_I2S_MODE_SLAVE);
            dma_cfg->src_periph = HAL_AUDMA_I2S1_RX;
        }
#endif
#endif
#ifdef AF_DEVICE_INT_CODEC
        else if(device == AUD_STREAM_USE_INT_CODEC)
        {
            AF_TRACE_DEBUG();
            codec_int_open();
            codec_int_stream_open(stream);

            dma_cfg->src_periph = HAL_AUDMA_CODEC_RX;
        }
#endif
#ifdef AF_DEVICE_SPDIF
        else if(device == AUD_STREAM_USE_INT_SPDIF)
        {
            AF_TRACE_DEBUG();
            hal_spdif_open(AF_SPDIF_INST, stream);

            dma_cfg->src_periph = HAL_AUDMA_SPDIF0_RX;
        }
#endif
#ifdef AF_DEVICE_BT_PCM
        else if(device == AUD_STREAM_USE_BT_PCM)
        {
            AF_TRACE_DEBUG();
            hal_btpcm_open(AF_BTPCM_INST, stream);

            dma_cfg->src_periph = HAL_AUDMA_BTPCM_RX;
        }
#endif
#ifdef AF_DEVICE_DPD_RX
        else if(device == AUD_STREAM_USE_DPD_RX)
        {
            AF_TRACE_DEBUG();
            dma_cfg->src_periph = HAL_AUDMA_DPD_RX;
        }
#endif
        else
        {
            ASSERT(0, "[%s] ERROR: device %d is not defined!", __func__, device);
        }
        dma_cfg->ch = hal_audma_get_chan(dma_cfg->src_periph, HAL_DMA_HIGH_PRIO);
    }
    af_stream_update_dma_buffer(stream, role, cfg);
    role->handler = cfg->handler;
    
    af_set_status(id, stream, AF_STATUS_STREAM_OPEN_CLOSE);
#ifndef RTOS
    af_clear_flag(&af_flag_signal, 1 << (id * 2 + stream));
    af_set_flag(&af_flag_open, 1 << (id * 2 + stream));
#endif

    AF_TRACE_DEBUG();
    ret = AF_RES_SUCCESS;

_exit:
    af_unlock_thread();
    if (ret == AF_RES_SUCCESS) {
        af_stream_setup(id, stream, &role->cfg);
    }

    return ret;
}

//volume, path, sample rate, channel num ...
uint32_t af_stream_setup(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream, const struct AF_STREAM_CONFIG_T *cfg)
{
    AF_TRACE_DEBUG();

    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;

    role = af_get_stream_role(id, stream);
    TRACE(3,"[%s] id = %d, stream = %d", __func__, id, stream);

    ret = AF_RES_FAILD;

    af_lock_thread();

    //check stream is open
    if(!(role->ctl.status & AF_STATUS_STREAM_OPEN_CLOSE))
    {
        TRACE(2,"[%s] ERROR: status = %d", __func__, role->ctl.status);
        goto _exit;
    }

    device = role->ctl.use_device;

    if (&role->cfg != cfg) {
        bool update_dma = false;

        if (role->cfg.bits != cfg->bits) {
            TRACE(4,"[%s] Change bits from %d to %d for stream %d", __func__, role->cfg.bits, cfg->bits, stream);
            update_dma = true;
        }
        if (role->cfg.data_ptr != cfg->data_ptr) {
            TRACE(4,"[%s] Change data_ptr from %p to %p for stream %d", __func__, role->cfg.data_ptr, cfg->data_ptr, stream);
            update_dma = true;
        }
        if (role->cfg.data_size != cfg->data_size) {
            TRACE(4,"[%s] Change data_size from %d to %d for stream %d", __func__, role->cfg.data_size, cfg->data_size, stream);
            update_dma = true;
        }
        if (update_dma) {
            // To avoid FIFO corruption, streams must be stopped before changing sample size
            ASSERT((role->ctl.status & AF_STATUS_STREAM_START_STOP) == 0,
                "[%s] ERROR: Update dma while stream %d started", __func__, stream);

            af_stream_update_dma_buffer(stream, role, cfg);
        }

        if (role->cfg.sample_rate != cfg->sample_rate) {
            TRACE(4,"[%s] Change sample rate from %d to %d for stream %d", __func__, role->cfg.sample_rate, cfg->sample_rate, stream);

            // To avoid L/R sample misalignment, streams must be stopped before changing sample rate
            ASSERT((role->ctl.status & AF_STATUS_STREAM_START_STOP) == 0,
                "[%s] ERROR: Change sample rate from %d to %d while stream %d started", __func__, role->cfg.sample_rate, cfg->sample_rate, stream);
        }

        role->cfg = *cfg;
    }

    AF_TRACE_DEBUG();
    if(0)
    {
    }
#ifdef AF_DEVICE_EXT_CODEC
    else if(device == AUD_STREAM_USE_EXT_CODEC)
    {
        AF_TRACE_DEBUG();

        struct tlv32aic32_config_t tlv32aic32_cfg;

        memset(&tlv32aic32_cfg, 0, sizeof(tlv32aic32_cfg));
        tlv32aic32_cfg.bits = cfg->bits;
        tlv32aic32_cfg.channel_num = cfg->channel_num;
        tlv32aic32_cfg.channel_map = cfg->channel_map;
        tlv32aic32_cfg.sample_rate = cfg->sample_rate;
        tlv32aic32_cfg.use_dma = AF_TRUE;
        tlv32aic32_cfg.chan_sep_buf = cfg->chan_sep_buf;
        tlv32aic32_cfg.sync_start = cfg->sync_start;
        tlv32aic32_cfg.slot_cycles = cfg->slot_cycles;
        tlv32aic32_stream_setup(stream, &tlv32aic32_cfg);
    }
#endif
#ifdef AF_DEVICE_I2S
    else if(device == AUD_STREAM_USE_I2S0_MASTER || device == AUD_STREAM_USE_I2S0_SLAVE
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
            || device == AUD_STREAM_USE_I2S1_MASTER || device == AUD_STREAM_USE_I2S1_SLAVE
#endif
            )
    {
        AF_TRACE_DEBUG();

        struct HAL_I2S_CONFIG_T i2s_cfg;
        enum HAL_I2S_ID_T i2s_id;

        i2s_id = HAL_I2S_ID_0;
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
        if(device == AUD_STREAM_USE_I2S1_MASTER || device == AUD_STREAM_USE_I2S1_SLAVE) {
            i2s_id = HAL_I2S_ID_1;
        }
#endif

        memset(&i2s_cfg, 0, sizeof(i2s_cfg));
        i2s_cfg.use_dma = AF_TRUE;
        i2s_cfg.chan_sep_buf = cfg->chan_sep_buf;
        i2s_cfg.sync_start = cfg->sync_start;
        i2s_cfg.cycles = cfg->slot_cycles;
        i2s_cfg.bits = cfg->bits;
        i2s_cfg.channel_num = cfg->channel_num;
        i2s_cfg.channel_map = cfg->channel_map;
        i2s_cfg.sample_rate = cfg->sample_rate;
        hal_i2s_setup_stream(i2s_id, stream, &i2s_cfg);
    }
#endif
#ifdef AF_DEVICE_TDM
    else if(device == AUD_STREAM_USE_TDM0_MASTER || device == AUD_STREAM_USE_TDM0_SLAVE
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
            || device == AUD_STREAM_USE_TDM1_MASTER || device == AUD_STREAM_USE_TDM1_SLAVE
#endif
            )
    {
        AF_TRACE_DEBUG();

        struct HAL_TDM_CONFIG_T tdm_cfg;
        enum HAL_I2S_ID_T i2s_id;

        i2s_id = HAL_I2S_ID_0;
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
        if(device == AUD_STREAM_USE_TDM1_MASTER || device == AUD_STREAM_USE_TDM1_SLAVE) {
            i2s_id = HAL_I2S_ID_1;
        }
#endif

        memset(&tdm_cfg, 0, sizeof(tdm_cfg));
        tdm_cfg.mode = HAL_TDM_MODE_FS_ASSERTED_AT_FIRST;
        tdm_cfg.edge = HAL_TDM_FS_EDGE_POSEDGE;
        tdm_cfg.cycles = (cfg->channel_num * cfg->slot_cycles);
        tdm_cfg.fs_cycles = cfg->fs_cycles;
        tdm_cfg.slot_cycles = cfg->slot_cycles;
        tdm_cfg.data_offset = 0;
        tdm_cfg.sync_start = cfg->sync_start;
        TRACE(5,"%s: cycle = %d, fs_cycles = %d, slot_cycles = %d,wait = %d.",
            __func__, tdm_cfg.cycles, tdm_cfg.fs_cycles, tdm_cfg.slot_cycles,tdm_cfg.sync_start);
        hal_tdm_setup_stream(i2s_id, stream, cfg->sample_rate, &tdm_cfg);
    }

#endif
#ifdef AF_DEVICE_INT_CODEC
    else if(device == AUD_STREAM_USE_INT_CODEC)
    {
        AF_TRACE_DEBUG();
        struct HAL_CODEC_CONFIG_T codec_cfg;
        memset(&codec_cfg, 0, sizeof(codec_cfg));
        codec_cfg.bits = cfg->bits;
        codec_cfg.sample_rate = cfg->sample_rate;
        codec_cfg.channel_num = cfg->channel_num;
        codec_cfg.channel_map = cfg->channel_map;
        codec_cfg.use_dma = AF_TRUE;
        codec_cfg.vol = cfg->vol;
        codec_cfg.io_path = cfg->io_path;

        AF_TRACE_DEBUG();
        codec_int_stream_setup(stream, &codec_cfg);
    }
#endif
#ifdef AF_DEVICE_SPDIF
    else if(device == AUD_STREAM_USE_INT_SPDIF)
    {
        AF_TRACE_DEBUG();
        struct HAL_SPDIF_CONFIG_T spdif_cfg;

        memset(&spdif_cfg, 0, sizeof(spdif_cfg));
        spdif_cfg.use_dma = AF_TRUE;
        spdif_cfg.bits = cfg->bits;
        spdif_cfg.channel_num = cfg->channel_num;
        spdif_cfg.sample_rate = cfg->sample_rate;
        hal_spdif_setup_stream(AF_SPDIF_INST, stream, &spdif_cfg);
    }
#endif
#ifdef AF_DEVICE_BT_PCM
    else if(device == AUD_STREAM_USE_BT_PCM)
    {
        AF_TRACE_DEBUG();
        struct HAL_BTPCM_CONFIG_T btpcm_cfg;

        memset(&btpcm_cfg, 0, sizeof(btpcm_cfg));
        btpcm_cfg.use_dma = AF_TRUE;
        btpcm_cfg.bits = cfg->bits;
        btpcm_cfg.channel_num = cfg->channel_num;
        btpcm_cfg.sample_rate = cfg->sample_rate;
        hal_btpcm_setup_stream(AF_BTPCM_INST, stream, &btpcm_cfg);
    }
#endif
#ifdef AF_DEVICE_DPD_RX
    else if(device == AUD_STREAM_USE_DPD_RX)
    {
        AF_TRACE_DEBUG();
    }
#endif
#ifdef AUDIO_ANC_FB_MC
    else if(device == AUD_STREAM_USE_MC)
    {
        AF_TRACE_DEBUG();
        hal_codec_setup_mc(cfg->channel_num, cfg->bits);
    }
#endif
    else
    {
        ASSERT(0, "[%s] ERROR: device %d is not defined!", __func__, device);
    }

    AF_TRACE_DEBUG();

    ret = AF_RES_SUCCESS;

_exit:
    af_unlock_thread();

    return ret;
}

uint32_t af_stream_mute(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream, bool mute)
{
    AF_TRACE_DEBUG();

    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;

    ret = AF_RES_FAILD;
    role = af_get_stream_role(id, stream);

    af_lock_thread();

    if ((role->ctl.status & AF_STATUS_STREAM_OPEN_CLOSE) == 0) {
        TRACE(2,"[%s] ERROR: status = %d", __func__, role->ctl.status);
        goto _exit;
    }

    device = role->ctl.use_device;

    if (device == AUD_STREAM_USE_INT_CODEC) {
        codec_int_stream_mute(stream, mute);
        ret = AF_RES_SUCCESS;
    }

_exit:
    af_unlock_thread();

    return ret;
}

uint32_t af_stream_set_chan_vol(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream, enum AUD_CHANNEL_MAP_T ch_map, uint8_t vol)
{
    AF_TRACE_DEBUG();

    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;

    ret = AF_RES_FAILD;
    role = af_get_stream_role(id, stream);

    af_lock_thread();

    if ((role->ctl.status & AF_STATUS_STREAM_OPEN_CLOSE) == 0) {
        TRACE(2,"[%s] ERROR: status = %d", __func__, role->ctl.status);
        goto _exit;
    }

    device = role->ctl.use_device;

    if (device == AUD_STREAM_USE_INT_CODEC) {
        codec_int_stream_set_chan_vol(stream, ch_map, vol);
        ret = AF_RES_SUCCESS;
    }

_exit:
    af_unlock_thread();

    return ret;
}

uint32_t af_stream_restore_chan_vol(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    AF_TRACE_DEBUG();

    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;

    ret = AF_RES_FAILD;
    role = af_get_stream_role(id, stream);

    af_lock_thread();

    if ((role->ctl.status & AF_STATUS_STREAM_OPEN_CLOSE) == 0) {
        TRACE(2,"[%s] ERROR: status = %d", __func__, role->ctl.status);
        goto _exit;
    }

    device = role->ctl.use_device;

    if (device == AUD_STREAM_USE_INT_CODEC) {
        codec_int_stream_restore_chan_vol(stream);
        ret = AF_RES_SUCCESS;
    }

_exit:
    af_unlock_thread();

    return ret;
}

#ifdef CODEC_PLAY_BEFORE_CAPTURE
static struct af_stream_cfg_t *codec_capture_role;

static void af_codec_stream_pre_start(enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role = NULL;

    if (stream == AUD_STREAM_CAPTURE) {
        return;
    }

    for(uint8_t id=0; id< AUD_STREAM_ID_NUM; id++)
    {
        role = af_get_stream_role((enum AUD_STREAM_ID_T)id, AUD_STREAM_CAPTURE);
        if (role->cfg.device == AUD_STREAM_USE_INT_CODEC &&
                role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP)) {
            hal_audma_stop(role->dma_cfg.ch);
            codec_int_stream_stop(AUD_STREAM_CAPTURE);
            codec_capture_role = role;
            return;
        }
    }
}

static void af_codec_stream_post_start(enum AUD_STREAM_T stream)
{
    if (stream == AUD_STREAM_CAPTURE) {
        return;
    }

    if (codec_capture_role) {
        hal_audma_sg_start(&codec_capture_role->dma_desc[0], &codec_capture_role->dma_cfg);
        codec_int_stream_start(AUD_STREAM_CAPTURE);
        codec_capture_role = NULL;
    }
}

static void af_codec_stream_pre_stop(enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role = NULL;

    if (stream == AUD_STREAM_CAPTURE) {
        return;
    }

    for(uint8_t id=0; id< AUD_STREAM_ID_NUM; id++)
    {
        role = af_get_stream_role((enum AUD_STREAM_ID_T)id, AUD_STREAM_CAPTURE);
        if (role->cfg.device == AUD_STREAM_USE_INT_CODEC &&
                role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP)) {
            hal_audma_stop(role->dma_cfg.ch);
            codec_int_stream_stop(AUD_STREAM_CAPTURE);
            codec_capture_role = role;
            return;
        }
    }
}

static void af_codec_stream_post_stop(enum AUD_STREAM_T stream)
{
    if (stream == AUD_STREAM_CAPTURE) {
        return;
    }

    if (codec_capture_role) {
        hal_audma_sg_start(&codec_capture_role->dma_desc[0], &codec_capture_role->dma_cfg);
        codec_int_stream_start(AUD_STREAM_CAPTURE);
        codec_capture_role = NULL;
    }
}
#endif

uint32_t af_stream_start(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;
    enum HAL_DMA_RET_T dma_ret;

    role = af_get_stream_role(id, stream);
    TRACE(3,"[%s] id = %d, stream = %d", __func__, id, stream);

    af_lock_thread();

    //check stream is open and not start.
    if(role->ctl.status != (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE))
    {
        TRACE(2,"[%s] ERROR: status = %d",__func__, role->ctl.status);
        ret = AF_RES_FAILD;
        goto _exit;
    }

    device = role->ctl.use_device;

    role->ctl.pp_index = PP_PING;
    role->ctl.pp_cnt = 0;

#ifndef RTOS
    af_clear_flag(&af_flag_signal, 1 << (id * 2 + stream));
#endif

    if (device == AUD_STREAM_USE_INT_CODEC && stream == AUD_STREAM_PLAYBACK) {
#ifdef AUDIO_OUTPUT_PA_ON_FADE_IN
        dac_pa_state = AF_DAC_PA_ON_TRIGGER;
        // Has the buffer been zero-ed out?
        af_zero_mem(role->dma_buf_ptr, role->dma_buf_size);
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
        af_codec_playback_sw_dc_calib(role->dma_buf_ptr, role->dma_buf_size, role->cfg.bits, role->cfg.channel_num);
#endif
        dac_dc_start_time = hal_sys_timer_get();
#endif
    }

#ifndef CHIP_BEST1000
    if (role->cfg.chan_sep_buf && role->cfg.channel_num > AUD_CHANNEL_NUM_1) {
        struct HAL_DMA_2D_CFG_T *src, *dst;
        struct HAL_DMA_2D_CFG_T dma_2d_cfg;
        uint8_t burst_size = 1;
        uint32_t chan_xfer_cnt;

        src = NULL;
        dst = NULL;
        if (stream == AUD_STREAM_PLAYBACK) {
            if (role->cfg.channel_num == AUD_CHANNEL_NUM_2) {
                ASSERT(role->dma_cfg.src_bsize == HAL_DMA_BSIZE_1,
                    "Play 2D DMA: Bad src burst size: %d", role->dma_cfg.src_bsize);
                burst_size = 1;
                src = &dma_2d_cfg;
            }
        } else {
            if (role->cfg.channel_num > AUD_CHANNEL_NUM_1) {
                ASSERT(role->dma_cfg.src_bsize == role->dma_cfg.dst_bsize,
                    "Cap 2D DMA: src burst size (%d) != dst (%d)", role->dma_cfg.src_bsize, role->dma_cfg.dst_bsize);
                if (role->dma_cfg.dst_bsize == HAL_DMA_BSIZE_1) {
                    burst_size = 1;
                } else if (role->dma_cfg.dst_bsize == HAL_DMA_BSIZE_4) {
                    burst_size = 4;
                } else if (role->dma_cfg.dst_bsize == HAL_DMA_BSIZE_8) {
                    burst_size = 8;
                } else {
                    ASSERT(false, "Cap 2D DMA: Bad dst burst size: %d", role->dma_cfg.dst_bsize);
                }
                dst = &dma_2d_cfg;
            }
        }

        if (src || dst) {
            chan_xfer_cnt = role->dma_cfg.src_tsize * AUDIO_BUFFER_COUNT / role->cfg.channel_num;
            dma_2d_cfg.xcount = role->cfg.channel_num;
            dma_2d_cfg.xmodify = chan_xfer_cnt - burst_size;
            dma_2d_cfg.ycount = chan_xfer_cnt / burst_size;
            dma_2d_cfg.ymodify = -chan_xfer_cnt * (role->cfg.channel_num - 1);
        }

        dma_ret = hal_dma_sg_2d_start(&role->dma_desc[0], &role->dma_cfg, src, dst);
    } else
#endif
    {
        dma_ret = hal_dma_sg_start(&role->dma_desc[0], &role->dma_cfg);
    }
    ASSERT(dma_ret == HAL_DMA_OK, "[%s] Failed to start dma for stream %d: ret=%d", __func__, stream, dma_ret);

    AF_TRACE_DEBUG();
    if(0)
    {
    }
#ifdef AF_DEVICE_EXT_CODEC
    else if(device == AUD_STREAM_USE_EXT_CODEC)
    {
        AF_TRACE_DEBUG();
        tlv32aic32_stream_start(stream);
    }
#endif
#ifdef AF_DEVICE_I2S
    else if(device == AUD_STREAM_USE_I2S0_MASTER || device == AUD_STREAM_USE_I2S0_SLAVE)
    {
        hal_i2s_start_stream(HAL_I2S_ID_0, stream);
    }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
    else if(device == AUD_STREAM_USE_I2S1_MASTER || device == AUD_STREAM_USE_I2S1_SLAVE)
    {
        hal_i2s_start_stream(HAL_I2S_ID_1, stream);
    }
#endif
#endif
#ifdef AF_DEVICE_TDM
    else if(device == AUD_STREAM_USE_TDM0_MASTER || device == AUD_STREAM_USE_TDM0_SLAVE)
    {
        hal_tdm_start_stream(HAL_I2S_ID_0, stream);
    }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
    else if(device == AUD_STREAM_USE_TDM1_MASTER || device == AUD_STREAM_USE_TDM1_SLAVE)
    {
        hal_tdm_start_stream(HAL_I2S_ID_1, stream);
    }
#endif
#endif
#ifdef AF_DEVICE_INT_CODEC
    else if(device == AUD_STREAM_USE_INT_CODEC)
    {
        AF_TRACE_DEBUG();
#ifdef CODEC_PLAY_BEFORE_CAPTURE
        af_codec_stream_pre_start(stream);
#endif
        codec_int_stream_start(stream);
#ifdef CODEC_PLAY_BEFORE_CAPTURE
        af_codec_stream_post_start(stream);
#endif
    }
#endif
#ifdef AF_DEVICE_SPDIF
    else if(device == AUD_STREAM_USE_INT_SPDIF)
    {
        AF_TRACE_DEBUG();
        hal_spdif_start_stream(AF_SPDIF_INST, stream);
    }
#endif
#ifdef AF_DEVICE_BT_PCM
    else if(device == AUD_STREAM_USE_BT_PCM)
    {
        AF_TRACE_DEBUG();
        hal_btpcm_start_stream(AF_BTPCM_INST, stream);
    }
#endif
#ifdef AF_DEVICE_DPD_RX
    else if(device == AUD_STREAM_USE_DPD_RX)
    {
        AF_TRACE_DEBUG();
        hal_btpcm_start_stream(AF_BTPCM_INST, stream);
    }
#endif
#ifdef AUDIO_ANC_FB_MC
    else if(device == AUD_STREAM_USE_MC)
    {
        AF_TRACE_DEBUG();
    }
#endif
    else
    {
        ASSERT(0, "[%s] ERROR: device %d is not defined!", __func__, device);
    }

    AF_TRACE_DEBUG();
    af_set_status(id, stream, AF_STATUS_STREAM_START_STOP);

    ret = AF_RES_SUCCESS;

_exit:
    af_unlock_thread();

    return ret;
}

uint32_t af_stream_stop(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;

    role = af_get_stream_role(id, stream);
    TRACE(3,"[%s] id = %d, stream = %d", __func__, id, stream);

    af_lock_thread();

    device = role->ctl.use_device;

    if (device == AUD_STREAM_USE_INT_CODEC &&
            role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP | AF_STATUS_STREAM_PAUSE_RESTART))
    {
        af_clear_status(id, stream, AF_STATUS_STREAM_PAUSE_RESTART);
        goto _pause_stop;
    }

    //check stream is start and not stop
    if (role->ctl.status != (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP))
    {
        TRACE(2,"[%s] ERROR: status = %d",__func__, role->ctl.status);
        ret = AF_RES_FAILD;
        goto _exit;
    }

#if defined(RTOS) && defined(AF_STREAM_ID_0_PLAYBACK_FADEOUT)
    if (id == AUD_STREAM_ID_0 && stream == AUD_STREAM_PLAYBACK){
        af_stream_fadeout_start(512);
        af_stream_stop_wait_finish();
    }
#endif

    if (device == AUD_STREAM_USE_INT_CODEC && stream == AUD_STREAM_PLAYBACK) {
#ifdef AUDIO_OUTPUT_PA_OFF_FADE_OUT
        dac_pa_state = AF_DAC_PA_OFF_TRIGGER;
        af_unlock_thread();
        while (dac_pa_state != AF_DAC_PA_NULL) {
#ifdef RTOS
            osSignalWait((1 << AF_FADE_OUT_SIGNAL_ID), 300);
            osSignalClear(fade_thread_id, (1 << AF_FADE_OUT_SIGNAL_ID));
#else
            af_thread(NULL);
#endif
        }
        af_lock_thread();
#elif defined(AUDIO_OUTPUT_PA_ON_FADE_IN)
        dac_pa_state = AF_DAC_PA_NULL;
        analog_aud_codec_speaker_enable(false);
        dac_pa_stop_time = hal_sys_timer_get();
#endif // !AUDIO_OUTPUT_PA_OFF_FADE_OUT && AUDIO_OUTPUT_PA_ON_FADE_IN
    }

    hal_audma_stop(role->dma_cfg.ch);

#if defined(RTOS) && defined(AF_STREAM_ID_0_PLAYBACK_FADEOUT)
    if (id == AUD_STREAM_ID_0 && stream == AUD_STREAM_PLAYBACK){
        af_stream_fadeout_stop();
    }
#endif

    if(0)
    {
    }
#ifdef AF_DEVICE_EXT_CODEC
    else if(device == AUD_STREAM_USE_EXT_CODEC)
    {
        AF_TRACE_DEBUG();
        tlv32aic32_stream_stop(stream);
    }
#endif
#ifdef AF_DEVICE_I2S
    else if(device == AUD_STREAM_USE_I2S0_MASTER || device == AUD_STREAM_USE_I2S0_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_i2s_stop_stream(HAL_I2S_ID_0, stream);
    }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
    else if(device == AUD_STREAM_USE_I2S1_MASTER || device == AUD_STREAM_USE_I2S1_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_i2s_stop_stream(HAL_I2S_ID_1, stream);
    }
#endif
#endif
#ifdef AF_DEVICE_TDM
    else if(device == AUD_STREAM_USE_TDM0_MASTER || device == AUD_STREAM_USE_TDM0_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_tdm_stop_stream(HAL_I2S_ID_0, stream);
    }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
    else if(device == AUD_STREAM_USE_TDM1_MASTER || device == AUD_STREAM_USE_TDM1_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_tdm_stop_stream(HAL_I2S_ID_1, stream);
    }
#endif
#endif
#ifdef AF_DEVICE_INT_CODEC
    else if(device == AUD_STREAM_USE_INT_CODEC)
    {
        AF_TRACE_DEBUG();
#ifdef CODEC_PLAY_BEFORE_CAPTURE
        af_codec_stream_pre_stop(stream);
#endif
        codec_int_stream_stop(stream);
#ifdef CODEC_PLAY_BEFORE_CAPTURE
        af_codec_stream_post_stop(stream);
#endif
    }
#endif
#ifdef AF_DEVICE_SPDIF
    else if(device == AUD_STREAM_USE_INT_SPDIF)
    {
        AF_TRACE_DEBUG();
        hal_spdif_stop_stream(AF_SPDIF_INST, stream);
    }
#endif
#ifdef AF_DEVICE_BT_PCM
    else if(device == AUD_STREAM_USE_BT_PCM)
    {
        AF_TRACE_DEBUG();
        hal_btpcm_stop_stream(AF_BTPCM_INST, stream);
    }
#endif
#ifdef AF_DEVICE_DPD_RX
    else if(device == AUD_STREAM_USE_DPD_RX)
    {
        AF_TRACE_DEBUG();
        hal_btpcm_stop_stream(AF_BTPCM_INST, stream);
    }
#endif
#ifdef AUDIO_ANC_FB_MC
    else if(device == AUD_STREAM_USE_MC)
    {
        AF_TRACE_DEBUG();
    }
#endif
    else
    {
        ASSERT(0, "[%s] ERROR: device %d is not defined!", __func__, device);
    }

_pause_stop:
    AF_TRACE_DEBUG();
    af_clear_status(id, stream, AF_STATUS_STREAM_START_STOP);

#ifndef RTOS
    af_clear_flag(&af_flag_signal, 1 << (id * 2 + stream));
#endif

    ret = AF_RES_SUCCESS;

_exit:
    af_unlock_thread();

    return ret;
}

uint32_t af_stream_pause(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
#ifdef CHIP_BEST1000

    struct af_stream_cfg_t *role = NULL;
    enum AF_RESULT_T ret;

    role = af_get_stream_role(id, stream);
    TRACE(3,"[%s] id = %d, stream = %d", __func__, id, stream);

    af_lock_thread();

    if (role->cfg.device != AUD_STREAM_USE_INT_CODEC)
    {
        ret = AF_RES_FAILD;
        goto _exit;
    }
    if (role->ctl.status != (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP))
    {
        TRACE(2,"[%s] ERROR: status = %d",__func__, role->ctl.status);
        ret = AF_RES_FAILD;
        goto _exit;
    }

#if defined(AUDIO_OUTPUT_PA_ON_FADE_IN) || defined(AUDIO_OUTPUT_PA_OFF_FADE_OUT)
    if (AUD_STREAM_PLAYBACK == stream)
    {
        dac_pa_state = AF_DAC_PA_NULL;
        analog_aud_codec_speaker_enable(false);
        dac_pa_stop_time = hal_sys_timer_get();
    }
#endif

    hal_audma_stop(role->dma_cfg.ch);

#ifndef FPGA
    codec_int_stream_stop(stream);
#endif

    af_set_status(id, stream, AF_STATUS_STREAM_PAUSE_RESTART);

    ret = AF_RES_SUCCESS;

_exit:
    af_unlock_thread();

    return ret;

#else

    return af_stream_stop(id, stream);

#endif
}

uint32_t af_stream_restart(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
#ifdef CHIP_BEST1000

    struct af_stream_cfg_t *role = NULL;
    enum AF_RESULT_T ret;

    role = af_get_stream_role(id, stream);
    TRACE(3,"[%s] id = %d, stream = %d", __func__, id, stream);

    af_lock_thread();

    if (role->cfg.device != AUD_STREAM_USE_INT_CODEC)
    {
        ret = AF_RES_FAILD;
        goto _exit;
    }
    if (role->ctl.status != (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP | AF_STATUS_STREAM_PAUSE_RESTART))
    {
        TRACE(2,"[%s] ERROR: status = %d",__func__, role->ctl.status);
        ret = AF_RES_FAILD;
        goto _exit;
    }

    role->ctl.pp_index = PP_PING;
    role->ctl.pp_cnt = 0;

#ifdef AUDIO_OUTPUT_PA_ON_FADE_IN
    if (AUD_STREAM_PLAYBACK == stream)
    {
        dac_pa_state = AF_DAC_PA_ON_TRIGGER;
        af_zero_mem(role->dma_buf_ptr, role->dma_buf_size);
#ifdef AUDIO_OUTPUT_DC_CALIB_SW
        af_codec_playback_sw_dc_calib(role->dma_buf_ptr, role->dma_buf_size, role->cfg.bits, role->cfg.channel_num);
#endif
        dac_dc_start_time = hal_sys_timer_get();
    }
#endif

    hal_audma_sg_start(&role->dma_desc[0], &role->dma_cfg);

#ifndef FPGA
    codec_int_stream_start(stream);
#endif

    af_clear_status(id, stream, AF_STATUS_STREAM_PAUSE_RESTART);

    ret = AF_RES_SUCCESS;

_exit:
    af_unlock_thread();

    return ret;

#else

    return af_stream_start(id, stream);

#endif
}

uint32_t af_stream_close(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role;
    enum AF_RESULT_T ret;
    enum AUD_STREAM_USE_DEVICE_T device;

    role = af_get_stream_role(id, stream);
    TRACE(3,"[%s] id = %d, stream = %d", __func__, id, stream);

    af_lock_thread();

    //check stream is stop and not close.
    if(role->ctl.status != (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE))
    {
        TRACE(2,"[%s] ERROR: status = %d",__func__, role->ctl.status);
        ret = AF_RES_FAILD;
        goto _exit;
    }

    device = role->ctl.use_device;

    memset(role->dma_buf_ptr, 0, role->dma_buf_size);
    hal_audma_free_chan(role->dma_cfg.ch);

    //  TODO: more parameter should be set!!!
//    memset(role, 0xff, sizeof(struct af_stream_cfg_t));
    role->handler = NULL;
    role->ctl.pp_index = PP_PING;
    role->ctl.use_device = AUD_STREAM_USE_DEVICE_NULL;
    role->dma_buf_ptr = NULL;
    role->dma_buf_size = 0;

    role->dma_cfg.ch = HAL_DMA_CHAN_NONE;

    if(0)
    {
    }
#ifdef AF_DEVICE_EXT_CODEC
    else if(device == AUD_STREAM_USE_EXT_CODEC)
    {
        AF_TRACE_DEBUG();
        tlv32aic32_stream_close(stream);
    }
#endif
#ifdef AF_DEVICE_I2S
    else if(device == AUD_STREAM_USE_I2S0_MASTER || device == AUD_STREAM_USE_I2S0_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_i2s_close(HAL_I2S_ID_0, stream);
    }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
    else if(device == AUD_STREAM_USE_I2S1_MASTER || device == AUD_STREAM_USE_I2S1_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_i2s_close(HAL_I2S_ID_1, stream);
    }
#endif
#endif
#ifdef AF_DEVICE_TDM
    else if(device == AUD_STREAM_USE_TDM0_MASTER || device == AUD_STREAM_USE_TDM0_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_tdm_close(HAL_I2S_ID_0, stream);
    }
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
    else if(device == AUD_STREAM_USE_TDM1_MASTER || device == AUD_STREAM_USE_TDM1_SLAVE)
    {
        AF_TRACE_DEBUG();
        hal_tdm_close(HAL_I2S_ID_1, stream);
    }
#endif
#endif
#ifdef AF_DEVICE_INT_CODEC
    else if(device == AUD_STREAM_USE_INT_CODEC)
    {
        AF_TRACE_DEBUG();
        codec_int_stream_close(stream);
        codec_int_close(CODEC_CLOSE_NORMAL);
    }
#endif
#ifdef AF_DEVICE_SPDIF
    else if(device == AUD_STREAM_USE_INT_SPDIF)
    {
        AF_TRACE_DEBUG();
        hal_spdif_close(AF_SPDIF_INST, stream);
    }
#endif
#ifdef AF_DEVICE_BT_PCM
    else if(device == AUD_STREAM_USE_BT_PCM)
    {
        AF_TRACE_DEBUG();
        hal_btpcm_close(AF_BTPCM_INST, stream);
    }
#endif
#ifdef AF_DEVICE_DPD_RX
    else if(device == AUD_STREAM_USE_DPD_RX)
    {
        AF_TRACE_DEBUG();
    }
#endif
#ifdef AUDIO_ANC_FB_MC
    else if(device == AUD_STREAM_USE_MC)
    {
        AF_TRACE_DEBUG();
    }
#endif
    else
    {
        ASSERT(0, "[%s] ERROR: device %d is not defined!", __func__, device);
    }

    AF_TRACE_DEBUG();
    af_clear_status(id, stream, AF_STATUS_STREAM_OPEN_CLOSE);

#ifndef RTOS
    af_clear_flag(&af_flag_open, 1 << (id * 2 + stream));
    if (af_flag_open == 0) {
        hal_cpu_wake_unlock(AF_CPU_WAKE_USER);
    }
#endif

    ret = AF_RES_SUCCESS;

_exit:
    af_unlock_thread();

    return ret;
}

uint32_t af_close(void)
{
    struct af_stream_cfg_t *role;

    // Avoid blocking shutdown process
    //af_lock_thread();

    for (uint8_t id=0; id < AUD_STREAM_ID_NUM; id++)
    {
        for (uint8_t stream=0; stream < AUD_STREAM_NUM; stream++)
        {
            role = af_get_stream_role((enum AUD_STREAM_ID_T)id, (enum AUD_STREAM_T)stream);
            role->ctl.status = AF_STATUS_NULL;
        }
    }

    codec_int_close(CODEC_CLOSE_FORCED);

    //af_unlock_thread();

    return AF_RES_SUCCESS;
}

uint32_t af_stream_get_cur_dma_addr(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role;
    int i;
    uint32_t addr = 0;

    role = af_get_stream_role(id, stream);

    //check stream is start and not stop
    if (role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP)) {
        if (role->dma_cfg.ch != HAL_DMA_CHAN_NONE) {
            for (i = 0; i < 2; i++) {
                if (stream == AUD_STREAM_PLAYBACK) {
                    addr = hal_audma_get_cur_src_addr(role->dma_cfg.ch);
                } else {
                    addr = hal_audma_get_cur_dst_addr(role->dma_cfg.ch);
                }
                if (addr) {
                    break;
                }
                // Previous link list item was just finished. Read current DMA address again.
            }
        }
    }

    return addr;
}

int af_stream_get_cur_dma_pos(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role;
    uint32_t addr;
    int pos;

    role = af_get_stream_role(id, stream);

    addr = af_stream_get_cur_dma_addr(id, stream);

    pos = addr - (uint32_t)role->dma_buf_ptr;

    if (pos < 0 || pos > role->dma_buf_size) {
        return -1;
    }

#ifndef CHIP_BEST1000
    if (role->cfg.chan_sep_buf && role->cfg.channel_num > AUD_CHANNEL_NUM_1) {
        uint32_t chan_size;
        uint8_t chan_idx;
        uint8_t desc_idx;
        uint16_t chan_desc_offset;
        uint16_t chan_desc_xfer_size;
        uint32_t chan_offset;

        chan_size = role->dma_buf_size / role->cfg.channel_num;
        chan_desc_xfer_size = chan_size / AUDIO_BUFFER_COUNT;

        chan_idx = pos / chan_size;
        chan_offset = pos % chan_size;
        desc_idx = chan_offset / chan_desc_xfer_size;
        chan_desc_offset = chan_offset % chan_desc_xfer_size;

        pos = desc_idx * (role->dma_buf_size / AUDIO_BUFFER_COUNT) + chan_idx * chan_desc_xfer_size + chan_desc_offset;
    }
#endif

    return pos;
}

int af_stream_buffer_error(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    return af_sig_lost_cnt[id][stream];
}

uint32_t af_stream_dma_tc_irq_enable(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role;

    role = af_get_stream_role(id, stream);

    // Check if opened
    if ((role->ctl.status & (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE)) ==
            (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE)) {
        hal_dma_tc_irq_enable(role->dma_cfg.ch);
        return AF_RES_SUCCESS;
    }

    return AF_RES_FAILD;
}

uint32_t af_stream_dma_tc_irq_disable(enum AUD_STREAM_ID_T id, enum AUD_STREAM_T stream)
{
    struct af_stream_cfg_t *role;

    role = af_get_stream_role(id, stream);

    // Check if opened
    if ((role->ctl.status & (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE)) ==
            (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE)) {
        hal_dma_tc_irq_disable(role->dma_cfg.ch);
        return AF_RES_SUCCESS;
    }

    return AF_RES_FAILD;
}

#ifdef RTOS
void af_set_irq_notification(AF_IRQ_NOTIFICATION_T notif)
{
    irq_notif = notif;
}

static int af_priority[AF_USER_NUM];

void af_set_priority(AF_USER_E user, int priority)
{
    uint8_t i = 0;
    int max_priority = 0;
    af_priority[user] = priority;

    for (i=0; i<AF_USER_NUM; i++)
    {
        if (max_priority < af_priority[i])
        {
            max_priority = af_priority[i];
        }
    }

    if (max_priority != af_get_priority())
    {
        osThreadSetPriority(af_thread_tid, max_priority);
    }
}

int af_get_priority(void)
{
    return (int)osThreadGetPriority(af_thread_tid);
}

int af_get_default_priority(void)
{
    return af_default_priority;
}

void af_reset_priority(void)
{
    osThreadSetPriority(af_thread_tid, af_default_priority);
}
#endif

void af_codec_tune_resample_rate(enum AUD_STREAM_T stream, float ratio)
{
    af_lock_thread();

    af_codec_direct_tune_resample_rate(stream, ratio);

    af_unlock_thread();
}

void af_codec_direct_tune_resample_rate(enum AUD_STREAM_T stream, float ratio)
{
    ASSERT(stream < AUD_STREAM_NUM, "[%s] Bad stream=%d", __func__, stream);

    hal_codec_tune_resample_rate(stream, ratio);
}

void af_codec_tune_both_resample_rate(float ratio)
{
    af_lock_thread();

    hal_codec_tune_both_resample_rate(ratio);

    af_unlock_thread();
}

void af_codec_tune_pll(float ratio)
{
    af_lock_thread();

    analog_aud_pll_tune(ratio);

    af_unlock_thread();
}

void af_codec_tune_xtal(float ratio)
{
    af_lock_thread();

    analog_aud_xtal_tune(ratio);

    af_unlock_thread();
}

void af_codec_tune(enum AUD_STREAM_T stream, float ratio)
{
    af_lock_thread();

    af_codec_direct_tune(stream, ratio);

    af_unlock_thread();
}

void af_codec_direct_tune(enum AUD_STREAM_T stream, float ratio)
{
    ASSERT(stream <= AUD_STREAM_NUM, "[%s] Bad stream=%d", __func__, stream);

#ifdef __AUDIO_RESAMPLE__
    if (hal_cmu_get_audio_resample_status()) {
        if (stream < AUD_STREAM_NUM) {
            hal_codec_tune_resample_rate(stream, ratio);
        } else {
            hal_codec_tune_both_resample_rate(ratio);
        }
    } else
#endif
    {
        analog_aud_pll_tune(ratio);
    }
}

void af_codec_set_perf_test_power(int type)
{
    af_lock_thread();

    hal_codec_dac_gain_m60db_check((enum HAL_CODEC_PERF_TEST_POWER_T)type);

    af_unlock_thread();
}

void af_codec_set_noise_reduction(bool enable)
{
    af_lock_thread();

    hal_codec_set_noise_reduction(enable);

    af_unlock_thread();
}

void af_codec_bt_trigger_config(bool en, AF_CODEC_BT_TRIGGER_CALLBACK callback)
{
    if (en)
    {
        hal_codec_set_bt_trigger_callback(callback);
        hal_codec_bt_trigger_start();
    }
    else
    {
        hal_codec_bt_trigger_stop();
    }
}

void af_codec_sync_config(enum AUD_STREAM_T stream, enum AF_CODEC_SYNC_TYPE_T type, bool enable)
{
#ifndef CHIP_BEST1000
    af_lock_thread();

    if (stream == AUD_STREAM_PLAYBACK) {
        if (enable) {
            hal_codec_sync_dac_enable((enum HAL_CODEC_SYNC_TYPE_T)type);
        } else {
            hal_codec_sync_dac_disable();
        }
    } else {
        if (enable) {
            hal_codec_sync_adc_enable((enum HAL_CODEC_SYNC_TYPE_T)type);
        } else {
            hal_codec_sync_adc_disable();
        }
    }

    af_unlock_thread();
#endif
}

void af_i2s_sync_config(enum AUD_STREAM_T stream, enum AF_I2S_SYNC_TYPE_T type, bool enable)
{
#ifdef CHIP_BEST2300A
    af_lock_thread();

    if (stream == AUD_STREAM_PLAYBACK) {
        if (enable) {
            hal_i2s_clk_sync_enable((enum HAL_I2S_SYNC_TYPE_T)type);
        } else {
            hal_i2s_clk_sync_disable();
        }
    } else {
        if (enable) {
            hal_i2s_clk_sync_enable((enum HAL_I2S_SYNC_TYPE_T)type);
        } else {
            hal_i2s_clk_sync_disable();
        }
    }

    af_unlock_thread();
#endif
}

void af_codec_sync_resample_rate_config(enum AUD_STREAM_T stream, enum AF_CODEC_SYNC_TYPE_T type, bool enable)
{
#ifndef CHIP_BEST1000
    af_lock_thread();

    if (stream == AUD_STREAM_PLAYBACK) {
        if (enable) {
            hal_codec_sync_dac_resample_rate_enable((enum HAL_CODEC_SYNC_TYPE_T)type);
        } else {
            hal_codec_sync_dac_resample_rate_disable();
        }
    } else {
        if (enable) {
            hal_codec_sync_adc_resample_rate_enable((enum HAL_CODEC_SYNC_TYPE_T)type);
        } else {
            hal_codec_sync_adc_resample_rate_disable();
        }
    }

    af_unlock_thread();
#endif
}

void af_codec_sync_gain_config(enum AUD_STREAM_T stream, enum AF_CODEC_SYNC_TYPE_T type, bool enable)
{
#ifndef CHIP_BEST1000
    af_lock_thread();

    if (stream == AUD_STREAM_PLAYBACK) {
        if (enable) {
            hal_codec_sync_dac_gain_enable((enum HAL_CODEC_SYNC_TYPE_T)type);
        } else {
            hal_codec_sync_dac_gain_disable();
        }
    } else {
        if (enable) {
            hal_codec_sync_adc_gain_enable((enum HAL_CODEC_SYNC_TYPE_T)type);
        } else {
            hal_codec_sync_adc_gain_disable();
        }
    }

    af_unlock_thread();
#endif
}

void af_codec_swap_output(bool swap)
{
    af_lock_thread();

    hal_codec_swap_output(swap);

    af_unlock_thread();
}

void af_codec_set_playback_post_handler(AF_CODEC_PLAYBACK_POST_HANDLER_T hdlr)
{
    codec_play_post_hdlr = hdlr;
}

int af_anc_open(enum ANC_TYPE_T type, enum AUD_SAMPRATE_T play_rate, enum AUD_SAMPRATE_T capture_rate, AF_ANC_HANDLER handler)
{
    FUNC_ENTRY_TRACE();

    af_lock_thread();

    codec_anc_open(type, play_rate, capture_rate, handler);

    af_unlock_thread();

    return AF_RES_SUCCESS;
}

int af_anc_close(enum ANC_TYPE_T type)
{
    FUNC_ENTRY_TRACE();

    af_lock_thread();

    codec_anc_close(type);

    af_unlock_thread();

    return AF_RES_SUCCESS;
}

#ifdef VOICE_DETECTOR_EN
int af_vad_open(const struct AUD_VAD_CONFIG_T *cfg)
{
    FUNC_ENTRY_TRACE();

    af_lock_thread();

    codec_vad_open(cfg);

    af_unlock_thread();

    return AF_RES_SUCCESS;
}

int af_vad_close(void)
{
    FUNC_ENTRY_TRACE();

    af_lock_thread();

    codec_vad_close();

    af_unlock_thread();

    return AF_RES_SUCCESS;
}

int af_vad_start(void)
{
    FUNC_ENTRY_TRACE();

    af_lock_thread();

    hal_codec_vad_start();

    af_unlock_thread();

    return AF_RES_SUCCESS;
}

int af_vad_stop(void)
{
    FUNC_ENTRY_TRACE();

    af_lock_thread();

    hal_codec_vad_stop();

    af_unlock_thread();

    return AF_RES_SUCCESS;
}

uint32_t af_vad_get_data(uint8_t *buf, uint32_t len)
{
    return hal_codec_vad_recv_data(buf, len);
}

void af_vad_get_data_info(struct CODEC_VAD_BUF_INFO_T * vad_buf_info)
{
    hal_codec_get_vad_data_info(vad_buf_info);
}

#endif

#ifdef CODEC_DSD
void af_dsd_enable(void)
{
    enum AUD_STREAM_ID_T id;
    struct af_stream_cfg_t *role;
    bool opened = false;

    af_lock_thread();

    for (id = AUD_STREAM_ID_0; id < AUD_STREAM_ID_NUM; id++) {
        role = af_get_stream_role(id, AUD_STREAM_PLAYBACK);
        if (role->ctl.status & AF_STATUS_STREAM_OPEN_CLOSE) {
            if (role->cfg.device == AUD_STREAM_USE_INT_CODEC && role->dma_cfg.dst_periph == HAL_AUDMA_CODEC_TX) {
                ASSERT(role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE),
                    "Bad stream status when DSD enabled: 0x%X", role->ctl.status);
                role->dma_cfg.dst_periph = HAL_AUDMA_DSD_TX;
                af_stream_update_dma_buffer(AUD_STREAM_PLAYBACK, role, &role->cfg);
                opened = true;
                break;
            }
        }
    }

    hal_codec_dsd_enable();
    af_dsd_enabled = true;

    af_unlock_thread();

    if (opened) {
        // Enable DSD sample rate handling
        af_stream_setup(id, AUD_STREAM_PLAYBACK, &role->cfg);
    }
}

void af_dsd_disable(void)
{
    enum AUD_STREAM_ID_T id;
    struct af_stream_cfg_t *role;
    bool opened = false;

    af_lock_thread();

    for (id = AUD_STREAM_ID_0; id < AUD_STREAM_ID_NUM; id++) {
        role = af_get_stream_role(id, AUD_STREAM_PLAYBACK);
        if (role->ctl.status & AF_STATUS_STREAM_OPEN_CLOSE) {
            if (role->cfg.device == AUD_STREAM_USE_INT_CODEC && role->dma_cfg.dst_periph == HAL_AUDMA_DSD_TX) {
                ASSERT(role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE),
                    "Bad stream status when DSD disabled: 0x%X", role->ctl.status);
                role->dma_cfg.dst_periph = HAL_AUDMA_CODEC_TX;
                af_stream_update_dma_buffer(AUD_STREAM_PLAYBACK, role, &role->cfg);
                opened = true;
                break;
            }
        }
    }

    hal_codec_dsd_disable();
    af_dsd_enabled = false;

    af_unlock_thread();

    if (opened) {
        // Disable DSD sample rate handling
        af_stream_setup(id, AUD_STREAM_PLAYBACK, &role->cfg);
    }
}
#endif
#ifdef __RAND_FROM_MIC__
    uint8_t random_mic_is_on(uint8_t *deviceId)
    {
        struct af_stream_cfg_t *role = NULL;

        *deviceId = 0;

        for(uint8_t id=0; id< AUD_STREAM_ID_NUM; id++)
        {
            role = af_get_stream_role((enum AUD_STREAM_ID_T)id, AUD_STREAM_CAPTURE);
            if (role->cfg.device == AUD_STREAM_USE_INT_CODEC &&
                    role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE | AF_STATUS_STREAM_START_STOP)) {
                return RAND_STATUS_MIC_STARTED;
            }
            else if (role->cfg.device == AUD_STREAM_USE_INT_CODEC &&
                role->ctl.status == (AF_STATUS_OPEN_CLOSE | AF_STATUS_STREAM_OPEN_CLOSE))
            {
                *deviceId = id;
                return RAND_STATUS_MIC_OPENED;
            }
        }

        return RAND_STATUS_CLOSE;
    }
#endif

