/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2017 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "board.h"
#include "fsl_flexio_i2s.h"
#include "fsl_debug_console.h"
#include "fsl_codec_common.h"

#include "clock_config.h"
#include "pin_mux.h"
#include "fsl_codec_adapter.h"

#include "fsl_i2c.h"  					
#include "fsl_wm8904.h"
#include "music.h"

#include "fsl_dmamux.h"
#include "fsl_flexio_i2s_dma.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
 #define LINE_IN 1																	// Uuer need connect Phone and MAO using 'LINE IN'. 
 #define Music_saved_in_Flash 0											// Play audio stored in flash. 
 #define MCLK_FLEXIO_USED 1     										// This means user can use MCLK provided by TIMER2 or not. 
 
/*--Relevant PIN*/
#define BCLK_PIN 4U
#define FRAME_SYNC_PIN 5U
#define TX_DATA_PIN 3U
#define RX_DATA_PIN 6U
#define MCLK_PIN 2U   

/*DMA definition*/
#define EXAMPLE_DMA DMA0
#define EXAMPLE_TX_CHANNEL 0U
#define EXAMPLE_RX_CHANNEL 1U
#define EXAMPLE_TX_DMA_SOURCE 10U
#define EXAMPLE_RX_DMA_SOURCE 11U
		
/* SAI and I2C instance and clock */
#define DEMO_FLEXIO_BASE FLEXIO

#define DEMO_FLEXIO_CLKSRC kCLOCK_CoreSysClk
#define DEMO_FLEXIO_CLK_FREQ CLOCK_GetFreq(kCLOCK_CoreSysClk)


#define OVER_SAMPLE_RATE (384)
#define BUFFER_SIZE (256)
#define BUFFER_NUM (4)
#define PLAY_COUNT (5000 * 2U)
#define ZERO_BUFFER_SIZE (BUFFER_SIZE * 2)

/* demo audio sample rate */
#define DEMO_AUDIO_SAMPLE_RATE (kFLEXIO_I2S_SampleRate48KHz)
/* demo audio master clock */
#define DEMO_AUDIO_MASTER_CLOCK DEMO_SAI_CLK_FREQ
/* demo audio data channel */
#define DEMO_AUDIO_DATA_CHANNEL (2U)
/* demo audio bit width */
#define DEMO_AUDIO_BIT_WIDTH (kFLEXIO_I2S_WordWidth16bits)

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
void FLEXIO_I2S_Init_FOR_MCLK(FLEXIO_I2S_Type *base, const flexio_i2s_config_t *config);

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*--copy from KL43's DMA...*/
flexio_i2s_dma_handle_t txHandle = {0};
flexio_i2s_dma_handle_t rxHandle = {0};
dma_handle_t txDmaHandle = {0};
dma_handle_t rxDmaHandle = {0};

static uint8_t __ALIGN_BEGIN txBuff[BUFFER_SIZE] __ALIGN_END;
static uint8_t __ALIGN_BEGIN rxBuff[BUFFER_SIZE] __ALIGN_END;
		

__ALIGN_BEGIN static uint8_t s_Buffer[400] __ALIGN_END; /* 100 samples => time about 2 ms */
extern codec_config_t boardCodecConfig;
uint8_t codecHandleBuffer[CODEC_HANDLE_SIZE] = {0U};
codec_handle_t *codecHandle                  = (codec_handle_t *)codecHandleBuffer;

wm8904_fll_config_t fll = {
    .source         = kWM8904_FLLClkSourceMCLK,
    .refClock_HZ    = 24000000U,
    .outputClock_HZ = 12288000U,
};
wm8904_config_t wm8904Config = {
    .i2cConfig    = {.codecI2CInstance = BOARD_CODEC_I2C_INSTANCE, .codecI2CSourceClock = BOARD_CODEC_I2C_CLOCK_FREQ},
    .recordSource = kWM8904_RecordSourceLineInput,
    .recordChannelLeft  = kWM8904_RecordChannelLeft2,
    .recordChannelRight = kWM8904_RecordChannelRight2,
    .playSource         = kWM8904_PlaySourceDAC,
    .slaveAddress       = WM8904_I2C_ADDRESS,
    .protocol           = kWM8904_ProtocolI2S,
    .format             = {.sampleRate = kWM8904_SampleRate48kHz, .bitWidth = kWM8904_BitWidth16},
    .mclk_HZ            = 12288000U,
    .master             = false,
    .sysClkSource       = kWM8904_SysClkSourceFLL,
    .fll                = &fll,
};
codec_config_t boardCodecConfig = {.codecDevType = kCODEC_WM8904, .codecDevConfig = &wm8904Config};


