/*
 *  Routines to access hardware
 *
 *  Copyright (c) 2013 Realtek Semiconductor Corp.
 *
 *  This module is a confidential and proprietary property of RealTek and
 *  possession or use of this module requires written permission of RealTek.
 */

#include "rtl8195a.h"
#include "rtl8195a_ssi.h"
#include "hal_ssi.h"

extern _LONG_CALL_
HAL_Status HalSsiInitRtl8195a(VOID *Adaptor);

extern _LONG_CALL_ 
u32 HalGetCpuClk(VOID);


VOID _SsiReadInterruptRtl8195a(VOID *Adapter)
{
    SSI_DBG_ENTRANCE("_SsiReadInterrupt()\n");
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    u32 ReceiveLevel;
    volatile u32 Readable = HalSsiReadableRtl8195a(Adapter);
    u8  Index = pHalSsiAdapter->Index;

    while (Readable) {
        ReceiveLevel = HalSsiGetRxFifoLevelRtl8195a(Adapter);

        while (ReceiveLevel--) {
            if (pHalSsiAdapter->RxData != NULL) {
                if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
                    // 16~9 bits mode
                    *((u16*)(pHalSsiAdapter->RxData)) = (u16)(HAL_SSI_READ32(Index, REG_DW_SSI_DR));
                    pHalSsiAdapter->RxData = (VOID*)(((u16*)pHalSsiAdapter->RxData) + 1);
                }
                else {
                    // 8~4 bits mode
                    *((u8*)(pHalSsiAdapter->RxData)) = (u8)(HAL_SSI_READ32(Index, REG_DW_SSI_DR));
                    pHalSsiAdapter->RxData = (VOID*)(((u8*)pHalSsiAdapter->RxData) + 1);
                }
            }
            else {
                // for Master mode, doing TX also will got RX data, so drop the dummy data
                HAL_SSI_READ32(Index, REG_DW_SSI_DR);
            }
            
            if (pHalSsiAdapter->RxLength > 0) {
                pHalSsiAdapter->RxLength--;
            }
#if 0
            else if (pHalSsiAdapter->RxLengthRemainder > 0) {
                pHalSsiAdapter->RxLengthRemainder--;
            }

            // Fixed length receive Complete. (RxLength & RxLengthRemainder == 0)
            if ((pHalSsiAdapter->RxLength == 0) && (pHalSsiAdapter->RxLengthRemainder == 0)) {
                break;
            }
#endif            
            if (pHalSsiAdapter->RxLength == 0) {
                break;
            }
        }

        if (pHalSsiAdapter->RxLength == 0) {
            break;
        }

        Readable = HalSsiReadableRtl8195a(Adapter);
    }

    if ((pHalSsiAdapter->RxLength > 0) && 
        (pHalSsiAdapter->RxLength < (pHalSsiAdapter->RxThresholdLevel+1))) {
        SSI_DBG_INT_READ("Setting Rx FIFO Threshold Level to 1\n");
        pHalSsiAdapter->RxThresholdLevel = 0;
        HalSsiSetRxFifoThresholdLevelRtl8195a((VOID*)pHalSsiAdapter);
    }

    if (pHalSsiAdapter->RxLength == 0) {
        DBG_SSI_INFO("_SsiReadInterruptRtl8195a: RX_Done\r\n");
        pHalSsiAdapter->InterruptMask &= ~(BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM);
        HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);
//        if (pHalSsiAdapter->RxData != NULL) {
            if (pHalSsiAdapter->RxCompCallback != NULL) {
                pHalSsiAdapter->RxCompCallback(pHalSsiAdapter->RxCompCbPara);
            }
//        }
    }

}


VOID _SsiWriteInterruptRtl8195a(VOID *Adapter)
{
    SSI_DBG_ENTRANCE("_SsiWriteInterrupt()\n");
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    u32 Writeable     = HalSsiWriteableRtl8195a(Adapter);
    u32 TxWriteMax    = SSI_TX_FIFO_DEPTH - pHalSsiAdapter->TxThresholdLevel;
    u8  Index         = pHalSsiAdapter->Index;

    if (Writeable) {
        /* Disable Tx FIFO Empty IRQ */
        pHalSsiAdapter->InterruptMask &= ~ BIT_IMR_TXEIM;
        HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);

        while (TxWriteMax--) {
            if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
                // 16~9 bits mode
                if (pHalSsiAdapter->TxData != NULL) {
                    HAL_SSI_WRITE16(Index, REG_DW_SSI_DR, *((u16*)(pHalSsiAdapter->TxData)));
                    pHalSsiAdapter->TxData = (VOID*)(((u16*)pHalSsiAdapter->TxData) + 1);
                }
                else {
                    // For master mode: Push a dummy to TX FIFO for Read
                    if (pHalSsiAdapter->Role == SSI_MASTER) {
                        HAL_SSI_WRITE16(Index, REG_DW_SSI_DR, (u16)SSI_DUMMY_DATA);    // Dummy byte
                    }
                }
            }
            else {
                // 8~4 bits mode
                if (pHalSsiAdapter->TxData != NULL) {
                    HAL_SSI_WRITE8(Index, REG_DW_SSI_DR, *((u8*)(pHalSsiAdapter->TxData)));
                    pHalSsiAdapter->TxData = (VOID*)(((u8*)pHalSsiAdapter->TxData) + 1);
                }
                else {
                    // For master mode: Push a dummy to TX FIFO for Read
                    if (pHalSsiAdapter->Role == SSI_MASTER) {
                        HAL_SSI_WRITE8(Index, REG_DW_SSI_DR, (u8)SSI_DUMMY_DATA);    // Dummy byte
                    }
                }
            }

            pHalSsiAdapter->TxLength--;

            if (pHalSsiAdapter->TxLength == 0)
                break;
        }

        /* Enable Tx FIFO Empty IRQ */
        pHalSsiAdapter->InterruptMask |= BIT_IMR_TXEIM;
        HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);
    }

    if (pHalSsiAdapter->TxLength == 0) {
        DBG_SSI_INFO("_SsiWriteInterruptRtl8195a: TX_Done\r\n");
        pHalSsiAdapter->InterruptMask &= ~(BIT_IMR_TXOIM | BIT_IMR_TXEIM);
        HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);
        // If it's not a dummy TX for master read SPI, then call the TX_done callback
        if (pHalSsiAdapter->TxData != NULL) {
            if (pHalSsiAdapter->TxCompCallback != NULL) {
                pHalSsiAdapter->TxCompCallback(pHalSsiAdapter->TxCompCbPara);
            }
        }
    }
}


