/**********************************************************************

Copyright(c) Analog Devices, Inc. All Rights Reserved. 

This software is proprietary and confidential.  By using this software 
you agree to the terms of the associated Analog Devices License Agreement.  


Description:
	

*****************************************************************************/

/*****************************************************************************\

  Includes:

\*****************************************************************************/

#include <services/services.h>          /* system service includes      */
#include <drivers/adi_dev.h>            /* device manager includes      */
#include <drivers/codec/adi_bf52xc1.h>  /* BF52xC1 driver includes      */

#include <mediaplayer.h>
#include <SDK-ezkitutilities.h>
#include <adac_buffer.h>


#include <WAV_codec.h>
#include <adi_mp3_dec.h>
#include <adi_heaacv2_decoder.h>


/*****************************************************************************\

  Defines:

\*****************************************************************************/

/* SPI device connected to BF52xC1 (SPI using PORTG MUX2 - so use the virtual SPI device number (device 1))*/
#define     SPI_DEVICE_NUMBER         	1
/* SPI Chipselect connected to BF52xC1 (SPISEL5) */
#define     BF52xC1_SPI_CS         		5
/* SPORT device connected to BF52xC1 (SPORT 0 using POTRTG pins - so use virtual SPORT 0 device number (device 2)) */
#define     SPORT_DEVICE_NUMBER         2


/*****************************************************************************\

  Declarations:

\*****************************************************************************/
/* Audio Codec buffer data */                                                       
section ("mpp_nocache")  volatile u8 OutboundData0 [AUDIO_CODEC_DAC_BUFFER_SIZE];   
section ("mpp_nocache")  volatile u8 OutboundData1 [AUDIO_CODEC_DAC_BUFFER_SIZE];   

/* 1D buffers to manage Outbound Audio data */                                          
section ("mpp_nocache") ADI_DEV_1D_BUFFER OutboundBuffer0,OutboundBuffer1;              

/* Handle to the Audio Codec driver */
static ADI_DEV_DEVICE_HANDLE 	AudioCodecDriverHandle;

/* BF52xC1 Register configuration table */
ADI_DEV_ACCESS_REGISTER_FIELD ConfigBF52xC1[] =
{
    { BF52xC1_REG_POWER,            BF52xC1_RFLD_LINE_IN_PDN,       0           },  /* Line-In Power ON                   	*/
    { BF52xC1_REG_ANALOGUE_PATH,   	BF52xC1_RFLD_ENABLE_MIC_MUTE,   1           },  /* Mute MIC                             */
    { BF52xC1_REG_ANALOGUE_PATH,   	BF52xC1_RFLD_ADC_IN_SELECT,     0           },  /* Select Line-In as ADC input          */
    { BF52xC1_REG_LEFT_ADC_VOL,     BF52xC1_RFLD_LIN_ENABLE_MUTE,   0           },  /* Un-mute Left Channel ADC             */
    { BF52xC1_REG_RIGHT_ADC_VOL,    BF52xC1_RFLD_RIN_ENABLE_MUTE,   0           },  /* Un-mute Right Channel ADC            */
    { BF52xC1_REG_LEFT_ADC_VOL,     BF52xC1_RFLD_LIN_VOL,           0x17        },  /* Left Channel ADC Volume set to 0dB   */
    { BF52xC1_REG_RIGHT_ADC_VOL,    BF52xC1_RFLD_RIN_VOL,           0x17        },  /* Right Channel ADC Volume set to 0dB  */
    { BF52xC1_REG_POWER,            BF52xC1_RFLD_POWER_OFF,         0           },  /* Device Power ON                      */
    { BF52xC1_REG_POWER,            BF52xC1_RFLD_ADC_PDN,           0           },  /* ADC Power ON                         */
    { BF52xC1_REG_POWER,            BF52xC1_RFLD_DAC_PDN,           0           },  /* DAC Power ON                         */
    { BF52xC1_REG_POWER,            BF52xC1_RFLD_OUT_PDN,           0           },  /* Outputs Power ON                     */
    { BF52xC1_REG_POWER,            BF52xC1_RFLD_OSC_PDN,           0           },  /* Oscillator Power ON                  */
    { BF52xC1_REG_ANALOGUE_PATH,    BF52xC1_RFLD_ENABLE_BYPASS,     0           },  /* Disable Bypass                       */
    { BF52xC1_REG_ANALOGUE_PATH,   	BF52xC1_RFLD_SELECT_DAC,        1           },  /* Select DAC (Enable DAC)              */
    { BF52xC1_REG_DIGITAL_PATH,		BF52xC1_RFLD_ENABLE_DAC_MUTE,   0           },  /* Un-mute DAC                          */
    { BF52xC1_REG_DIGITAL_IFACE,  	BF52xC1_RFLD_IFACE_FORMAT,      2			},  /* I2S mode								*/
    { BF52xC1_REG_DIGITAL_IFACE,    BF52xC1_RFLD_AUDIO_DATA_LEN,    0    		},  /* Set Input Audio data length (16bit)	*/
    { BF52xC1_REG_DIGITAL_IFACE,    BF52xC1_RFLD_ENABLE_MASTER,     1           },  /* Enable Master Mode                   */
    { BF52xC1_REG_SAMPLING_CTRL,    BF52xC1_RFLD_ENABLE_USB_MODE,  	1           },  /* Enable USB Mode                      */
    { BF52xC1_REG_SAMPLING_CTRL,	BF52xC1_RFLD_BOS_RATE,			1			},
    { ADI_DEV_REGEND,               0,                              0           }   /* End of register access               */
    
};


