/**
 * @file hv_drv_EdpTxTraining.c
 * @brief EdpTxTraining module driver
 * @details This module provide interfaces for Hiview-Madia-Ware.
 * @author HiView SoC Software Team
 * @version 0.0.1
 * @date 20221111
 * @copyright Copyright(c),2022-11, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                         <th>Date             <th>Details
 * <tr><td>HiView SoC Software Team   <td>20221111       <td>Create and first Version.
 * </table>
 **/

#include "hv_drv_EdpTxTraining.h"
#include "hv_cal_EdpTxTraining.h"
#include "hv_comm_Log.h"
#include "hv_comm_Assert.h"
#include "PanelConfig.h"
#include "hv_drv_EdpTx.h"
#include "hv_cal_EdpTx.h"

/* EDP training status */
#define EDPTX_TRAINING_FAIL                    1
#define EDPTX_TRAINING_CDR_DONE                4
#define EDPTX_TRAINING_EQR_DONE                5
#define EDPTX_TRAINING_CDR_LOST                6
#define EDPTX_TRAINING_EQR_FAIL                7

/* default swing level, pre-emp level */
#define EDPTX_SWING_LEVEL_DEF             0
#define EDPTX_PRE_EMP_LEVEL_DEF           0
#define EDPTX_SWING_MAX_LEVEL             3
#define EDPTX_PRE_EMP_MAX_LEVEL           3

static Status EdpTxWriteSinkLaneAna(EdpAuxPortIndex enEdpAuxPort, UCHAR8 ucLaneCount, UCHAR8 ucSwingLevel, UCHAR8 ucPreEmpLevel);
static Status EdpTxGetSinkLinkInof(EdpAuxPortIndex enEdpAuxPort, HvEdpTxSinkLinkStatusInfo *pstSinkLinkInfo);
static VOID EdpTxGetPhyAnaMaxLevel(const HvEdpTxSinkLinkStatusInfo *pstSinkLinkInfo, UCHAR8 ucLaneCout, UCHAR8 *ucSwingLevel, UCHAR8 *ucPreEmpLevel);
static Status EdpTxLtCrTraining(const HvEdpTxTrainingInputParam *pstTrainingInputParam, HvEdpTxSinkLinkStatusInfo *pstSinkLinkStatusInfo);
static Status EdpTxLtEqTraining(const HvSinkDpcdAbility *pstSinkDpcdAbility, \
                                const HvEdpTxTrainingInputParam *pstTrainingInputParam, \
                                HvEdpTxSinkLinkStatusInfo *pstSinkLinkInfo);

static Status EdpTxWriteSinkLaneAna(EdpAuxPortIndex enEdpAuxPort, UCHAR8 ucLaneCount, UCHAR8 ucSwingLevel, UCHAR8 ucPreEmpLevel)
{
    HvEdpTxAuxTransInfo stEdpTxAuxTransInfo;
    HV_MEMSET(&stEdpTxAuxTransInfo, 0 , sizeof(stEdpTxAuxTransInfo));
    UCHAR8 ucMaxSwMaxReached = 0;
    UCHAR8 ucMaxPreEmMaxReached = 0;
    if (ucSwingLevel >= EDPTX_SWING_MAX_LEVEL)
    {
        ucMaxSwMaxReached = BIT_2;
    }
    if (ucMaxPreEmMaxReached >= EDPTX_PRE_EMP_MAX_LEVEL)
    {
        ucMaxPreEmMaxReached = BIT_5;
    }

    stEdpTxAuxTransInfo.uiAuxAddr = TRAINING_LANE0_SET;         //0x103
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    stEdpTxAuxTransInfo.aucData[0] = ucMaxPreEmMaxReached | ucMaxSwMaxReached | ucSwingLevel | (ucPreEmpLevel << 3);    // lane0
    stEdpTxAuxTransInfo.aucData[1] = ucMaxPreEmMaxReached | ucMaxSwMaxReached | ucSwingLevel | (ucPreEmpLevel << 3);    // lane1
    stEdpTxAuxTransInfo.aucData[2] = ucMaxPreEmMaxReached | ucMaxSwMaxReached | ucSwingLevel | (ucPreEmpLevel << 3);    // lane2
    stEdpTxAuxTransInfo.aucData[3] = ucMaxPreEmMaxReached | ucMaxSwMaxReached | ucSwingLevel | (ucPreEmpLevel << 3);    // lane3
    stEdpTxAuxTransInfo.ucDataLen = ucLaneCount;
    HV_LOG_DEBUG(PANEL, "EdpTxWriteSinkLaneAna aucData[0]: %x: ", stEdpTxAuxTransInfo.aucData[0]);
    HV_LOG_DEBUG(PANEL, "EdpTxWriteSinkLaneAna aucData[1]: %x: ", stEdpTxAuxTransInfo.aucData[1]);
    HV_LOG_DEBUG(PANEL, "EdpTxWriteSinkLaneAna aucData[2]: %x: ", stEdpTxAuxTransInfo.aucData[2]);
    HV_LOG_DEBUG(PANEL, "EdpTxWriteSinkLaneAna aucData[3]: %x: ", stEdpTxAuxTransInfo.aucData[3]);
    HV_ASSERT_SUCCESS(Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo));
    return HV_SUCCESS;
}