u32 _SsiIrqHandleRtl8195a(VOID *Adaptor)
{
    SSI_DBG_ENTRANCE("_SsiIrqHandle()\n");
    PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor;
    u32 InterruptStatus = HalSsiGetInterruptStatusRtl8195a(Adaptor);
    u8  Index = pHalSsiAdaptor->Index;

    if (InterruptStatus & BIT_ISR_TXOIS) {
        SSI_DBG_INT_HNDLR("[INT][SSI%d] Transmit FIFO Overflow Interrupt\n", Index);
        HAL_SSI_READ32(Index, REG_DW_SSI_TXOICR);
    }

    if (InterruptStatus & BIT_ISR_RXUIS) {
        SSI_DBG_INT_HNDLR("[INT][SSI%d] Receive FIFO Underflow Interrupt\n", Index);
        HAL_SSI_READ32(Index, REG_DW_SSI_RXUICR);
    }

    if (InterruptStatus & BIT_ISR_RXOIS) {
        SSI_DBG_INT_HNDLR("[INT][SSI%d] Receive FIFO Overflow Interrupt\n", Index);
        HAL_SSI_READ32(Index, REG_DW_SSI_RXOICR);
    }

    if (InterruptStatus & BIT_ISR_MSTIS) {
        SSI_DBG_INT_HNDLR("[INT][SSI%d] Multi-Master Contention Interrupt\n", Index);
        /* Another master is actively transferring data */
        /* TODO: Do reading data... */
        HAL_SSI_READ32(Index, REG_DW_SSI_MSTICR);
    }

    if ((InterruptStatus & BIT_ISR_RXFIS) ) {
        SSI_DBG_INT_HNDLR("[INT][SSI%d] Receive FIFO Full Interrupt\n", Index);
        _SsiReadInterruptRtl8195a(Adaptor);
    }

    if (InterruptStatus & BIT_ISR_TXEIS) {
        /* Tx FIFO is empty, need to transfer data */
        SSI_DBG_INT_HNDLR("[INT][SSI%d] Transmit FIFO Empty Interrupt\n", Index);
        _SsiWriteInterruptRtl8195a(Adaptor);
    }

    return 0;
}

HAL_Status HalSsiInitRtl8195a_Patch(VOID *Adaptor)
{
    SSI_DBG_ENTRANCE("HalSsiInitRtl8195a()\n");
    PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor;
    volatile IRQn_Type IrqNum;
    u32  IRQ_UNKNOWN  = -999;
    u32  Ctrlr0Value  = 0;
    u32  Ctrlr1Value  = 0;
    u32  SerValue     = 0;
    u32  BaudrValue   = 0;
    u32  TxftlrValue  = 0;
    u32  RxftlrValue  = 0;
    u32  MwcrValue    = 0;
    u8   MicrowireTransferMode = pHalSsiAdaptor->MicrowireTransferMode;
    u8   DataFrameFormat = pHalSsiAdaptor->DataFrameFormat;
    u8   TransferMode = pHalSsiAdaptor->TransferMode;
    u8   Index = pHalSsiAdaptor->Index;
    u8   Role  = pHalSsiAdaptor->Role;

    if (Index > 2) {
        DBG_SSI_ERR("HalSsiInitRtl8195a: Invalid SSI Idx %d\r\n", Index);
        return HAL_ERR_PARA;
    }
    HalSsiPinmuxEnableRtl8195a_Patch(pHalSsiAdaptor);    
    HalSsiDisableRtl8195a(Adaptor);

    /* REG_DW_SSI_CTRLR0 */
    Ctrlr0Value |= BIT_CTRLR0_DFS(pHalSsiAdaptor->DataFrameSize);
    Ctrlr0Value |= BIT_CTRLR0_FRF(pHalSsiAdaptor->DataFrameFormat);
    Ctrlr0Value |= BIT_CTRLR0_SCPH(pHalSsiAdaptor->SclkPhase);
    Ctrlr0Value |= BIT_CTRLR0_SCPOL(pHalSsiAdaptor->SclkPolarity);
    Ctrlr0Value |= BIT_CTRLR0_TMOD(pHalSsiAdaptor->TransferMode);
    Ctrlr0Value |= BIT_CTRLR0_CFS(pHalSsiAdaptor->ControlFrameSize);

    if (Role == SSI_SLAVE)
        Ctrlr0Value |= BIT_CTRLR0_SLV_OE(pHalSsiAdaptor->SlaveOutputEnable);

    SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_CTRLR0 Value: %X\n", Index, Ctrlr0Value);

    HAL_SSI_WRITE32(Index, REG_DW_SSI_CTRLR0, Ctrlr0Value);

    SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_CTRLR0(%X) = %X\n", Index,
            SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_CTRLR0,
            HAL_SSI_READ32(Index, REG_DW_SSI_CTRLR0));

    /* REG_DW_SSI_TXFTLR */
    TxftlrValue = BIT_TXFTLR_TFT(pHalSsiAdaptor->TxThresholdLevel);
    SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_TXFTLR Value: %X\n", Index, TxftlrValue);

    HAL_SSI_WRITE32(Index, REG_DW_SSI_TXFTLR, TxftlrValue);

    SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_TXFTLR(%X) = %X\n", Index,
            SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_TXFTLR,
            HAL_SSI_READ32(Index, REG_DW_SSI_TXFTLR));

    /* REG_DW_SSI_RXFTLR */
    RxftlrValue = BIT_RXFTLR_RFT(pHalSsiAdaptor->RxThresholdLevel);
    SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_RXFTLR Value: %X\n", Index, RxftlrValue);

    HAL_SSI_WRITE32(Index, REG_DW_SSI_RXFTLR, RxftlrValue);

    SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_RXFTLR(%X) = %X\n", Index,
            SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_RXFTLR,
            HAL_SSI_READ32(Index, REG_DW_SSI_RXFTLR));
    /**
     * Master Only
     * REG_DW_SSI_CTRLR1, REG_DW_SSI_SER, REG_DW_SSI_BAUDR
     */
    if (Role & SSI_MASTER) {
        if ((TransferMode == TMOD_RO) || (TransferMode == TMOD_EEPROM_R) ||
                ((DataFrameFormat == FRF_NS_MICROWIRE) && (MicrowireTransferMode == MW_TMOD_SEQUENTIAL))) {
            Ctrlr1Value = BIT_CTRLR1_NDF(pHalSsiAdaptor->DataFrameNumber);
            SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_CTRLR1 Value: %X\n", Index, Ctrlr1Value);

            HAL_SSI_WRITE32(Index, REG_DW_SSI_CTRLR1, Ctrlr1Value);

            SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_CTRLR1(%X) = %X\n", Index,
                    SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_CTRLR1,
                    HAL_SSI_READ32(Index, REG_DW_SSI_CTRLR1));
        }

        SerValue = BIT_SER_SER(1 << (pHalSsiAdaptor->SlaveSelectEnable));
        SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_SER Value: %X\n", Index, SerValue);

        //HAL_SSI_WRITE32(Index, REG_DW_SSI_SER, SerValue);
        HalSsiSetSlaveEnableRegisterRtl8195a(Adaptor, pHalSsiAdaptor->SlaveSelectEnable);

        SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_SER(%X) = %X\n", Index,
                SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_SER,
                HalSsiGetSlaveEnableRegisterRtl8195a(Adaptor));

        BaudrValue = BIT_BAUDR_SCKDV(pHalSsiAdaptor->ClockDivider);
        SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_BAUDR Value: %X\n", Index, BaudrValue);

        HAL_SSI_WRITE32(Index, REG_DW_SSI_BAUDR, BaudrValue);

        SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_BAUDR(%X) = %X\n", Index,
                SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_BAUDR,
                HAL_SSI_READ32(Index, REG_DW_SSI_BAUDR));
    }

    // Microwire
    MwcrValue |= BIT_MWCR_MWMOD(pHalSsiAdaptor->MicrowireTransferMode);
    MwcrValue |= BIT_MWCR_MDD(pHalSsiAdaptor->MicrowireDirection);
    MwcrValue |= BIT_MWCR_MHS(pHalSsiAdaptor->MicrowireHandshaking);
    SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_MWCR Value: %X\n", Index, MwcrValue);

    HAL_SSI_WRITE32(Index, REG_DW_SSI_MWCR, MwcrValue);

    SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_MWCR(%X) = %X\n", Index,
            SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_MWCR,
            HAL_SSI_READ32(Index, REG_DW_SSI_MWCR));

    SSI_DBG_INIT("SSI%d TransferMechanism: %d\n", Index, pHalSsiAdaptor->TransferMechanism);
    if (pHalSsiAdaptor->TransferMechanism == SSI_DTM_INTERRUPT)
    {
        SSI_DBG_INIT("SSI%d Interrupt initialize, Interrupt: %X\n", Index, pHalSsiAdaptor->InterruptMask);
        switch (Index) {
            case 0:
                IrqNum = SPI0_IRQ;
                break;
            case 1:
                IrqNum = SPI1_IRQ;
                break;
            case 2:
                IrqNum = SPI2_IRQ;
                break;
            default:
                IrqNum = IRQ_UNKNOWN;
                break;
        }

        if (likely(IrqNum != IRQ_UNKNOWN)) {
            /* REG_DW_SSI_IMR */
            HalSsiSetInterruptMaskRtl8195a(Adaptor);

            pHalSsiAdaptor->IrqHandle.Data     = (u32)pHalSsiAdaptor;
            pHalSsiAdaptor->IrqHandle.IrqFun   = (IRQ_FUN)_SsiIrqHandleRtl8195a;
            pHalSsiAdaptor->IrqHandle.IrqNum   = (IRQn_Type)IrqNum;
            pHalSsiAdaptor->IrqHandle.Priority = pHalSsiAdaptor->InterruptPriority;

            InterruptRegister(&pHalSsiAdaptor->IrqHandle);
            InterruptEn(&pHalSsiAdaptor->IrqHandle);
        }
        else {
            SSI_DBG_INIT("Unknown SSI Index.\n");
            pHalSsiAdaptor->TransferMechanism = SSI_DTM_BASIC;
        }
    }

    HalSsiEnableRtl8195a(Adaptor);

    return HAL_OK;
}

