/**
 * @file hv_drv_PLL.c
 * @brief PLL模块提供的驱动层
 * @details 该模块接口提供给Hiview-Madia-Ware及其他上层应用使用。
 * @author HiView SoC Software Team
 * @version 0.0.1
 * @date 20220822
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History: 
 * <table>
 * <tr><th>作者                       <th>日期              <th>描述
 * <tr><td>HiView SoC Software Team <td>20220822        <td>Create and first Version.
 * </table>
**/

#include "hv_chip_Irq.h"
#include "hv_cal_PLL.h"
#ifdef CONFIG_USER_DOUBLE_CHANNEL_ON
#include "hv_cal_DpuSubScaler.h"
#endif

#include "hv_cal_DpuMainScaler.h"
#include "hv_comm_Assert.h"
#include "hv_comm_DataBase.h"
#include "hv_drv_OsdDMA.h"
#include "hv_drv_PLL.h"
#include "hv_drv_DpuCommon.h"
#include "hv_drv_Gpio.h"
#include <math.h>

#define FBDIV_RANG_MIN_UNIT_27MHZ       (40)    /*40*27MHZ  = 1080MHZ > 800MHZ*/
#define FBDIV_RANG_MAX_UNIT_27MHZ       (100)   /*100*27MHZ = 2700MHZ < 3000MHZ*/

#define RXDPLL_POSTDIV_RANG_MIN         (1)
#define RXDPLL_POSTDIV_RANG_MAX         (128)

#define RXDPLL_MIN_DIFF_VALUE           (200)     /*时钟计算允许的最小误差KHZ*/
#define RXDPLL_MAX_DIFF_VALUE           (1100)    /*时钟计算允许的最大误差KHZ*/
#define RXDPLL_DIFF_STEP_VALUE          (100)     /*时钟计算误差迭代步长KHZ*/

#define PTCDPLL_POSTDIV0_1_RANG_MIN     (1)
#define PTCDPLL_POSTDIV0_1_RANG_MAX     (64)

#define PTCDPLL_MIN_DIFF_VALUE          (300)     /*时钟计算允许的最小误差KHZ*/
#define PTCDPLL_MAX_DIFF_VALUE          (1200)    /*时钟计算允许的最大误差KHZ*/
#define PTCDPLL_DIFF_STEP_VALUE         (100)     /*时钟计算误差迭代步长KHZ*/


/*小数部分精度,RX Dclock和PTCD PLL FBdiv小数部分精度定义为1 / FRAC_LOOP_TIME_MAX,需要根据芯片计算timing性能动态调整*/
#define FRAC_LOOP_TIME_MAX              (10)  /*小数部分的最大值为0XFFFF, 0XFFFF/FRAC_LOOP_TIME_MAX代表小数部分为 1/FRAC_LOOP_TIME_MAX*/


Status Hv_Drv_PLL_SetAudioLaneClock(LinkPortIndex enAudioRefPortIndex)
{
    return Hv_Cal_PLL_SetAudioLaneClock(enAudioRefPortIndex);
}

Status Hv_Drv_PLL_ConfigRxDpllSel(UINT32 uiRxPllIndex, LinkPortIndex enReleasePortIndex, LinkPortIndex enBindPortIndex)
{
    return Hv_Cal_PLL_ConfigRxDpllSel(uiRxPllIndex, enReleasePortIndex, enBindPortIndex);
}

Status Hv_Drv_PLL_ConfigRxDpllMuteSel(BOOL bSamePll)
{
    return Hv_Cal_PLL_ConfigRxDpllMuteSel(bSamePll);
}

/*RXdpll_clk=clkin * fbdiv / prediv / postdiv0 / postdiv0_0 */
static BOOL FindRxDPLLParameter( UINT32 uiClockDst, UINT32 uiFbdiv, UINT32* puiPostdivBest, UINT32 uiClockDiffValue, UINT32 uiPllnFrac, UINT32 *uiRxPllClockValue)
{
    UINT32 uiPostdivMin     = RXDPLL_POSTDIV_RANG_MIN;
    UINT32 uiPostdivMax     = RXDPLL_POSTDIV_RANG_MAX;
    UINT32 uiPostdivLoop    = 0;
    UINT32 uiClockTemp      = 0;
    UINT32 uiClockMinDiffTemp = 0;
    UINT32 uiClockMinDiffBest = uiClockDiffValue;

    for (uiPostdivLoop = uiPostdivMin; uiPostdivLoop < uiPostdivMax; uiPostdivLoop++)
    {
        HV_ASSERT_TRUE_RET(uiPostdivLoop > 0, HV_FALSE);
        uiClockTemp = ((UINT64)27000 * ((uiFbdiv << 16) + uiPllnFrac) / uiPostdivLoop) >> 16;
        uiClockMinDiffTemp = ABS(uiClockTemp, uiClockDst);
        if (uiClockMinDiffTemp == 0)
        {
            *puiPostdivBest = uiPostdivLoop;
            *uiRxPllClockValue = uiClockTemp;
            return HV_TRUE;
        }
        else if ((uiClockMinDiffTemp < uiClockMinDiffBest) && (uiClockTemp > uiClockDst ))
        {
            *puiPostdivBest = uiPostdivLoop;
            *uiRxPllClockValue = uiClockTemp;
            uiClockMinDiffBest = uiClockMinDiffTemp;
            return HV_TRUE;
        }
    }
    return HV_FALSE;
}

