/**
 * @file hv_drv_OsdDataPath.c
 * @brief Osd Driver DataPath c file.
 * @details This file provides the following functions: \n
 *          (1) DataPath initialization functions \n
 *          (2) DDR Draw functions \n
 *          (3) DDR Show functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-31
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author         <th>Date        <th>Change Description
 * <tr><td>OSD Group      <td>2022-08-31   <td>create
 * </table>
*/

#include "hv_drv_Osd.h"
#include "hv_cal_Osd.h"
#include "hv_cal_Dma.h"
#include "hv_drv_Dma.h"

static OsdDataPathParam *s_pstDataPathParams = NULL;

VOID Hv_Drv_OsdDP_GetPreScalerSize(USHORT16 *pusWidth, USHORT16 *pusHeight)
{
    Hv_Cal_OsdDP_GetPreScalerSize(pusWidth, pusHeight);
}

USHORT16 Hv_Drv_OsdDP_GetRealHActive(VOID)
{
    return Hv_Cal_OsdDP_GetRealHActive();
}

USHORT16 Hv_Drv_OsdDP_GetRealVActive(VOID)
{
    return Hv_Cal_OsdDP_GetRealVActive();
}

VOID Hv_Drv_OsdDP_SetOsdScalerup(BOOL bEnable)
{
    Hv_Cal_OsdDP_SetOsdScalerup(bEnable);
}

#if (HV_PROJECT_CONFIG_OSD_DDRPATH == HV_CONFIG_ON)
USHORT16 Hv_Drv_OsdDP_GetBufferWidth(VOID)
{
    USHORT16 usWidth = 0;
    USHORT16 usLimit = 0;

    usWidth = Hv_Drv_OsdDP_GetRealHActive();
#if (defined(CONFIG_USER_PANEL_RESOLUTION_TYPE_UHD))
#if (HV_OSD_CONFIG_EXTRA_DDR_BUFFER == HV_CONFIG_ON)
    usLimit = HV_OSD_CONFIG_EXTRA_BUFFER_HACTIVE;
#else
    usLimit = _PANEL_OSD_H_ACTIVE / 2;
#endif
    if (usWidth > usLimit)
    {
        usWidth = usLimit;
    }
#endif

    return usWidth;
}

USHORT16 Hv_Drv_OsdDP_GetBufferHeight(VOID)
{
    USHORT16 usHeight = 0;
    USHORT16 usLimit = 0;

    usHeight = Hv_Drv_OsdDP_GetRealVActive();
#if (defined(CONFIG_USER_PANEL_RESOLUTION_TYPE_UHD))
#if (HV_OSD_CONFIG_EXTRA_DDR_BUFFER == HV_CONFIG_ON)
    usLimit = HV_OSD_CONFIG_EXTRA_BUFFER_VACTIVE;
#else
    usLimit = _PANEL_OSD_V_ACTIVE / 2;
#endif
    if (usHeight > usLimit)
    {
        usHeight = usLimit;
    }
#endif

    return usHeight;
}

static USHORT16 Hv_Drv_OsdDataPath_DrawDDRAlignWidth(USHORT16 usWidth)
{
    USHORT16 usOrigWidth = usWidth;
    UCHAR8 ucRoundTo = 0;

    switch (HV_OSD_CONFIG_DDR_FORMAT)
    {
        case OSD_PIXEL_FORMAT_RGBA8888:
        {
            ucRoundTo = 4;
            break;
        }
        case OSD_PIXEL_FORMAT_RGBA4444:
        case OSD_PIXEL_FORMAT_RGB565:
        {
            ucRoundTo = 8;
            break;
        }
        case OSD_PIXEL_FORMAT_RGB888:
        {
            ucRoundTo = 16;
            break;
        }
        default:
        {
            ucRoundTo = 16;
            break;
        }
    }

    usWidth = roundup(usWidth, ucRoundTo);
    OSD_DRV_LOG_DEBUG("Draw DDR align usWidth from %u to %u", usOrigWidth, usWidth);

    return usWidth;
}