/* BF52xC1 Volume control register configuration table */
	ADI_DEV_ACCESS_REGISTER_FIELD ConfigVolControl[] = 
{	
	{ BF52xC1_REG_LEFT_DAC_VOL,    	BF52xC1_RFLD_ENABLE_LZC,        0        	}, 
  	{ BF52xC1_REG_LEFT_DAC_VOL,    	BF52xC1_RFLD_LRHP_BOTH,         1        	},
	{ BF52xC1_REG_LEFT_DAC_VOL, 	BF52xC1_RFLD_LHP_VOL, 			VOLUME_MIN 	},	
    { ADI_DEV_REGEND,               0,                              0           } 
      /* End of register access               */
};

static volatile u16 CodecVolume = VOLUME_MIN;

/*****************************************************************************\

  Externs

\*****************************************************************************/



/*****************************************************************************\

  Function Prototypes

\*****************************************************************************/
static void AudioCodecCallback(
	void 	*AppHandle,
	u32  	Event,
	void 	*pArg); 
	
int init_codec_52xc1(void);
int close_codec_52xc1(void);	
	

/*********************************************************************

    Function:       init adi_52xc1 codec
    Description:    Initialises Audio Codec
    
*********************************************************************/
int init_codec_52xc1(void)
{

    u32 Result = ADI_DEV_RESULT_SUCCESS;
    
    if((Result = adi_dev_Open(  adi_dev_ManagerHandle,          /* Dev manager Handle                       */
                                &ADIBF52xC1EntryPoint,          /* Entry point for BF52xC1 driver           */
                                0,                              /* Device number                            */
                                NULL,                           /* No client handle                         */
                                &AudioCodecDriverHandle,        /* Location to store BF52xC1 driver handle  */
                                ADI_DEV_DIRECTION_OUTBOUND,		/* Data Direction                           */
                               	adi_dma_ManagerHandle,          /* Handle to DMA Manager                    */
                               	NULL,            				/* Live callback			               	*/
                               	AudioCodecCallback))            /* Callback Function                        */
                    			!= ADI_DEV_RESULT_SUCCESS) 
	{
    	printf("Failed to open BF52xC1 device , Error Code: 0x%08X\n",Result);
	}
	
    /* Set BF52xC1 driver to use SPI to access BF52xC1 hardware registers */
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_BF52xC1_CMD_SET_SPI_DEVICE_NUMBER, (void *)SPI_DEVICE_NUMBER))!= ADI_DEV_RESULT_SUCCESS)
    {
	    printf("Failed to set SPI device number to use for BF52xC1 register access, Error Code: 0x%08X\n",Result);
    }
        
    /* Set SPI Chipselect connected to BF52xC1 */
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_BF52xC1_CMD_SET_SPI_CS, (void *)BF52xC1_SPI_CS))!= ADI_DEV_RESULT_SUCCESS)
    {
    	printf("Failed to set SPI Chipselect number connected to BF52xC1 CSB pin, Error Code: 0x%08X\n",Result);
    }	
    
    /* Set SPORT device number connected to BF52xC1 Digital Audio Interface port */
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_BF52xC1_CMD_SET_SPORT_DEVICE_NUMBER, (void *)SPORT_DEVICE_NUMBER))!= ADI_DEV_RESULT_SUCCESS)
    {
    	printf("Failed to set SPORT device number connected to BF52xC1 Digital Audio Interface port, Error Code: 0x%08X\n",Result);
    }

    /* Configure BF52xC1 registers */
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_REGISTER_FIELD_TABLE_WRITE, (void *)ConfigBF52xC1))!= ADI_DEV_RESULT_SUCCESS)
    {
    	printf("Failed to configure BF52xC1 register fields, Error Code: 0x%08X\n",Result);
    }
    
    
    /******************Buffer preparation **********************/
    	    
    /* Outbound 1D buffers */
    OutboundBuffer0.Data                = (void *)OutboundData0;
    OutboundBuffer0.ElementCount        = (AUDIO_CODEC_DAC_BUFFER_SIZE/DMA_BUS_WIDTH_IN_BYTES);							/*(1024*16*2);*/
    OutboundBuffer0.ElementWidth        = DMA_BUS_WIDTH_IN_BYTES;
    OutboundBuffer0.CallbackParameter   = &OutboundBuffer0;   /* enable callback on completion of this buffer */
    OutboundBuffer0.ProcessedFlag       = FALSE;
    OutboundBuffer0.pNext               = &OutboundBuffer1;
    
    OutboundBuffer1.Data                = (void *)OutboundData1;
    OutboundBuffer1.ElementCount        = (AUDIO_CODEC_DAC_BUFFER_SIZE/DMA_BUS_WIDTH_IN_BYTES);    
    OutboundBuffer1.ElementWidth        = DMA_BUS_WIDTH_IN_BYTES;
    OutboundBuffer1.CallbackParameter   = &OutboundBuffer1;   /* enable callback on completion of this buffer */
    OutboundBuffer1.ProcessedFlag       = FALSE;
    OutboundBuffer1.pNext               = NULL;	
    
    return (Result);
}