/*0_1 -D-clock   pg1-ipclock*/
Status Hv_Drv_PLL_SetRxdpllPixelClock(ChannelType enChannelType, UINT32* puiRxDclockOut, UINT32 uiRxClockDst)
{
    UINT32 uiPrediv         = 1;
    UINT32 uiClockStepLoop  = 0;
    UINT32 uiFbdivLoop      = 0;
    UINT32 uiPostdivBest    = 0;
    BOOL   bfind            = HV_FALSE;
    UINT32 uiPllnFrac       = 0;

    for (uiClockStepLoop = RXDPLL_MIN_DIFF_VALUE; uiClockStepLoop <= RXDPLL_MAX_DIFF_VALUE; uiClockStepLoop += RXDPLL_DIFF_STEP_VALUE)
    {
        for (uiFbdivLoop = FBDIV_RANG_MIN_UNIT_27MHZ; uiFbdivLoop < FBDIV_RANG_MAX_UNIT_27MHZ; uiFbdivLoop++)
        {
            for(UINT32 uifracLoop = 0; uifracLoop < FRAC_LOOP_TIME_MAX; uifracLoop++)
            {
                uiPllnFrac = (0xFFFF / FRAC_LOOP_TIME_MAX) * uifracLoop;
                bfind = FindRxDPLLParameter(uiRxClockDst, uiFbdivLoop, &uiPostdivBest, uiClockStepLoop, uiPllnFrac, puiRxDclockOut);
                if(HV_TRUE == bfind)
                {
                    HV_ASSERT_TRUE(uiPostdivBest > 0);
                    Hv_Cal_PLL_SetRxDpllFbdiv(enChannelType, uiFbdivLoop);
                    Hv_Cal_PLL_SetRxDpllPreDiv(enChannelType, uiPrediv);
                    Hv_Cal_PLL_SetRxDpllPostdivIP(enChannelType, uiPostdivBest);
                    Hv_Cal_PLL_SetRxDpllPostdiv0_XIP(enChannelType, 1);
                    Hv_Cal_PLL_SetRxDpllFrac(enChannelType, uiPllnFrac);
                    
                    Hv_Vos_Delayus(2000);
                    Hv_Cal_PLL_RXD_WaitUntilLock(enChannelType);/*wait until clock lock, forever*/
                    HV_LOG_DEBUG(VIDEO, "RXD_Clk Fbdiv=%u,PostdivInt=%u, FbdivFrac=%u,RxCllkDst=%u", uiFbdivLoop,uiPostdivBest, uiPllnFrac,uiRxClockDst);
                    return HV_SUCCESS;
                }
            }
        }
    }
    HV_LOG_ERROR(SYSTEM, "Set Rx PLL clock without suitable value.");

    return HV_FAILURE;
}

/* d_clk =clkin * fbdiv / prediv * postdiv0 * postdiv0_1 */
BOOL FindPtcdPLLParameter(UINT32 uiClockDst, UINT32 uiFbdiv, UINT32* puiPostdivBest, UINT32 uiClockDiffValue, UINT32 uiPllnFrac, UINT32* puiDClkOut)
{
    UINT32 uiPostdivMin = PTCDPLL_POSTDIV0_1_RANG_MIN;
    UINT32 uiPostdivMax = PTCDPLL_POSTDIV0_1_RANG_MAX;
    UINT32 uiPostdivLoop = 0;
    UINT32 uiClockTemp = 0;
    UINT32 uiClockMinDiffTemp = 0;
    UINT32 uiClockMinDiffBest = uiClockDiffValue;
    for (uiPostdivLoop = uiPostdivMin; uiPostdivLoop < uiPostdivMax; uiPostdivLoop++)
    {
        HV_ASSERT_TRUE_RET(uiPostdivLoop > 0, HV_FALSE);
        uiClockTemp = ((UINT64)27000* ((uiFbdiv << 16) + uiPllnFrac) / uiPostdivLoop) >> 16;
        uiClockMinDiffTemp = ABS(uiClockTemp, uiClockDst);
        if (uiClockMinDiffTemp == 0)
        {
            *puiPostdivBest = uiPostdivLoop;
            *puiDClkOut = uiClockTemp;
            return HV_TRUE;
        }
        else if ((uiClockMinDiffTemp < uiClockMinDiffBest) && (uiClockTemp > uiClockDst))
        {
            *puiPostdivBest = uiPostdivLoop;
            uiClockMinDiffBest = uiClockMinDiffTemp;
            *puiDClkOut = uiClockTemp;
            return HV_TRUE;
        }
    }
    return HV_FALSE;
}

Status CalculatePllParameter(UINT32* puiDClkCfg, UINT32 uiDisplayDestClock,UINT32 *puiFbdivInt,UINT32 *puiFbdivFrac,UINT32 *puiPostdiv)
{
    UINT32 uiClockStepLoop  = 0;
    UINT32 uiFbdivLoop      = 0;
    UINT32 uiPostdivDBest   = 0;
    BOOL  bfind             = HV_FALSE;
    UINT32 uiPllnFrac       = 0;
    /*先计算在误差DISPLAY_CLOCK_MIN_DIFF_VALUE时是否能求出来值，之后误差逐渐按照+DISPLAY_CLOCK_DIFF_STEP_VALUE迭代，理论上6M误差必然能求出来适合的时钟.*/
    for (uiClockStepLoop = PTCDPLL_MIN_DIFF_VALUE; uiClockStepLoop <= PTCDPLL_MAX_DIFF_VALUE;uiClockStepLoop += PTCDPLL_DIFF_STEP_VALUE)
    {
        for (uiFbdivLoop = FBDIV_RANG_MIN_UNIT_27MHZ; uiFbdivLoop < FBDIV_RANG_MAX_UNIT_27MHZ; uiFbdivLoop++)
        {
            for (UINT32 uifracLoop = 0; uifracLoop < FRAC_LOOP_TIME_MAX; uifracLoop++)
            {
                uiPllnFrac = (0xFFFF / FRAC_LOOP_TIME_MAX) * uifracLoop;
                bfind = FindPtcdPLLParameter(uiDisplayDestClock, uiFbdivLoop, &uiPostdivDBest, uiClockStepLoop, uiPllnFrac, puiDClkCfg);
                /*如果在当前误差值uiClockStepLoop下能找到合适的时钟，那么函数会设置ptcdpll时钟，并且return SUCCESS*/
                if (HV_TRUE == bfind)
                {
                    *puiFbdivInt = uiFbdivLoop;
                    *puiFbdivFrac = uiPllnFrac;
                    *puiPostdiv = uiPostdivDBest;
                    return HV_SUCCESS;
                }
            }
        }
    }
    return HV_FAILURE;
}