HAL_Status HalSsiPinmuxEnableRtl8195a_Patch(VOID *Adaptor)
{
    SSI_DBG_ENTRANCE("HalSsiPinmuxEnableRtl8195a()\n");
    PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor;
    volatile HAL_Status Result;
    u32 PinmuxSelect = pHalSsiAdaptor->PinmuxSelect;
    u8  Index = pHalSsiAdaptor->Index;

    SSI_DBG_PINMUX("[1] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index,
            PERI_ON_BASE + REG_SPI_MUX_CTRL,
            HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL));

    switch (Index)
    {
        case 0:
        {
            ACTCK_SPI0_CCTRL(ON);
            SLPCK_SPI0_CCTRL(ON);
            PinCtrl(SPI0, PinmuxSelect, ON);
            SPI0_FCTRL(ON);
            Result = HAL_OK;
            break;
        }
        case 1:
        {
            ACTCK_SPI1_CCTRL(ON);
            SLPCK_SPI1_CCTRL(ON);
            PinCtrl(SPI1, PinmuxSelect, ON);
            SPI1_FCTRL(ON);
            Result = HAL_OK;
            break;
        }
        case 2:
        {
            ACTCK_SPI2_CCTRL(ON);
            SLPCK_SPI2_CCTRL(ON);
            PinCtrl(SPI2, PinmuxSelect, ON);
            SPI2_FCTRL(ON);
            Result = HAL_OK;
            break;
        }
        default:
        {
            DBG_SSI_ERR("Invalid SSI Index %d!\n", Index);
            Result = HAL_ERR_PARA;
            break;
        }
    }

    SSI_DBG_PINMUX("[2] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index,
            PERI_ON_BASE + REG_SPI_MUX_CTRL,
            HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL));

    return Result;
}

