/**********************************************************************

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 <mediaplayer.h>
#include <codec_1980.h>
#include <SDK-ezkitutilities.h>
#include <adac_buffer.h>

#include <drivers/codec/adi_ac97.h>
#include <drivers/codec/adi_ad1980.h>

#include <WAV_codec.h>
#include <adi_mp3_dec.h>
#include <adi_heaacv2_decoder.h>


/*****************************************************************************\

  Defines:

\*****************************************************************************/

/*****************************************************************************\

  Declarations:

\*****************************************************************************/

/* Handle to the Audio Codec driver */
static ADI_DEV_DEVICE_HANDLE 	AudioCodecDriverHandle;
/* Memory for AD1980 AC'97 audio frame */
section ("mpp_nocache") u8 	AudioFrame[AUDIO_FRAME_SIZE];
/* memory for AC'97 driver instance */
ADI_AC97_DRIVER_INSTANCE    AC97_Instance;

static char FullFilePath[MAX_FILE_NAME_LEN * MAX_PATH_NAME_LEN];

/* 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;

section ("mpp_nocache")
volatile u8  AudioDecoderOutBuf[AUDIO_DECODER_OUT_BUFFER_SIZE];
section ("mpp_nocache")
volatile u8  AudioDecoderInBuf[AUDIO_DECODER_IN_BUFFER_SIZE];

volatile u8   Ac97RegAccessComplete;  /* AC'97 Register access complete flag          */
volatile u8   AudioEnabledFlag;       /* Audio status flag (audio enabled/disabled)   */
volatile u8   LastSecond;

/* AD1980 register configuration table */
ADI_DEV_ACCESS_REGISTER ConfigAD1980Reg[] =
{
    { AD1980_REG_SERIAL_CONFIG,		0x9000		},	/* Enable slot-16 mode              */
    { AD1980_REG_MISC_CTRL,			0x0040		},	/* Enable Stereo MIC option 		*/
    { AD1980_REG_RECORD_SELECT,		0x0404		},	/* Select Line In as record source	*/
	{ AD1980_REG_RECORD_GAIN,		0x0000		},	/* Unmute Record Gain, 0dB 			*/
    { AD1980_REG_CTR_LFE_VOL_CTRL,	0x0000		},	/* Unmute Center/LFE Volume, 0dB 	*/
	{ AD1980_REG_SURR_VOL_CTRL,		0x0000		},	/* Unmute Surround out Volume, 0dB 	*/
    { ADI_DEV_REGEND,     			0			}	/* End of register access 			*/
};
    
/* AD1980 Sample rate configuration table */
ADI_DEV_ACCESS_REGISTER AD1980SampleRate[] =
{
    { AD1980_REG_POWER_CTRL_STAT,	0x030C	    },	/* Power down the DACs and ADC before changing sampling rate*/
	{ AD1980_REG_EXTD_AUDIO_CTRL,	0x0000		},	/* Disable variable audio rate & Double audio rate          */
	{ AD1980_REG_FRONT_DAC_RATE, 	48000	    },	/* Set Front out DAC sample rate	                        */
	{ AD1980_REG_SURR_DAC_RATE, 	48000	    },	/* Set Surround DAC sample rate		                        */
	{ AD1980_REG_CTR_LFE_DAC_RATE, 	48000	    },	/* Set Center/LFE DAC sample rate	                        */
	{ AD1980_REG_ADC_RATE, 			48000	    },	/* Set ADC sample rate				                        */
	{ AD1980_REG_POWER_CTRL_STAT,	0x000F		},	/* Power-up the DACs & ADC			                        */
	{ ADI_DEV_REGEND,     			0			}	/* End of register access 			                        */
};

