/*
 * Copyright 2018-2025 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_dma.h"
#include "fsl_device_registers.h"
#include "fsl_gpio.h"
#include "fsl_mu.h"
#include "fsl_i2s.h"
#include "fsl_i2s_dma.h"

#include "usb_device_config.h"
#include "usb.h"
#include "usb_device.h"
#include "usb_device_class.h"
#include "usb_device_descriptor.h"

#include "usb_device_audio.h"

#include "GlobalDef.h"
#include "audio_unified.h"
#include "AudioIoInit.h"
#include "Main.h"
#include "SubFunc.h"
#include "CircularBufManagement.h"
#include "CircularBuf.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

//---------------var for I2S Rx0,Tx0,Tx1 DMA IO----------------- beg------
#if 1 //for folding

i2s_config_t I2S_Tx_config;
i2s_config_t I2S_Rx_config;

i2s_transfer_t I2SRx0Transfer;
i2s_transfer_t I2STx0Transfer;
i2s_transfer_t I2STx1Transfer;

dma_handle_t I2SRx0DmaHandle;
dma_handle_t I2STx0DmaHandle;
dma_handle_t I2STx1DmaHandle;

i2s_dma_handle_t I2SRx0Handle;
i2s_dma_handle_t I2STx0Handle;
i2s_dma_handle_t I2STx1Handle;

__attribute__((__section__(".data.$Audio_io_data_non_cached"))) __attribute__((aligned(4)))
volatile int I2SRxBufCh0And1Mixed_A[AudioFrameSizeInSamplePerCh*2];    //1 frame samples --- for 2 channels mixed I2S audio data

__attribute__((__section__(".data.$Audio_io_data_non_cached"))) __attribute__((aligned(4)))
volatile int I2SRxBufCh0And1Mixed_B[AudioFrameSizeInSamplePerCh*2];    //1 frame samples --- for 2 channels mixed I2S audio data

__attribute__((__section__(".data.$Audio_io_data_non_cached"))) __attribute__((aligned(4)))
volatile int I2STxBufCh0And1Mixed_A[AudioFrameSizeInSamplePerCh*2];    //1 frame samples --- for 2 channels mixed I2S audio data

__attribute__((__section__(".data.$Audio_io_data_non_cached"))) __attribute__((aligned(4)))
volatile int I2STxBufCh0And1Mixed_B[AudioFrameSizeInSamplePerCh*2];    //1 frame samples --- for 2 channels mixed I2S audio data

__attribute__((__section__(".data.$Audio_io_data_non_cached"))) __attribute__((aligned(4)))
volatile int I2STxBufCh2And3Mixed_A[AudioFrameSizeInSamplePerCh*2];    //1 frame samples --- for 2 channels mixed I2S audio data

__attribute__((__section__(".data.$Audio_io_data_non_cached"))) __attribute__((aligned(4)))
volatile int I2STxBufCh2And3Mixed_B[AudioFrameSizeInSamplePerCh*2];    //1 frame samples --- for 2 channels mixed I2S audio data

#endif //for folding
//---------------var for I2S Rx0,Tx0,Tx1 DMA IO----------------- end------

volatile unsigned char I2SRx0DmaTransferringIsUsingBufA;
volatile unsigned char I2STxDmaTransferringIsUsingBufA;
volatile unsigned char OneFrameI2SAudioOutputTxIsFinished;
volatile int RestartI2S2Cnt;

void I2SRx0_Callback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData)
{
	DbgPin2Up;
	I2SRx0DmaTransferringIsUsingBufA=1-I2SRx0DmaTransferringIsUsingBufA;

	if(I2SRx0DmaTransferringIsUsingBufA)
	{	//now DMA is using buffer A --> next to transfer buffer B --> now software processing should also use buffer B
		I2SRx0Transfer.data= (uint8_t *)I2SRxBufCh0And1Mixed_B;
		VarBlockSharedByDspAndMcu.I2SInputCh01Ptr=I2SRxBufCh0And1Mixed_B;
	}else
	{	//now DMA is using buffer B --> next to transfer buffer A --> now software processing should also use buffer A
		I2SRx0Transfer.data= (uint8_t *)I2SRxBufCh0And1Mixed_A;
		VarBlockSharedByDspAndMcu.I2SInputCh01Ptr=I2SRxBufCh0And1Mixed_A;
	}
	I2S_RxTransferReceiveDMA(DEMO_I2S_RX0, &I2SRx0Handle, I2SRx0Transfer);

	uint32_t I2Sstat = ((I2S_Type *)DEMO_I2S_RX0)->STAT;

	if(I2Sstat&0x02)
	{
		DbgPin4Up;
		//LRCK BICK error --- I2S line BICK or LRCK detached !!!
		//LRCK BICK error --- I2S line BICK or LRCK detached !!!
		//LRCK BICK error --- I2S line BICK or LRCK detached !!!
		((I2S_Type *)DEMO_I2S_RX0)->STAT|=0x02;

		//clear
		((I2S_Type *)DEMO_I2S_RX0)->FIFOSTAT|=0xfb;
		((I2S_Type *)DEMO_I2S_RX0)->FIFOCFG |= (1<<17);	//empty RX fifo

		//close I2S2
		((I2S_Type *)DEMO_I2S_RX0)->CFG1&=~0x01;		//disable
		//should wait 100ms and restart I2S2
		RestartI2S2Cnt=800;

		I2S_DisableInterrupts(DEMO_I2S_RX0, (uint32_t)kI2S_RxErrorFlag | (uint32_t)kI2S_RxLevelFlag);
		DisableIRQ((IRQn_Type)FLEXCOMM2_IRQn);
		DbgPin4Dn;
	}


	//copy DMA buffer I2SRx samples to circular buffer
	if((AudioSourceIdx==AudioSrc2_External33KHzI2S)||(AudioSourceIdx==AudioSrc3_External48KHzI2S))
	{
		if(DelayCntAsrcFeedIn_ExtI2S)
		{
			DelayCntAsrcFeedIn_ExtI2S--;
		}else
		{
			memcpy(VarBlockSharedByDspAndMcu.AsrcInBuf,(int *)VarBlockSharedByDspAndMcu.I2SInputCh01Ptr,AudioFrameSizeInSamplePerCh*2*sizeof(int));
			MU_SendMsgNonBlocking(APP_MU, CHN_MU_REG_NUM, AudioFrameSizeInSamplePerCh);		//send number of samples to DSP, to trigger DSP MU interrupt
		}
	}

	DbgPin2Dn;
}

void I2STx0_Callback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData)
{
	I2STxDmaTransferringIsUsingBufA=1-I2STxDmaTransferringIsUsingBufA;

	if(I2STxDmaTransferringIsUsingBufA)		//I2STxDmaTransferringIsUsingBufA tells the previous DMA transfer
	{	//now DMA is using buffer A --> next to transfer buffer B
		I2STx0Transfer.data= (uint8_t *)I2STxBufCh0And1Mixed_B;
		I2STx1Transfer.data= (uint8_t *)I2STxBufCh2And3Mixed_B;
	}else
	{	//now DMA is using buffer B --> next to transfer buffer A
		I2STx0Transfer.data= (uint8_t *)I2STxBufCh0And1Mixed_A;
		I2STx1Transfer.data= (uint8_t *)I2STxBufCh2And3Mixed_A;
	}
	I2S_TxTransferSendDMA   (DEMO_I2S_TX0, &I2STx0Handle, I2STx0Transfer);
	I2S_TxTransferSendDMA   (DEMO_I2S_TX1, &I2STx1Handle, I2STx1Transfer);

	OneFrameI2SAudioOutputTxIsFinished=1;
}
#if 0	//it doesn't matter if this part is opened or closed
void I2STx1_Callback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData)
{
}
#endif

void BOARD_Init_DMA(void)
{
	DMA_Init(DEMO_DMA);
	DMA_EnableChannel(DEMO_DMA, I2S_FlexComm1_DMA_CHANNEL);
	DMA_EnableChannel(DEMO_DMA, I2S_FlexComm2_DMA_CHANNEL);
	DMA_EnableChannel(DEMO_DMA, I2S_FlexComm3_DMA_CHANNEL);
	DMA_SetChannelPriority(DEMO_DMA, I2S_FlexComm1_DMA_CHANNEL, kDMA_ChannelPriority3);
	DMA_SetChannelPriority(DEMO_DMA, I2S_FlexComm2_DMA_CHANNEL, kDMA_ChannelPriority4);
	DMA_SetChannelPriority(DEMO_DMA, I2S_FlexComm3_DMA_CHANNEL, kDMA_ChannelPriority5);
}

void BOARD_Init_I2S(void)
{
	I2S_RxGetDefaultConfig(&I2S_Rx_config);
	I2S_Rx_config.divider     = DEMO_I2S_CLOCK_DIVIDER;
	I2S_Rx_config.masterSlave = DEMO_I2S_RX_MODE;

	I2S_Rx_config.dataLength  = 32;
	I2S_Rx_config.frameLength = 64;
	I2S_Rx_config.position    =  0;

	I2S_TxGetDefaultConfig(&I2S_Tx_config);
	I2S_Tx_config.divider     = DEMO_I2S_CLOCK_DIVIDER;
	I2S_Tx_config.masterSlave = DEMO_I2S_TX_MODE;

	I2S_Tx_config.dataLength  = 32;
	I2S_Tx_config.frameLength = 64;

	I2S_Tx_config.position    =  0;

	I2S_Rx_config.watermark  = 4;
	I2S_Rx_config.oneChannel  = false;
	I2S_Tx_config.oneChannel  = false;

	I2S_RxInit(DEMO_I2S_RX0, &I2S_Rx_config);
	I2S_TxInit(DEMO_I2S_TX0, &I2S_Tx_config);
	I2S_TxInit(DEMO_I2S_TX1, &I2S_Tx_config);

	//to make I2S rx and tx interrupt be strictly aligned in time --- pause tx and rx at the beginning
	//((I2S_Type *)DEMO_I2S_RX0)->CFG1|=0x02;		//pause
	((I2S_Type *)DEMO_I2S_TX0)->CFG1|=0x02;		//pause
	((I2S_Type *)DEMO_I2S_TX1)->CFG1|=0x02;		//pause
}


void CreateAndStartFlexcommTxAudioDMA(void)
{
	memset((void *)I2STxBufCh0And1Mixed_A,0,sizeof(I2STxBufCh0And1Mixed_A));
	memset((void *)I2STxBufCh0And1Mixed_B,0,sizeof(I2STxBufCh0And1Mixed_B));
	memset((void *)I2STxBufCh2And3Mixed_A,0,sizeof(I2STxBufCh2And3Mixed_A));
	memset((void *)I2STxBufCh2And3Mixed_B,0,sizeof(I2STxBufCh2And3Mixed_B));

	//config dma for I2S tx0 and tx1
	DMA_CreateHandle(&I2STx0DmaHandle, DEMO_DMA, I2S_FlexComm1_DMA_CHANNEL);
	DMA_CreateHandle(&I2STx1DmaHandle, DEMO_DMA, I2S_FlexComm3_DMA_CHANNEL);

	I2S_TxTransferCreateHandleDMA(DEMO_I2S_TX0, &I2STx0Handle, &I2STx0DmaHandle, I2STx0_Callback,NULL);
	//I2S_TxTransferCreateHandleDMA(DEMO_I2S_TX0, &I2STx0Handle, &I2STx0DmaHandle, NULL,NULL);

	//I2S_TxTransferCreateHandleDMA(DEMO_I2S_TX1, &I2STx1Handle, &I2STx1DmaHandle, I2STx1_Callback,NULL);
	I2S_TxTransferCreateHandleDMA(DEMO_I2S_TX1, &I2STx1Handle, &I2STx1DmaHandle, NULL,NULL);


    /* need to queue two transmit buffers so when the first one
     * finishes transfer, the other immediately starts */
    I2STx0Transfer.data     = (uint8_t*) &I2STxBufCh0And1Mixed_A[0];
    I2STx0Transfer.dataSize = sizeof(I2STxBufCh0And1Mixed_A);
    I2S_TxTransferSendDMA(DEMO_I2S_TX0, &I2STx0Handle, I2STx0Transfer);
    I2STx0Transfer.data     = (uint8_t*) &I2STxBufCh0And1Mixed_B[0];
    I2STx0Transfer.dataSize = sizeof(I2STxBufCh0And1Mixed_B);
    I2S_TxTransferSendDMA(DEMO_I2S_TX0, &I2STx0Handle, I2STx0Transfer);

    I2STx1Transfer.data     = (uint8_t*) &I2STxBufCh2And3Mixed_A[0];
    I2STx1Transfer.dataSize = sizeof(I2STxBufCh2And3Mixed_A);
    I2S_TxTransferSendDMA(DEMO_I2S_TX1, &I2STx1Handle, I2STx1Transfer);
    I2STx1Transfer.data     = (uint8_t*) &I2STxBufCh2And3Mixed_B[0];
    I2STx1Transfer.dataSize = sizeof(I2STxBufCh2And3Mixed_B);
    I2S_TxTransferSendDMA(DEMO_I2S_TX1, &I2STx1Handle, I2STx1Transfer);


	((DMA_Type *)(DEMO_DMA))->CHANNEL[I2S_FlexComm1_DMA_CHANNEL].XFERCFG |= DMA_CHANNEL_XFERCFG_SWTRIG_MASK;
	((DMA_Type *)(DEMO_DMA))->CHANNEL[I2S_FlexComm3_DMA_CHANNEL].XFERCFG |= DMA_CHANNEL_XFERCFG_SWTRIG_MASK;


	#if 1
		//this part is to make sure I2S tx rx starts immediately after the LR edge
		//wait till it is I2S left  (LRCK falling edge)
		while(1)
		{
			if(
					((((I2S_Type *)DEMO_I2S_TX0)->STAT) & 0x4) ==0x0	//now it is left channel
			  )
				break;
		}
		//now it is left
		while(1)
		{
			if(
					((((I2S_Type *)DEMO_I2S_TX0)->STAT) & 0x4) ==0x4	//now it is right channel
			  )
				break;
		}
		//now it is just coming into right
	#endif


	//start flexcomm I2S
	((I2S_Type *)DEMO_I2S_TX0)->FIFOCFG |= (1<<16);	//empty fifo
	((I2S_Type *)DEMO_I2S_TX1)->FIFOCFG |= (1<<16);	//empty fifo

	//read/write I2S fifo buffer to empty the Rx and full the Tx --- so that I2S rx and tx interrupt could be strictly aligned in time
	for(int i=0;i<8;i++)
	{
		((I2S_Type *)DEMO_I2S_TX0)->FIFOWR=0;
		((I2S_Type *)DEMO_I2S_TX1)->FIFOWR=0;
	}

	((I2S_Type *)DEMO_I2S_TX0)->CFG1&=~0x02;		//continue
	((I2S_Type *)DEMO_I2S_TX1)->CFG1&=~0x02;		//continue


	((I2S_Type *)DEMO_I2S_TX0)->FIFOCFG |= I2S_FIFOCFG_DMATX_MASK;
	((I2S_Type *)DEMO_I2S_TX1)->FIFOCFG |= I2S_FIFOCFG_DMATX_MASK;

	//set the flag variables to initial value
	I2STxDmaTransferringIsUsingBufA=0;
	OneFrameI2SAudioOutputTxIsFinished=0;


	//start flexcomm I2S
	((I2S_Type *)DEMO_I2S_RX0)->FIFOCFG |= (1<<16);	//empty fifo
	((I2S_Type *)DEMO_I2S_RX0)->CFG1|= 0x01;		//enalbe
	((I2S_Type *)DEMO_I2S_RX0)->CFG1&=~0x02;		//continue
	((I2S_Type *)DEMO_I2S_RX0)->FIFOCFG |= I2S_FIFOCFG_DMARX_MASK;

	I2S_EnableInterrupts(DEMO_I2S_RX0, (uint32_t)kI2S_RxErrorFlag | (uint32_t)kI2S_RxLevelFlag);
    NVIC_SetPriority((IRQn_Type)FLEXCOMM2_IRQn, 1);
    EnableIRQ((IRQn_Type)FLEXCOMM2_IRQn);

	//set the flag variables to initial value
	I2SRx0DmaTransferringIsUsingBufA=0;
	RestartI2S2Cnt=0;
}