static VOID WaitOsdDmaReady(VOID)
{
    UINT32 uiCheckTimes = 0;
    do
    {
        if(HV_TRUE != Hv_Drv_OsdDMA_GetTransferDone())
        {
            Hv_Vos_Delayus(5000);
            uiCheckTimes++;
            HV_LOG_INFO(VIDEO, "##Wait OSD DMA ready ...");
        }
        else
        {
            return;
        }
    }
    while (uiCheckTimes < 5);
    return;
}

UINT32 g_uiNeedConfigPtcdPll = 0;         /*切换timing时需要配置ptcd pll的标志*/
UINT32 SetPllParameter(UINT32 uiFbdivInt,UINT32 uiFbdivFrac,UINT32 uiPostdiv,UINT32 uiInitFlag,VideoConfigParams* pstVideoConfigParams)
{
    pstVideoConfigParams->uiPtcdPreDiv = 1;
    pstVideoConfigParams->uiPtcdFBDivInt = uiFbdivInt;
    pstVideoConfigParams->uiPtcdFBDivFrac = uiFbdivFrac;
    pstVideoConfigParams->uiPtcdPostDiv = uiPostdiv;
    if(0 == uiInitFlag)/*系统初始化时，中断未使能，直接配置*/
    {
        Hv_Cal_PLL_SetPtcdpllPreDiv(1);
        Hv_Cal_PLL_SetPtcdPllFbdiv(uiFbdivInt);
        Hv_Cal_PLL_SetPtcdpllSscgPllnFrac(uiFbdivFrac);
        Hv_Cal_PLL_SetPtcdpllPostdivD(uiPostdiv);

        Hv_Cal_PLL_TogglePtcdUpdate();
        Hv_Cal_PLL_FN_ClearPllLockFlag();
        Hv_Vos_Delayus(2000);
        if(HV_FALSE ==  Hv_Cal_PLL_Ptcd_WaitUntilLock()) /*wait until clock lock, forever*/
        {
            HV_LOG_ERROR(VIDEO, "###PTCD Pll lock timeout");
            return HV_FAILURE;
        }
        Hv_Cal_PLL_FN_ToggleDpuClkCapRst();
    }
    else/*在vlank配置*/
    {       
        WaitOsdDmaReady();/*wait osd dma ready*/
        g_uiNeedConfigPtcdPll = 1;
        Hv_Drv_DpuScaler_Enable_Vsync_Interrupt();
        UINT32 uiCheckTimes = 0;
        do
        {
            if(uiCheckTimes < 200)
            {
                uiCheckTimes++;
                Hv_Vos_Delayus(1000);
            }
            else
            {
                Hv_Drv_DpuScaler_Disable_Vsync_Interrupt();
                Hv_Cal_PLL_SetPtcdpllPreDiv(1);
                Hv_Cal_PLL_SetPtcdPllFbdiv(uiFbdivInt);
                Hv_Cal_PLL_SetPtcdpllSscgPllnFrac(uiFbdivFrac);
                Hv_Cal_PLL_SetPtcdpllPostdivD(uiPostdiv);
                Hv_Cal_PLL_TogglePtcdUpdate();
                Hv_Vos_Delayus(5000);
                HV_LOG_INFO(VIDEO, "Config ptcd pll Fail in ISR,config it directly!");
                break;
            }
        }while (1 == g_uiNeedConfigPtcdPll);

        HV_LOG_INFO(VIDEO, "Wait PTCD clk...");
        if(HV_FALSE ==  Hv_Cal_PLL_Ptcd_WaitUntilLock()) /*wait until clock lock, forever*/
        {
            HV_LOG_ERROR(VIDEO, "###PTCD Pll clk timeout");
            return HV_FAILURE;
        }
    }
    HV_LOG_DEBUG(VIDEO, "Set dclk Fbdiv=%u,PostdivInt=%u, FbdivFrac=%u", uiFbdivInt,uiPostdiv, uiFbdivFrac);
    return HV_SUCCESS;
}

typedef enum _ExceedChipAbilityType
{
    EXCEED_CHIP_ABILITY_NO = 0,
    EXCEED_CHIP_ABILITY_HACTIVE,
    EXCEED_CHIP_ABILITY_VACTIVE,
    EXCEED_CHIP_ABILITY_FRAME_RATE,
}ExceedChipAbilityType;