/* AD1980 Volume control configuration table */
ADI_DEV_ACCESS_REGISTER AD1980VolumeConfig[] =
{
	{ AD1980_REG_MASTER_VOL_CTRL,	0x0000		},	/* Unmute Master Volume, 0dB 		*/
    	{ AD1980_REG_LINE_IN_VOL_CTRL,	0x0000		},	/* Unmute Line In Volume, 0dB 		*/
	{ AD1980_REG_PCM_OUT_VOL_CTRL,VOLUME_MIN	},	/* Min PCM Out Volume  */
	{ ADI_DEV_REGEND,     			0			}	/* End of register access 			                        */
};

static volatile int CodecVolume = VOLUME_MIN;

/*****************************************************************************\

  Externs

\*****************************************************************************/



/*****************************************************************************\

  Function Prototypes

\*****************************************************************************/
static void AudioCodecCallback(
	void 	*AppHandle,
	u32  	Event,
	void 	*pArg); 
	
int init_codec_1980(void);
int close_codec_1980(void);	
	
/*********************************************************************

    Function:       DisplayProgress
    Description:    Displays file progress
    
*********************************************************************/
section("adi_code_cache")
void DisplayProgress(
    FILE    *fp, 
    u32     FileLength, 
    u32     TrackLength, 
    u32     NumDecodedSamples, 
    u32     SamplingRate
){
    u8 Minutes,Seconds,MinRemain,SecRemain;
    u32 FilePos,TimeRemain;
    float s;
    
    /* IF (we've a valid sampling rate) */
    if (SamplingRate)
    {
        s = NumDecodedSamples/SamplingRate;
        Minutes = (u8)(s/60);
        Seconds = (u8)(s - (Minutes * 60));
        /* update time elapsed? */
        if (Seconds != LastSecond)
        {
            /* store the seconds count */
            LastSecond = Seconds;
            printf("\r\tTime Elapsed: %d:%02d", Minutes, Seconds);
            /* IF (Track length is available for this file) */
    		if (TrackLength)
	    	{
		        TimeRemain  = TrackLength - (Minutes*60+Seconds);
			    MinRemain   = (u8) (TimeRemain/60);
                SecRemain   = (u8) (TimeRemain - (MinRemain * 60));
                printf("\tTime Remain: %d:%02d", MinRemain, SecRemain);
		    }
    		/* ELSE IF (we've a valid file length) */
	    	else if (FileLength)
            {
                /* get the present file position (in percentage) */
    	        FilePos = 100*ftell(fp)/FileLength;
                printf("\t(%d%% Complete)",FilePos);
            }
        }
    }
}

void calculate_play_time(
	unsigned int *minute,
	unsigned int *second,
	unsigned int single_chan_acc_sample_count,
	unsigned int Fs)
{
	float s;
	
	s = single_chan_acc_sample_count/Fs;
	*minute = (unsigned int)(s / 60);	
	*second = (unsigned int)(s - *minute * 60);
}