static Status EdpTxGetSinkLinkInof(EdpAuxPortIndex enEdpAuxPort, HvEdpTxSinkLinkStatusInfo *pstSinkLinkInfo)
{
    HvEdpTxAuxTransInfo stEdpTxAuxTransInfo;
    HV_MEMSET(&stEdpTxAuxTransInfo, 0 , sizeof(stEdpTxAuxTransInfo));

    /* 读:sink 0x202-0x207 LANE0-3 CR_DONE,EQ_DONE,SYMBOL_DONE. LANE_ALIGN_STATUS_UPDATED */
    stEdpTxAuxTransInfo.uiAuxAddr = LANE0_1_STATUS;   // 0x202
    stEdpTxAuxTransInfo.ucDataLen = 6;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_R;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;

    if(HV_FAILURE == Hv_Cal_EdpTx_AuxNativeMasterRead(&stEdpTxAuxTransInfo))
    {
        HV_LOG_INFO(PANEL, "EdpTx aux read fail, port %u", enEdpAuxPort);
        return HV_FAILURE;
    }
    pstSinkLinkInfo->ucLane01Status      = stEdpTxAuxTransInfo.aucData[0];                     /*DPCD 202*/
    pstSinkLinkInfo->ucLane23Status      = stEdpTxAuxTransInfo.aucData[1];                     /*DPCD 203*/
    pstSinkLinkInfo->ucLaneAlignStatus   = stEdpTxAuxTransInfo.aucData[2];                     /*DPCD 204*/
    pstSinkLinkInfo->ucSinkStatus        = stEdpTxAuxTransInfo.aucData[3];                     /*DPCD 205*/
    pstSinkLinkInfo->ucLane0SwingLevel   = stEdpTxAuxTransInfo.aucData[4] & 0x03;              /*DPCD 206*/
    pstSinkLinkInfo->ucLane0PreEmpLevel  = (stEdpTxAuxTransInfo.aucData[4] & 0x0C) >> 2;       /*DPCD 206*/
    pstSinkLinkInfo->ucLane1SwingLevel   = (stEdpTxAuxTransInfo.aucData[4] & 0x30) >> 4;       /*DPCD 206*/
    pstSinkLinkInfo->ucLane1PreEmpLevel  = (stEdpTxAuxTransInfo.aucData[4] & 0xC0) >> 6;       /*DPCD 206*/
    pstSinkLinkInfo->ucLane2SwingLevel   = stEdpTxAuxTransInfo.aucData[5] & 0x03;              /*DPCD 207*/
    pstSinkLinkInfo->ucLane2PreEmpLevel  = (stEdpTxAuxTransInfo.aucData[5] & 0x0C) >> 2;       /*DPCD 207*/
    pstSinkLinkInfo->ucLane3SwingLevel   = (stEdpTxAuxTransInfo.aucData[5] & 0x30) >> 4;       /*DPCD 207*/
    pstSinkLinkInfo->ucLane3PreEmpLevel  = (stEdpTxAuxTransInfo.aucData[5] & 0xC0) >> 6;       /*DPCD 207*/
    HV_LOG_DEBUG(PANEL, "EdpTx train status ucLane01Status:%x, ucLane23Status:%x, ucLaneAlignStatus:%x, Port: %u",  pstSinkLinkInfo->ucLane01Status, pstSinkLinkInfo->ucLane23Status, pstSinkLinkInfo->ucLaneAlignStatus, enEdpAuxPort);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane0SwingLevel: %d", pstSinkLinkInfo->ucLane0SwingLevel);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane1SwingLevel: %d", pstSinkLinkInfo->ucLane1SwingLevel);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane2SwingLevel: %d", pstSinkLinkInfo->ucLane2SwingLevel);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane3SwingLevel: %d", pstSinkLinkInfo->ucLane3SwingLevel);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane0PreEmpLevel: %d", pstSinkLinkInfo->ucLane0PreEmpLevel);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane1PreEmpLevel: %d", pstSinkLinkInfo->ucLane1PreEmpLevel);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane2PreEmpLevel: %d", pstSinkLinkInfo->ucLane2PreEmpLevel);
    HV_LOG_DEBUG(PANEL, "EdpTxGetSinkLinkInof pstSinkLinkInfo->ucLane3PreEmpLevel: %d", pstSinkLinkInfo->ucLane3PreEmpLevel);
    Hv_Vos_Delayus(70000);
    return HV_SUCCESS;
}

static Status EdptxSetTrainingFinish(VOID)
{
    HvEdpTxAuxTransInfo stEdpTxAuxTransInfo;
    HV_MEMSET(&stEdpTxAuxTransInfo, 0 , sizeof(stEdpTxAuxTransInfo));
    stEdpTxAuxTransInfo.uiAuxAddr = TRAINING_PATTERN_SELECT;        //0x102
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = EDP_AUX_PROT_INDEX0;
    stEdpTxAuxTransInfo.aucData[0] = 0;    // Training not in progress (or disabled).
    stEdpTxAuxTransInfo.ucDataLen = 1;
    if (HV_FAILURE == Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo))
    {
        HV_LOG_DEBUG(PANEL, "exit edp traing, fail port:%u", stEdpTxAuxTransInfo.enEdpAuxPortIndex);
    }
    Hv_Vos_Delayus(100);
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = EDP_AUX_PROT_INDEX1;
    if (HV_FAILURE == Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo))
    {
        HV_LOG_DEBUG(PANEL, "exit edp traing, fail port:%u", stEdpTxAuxTransInfo.enEdpAuxPortIndex);
    }
    return HV_SUCCESS;
}