HAL_Status HalSsiPinmuxDisableRtl8195a(VOID *Adaptor)
{
    SSI_DBG_ENTRANCE("HalSsiPinmuxEnableRtl8195a()\n");
    PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor;
    volatile HAL_Status Result;
    u32 PinmuxSelect = pHalSsiAdaptor->PinmuxSelect;
    u8  Index = pHalSsiAdaptor->Index;

    SSI_DBG_PINMUX("[1] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index,
            PERI_ON_BASE + REG_SPI_MUX_CTRL,
            HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL));

    switch (Index)
    {
        case 0:
        {
            ACTCK_SPI0_CCTRL(OFF);
            SLPCK_SPI0_CCTRL(OFF);
            PinCtrl(SPI0, PinmuxSelect, OFF);
            SPI0_FCTRL(OFF);
            Result = HAL_OK;
            break;
        }
        case 1:
        {
            ACTCK_SPI1_CCTRL(OFF);
            SLPCK_SPI1_CCTRL(OFF);
            PinCtrl(SPI1, PinmuxSelect, OFF);
            SPI1_FCTRL(OFF);
            Result = HAL_OK;
            break;
        }
        case 2:
        {
            ACTCK_SPI2_CCTRL(OFF);
            SLPCK_SPI2_CCTRL(OFF);
            PinCtrl(SPI2, PinmuxSelect, OFF);
            SPI2_FCTRL(OFF);
            Result = HAL_OK;
            break;
        }
        default:
        {
            DBG_SSI_ERR("Invalid SSI Index %d!\n", Index);
            Result = HAL_ERR_PARA;
            break;
        }
    }

    SSI_DBG_PINMUX("[2] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index,
            PERI_ON_BASE + REG_SPI_MUX_CTRL,
            HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL));

    return Result;
}

HAL_Status HalSsiDeInitRtl8195a(VOID *Adapter)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    u8 Index;
    volatile HAL_Status Result;

    Index = pHalSsiAdapter->Index;

    if(Index > 2){
        DBG_SSI_ERR("Invalid SSI Index %d!\n", Index);
        return HAL_ERR_PARA;
    }

    HalSsiInterruptDisableRtl8195a(pHalSsiAdapter);  
    HalSsiDisableRtl8195a(pHalSsiAdapter);
    
    Result = HalSsiPinmuxDisableRtl8195a(pHalSsiAdapter);   
    if(Result != HAL_OK){
        DBG_SSI_ERR("Pinmux Disable Error\n");
        return Result;
    }

    return Result;
}

HAL_Status HalSsiClockOnRtl8195a(VOID *Adapter)
{
    PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adapter;
    volatile HAL_Status Result;
    u8 Index = pHalSsiAdaptor->Index;

    switch (Index)
    {
        case 0:
        {
            ACTCK_SPI0_CCTRL(ON);
            SLPCK_SPI0_CCTRL(ON);
            Result = HAL_OK;
            break;
        }
        case 1:
        {
            ACTCK_SPI1_CCTRL(ON);
            SLPCK_SPI1_CCTRL(ON);
            Result = HAL_OK;
            break;
        }
        case 2:
        {
            ACTCK_SPI2_CCTRL(ON);
            SLPCK_SPI2_CCTRL(ON);
            Result = HAL_OK;
            break;
        }
        default:
        {
            DBG_SSI_ERR("Invalid SSI Index %d!\n", Index);
            Result = HAL_ERR_PARA;
            break;
        }
    }
    return Result;
}

HAL_Status HalSsiClockOffRtl8195a(VOID *Adapter)
{
    PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adapter;
    volatile HAL_Status Result;
    u8 Index = pHalSsiAdaptor->Index;

    switch (Index)
    {
        case 0:
        {
            ACTCK_SPI0_CCTRL(OFF);
            SLPCK_SPI0_CCTRL(OFF);
            Result = HAL_OK;
            break;
        }
        case 1:
        {
            ACTCK_SPI1_CCTRL(OFF);
            SLPCK_SPI1_CCTRL(OFF);
            Result = HAL_OK;
            break;
        }
        case 2:
        {
            ACTCK_SPI2_CCTRL(OFF);
            SLPCK_SPI2_CCTRL(OFF);
            Result = HAL_OK;
            break;
        }
        default:
        {
            DBG_SSI_ERR("Invalid SSI Index %d!\n", Index);
            Result = HAL_ERR_PARA;
            break;
        }
    }
    return Result;

}

VOID HalSsiSetSclkRtl8195a(VOID *Adapter, u32 ClkRate)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    u32 ssi_clk;
    u32 ClockDivider;
    u32 SsiEn;
    u32 RegValue;
    u32 SystemClock;
    u8  spi_idx = pHalSsiAdapter->Index;

    // Set SCLK Freq only available for Master mode
    // For Slave mode, the baud rate is depends on the Master side, max rate = ssi_clk/2
    // Fsclk_out = Fssi_clk/SCKDV
    SystemClock = HalGetCpuClk();
    
    if (spi_idx == 1) {
        ssi_clk = SystemClock >> 1;
        RegValue = HAL_READ32(SYSTEM_CTRL_BASE, 0x250);
        if (ClkRate > (ssi_clk/2)) {
            // Use High speed clock: Fixed Freq.
            RegValue |= BIT18;
            ssi_clk = (200000000*5/6) >> 1;            
        }
        else {
            // Use Normal speed clock: CPU_Clk/2
            RegValue &= ~BIT18;
        }
        HAL_WRITE32(SYSTEM_CTRL_BASE, 0x250, RegValue);
    }
    else {
        ssi_clk = SystemClock >> 2;
    }

    if (pHalSsiAdapter->Role == SSI_MASTER) {
        if (ClkRate > (ssi_clk/2)) {
            DBG_SSI_ERR("spi_frequency: Freq %d is too high, available highest Freq=%d\r\n", ClkRate, (ssi_clk/2));
            ClockDivider = 2;
        } 
        else {
            ClockDivider = ssi_clk/ClkRate + 1;
            if ((ssi_clk%ClkRate) > (ClkRate/2)) {
                ClockDivider++;
            }
            if (ClockDivider >= 0xFFFF) {
                // devider is 16 bits
                ClockDivider = 0xFFFE;
            }
            ClockDivider &= 0xFFFE;     // bit 0 always is 0
        }
        DBG_SSI_INFO("spi_frequency: Set SCLK Freq=%d\r\n", (ssi_clk/ClockDivider));
        pHalSsiAdapter->ClockDivider = ClockDivider;
        
        SsiEn = HAL_SSI_READ32(spi_idx, REG_DW_SSI_SSIENR); // Backup SSI_EN register
        
        // Disable SSI first, so we can modify the Clock Divider
        RegValue = SsiEn & BIT_INVC_SSIENR_SSI_EN;
        HAL_SSI_WRITE32(spi_idx, REG_DW_SSI_SSIENR, RegValue);
        HAL_SSI_WRITE32(spi_idx, REG_DW_SSI_BAUDR, (pHalSsiAdapter->ClockDivider & 0xFFFF));
        // recover the SSI_EN setting
        HAL_SSI_WRITE32(spi_idx, REG_DW_SSI_SSIENR, SsiEn);
    }
    else {
        if (ClkRate > (ssi_clk/10)) {
            DBG_SSI_ERR("spi_frequency: Freq %d is too high, available highest Freq=%d\r\n", ClkRate, (ssi_clk/10));
        }
    }    
}