/*********************************************************************

    Function:       init 1980 codec
    Description:    Initialises Audio Codec
    
*********************************************************************/
int init_codec_1980(void)
{

    u32 Result = ADI_DEV_RESULT_SUCCESS;

    /* Structure to init AD1980 driver */
    ADI_AD1980_INIT_DRIVER  	InitAD1980;

    /* Clear register access complete flag to start with */
    Ac97RegAccessComplete = TRUE;
    
    /* Populate AD1980 driver Initialisation structure */
	InitAD1980.pDataFrame       = (void *)AudioFrame;	/* SPORT Audioframe data location 	*/
	InitAD1980.DataFrameSize    = AUDIO_FRAME_SIZE;		/* Size of SPORT Audio Frame		*/
	InitAD1980.pAC97            = &AC97_Instance;		/* Location to store AC'97 driver instance	*/
	InitAD1980.ResetFlagId      = AD1980_RESET_FLAG;	/* AD1980 reset flag ID				*/
	InitAD1980.SportDevNumber   = AD1980_SPORT_DEV_NUMBER;	/* SPORT device connected to AD1980	*/
	
    do
    {
        /************** Open AD1980 device ***************/
    
        /* open the AD1980 driver for loopback */
        if((Result = adi_dev_Open(  adi_dev_ManagerHandle,     		/* Dev manager Handle                       */
                                    &ADIAD1980EntryPoint,           /* Entry point for AD1980 driver            */
                                    0,           					/* Device number                            */
	                                NULL,                           /* No client handle                         */
    	                            &AudioCodecDriverHandle,        /* Location to store AD1980 driver handle   */
        	                        ADI_DEV_DIRECTION_BIDIRECTIONAL,/* Data Direction                           */
            	                    adi_dma_ManagerHandle,         	/* Handle to DMA Manager                    */
                	                adi_dcb_QueueHandle,            /* Handle to callback manager               */
                    	            AudioCodecCallback))	        /* Callback Function                        */
		            != ADI_DEV_RESULT_SUCCESS) 
        {
     	    printf("Failed to open AD1980 device , Error Code: 0x%08X\n",Result);
     	    break;
        }

        /************** Configure AD1980 device ***************/
        
        /* Initialise AD1980 driver instance */
        if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_AD1980_CMD_INIT_DRIVER, (void*)&InitAD1980))!= ADI_DEV_RESULT_SUCCESS)
        {
            printf("Failed to Initialise AD1980, Error Code: 0x%08X\n",Result);
            break;
        }
        
		/* Set driver to process AC'97 frames at DCB level (IVG 14) */
        if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_AD1980_CMD_USE_DCB_TO_PROCESS_FRAMES, (void*)adi_dcb_QueueHandle))!= ADI_DEV_RESULT_SUCCESS)
        {
            printf("Failed to set AD1980 to use Deferred callback to process AC'97 frames, Error Code: 0x%08X\n",Result);
            break;
        }

        /* Set Dataflow method */
        if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void*)ADI_DEV_MODE_CHAINED))!= ADI_DEV_RESULT_SUCCESS)
        {
            printf("Failed to set dataflow method, Error Code: 0x%08X\n",Result);
            break;
        }

        /* wait until the previous AC'97 register access request gets serviced */
        while(Ac97RegAccessComplete == FALSE);
        Ac97RegAccessComplete = FALSE;  /* lock the AC'97 register access */
        
        /* configure AD1980 registers */
		if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_REGISTER_TABLE_WRITE, (void *)ConfigAD1980Reg))!= ADI_DEV_RESULT_SUCCESS)
    	{
			printf("Failed to configure AD1980 registers, Error Code: 0x%8X\n",Result);
		}
    	
        /******************Buffer preparation **********************/
	    
        /* Outbound 1D buffers */
        OutboundBuffer0.Data                = (void *)OutboundData0;
        OutboundBuffer0.ElementCount        = sizeof(OutboundData0);
	    OutboundBuffer0.ElementWidth        = 1;
    	OutboundBuffer0.CallbackParameter   = &OutboundBuffer0;   /* enable callback on completion of this buffer */
	    OutboundBuffer0.ProcessedFlag       = FALSE;
    	OutboundBuffer0.pNext               = NULL;
    	
		OutboundBuffer1.Data                = (void *)OutboundData1;
		OutboundBuffer0.ElementCount        = sizeof(OutboundData1);
	    OutboundBuffer1.ElementWidth        = 1;
    	OutboundBuffer1.CallbackParameter   = &OutboundBuffer1;   /* enable callback on completion of this buffer */
	    OutboundBuffer1.ProcessedFlag       = FALSE;
    	OutboundBuffer1.pNext               = NULL;	
    	
    	
    }while(0);
    
    
    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;

    /* IF (Sample rate is set to 48kHz or 96kHz) */
    if ((SamplingRate == 48000) ||
        (SamplingRate == 96000))
    {
        /* disable Variable audio rate */
        AD1980SampleRate[1].Data &= ~AC97_RFLD_ENABLE_VRA;
    }
    else
    {
        /* enable Variable audio rate */
        AD1980SampleRate[1].Data |= AC97_RFLD_ENABLE_VRA;
    }
    
    /* IF (Sampling rate is greater than 48kHz) */
    if (SamplingRate > 48000)
    {
        /* enable Double rate audio */
        AD1980SampleRate[1].Data |= AC97_RFLD_ENABLE_DRA;
        /* and half the sampling rate */
        SamplingRate = SamplingRate/2;
    }
    /* ELSE (Sampling rate is less than or equal to 48kHz) */
    else
    {
        /* disable Double rate audio */
        AD1980SampleRate[1].Data &= ~AC97_RFLD_ENABLE_DRA;
    }
    
    /* update sampling rate configuration table */
    /* update Front out DAC sample rate	*/
    AD1980SampleRate[2].Data = SamplingRate;
    /* update Surround DAC sample rate	*/
    AD1980SampleRate[3].Data = SamplingRate;
    /* update Center/LFE DAC sample rate	*/
    AD1980SampleRate[4].Data = SamplingRate;
    /* update ADC sample rate	*/
    AD1980SampleRate[5].Data = SamplingRate;

    do
    {
        /* Disable codec dataflow before updating sample rate or loading new buffers */
        if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)FALSE))!= ADI_DEV_RESULT_SUCCESS)
        {
		    printf("Failed to disable AD1980 dataflow, Error Code: 0x%08X\n",Result);
		    break;
        }
    
        /* Set AD1980 ins pseudo multichannel audio mode*/
		if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_AC97_CMD_ENABLE_TRUE_MULTICHANNEL_AUDIO, (void *)FALSE))!= ADI_DEV_RESULT_SUCCESS)
    	{
			printf("Failed set AD1980 driver in pseudo multi-channel mode, Error Code: 0x%08X\n",Result);
			break;
		}

		/* wait until the previous AC'97 register access request gets serviced */
		while(Ac97RegAccessComplete == FALSE);
		Ac97RegAccessComplete = FALSE;  /* lock the AC'97 register access */
		
		/* Update AD1980 registers with new sampling rate */
		if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_REGISTER_TABLE_WRITE, (void *)AD1980SampleRate))!= ADI_DEV_RESULT_SUCCESS)
    	{
			printf("Failed to update AD1980 DAC sampling rate, Error Code: 0x%08X\n",Result);
			break;
		}
		
		/* Flush DAC application buffer queue before submitting new buffers */
		if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_AC97_CMD_CLEAR_DAC_APP_BUFFER_PTRS, (void *)NULL))!= ADI_DEV_RESULT_SUCCESS)
    	{
			printf("Failed to flush AD1980 DAC application buffer queue, Error Code: 0x%08X\n",Result);
			break;
		}

        /* Initialize Outbound buffers ElementCount */
		OutboundBuffer0.ElementCount = AUDIO_CODEC_DAC_BUFFER_SIZE;
		OutboundBuffer1.ElementCount = AUDIO_CODEC_DAC_BUFFER_SIZE;
		
		/* 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 AD1980, Error Code: 0x%08X\n",Result);
    	    break;
        }

        		/* submit Outbound audio buffers */
	    if ((Result = adi_dev_Write(AudioCodecDriverHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)&OutboundBuffer1)) != ADI_DEV_RESULT_SUCCESS)
        {
            printf("Failed to submit Outbound Audio Buffer 1 to AD1980, Error Code: 0x%08X\n",Result);
    	    break;
        }
        
        /* wait until AC'97 driver configures codec registers with new sampling rate */
		while(Ac97RegAccessComplete == FALSE);
        Ac97RegAccessComplete = FALSE;  /* lock the AC'97 register access */


    	/* configure AD1980 volume control registers */
	if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_REGISTER_TABLE_WRITE, (void *)AD1980VolumeConfig))!= ADI_DEV_RESULT_SUCCESS)
    	{
		printf("Failed to configure AD1980 volume control registers, Error Code: 0x%8X\n",Result);
	}

        /* wait until AC'97 driver configures codec registers with initial volume control */
		while(Ac97RegAccessComplete == FALSE);
        Ac97RegAccessComplete = FALSE;  /* lock the AC'97 register access */
        
       	CodecVolume = VOLUME_MIN;


		/* Enable codec dataflow */
        if((Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE))!= ADI_DEV_RESULT_SUCCESS)
        {
		    printf("Failed to enable AD1980 dataflow, Error Code: 0x%08X\n",Result);
        }
        
    }while(0);
    

    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:       SetDacSamplingRate
    Description:    Sets Audio Codec DAC sampling rate
    
