// Welch, Wright, & Morrow, 
// Real-time Digital Signal Processing, 2005

///////////////////////////////////////////////////////////////////////
// Filename: DSK_Support.c
//
// Synopsis: Functions to support basic initialization of the DSK
//           hardware resources.
//
///////////////////////////////////////////////////////////////////////

#include "DSK_Support.h"   
#include "DSK_Config.h"   


static float SampleFreq;

///////////////////////////////////////////////////////////////////////
// Purpose:   Returns the current sample frequency as determined by the 
//            DSK_Init function.
//
// Input:     None
//
// Returns:   Sample frequency in floating point format
//
// Calls:     None
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
float GetSampleFreq()
{
	return SampleFreq;
}

void Init_AIC23(int codec)
///////////////////////////////////////////////////////////////////////
// Purpose:   Performs initialization of the 6713 DSK's onboard codec
//
// Input:     codec - selects which input mode of the AIC23
//            codec to use (line, mic 0db, mic 20db)
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{	
	#define NUM_AIC23_REGISTERS 10
	McBSP *port  = McBSP0_Base;
	volatile int i, j;
	unsigned short AIC23_data[NUM_AIC23_REGISTERS] = {
		// line inputs have ~6db attentuation network on DSK
    	(0<<9) | 0x01b, // reg 0 - left line volume (+6 db)
    	(1<<9) | 0x01b, // reg 1 - right line volume (+6 db)
    	(2<<9) | 0x0f9, // reg 2 - left headphone volume (0 db)
    	(3<<9) | 0x0f9, // reg 3 - right headphone volume (0 db)
    	(4<<9) | 0x012, // reg 4 - analog audio path (DAC on, line input)
    	(5<<9) | 0x000, // reg 5 - digital audio path (no deemphasis)
    	(6<<9) | 0x000, // reg 6 - power down (all on)
    	(7<<9) | 0x053, // reg 7 - digital audio interface (DSP mode)
    	(8<<9) | 0x001, // reg 8 - sample rate (48kHz)
    	(9<<9) | 0x001  // reg 9 - digital interface (activate)
	};
	
	// set AIC23 input mode (defaults to line in)
	switch(codec) {
		case DSK6713_MicInput_0db:
			AIC23_data[4] = (4<<9) | 0x014; // reg 4 - analog audio path (DAC on, mic input, 0db)
			break;
		case DSK6713_MicInput_20db:
			AIC23_data[4] = (4<<9) | 0x015; // reg 4 - analog audio path (DAC on, mic input, +20db)
			break;
	}

	port->spcr = 0;          	// reset port
	port->pcr  = 0x00000A0A; 	// fsxm/clkx outputs, spi format
	port->rcr  = 0x00000000;
	port->xcr  = 0x00000040; 	// single 16-bit word
	port->srgr = 0x20100F04; 	// fs on write, 16-bits,  
	port->spcr = 0x00C00000; 	// start fsg 
	for(i = 0;i < 1000;i++)
		;
	port->spcr = 0x00C11000; 	// enable tx, clkstp for spi

	for(i = 0;i < NUM_AIC23_REGISTERS;i++) {
		while(!(port->spcr & 0x00020000)) // wait for codec ready
			;
		port->dxr = AIC23_data[i]; // write codec registers
	}
	
	SampleFreq = 48000.0;	 	// set in codec registers
	
	Init_McBSP(McBSP1_Base, AIC23_mode); // initialize data link codec
}				 


void DSK_Init(int Codec, unsigned int Divider)
///////////////////////////////////////////////////////////////////////
// Purpose:   Performs basic hardware initialization of the DSK 
//            interrupts, serial ports, and timer1
//
// Input:     Codec - indicates which codec support to configure.  Support
//                    is provided for the onboard TLC32AD535 codec, the
//                    Texas Instruments Analog Daughtercard, and the
//                    Educational DSP, LLC PCM3006 daughtercard.  The 
//                    required values are enumerated in DSK_Support.h
//
//            TimerDivider - divider value to initialize Timer1 with
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{
  	CSR=0x100;			        		// disable all interrupts
  	IER=0;
	
	if((Codec == DSK6713_LineInput) || (Codec == DSK6713_MicInput_0db) || (Codec == DSK6713_MicInput_20db)) {
		Init_6713PLL();								// set up 6713 PLL
 		*(unsigned volatile int *)IMH = 0x35803DC3; // assign McBSP1 to INT11/12	
		Init_AIC23(Codec);							// initialize codec using McBSP0 
	}	
	else {
		if(C62_McBSP_IMH_Bug()) { 		// is C6211 Rev 1.X silicon with IMH bug
		   	if(Codec == TLC320AD535) 	// so intentionally reverse IMH settings for McBSPs
 	 			*(unsigned volatile int *)IMH = 0x35803DC3; // assign McBSP1 to INT11/12
	 	 	else
 		 		*(unsigned volatile int *)IMH = 0x3DC03583; // assign McBSP0 to INT11/12
		}
		else { 							// is C6711 or C6211 Rev 2.0+ silicon
	   		if(Codec == TLC320AD535)
 	 			*(unsigned volatile int *)IMH = 0x3DC03583; // assign McBSP0 to INT11/12
 	 		else
	 	 		*(unsigned volatile int *)IMH = 0x35803DC3; // assign McBSP1 to INT11/12
		}
	
		if(Codec == TLC320AD535) { 		// using onboard codec
			SampleFreq = 8000.0;
			Init_McBSP(McBSP0_Base, AD535_mode);
		}
		else { 							// using codec daughtercard
	    	SampleFreq = SetTimer1Period(Divider);
			Init_McBSP(McBSP1_Base, (Codec == TI_PCM3003_16bit)?PCM0_Slave_Inverted_mode:PCM0_Master_mode); 
		}
	}

  	IER |= 0x1802;  					// enable McBSP Rx/Tx interrupts (11/12)
  	ICR = 0xffff;       				// clear all pending interrupts
  	CSR |= 1;           				// set GIE
}