static VOID EdpTxGetPhyAnaMaxLevel(const HvEdpTxSinkLinkStatusInfo *pstSinkLinkInfo, UCHAR8 ucLaneCout, UCHAR8 *ucSwingLevel, UCHAR8 *ucPreEmpLevel)
{
    UCHAR8 ucSwing = 0;
    UCHAR8 ucPreEmp = 0;

    ucSwing = pstSinkLinkInfo->ucLane0SwingLevel;
    ucPreEmp = pstSinkLinkInfo->ucLane0PreEmpLevel;

    if ((ucLaneCout & 0x0F) >= 0x02)
    {
        if (ucSwing < pstSinkLinkInfo->ucLane1SwingLevel)
        {
            ucSwing = pstSinkLinkInfo->ucLane1SwingLevel;
        }
        if (ucPreEmp < pstSinkLinkInfo->ucLane1PreEmpLevel)
        {
            ucPreEmp = pstSinkLinkInfo->ucLane1PreEmpLevel;
        }
        if ((ucLaneCout & 0x0F) == 0x04)
        {
            if (ucSwing < pstSinkLinkInfo->ucLane2SwingLevel)
            {
                ucSwing = pstSinkLinkInfo->ucLane2SwingLevel;
            }
            if (ucSwing < pstSinkLinkInfo->ucLane3SwingLevel)
            {
                ucSwing = pstSinkLinkInfo->ucLane3SwingLevel;
            }
            if (ucPreEmp < pstSinkLinkInfo->ucLane2PreEmpLevel)
            {
                ucPreEmp = pstSinkLinkInfo->ucLane2PreEmpLevel;
            }
            if (ucPreEmp < pstSinkLinkInfo->ucLane3PreEmpLevel)
            {
                ucPreEmp = pstSinkLinkInfo->ucLane3PreEmpLevel;
            }
        }
    }
    *ucSwingLevel = ucSwing;
    *ucPreEmpLevel = ucPreEmp;
    if (*ucSwingLevel > EDPTX_SWING_MAX_LEVEL)
    {
        *ucSwingLevel = EDPTX_SWING_MAX_LEVEL;
    }
    if(*ucPreEmpLevel > EDPTX_PRE_EMP_MAX_LEVEL)
    {
        *ucPreEmpLevel = EDPTX_PRE_EMP_MAX_LEVEL;
    }
    return;
}