static USHORT16 Hv_Drv_OsdDataPath_DrawDDRAlignX(USHORT16 usX)
{
    USHORT16 usOrigX = usX;
    UCHAR8 ucRoundTo = 0;

    switch (HV_OSD_CONFIG_DDR_FORMAT)
    {
        case OSD_PIXEL_FORMAT_RGBA8888:
        {
            ucRoundTo = 4;
            break;
        }
        case OSD_PIXEL_FORMAT_RGBA4444:
        case OSD_PIXEL_FORMAT_RGB565:
        {
            ucRoundTo = 8;
            break;
        }
        case OSD_PIXEL_FORMAT_RGB888:
        {
            ucRoundTo = 16;
            break;
        }
        default:
        {
            ucRoundTo = 16;
            break;
        }
    }

    usX = rounddown(usX, ucRoundTo);
    OSD_DRV_LOG_DEBUG("Draw DDR align usX from %u to %u", usOrigX, usX);

    return usX;
}
#endif

UCHAR8 Hv_Drv_OsdDataPath_GetDrawParts(VOID)
{
    UCHAR8 ucBit = 0;
    UCHAR8 ucFlag = 0;
    BOOL bEnable = HV_FALSE;
    /* System task call this function asynchronously, so must need back up current win-id! */
    UCHAR8 ucWinBackUp = Hv_Cal_OsdWindow_GetCurrentWindow();

    /* win0 ~ win_rect2 */
    for (ucBit = 0; ucBit < PHY_WIN_ID_MAX; ++ucBit)
    {
        Hv_Drv_OsdWindow_SetCurrentWindow(ucBit);
        bEnable = Hv_Cal_OsdWindow_GetEnable();
        ucFlag |= bEnable << ucBit;
    }

    /* Graphic */
#if (HV_PROJECT_CONFIG_OSD_GRAPHIC == HV_CONFIG_ON)
    bEnable = Hv_Cal_OsdGraphic_GetEnable();
    ucFlag |= bEnable << ucBit;
#endif

    /* Recover current win-id */
    Hv_Drv_OsdWindow_SetCurrentWindow(ucWinBackUp);

    return ucFlag;
}

VOID Hv_Drv_OsdDataPath_DrawPartsSetEnable(UCHAR8 ucFlag, BOOL bEnable)
{
    UCHAR8 ucWinId = 0;
    /* Enable/Disable win0 ~ win_rect2 */
    for (ucWinId = 0; ucWinId < PHY_WIN_ID_MAX; ++ucWinId)
    {
        if (ucFlag & (0x1 << ucWinId))
        {
            Hv_Drv_OsdWindow_SetCurrentWindow(ucWinId);
            Hv_Cal_OsdWindow_SetEnable(bEnable);
        }
    }
    /* Enable/Disable Graphic */
    if (ucFlag & (0x1 << ucWinId))
    {
        Hv_Cal_OsdGraphic_SetEnable(bEnable);
    }
}

VOID Hv_Drv_OsdDataPath_ExpandDrawRange(OsdArea *pstArea, OsdArea *pstMerge)
{
    OSD_DRV_LOG_DEBUG("Merge Range (%u, %u, %u, %u) with Range (%u, %u, %u, %u)",
                    pstArea->usX, pstArea->usY, pstArea->usWidth, pstArea->usHeight,
                    pstMerge->usX, pstMerge->usY, pstMerge->usWidth, pstMerge->usHeight);
    if (pstMerge->usX + pstMerge->usWidth > pstArea->usX + pstArea->usWidth)
    {
        pstArea->usWidth += pstMerge->usX + pstMerge->usWidth - pstArea->usX - pstArea->usWidth;
    }
    if (pstMerge->usX < pstArea->usX)
    {
        pstArea->usWidth += pstArea->usX - pstMerge->usX;
        pstArea->usX = pstMerge->usX;
    }
    if (pstMerge->usY + pstMerge->usHeight > pstArea->usY + pstArea->usHeight)
    {
        pstArea->usHeight += pstMerge->usY + pstMerge->usHeight - pstArea->usY - pstArea->usHeight;
    }
    if (pstMerge->usY < pstArea->usY)
    {
        pstArea->usHeight += pstArea->usY - pstMerge->usY;
        pstArea->usY = pstMerge->usY;
    }
    OSD_TEST_LOG_DEBUG("Range after merge (%u, %u, %u, %u)", pstArea->usX, pstArea->usY,
                    pstArea->usWidth, pstArea->usHeight);
}

