/**
 * @file hv_mem_Parser.c
 * @brief get pq table offset & size
 *
 * @verbatim
 * ==============================================================================
 *                        ##### How to use #####
 * ==============================================================================
 *
 *
 * @endverbatim
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2023-05-18
 */

#include "hv_comm_MemParser.h"
#include "hv_chip_Memory.h"
#include "ProjectConfig.h"
#include "hv_drv_Osd.h"
#include "hv_drv_Flash.h"

/* pq partition has decompressed or not */
static CHAR8 s_cPartDecompressed = 0;


__attribute__((long_call)) extern INT32 Hv_Boot_LzssDecompress(char *pcSrc, char *pcDst, INT32 iChanegeEndian, INT32 iSize);
__attribute__((long_call))unsigned char Hv_Ddr_Type(void);

/**
 * @brief get pq table addr & size
  * @param[in] enType: PqTableType
  * @param[in] uiIndex: index
  * @param[out] puiAddr: table addr
  * @param[out] puiSize: table size
 */
 Status Hv_Pq_GetTable(PqTableType enType, UINT32 uiIndex, UINT32 *puiAddr, UINT32 *puiSize)
 {
    UINT32 uiTempAddr   = 0;
    UINT32 uiStartAddr  = 0;
    UINT32 uiTempType   = 0;
    UINT32 uiTempInode  = 0;
    UINT32 uiOffset     = 0;

    uiTempAddr = HV_MEMORY_CONFIG_PQ_DATA_START;
    uiStartAddr = uiTempAddr;
    HV_LOGV("base addr %x find for %d %d\n", uiTempAddr, enType, uiIndex);

    for (;;)
    {
        HV_LOGV("header addr %x\n", uiTempAddr);
        if (uiTempAddr > uiStartAddr + 0x1000)
        {
            HV_LOGW("no valid head for type %d, %d", enType, uiIndex);
        }
        /* check header magic */
        if (*(UCHAR8 *)(uiTempAddr) != 'H')
        {
            return HV_FAILURE;
        }
        if (*(UCHAR8 *)(uiTempAddr + 1) != 'E')
        {
            return HV_FAILURE;
        }
        if (*(UCHAR8 *)(uiTempAddr + 2) != 'A')
        {
            return HV_FAILURE;
        }
        if (*(UCHAR8 *)(uiTempAddr + 3) != 'D')
        {
            return HV_FAILURE;
        }

        /* check type */
        uiTempType  = *(UCHAR8 *)(uiTempAddr + 4) << 8;
        uiTempType |= *(UCHAR8 *)(uiTempAddr + 5) << 0;
        uiTempInode = *(UCHAR8 *)(uiTempAddr + 6) << 8;
        uiTempInode |= *(UCHAR8 *)(uiTempAddr + 7) << 0;
        HV_LOGV("header need %d, act %d \n", enType, uiTempType);
        if (uiTempType < enType)
        {
            /* get next header */
            uiTempAddr = uiTempAddr + 0x10;
            continue;
        }
        else if (uiTempType > enType)
        {
            break;
        }
        else
        {
            if (uiTempInode < uiIndex)
            {
                /* get next header */
                uiTempAddr = uiTempAddr + 0x10;
                continue;
            }
            else if (uiTempInode > uiIndex)
            {
                break;
            }
            else
            {
                *puiSize     = *(UCHAR8 *)(uiTempAddr + 8) << 24;
                *puiSize    |= *(UCHAR8 *)(uiTempAddr + 9) << 16;
                *puiSize    |= *(UCHAR8 *)(uiTempAddr + 10) << 8;
                *puiSize    |= *(UCHAR8 *)(uiTempAddr + 11) << 0;
                uiOffset     = *(UCHAR8 *)(uiTempAddr + 12) << 24;
                uiOffset    |= *(UCHAR8 *)(uiTempAddr + 13) << 16;
                uiOffset    |= *(UCHAR8 *)(uiTempAddr + 14) << 8;
                uiOffset    |= *(UCHAR8 *)(uiTempAddr + 15) << 0;
                *puiAddr     = uiOffset + HV_MEMORY_CONFIG_PQ_DATA_START;
                HV_LOGV("header uiOffset %x, size %d \n", uiOffset, *puiSize);
                return HV_SUCCESS;
            }
        }
    }
    return HV_FAILURE;
 }