static Status EdpTxLtCrTraining(const HvEdpTxTrainingInputParam *pstTrainingInputParam, HvEdpTxSinkLinkStatusInfo *pstSinkLinkStatusInfo)
{
    const UCHAR8 ucCrAuxDelay = 100;       /*100us*/
    UCHAR8 ucRequstCrSwingLevel = 0;       /*bit0:lane0, bit2:lane1, bit4:lane2, bit6:lane3*/
    UCHAR8 ucCurCrSwingLevel = 0;          /*bit0:lane0, bit2:lane1, bit4:lane2, bit6:lane3*/
    UCHAR8 ucChangeSwingLevelFlag = 0;
    UCHAR8 ucSwingLevel = 0;
    UCHAR8 ucPreEmpLevel = 0;
    UCHAR8 ucRetry = 0;
    HvEdpTxAuxTransInfo stEdpTxAuxTransInfo;
    HV_MEMSET(&stEdpTxAuxTransInfo, 0 , sizeof(stEdpTxAuxTransInfo));
    EdpAuxPortIndex enEdpAuxPort = pstTrainingInputParam ->enEdpAuxPortIndex;
    EdpTxPortIndex enEdpTxPort = pstTrainingInputParam ->enEdpTxPortIndex;

    Hv_Cal_EdpTx_SendIdlePatternEn(enEdpTxPort, HV_FALSE);
    /* SOURCE: send TPS1 */
    Hv_Cal_EdpTx_SendTpsPattern(enEdpTxPort, EDPTX_SEND_TPS1);

    // /*写sink 0x103的默认swing和pre-emp的等级*/
    HV_ASSERT_SUCCESS(EdpTxWriteSinkLaneAna(enEdpAuxPort, pstTrainingInputParam->ucLaneCount, EDPTX_SWING_LEVEL_DEF, EDPTX_PRE_EMP_LEVEL_DEF));
    Hv_Vos_Delayus(ucCrAuxDelay);

    /* 写: sink接收 TPS1; disable scrambler */
    stEdpTxAuxTransInfo.uiAuxAddr = TRAINING_PATTERN_SELECT;    //0x102
    stEdpTxAuxTransInfo.ucDataLen = 1;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    stEdpTxAuxTransInfo.aucData[0] = 0x21;
    HV_LOG_DEBUG(PANEL, "EdpTxLtCrTraining wite TRAINING_PATTERN_SELECT");
    HV_ASSERT_SUCCESS(Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo));
    /*尝试16次不同CR挡位*/
    for (ucRetry = 0; ucRetry < 16; ucRetry++)
    {
        if (1 != Hv_Cal_EdpTx_GetHpdStatus(pstTrainingInputParam->enEdpHpdPortIndex))
        {
            HV_LOG_INFO(PANEL, "Edp HPD low!");
            Hv_Drv_EdpTx_Enable(HV_FALSE);
            return EDPTX_TRAINING_FAIL;
        }
        if (Hv_Cal_EdpTx_GetHpdEvt(pstTrainingInputParam->enEdpHpdPortIndex))
        {
            HV_LOG_INFO(PANEL, "Edp HPD evt!");
            Hv_Drv_EdpTx_Enable(HV_FALSE);
            return EDPTX_TRAINING_FAIL;
        }
        Hv_Vos_Delayus(1000);
        HV_LOG_DEBUG(PANEL, "Cr training loop count %u", ucRetry);

        /* 读:sink 0x202-207 LANE0-3 CR_DONE,EQ_DONE,SYMBOL_DONE. LANE_ALIGN_STATUS_UPDATED */
        HV_ASSERT_SUCCESS(EdpTxGetSinkLinkInof(enEdpAuxPort, pstSinkLinkStatusInfo));

        ucRequstCrSwingLevel = (pstSinkLinkStatusInfo->ucLane0SwingLevel | (pstSinkLinkStatusInfo->ucLane1SwingLevel << 2) \
                            | (pstSinkLinkStatusInfo->ucLane2SwingLevel << 4) | (pstSinkLinkStatusInfo->ucLane3SwingLevel << 6));

        if (pstTrainingInputParam->ucLaneCount == 4)
        {
            if (((pstSinkLinkStatusInfo->ucLane01Status & 0x11) == 0x11) && ((pstSinkLinkStatusInfo->ucLane23Status & 0x11) == 0x11))
            {
                HV_LOG_INFO(PANEL, "Edp CR OK!");
                return EDPTX_TRAINING_CDR_DONE;
            }
            else
            {
                HV_LOG_ERROR(PANEL, "Edp CR Fail! Retry: %d", ucRetry + 1);
            }

            if (ucCurCrSwingLevel != ucRequstCrSwingLevel)
            {
                ucChangeSwingLevelFlag = 1;
            }
        }
        else if (pstTrainingInputParam->ucLaneCount == 2)
        {
            if ((pstSinkLinkStatusInfo->ucLane01Status & 0x11) == 0x11)
            {
                HV_LOG_INFO(PANEL, "Edp CR OK!");
                return EDPTX_TRAINING_CDR_DONE;
            }
            else
            {
                HV_LOG_ERROR(PANEL, "Edp CR Fail! Retry: %d ", ucRetry + 1);
            }

            if ((ucCurCrSwingLevel && 0x0F) != (ucRequstCrSwingLevel && 0x0F))
            {
                ucChangeSwingLevelFlag = 1;
            }
        }
        else
        {
            if ((pstSinkLinkStatusInfo->ucLane01Status & 0x01) == 0x01)
            {
                HV_LOG_INFO(PANEL, "Edp CR OK!");
                return EDPTX_TRAINING_CDR_DONE;
            }
            else
            {
                HV_LOG_ERROR(PANEL, "Edp CR Fail! Retry: %d", ucRetry + 1);
            }

            if ((ucCurCrSwingLevel && 0x03) != (ucRequstCrSwingLevel && 0x03))
            {
                ucChangeSwingLevelFlag = 1;
            }
        }
        ucCurCrSwingLevel = ucRequstCrSwingLevel;

        if (ucChangeSwingLevelFlag)
        {
            /*找出sink返回的每条lane的swing和pre-emp的最大level*/
            EdpTxGetPhyAnaMaxLevel(pstSinkLinkStatusInfo, pstTrainingInputParam->ucLaneCount, &ucSwingLevel, &ucPreEmpLevel);
            HV_LOG_INFO(PANEL, "Edp CR request SwingLevel:%d, PreEmpLevel:%d", ucSwingLevel, ucPreEmpLevel);

            /*写sink 0x103 调整后的swing和pre-emp的等级*/
            HV_ASSERT_SUCCESS(EdpTxWriteSinkLaneAna(enEdpAuxPort, pstTrainingInputParam->ucLaneCount, ucSwingLevel, ucPreEmpLevel));

            /*设置source phy的模拟参数值，根据swing和pre-emp的level，查找对应的模拟参数值*/
            Hv_Cal_EdpTx_SetSourcePhyAnaValue(ucSwingLevel, ucPreEmpLevel);
        }
        else
        {
            continue;
        }
    }
    Hv_Cal_EdpTX_SetEdptxEqDone(enEdpTxPort, HV_TRUE);
    EdptxSetTrainingFinish();
    return EDPTX_TRAINING_FAIL;
}