VOID Hv_Drv_OsdDataPath_MergeOsdArea(UCHAR8 ucFlags, OsdArea *pstOutArea)
{
    OsdArea stArea;
    OsdArea stTempstArea;
    OsdArea *pstArea = NULL;
    UCHAR8 ucBit = 0;

    for (; ucBit <= PHY_WIN_ID_MAX; ++ucBit)
    {
        if (ucFlags & (1 << ucBit))
        {
            /* Graphic */
            if (ucBit == PHY_WIN_ID_MAX)
            {
                Hv_Cal_OsdGraphic_GetRange(&stTempstArea);
            }
            else
            {
                Hv_Drv_OsdWindow_SetCurrentWindow(ucBit);
                Hv_Cal_OsdWindow_GetPos(&stTempstArea.usX, &stTempstArea.usY);
                Hv_Cal_OsdWindow_GetSize(&stTempstArea.usWidth, &stTempstArea.usHeight);
            }
            if (NULL == pstArea)
            {
                pstArea = &stArea;
                HV_MEMCPY(pstArea, &stTempstArea, sizeof(*pstArea));
            }
            else
            {
                Hv_Drv_OsdDataPath_ExpandDrawRange(pstArea, &stTempstArea);
            }
        }
    }

    if (NULL != pstArea)
    {
        *pstOutArea = *pstArea;
    }
}

#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)

VOID Hv_Drv_OsdDataPath_GetRotationArea(USHORT16 *pusWidth, USHORT16 *pusHeight)
{
    if (NULL != pusWidth)
    {
        *pusWidth = s_pstDataPathParams->usRotateBufferWidth;
    }
    if (NULL != pusHeight)
    {
        *pusHeight = s_pstDataPathParams->usRotateBufferHeight;
    }
}

VOID Hv_Drv_OsdDataPath_GetComponentRotationPos(OsdComponentNode *pstComponent,
                                                USHORT16 *pusX, USHORT16 *pusY)
{
    USHORT16 usX = 0;
    USHORT16 usY = 0;
    OsdWindowPhyId enWindowID = 0;

    //enWindowID = Hv_Drv_Osd_Font_GetComponentWinID(pstComponent);

    Hv_Cal_OsdWindow_GetPos(&usX, &usY);

    if (NULL != pusX)
    {
        *pusX = usX + pstComponent->stBox.usX;
    }
    if (NULL != pusY)
    {
        *pusY = usY + pstComponent->stBox.usY;
    }
}

#endif

#if (HV_PROJECT_CONFIG_OSD_DDRPATH == HV_CONFIG_ON)

static Status Hv_Drv_OsdDataPath_CopyDoubleBufferCb(VOID *arg)
{
    s_pstDataPathParams->bCopyDone = HV_TRUE;

    return HV_SUCCESS;
}

static Status Hv_Drv_OsdDataPath_DrawDDRPrepare(VOID)
{
    UCHAR8 ucCount = 0;

    if (s_pstDataPathParams->bCopyDone)
    {
        return HV_SUCCESS;
    }

    while ((!s_pstDataPathParams->bCopyDone)
            && (++ucCount <= OSD_DOUBLE_BUFFER_COPY_MS))
    {
        Hv_Vos_Delayus(1000);
    }

    OSD_DRV_LOG_DEBUG("Osd double buffer copy wait %u ms", ucCount);

    if (!s_pstDataPathParams->bCopyDone)
    {
        OSD_DRV_LOG_ERROR("double buffer copy timeout");
        return HV_FAILURE;
    }

    return HV_SUCCESS;
}

VOID Hv_Drv_OsdDataPath_SetDDREnable(BOOL bEnable)
{
    Hv_Cal_OsdDDR_ReadSetEnable(bEnable);
    Hv_Cal_OsdOuterRDMA_SetEnable(OSD_DATAPATH_DDRREAD, bEnable);
    Hv_Cal_OsdOuterWDMA_SetEnable(OSD_DATAPATH_DDRWRITE, bEnable);
}

BOOL Hv_Drv_OsdDataPath_GetDDREnable(VOID)
{
    return Hv_Cal_OsdDDR_ReadGetEnable();
}

#if (HV_OSD_CONFIG_REAL_DDR_CANVAS == HV_CONFIG_ON)
VOID Hv_Drv_OsdDataPath_SetSceneDrawPartsStart(VOID)
{
    s_pstDataPathParams->ucScenePartCount = 0;

    HV_MEMSET(&s_pstDataPathParams->stDDRCanvas, 0, sizeof(OsdArea));
}