HAL_Status HalSsiIntReadRtl8195a(VOID *Adapter, VOID *RxData, u32 Length)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    u32 RxFifoThresholdLevel;
    u8  Index = pHalSsiAdapter->Index;

    DBG_SSI_INFO("HalSsiIntReadRtl8195a: Idx=%d, RxData=0x%x, Len=0x%x\r\n", Index, RxData, Length);
    if (HalSsiBusyRtl8195a(Adapter)) {
        // As a Slave mode, if the peer(Master) side is power off, the BUSY flag is always on
        DBG_SSI_WARN("HalSsiIntReadRtl8195a: SSI%d is busy\n", Index);
        return HAL_BUSY;
    }

    if (Length == 0) {
        SSI_DBG_INT_READ("SSI%d RxData addr: 0x%X, Length: %d\n", Index, RxData, Length);
        return HAL_ERR_PARA;
    }

    if (Length > (pHalSsiAdapter->DefaultRxThresholdLevel)) {
        RxFifoThresholdLevel = pHalSsiAdapter->DefaultRxThresholdLevel;
    }
    else {
        RxFifoThresholdLevel = 0;        
    }

    if (pHalSsiAdapter->RxThresholdLevel != RxFifoThresholdLevel) {
        DBG_SSI_INFO("Setting Rx FIFO Threshold Level to %d\n", RxFifoThresholdLevel);
        pHalSsiAdapter->RxThresholdLevel = RxFifoThresholdLevel;
        HalSsiSetRxFifoThresholdLevelRtl8195a((VOID*)pHalSsiAdapter);
    }

    if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
        // 16~9 bits mode
        pHalSsiAdapter->RxLength = Length >> 1; // 2 bytes(16 bit) every transfer
    }
    else {
        // 8~4 bits mode
        pHalSsiAdapter->RxLength = Length; // 1 byte(8 bit) every transfer
    }

    pHalSsiAdapter->RxData = RxData;
    DBG_SSI_INFO("SSI%d RxData addr: 0x%X, Length: %d\n", Index,
            pHalSsiAdapter->RxData, pHalSsiAdapter->RxLength);

    pHalSsiAdapter->InterruptMask |= BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM;
    HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);

    return HAL_OK;
}


HAL_Status
HalSsiIntWriteRtl8195a(
    IN VOID *Adapter,      // PHAL_SSI_ADAPTOR
    IN u8 *pTxData,     // the Buffer to be send
    IN u32 Length      // the length of data to be send
)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;

    DBG_SSI_INFO("HalSsiIntWriteRtl8195a: Idx=%d, RxData=0x%x, Len=0x%x\r\n", pHalSsiAdapter->Index, pTxData, Length);
    if ((Length == 0)) {
        DBG_SSI_ERR("HalSsiIntSendRtl8195a: Err: pTxData=0x%x,  Length=%d\n", pTxData, Length);
        return HAL_ERR_PARA;
    }

    if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
        // 16~9 bits mode
        pHalSsiAdapter->TxLength = Length >> 1; // 2 bytes(16 bit) every transfer
    }
    else {
        // 8~4 bits mode
        pHalSsiAdapter->TxLength = Length; // 1 byte(8 bit) every transfer
    }

    pHalSsiAdapter->TxData = (void*)pTxData;
    pHalSsiAdapter->InterruptMask |= BIT_IMR_TXOIM | BIT_IMR_TXEIM;
    HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);

    return HAL_OK;
}

#ifdef CONFIG_GDMA_EN
/**
 * GDMA IRQ Handler
 */
VOID SsiTxGdmaIrqHandle (VOID *Data)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Data;
    PSSI_DMA_CONFIG pDmaConfig = &pHalSsiAdapter->DmaConfig;;
    PHAL_GDMA_ADAPTER pHalGdmaAdapter;
    PHAL_GDMA_OP pHalGdmaOp;

    pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter;
    pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp;

    /* Maintain Block Count */
#if 0
    u8  IsrTypeMap = 0;
    /* Clear Pending ISR */
    IsrTypeMap = pHalGdmaOp->HalGdmaChIsrClean((VOID*)pHalGdmaAdapter);

    if (IsrTypeMap & BlockType) {
        DBG_SSI_WARN("DMA Block %d\n",pHalGdmaAdapter->MuliBlockCunt);
        pHalGdmaAdapter->MuliBlockCunt++;
    }
#else
    /* Clear Pending ISR */
    pHalGdmaOp->HalGdmaChIsrClean((VOID*)pHalGdmaAdapter);
#endif

#if 0
    /* Set SSI DMA Disable */
    HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \
        (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) & ~SSI_TXDMA_ENABLE));
#endif
    pHalGdmaOp->HalGdmaChDis((VOID*)(pHalGdmaAdapter));
    // Call user TX complete callback
    if (NULL != pHalSsiAdapter->TxCompCallback) {
        pHalSsiAdapter->TxCompCallback(pHalSsiAdapter->TxCompCbPara);
    }
}

VOID SsiRxGdmaIrqHandle (VOID *Data)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Data;
    PSSI_DMA_CONFIG pDmaConfig = &pHalSsiAdapter->DmaConfig;;
    PHAL_GDMA_ADAPTER pHalGdmaAdapter;
    PHAL_GDMA_OP pHalGdmaOp;


    pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter;
    pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp;

    /* Maintain Block Count */
#if 0
    u8  IsrTypeMap = 0;
    /* Clear Pending ISR */
    IsrTypeMap = pHalGdmaOp->HalGdmaChIsrClean((VOID*)pHalGdmaAdapter);

    if (IsrTypeMap & BlockType) {
        DBG_SSI_WARN("DMA Block %d\n",pHalGdmaAdapter->MuliBlockCunt);
        pHalGdmaAdapter->MuliBlockCunt++;
    }
#else
    /* Clear Pending ISR */
    pHalGdmaOp->HalGdmaChIsrClean((VOID*)pHalGdmaAdapter);