static Status EdpTxLtEqTraining(const HvSinkDpcdAbility *pstSinkDpcdAbility, \
                                const HvEdpTxTrainingInputParam *pstTrainingInputParam, \
                                HvEdpTxSinkLinkStatusInfo *pstSinkLinkInfo)
{
    UCHAR8 ucSourceEqMode = 0;
    UCHAR8 ucTpsType = 0;
    UCHAR8 ucSwingLevel = 0;
    UCHAR8 ucPreEmpLevel = 0;
    UCHAR8 ucRetry = 0;
    UINT32 uiEqAuxDelayUs = (pstSinkDpcdAbility->ucRdItrVal == 0) ? 400 : pstSinkDpcdAbility->ucRdItrVal * 4000;  // 0:400us, 01:4ms, 02:8ms, 03:12ms, 04:16ms
    HvEdpTxAuxTransInfo stEdpTxAuxTransInfo;
    HV_MEMSET(&stEdpTxAuxTransInfo, 0, sizeof(stEdpTxAuxTransInfo));
    EdpTxPortIndex enEdpTxPortIndex = pstTrainingInputParam->enEdpTxPortIndex;
    EdpAuxPortIndex enEdpAuxPort = pstTrainingInputParam ->enEdpAuxPortIndex;

    /*tps3, 禁用scramble*/
    if (pstSinkDpcdAbility->ucTPS3Sprt)
    {
        ucSourceEqMode = EDPTX_SEND_TPS3;
        ucTpsType = 0x23;
    }
    /*tps4, 打开scramble*/
    else if (pstSinkDpcdAbility->ucTPS4Sprt && pstSinkDpcdAbility->ucDpcdRev >= 0x14)
    {
        ucSourceEqMode = EDPTX_SEND_TPS4;
        ucTpsType = 0x07;
    }
    /*tps2, 禁用scramble*/
    else
    {
        ucSourceEqMode = EDPTX_SEND_TPS2;
        ucTpsType = 0x22;
    }
    /*发送EQ pattern，TPS2/3/4*/
    Hv_Cal_EdpTx_SendTpsPattern(enEdpTxPortIndex, ucSourceEqMode);
    if (ucTpsType == 7)
    {
        Hv_Cal_EdpTx_SetEdptxScrambDis(enEdpTxPortIndex, HV_FALSE);
    }
    else
    {
        Hv_Cal_EdpTx_SetEdptxScrambDis(enEdpTxPortIndex, HV_TRUE);
    }

    /* 写: sink TRAINING_PATTERN_SELECT接收 EQ TPS; */
    stEdpTxAuxTransInfo.uiAuxAddr = TRAINING_PATTERN_SELECT;   //0x102
    stEdpTxAuxTransInfo.ucDataLen = 1;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    stEdpTxAuxTransInfo.aucData[0] = ucTpsType;
    HV_LOG_DEBUG(PANEL, "Edp EQ wite TRAINING_PATTERN_SELECT: %x", ucTpsType);
    HV_ASSERT_SUCCESS(Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo));
    /*尝试16次不同EQ挡位*/
    for (ucRetry = 0; ucRetry < 16; ucRetry++)
    {

        if (1 != Hv_Cal_EdpTx_GetHpdStatus(pstTrainingInputParam->enEdpHpdPortIndex))
        {
            HV_LOG_INFO(PANEL, "Edp HPD low!");
            Hv_Drv_EdpTx_Enable(HV_FALSE);
            return EDPTX_TRAINING_FAIL;
        }
        if (Hv_Cal_EdpTx_GetHpdEvt(pstTrainingInputParam->enEdpHpdPortIndex))
        {
            HV_LOG_INFO(PANEL, "Edp HPD evt!");
            Hv_Drv_EdpTx_Enable(HV_FALSE);
            return EDPTX_TRAINING_FAIL;
        }
        Hv_Vos_Delayus(uiEqAuxDelayUs);
        HV_LOG_DEBUG(PANEL, "Eq training loop count %u, uiEqAuxDelayUs:%u", ucRetry, uiEqAuxDelayUs);

        /* 读:sink 0x202-0x207 LANE0-3 CR_DONE,EQ_DONE,SYMBOL_DONE. LANE_ALIGN_STATUS_UPDATED */
        HV_ASSERT_SUCCESS(EdpTxGetSinkLinkInof(enEdpAuxPort, pstSinkLinkInfo));
        if (pstTrainingInputParam->ucLaneCount == 4)
        {
            if (((pstSinkLinkInfo->ucLane01Status & 0x77) == 0x77) && ((pstSinkLinkInfo->ucLane23Status & 0x77) == 0x77))
            {
                HV_LOG_INFO(PANEL, "Edp EQ OK!");
                return EDPTX_TRAINING_EQR_DONE;
            }
            else if (((pstSinkLinkInfo->ucLane01Status & 0x11) != 0x11) && ((pstSinkLinkInfo->ucLane23Status & 0x11) != 0x11))
            {
                HV_LOG_ERROR(PANEL, "EDP CR Lost!");
                break;
            }
            else
            {
                HV_LOG_ERROR(PANEL, "Edp EQ Fail! Retry: %d", ucRetry + 1);
            }
        }
        else if (pstTrainingInputParam->ucLaneCount == 2)
        {
            if ((pstSinkLinkInfo->ucLane01Status & 0x77) == 0x77)
            {
                HV_LOG_INFO(PANEL, "Edp EQ OK!");
                return EDPTX_TRAINING_EQR_DONE;
            }
            else if ((pstSinkLinkInfo->ucLane01Status & 0x11) != 0x11)
            {
                HV_LOG_ERROR(PANEL, "Edp CR Lost!");
                break;
            }
            else
            {
                HV_LOG_ERROR(PANEL, "Edp EQ Fail! Retry: %d", ucRetry + 1);
            }
        }
        else
        {
            if ((pstSinkLinkInfo->ucLane01Status & 0x07) == 0x07)
            {
                HV_LOG_INFO(PANEL, "Edp EQ OK!");
                Hv_Cal_EdpTX_SetEdptxEqDone(enEdpTxPortIndex, HV_TRUE);
                return EDPTX_TRAINING_EQR_DONE;
            }
            else if ((pstSinkLinkInfo->ucLane01Status & 0x01) != 0x01)
            {
                HV_LOG_ERROR(PANEL, "Edp CR Lost!");
                break;
            }
            else
            {
                HV_LOG_ERROR(PANEL, "Edp EQ Fail! Retry: %d", ucRetry + 1);
            }
        }
        /*找出sink返回的每条lane的swing和pre-emp的最大level*/
        EdpTxGetPhyAnaMaxLevel(pstSinkLinkInfo, pstTrainingInputParam->ucLaneCount, &ucSwingLevel, &ucPreEmpLevel);
        HV_LOG_INFO(PANEL, "Edp EQ request SwingLevel:%d, PreEmpLevel:%d", ucSwingLevel, ucPreEmpLevel);
        /*设置source phy的模拟参数值，根据swing和pre-emp的level，查找对应的模拟参数值*/
        Hv_Cal_EdpTx_SetSourcePhyAnaValue(ucSwingLevel, ucPreEmpLevel);
        /*写sink 0x103 调整后的swing和pre-emp的等级*/
        HV_ASSERT_SUCCESS(EdpTxWriteSinkLaneAna(enEdpAuxPort, pstTrainingInputParam->ucLaneCount, ucSwingLevel, ucPreEmpLevel));
    }
    Hv_Cal_EdpTX_SetEdptxEqDone(enEdpTxPortIndex, HV_TRUE);
    EdptxSetTrainingFinish();
    return EDPTX_TRAINING_EQR_FAIL;
}