VOID Hv_Drv_OsdDataPath_SetSceneDrawPartsEnd(VOID)
{
    UCHAR8 ucSceneIndex = 0;
    UCHAR8 ucWindowIndex = 0;
    OsdSceneDrawPart *pstScenePart = NULL;
    OsdWindowDrawPart *pstWindowPart = NULL;
    OsdArea stBox = {0};
    OsdArea stSceneBox = {0};
    OsdArea *pstSceneBox = &stSceneBox;
    OsdRotationType enRotateType = 0;
    USHORT16 usHActive = 0;
    USHORT16 usVActive = 0;
    BOOL bCopied = HV_FALSE;

    enRotateType = Hv_Drv_OsdGetRealRotateType();

    usHActive = Hv_Cal_OsdDP_GetRealHActive();
    usVActive = Hv_Cal_OsdDP_GetRealVActive();

    /* merge rotated area */
    for (ucSceneIndex = 0; ucSceneIndex < s_pstDataPathParams->ucScenePartCount; ++ucSceneIndex)
    {
        pstScenePart = &s_pstDataPathParams->astAllDrawParts[ucSceneIndex];

        for (ucWindowIndex = 0; ucWindowIndex < pstScenePart->ucPartCount; ++ucWindowIndex)
        {
            pstWindowPart = &pstScenePart->astAllWindowDrawParts[ucWindowIndex];

            if (ucWindowIndex == 0)
            {
                *pstSceneBox = pstWindowPart->stDrawArea;
            }
            else
            {
                Hv_Drv_OsdDataPath_ExpandDrawRange(pstSceneBox, &pstWindowPart->stDrawArea);
            }
        }

        if ((0 == pstSceneBox->usWidth) || (0 == pstSceneBox->usHeight))
        {
            continue;
        }

        pstScenePart->stRotateArea = pstScenePart->stDrawArea = *pstSceneBox;

#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
        if (pstScenePart->bRotateEnable)
        {
#if (HV_PROJECT_CONFIG_OSD_ROTATION_TYPE == OSD_ROTATE_IN_PLACE_ALL)
            Hv_Drv_OsdCalculateUnrotateBoxOnScreen((OsdBox*)&pstScenePart->stRotateArea, usHActive, usVActive, enRotateType);
#else
            Hv_Drv_OsdCalculateRotateBoxOnScreen((OsdBox*)&pstScenePart->stRotateArea, usHActive, usVActive, enRotateType);
#endif
        }
        if (pstScenePart->bSceneAbsolutePostion)
        {
            pstScenePart->stRotateArea.usX = pstScenePart->usSceneAbsoluteX;
            pstScenePart->stRotateArea.usY = pstScenePart->usSceneAbsoluteY;
        }
#endif

        OSD_DRV_LOG_DEBUG("scene merged draw area(%d, %d, %d, %d), rotated area(%d, %d, %d, %d)",
                pstScenePart->stDrawArea.usX, pstScenePart->stDrawArea.usY, pstScenePart->stDrawArea.usWidth, pstScenePart->stDrawArea.usHeight,
                pstScenePart->stRotateArea.usX, pstScenePart->stRotateArea.usY, pstScenePart->stRotateArea.usWidth, pstScenePart->stRotateArea.usHeight);

        if (!bCopied)
        {
            stBox = pstScenePart->stRotateArea;
            bCopied = HV_TRUE;
        }
        else
        {
            Hv_Drv_OsdDataPath_ExpandDrawRange(&stBox, &pstScenePart->stRotateArea);
        }
    }

    OSD_DRV_LOG_DEBUG("all scene merged rotated area(%d, %d, %d, %d)", stBox.usX, stBox.usY, stBox.usWidth, stBox.usHeight);

    stBox.usWidth = Hv_Drv_OsdDataPath_DrawDDRAlignWidth(stBox.usWidth);
    if ((0 == stBox.usWidth) || (0 == stBox.usHeight))
    {
        stBox.usWidth = Hv_Drv_OsdDP_GetBufferWidth();
        stBox.usHeight = Hv_Drv_OsdDP_GetBufferHeight();
    }

    s_pstDataPathParams->stDDRCanvas = stBox;

    return;
}

VOID Hv_Drv_OsdDataPath_SetSceneDrawParts(OsdSceneDrawPart *pstScenePart)
{
    UCHAR8 ucIndex = 0;
    OsdWindowDrawPart *pstPart = NULL;
    OsdArea *pstBox = NULL;

    OSD_DRV_LOG_DEBUG("scene: rotate enable: %d, part count: %d", pstScenePart->bRotateEnable, pstScenePart->ucPartCount);

    HV_MEMCPY(&s_pstDataPathParams->astAllDrawParts[s_pstDataPathParams->ucScenePartCount], pstScenePart, sizeof(OsdSceneDrawPart));
    ++s_pstDataPathParams->ucScenePartCount;

    for (ucIndex = 0; ucIndex < pstScenePart->ucPartCount; ++ucIndex)
    {
        pstPart = &pstScenePart->astAllWindowDrawParts[ucIndex];
        pstBox = &pstPart->stDrawArea;

        OSD_DRV_LOG_DEBUG("win-%d draw area @(%d, %d, %d, %d)", pstPart->enWinId, pstBox->usX, pstBox->usY, pstBox->usWidth, pstBox->usHeight);
    }

    return;
}