Status Hv_Drv_PLL_SetDisplayClock(UINT32* puiDClkCfg, UINT32 uiDisplayDestClock, VideoConfigParams* pstVideoConfigParams)
{
    UINT32 uiFbdivIntValue  = 0;
    UINT32 uiPostdivValue   = 0;
    UINT32 uiFbdivFracValue = 0;
    static UINT32 uiInitFlag = 0; /*上电首次设置，不使用vsync中断方式*/

    HV_ASSERT_TRUE(0 != uiDisplayDestClock);
    HV_ASSERT_SUCCESS(CalculatePllParameter(puiDClkCfg,uiDisplayDestClock,&uiFbdivIntValue,&uiFbdivFracValue,&uiPostdivValue));

    if(0 == uiInitFlag)
    {
        HV_ASSERT_SUCCESS(SetPllParameter(uiFbdivIntValue,uiFbdivFracValue,uiPostdivValue,uiInitFlag,pstVideoConfigParams));
        uiInitFlag = 1;
    }
    else
    {
        HV_ASSERT_SUCCESS(SetPllParameter(uiFbdivIntValue,uiFbdivFracValue,uiPostdivValue,uiInitFlag,pstVideoConfigParams));
    }

    return HV_SUCCESS;
}

Status Hv_Drv_EdpTx_PllCalculator(const HvDrvKtxInParam *pstKtxInParam, HvKtxPllCfgParam *pstKtxPllCfgParam)
{
    UINT32      uiCurEdpVco                 = 0;
    UCHAR8      ucHalfModeDiv               = 2;
    USHORT16    usPllPacketLen              = 20;
    USHORT16    usPllPacketVld              = 20;
    uiCurEdpVco                             = pstKtxInParam->uiKtxInDptxFreq / ucHalfModeDiv;
    pstKtxPllCfgParam->ucTxPllPreDiv        = 1;
    pstKtxPllCfgParam->usTxPllFbdivInt      = uiCurEdpVco / pstKtxInParam->uiKtxInRefClkFreq;
    pstKtxPllCfgParam->usTxPllPostDiv0      = 1;
    pstKtxPllCfgParam->ucTxPllPostDiv0Div2  = (ucHalfModeDiv == 2) ? 0 : 1;
    pstKtxPllCfgParam->ucTxPllPifDiv        = usPllPacketLen / ucHalfModeDiv;
    pstKtxPllCfgParam->ucTxPllRdDiv         = 0;
    pstKtxPllCfgParam->ucTxPllClklvdsEn     = 0;
    return HV_SUCCESS;
}