#endif

    /* Set SSI DMA Disable */
    HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \
        (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) & ~SSI_RXDMA_ENABLE));
    pHalGdmaOp->HalGdmaChDis((VOID*)(pHalGdmaAdapter));
    // Call user RX complete callback
    if (NULL != pHalSsiAdapter->RxCompCallback) {
        pHalSsiAdapter->RxCompCallback(pHalSsiAdapter->RxCompCbPara);
    }

}

VOID
HalSsiTxGdmaLoadDefRtl8195a(
        IN VOID *Adapter
)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    PSSI_DMA_CONFIG pDmaConfig;
    PHAL_GDMA_ADAPTER pHalGdmaAdapter;
    u8 *pDst;
    u8 DmaIdx;
    u8 DmaCh;
    u8 DstPer;
    u8 ssi_idx;
    u32 DmaChEn;
    IRQn_Type IrqNum;

    if ((NULL == pHalSsiAdapter)) {
        return;
    }
    pDmaConfig = &pHalSsiAdapter->DmaConfig;

    ssi_idx = pHalSsiAdapter->Index;
    pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter;
    if (NULL == pHalGdmaAdapter) {
        DBG_SSI_ERR("HalSsiTxGdmaLoadDefRtl8195a: HalGdmaAdapter is NULL\r\n");
        return;
    }
    _memset((void *)pHalGdmaAdapter, 0, sizeof(HAL_GDMA_ADAPTER));

    pHalSsiAdapter->DmaControl |= SSI_TXDMA_ENABLE;
    if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
        // 16~9 bits mode
        pHalSsiAdapter->DmaTxDataLevel = 48;  // When TX FIFO entity number <=48 then DMA Request asserted
    }
    else {
        // 8~4 bits mode
        pHalSsiAdapter->DmaTxDataLevel = 56;  // When TX FIFO entity number <=56 then DMA Request asserted
    }

    switch (ssi_idx) {
        case 0:
            pDst = (u8*) (SSI0_REG_BASE + REG_DW_SSI_DR);
            DmaIdx = 0;
            DmaCh = 1;
            DmaChEn = GdmaCh1;
            IrqNum = GDMA0_CHANNEL1_IRQ;
            DstPer = GDMA_HANDSHAKE_SSI0_TX;
            break;

        case 1:
            pDst = (u8*) (SSI1_REG_BASE + REG_DW_SSI_DR);
            DmaIdx = 1;
            DmaCh = 1;
            DmaChEn = GdmaCh1;
            IrqNum = GDMA1_CHANNEL1_IRQ;
            DstPer = GDMA_HANDSHAKE_SSI1_TX;
            break;

        case 2:
            pDst = (u8*) (SSI2_REG_BASE + REG_DW_SSI_DR);
            DmaIdx = 0;     // SPI2 TX only can use GDMA0
            DmaCh = 3;
            DmaChEn = GdmaCh3;
            IrqNum = GDMA0_CHANNEL3_IRQ;
            DstPer = GDMA_HANDSHAKE_SSI2_TX;
            break;

        default:
            return;
    }

    pHalGdmaAdapter->GdmaCtl.TtFc      = TTFCMemToPeri;
    pHalGdmaAdapter->GdmaCtl.Done      = 1;
    pHalGdmaAdapter->MuliBlockCunt     = 0;
    pHalGdmaAdapter->MaxMuliBlock      = 1;
    pHalGdmaAdapter->GdmaCfg.DestPer   = DstPer;
    pHalGdmaAdapter->ChDar = (u32)pDst;
    pHalGdmaAdapter->GdmaIndex   = DmaIdx;
    pHalGdmaAdapter->ChNum       = DmaCh;
    pHalGdmaAdapter->ChEn        = DmaChEn;
    pHalGdmaAdapter->GdmaIsrType = (BlockType|TransferType|ErrType);
    pHalGdmaAdapter->IsrCtrl     = ENABLE;
    pHalGdmaAdapter->GdmaOnOff   = ON;

    pHalGdmaAdapter->GdmaCtl.IntEn      = 1;
    pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeOne;
    pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeFour;
    pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes;
    pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthOneByte;
    pHalGdmaAdapter->GdmaCtl.Dinc = NoChange;
    pHalGdmaAdapter->GdmaCtl.Sinc = IncType;

    pDmaConfig->TxGdmaIrqHandle.Data = (u32)pHalSsiAdapter;
    pDmaConfig->TxGdmaIrqHandle.IrqNum = IrqNum;
    pDmaConfig->TxGdmaIrqHandle.IrqFun = (IRQ_FUN)SsiTxGdmaIrqHandle;
    pDmaConfig->TxGdmaIrqHandle.Priority = 0x10;
}


