/**
 * @file hv_mw_Audio.c
 * @brief audio middlware c file.
 * @details This file provides the following functions: \n
 *          (1) Initialization and de-initialization functions \n
 *          (2) Start and stop functions \n
 *          (3) Feed functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-18
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                        <th>Date         <th>Change Description
 * <tr><td>HiView SoC Software Team      <td>2022-08-23   <td>init
 * </table>
 */


#include "hv_comm_Assert.h"
#include "hv_comm_Log.h"
#include "hv_comm_Timer.h"
#include "hv_mw_Audio.h"
#include "hv_drv_Audio.h"
#include "hv_cal_Audio.h"
#include "hv_drv_PLL.h"
#include "hv_mw_DisplayPort.h"

BOOL    g_bHeadsetInsertion         = 1;    /*记录耳机是否插入，0-未插入；1-已插入*/
UINT32  g_uiSoundVolumeValue        = 80;   /*记录当前音量，0-100*/
UINT32  g_uiEQMode                  = 0;    /*记录当前音效模式*/
UINT32  g_uiMuteState               = 0;    /*记录当前用户是否设置静音*/
UINT32  g_uiSoundDelayTime          = 0;    /*记录当前声音延时时间*/
UINT32  g_uiHardwareMute            = 1;    /*支持硬件mute*/
UINT32  g_uiInterruptTriggerMode    = 0;    /*记录当前中断trigger方式*/
UINT32  g_uiAudioInit               = 0;    /*记录当前Audio是否完成初始化*/
HV_TIMER_S *xSoundDelayTimer        = NULL; /*Audio delay timer*/

#define _AUDIO_MAX_DELAY_TIME   600

VOID AudioDelayTimerInit(VOID)
{
    UINT32 uiDelayTime = Hv_Mw_Audio_GetDelayTime();
    HV_TIMER_INIT_PARAMS timerInitParam = \
    {"SoundDelayTimer", uiDelayTime, HV_FALSE, NULL, Hv_Drv_Audio_DmReadEnable, SYS_EVENT_TIMER_AUDIO_DELAY};
    xSoundDelayTimer = Hv_Vos_Timer_Create(&timerInitParam);
    HV_ASSERT_VALID_PTR_VOID(xSoundDelayTimer);
    return;
}

/**
 * @brief set 0-100 value to DAC.
 * @param[in] uiDelayTime: xx ms.
 * @return Status, 0-SUCCESS, 1-FAILUER.
 */
Status ReStartSoundDelayTimer(UINT32 uiDelayTime)
{
    HV_ASSERT_VALID_PTR(xSoundDelayTimer);
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_SetSoundDelayTimer. Delay %dms\n", uiDelayTime);
    HV_ASSERT_TRUE(Hv_Vos_Timer_ChangePeriod(xSoundDelayTimer, uiDelayTime));
    HV_ASSERT_TRUE(Hv_Vos_Timer_Start(xSoundDelayTimer));
    return HV_SUCCESS;
}

/**
 * @brief Start Sound DelayTimer.
 */
VOID Hv_Mw_Audio_StartSoundDelayTimer(VOID)
{
    HV_ASSERT_VALID_PTR_VOID(xSoundDelayTimer);
    Hv_Vos_Timer_Start(xSoundDelayTimer);
    return;
}

/**
 * @brief initialize audio module, for system module.
 * @param[in] VOID.
 * @return SUCCESS.
 */