static Status P2pTxPllReview(const HvDrvKtxInParam *pstKtxInParam, HvDrvKtxDesign *pstKtxDesign, HvKtxPllCfgParam *pstKtxPllCfgParam)
{
    while ((pstKtxPllCfgParam->usTxPllFbdivInt % 2 == 0) && (pstKtxPllCfgParam->usTxPllPostDiv0 % 2 == 0))
    {
        pstKtxPllCfgParam->usTxPllFbdivInt /= 2;
        pstKtxPllCfgParam->usTxPllPostDiv0 /= 2;
    }
    while ((pstKtxPllCfgParam->usTxPllFbdivInt % 3 == 0) && (pstKtxPllCfgParam->usTxPllPostDiv0 % 3 == 0))
    {
        pstKtxPllCfgParam->usTxPllFbdivInt /= 3;
        pstKtxPllCfgParam->usTxPllPostDiv0 /= 3;
    }
    while ((pstKtxPllCfgParam->usTxPllFbdivInt % 5 == 0) && (pstKtxPllCfgParam->usTxPllPostDiv0 % 5 == 0))
    {
        pstKtxPllCfgParam->usTxPllFbdivInt /= 5;
        pstKtxPllCfgParam->usTxPllPostDiv0 /= 5;
    }

    while ((pstKtxPllCfgParam->usTxPllFbdivInt % 2 == 0) && (pstKtxPllCfgParam->ucTxPllPreDiv % 2 == 0))
    {
        pstKtxPllCfgParam->usTxPllFbdivInt /= 2;
        pstKtxPllCfgParam->ucTxPllPreDiv /= 2;
    }

    while ((pstKtxPllCfgParam->usTxPllFbdivInt % 3 == 0) && (pstKtxPllCfgParam->ucTxPllPreDiv % 3 == 0))
    {
        pstKtxPllCfgParam->usTxPllFbdivInt /= 3;
        pstKtxPllCfgParam->ucTxPllPreDiv /= 3;
    }

    while ((pstKtxPllCfgParam->usTxPllFbdivInt % 5 == 0) && (pstKtxPllCfgParam->ucTxPllPreDiv % 5 == 0))
    {
        pstKtxPllCfgParam->usTxPllFbdivInt /= 5;
        pstKtxPllCfgParam->ucTxPllPreDiv /= 5;
    }

    if ((pstKtxInParam->uiKtxInRefClkFreq / (pstKtxPllCfgParam->ucTxPllPreDiv * pstKtxPllCfgParam->usTxPllPostDiv0) >= pstKtxDesign->uiTxPllMinRef))
    {
        pstKtxPllCfgParam->ucTxPllPreDiv *= pstKtxPllCfgParam->usTxPllPostDiv0;
        pstKtxPllCfgParam->usTxPllPostDiv0 = 1;
    }

    BOOL bLoopFlag = HV_TRUE;
    while (bLoopFlag)
    {
        if ((pstKtxPllCfgParam->usTxPllPostDiv0 % 2 == 0) && (pstKtxInParam->uiKtxInRefClkFreq / (pstKtxPllCfgParam->ucTxPllPreDiv * 2) >= pstKtxDesign->uiTxPllMinRef))
        {
            pstKtxPllCfgParam->ucTxPllPreDiv *= 2;
            pstKtxPllCfgParam->usTxPllPostDiv0 /= 2;
        }
        else if ((pstKtxPllCfgParam->usTxPllPostDiv0 % 3 == 0) && (pstKtxInParam->uiKtxInRefClkFreq / (pstKtxPllCfgParam->ucTxPllPreDiv * 3) >= pstKtxDesign->uiTxPllMinRef))
        {
            pstKtxPllCfgParam->ucTxPllPreDiv *= 3;
            pstKtxPllCfgParam->usTxPllPostDiv0 /= 3;
        }
        else
        {
            bLoopFlag = HV_FALSE;
        }
    }

    while (pstKtxInParam->uiKtxInRefClkFreq / pstKtxPllCfgParam->ucTxPllPreDiv < pstKtxDesign->uiTxPllMinRef)
    {
        if ((pstKtxPllCfgParam->ucTxPllPreDiv % 2 == 0) && (pstKtxPllCfgParam->usTxPllFbdivInt % 2 == 0))
        {
            pstKtxPllCfgParam->ucTxPllPreDiv /= 2;
            pstKtxPllCfgParam->usTxPllFbdivInt /= 2;
        }
        else if ((pstKtxPllCfgParam->ucTxPllPreDiv % 3 == 0) && (pstKtxPllCfgParam->usTxPllFbdivInt % 3 == 0))
        {
            pstKtxPllCfgParam->ucTxPllPreDiv /= 3;
            pstKtxPllCfgParam->usTxPllFbdivInt /= 3;
        }
        else if ((pstKtxPllCfgParam->ucTxPllPreDiv % 2 == 0) && (pstKtxInParam->uiKtxInRefClkFreq * 2 * pstKtxPllCfgParam->usTxPllFbdivInt / pstKtxPllCfgParam->ucTxPllPreDiv < pstKtxDesign->uiTxPllMaxVco))
        {
            pstKtxPllCfgParam->ucTxPllPreDiv /= 2;
            pstKtxPllCfgParam->usTxPllPostDiv0 *= 2;
        }
        else if ((pstKtxPllCfgParam->ucTxPllPreDiv % 3 == 0) && (pstKtxInParam->uiKtxInRefClkFreq * 3 * pstKtxPllCfgParam->usTxPllFbdivInt / pstKtxPllCfgParam->ucTxPllPreDiv < pstKtxDesign->uiTxPllMaxVco))
        {
            pstKtxPllCfgParam->ucTxPllPreDiv /= 3;
            pstKtxPllCfgParam->usTxPllPostDiv0 *= 3;
        }
        else
        {
            HV_LOG_DEBUG(PANEL, "ERROR : Refclk Range underflow , can't support current case !!!");
            return HV_FAILURE;
        }
    }

    while (pstKtxInParam->uiKtxInRefClkFreq / pstKtxPllCfgParam->ucTxPllPreDiv > pstKtxDesign->uiTxPllMaxRef)
    {
        pstKtxPllCfgParam->ucTxPllPreDiv *= 2;
        pstKtxPllCfgParam->usTxPllFbdivInt *= 2;
    }

    // uiCurVco = pstKtxInParam->uiKtxInRefClkFreq / pstKtxPllCfgParam->ucTxPllPreDiv * pstKtxPllCfgParam->usTxPllFbdivInt;

    HV_LOG_DEBUG(PANEL, "********** PLL_CFG pstKtxDesign->uiTxPllMinVco: %d", pstKtxDesign->uiTxPllMinVco);
    HV_LOG_DEBUG(PANEL, "********** PLL_CFG pstKtxPllCfgParam->ucTxPllPreDiv: %d", pstKtxPllCfgParam->ucTxPllPreDiv);
    HV_LOG_DEBUG(PANEL, "********** PLL_CFG pstKtxPllCfgParam->usTxPllFbdivInt: %d", pstKtxPllCfgParam->usTxPllFbdivInt);
    HV_LOG_DEBUG(PANEL, "********** PLL_CFG pstKtxPllCfgParam->usTxPllPostDiv0: %d", pstKtxPllCfgParam->usTxPllPostDiv0);
    HV_LOG_DEBUG(PANEL, "********** PLL_CFG pstKtxInParam->uiKtxInRefClkFreq: %d", pstKtxInParam->uiKtxInRefClkFreq);

    while (pstKtxInParam->uiKtxInRefClkFreq / pstKtxPllCfgParam->ucTxPllPreDiv * pstKtxPllCfgParam->usTxPllFbdivInt < pstKtxDesign->uiTxPllMinVco)
    {
        pstKtxPllCfgParam->usTxPllFbdivInt *= 2;
        pstKtxPllCfgParam->usTxPllPostDiv0 *= 2;
        // uiCurVco = pstKtxInParam->uiKtxInRefClkFreq / pstKtxPllCfgParam->ucTxPllPreDiv * pstKtxPllCfgParam->usTxPllFbdivInt;
    }

    if (pstKtxInParam->uiKtxInRefClkFreq / pstKtxPllCfgParam->ucTxPllPreDiv * pstKtxPllCfgParam->ucTxPllPreDiv > pstKtxDesign->uiTxPllMaxVco)
    {
        HV_LOG_ERROR(PANEL, "ERROR : can't config suitable vco , not support current case !!!");
        return HV_FAILURE;
    }
    return HV_SUCCESS;
}