/**
 * @brief show pq table addr & size
 */
 Status Hv_Pq_ShowTable(void)
 {
    UINT32 uiTempAddr   = 0;
    UINT32 uiStartAddr  = 0;
    UINT32 uiTempType   = 0;
    UINT32 uiTempInode  = 0;
    UINT32 uiOffset     = 0;
    UINT32 uiSize       = 0;

    uiTempAddr = HV_MEMORY_CONFIG_PQ_DATA_START;
    uiStartAddr = uiTempAddr;
    HV_LOGI("base addr %x\n", uiTempAddr);

    for (;;)
    {
        HV_LOGI("header addr %x\n", uiTempAddr);
        if (uiTempAddr > uiStartAddr + 0x1000)
        {
            break;
        }
        /* check header magic */
        if (*(UCHAR8 *)(uiTempAddr) != 'H')
        {
            return HV_FAILURE;
        }
        if (*(UCHAR8 *)(uiTempAddr + 1) != 'E')
        {
            return HV_FAILURE;
        }
        if (*(UCHAR8 *)(uiTempAddr + 2) != 'A')
        {
            return HV_FAILURE;
        }
        if (*(UCHAR8 *)(uiTempAddr + 3) != 'D')
        {
            return HV_FAILURE;
        }

        /* check type */
        uiTempType  = *(UCHAR8 *)(uiTempAddr + 4) << 8;
        uiTempType  |= *(UCHAR8 *)(uiTempAddr + 5) << 0;
        uiTempInode = *(UCHAR8 *)(uiTempAddr + 6) << 8;
        uiTempInode |= *(UCHAR8 *)(uiTempAddr + 7) << 0;
        uiSize       = *(UCHAR8 *)(uiTempAddr + 8) << 24;
        uiSize      |= *(UCHAR8 *)(uiTempAddr + 9) << 16;
        uiSize      |= *(UCHAR8 *)(uiTempAddr + 10) << 8;
        uiSize      |= *(UCHAR8 *)(uiTempAddr + 11) << 0;
        uiOffset     = *(UCHAR8 *)(uiTempAddr + 12) << 24;
        uiOffset    |= *(UCHAR8 *)(uiTempAddr + 13) << 16;
        uiOffset    |= *(UCHAR8 *)(uiTempAddr + 14) << 8;
        uiOffset    |= *(UCHAR8 *)(uiTempAddr + 15) << 0;
        HV_LOGI("header %d, index %d, offset %x, size %x\n", uiTempType, uiTempInode, uiOffset, uiSize);
        uiTempAddr = uiTempAddr + 0x10;
    }
    return HV_SUCCESS;
 }
/**
 * @brief get code superblock version.
 */
UINT32 Hv_Comm_GetCodeVersion(void)
{
    UINT32 uiVersion    = 0;
    UCHAR8 aucData[4]   = {0};

    Hv_Drv_Flash_OnlyRead((HV_FLASH_CONFIG_SUPERBLOCK_START + 0x60 + 0x1c), aucData, 4);
    uiVersion = aucData[0]<<24 | aucData[1]<<16 | aucData[2]<<8 | aucData[3];
    HV_LOGV(">>> get version %x\n", uiVersion);
    return uiVersion;
}

void Hv_Comm_Copy(INT32 *iSrc, INT32 *iDst, INT32 iSize)
{
    INT32 iLoop = 0;

    for (iLoop=0; iLoop<iSize/4; iLoop++)
    {
        *iDst = *iSrc;
        iSrc++;
        iDst++;
    }
    return;
}

void Hv_Comm_CopyLogoData(void)
{

    Hv_Comm_Copy((INT32 *)(HV_FLASH_CONFIG_LOGO_PART_START+ HV_FLASH_CONFIG_START_XIP),(INT32 *)(HV_MEMORY_CONFIG_OSD_LOGO_DDR_START), HV_FLASH_CONFIG_LOGO_PART_SIZE);
    return;
}

UINT32 Hv_Board_Memory_CalculateOsdBlendSize(VOID)
{
#if (HV_PROJECT_CONFIG_OSD_DDRPATH == HV_CONFIG_ON)
    return (PANEL_OSD_H_ACTIVE * PANEL_OSD_V_ACTIVE * HV_OSD_CONFIG_DDR_PIXEL_BYTES * 2);
#else
    return 0;
#endif
}

UINT32 Hv_Board_Memory_CalculateOsdRotationSize(VOID)
{
#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
    return (HV_OSD_CONFIG_ROTATION_H_ACTIVE * HV_OSD_CONFIG_ROTATION_V_ACTIVE * HV_OSD_CONFIG_DDR_PIXEL_BYTES);
#else
    return 0;
#endif
}