VOID
HalSsiRxGdmaLoadDefRtl8195a(
    IN VOID *Adapter
)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    PSSI_DMA_CONFIG pDmaConfig;
    PHAL_GDMA_ADAPTER pHalGdmaAdapter;
    u8  *pSrc;
    u8 DmaIdx;
    u8 DmaCh;
    u8 SrcPer;
    u8 ssi_idx;
    u32 DmaChEn;
    IRQn_Type IrqNum;

    if ((NULL == pHalSsiAdapter)) {
        return;
    }

    pDmaConfig = &pHalSsiAdapter->DmaConfig;

    ssi_idx = pHalSsiAdapter->Index;

    pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter;
    if (NULL == pHalGdmaAdapter) {
        return;
    }

    _memset((void *)pHalGdmaAdapter, 0, sizeof(HAL_GDMA_ADAPTER));

    pHalSsiAdapter->DmaControl |= SSI_RXDMA_ENABLE;
    if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
        // 16~9 bits mode
        pHalSsiAdapter->DmaRxDataLevel = 7;  // RX FIFO stored bytes > (DMARDLR(7) + 1) then request DMA transfer
    }
    else {
        // 8~4 bits mode
        pHalSsiAdapter->DmaRxDataLevel = 3;  // RX FIFO stored bytes > (DMARDLR(3) + 1) then request DMA transfer
    }
    switch (ssi_idx) {
        case 0:
            pSrc = (u8*) (SSI0_REG_BASE + REG_DW_SSI_DR);
            DmaIdx = 0;
            DmaCh = 2;
            DmaChEn = GdmaCh2;
            SrcPer = GDMA_HANDSHAKE_SSI0_RX;
            IrqNum = GDMA0_CHANNEL2_IRQ;
            break;

        case 1:
            pSrc = (u8*) (SSI1_REG_BASE + REG_DW_SSI_DR);
            DmaIdx = 1;
            DmaCh = 2;
            DmaChEn = GdmaCh2;
            SrcPer = GDMA_HANDSHAKE_SSI1_RX;
            IrqNum = GDMA1_CHANNEL2_IRQ;
            break;

        case 2:
            pSrc = (u8*) (SSI2_REG_BASE + REG_DW_SSI_DR);
            DmaIdx = 1; // SSI2 RX only can use GDMA1
            DmaCh = 3;
            DmaChEn = GdmaCh3;
            SrcPer = GDMA_HANDSHAKE_SSI2_RX;
            IrqNum = GDMA1_CHANNEL3_IRQ;
            break;

        default:
            return;
    }

    pHalGdmaAdapter->GdmaCtl.TtFc      = TTFCPeriToMem;
    pHalGdmaAdapter->GdmaCtl.Done      = 1;
    pHalGdmaAdapter->GdmaCfg.ReloadSrc = 1;
    pHalGdmaAdapter->GdmaCfg.SrcPer    = SrcPer;
    pHalGdmaAdapter->MuliBlockCunt     = 0;
    pHalGdmaAdapter->MaxMuliBlock      = 1;
    pHalGdmaAdapter->ChSar = (u32)pSrc;
    pHalGdmaAdapter->GdmaIndex   = DmaIdx;
    pHalGdmaAdapter->ChNum       = DmaCh;
    pHalGdmaAdapter->ChEn        = DmaChEn;
    pHalGdmaAdapter->GdmaIsrType = (BlockType|TransferType|ErrType);
    pHalGdmaAdapter->IsrCtrl     = ENABLE;
    pHalGdmaAdapter->GdmaOnOff   = ON;

    pHalGdmaAdapter->GdmaCtl.IntEn      = 1;
    pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeEight;
    pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeFour;
    pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthTwoBytes;
    pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes;
    pHalGdmaAdapter->GdmaCtl.Dinc = IncType;
    pHalGdmaAdapter->GdmaCtl.Sinc = NoChange;

    pDmaConfig->RxGdmaIrqHandle.Data = (u32)pHalSsiAdapter;
    pDmaConfig->RxGdmaIrqHandle.IrqNum = IrqNum;
    pDmaConfig->RxGdmaIrqHandle.IrqFun = (IRQ_FUN)SsiRxGdmaIrqHandle;
    pDmaConfig->RxGdmaIrqHandle.Priority = 0x10;
}

VOID
HalSsiDmaInitRtl8195a(
    IN VOID *Adapter
)
{
    u32 RegValue;
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    PSSI_DMA_CONFIG pDmaConfig;
    PHAL_GDMA_ADAPTER pTxHalGdmaAdapter;
    PHAL_GDMA_ADAPTER pRxHalGdmaAdapter;
    u8 ssi_idx;
    u32 hdk_tx_bit;
    u32 hdk_rx_bit;
    u32 DmatdlrValue = 0;
    u32 DmardlrValue = 0;

    pDmaConfig = &pHalSsiAdapter->DmaConfig;
    pTxHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter;
    pRxHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter;
    ssi_idx = pHalSsiAdapter->Index;

    // Set REG_PESOC_SOC_CTRL[28:16] to configure the GDMA handshake connection
    // SSI2 handshake connection is hardware fixed
    if (ssi_idx != 2) {
        hdk_tx_bit = 16+pTxHalGdmaAdapter->GdmaCfg.DestPer;
        hdk_rx_bit = 16+pRxHalGdmaAdapter->GdmaCfg.SrcPer;
    }
    else {
        hdk_tx_bit = 0;
        hdk_rx_bit = 0;
    }

    HalSsiDisableRtl8195a(pHalSsiAdapter);
    
    RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL);
    if (pHalSsiAdapter->DmaControl & SSI_TXDMA_ENABLE) {
        // TX DMA is enabled
        if (pTxHalGdmaAdapter->GdmaIndex ==0) {
            ACTCK_GDMA0_CCTRL(ON);
            GDMA0_FCTRL(ON);
            if (hdk_tx_bit != 0) {
                RegValue &= ~(1<<hdk_tx_bit);
            }
        }
        else {
            ACTCK_GDMA1_CCTRL(ON);
            GDMA1_FCTRL(ON);
            if (hdk_tx_bit != 0) {
                RegValue |= (1<<hdk_tx_bit);
            }
        }
        /* Set TX FIFO water level to trigger Tx DMA transfer */
        DmatdlrValue = BIT_DMATDLR_DMATDL(pHalSsiAdapter->DmaTxDataLevel);
        HAL_SSI_WRITE32(ssi_idx, REG_DW_SSI_DMATDLR, DmatdlrValue);

        /* Set SSI DMA Enable */
        HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \
            (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) | SSI_TXDMA_ENABLE));
    }

    if (pHalSsiAdapter->DmaControl & SSI_RXDMA_ENABLE) {
        // RX DMA is enabled
        if (pRxHalGdmaAdapter->GdmaIndex ==0) {
            ACTCK_GDMA0_CCTRL(ON);
            GDMA0_FCTRL(ON);
            if (hdk_rx_bit != 0) {
                RegValue &= ~(1<<hdk_rx_bit);
            }
        }
        else {
            ACTCK_GDMA1_CCTRL(ON);
            GDMA1_FCTRL(ON);
            if (hdk_rx_bit != 0) {
                RegValue |= (1<<hdk_rx_bit);
            }
        }
        /* Set RX FIFO water level to trigger Rx DMA transfer */
        DmardlrValue = BIT_DMARDLR_DMARDL(pHalSsiAdapter->DmaRxDataLevel);
        HAL_SSI_WRITE32(ssi_idx, REG_DW_SSI_DMARDLR, DmardlrValue);        
        // the RX DMA will be enabled at read start.
    }

    HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, RegValue);

    HalSsiEnableRtl8195a(pHalSsiAdapter);
}