*********************************************************************/
//section ("L1_code")
section ("callback_code")

static void AudioCodecCallback(
	void 	*AppHandle,
	u32  	Event,
	void 	*pArg
){
    u32 NumBytesRead=0;

    ADI_DEV_1D_BUFFER *pBuffer = (ADI_DEV_1D_BUFFER *)pArg;
	switch (Event)
	{
	    /* AC'97 register access complete */
	    case (ADI_AC97_EVENT_REGISTER_ACCESS_COMPLETE):
	        /* Mark as AD1980 register access complete */
	        Ac97RegAccessComplete = TRUE;
		    break;
		    
		/* CASE (DMA buffer processed) */
		case ADI_DEV_EVENT_BUFFER_PROCESSED:

				// DAC processing			
				DACUserISR(pArg,(AUDIO_CODEC_DAC_BUFFER_SIZE/NUM_AUDIO_DAC_CHANNELS));
				/* re-submit the buffer */
               adi_dev_Write(AudioCodecDriverHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)pBuffer);
                    
			break;
		
		/* CASE (DMA error) */
		case ADI_DEV_EVENT_DMA_ERROR_INTERRUPT:
	        printf("DMA error\n");
			break;
		
		default:

            printf ("AudioCodecCallback: Unrecognised Event code: 0x%08X\n",Event); 
            break;
	}
}