UINT32 Hv_Board_Memory_CalculateOsdGraphicSize(VOID)
{
#if (HV_PROJECT_CONFIG_OSD_GRAPHIC == HV_CONFIG_ON)
    return (HV_OSD_CONFIG_GRAPHIC_H_ACTIVE * HV_OSD_CONFIG_GRAPHIC_V_ACTIVE * HV_OSD_CONFIG_GRAPHIC_PIXEL_BYTES);
#else
    return 0;
#endif
}

UINT32 Hv_Board_Memory_CalculateSizeForOsd(VOID)
{
    UINT32 uiDDRSize = Hv_Board_Memory_CalculateOsdBlendSize();
    UINT32 uiRotationSize = Hv_Board_Memory_CalculateOsdRotationSize();
    UINT32 uiGraphicSize = Hv_Board_Memory_CalculateOsdGraphicSize();

    HV_LOGV("Osd DDR size: %u", uiDDRSize);
    HV_LOGV("OSD Rotation Size: %u", uiRotationSize);
    HV_LOGV("OSD Graphic Size: %u", uiGraphicSize);

    return uiDDRSize + uiRotationSize + uiGraphicSize;
}

FLOAT32 Hv_Board_Memory_CalculateBandForOsd(UINT32 uiVideoFrameRate)
{
    UCHAR8 ucOsdFrameRate = 0;
    FLOAT32 fBandWidth = 0;
    UINT32 fBandDDRR = 0;
    UINT32 fBandDDRW = 0;
    UINT32 fBandGraphic = 0;
    UCHAR8 ucBits = 0;

    ucOsdFrameRate = Hv_Drv_OsdFrame_GetOsdFrameRate(uiVideoFrameRate, HV_FALSE);
#if (HV_PROJECT_CONFIG_OSD_DDRPATH == HV_CONFIG_ON)
    ucBits = Hv_Drv_OsdCommon_GetPixelBits(HV_OSD_CONFIG_DDR_FORMAT);
    fBandDDRW = (ucOsdFrameRate * PANEL_OSD_H_ACTIVE * PANEL_OSD_V_TOTAL * ucBits) / 8;
    HV_LOGV("DDR Write Bandwidth: %u", fBandDDRW);
    fBandDDRW /= 1000000;

    fBandDDRR = (uiVideoFrameRate * PANEL_OSD_H_ACTIVE * PANEL_V_TOTAL_TYP * ucBits) / 8;
    HV_LOGV("DDR Read Bandwidth: %u", fBandDDRR);
    fBandDDRR /= 1000000;
#endif /* HV_PROJECT_CONFIG_OSD_DDRPATH */

#if (HV_PROJECT_CONFIG_OSD_GRAPHIC == HV_CONFIG_ON)
    ucBits = Hv_Drv_OsdCommon_GetPixelBits(HV_OSD_CONFIG_GRAPHIC_FORMAT);
    fBandGraphic = (ucOsdFrameRate * PANEL_OSD_H_ACTIVE * PANEL_OSD_V_TOTAL * ucBits) / 8;
    HV_LOGV("Graphic Read Bandwidth: %u", fBandGraphic);
    fBandGraphic /= 1000000;
#endif

    fBandWidth = fBandDDRR + fBandDDRW + fBandGraphic;
    return fBandWidth;
}

/* 3840*2250*24/8/5.83 */
UINT32  Hv_Board_Memory_CalODSize(VOID)
{

    FLOAT32  ufOdSize   = 0;
    UINT32 uiOdSize     = 0;
    #ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_UHD
    uiOdSize = 0x413000;
    #endif
    #ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_QHD
    uiOdSize = 0x1D0000;
    #endif
    #ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_FHD
    uiOdSize = 0x110000;
    #endif
    #ifdef CONFIG_USER_PANEL_RESOLUTION_TYPE_CUSTOMIZE
    ufOdSize = HV_PROJECT_CONFIG_PANEL_H_ACTIVE_CUSTOMIZE * HV_PROJECT_CONFIG_PANEL_V_ACTIVE_CUSTOMIZE * 1.1 * 24 /8 /5.83;
    uiOdSize = ((UINT32)ufOdSize + 0xFFF)  >> 12 << 12;
    HV_LOGI("OD size: %x\n", uiOdSize);
    #endif

    return uiOdSize;
}
/* 3840*2250*24/8/5.83 */
FLOAT32  Hv_Board_Memory_CalODBand(VOID)
{

    FLOAT32  ufOdBand   = 0;

    #ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_UHD
    ufOdBand = 71.14;
    #endif
    #ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_QHD
    ufOdBand = 31.20;
    #endif
    #ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_FHD
    ufOdBand = 17.79;
    #endif
    #ifdef CONFIG_USER_PANEL_RESOLUTION_TYPE_CUSTOMIZE
    ufOdBand = HV_PROJECT_CONFIG_PANEL_H_ACTIVE_CUSTOMIZE * HV_PROJECT_CONFIG_PANEL_V_ACTIVE_CUSTOMIZE * 1.1 *24 *2 /5.83 /1000 /1000;
    HV_LOGI("OD band: %x\n", ufOdBand);
    #endif

    return ufOdBand;
}