HAL_Status
HalSsiDmaSendRtl8195a(
    IN VOID *Adapter,      // PHAL_SSI_ADAPTOR
    IN u8 *pTxData,     // the Buffer to be send
    IN u32 Length      // the length of data to be send
)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    PSSI_DMA_CONFIG pDmaConfig;
    PHAL_GDMA_ADAPTER pHalGdmaAdapter;
    PHAL_GDMA_OP pHalGdmaOp;


    if ((pTxData == NULL) || (Length == 0)) {
        DBG_SSI_ERR("HalSsiDmaSendRtl8195a: Err: pTxData=0x%x,  Length=%d\n", pTxData, Length);
        return HAL_ERR_PARA;
    }
    pDmaConfig = &pHalSsiAdapter->DmaConfig;
    pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter;

    pHalSsiAdapter->TxLength = Length;
    pHalSsiAdapter->TxData = (void*)pTxData;

    // Cofigure GDMA transfer
    if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
        // 16~9 bits mode
        if (((Length & 0x03)==0) &&
            (((u32)(pTxData) & 0x03)==0)) {
            // 4-bytes aligned, move 4 bytes each transfer
            pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeFour;
            pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes;
            pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeEight;
            pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthTwoBytes;
            pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 2;
        }
        else if (((Length & 0x01)==0) &&
            (((u32)(pTxData) & 0x01)==0)) {
            // 2-bytes aligned, move 2 bytes each transfer
            pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeEight;
            pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthTwoBytes;
            pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeEight;
            pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthTwoBytes;
            pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 1;
        }
        else {
            DBG_SSI_ERR("HalSsiDmaSendRtl8195a: Aligment Err: pTxData=0x%x,  Length=%d\n", pTxData, Length);
            return HAL_ERR_PARA;
        }
    }
    else {
        // 8~4 bits mode
        if (((Length & 0x03)==0) &&
            (((u32)(pTxData) & 0x03)==0)) {
            // 4-bytes aligned, move 4 bytes each transfer
            pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeOne;
            pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes;
            pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 2;
        }
        else {
            pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeFour;
            pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthOneByte;
            pHalGdmaAdapter->GdmaCtl.BlockSize = Length;
        }
        pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeFour;
        pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthOneByte;
    }

    DBG_SSI_INFO("SrcMsize=%d SrcTrWidth=%d DestMsize=%d DstTrWidth=%d BlockSize=%d\n", \
        pHalGdmaAdapter->GdmaCtl.SrcMsize, pHalGdmaAdapter->GdmaCtl.SrcTrWidth, \
        pHalGdmaAdapter->GdmaCtl.DestMsize, pHalGdmaAdapter->GdmaCtl.DstTrWidth, \
        pHalGdmaAdapter->GdmaCtl.BlockSize);

    if (pHalGdmaAdapter->GdmaCtl.BlockSize > 4096) {
        // over Maximum block size 4096
        DBG_SSI_ERR("HalSsiDmaSendRtl8195a: GDMA Block Size(%d) too big\n", pHalGdmaAdapter->GdmaCtl.BlockSize);
        return HAL_ERR_PARA;
    }

    pHalGdmaAdapter->ChSar = (u32)pTxData;

    // Enable GDMA for TX
    pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp;
    pHalGdmaOp->HalGdmaChSeting((VOID*)(pHalGdmaAdapter));
    pHalGdmaOp->HalGdmaChEn((VOID*)(pHalGdmaAdapter));

#if 0
    /* Set SSI DMA Enable */
    // TODO: protect the enable DMA register, it may collision with the DMA disable in the GDMA done ISR
    HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \
        (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) | SSI_TXDMA_ENABLE));
#endif

    return HAL_OK;
}

HAL_Status
HalSsiDmaRecvRtl8195a(
    IN VOID *Adapter,      // PHAL_SSI_ADAPTOR
    IN u8  *pRxData,  ///< Rx buffer
    IN u32 Length      // buffer length
)
{
    PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter;
    PSSI_DMA_CONFIG pDmaConfig;
    PHAL_GDMA_ADAPTER pHalGdmaAdapter;
    PHAL_GDMA_OP pHalGdmaOp;

    if ((pRxData == NULL) || (Length == 0)) {
        DBG_SSI_ERR("HalRuartDmaRecvRtl8195a: Null Err: pRxData=0x%x, Length=%d\n",  pRxData, Length);
        return HAL_ERR_PARA;
    }
    
    pDmaConfig = &pHalSsiAdapter->DmaConfig;
    pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter;

    pHalSsiAdapter->RxLength = Length;
    pHalSsiAdapter->RxData = (void*)pRxData;

    // Cofigure GDMA transfer
    if ((pHalSsiAdapter->DataFrameSize+1) > 8) {
        // 16~9 bits mode
        pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeEight;
        pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthTwoBytes;
        pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 1;

        if (((Length & 0x03)==0) &&
            (((u32)(pRxData) & 0x03)==0)) {
            // 4-bytes aligned, move 4 bytes each transfer
            pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeFour;
            pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes;
        }
        else if (((Length & 0x01)==0) &&
            (((u32)(pRxData) & 0x01)==0)) {
            // 2-bytes aligned, move 2 bytes each transfer
            pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeEight;
            pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthTwoBytes;
        }
        else {
            DBG_SSI_ERR("HalSsiDmaSendRtl8195a: Aligment Err: pTxData=0x%x,  Length=%d\n", pRxData, Length);
            return HAL_ERR_PARA;
        }
    }
    else {
        // 8~4 bits mode
        pHalGdmaAdapter->GdmaCtl.SrcMsize   = MsizeFour;
        pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthOneByte;
        pHalGdmaAdapter->GdmaCtl.BlockSize = Length;
        if (((Length & 0x03)==0) &&
            (((u32)(pRxData) & 0x03)==0)) {
            // 4-bytes aligned, move 4 bytes each transfer
            pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeOne;
            pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes;
        }
        else {
            pHalGdmaAdapter->GdmaCtl.DestMsize  = MsizeFour;
            pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthOneByte;
        }
    }

    if (pHalGdmaAdapter->GdmaCtl.BlockSize > 4096) {
        // over Maximum block size 4096
        DBG_SSI_ERR("HalRuartDmaRecvRtl8195a: GDMA Block Size(%d) too big\n", pHalGdmaAdapter->GdmaCtl.BlockSize);
        return HAL_ERR_PARA;
    }

    pHalGdmaAdapter->ChDar = (u32)pRxData;

    // Enable GDMA for RX
    pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp;
    pHalGdmaOp->HalGdmaChSeting((VOID*)(pHalGdmaAdapter));
    pHalGdmaOp->HalGdmaChEn((VOID*)(pHalGdmaAdapter));

    /* Set SSI DMA Enable */
    // TODO: protect the enable DMA register, it may collision with the DMA disable in the GDMA TX done ISR
    HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \
        (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) | SSI_RXDMA_ENABLE));

    return HAL_OK;
}

#endif // end of "#ifdef CONFIG_GDMA_EN"
