/**
 * @file hv_drv_Dma.c
 * @brief wdg driver chip file.
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2023-06-05
 * @copyright Copyright(c),2023-6, Hiview Software. All rights reserved.
 * </table>
 */

#include "hv_chip_Config.h"
#include "hv_vos_Comm.h"
#include "hv_comm_DataType.h"
#include "hv_comm_Define.h"
#include "hv_cal_Dma.h"
#include "hv_drv_Dma.h"

#define SIZE_NO_CARE 2048

typedef void (*Hv_Dma_Callback)(void);

static DmaSelf* g_apDmaSelf[DMA_CHANNEL_COUNT] = {NULL};
static Hv_Dma_Callback  g_apDmaCallback[DMA_CHANNEL_COUNT] = {NULL};
static DmaLinkedList* g_apLLI[DMA_CHANNEL_COUNT] = {NULL};

static void Hv_Drv_Dma_MemMove_LinkedList(DmaSelf* pstself,DmaLinkedList* pstStemAddr, UINT32 uiSrcAddr, UINT32 uiDstAddr)
{
    Hv_Cal_Dma_SetLinkedListAddr(pstself, pstStemAddr);
    Hv_Cal_Dma_MemMove(pstself, uiSrcAddr, uiDstAddr, SIZE_NO_CARE);

    return;
}

static Status Hv_Dma_CpltCallback(void* arg)
{
    DmaSelf* pstSelf = (DmaSelf*)arg;
    DmaPortChannel enPortChannelNum = Hv_Cal_Dma_GetPortChannelNum(pstSelf);

    if (NULL != g_apLLI[enPortChannelNum])
    {
        Hv_Vos_Free(g_apLLI[enPortChannelNum]);
        g_apLLI[enPortChannelNum] = NULL;
    }
    Hv_Vos_InvalidAllDCache();
    g_apDmaCallback[enPortChannelNum]();

    return HV_SUCCESS;
}

/** @brief dma初始化，选择通道和设置回调函数。type类型0:1个src对应1个dst，size可能超过block_ts,函数内部生成linklist.
 *  @self channelparam no.
 *  @param Callback callback when move over.
 */
void Hv_Drv_Dma_Init(DmaPortChannel enPortChannelNum, void* Callback)
{

    DmaInitParam InitParam;
    HV_MEMSET(&InitParam,0, sizeof(DmaInitParam));

    InitParam.PortChannelNum = enPortChannelNum;
    InitParam.Application = DMA_APPLI_MEM;
    InitParam.transType = DMA_TYPE_LINKEDLIST;
    InitParam.XferCpltCallback = (XferCpltCallbackType)Hv_Dma_CpltCallback;
 
    g_apDmaCallback[enPortChannelNum] = (Hv_Dma_Callback)Callback;
    g_apDmaSelf[enPortChannelNum] = Hv_Cal_Dma_ChannelInit(&InitParam);

    return;
}

/** @brief dma move。
 *  @self channelparam no.
 *  @param SrcAddr src address。
 *  @param DstAddr dst address。
 *  @param Size size to move。
 */
void Hv_Drv_Dma_MemMove(DmaPortChannel enPortChannelNum, UINT32 uiSrcAddr, UINT32 uiDstAddr, UINT32 uiSize)
{
    DmaSelf* pstSelf = g_apDmaSelf[enPortChannelNum];

    UINT32 uiBlockSize = DMA_MEM_BLOCK_SIZE;
    UINT32 uiTotolListNum = 0;
    UINT32 uiLast = 0;

    HV_LOG_DEBUG(SYSTEM, "Hv_Drv_Dma_MemMove src %x, dst %x, size %x!\n", uiSrcAddr, uiDstAddr, uiSize);
    uiTotolListNum = uiSize / uiBlockSize;
    if (uiSize % uiBlockSize)
    {
        uiTotolListNum++;
    }

    g_apLLI[enPortChannelNum] = (DmaLinkedList*)Hv_Vos_Malloc(uiTotolListNum * sizeof(DmaLinkedList));
    if (g_apLLI[enPortChannelNum] ==  NULL)
    {
        HV_LOG_ERROR(SYSTEM, "Hv_Drv_Dma_MemMove Malloc Fail, size = 0x%x!\n", uiTotolListNum * sizeof(DmaLinkedList));
        return;
    }

    Hv_Cal_Dma_LLIbuild_BothContinous(g_apLLI[enPortChannelNum], uiSrcAddr, uiDstAddr, uiSize, 1);
    Hv_Drv_Dma_MemMove_LinkedList(pstSelf, g_apLLI[enPortChannelNum], uiSrcAddr, uiDstAddr);

    return;
}

/** @brief dma move with multy src。
 *  @self channelparam no.
 *  @param SrcArray src1 address， src1 size， src2 address， src2 size...。
 *  @param ArraySize src number。
 *  @param DstAddr dst address。
 */
void Hv_Drv_Dma_MemMoveArray(DmaPortChannel enPortChannelNum, UINT32 *puiSrcArray, UINT32 uiArraySize, UINT32 uiDstAddr)
{
    DmaSelf* pstSelf = g_apDmaSelf[enPortChannelNum];
    UINT32 uiStemAddr = 0x00;
    UINT32 uiLoop = 0;
    UINT32 uiAarryNum = 0;
    UINT32 uiSize = 0;
    UINT32 uiBlockSize = DMA_MEM_BLOCK_SIZE;
    UINT32 uiTotolListNum = 0;
    UINT32 uiIndexListNum = 0;
    UINT32 uiLast = 0;

    uiAarryNum = uiArraySize;
    for (uiLoop = 0; uiLoop < uiAarryNum; uiLoop++)
    {
        uiSize = *(puiSrcArray + 1 + uiLoop * 2);
        uiTotolListNum += uiSize / uiBlockSize;
        if (uiSize % uiBlockSize)
        {
            uiTotolListNum++;
        }
    }

    g_apLLI[enPortChannelNum] = (DmaLinkedList*)Hv_Vos_Malloc(uiTotolListNum * sizeof(DmaLinkedList));
    if (g_apLLI[enPortChannelNum] ==  NULL)
    {
        return;
    }

    for (uiLoop = 0; uiLoop < uiAarryNum; uiLoop++)
    {
        uiStemAddr = (UINT32)(g_apLLI[enPortChannelNum] + uiIndexListNum);
        if (uiLoop ==  (uiAarryNum - 1))
        {
            uiLast = 1;
        }
        uiSize = *(puiSrcArray + 1 + uiLoop * 2);

        Hv_Cal_Dma_LLIbuild_BothContinous((DmaLinkedList*)uiStemAddr, *(puiSrcArray + uiLoop * 2), uiDstAddr, uiSize, uiLast);

        uiIndexListNum += (uiSize / uiBlockSize);
        if (uiSize % uiBlockSize)
        {
            uiIndexListNum++;
        }
    }

    Hv_Drv_Dma_MemMove_LinkedList(pstSelf, g_apLLI[enPortChannelNum], *puiSrcArray, uiDstAddr);
    return;
}