/* 30*481*271/8 */
UINT32  Hv_Board_Memory_CalDemuraSize(VOID)
{

    FLOAT32  ufDemuraSize   = 0;
    UINT32 uiDemuraSize     = 0;
#ifdef DEMURA_COLOR_MODE
    UCHAR8 ucColorModeMul = (DEMURA_COLOR_MODE == 0) ? 1 : 3;
#else
    UCHAR8 ucColorModeMul = 1;
#endif

#ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_UHD
    uiDemuraSize = 0x80000;
#endif
#ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_QHD
    uiDemuraSize = 0x36000;
#endif
#ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_FHD
    uiDemuraSize = 0x1F000;
#endif

#ifdef CONFIG_USER_PANEL_RESOLUTION_TYPE_CUSTOMIZE
    uiDemuraSize = (HV_PROJECT_CONFIG_PANEL_H_ACTIVE_CUSTOMIZE+7)/8 * (HV_PROJECT_CONFIG_PANEL_V_ACTIVE_CUSTOMIZE+7)/8;
    ufDemuraSize = uiDemuraSize * 1.1 * 30 /8;
    uiDemuraSize = ((UINT32)ufDemuraSize + 0xFFF)  >> 12 << 12;
    HV_LOGI("Demura size: %x\n", uiDemuraSize);
#endif

    return (uiDemuraSize * ucColorModeMul);
}
/* 321*186*30*Freq/1000/1000 */
FLOAT32  Hv_Board_Memory_CalDemuraBand(VOID)
{

    FLOAT32  ufDemuraBand   = 0;
#ifdef DEMURA_COLOR_MODE
    UCHAR8 ucColorModeMul = (DEMURA_COLOR_MODE == 0) ? 1 : 3;
#else
    UCHAR8 ucColorModeMul = 1;
#endif

#ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_UHD
    ufDemuraBand = 4.07;
#endif
#ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_QHD
    ufDemuraBand = 1.80;
#endif
#ifdef  CONFIG_USER_PANEL_RESOLUTION_TYPE_FHD
    ufDemuraBand = 1.03;
#endif
#ifdef CONFIG_USER_PANEL_RESOLUTION_TYPE_CUSTOMIZE
    ufDemuraBand = (HV_PROJECT_CONFIG_PANEL_H_ACTIVE_CUSTOMIZE+7)/8 * (HV_PROJECT_CONFIG_PANEL_V_ACTIVE_CUSTOMIZE+7)/8 * 1.1 *30 /1000 /1000;
    HV_LOGI("Demura band: %x\n", ufDemuraBand);
#endif

    return (ufDemuraBand * ucColorModeMul);
}


/* return address */
UINT32  Hv_Board_Memory_FRCStart(void)
{
    UINT32  uiDDRStart = 0;
    UINT32  uiOsdSize = 0;
    UINT32  uiOdSize = 0;
    uiDDRStart += HV_MEMORY_CONFIG_STATIC_DDR_SIZE;
    uiDDRStart += Hv_Board_Memory_CalDemuraSize();

    uiOsdSize = Hv_Board_Memory_CalculateSizeForOsd();
    uiOdSize  = Hv_Board_Memory_CalODSize();
    if (uiOsdSize < uiOdSize)
    {
        uiOsdSize = uiOdSize;
    }
    uiDDRStart += uiOsdSize;
    return PHY_TO_VDM(uiDDRStart);
}