VOID Hv_Drv_OsdDataPath_SetSceneDrawPartsCurrent(VOID)
{
    OsdArea stTempstArea;
    UCHAR8 ucBit = 0;
    UCHAR8 ucFlags = 0;
    OsdSceneDrawPart stScenePart = {0};
    OsdWindowDrawPart *pstWindowPart = NULL;

    stScenePart.bRotateEnable = HV_TRUE;

    ucFlags = Hv_Drv_OsdDataPath_GetDrawParts();

    for (; ucBit <= PHY_WIN_ID_MAX; ++ucBit)
    {
        if (ucFlags & (1 << ucBit))
        {
            /* Graphic */
            if (ucBit == PHY_WIN_ID_MAX)
            {
                Hv_Cal_OsdGraphic_GetRange(&stTempstArea);
            }
            else
            {
                Hv_Drv_OsdWindow_SetCurrentWindow(ucBit);
                Hv_Cal_OsdWindow_GetPos(&stTempstArea.usX, &stTempstArea.usY);
                Hv_Cal_OsdWindow_GetSize(&stTempstArea.usWidth, &stTempstArea.usHeight);
            }

            pstWindowPart = &stScenePart.astAllWindowDrawParts[stScenePart.ucPartCount++];
            pstWindowPart->enWinId = ucBit;
            pstWindowPart->stDrawArea = stTempstArea;
        }
    }

    Hv_Drv_OsdDataPath_SetSceneDrawParts(&stScenePart);

    return;
}

#endif

Status Hv_Drv_OsdDataPath_DrawDDRWhole(VOID)
{
    USHORT16 usWidth = 0;
    USHORT16 usHeight = 0;
    USHORT16 usVTotal = 0;
    UCHAR8 ucFlags = 0;
    Status uiStatus = HV_FAILURE;

    usWidth = Hv_Drv_OsdDP_GetBufferWidth();
    usHeight = Hv_Drv_OsdDP_GetBufferHeight();
    usVTotal = Hv_Cal_OsdDP_GetRealVTotal();

    uiStatus = Hv_Drv_OsdDataPath_DrawDDRPrepare();
    if (uiStatus != HV_SUCCESS)
    {
        return HV_FAILURE;
    }

    ucFlags = Hv_Drv_OsdDataPath_GetDrawParts();

    /* 设置timing大小*/
    Hv_Cal_OsdTmg_SetHtotalRatio(s_pstDataPathParams->ucVsDivNum);
    Hv_Cal_OsdTmg_SetTmgSize(usWidth, usHeight);
    OSD_DRV_LOG_DEBUG("Osd Timing Gen set to (%u, %u, %u)",
            usVTotal, usWidth, usHeight);

    Hv_Cal_OsdDDR_WriteSetDDRAddr(s_pstDataPathParams->uiDrawBuffer);
    Hv_Cal_OsdDDR_WriteSetSize(usWidth, usHeight, OSD_RWDMA_BURSTLEN);

    OSD_DRV_LOG_DEBUG("Draw whole DDR: width %u height %u pos 0x%x",
            usWidth, usHeight, s_pstDataPathParams->uiDrawBuffer);

    return HV_SUCCESS;
}