Status Hv_Mw_Audio_Init(VOID)
{
    UINT32 uiVolumeValue = 0;
    //UINT32 uiSoundDelayTime = 0;

    Hv_Drv_Audio_SetReset();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_SetReset success.");

    Hv_Drv_Audio_PllInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_PllInit success.");

    Hv_Drv_Audio_GpioInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_GpioInit success.");

    Hv_Drv_Audio_RxInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_RxInit success.");

    Hv_Drv_Audio_TxInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_TxInit success.");

    Hv_Drv_Audio_DacInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_DacInit success.");

    Hv_Drv_Audio_HwInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_HwInit success.");
    uiVolumeValue = Hv_Mw_Audio_GetVolume();
    Hv_Mw_Audio_SetVolume(uiVolumeValue);
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_SetVolume success.");

    Hv_Drv_Audio_PathInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_PathInit success.");

    Hv_Drv_Audio_InterruptInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_InterruptInit success.");

    Hv_Mw_Audio_ChannelMute(_AUDIO_MUTE_BIT_HW);
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_ChannelMute success.");

    Hv_Drv_Audio_DmInit();
    HV_LOG_DEBUG(AUDIO, "Hv_Drv_Audio_DmInit success.");
    AudioDelayTimerInit();
    //uiSoundDelayTime = Hv_Mw_Audio_GetDelayTime();
    //Hv_Mw_Audio_SetDelayTime(uiSoundDelayTime);
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_SetDelayTime success.");

    g_uiAudioInit = 1;
    return HV_SUCCESS;
}

/**
 * @brief select one input from HDMI1/HDMI2/DP1/DP2, for channel manager and setting APP module.
 * @param[in] uiSoundChannelIndex: HDMI1/HDMI2/DP1/DP2.
 * @return Status, 0-SUCCESS, 1-HV_FAILURE.
 */
Status Hv_Mw_Audio_SelectSound(LinkPortIndex uiSoundChannelIndex, ChannelType enChannelType)
{
    Hv_Drv_PLL_SetAudioLaneClock(uiSoundChannelIndex);

    switch(uiSoundChannelIndex)
    {
        case LINK_PORT_INDEX_HDMI_RX0:
        {
            Hv_Drv_Audio_SetSource(_AUDIO_HDMI_0,   enChannelType);
            break;
        }
        case LINK_PORT_INDEX_HDMI_RX1:
        {
            Hv_Drv_Audio_SetSource(_AUDIO_HDMI_1,   enChannelType);
            break;
        }
        case LINK_PORT_INDEX_DP_RX0:
        {
            Hv_Drv_Audio_SetSource(_AUDIO_DP_0,     enChannelType);
            break;
        }
        case LINK_PORT_INDEX_DP_RX1:
        {
            Hv_Drv_Audio_SetSource(_AUDIO_DP_1,     enChannelType);
            break;
        }
        default:
        {
            HV_LOG_ASSERT(AUDIO, "Set an error audio source!");
            return HV_FAILURE;
        }
    }

    return HV_SUCCESS;
}

/**
 * @brief get volume value from datebase, for setting APP module.
 * @param[in] VOID.
 * @return 0-100 value.
 */
UINT32 Hv_Mw_Audio_GetVolume(VOID)
{
    g_uiSoundVolumeValue = HV_COMMON_DATABASE_GET(UserData, ucVolume);

    return g_uiSoundVolumeValue;
}

/**
 * @brief set 0-100 value to DAC & datebase, for setting APP module.
 * @param[in] uiSoundVolumeValue: 0-100.
 * @return Status, 0-SUCCESS, 1-HV_FAILURE.
 */
Status Hv_Mw_Audio_SetVolume(UINT32 uiSoundVolumeValue)
{
    if (uiSoundVolumeValue > _AUDIO_MAX_VOLUME)
    {
        HV_LOG_ASSERT(AUDIO, "VolumeValue exceeds the MaxValue");
        return HV_FAILURE;
    }
    else
    {
        g_uiSoundVolumeValue = uiSoundVolumeValue;
    }

    if (g_bHeadsetInsertion || HV_PROJECT_CONFIG_VOLUME_CONSISTENT)
    {
        Hv_Drv_Audio_SetHeadsetVolume(g_uiSoundVolumeValue);
    }
    else
    {
        Hv_Drv_Audio_SetSpeakerVolume(g_uiSoundVolumeValue);
    }

    HV_COMMON_DATABASE_SET(UserData, ucVolume, g_uiSoundVolumeValue);

    return HV_SUCCESS;
}