/*********************************************************************

    Function:       EnableCodecDataflow
    Description:    Sets Audio Codec DAC sampling rate and 
                    enables audio dataflow
    
*********************************************************************/
u32 EnableCodecDataflow(u32  SamplingRate)
{
    u32 Result = ADI_DEV_RESULT_SUCCESS;
    ADI_BF52xC1_SAMPLE_RATE     SampleRate;

    /* copy DAC sampling rate to ADC */
    SampleRate.DacSampleRate = SamplingRate;
    SampleRate.AdcSampleRate = SamplingRate;
        
	
	/* Set ADC/DAC Sample rate */
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_BF52xC1_CMD_SET_SAMPLE_RATE, (void *)&SampleRate))!= ADI_DEV_RESULT_SUCCESS)
    {
    	printf("Failed to set BF52xC1 DAC sample rate, Error Code: 0x%08X\n",Result);
    }
	
    // set dataflow method
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_CHAINED_LOOPBACK))!=ADI_DEV_RESULT_SUCCESS)
    {
    	printf("adi_dev_Control failed to set dataflow method, Error Code: 0x%08X\n",Result);	    
    }	

	/* submit Outbound audio buffers */
    if ((Result = adi_dev_Write(AudioCodecDriverHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)&OutboundBuffer0)) != ADI_DEV_RESULT_SUCCESS)
    {
    	printf("Failed to submit Outbound Audio Buffer 0 to BF52xC1, Error Code: 0x%08X\n",Result);
    }
        
    
	/* Enable BF52xC1 dataflow  - this activates BF52xC1 Digital Audio Interface */
    if ((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE)) != ADI_DEV_RESULT_SUCCESS)
    {
    printf("Failed to enable BF52xC1 dataflow, Error Code: 0x%08X\n",Result);
    }

    /* set the initial volume level */
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_REGISTER_FIELD_TABLE_WRITE, (void *)ConfigVolControl))!= ADI_DEV_RESULT_SUCCESS)
    {
    	printf("Failed to configure volume control register fields, Error Code: 0x%08X\n",Result);
    }

    
    
    return (Result);
}

/*********************************************************************

    Function:       DisableCodecDataflow
    Description:    Disables audio dataflow
    
*********************************************************************/
u32 DisableCodecDataflow(void)
{
    u32 Result = ADI_DEV_RESULT_SUCCESS;
    
    /* Disable codec dataflow */                                                                                               
    if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)FALSE))!= ADI_DEV_RESULT_SUCCESS)   
    {                                                                                                                          
	    printf("Failed to disable audio codec dataflow, Error Code: 0x%08X\n",Result);                                         
    }                                                                                                                          
    
    return (Result);
            
}
        