Status Hv_Drv_OsdDataPath_DrawDDRReal(BOOL bDrawToRotateBuffer)
{
    OsdArea stArea;
    OsdArea stCurArea;
    USHORT16 usOrigX = 0;
    USHORT16 usColStep = 0;
    USHORT16 usWidth = 0;
    USHORT16 usVActive = 0;
    UCHAR8 ucBit = 0;
    UCHAR8 ucFlags = 0;
    UINT32 uiDDRPos = 0;
    Status uiStatus = HV_FAILURE;
#ifdef HV_OSD_CONFIG_DDR_TOP_MARGIN
    USHORT16 usMargin = HV_OSD_CONFIG_DDR_TOP_MARGIN;
#else
    USHORT16 usMargin = 30;
#endif

    uiStatus = Hv_Drv_OsdDataPath_DrawDDRPrepare();
    if (uiStatus != HV_SUCCESS)
    {
        return HV_FAILURE;
    }
    ucFlags = Hv_Drv_OsdDataPath_GetDrawParts();

    Hv_Drv_OsdDataPath_MergeOsdArea(ucFlags, &stCurArea);

    usVActive = Hv_Cal_OsdDP_GetRealVActive();
    if (stCurArea.usHeight + usMargin > usVActive)
    {
        usMargin = usVActive - stCurArea.usHeight;

        OSD_DRV_LOG_WARN("Osd draw area height %d reaches vactive %d, adjust margin to %d", stCurArea.usHeight, usVActive, usMargin);
    }

#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
    if (bDrawToRotateBuffer)
    {
        uiDDRPos = s_pstDataPathParams->uiRotateBuffer;

        s_pstDataPathParams->usRotateOriWidth = stCurArea.usWidth;
        s_pstDataPathParams->usRotateOriHeight = stCurArea.usHeight;

        stCurArea.usWidth = Hv_Drv_OsdDataPath_DrawDDRAlignWidth(stCurArea.usWidth);
        usColStep = stCurArea.usWidth * HV_OSD_CONFIG_DDR_PIXEL_BYTES;

        s_pstDataPathParams->usRotateX = stCurArea.usX;
        s_pstDataPathParams->usRotateY = stCurArea.usY;
        s_pstDataPathParams->usRotateBufferWidth = stCurArea.usWidth;
        s_pstDataPathParams->usRotateBufferHeight = stCurArea.usHeight;

        OSD_DRV_LOG_DEBUG("Osd Draw to RotateBuffer: 0x%x", uiDDRPos);
    }
    else
    {
#endif
        /*
         * ----------------
         * |     |        |
         * |     |        |
         * |     |        |
         * ----------------
         * usX  usOrigX
         *
         * 将usOrigX对齐到在DDR中16字节对齐的位置
         */
        usOrigX = stCurArea.usX;
        stCurArea.usX = Hv_Drv_OsdDataPath_DrawDDRAlignX(stCurArea.usX);
        stCurArea.usWidth += (usOrigX - stCurArea.usX);

        uiDDRPos = s_pstDataPathParams->uiDrawBuffer;
#if (HV_OSD_CONFIG_REAL_DDR_CANVAS == HV_CONFIG_ON)
        usWidth = s_pstDataPathParams->stDDRCanvas.usWidth;
#else
        usWidth = Hv_Cal_OsdDP_GetRealHActive();
#endif

        if (stCurArea.usY < usMargin)
        {
            usMargin = stCurArea.usY;
        }

        uiDDRPos += ((stCurArea.usY - usMargin) * usWidth + stCurArea.usX) * HV_OSD_CONFIG_DDR_PIXEL_BYTES;
        usColStep = usWidth * HV_OSD_CONFIG_DDR_PIXEL_BYTES;

        OSD_DRV_LOG_DEBUG("Osd Draw to DrawBuffer: 0x%x", uiDDRPos);
#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
    }
#endif

    OSD_DRV_LOG_DEBUG("Osd Draw DDR Real: Merged area (%u, %u, %u, %u)",
            stCurArea.usX, stCurArea.usY, stCurArea.usWidth, stCurArea.usHeight);

    /* resize window */
    for (ucBit = 0; ucBit <= PHY_WIN_ID_MAX; ++ucBit)
    {
        if (ucFlags & (1 << ucBit))
        {
            /* Graphic */
            if (ucBit == PHY_WIN_ID_MAX)
            {
                Hv_Cal_OsdGraphic_GetRange(&stArea);
                stArea.usX -= stCurArea.usX;
                stArea.usY -= stCurArea.usY;
                /* vbph limitation */
                stArea.usY += usMargin;
                Hv_Cal_OsdGraphic_SetRange(&stArea);

                OSD_DRV_LOG_DEBUG("Graphic @(%u, %u) relocated to (%u, %u)",
                        stArea.usX, stArea.usY, stArea.usX, stArea.usY);
            }
            else
            {
                Hv_Drv_OsdWindow_SetCurrentWindow(ucBit);
                Hv_Cal_OsdWindow_GetPos(&stArea.usX, &stArea.usY);
                Hv_Cal_OsdWindow_SetPos(stArea.usX - stCurArea.usX, stArea.usY - stCurArea.usY + usMargin);

                OSD_DRV_LOG_DEBUG("window-%u @(%u, %u) relocated to (%u, %u)",
                        ucBit, stArea.usX, stArea.usY,
                        stArea.usX - stCurArea.usX, stArea.usY - stCurArea.usY + usMargin);
            }
        }
    }

    stCurArea.usHeight += usMargin;

    Hv_Cal_OsdTmg_SetHtotalRatio(s_pstDataPathParams->ucVsDivNum);
    Hv_Cal_OsdTmg_SetTmgSize(stCurArea.usWidth, stCurArea.usHeight);
    OSD_DRV_LOG_DEBUG("Osd Timing Gen set to (%u, %u, %u)",
            stCurArea.usHeight + 20, stCurArea.usWidth, stCurArea.usHeight);

    Hv_Cal_OsdDDR_WriteSetDDRAddr(uiDDRPos);
    Hv_Cal_OsdDDR_WriteSetSize(stCurArea.usWidth, stCurArea.usHeight, OSD_RWDMA_BURSTLEN);
    Hv_Cal_OsdDDR_WriteSetColStep(usColStep);

    OSD_DRV_LOG_DEBUG("Draw Real DDR: width %u height %u margin: %d", stCurArea.usWidth, stCurArea.usHeight, usMargin);

    s_pstDataPathParams->usMargin = usMargin;
    return HV_SUCCESS;
}