/**
 * @brief mute audio, for setting APP module.
 * @return Status, 0-SUCCESS, 1-HV_FAILURE.
 */
Status Hv_Mw_Audio_UIMute(VOID)
{
    Hv_Drv_Audio_DACMute(_AUDIO_MUTE_BIT_UI);

    HV_COMMON_DATABASE_SET(UserData, bMute, HV_TRUE);

    return HV_SUCCESS;
}

/**
 * @brief unmute audio, for setting APP module.
 * @return Status, 0-SUCCESS, 1-HV_FAILURE.
 */
Status Hv_Mw_Audio_UIUnMute(VOID)
{
    Hv_Drv_Audio_DACUnMute(_AUDIO_MUTE_BIT_UI);

    HV_COMMON_DATABASE_SET(UserData, bMute, HV_FALSE);

    return HV_SUCCESS;
}

/**
 * @brief mute audio, for channel manager.
 * @return Status, 0-SUCCESS, 1-FAILUER.
 */
Status Hv_Mw_Audio_ChannelMute(UINT32 uiBitMsk)
{
    Hv_Drv_Audio_TXMute(uiBitMsk);
    return HV_SUCCESS;
}

/**
 * @brief unmute audio, for channel manager.
 * @return Status, 0-SUCCESS, 1-FAILUER.
 */
Status Hv_Mw_Audio_ChannelUnMute(UINT32 uiBitMsk)
{
    Hv_Drv_Audio_TXUnMute(uiBitMsk);
    return HV_SUCCESS;
}

/**
 * @brief get eq mode, for setting APP module.
 * @param[in] void.
 * @return uiEQMode: Rock/Pop/Live/Dance/Club/Powerful MODE.
 */
UINT32 Hv_Mw_Audio_GetEQMode(VOID)
{
    g_uiEQMode = HV_COMMON_DATABASE_GET(UserData, uiEQMode);

    return g_uiEQMode;
}

/**
 * @brief set eq mode to HW-EQ, for setting APP module.
 * @param[in] uiEQMode: Rock/Pop/Live/Dance/Club/Powerful MODE.
 * @return Status, 0-SUCCESS, 1-HV_FAILURE.
 */
Status Hv_Mw_Audio_SetEQMode(UINT32 uiEQMode)
{
    if (uiEQMode > MODE_MAX)
    {
        HV_LOG_ASSERT(AUDIO, "Set an error EQ mode!");
        return HV_FAILURE;
    }
    else
    {
        g_uiEQMode = uiEQMode;
    }

#ifdef HV_PROJECT_CONFIG_AUDIO_EQ
    Hv_Drv_Audio_GetLpf0DataAndSetRegs(g_uiEQMode);
    Hv_Drv_Audio_GetHpf0DataAndSetRegs(g_uiEQMode);
    Hv_Drv_Audio_GetBpf0DataAndSetRegs(g_uiEQMode);
    Hv_Drv_Audio_GetBpf1DataAndSetRegs(g_uiEQMode);
    Hv_Drv_Audio_GetBpf2DataAndSetRegs(g_uiEQMode);
    Hv_Drv_Audio_GetBpf3DataAndSetRegs(g_uiEQMode);
    Hv_Drv_Audio_GetBpf4DataAndSetRegs(g_uiEQMode);
#endif

    HV_COMMON_DATABASE_SET(UserData, uiEQMode, g_uiEQMode);

    return HV_SUCCESS;
}

/**
 * @brief get delay time value from datebase, for setting APP module.
 * @param[in] VOID.
 * @return 0-500 value.
 */
UINT32 Hv_Mw_Audio_GetDelayTime(VOID)
{
    g_uiSoundDelayTime = HV_COMMON_DATABASE_GET(UserData, uiSoundDelayTime);

    return g_uiSoundDelayTime;
}

/**
 * @brief delay the sound by xx ms over the video, for setting APP module.
 * @param[in] uiSoundDelayTime: 50-500 ms. 50 ms per-step.
 * @return Status, 0-SUCCESS, 1-HV_FAILURE.
 */