/*********************************************************************

    Function:       AdjustCodecVolume
    Description:    Adjust Audio Codec volume level
    
*********************************************************************/
u32 AdjustCodecVolume(u32 VolumeLevel)
{
	 u32 Result = ADI_DEV_RESULT_SUCCESS;
	 bool VolumeChange;

	ADI_DEV_ACCESS_REGISTER VolCtrlReg = { AD1980_REG_PCM_OUT_VOL_CTRL,0 };
	
	VolumeChange = false;
	
	if(VolumeLevel == MSGID_VOLUME_DN)
	{
				
		if (CodecVolume >= VOLUME_MIN)
		{
		   	CodecVolume = VOLUME_MIN;
		}
		else
		{
			CodecVolume+=VOLUME_STRIDE;
		
			/* adjust the volume */
			VolumeChange = true;
		}
	}
	else if (VolumeLevel == MSGID_VOLUME_UP)
	{
		
		if (CodecVolume <= VOLUME_MAX)
		{
		   	CodecVolume = VOLUME_MAX;
		}
		else
		{
			CodecVolume-=VOLUME_STRIDE;
			/* adjust the volume */
			VolumeChange = true;

		}

	}
	
	/*APP_TRACE("Volume(%x)\n",CodecVolume);*/

	
	if (VolumeChange)
	{
		VolCtrlReg.Data = (u16)CodecVolume;
		/* configure AD1980 registers */
		Result = adi_dev_Control(AudioCodecDriverHandle, ADI_DEV_CMD_REGISTER_WRITE, (void *)&VolCtrlReg);
		if(Result != ADI_DEV_RESULT_SUCCESS)
    		{
			printf("Failed to configure AD1980 registers, Error Code: 0x%8X\n",Result);
		}
	
		/* wait until the previous AC'97 register access request gets serviced */
		while(Ac97RegAccessComplete == FALSE);
		Ac97RegAccessComplete = FALSE;  /* lock the AC'97 register access */	
	}
	
	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_1980 codec

	Description:	close 1980 codec.  

*********************************************************************/

int close_codec_1980(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);

}
/*****/