VOID Hv_Drv_OsdDataPath_SwitchDrawBuffer(VOID)
{
    UINT32 uiTemp = 0;

    uiTemp = s_pstDataPathParams->uiDrawBuffer;
    s_pstDataPathParams->uiDrawBuffer = s_pstDataPathParams->uiDisplayBuffer;
    s_pstDataPathParams->uiDisplayBuffer = uiTemp;
}

VOID Hv_Drv_OsdDataPath_ShowDDR()
{
    OsdArea stArea;
    USHORT16 usWidth = 0;
    USHORT16 usHeight = 0;

    Hv_Cal_OsdDDR_ReadSetDDRAddr(s_pstDataPathParams->uiDrawBuffer);
    OSD_DRV_LOG_DEBUG("read ddr addr set to 0x%x", s_pstDataPathParams->uiDrawBuffer);

#if (HV_OSD_CONFIG_REAL_DDR_CANVAS == HV_CONFIG_ON)
    stArea = s_pstDataPathParams->stDDRCanvas;
    usWidth = stArea.usWidth;
    usHeight = stArea.usHeight;
#else
    usWidth = Hv_Cal_OsdDP_GetRealHActive();
    usHeight = Hv_Cal_OsdDP_GetRealVActive();

    stArea.usX = 0;
    stArea.usY = 0;
    stArea.usWidth = usWidth;
    stArea.usHeight = usHeight;
#endif
    Hv_Cal_OsdDP_SetDDRCanvas(&stArea);

    /* set scaler param here */
    Hv_Cal_OsdScaler_SetScalerRatio();

    Hv_Cal_OsdDDR_ReadSetSize(usWidth, usHeight, OSD_RWDMA_BURSTLEN);

    /* 切换buffer */
    Hv_Drv_OsdDataPath_SwitchDrawBuffer();

    Hv_Drv_OsdDataPath_SetDDREnable(Hv_Drv_OsdDataPath_GetDrawParts() != 0);
    Hv_Cal_OsdOuterWDMA_SetEnable(OSD_DATAPATH_DDRWRITE, 0);

    return;
}