Status Hv_Drv_VboTx_PllCalculator(const HvDrvKtxInParam *pstKtxInParam, HvDrvKtxDesign *pstKtxDesign, HvKtxPllCfgParam *pstKtxPllCfgParam)
{
    UINT32      uiCurVboVco     = 0;
    UCHAR8      ucHalfModeDiv   = 2;
    USHORT16    usPllPacketLen  = 0;
    USHORT16    usPllPacketVld  = 0;
    USHORT16    usRefFbNum      = 0;
    UCHAR8      usPreFbDiv      = 1;
    UCHAR8      usTotalLane     = 8;

    usRefFbNum = pstKtxInParam->uiKtxInPixClkFreq / pstKtxInParam->uiKtxInRefClkFreq;
    usTotalLane = pstKtxInParam->usKtxInPortNum * pstKtxInParam->usKtxInPairNum;
    // byte_mode=usKtxInColorDepth*3/usPllPacketVld
    if (pstKtxInParam->usKtxInColorDepth == 6)
    {
        usPllPacketLen = 10;
        usPllPacketVld = 6;
    }
    else if (pstKtxInParam->usKtxInColorDepth == 8)
    {
        usPllPacketLen = 10;
        usPllPacketVld = 8;
    }
    else if (pstKtxInParam->usKtxInColorDepth == 10)
    {
        usPllPacketLen = 10;
        usPllPacketVld = 6;
    }
    else if (pstKtxInParam->usKtxInColorDepth == 12)
    {
        usPllPacketLen = 10;
        usPllPacketVld = 7;
    }
    else
    {
        HV_LOG_ERROR(PANEL, "ERROR : P2P bit mode not support !!!");
        return HV_FAILURE;
    }

    if (pstKtxInParam->usKtxInColorDepth == 10)
    {
        uiCurVboVco = pstKtxInParam->uiKtxInPixClkFreq * 4 * usPllPacketLen / ucHalfModeDiv / usTotalLane;
    }
    else if (pstKtxInParam->usKtxInColorDepth == 12)
    {
        /* vby 12bit */
        uiCurVboVco = pstKtxInParam->uiKtxInPixClkFreq * 5 * usPllPacketLen / ucHalfModeDiv / usTotalLane;
    }
    else
    {
        uiCurVboVco = pstKtxInParam->uiKtxInPixClkFreq * 3 * pstKtxInParam->usKtxInColorDepth * usPllPacketLen / ucHalfModeDiv / usTotalLane / usPllPacketVld;
    }

    if (uiCurVboVco > pstKtxDesign->uiTxPllMaxVco)
    {
        HV_LOG_ERROR(PANEL, "ERROR : VCO Range overflow , can't support current case !!!");
        return HV_FAILURE;
    }

    /* vby 10bit */
    if (pstKtxInParam->usKtxInColorDepth == 10)
    {
        pstKtxPllCfgParam->usTxPllFbdivInt = usRefFbNum * 4 * usPllPacketLen / ucHalfModeDiv / usPreFbDiv;
        pstKtxPllCfgParam->usTxPllPostDiv0 = 1;
        pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = (ucHalfModeDiv == 2) ? 0 : 1;
    }
    /* vby 12bit */
    else if (pstKtxInParam->usKtxInColorDepth == 12)
    {
        pstKtxPllCfgParam->usTxPllFbdivInt = usRefFbNum * 5 * usPllPacketLen / ucHalfModeDiv / usPreFbDiv;
        pstKtxPllCfgParam->usTxPllPostDiv0 = 1;
        pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = (ucHalfModeDiv == 2) ? 0 : 1;
    }
    else
    {
        HV_LOG_DEBUG(PANEL, "********** PLL_CFG usRefFbNum: %d, usPllPacketLen: %d ucHalfModeDiv: %d, usPreFbDiv: %d", usRefFbNum, usPllPacketLen, ucHalfModeDiv, usPreFbDiv);

        pstKtxPllCfgParam->usTxPllFbdivInt = usRefFbNum * 3 * pstKtxInParam->usKtxInColorDepth * usPllPacketLen / ucHalfModeDiv / usPreFbDiv;
        pstKtxPllCfgParam->usTxPllPostDiv0 = usPllPacketVld;
        pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = (ucHalfModeDiv == 2) ? 0 : 1;
    }
    pstKtxPllCfgParam->ucTxPllPreDiv = usTotalLane;
    pstKtxPllCfgParam->ucTxPllPifDiv = usPllPacketLen / ucHalfModeDiv;
    pstKtxPllCfgParam->ucTxPllRdDiv = 0;
    pstKtxPllCfgParam->ucTxPllClklvdsEn = 0;
    HV_ASSERT_SUCCESS(P2pTxPllReview(pstKtxInParam, pstKtxDesign, pstKtxPllCfgParam));
    /*原始方案存在usTxPllPostDiv2是小数的情况，无法配出来给vbo的d clk*/
    pstKtxPllCfgParam->usTxPllPostDiv2 = pstKtxPllCfgParam->usTxPllFbdivInt / pstKtxPllCfgParam->ucTxPllPreDiv;

#ifdef CONFIG_USER_PANEL_TYPE_VBOTX
    // fhd60 2lane
    // pstKtxPllCfgParam->ucTxPllPreDiv = 2;
    // pstKtxPllCfgParam->usTxPllFbdivInt = 20;
    // pstKtxPllCfgParam->usTxPllPostDiv0 = 1;
    // pstKtxPllCfgParam->usTxPllPostDiv2 = 10;
    // pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = 0;
    // pstKtxPllCfgParam->ucTxPllPifDiv = 5;
    // pstKtxPllCfgParam->ucTxPllRdDiv = 0;
    //fhd60 4lane
    // pstKtxPllCfgParam->ucTxPllPreDiv = 2;
    // pstKtxPllCfgParam->usTxPllFbdivInt = 20;
    // pstKtxPllCfgParam->usTxPllPostDiv0 = 2;
    // pstKtxPllCfgParam->usTxPllPostDiv2 = 5;
    // pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = 0;
    // pstKtxPllCfgParam->ucTxPllPifDiv = 5;
    // pstKtxPllCfgParam->ucTxPllRdDiv = 0;

    //fhd120 4lane
    // pstKtxPllCfgParam->ucTxPllPreDiv = 4;
    // pstKtxPllCfgParam->usTxPllFbdivInt = 20;
    // pstKtxPllCfgParam->usTxPllPostDiv0 = 1;
    // pstKtxPllCfgParam->usTxPllPostDiv2 = 5;
    // pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = 0;
    // pstKtxPllCfgParam->ucTxPllPifDiv = 5;
    // pstKtxPllCfgParam->ucTxPllRdDiv = 0;

    //UHD60 8lane
    // pstKtxPllCfgParam->ucTxPllPreDiv = 22;
    // pstKtxPllCfgParam->usTxPllFbdivInt = 110;
    // pstKtxPllCfgParam->usTxPllPostDiv0 = 1;
    // pstKtxPllCfgParam->usTxPllPostDiv2 = 5;
    // pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = 1;
    // pstKtxPllCfgParam->ucTxPllPifDiv = 10;
    // pstKtxPllCfgParam->ucTxPllRdDiv = 0;
#endif

    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->ucTxPllPreDiv:%d", pstKtxPllCfgParam->ucTxPllPreDiv);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->usTxPllFbdivInt:%d", pstKtxPllCfgParam->usTxPllFbdivInt);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->usTxPllFbdivFrac:%d", pstKtxPllCfgParam->usTxPllFbdivFrac);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->usTxPllPostDiv0:%d", pstKtxPllCfgParam->usTxPllPostDiv0);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->usTxPllPostDiv1:%d", pstKtxPllCfgParam->usTxPllPostDiv1);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->usTxPllPostDiv2:%d", pstKtxPllCfgParam->usTxPllPostDiv2);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->ucTxPllPostDiv0Div2:%d", pstKtxPllCfgParam->ucTxPllPostDiv0Div2);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->ucTxPllPifDiv:%d", pstKtxPllCfgParam->ucTxPllPifDiv);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->ucTxPllRdDiv:%d", pstKtxPllCfgParam->ucTxPllRdDiv);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->ucTxPllPostDivLvds:%d", pstKtxPllCfgParam->ucTxPllPostDivLvds);
    HV_LOG_DEBUG(PANEL, "pstKtxPllCfgParam->ucTxPllClklvdsEn:%d", pstKtxPllCfgParam->ucTxPllClklvdsEn);

    return HV_SUCCESS;
}