/* return bytes */
UINT32 Hv_Board_Memory_RemainSizeForFRC(void)
{
    UINT32 uiFRCSize = 0;
    uiFRCSize = PHY_TO_VDM(HV_MEMORY_CONFIG_DDR_SIZE) - Hv_Board_Memory_FRCStart();
    return uiFRCSize;
}
/* return xxMbit/s */
UINT32 Hv_Board_Memory_RemainBandForFRC(UINT32 PanelFreq)
{
    float fDDRBand = 0;
    float fOsdBand = 0;
    float fOdBand  = 0;
    UINT32 uiFrcBand = 0;
    UINT32 uiDDRFreq = 0;
    float fDDRRatio = 0;
    UCHAR8 ucDDRType = 0;

    ucDDRType =  Hv_Ddr_Type();
    if (ucDDRType)
    {
        uiDDRFreq = HV_MEMORY_CONFIG_DDR3_FREQ;
        fDDRRatio = HV_MEMORY_CONFIG_DDR3_UTILIZATION_RATIO;
    }
    else
    {
        uiDDRFreq = HV_MEMORY_CONFIG_DDR2_FREQ;
        fDDRRatio = HV_MEMORY_CONFIG_DDR2_UTILIZATION_RATIO;
    }

    fDDRBand += HV_MEMORY_CONFIG_STATIC_DDR_BANDWIDTH;
    fDDRBand += PanelFreq * Hv_Board_Memory_CalDemuraBand();
    fOsdBand = Hv_Board_Memory_CalculateBandForOsd(PanelFreq);
    fOdBand   = Hv_Board_Memory_CalODBand() * PanelFreq;

    if (fOsdBand < fOdBand)
    {
        fOsdBand = fOdBand;
    }
    fDDRBand += fOsdBand;

    uiFrcBand = (UINT32 )(uiDDRFreq * HV_MEMORY_CONFIG_DDR_PHY * fDDRRatio - fDDRBand);

    return uiFrcBand;
}
/* magic number(32bit)，main type(8bit)， subtype(8bit)，起始位置(8bit)，终止位置(8bit)，寄存器地址(32bit)，值(32bit)*/
#define PER_SIZE 16
VOID Hv_Comm_SetHotfixRegister(UCHAR8 ucMain, UCHAR8 ucSub)
{
    UCHAR8 ucStart      = 0;
    UCHAR8 ucEnd        = 0;
    INT32 iIndex        = 0;
    INT32 iCount        = 0;
    UINT32 uRegister    = 0;
    UINT32 uValue       = 0;
    UINT32 uMask        = 0;

    UCHAR8 *pPostison = (UCHAR8 *)HV_MEMORY_CONFIG_MONITOR_HOTFIX_START;
    HV_LOGV("fire main: %d, sub %d\n", ucMain, ucSub);
    if (ucMain > PER_SIZE)
    {
        HV_LOGI("wrong main type\n", ucMain);
        return;
    }
    /* 16bytes per title, 16 titles per type. */
    pPostison += ucMain << 4 << 4;
    HV_LOGV("positon 0x%x\n", (UINT32)pPostison);

    for (iIndex=0; iIndex<PER_SIZE; iIndex++, pPostison+=PER_SIZE)
    {
        HV_LOGV("index %x\n", iIndex);
        /* FIRE or fire */
        if ((*(UINT32*)pPostison != 0x45524946)
            &(*(UINT32*)pPostison != 0x65726966))
        {
            HV_LOGV("no magic\n", iIndex);
            break;
        }
        if (pPostison[4] != ucMain)
        {
            HV_LOGV("main dismatch\n");
            continue;
        }
        if (pPostison[5] != ucSub)
        {
            HV_LOGV("sub dismatch\n");
            /* bin has sorted */
            break;
        }
        ucStart     = pPostison[6];
        ucEnd       = pPostison[7];
        if (ucStart > 31)
        {
            HV_LOGV("start err\n");
            continue;
        }
        if (ucEnd > 31)
        {
            HV_LOGV("end err\n");
            continue;
        }
        if (ucStart > ucEnd)
        {
            HV_LOGV("s > e\n");
            continue;
        }
        uRegister   = *(UINT32*)(pPostison+8);
        uValue      = *(UINT32*)(pPostison+12);
        uMask       = ((1U << (ucEnd - ucStart + 1)) - 1);
        if (ucEnd - ucStart == 31)
        {
            uMask       = 0xFFFFFFFF;
        }
        if (uValue > uMask)
        {
            HV_LOGV("value err\n");
            continue;
        }
        HV_LOGV("fire register: 0x%x, at bit%x, 0x%x\n", uRegister, ucStart, uValue);
        uMask       = uMask << ucStart;
        uValue      = uValue << ucStart;
        HV_W32_MASK(uRegister, uMask, uValue);
    }
    return;
}