VOID Hv_Drv_OsdDataPath_DoubleBufferCopy(VOID)
{
    USHORT16 usWidth = 0;
    USHORT16 usHeight = 0;
    UINT32 uiCopySize = 0;

#if (HV_OSD_CONFIG_REAL_DDR_CANVAS == HV_CONFIG_ON)
    usWidth = s_pstDataPathParams->stDDRCanvas.usWidth;
    usHeight = s_pstDataPathParams->stDDRCanvas.usHeight;
#else
    usWidth = Hv_Cal_OsdDP_GetRealHActive();
    usHeight = Hv_Cal_OsdDP_GetRealVActive();
#endif
    uiCopySize = ((UINT32)usWidth * usHeight * HV_OSD_CONFIG_DDR_PIXEL_BYTES);

    /* 设置系统DMA copy double buffer
     *
     * 拷贝时间：至少有一帧的时间可以拷贝
     *
     * -- Show DDR, Set Regen --- Vsync -- Draw DDR, Set Regen ----- Vsync
     *
     *                  <------------------------------------------>
     */
#if (HV_OSD_CONFIG_DOUBLE_BUFFER_USING_MEMCPY == HV_CONFIG_OFF)
    Hv_Drv_Dma_Init(OSD_SYSDMA_PORT_NUMBER,
            Hv_Drv_OsdDataPath_CopyDoubleBufferCb);

    s_pstDataPathParams->bCopyDone = HV_FALSE;
    Hv_Drv_Dma_MemMove(OSD_SYSDMA_PORT_NUMBER,
            s_pstDataPathParams->uiDisplayBuffer,
            s_pstDataPathParams->uiDrawBuffer,
            uiCopySize);
#else

#ifdef HV_SCALER_DEBUG_VERSION
    UINT64 currTick = Hv_Vos_GetTick();
#endif
    HV_MEMCPY((UCHAR8 *)(s_pstDataPathParams->uiDrawBuffer), (UCHAR8 *)(s_pstDataPathParams->uiDisplayBuffer), uiCopySize);
    Hv_Vos_InvalidAllDCache();
#ifdef HV_SCALER_DEBUG_VERSION
    currTick = Hv_Vos_GetTick() - currTick;
    OSD_DRV_LOG_DEBUG("Double buffer copy: Tick passed %llu", currTick);
#endif
    s_pstDataPathParams->bCopyDone = HV_TRUE;
#endif

    return;
}

#endif

VOID Hv_Drv_OsdDataPath_SetDrawMode(OsdHsBlendMode enMode)
{
    USHORT16 usHActive = 0;
    USHORT16 usVActive = 0;

    Hv_Cal_OsdDP_SetHsBlendMode(enMode);

#if (HV_PROJECT_CONFIG_OSD_DDRPATH == HV_CONFIG_ON)
    if (enMode == OSD_HSBLEND_MODE_OSD_ONLY)
    {
        Hv_Drv_OsdDataPath_SetDDREnable(HV_FALSE);
    }
#endif

    usHActive = Hv_Cal_OsdDP_GetRealHActive();
    usVActive = Hv_Cal_OsdDP_GetRealVActive();

    if ((enMode == OSD_HSBLEND_MODE_OSD_FIRST)
            || (enMode == OSD_HSBLEND_MODE_OSD_ONLY) )
    {
        Hv_Cal_OsdFrame_VsDivNum(0);
        Hv_Cal_OsdTmg_SetHtotalRatio(1);
        Hv_Cal_OsdTmg_SetTmgSize(usHActive, usVActive);

        /* set scaler param here */
        Hv_Cal_OsdScaler_SetScalerRatio();
    }
    else
    {
        Hv_Cal_OsdFrame_VsDivNum(s_pstDataPathParams->ucVsDivNum);
        //Hv_Cal_OsdTmg_SetHtotalRatio(s_pstDataPathParams->ucVsDivNum);
    }
}

VOID Hv_Drv_OsdDataPath_SetVsDivNumber(UCHAR8 ucDiv)
{
    s_pstDataPathParams->ucVsDivNum = ucDiv;
}

OsdDataPathParam* Hv_Drv_OsdDataPath_GetDataPathParam(VOID)
{
    return s_pstDataPathParams;
}

VOID Hv_Drv_OsdDataPath_Init(VOID)
{
    s_pstDataPathParams = (OsdDataPathParam*)OSD_DRIVER_ALLOC_STATIC(OsdDataPathParam, 1);
    HV_MEMSET(s_pstDataPathParams, 0, sizeof(*s_pstDataPathParams));

    s_pstDataPathParams->ucVsDivNum = 1;
    Hv_Drv_OsdDataPath_SetDrawMode(OSD_HSBLEND_MODE_OSD_ONLY);
#if (HV_PROJECT_CONFIG_OSD_DDRPATH == HV_CONFIG_ON)
    s_pstDataPathParams->bCopyDone = HV_TRUE;
    s_pstDataPathParams->uiDrawBuffer = OSD_DRAW_BUFFER0_START;
    s_pstDataPathParams->uiDisplayBuffer = OSD_DRAW_BUFFER1_START;
#endif

#if (HV_PROJECT_CONFIG_OSD_ROTATION == HV_CONFIG_ON)
    s_pstDataPathParams->uiRotateBuffer = HV_MEMORY_CONFIG_OSD_ROTATION_DDR_START;

    s_pstDataPathParams->enRotate = OSD_ROTATION_NONE;
    s_pstDataPathParams->usRotateX = 0;
    s_pstDataPathParams->usRotateY = 0;
#endif
}
