/* mbed Microcontroller Library
 *******************************************************************************
 * Copyright (c) 2015, Realtek Semiconductor Corp.
 * All rights reserved.
 *
 * This module is a confidential and proprietary property of RealTek and
 * possession or use of this module requires written permission of RealTek.
 *******************************************************************************
 */
#include "objects.h"
#include "i2s_api.h"
#include "pinmap.h"

#if CONFIG_I2S_EN
static const PinMap PinMap_I2S_TX[] = {
    {PE_2,  RTL_PIN_PERI(I2S0, 0, S0), RTL_PIN_FUNC(I2S0, S0)},
    {PH_2,  RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)},
    {PD_2,  RTL_PIN_PERI(I2S0, 0, S2), RTL_PIN_FUNC(I2S0, S2)},
    {PC_7,  RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)},
    {PC_2,  RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)},
    {PD_6,  RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)},
    {PE_6,  RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S2)},
    {NC,    NC,     0}
};

static const PinMap PinMap_I2S_RX[] = {
    {PH_5,  RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)},
    {PC_5,  RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)},
    {PC_4,  RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)},
    {PD_3,  RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)},
    {PE_8,  RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S1)},
    {NC,    NC,     0}
};

static const PinMap PinMap_I2S_CLK[] = {
    {PE_1,  RTL_PIN_PERI(I2S0, 0, S0), RTL_PIN_FUNC(I2S0, S0)},
    {PH_1,  RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)},
    {PD_1,  RTL_PIN_PERI(I2S0, 0, S2), RTL_PIN_FUNC(I2S0, S2)},
    {PC_8,  RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)},
    {PC_1,  RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)},
    {PD_5,  RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)},
    {PE_5,  RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S2)},
    {NC,    NC,     0}
};

static const PinMap PinMap_I2S_WS[] = {
    {PE_0,  RTL_PIN_PERI(I2S0, 0, S0), RTL_PIN_FUNC(I2S0, S0)},
    {PH_0,  RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)},
    {PD_0,  RTL_PIN_PERI(I2S0, 0, S2), RTL_PIN_FUNC(I2S0, S2)},
    {PC_9,  RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)},
    {PC_0,  RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)},
    {PD_4,  RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)},
    {PE_4,  RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S2)},
    {NC,    NC,     0}
};

static const HAL_I2S_DEF_SETTING I2SDefaultSetting = {
    .I2SMaster = I2S_MASTER_MODE,       // I2S Function Mode
    .DevSts = I2S_STS_UNINITIAL,        //I2S device status
    .I2SChNum = I2S_CH_STEREO,          //I2S Channel number mono or stereo
    .I2SPageNum = I2S_4PAGE,            //I2S Page number 2~4
    .I2STRxAct = I2S_TXRX,              //I2S tx rx act, tx only or rx only or tx+rx
    .I2SWordLen = I2S_WL_16,            //I2S Word length 16bit or 24bit
    .I2SPageSize = (768/4)-1,           //I2S Page size 1~4096 word
    .I2SRate = I2S_SR_48KHZ,           //I2S sample rate 8k ~ 96khz
    
    .I2STxIntrMSK = I2S_TX_INT_PAGE0_OK|I2S_TX_INT_PAGE1_OK| \
                    I2S_TX_INT_PAGE2_OK|I2S_TX_INT_PAGE3_OK,   /*I2S Tx Interrupt Mask*/
    .I2SRxIntrMSK = I2S_RX_INT_PAGE0_OK|I2S_RX_INT_PAGE1_OK| \
                    I2S_RX_INT_PAGE2_OK|I2S_RX_INT_PAGE3_OK   /*I2S Rx Interrupt Mask*/
};

void i2s_init(i2s_t *obj, PinName sck, PinName ws, PinName sd)
{
    uint32_t i2s_sck, i2s_ws, i2s_tx, i2s_rx;
    uint32_t i2s_sck_ws, i2s_sel;;
    uint8_t i2s_idx;
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    u8 trx_act;

    // Determine the UART to use (UART0, UART1, or UART3)
    i2s_sck = pinmap_peripheral(sck, PinMap_I2S_CLK);
    i2s_ws = pinmap_peripheral(ws, PinMap_I2S_WS);
    i2s_tx = pinmap_peripheral(sd, PinMap_I2S_TX);
    i2s_rx = pinmap_peripheral(sd, PinMap_I2S_RX);

    i2s_sck_ws = pinmap_merge(i2s_sck, i2s_ws);
    if (unlikely(i2s_sck_ws == NC)) {
        DBG_I2S_ERR("%s: Cannot find matched I2S for given pin\n", __FUNCTION__);
        return;
    }

    trx_act = I2S_TXRX;
    i2s_sel = pinmap_merge(i2s_sck_ws, i2s_tx);
    if (i2s_sel == NC) {
        i2s_sel = pinmap_merge(i2s_sck_ws, i2s_rx);
        trx_act = I2S_ONLY_RX;
        if (unlikely(i2s_sel == NC)) {
            DBG_I2S_ERR("%s: Cannot find matched I2S for given pin\n", __FUNCTION__);
            return;            
        }
    }
    
    i2s_idx = RTL_GET_PERI_IDX(i2s_sel);
	
	pI2SAdapter->DevNum =  i2s_idx;
	pI2SAdapter->PinMux =  RTL_GET_PERI_SEL(i2s_sel);;
    DBG_I2S_INFO("%s: Use I2S%d Sel%d\r\n", __FUNCTION__, pI2SAdapter->DevNum, pI2SAdapter->PinMux);

	pI2SAdapter->pInitDat = &obj->InitDat;
	RtkI2SLoadDefault(pI2SAdapter, (VOID*)&I2SDefaultSetting);
    pI2SAdapter->pInitDat->I2STRxAct = trx_act;

    // Load user defined parameters
	pI2SAdapter->pInitDat->I2SChNum = obj->channel_num;
	pI2SAdapter->pInitDat->I2SRate = obj->sampling_rate;
	pI2SAdapter->pInitDat->I2SWordLen = obj->word_length;	
	pI2SAdapter->pInitDat->I2STRxAct = obj->direction;	

    RtkI2SInit(pI2SAdapter);
}