static volatile bool isTxFinished = false;
static volatile bool isRxFinished = false;
AT_NONCACHEABLE_SECTION_ALIGN(uint8_t audioBuff[BUFFER_SIZE * BUFFER_NUM], 4);
AT_NONCACHEABLE_SECTION_ALIGN_INIT(static uint8_t zeroBuff[ZERO_BUFFER_SIZE], 4) = {0};

extern codec_config_t boardCodecConfig;
static volatile uint32_t beginCount   = 0;
static volatile uint32_t sendCount    = 0;
static volatile uint32_t receiveCount = 0;
static volatile uint8_t emptyBlock    = 0;
static volatile bool isZeroBuffer     = true;

static volatile bool istxFinished = false;/*--copy from KL43...*/
static volatile bool isrxFinished = false;

FLEXIO_I2S_Type base;
/*******************************************************************************
 * Code
 ******************************************************************************/
static void txCallback(FLEXIO_I2S_Type *i2sBase, flexio_i2s_dma_handle_t *handle, status_t status, void *userData)
{
    istxFinished = true;
}
static void rxCallback(FLEXIO_I2S_Type *i2sBase, flexio_i2s_dma_handle_t *handle, status_t status, void *userData)
{
    isrxFinished = true;
}


int main(void)
{
    DMAMUX_Type *dmamuxBase = NULL;
	
    flexio_i2s_config_t config;
		uint32_t sourceClockHz = 0U;
    flexio_i2s_format_t format;
    flexio_i2s_transfer_t txXfer, rxXfer;
    uint8_t txIndex = 0, rxIndex = 0;
    flexio_i2s_transfer_t xferTx, xferRx;
	  uint32_t i = 0;
    status_t status = kStatus_Success;	
		dma_transfer_config_t dmaConfig = {0};
		
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    CLOCK_SetFlexio0Clock(1U);
    PRINTF("FLEXIO_I2S DMA example started!\n\r");

/* Set flexio i2s pin, shifter and timer */
    base.bclkPinIndex   = BCLK_PIN;
    base.fsPinIndex     = FRAME_SYNC_PIN;
    base.txPinIndex     = TX_DATA_PIN;
    base.rxPinIndex     = RX_DATA_PIN;																	
    base.txShifterIndex = 0;
    base.rxShifterIndex = 1;              
    base.bclkTimerIndex = 0;
    base.fsTimerIndex   = 1;																		  
    base.flexioBase     = DEMO_FLEXIO_BASE;
	
/* Create DMA handle */
    DMA_Init(EXAMPLE_DMA);
    DMA_CreateHandle(&txDmaHandle, EXAMPLE_DMA, EXAMPLE_TX_CHANNEL);
    DMA_CreateHandle(&rxDmaHandle, EXAMPLE_DMA, EXAMPLE_RX_CHANNEL);

    DMAMUX_Init(DMAMUX0);
    DMAMUX_SetSource(DMAMUX0, EXAMPLE_TX_CHANNEL, EXAMPLE_TX_DMA_SOURCE);
    DMAMUX_EnableChannel(DMAMUX0, EXAMPLE_TX_CHANNEL);
    DMAMUX_SetSource(DMAMUX0, EXAMPLE_RX_CHANNEL, EXAMPLE_RX_DMA_SOURCE);
    DMAMUX_EnableChannel(DMAMUX0, EXAMPLE_RX_CHANNEL);

    /*
     * config.enableI2S = true;
     */
    FLEXIO_I2S_GetDefaultConfig(&config);
    config.masterSlave = kFLEXIO_I2S_Master;
		/*'Active low': set this for WM8905...*/
		config.bclkPinPolarity = kFLEXIO_PinActiveLow;  
    FLEXIO_I2S_Init(&base, &config);
		FLEXIO_I2S_Init_FOR_MCLK(&base, &config);

    /* Use default setting to init codec */
    /* protocol: i2s
     * sampleRate: 48K
     * bitwidth:16
     */
    if (CODEC_Init(codecHandle, &boardCodecConfig) != kStatus_Success)
    {
        PRINTF("WM8904_Init failed!\r\n");
    }
    /* Initial volume kept low for hearing safety. */
    /* Adjust it to your needs, 0x0006 for -51 dB, 0x0039 for 0 dB etc. */
		CODEC_SetVolume(codecHandle, kCODEC_PlayChannelHeadphoneLeft | kCODEC_PlayChannelHeadphoneRight, 0x0015);//  Default value is 0x0006
		
    FLEXIO_I2S_TransferTxCreateHandleDMA(&base, &txHandle, txCallback, NULL, &txDmaHandle);
    FLEXIO_I2S_TransferRxCreateHandleDMA(&base, &rxHandle, rxCallback, NULL, &rxDmaHandle);
		
    /* Configure the audio format */
    format.bitWidth      = DEMO_AUDIO_BIT_WIDTH;
    format.sampleRate_Hz = DEMO_AUDIO_SAMPLE_RATE;

    sourceClockHz = DEMO_FLEXIO_CLK_FREQ;
    FLEXIO_I2S_TransferSetFormatDMA(&base, &txHandle, &format, sourceClockHz);
    FLEXIO_I2S_TransferSetFormatDMA(&base, &rxHandle, &format, sourceClockHz);

		
#if LINE_IN
    /*  xfer structure */
    xferTx.data = txBuff;
    xferTx.dataSize = BUFFER_SIZE;
    xferRx.data = rxBuff;
    xferRx.dataSize = BUFFER_SIZE;
    /* Add a transfer to trigger bit clock */
    FLEXIO_I2S_TransferSendDMA(&base, &txHandle, &xferTx);
		
    /* First record a piece of data */
    for (i = 0; i < 5000; i++)
    {
        isrxFinished = false;
        /* Fill the queue to full */
        status = kStatus_Success;
        while (status != kStatus_FLEXIO_I2S_QueueFull)
        {
            status = FLEXIO_I2S_TransferReceiveDMA(&base, &rxHandle, &xferRx);
        }
        /* Wait for data record finished */
        while (isrxFinished != true)
        {
        }
        /* Copy data from RX buffer to tx buffer */
        memcpy(txBuff, rxBuff, BUFFER_SIZE);
        FLEXIO_I2S_TransferSendDMA(&base, &txHandle, &xferTx);
    }		
    CODEC_Deinit(codecHandle);
		PRINTF("\n\r FLEXIO I2S DMA example finished!\n\r ");	
#endif

#if Music_saved_in_Flash
    /*  xfer structure */
    xferTx.data = &g_Music[0];
    xferTx.dataSize = sizeof(g_Music);

    /* Add a transfer to trigger bit clock */
    FLEXIO_I2S_TransferSendDMA(&base, &txHandle, &xferTx);
		PRINTF("\n\r FLEXIO I2S DMA example finished!\n\r ");	
#endif	
}
void FLEXIO_I2S_Init_FOR_MCLK(FLEXIO_I2S_Type *base, const flexio_i2s_config_t *config)
{
		flexio_timer_config_t timerConfig     = {0};	
/* Set Timer to I2S 'MCLK' */
    if (config->masterSlave == kFLEXIO_I2S_Master)
    {
        timerConfig.triggerSelect   = FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT(base->txShifterIndex);	
//        timerConfig.triggerSelect   = FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT(base->bclkPinIndex);	
//				timerConfig.triggerSelect   = FLEXIO_TIMER_TRIGGER_SEL_PININPUT(base->txPinIndex); 		
        timerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveLow;
        timerConfig.triggerSource   = kFLEXIO_TimerTriggerSourceInternal;
        timerConfig.pinSelect       = 2;//base->bclkPinIndex;                                     
        timerConfig.pinConfig       = kFLEXIO_PinConfigOutput;
        timerConfig.pinPolarity     = kFLEXIO_PinActiveHigh;     																	
        timerConfig.timerMode       = kFLEXIO_TimerModeDual8BitBaudBit;
        timerConfig.timerOutput     = kFLEXIO_TimerOutputOneNotAffectedByReset;
        timerConfig.timerDecrement  = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
        timerConfig.timerReset      = kFLEXIO_TimerResetNever;
        timerConfig.timerDisable    = kFLEXIO_TimerDisableNever;
        timerConfig.timerEnable     = kFLEXIO_TimerEnableOnTriggerHigh;
        timerConfig.timerStart      = kFLEXIO_TimerStartBitEnabled;
        timerConfig.timerStop       = kFLEXIO_TimerStopBitDisabled;
    }		
		FLEXIO_SetTimerConfig(base->flexioBase, 2, &timerConfig);																		 

/* Set Frame sync timer cmp */
    base->flexioBase->TIMCMP[2] = FLEXIO_TIMCMP_CMP(2 - 1U);/*--10-12M...*/
}