void CreateAndStartFlexcomm2RxAudioDMA(void)
{
	memset((void *)I2SRxBufCh0And1Mixed_A,0,sizeof(I2SRxBufCh0And1Mixed_A));
	memset((void *)I2SRxBufCh0And1Mixed_B,0,sizeof(I2SRxBufCh0And1Mixed_B));

	//config dma for I2S tx0 and tx1
	DMA_CreateHandle(&I2SRx0DmaHandle, DEMO_DMA, I2S_FlexComm2_DMA_CHANNEL);

	I2S_RxTransferCreateHandleDMA(DEMO_I2S_RX0, &I2SRx0Handle, &I2SRx0DmaHandle, I2SRx0_Callback,NULL);

    /* need to queue two receive buffers so when the first one is filled,
     * the other is immediately starts to be filled */
	I2SRx0Transfer.data     = (uint8_t*) &I2SRxBufCh0And1Mixed_A[0];
	I2SRx0Transfer.dataSize = sizeof(I2SRxBufCh0And1Mixed_A);
    I2S_RxTransferReceiveDMA(DEMO_I2S_RX0, &I2SRx0Handle, I2SRx0Transfer);
    I2SRx0Transfer.data     = (uint8_t*) &I2SRxBufCh0And1Mixed_B[0];
    I2SRx0Transfer.dataSize = sizeof(I2SRxBufCh0And1Mixed_B);
    I2S_RxTransferReceiveDMA(DEMO_I2S_RX0, &I2SRx0Handle, I2SRx0Transfer);

	((DMA_Type *)(DEMO_DMA))->CHANNEL[I2S_FlexComm2_DMA_CHANNEL].XFERCFG |= DMA_CHANNEL_XFERCFG_SWTRIG_MASK;

	I2SRx0DmaTransferringIsUsingBufA=0;
}