void i2s_set_dma_buffer(i2s_t *obj, char *tx_buf, char *rx_buf, 
    uint32_t page_num, uint32_t page_size)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    u32 i;

    if ((page_num < 2) || (page_num > 4) || (page_size < 8)) {
        DBG_I2S_INFO("%s: PageNum(%d) valid value is 2~4; PageSize(%d must > 8)\r\n", \
            __FUNCTION__, page_num, page_size);
        return;
    }
    
    pI2SAdapter->pInitDat->I2SPageNum = page_num - 1; 
    pI2SAdapter->pInitDat->I2SPageSize = page_size/4 - 1;    // unit is 4-bytes
    pI2SAdapter->pInitDat->I2STxData = (u8*)tx_buf;
    pI2SAdapter->pInitDat->I2SRxData = (u8*)rx_buf;
    HalI2SSetDMABuf(pI2SAdapter->pInitDat);

    for (i=0;i<page_num;i++) {
        pI2SAdapter->TxPageList[i] = (uint32_t*)(tx_buf + ((page_size) * i)); 
        pI2SAdapter->RxPageList[i] = (uint32_t*)(rx_buf + ((page_size) * i)); 
    }
}

void i2s_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    
    pI2SAdapter->UserCB.TxCCB = handler;
    pI2SAdapter->UserCB.TxCBId = id;
}

void i2s_rx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    
    pI2SAdapter->UserCB.RxCCB = handler;
    pI2SAdapter->UserCB.RxCBId = id;
}

void i2s_set_direction(i2s_t *obj, int trx_type)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;

    obj->direction = trx_type;
	pI2SAdapter->pInitDat->I2STRxAct = trx_type;
    HalI2SSetDirection(pI2SAdapter->pInitDat);
}

void i2s_set_param(i2s_t *obj, int channel_num, int rate, int word_len)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
	
    obj->channel_num = channel_num;
    obj->sampling_rate = rate;
    obj->word_length = word_len;
	pI2SAdapter->pInitDat->I2SChNum = channel_num;
	pI2SAdapter->pInitDat->I2SRate = rate;
	pI2SAdapter->pInitDat->I2SWordLen = word_len;
	HalI2SSetChNum(pI2SAdapter->pInitDat);
    HalI2SSetRate(pI2SAdapter->pInitDat);
    HalI2SSetWordLen(pI2SAdapter->pInitDat);
}

void i2s_deinit(i2s_t *obj)
{
	RtkI2SDeInit((VOID*)&obj->I2SAdapter);
}

int* i2s_get_tx_page(i2s_t *obj)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    u8 page_idx;

    page_idx = HalI2SGetTxPage((VOID*)pI2SAdapter->pInitDat);
    if (page_idx <= pI2SAdapter->pInitDat->I2SPageNum) {
        return ((int*)pI2SAdapter->TxPageList[page_idx]);
    } else {
        return NULL;
    }
}

void i2s_send_page(i2s_t *obj, uint32_t *pbuf)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    u32 page_num, i;

    page_num = pI2SAdapter->pInitDat->I2SPageNum + 1;
    for (i=0;i<page_num;i++) {
        if (pI2SAdapter->TxPageList[i] == pbuf) {
            HalI2SPageSend(pI2SAdapter->pInitDat, i);
            break;  // break the for loop
        }
    }

    if (i == page_num) {
        DBG_I2S_ERR("i2s_send_page: the pbuf(0x%x) is not a DMA buffer\r\n", pbuf);
    }
}

void i2s_recv_page(i2s_t *obj)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;

    HalI2SPageRecv(pI2SAdapter->pInitDat);
}

void i2s_enable(i2s_t *obj)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    
    RtkI2SEnable(pI2SAdapter);
}

void i2s_disable(i2s_t *obj)
{
    PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter;
    
    RtkI2SDisable(pI2SAdapter);
}

#endif  // end of "#if CONFIG_I2S_EN"