Status Hv_Drv_EdpTx_NoLinkTrainingStatus(VOID)
{
    HvEdpTxSinkLinkStatusInfo stLinkStatusInfoZero = {0};
    HvEdpTxSinkLinkStatusInfo stLinkStatusInfoFirst = {0};
    EdpTxGetSinkLinkInof(1, &stLinkStatusInfoFirst);
    EdpTxGetSinkLinkInof(0, &stLinkStatusInfoZero);
    USHORT16 usLaneNum = Hv_Cal_EdpTX_GetKtxPairNum();
    BOOL bTrainingStatus = HV_FAILURE;
    if (usLaneNum == 8)
    {
        if ((stLinkStatusInfoZero.ucLane01Status == 119) && (stLinkStatusInfoZero.ucLane23Status == 119) \
         && (stLinkStatusInfoFirst.ucLane01Status == 119) && (stLinkStatusInfoFirst.ucLane23Status == 119))
         //&&((stLinkStatusInfoZero.ucLaneAlignStatus & BIT_0) == 1) && ((stLinkStatusInfoFirst.ucLaneAlignStatus & BIT_0) == 1))
         /* TODO: 需要参考9810代码，考虑是全部掉INTERLANE_ALIGN_DONE判断，还是通过aux、屏参来做区分判断*/
        {
            bTrainingStatus = HV_SUCCESS;
        }
        else
        {
            bTrainingStatus = HV_FAILURE;
        }
    }
    else if (usLaneNum == 4)
    {
        if (((stLinkStatusInfoZero.ucLane01Status == 119) && (stLinkStatusInfoZero.ucLane23Status == 119))
        //&&((stLinkStatusInfoZero.ucLaneAlignStatus & BIT_0) == 1))
        || ((stLinkStatusInfoFirst.ucLane01Status == 119) && (stLinkStatusInfoFirst.ucLane23Status == 119)))
        //&&((stLinkStatusInfoFirst.ucLaneAlignStatus & BIT_0) == 1)))
        {
            bTrainingStatus = HV_SUCCESS;
        }
        else
        {
            bTrainingStatus = HV_FAILURE;
        }
    }
    else if (usLaneNum == 2)
    {
        if (((stLinkStatusInfoZero.ucLane01Status == 119) || (stLinkStatusInfoZero.ucLane23Status == 119)))
        //&&((stLinkStatusInfoZero.ucLaneAlignStatus & BIT_0) == 1))
        {
            bTrainingStatus = HV_SUCCESS;
        }
        else
        {
            bTrainingStatus = HV_FAILURE;
        }
    }
    else
    {
        if (((stLinkStatusInfoZero.ucLane01Status & 0x7) || (stLinkStatusInfoZero.ucLane01Status & 0x70)\
            || (stLinkStatusInfoZero.ucLane23Status & 0x7) || (stLinkStatusInfoZero.ucLane23Status & 0x70)))
            //&&((stLinkStatusInfoZero.ucLaneAlignStatus & BIT_0) == 1))
        {
            bTrainingStatus = HV_SUCCESS;
        }
        else
        {
            bTrainingStatus = HV_FAILURE;
        }
    }
    if (bTrainingStatus == HV_SUCCESS)
    {
        EdptxSetTrainingFinish();
    }
    return bTrainingStatus;
}