Status Hv_Mw_Audio_SetDelayTime(UINT32 uiSoundDelayTime)
{
    UINT32 uiReturn = 0;
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_SetDelayTime!");

    if (uiSoundDelayTime)
    {
        if (uiSoundDelayTime > _AUDIO_MAX_DELAY_TIME)
        {
            HV_LOG_DEBUG(AUDIO, "SoundDelayTime exceeds the MaxValue");
            return HV_FAILURE;
        }
        else
        {
            g_uiSoundDelayTime = uiSoundDelayTime;
        }

        Hv_Drv_Audio_SetSoundDelayDDRSize(g_uiSoundDelayTime);
        Hv_Drv_Audio_SetSoundDelayDDRAddress();
        Hv_Drv_Audio_DmWriteEnable();

        if (1 == g_uiAudioInit)
        {
            uiReturn = ReStartSoundDelayTimer(g_uiSoundDelayTime);
            if (uiReturn) {
                HV_LOG_ASSERT(AUDIO, "Hv_Drv_Audio_SetSoundDelayTimer error!");
                g_uiSoundDelayTime = 0;
                Hv_Drv_Audio_DmDisable();
                return HV_FAILURE;
            }
        }
    }
    else
    {
        HV_LOG_DEBUG(AUDIO, "SoundDelayTime is zero. Disable DM!");
        g_uiSoundDelayTime = 0;
        Hv_Drv_Audio_DmDisable();
    }

    HV_COMMON_DATABASE_SET(UserData, uiSoundDelayTime, g_uiSoundDelayTime);

    return HV_SUCCESS;
}

/**
 * @brief audio spdif out, for channel manager and setting APP module.
 * @param[in] bAudioSpdifOut: ENABLE or DISABLE.
 */
VOID Hv_Mw_Audio_SpdifOut(BOOL bAudioSpdifOut)
{
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_SpdifOut.");

    //set spdif gpio
    Hv_Drv_Audio_SetSpdifOutMode(bAudioSpdifOut);

    return;
}

/**
 * @brief Set PLL ManualConfig Fbdiv, for channel manager and setting APP module.
 */
VOID Hv_Mw_Audio_SetPllManualConfig(VOID)
{
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_SetPllManualConfig.");

    //set VCO to 27M and fs to 48K
    Hv_Drv_Audio_ManualDefaultClock();

    return;
}

/**
 * @brief Set PLL AutoConfig Fbdiv, for channel manager and setting APP module.
 */
VOID Hv_Mw_Audio_SetPllAutoConfig(VOID)
{
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_SetPllAutoConfig.");

    //set VCO and new fs
    Hv_Drv_Audio_SetAudioClock();

    return;
}

/**
 * @brief Lock PLL, for channel manager and setting APP module.
 */
VOID Hv_Mw_Audio_ClockLock(VOID)
{
    //HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_ClockLock.");

    Hv_Drv_Audio_ClockLock();

    return;
}

/**
 * @brief TurnOnAmbientLight, for setting AmbientLight module.
 */
VOID Hv_Mw_Audio_TurnOnAmbientLight(VOID)
{
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_TurnOnAmbientLight!");

    if(g_uiSoundDelayTime)
    {
        return;
    }

    Hv_Drv_Audio_SetSoundDelayDDRSize(400);
    Hv_Drv_Audio_SetSoundDelayDDRAddress();
    Hv_Drv_Audio_DmWriteEnable();

    return;
}

/**
 * @brief TurnOffAmbientLight, for setting AmbientLight module.
 */
VOID Hv_Mw_Audio_TurnOffAmbientLight(VOID)
{
    HV_LOG_DEBUG(AUDIO, "Hv_Mw_Audio_TurnOffAmbientLight!");

    if(g_uiSoundDelayTime)
    {
        return;
    }

    Hv_Drv_Audio_DmDisable();

    return;
}