void FLEXCOMM2_IRQHandler(void)
{
	//DbgPin1Up;DbgPin4Up;
    uint32_t intstat = ((I2S_Type *)DEMO_I2S_RX0)->FIFOINTSTAT;
    uint32_t I2Sstat = ((I2S_Type *)DEMO_I2S_RX0)->STAT;

    if ((intstat & I2S_FIFOINTSTAT_RXERR_MASK) != 0UL)
    {
    	//RX fifo overflow
    	((I2S_Type *)DEMO_I2S_RX0)->FIFOCFG |= (1<<17);	//empty RX fifo
        /* Clear RX error interrupt flag */
        ((I2S_Type *)DEMO_I2S_RX0)->FIFOSTAT = I2S_FIFOSTAT_RXERR(1U);
    }else if ((intstat & I2S_FIFOINTSTAT_RXLVL_MASK) != 0UL)
    {
    	//RX fifo watermark is reached
    	if(I2Sstat&0x04)
    	{
    		//now is at I2S right half
    		//start DMA here in the right slot
    		I2S_DisableInterrupts(DEMO_I2S_RX0, (uint32_t)kI2S_RxErrorFlag | (uint32_t)kI2S_RxLevelFlag);
    		CreateAndStartFlexcomm2RxAudioDMA();
    	}else
    	{
    		//now is at I2S left half
    		//not to start DMA here, wait to start next fifo full interrupt and in the right side
    	}

    	for(int i=0;i<4;i++)
    	{
    		volatile int a;
    		a=((I2S_Type *)DEMO_I2S_RX0)->FIFORD;		//to clear rx fifo
    	}

        /* Clear RX level interrupt flag */
        ((I2S_Type *)DEMO_I2S_RX0)->FIFOSTAT = I2S_FIFOSTAT_RXLVL(1U);
    }else
    {
    	((I2S_Type *)DEMO_I2S_RX0)->FIFOSTAT|=0xfb;
    }
    //DbgPin1Dn;DbgPin4Dn;
}