VOID Hv_Drv_EdpTx_SetAssrEn(EdpTxPortIndex enEdpTxPort, EdpAuxPortIndex enAuxPort)
{
#if HV_CONFIG_ON == HV_PROJECT_CONFIG_EDP_TX_ASSR_EN
    HvEdpTxAuxTransInfo stEdpTxAuxTransInfo = {0};
    stEdpTxAuxTransInfo.uiAuxAddr = 0x00D;
    stEdpTxAuxTransInfo.ucDataLen = 1;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_R;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enAuxPort;
    Hv_Cal_EdpTx_AuxNativeMasterRead(&stEdpTxAuxTransInfo);

    if (stEdpTxAuxTransInfo.aucData[0] & BIT_0)
    {
        /*sink支持ASSR*/
        stEdpTxAuxTransInfo.uiAuxAddr = 0x10A;
        stEdpTxAuxTransInfo.aucData[0] = BIT_0;
        Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo);
        Hv_Cal_EdpTx_SetAssrEn(enEdpTxPort, HV_TRUE);
    }
    else
    {
        /*sink不支持支持ASSR*/
        stEdpTxAuxTransInfo.uiAuxAddr = 0x10A;
        stEdpTxAuxTransInfo.aucData[0] = 0;
        Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo);
        Hv_Cal_EdpTx_SetAssrEn(enEdpTxPort, HV_FALSE);
    }
#endif
    return;     
}