static VOID VboTxTaibaiVcoCalculator(UINT32 uiPixClkFreq, UINT32 uiClockDst, UINT32 uiLaneNum, UINT32* puiPreDiv, UINT32* puiFbdiv) 
{
    UINT32 uiBestPreDiv = 0;
    UINT32 uiBestFbdiv = 0;
    DOUBLE64 bestError = (DOUBLE64)UINT_MAX;
    /*prediv太小存在时钟不准的情况*/  
    for (UINT32 uiPreDiv = uiLaneNum; uiPreDiv <= 63; ++uiPreDiv) 
    { // puiPreDiv的范围是1到63
        for (UINT32 uiFbDiv = 1; uiFbDiv <= 1023; ++uiFbDiv) 
        { // puiFbdiv的范围是1到1023
            DOUBLE64 calculatedClock = (DOUBLE64)uiPixClkFreq / uiPreDiv * uiFbDiv;
            DOUBLE64 error = fabs(calculatedClock - uiClockDst);

            if (error < bestError) 
            {
                bestError = error;
                uiBestPreDiv = uiPreDiv;
                uiBestFbdiv = uiFbDiv;
            }

            // 如果误差小于1000，直接返回
            if (error < 1000) 
            {
                *puiPreDiv = uiBestPreDiv;
                *puiFbdiv = uiBestFbdiv;
                return;
            }
        }
    }

    *puiPreDiv = uiBestPreDiv;
    *puiFbdiv = uiBestFbdiv;
}


Status Hv_Drv_VboTx_TaibaiPllCalculator(const HvDrvKtxInParam *pstKtxInParam, UINT32 uiPixClkFreq, HvKtxPllCfgParam *pstKtxPllCfgParam)
{
    UCHAR8 usTotalLane = pstKtxInParam->usKtxInPortNum * pstKtxInParam->usKtxInPairNum;
    UINT32 uiVboPixPifRatio = (pstKtxInParam->usKtxInColorDepth == 10 ? 4 : 3) / usTotalLane;          // byte_mode
    UINT32 uiVboPifClk = uiPixClkFreq * (pstKtxInParam->usKtxInColorDepth == 10 ? 4 : 3) / usTotalLane;
    UINT32 uiCurVboVco = 0;
    UINT32 ucTxPllPreDiv = 0;
    UINT32 usTxPllFbdivInt = 0;
    if (8 == usTotalLane)
    {
        uiCurVboVco = uiVboPifClk * 10;
        pstKtxPllCfgParam->usTxPllPostDiv0 = 2;
    }
    else
    {
        uiCurVboVco = uiVboPifClk * 5;
        pstKtxPllCfgParam->usTxPllPostDiv0 = 1;
    }
    VboTxTaibaiVcoCalculator(uiPixClkFreq, uiCurVboVco, usTotalLane, &(ucTxPllPreDiv), &(usTxPllFbdivInt));
    pstKtxPllCfgParam->ucTxPllPreDiv = ucTxPllPreDiv;
    pstKtxPllCfgParam->usTxPllFbdivInt = usTxPllFbdivInt;
    pstKtxPllCfgParam->ucTxPllPifDiv = 5;
    pstKtxPllCfgParam->ucTxPllRdDiv = 0;
    pstKtxPllCfgParam->ucTxPllPostDiv0Div2 = 0;

    pstKtxPllCfgParam->usTxPllPostDiv2 = pstKtxPllCfgParam->usTxPllFbdivInt / pstKtxPllCfgParam->ucTxPllPreDiv;
    return HV_SUCCESS;
}