void DSK_Init_EDMA(int Codec, unsigned int Divider)
///////////////////////////////////////////////////////////////////////
// Purpose:   Performs basic hardware initialization of the DSK 
//            interrupts, serial ports, timer1 (for use with EDMA)
//
// Input:     Codec - indicates which codec support to configure.  Support
//                    is provided for the onboard TLC32AD535 codec, the
//                    Texas Instruments Analog Daughtercard, and the
//                    Educational DSP, LLC PCM3006 daughtercard.  The 
//                    required values are enumerated in DSK_Support.h
//
//            TimerDivider - divider value to initialize Timer1 with
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{
  	CSR=0x100;			        	// disable all interrupts
  	IER=0;
	
	if((Codec == DSK6713_LineInput) || (Codec == DSK6713_MicInput_0db) || (Codec == DSK6713_MicInput_20db)) {
		Init_6713PLL();
		Init_AIC23(Codec);					// initialize codec using McBSP0 
	}	
	else {
		if(Codec == TLC320AD535) { 		// using onboard codec
			SampleFreq = 8000.0;
			Init_McBSP(McBSP0_Base, AD535_mode);
		}
		else { 							// using codec daughtercard
	    	SampleFreq = SetTimer1Period(Divider);
			Init_McBSP(McBSP1_Base, (Codec == TI_PCM3003_16bit)?PCM0_Slave_Inverted_mode:PCM0_Master_mode); 
		}
	}

  	IER |= 0x0102;  				// enable EDMA interrupt (INT8)
  	ICR = 0xffff;       			// clear all pending interrupts
  	CSR |= 1;           			// set GIE
}


void Init_McBSP(McBSP *port, int mode)
///////////////////////////////////////////////////////////////////////
// Purpose:   Initializes McBSP to support the selected codec. For stereo
//            codecs, the port is configured so that both 16-bit 
//            channel's data is packed into a single 32-bit word
//
// Input:     mode - selected codec to support
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{   
	switch(mode) {
	case PCM0_Master_mode:	// PCM300X format 0, master
		port->spcr = 0;          /* reset port */
		port->pcr  = 0x00000A03; /* fsx/clkx output, fsr, clkr input */                          
		/* srg set up for clks/8, 32 bits, fsr width = 16bits */
		port->srgr = (1 << 28) + (31 << 16) + (15 << 8) + (7);
		port->rcr  = 0x000400A0; /* 1-phase, 32-bits */
		port->xcr  = 0x000400A0; 
		/* delay */
		port->spcr = 0x00400000; /* start srg */ 
		/* delay */
		port->spcr = 0x00C00000; /* start fsg */ 
		/* delay */
		port->spcr = 0x00C10001; /* enable tx/rx */
		port->dxr  = 0;
		break;
	case PCM0_Slave_mode:	// PCM300X format 0, slave
		port->spcr = 0;          /* reset port */
		port->pcr  = 0x00000003; /* fsx/clkx/fsr/clkr input */                          
		port->srgr = 0;  		 /* srgr disabled */
		port->rcr  = 0x000400A0; /* 1-phase, 32-bits */
		port->xcr  = 0x000400A0; 
		port->spcr = 0x00010001; /* enable tx/rx */
		port->dxr  = 0;
		break;	
	case PCM0_Slave_Inverted_mode:	// PCM300X format 0, slave with inverted signals
		port->spcr = 0;          /* reset port */
		port->pcr  = 0x00000000; /* fs/clk input */                          
		port->srgr = 0;  		 /* srgr disabled */
		port->rcr  = 0x000400A0; /* 1-phase, 32-bits */
		port->xcr  = 0x000400A0; 
		port->spcr = 0x00010001; /* enable tx/rx */
		port->dxr  = 0;
		break;
	case AIC23_mode: // using AIC23 codec in DSP mode
		port->spcr = 0;          /* reset port */
		port->pcr  = 0x0000000f; /* fsx/clkx/fsr/clkr input, fs active low */                          
		port->srgr = 0;  		 /* srgr disabled */
		port->rcr  = 0x000400A0; /* 1-phase, 32-bits */
		port->xcr  = 0x000400A0; 
		port->spcr = 0x00010001; /* enable tx/rx */
		port->dxr  = 0;
		break;	
	case AD535_mode: 
	default:
		port->spcr = 0;
		port->pcr  = 0;
		port->rcr  = 0x10040;
		port->xcr  = 0x10040;
		port->spcr = 0x12001;  
		port->dxr  = 0;
		break;
	}
}