BOOL Hv_Drv_EdpTx_ExecuteTraining(HvEdpTxTrainingInputParam *pstTrainingInputParam)
{
    HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining ");

    HV_LOG_DEBUG(PANEL, "EDP LaneCount:%d, LinkRate:%d, EdpTxPort: %d, AuxPort:%d, HpdPort:%d", pstTrainingInputParam->ucLaneCount, pstTrainingInputParam->ucLinkRate, \
    pstTrainingInputParam->enEdpTxPortIndex, pstTrainingInputParam->enEdpAuxPortIndex, pstTrainingInputParam->enEdpHpdPortIndex);
    EdpTxPortIndex enEdpTxPortIndex = pstTrainingInputParam->enEdpTxPortIndex;
    EdpAuxPortIndex enEdpAuxPort = pstTrainingInputParam->enEdpAuxPortIndex;
    HvEdpTxAuxTransInfo stEdpTxAuxTransInfo = {0};
    HvSinkDpcdAbility stSinkDpcdAbility = {0};
    UCHAR8 ucCnt = 0;
    UCHAR8 ucTrainingStatus = EDPTX_TRAINING_FAIL;

    Hv_Cal_EdpTx_InitLink(enEdpTxPortIndex, pstTrainingInputParam->ucLaneCount);

    /*ASSR*/
    Hv_Drv_EdpTx_SetAssrEn(enEdpTxPortIndex, enEdpAuxPort);

    /*write: Wake Down Stream Device, set sink to D0 (normal operation mode)*/
    stEdpTxAuxTransInfo.uiAuxAddr = SET_DP_PWR_VOLTAGE;     //0x600
    stEdpTxAuxTransInfo.ucDataLen = 1;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.aucData[0] = 0x01;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining write SET_DP_PWR_VOLTAGE");
    HV_LOG_DEBUG(PANEL, "stEdpTxAuxTransInfo.ucDataLen: %d ", stEdpTxAuxTransInfo.ucDataLen);
    Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo);
    Hv_Vos_Delayus(1000);

    /* read: get sink count */
    // stEdpTxAuxTransInfo.uiAuxAddr = SINK_COUNT;             //0x200
    // stEdpTxAuxTransInfo.ucDataLen = 1;
    // stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_R;
    // stEdpTxAuxTransInfo.aucData[0] = 0x0;
    // stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    // HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining read SINK_COUNT");
    // Hv_Cal_EdpTx_AuxNativeMasterRead(&stEdpTxAuxTransInfo);

    /* wirte: sink is a presence device for source */
    stEdpTxAuxTransInfo.uiAuxAddr = MSTM_CTRL;                  // 0x111
    stEdpTxAuxTransInfo.ucDataLen = 1;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.aucData[0] = 0x04;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining write MSTM_CTRL");
    //Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo);

    /* write: clear sink DPCD */
    stEdpTxAuxTransInfo.uiAuxAddr = LINK_BW_SET;        // 0x100
    stEdpTxAuxTransInfo.ucDataLen = 2;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.aucData[0] = 0;
    stEdpTxAuxTransInfo.aucData[1] = 0;
    HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining write LINK_BW_SET");
    Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo);
    Hv_Vos_Delayus(1000);

    /* read: 00 DPCD_REV; 01 LINK_RATE; 02 LANE_COUNT; 03 MAX_DOWNSPREAD */
    stEdpTxAuxTransInfo.uiAuxAddr = DPCD_REV;
    stEdpTxAuxTransInfo.ucDataLen = 4;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_R;
    for (ucCnt=0; ucCnt<4; ucCnt++)
    {
        stEdpTxAuxTransInfo.aucData[ucCnt] = 0x0;
    }
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining read DPCD_REV");
    Hv_Cal_EdpTx_AuxNativeMasterRead(&stEdpTxAuxTransInfo);
    stSinkDpcdAbility.ucDpcdRev = stEdpTxAuxTransInfo.aucData[0];
    stSinkDpcdAbility.ucLinkRate = stEdpTxAuxTransInfo.aucData[1];
    stSinkDpcdAbility.ucLaneCount = stEdpTxAuxTransInfo.aucData[2] & 0x1F;             /* LANE COUNT */
    stSinkDpcdAbility.ucTPS3Sprt = (stEdpTxAuxTransInfo.aucData[2] & 0x40) >> 6;       /* TPS3 */
    stSinkDpcdAbility.ucEnhnFrame = (stEdpTxAuxTransInfo.aucData[2] & 0x80) >> 7;      /* Enhanced frame symbol */
    stSinkDpcdAbility.ucTPS4Sprt = (stEdpTxAuxTransInfo.aucData[3] & 0x80) >> 7;       /* TPS4 */
    HV_LOG_DEBUG(PANEL, "edp read DPCD_REV:%x, LinkRate:%u, LaneCount:%u, TPS3Sprt:%u, EnhnFrame:%u, TPS4Sprt:%u", stSinkDpcdAbility.ucDpcdRev, \
    stSinkDpcdAbility.ucLinkRate, stSinkDpcdAbility.ucLaneCount, stSinkDpcdAbility.ucTPS3Sprt, stSinkDpcdAbility.ucEnhnFrame, stSinkDpcdAbility.ucTPS4Sprt);

    /* read: AUX_RD_INTERVAL */
    stEdpTxAuxTransInfo.uiAuxAddr = TRAINING_AUX_RD_INTERVAL;       //0x0E
    stEdpTxAuxTransInfo.ucDataLen = 1;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_R;
    stEdpTxAuxTransInfo.aucData[0] = 0x0;
    stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
    HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining read TRAINING_AUX_RD_INTERVAL");
    Hv_Cal_EdpTx_AuxNativeMasterRead(&stEdpTxAuxTransInfo);
    stSinkDpcdAbility.ucRdItrVal = stEdpTxAuxTransInfo.aucData[0] & 0x7F;
    stSinkDpcdAbility.ucRdItrVal = stSinkDpcdAbility.ucRdItrVal > 4 ? 4 : stSinkDpcdAbility.ucRdItrVal;
    Hv_Vos_Delayus(1000);

    /* write: 100 LANE_RATE; 101 LANE_COUNT */
    stEdpTxAuxTransInfo.uiAuxAddr = LINK_BW_SET;        //0x0100
    stEdpTxAuxTransInfo.ucDataLen = 2;
    stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
    stEdpTxAuxTransInfo.aucData[0] = pstTrainingInputParam->ucLinkRate;
    stEdpTxAuxTransInfo.aucData[1] = (stSinkDpcdAbility.ucEnhnFrame << 7) | (pstTrainingInputParam->ucLaneCount & 0x1F);
    HV_LOG_DEBUG(PANEL, "Hv_Drv_EdpTx_ExecuteTraining write LINK_BW_SET");
    Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo);
    Hv_Vos_Delayus(1000);

    //SSC

    HvEdpTxSinkLinkStatusInfo stSinkLinkStatusInfo;
    HV_MEMSET(&stSinkLinkStatusInfo, 0, sizeof(stSinkLinkStatusInfo));
    ucTrainingStatus = EdpTxLtCrTraining(pstTrainingInputParam, &stSinkLinkStatusInfo);

    if (ucTrainingStatus == EDPTX_TRAINING_CDR_DONE)
    {
        ucTrainingStatus = EdpTxLtEqTraining(&stSinkDpcdAbility, pstTrainingInputParam, &stSinkLinkStatusInfo);
        if (ucTrainingStatus == EDPTX_TRAINING_EQR_DONE)
        {
            Hv_Cal_EdpTX_SetEdptxEqDone(enEdpTxPortIndex, HV_TRUE);
            Hv_Cal_EdpTx_SetEdptxScrambDis(enEdpTxPortIndex, 0);

            /* write: TPS scrambling off */
            stEdpTxAuxTransInfo.uiAuxAddr = TRAINING_PATTERN_SELECT;    //0x102
            stEdpTxAuxTransInfo.ucDataLen = 1;
            stEdpTxAuxTransInfo.ucAuxCmd = EDPTX_NATIVE_AUX_W;
            stEdpTxAuxTransInfo.enEdpAuxPortIndex = enEdpAuxPort;
            stEdpTxAuxTransInfo.aucData[0] = 0;
            HV_LOG_DEBUG(PANEL, "EdpTxLtEqDone write TRAINING_PATTERN_SELECT");
            Hv_Cal_EdpTx_AuxNativeMasterWrite(&stEdpTxAuxTransInfo);
            return HV_TRUE;
        }
        else if (ucTrainingStatus == EDPTX_TRAINING_CDR_LOST)
        {
            HV_LOG_INFO(PANEL, "CDR_LOST retry EdpTxLtCrTraining");
            //Hv_Cal_EdpTX_RetEdptxEnable(0, 0);
            //EdpTxLtCrTraining(pstTrainingInputParam, &stSinkLinkStatusInfo);
        }
        else
        {
            HV_LOG_ERROR(PANEL, "EDP_TX EQ training fail!");
        }
    }
    else
    {
        HV_LOG_ERROR(PANEL, "EDP_TX CR training fail!");
        Hv_Cal_EdpTx_Tps1Done(1);
        Hv_Cal_EdpTx_EqDone(1);
        Hv_Cal_EdpTx_SetEdptxScrambDis(EDP_TX_PROT_INDEX0, HV_FALSE);
        Hv_Cal_EdpTx_SetEdptxScrambDis(EDP_TX_PROT_INDEX1, HV_FALSE);
    }

    return HV_FALSE;
}