/*********************************************************************

    Function:       AudioCodecCallback
    Description:    Each type of callback event has it's own unique ID 
                    so we can use a single callback function for all 
                    callback events.  The switch statement tells us 
                    which event has occurred.
                    
                    In the example, we'll get a callback when the SPORT 
                    has completed processing of the last buffer in the 
                    buffer chains.  If loopback is enabled, 
                    the device driver will automatically reuse the buffer 
                    chains so there's really nothing to do.  
                    However, if loopback is not used, the 
                    callback function must requeue the chain that just 
                    finished in order to keep the device driver saturated 
                    with data.  
    
*********************************************************************/
//section ("L1_code")
section ("callback_code")

static void AudioCodecCallback(
	void 	*AppHandle,
	u32  	Event,
	void 	*pArg
){
	
	ADI_DEV_1D_BUFFER *pBuffer = (ADI_DEV_1D_BUFFER *)pArg;
	
    /* CASEOF (Event) */
    switch (Event)
    {        
        /* CASE (sub-buffer complete */
        case ADI_DEV_EVENT_BUFFER_PROCESSED:
        	// DAC processing			
			DACUserISR(pArg,(AUDIO_CODEC_DAC_BUFFER_SIZE/NUM_AUDIO_DAC_CHANNELS));
            break;
            
            
		case ADI_DEV_EVENT_DMA_ERROR_INTERRUPT:
	        printf("DMA error\n");
			break;                        

        default:
            printf("Callback occured. Event code: 0x%08X\n",Event);
            break;
    }
    /* return */	

}

/*********************************************************************

    Function:       AdjustCodecVolume
    Description:    Sets Audio Codec volume level
    
*********************************************************************/
u32 AdjustCodecVolume(u32 VolumeLevel)
{
	 u32 Result = ADI_DEV_RESULT_SUCCESS;
	 bool VolumeChange;
	/* BF52xC1 Volume control register */
	ADI_DEV_ACCESS_REGISTER_FIELD LeftDACField	= 
	{ BF52xC1_REG_LEFT_DAC_VOL, BF52xC1_RFLD_LHP_VOL, 0 };	

	VolumeChange = false;
	
	if (VolumeLevel == MSGID_VOLUME_UP) /* volume up*/
	{
		if (CodecVolume > VOLUME_MAX)
		{
			CodecVolume = VOLUME_MAX;
		}
		else
		{
			CodecVolume+=VOLUME_STRIDE;
			/* adjust the volume up*/
			VolumeChange = true;
		}
	}
	else if (VolumeLevel == MSGID_VOLUME_DN) /* volume down */
	{
		if (CodecVolume < VOLUME_MIN)
		{
		   	CodecVolume = VOLUME_MIN;
		}
		else
		{
			CodecVolume-=VOLUME_STRIDE;
			/* adjust the volume down */
			VolumeChange = true;
		}
	}
	
	if (VolumeChange)
	{
		/* Load the register field configuration value*/
		LeftDACField.Data = CodecVolume;

		/* to configure Reg1Field1*/
		Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_REGISTER_FIELD_WRITE, (void *) &LeftDACField);
		if(Result != 0)
		{
    		printf("ADI_DEV_CMD_REGISTER_FIELD_WRITE Error Code: 0x%08X\n",Result);
    	}
    	
		/*APP_TRACE("AdjustVolume(%d)\n",CodecVolume);*/

	}
	
	return(Result);
	
}				

/*********************************************************************

    Function:       ClearOutboundBuffer
    Description:    clear audio outbound buffer 
    
*********************************************************************/
void ClearOutboundBuffer(void)
{
	memset((char *)OutboundData0,0,AUDIO_CODEC_DAC_BUFFER_SIZE);
	memset((char *)OutboundData1,0,AUDIO_CODEC_DAC_BUFFER_SIZE);
}   

/*********************************************************************

	Function:		close_52xc1 codec

	Description:	close 52xc1 codec.  

*********************************************************************/

int close_codec_52xc1(void)
{
	u32 Result= ADI_DEV_RESULT_SUCCESS;
	 
    /* Disable codec dataflow */
    if (Result == ADI_DEV_RESULT_SUCCESS)
    	Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)FALSE);
   		
   	if (Result == ADI_DEV_RESULT_SUCCESS)
   		Result = adi_dev_Close(AudioCodecDriverHandle);
   		
   	return (Result);

}
/*****/