/**
 * @brief initialize sscg.
 */
VOID Hv_Drv_Sscg_Init(VOID)
{
    Hv_Cal_Sscg_Init();
    return;
}

/**
 * @brief set sscg enable.
 * @param[in] enType: vbo/edp/ddr.  bEnble: on/off
 */
VOID Hv_Drv_Sscg_Enalbe(SscgType enType, BOOL bEnble)
{
    Hv_Cal_Sscg_Enalbe(enType, bEnble);
    return;
}

/**
 * @brief set sscg freq.
  * @param[in] enType: vbo/edp/ddr.  uiFreq: modulation frequency: 0-100(k)
 */
VOID Hv_Drv_Sscg_SetSscgFreq(SscgType enType, UCHAR8 ucFreq)
{
    Hv_Cal_Sscg_SetSscgFreq(enType, ucFreq);
    return;
}
/**
 * @brief set sscg step.
  * @param[in] enType: vbo/edp/ddr.  uiFreq: modulation step: 0-30(0%-3%)
 */
VOID Hv_Drv_Sscg_SetSscgStep(SscgType enType, UCHAR8 ucStep)
{
    Hv_Cal_Sscg_SetSscgStep(enType, ucStep);
    return;
}

/**
 * @brief set sscg spread.
  * @param[in] enType: vbo/edp/ddr.
  * @param[in] ucFreq: vbo/edp/ddr.  uiFreq: modulation frequency: 0-100(k)
  * @param[in] ucStep: vbo/edp/ddr.  uiFreq: modulation step: 0-30(0%-3%)
 */
VOID Hv_Drv_Sscg_SetSscg(SscgType enType, UCHAR8 ucStep, UCHAR8 ucFreq)
{
    Hv_Cal_Sscg_SetSscg(enType, ucStep, ucFreq);
    return;
}

/**
 * @brief set sscg mode.
  * @param[in] enType: vbo/edp/ddr.  uiFreq: modulation ucMode: up/down/center
 */
VOID Hv_Drv_Sscg_SetSscgMode(SscgType enType, UCHAR8 ucMode)
{
    Hv_Cal_Sscg_SetSscgMode(enType, ucMode);
    return;
}

/**
 * @brief initialize pll.
 */

VOID Hv_Drv_PLL_Init(VOID)
{
    Hv_Cal_PLL_RxDpllInit(0);
    Hv_Cal_PLL_RxDpllInit(1);
    Hv_Cal_PLL_PtcdpllInit();
    Hv_Cal_PLL_SysPllInit();
    Hv_Cal_PLL_TxdPllInit();
    Hv_Cal_PLL_AudioPllInit();
    Hv_Cal_PLL_FNPllInit();
    return;
}

Status Hv_Drv_PLL_CheckRxDpllLock(UCHAR8 ucPortIndex)
{
    return Hv_Cal_PLL_CheckRxDpllLock(ucPortIndex);
}

Status Hv_Drv_PLL_CheckPtcdPllLock(VOID)
{
    return Hv_Cal_PLL_CheckPtcdPllLock();
}

Status Hv_Drv_PLL_CheckSysPllLock(VOID)
{
    return Hv_Cal_PLL_CheckSysPllLock();
}

Status Hv_Drv_PLL_CheckTxdPllLock(VOID)
{
    return Hv_Cal_PLL_CheckTxdPllLock();
}

Status Hv_Drv_PLL_CheckAudioPllLock(VOID)
{
    return Hv_Cal_PLL_CheckAudioPllLock();
}

Status Hv_Drv_PLL_CheckFNPllLock(VOID)
{
    return Hv_Cal_PLL_CheckFNPllLock();
}

VOID Hv_Drv_PLL_ResetRxDpll(UCHAR8 ucPortIndex)
{
    Hv_Cal_PLL_ResetRxDpll(ucPortIndex);
}

VOID Hv_Drv_PLL_ResetPtcdPll(VOID)
{
    Hv_Cal_PLL_ResetPtcdPll();
}

VOID Hv_Drv_PLL_ResetSysPll(VOID)
{
    Hv_Cal_PLL_ResetSysPll();
}

VOID Hv_Drv_PLL_ResetTxdPll(VOID)
{
    Hv_Cal_PLL_ResetTxdPll();
}

VOID Hv_Drv_PLL_ResetFNPll(VOID)
{
    Hv_Cal_PLL_ResetFNPll();
}

VOID Hv_Drv_PLL_ResetAudioPll(VOID)
{
    Hv_Cal_PLL_ResetAudioPll();
}

VOID Hv_Drv_PLL_SetPtcdpllSscgPlln(UINT32 uiPtcdFBDivInt, UINT32 uiPtcdFBDivFrac, UINT32 uiPtcdPostDiv)
{
    Hv_Cal_PLL_SetPtcdPllFbdiv(uiPtcdFBDivInt);
    Hv_Cal_PLL_SetPtcdpllSscgPllnFrac(uiPtcdFBDivFrac);
    Hv_Cal_PLL_SetPtcdpllPostdivD(uiPtcdPostDiv);
    Hv_Cal_PLL_TogglePtcdUpdate();
    Hv_Cal_PLL_FN_ClearPllLockFlag();
    Hv_Vos_Delayus(2000);
    return;
}