float SetTimer1Period(unsigned int div)
///////////////////////////////////////////////////////////////////////
// Purpose:   Initializes the Timer1 by setting it to clock mode
//			  and using the divider value given to drive the codec
//            daughtercard.  If the codec daughtercard is using its
//            onboard oscillator, the timer set-up is bypassed and
//            a default value of 48kHz is returned.
//
// Input:     None
//
// Returns:   Actual sample frequency attained (in hz), or 48000 when
//            using the onboard oscillator.
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{               
	if(div == EXTERNAL_CLOCK) // codec is running on onboard oscillator
		return 48000.0; 
	else {
		*(volatile unsigned int *)TIMER1_CTRL = 0;		/* stop timer */
		/* freq = 37.5Mhz / (2 * divider) */
	  	*(volatile unsigned int *)TIMER1_PRD = div;		/* set freq ~9.5Mhz */
		*(volatile unsigned int *)TIMER1_CTRL = 0x3D1;	/* clock mode, int clock */
		return ((TIMER_CLOCK/2) / 256) / div;
	}
}


int C62_McBSP_IMH_Bug() 
///////////////////////////////////////////////////////////////////////
// Purpose:   Detects a silicon bug present in Rev 1.X C6211 silicon
//			  where McBSP0 and McBSP1 switched in interrupt mux (IMH)
//
// Input:     None
//
// Returns:   1 if silicon bug is present, 0 otherwise
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{        
	volatile McBSP *port0  = McBSP0_Base, *port1  = McBSP1_Base; 
	volatile int i;
	
  	IER=1;                  // disable all interrupts except NMI */
  	*(unsigned volatile int *)IMH = 0x08203DC3; /* assign McBSP1 Tx/Rx to INT11/12 */
 	ICR=0xffff;             /* clear all pending interrupts      */
 	port1->spcr = 0; 		/* reset McBSP1 */
 	port0->spcr = 0; 		/* reset McBSP0 */
 	ICR=0xffff;             /* clear all pending interrupts      */
	port0->spcr = 0x312031; /* McBSP0 irq on sync error */
	port0->spcr = 0x392039; /* cause McBSP0 rx/tx sync error */

	i = 20;	                /* delay for irq recognition */
	while(i--)
		;

	if((IFR & 0x1800) == 0x1800) /* IFR set by McBSP1, so is Rev 1.X silicon */
		return 1;
	return 0;
}


// defines for 6713 PLL registers
#define PLL_BASE_ADDR   0x01b7c000
#define PLL_PID         (PLL_BASE_ADDR + 0x000)
#define PLL_CSR         (PLL_BASE_ADDR + 0x100)
#define PLL_MULT        (PLL_BASE_ADDR + 0x110)
#define PLL_DIV0        (PLL_BASE_ADDR + 0x114)
#define PLL_DIV1        (PLL_BASE_ADDR + 0x118)
#define PLL_DIV2        (PLL_BASE_ADDR + 0x11C)
#define PLL_DIV3        (PLL_BASE_ADDR + 0x120)
#define PLL_OSCDIV1     (PLL_BASE_ADDR + 0x124)
#define CSR_PLLEN          0x00000001
#define CSR_PLLPWRDN       0x00000002
#define CSR_PLLRST         0x00000008 
#define CSR_PLLSTABLE      0x00000040
#define DIV_ENABLE         0x00008000

void Init_6713PLL()
///////////////////////////////////////////////////////////////////////
// Purpose:   Configures the 6713 PLL for 225MHz clock
//
// Input:     None
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{
	*(unsigned int *)PLL_CSR = 		0x00000048; 	// Set the PLL back to power on reset state
	*(unsigned int *)PLL_DIV3 = 	0x00008001;
	*(unsigned int *)PLL_DIV2 = 	0x00008001;
	*(unsigned int *)PLL_DIV1 = 	0x00008000;
	*(unsigned int *)PLL_DIV0 = 	0x00008000;
	*(unsigned int *)PLL_MULT = 	0x00000007;
	*(unsigned int *)PLL_MULT = 	0x00000007;
	*(unsigned int *)PLL_OSCDIV1 = 	0x00080007;
	*(unsigned int *)PLL_DIV0 = 	DIV_ENABLE + 0; // now set for 225MHz
	*(unsigned int *)PLL_MULT = 	9;
	*(unsigned int *)PLL_OSCDIV1 = 	DIV_ENABLE + 4;
	*(unsigned int *)PLL_DIV3 = 	DIV_ENABLE + 4;
	*(unsigned int *)PLL_DIV2 = 	DIV_ENABLE + 3;
	*(unsigned int *)PLL_DIV1 = 	DIV_ENABLE + 1;
	*(unsigned int *)PLL_CSR = 		0x00000040; 	// out of reset
	*(unsigned int *)PLL_CSR = 		0x00000041;  	// enable
}
