/**
 * @file hv_drv_OsdDMA.c
 * @brief Osd Driver Inner DMA c file.
 * @details This file provides the following functions: \n
 *          (1) Inner DMA initialization functions \n
 *          (2) Inner DMA Pend functions \n
 *          (3) Inner DMA transfer 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_cal_Osd.h"
#include "hv_drv_Osd.h"

static OsdDMAEntry *s_pstAllDMAEntries = NULL;
static UCHAR8 s_ucDMAEntryCount = 0;
static BOOL g_bDMATransferDone = HV_TRUE;

VOID Hv_Drv_OsdDMA_DebugPrintState(VOID)
{
    Hv_Cal_OsdInnerDMA_DebugPrint();
}

BOOL Hv_Drv_OsdDMA_GetTransferDone(VOID)
{
    return g_bDMATransferDone;
}

VOID Hv_Drv_OsdDMA_TransferFinish(VOID)
{
    g_bDMATransferDone = HV_TRUE;
}

Status Hv_Drv_OsdDMA_TransferPrepare(VOID)
{
    UCHAR8 ucCount = 0;

    if (g_bDMATransferDone)
    {
        return HV_SUCCESS;
    }

    while (!g_bDMATransferDone && ++ucCount <= OSD_DMA_WAITDONE_MS)
    {
        Hv_Vos_Delayus(1000);
    }

    OSD_DRV_LOG_DEBUG("wait DMA done for %u ms", ucCount);

    if (g_bDMATransferDone)
    {
        return HV_SUCCESS;
    }

    OSD_DRV_LOG_ERROR("DMA transfer timeout, try to recover");

    /* 检查寄存器中的done flag是否设置? */
    if (Hv_Cal_OsdInnerDMA_CheckDone())
    {
        OSD_DRV_LOG_ERROR("circuit Done flag set, Software done flag not, Maybe interrupt lost");

        g_bDMATransferDone = HV_TRUE;
        Hv_Cal_OsdInnerDMA_ClearDoneFlag();
        Hv_Drv_OsdDMA_ClearDMAChannel();

        return HV_SUCCESS;
    }

    /* 检查寄存器中的going flag是否设置？*/
    if (!Hv_Cal_OsdInnerDMA_CheckGoing())
    {
        OSD_DRV_LOG_ERROR("circuit not transfering, ready to start a new transfer");

        g_bDMATransferDone = HV_TRUE;
        Hv_Drv_OsdDMA_ClearDMAChannel();

        return HV_SUCCESS;
    }

    OSD_DRV_LOG_ERROR("circuit transfering, could not start a new transfer");
    Hv_Drv_OsdDMA_ClearDMAChannel();

    return HV_FAILURE;
}

Status Hv_Drv_OsdDMA_Walk(pfOsdDMACallback pfCallback, UCHAR8 ucCount)
{
    OsdDMAEntry *pstCur = NULL;
    BOOL bRet = HV_FALSE;
    UCHAR8 ucIndex = 0;

    for (ucIndex = 0; ucIndex < ucCount; ++ucIndex)
    {
        pstCur = s_pstAllDMAEntries + ucIndex;

        bRet = pfCallback(pstCur);
        if (bRet == HV_FAILURE)
        {
            return HV_FAILURE;
        }
    }
    return HV_SUCCESS;
}

VOID Hv_Drv_OsdDMA_Pend(UINT32 uiSrcAddr, UINT32 uiDstAddr, UINT32 uiLength)
{
    OsdDMAEntry *pstCur = NULL;

    OSD_DRV_LOG_DEBUG("DMA Pending: src 0x%x dst 0x%x length 0x%x \n",
                    uiSrcAddr, uiDstAddr, uiLength);

    HV_ASSERT_TRUE_VOID(s_ucDMAEntryCount <= OSD_DMA_CHANNEL_COUNTS);
    HV_ASSERT_TRUE_VOID((uiSrcAddr & 0x0f) == 0);
    HV_ASSERT_TRUE_VOID((uiDstAddr & 0x0f) == 0);
    HV_ASSERT_TRUE_VOID(uiLength != 0 && (uiLength & 0x0f) == 0);
    HV_ASSERT_TRUE_VOID(uiDstAddr >= OSD_PALETTE_BUFFER_BASE_ADDR);
    HV_ASSERT_TRUE_VOID(uiDstAddr < OSD_RESOURCE_BUFFER_END);

    pstCur = s_pstAllDMAEntries + s_ucDMAEntryCount;
    pstCur->uiSrcAddr = uiSrcAddr;
    pstCur->uiDstAddr = uiDstAddr;
    pstCur->uiLength = uiLength;

    ++s_ucDMAEntryCount;
}

VOID Hv_Drv_OsdDMA_ClearDMAChannel(VOID)
{
    s_ucDMAEntryCount = 0;
}

VOID Hv_Drv_OsdDMA_Transfer(VOID)
{
    UCHAR8 ucIndex = 0;
    OsdDMAEntry *pstCur = NULL;
#if (HV_OSD_CONFIG_TRANSFER_USING_MEMCPY == HV_CONFIG_ON)
    UINT32 *puiSrc = NULL, *puiDst = NULL;
    UINT32 uiData = 0;
#endif

    if (0 == s_ucDMAEntryCount)
    {
        return;
    }

    OSD_DRV_LOG_DEBUG("DMA transfer start\n");

    for (ucIndex = 0; ucIndex < s_ucDMAEntryCount; ++ucIndex)
    {
        pstCur = s_pstAllDMAEntries + ucIndex;

#if (HV_OSD_CONFIG_TRANSFER_USING_MEMCPY == HV_CONFIG_ON)
        puiSrc = (UINT32*)(pstCur->uiSrcAddr | 0x80000000);
        puiDst = (UINT32*)pstCur->uiDstAddr;
        OSD_DRV_LOG_DEBUG("Copy 0x%x bytes from 0x%x to 0x%x", pstCur->uiLength, puiSrc, (UINT32*)HV_PHY_ADDR((UINT32)puiDst));
        for (; pstCur->uiLength > 0; pstCur->uiLength -= 4)
        {
            HV_W32((UINT32)puiDst, *puiSrc);
            puiDst++;
            puiSrc++;
        }
        OSD_DRV_LOG_DEBUG("Copy Done!!");
#else
        Hv_Cal_OsdInnerDMA_SetGroupInfo(ucIndex, pstCur->uiDstAddr, pstCur->uiSrcAddr, pstCur->uiLength);
#endif
    }
#if (HV_OSD_CONFIG_TRANSFER_USING_MEMCPY == HV_CONFIG_ON)
        s_ucDMAEntryCount = 0;

        return;
#else
        Hv_Cal_OsdInnerDMA_SetTotalGroups(s_ucDMAEntryCount);

        OSD_DRV_LOG_DEBUG("Start DMA");
        g_bDMATransferDone = HV_FALSE;
        Hv_Cal_OsdInnerDMA_Start();

        s_ucDMAEntryCount = 0;
#endif
}

VOID Hv_Drv_OsdDMA_Init(VOID)
{
    s_pstAllDMAEntries = (OsdDMAEntry*)OSD_DRIVER_ALLOC_STATIC(
            OsdDMAEntry, OSD_DMA_CHANNEL_COUNTS);
    s_ucDMAEntryCount = 0;
    OSD_DRV_LOG_DEBUG("%u DMA entries allocated at 0x%x",
            OSD_DMA_CHANNEL_COUNTS, s_pstAllDMAEntries);

    g_bDMATransferDone = HV_TRUE;
}
