#include "Page0.h"

#include "PalmChip.h"
#include "Ac97.h"
#include "p_dsp.h"

#define AC97_LOOP_BUF	8

BOOLEAN Ac97_bLoopTest, Ac97_bOutputOnly;

static volatile BOOLEAN _bWorking, _bNeedBuffer, _bLoopAlone;

void ac97_init()
{
	Ac97WriteReg(AC97_RESET, 0, 0);			// reset
	Ac97WriteReg(AC97_POWER_CONTROL, 0, 0);	// power up

	// mute unused volumes
	Ac97WriteReg(AC97_MASTER_VOL_STEREO, AC97_MUTE_HI, AC97_MUTE_LO);  // Line Out
	Ac97WriteReg(AC97_HEADPHONE_VOL, AC97_MUTE_HI, AC97_MUTE_LO);      // 
	Ac97WriteReg(AC97_MASTER_VOL_MONO, AC97_MUTE_HI, AC97_MUTE_LO);    // TAD Output
	Ac97WriteReg(AC97_PCBEEP_VOL, AC97_MUTE_HI, AC97_MUTE_LO);         // none
	Ac97WriteReg(AC97_PHONE_VOL, AC97_MUTE_HI, AC97_MUTE_LO);          // TAD Input (mono)
#ifdef VER_IPH_E00
	Ac97WriteReg(AC97_MIC_VOL, 0x00, 0x57);							   // EN +20DB boost, MIC gain: -19.5dB
#else
#ifdef _USE_FXS
	Ac97WriteReg(AC97_MIC_VOL, AC97_MUTE_HI, AC97_MUTE_LO);            // MIC Input (mono)
#else
	Ac97WriteReg(AC97_MIC_VOL, AC97_MUTE_HI, AC97_MICBOOST_LO);            // MIC Input (mono)
#endif
#endif
	Ac97WriteReg(AC97_LINEIN_VOL, AC97_MUTE_HI, AC97_MUTE_LO);         // Line Input (stereo)
	Ac97WriteReg(AC97_CD_VOL, AC97_MUTE_HI, AC97_MUTE_LO);             // CD Input (stereo)
	Ac97WriteReg(AC97_VIDEO_VOL, AC97_MUTE_HI, AC97_MUTE_LO);          // none
	Ac97WriteReg(AC97_AUX_VOL, AC97_MUTE_HI, AC97_MUTE_LO);            // Aux Input (stereo)
	Ac97WriteReg(AC97_PCMOUT_VOL, AC97_MUTE_HI, AC97_MUTE_LO);         // Wave Output (stereo)

	Ac97WriteReg(AC97_PCMOUT_VOL, AC97_RIGHTVOL_HI, AC97_RIGHTVOL_LO);		// Waveout volume 
	
	Ac97WriteReg(AC97_RECORD_GAIN, AC97_RECGAIN, AC97_RECGAIN);	   // set record gain 

#ifdef AC97_MIC_INPUT
	Ac97WriteReg(AC97_RECORD_SELECT, AC97_RECMUX_MIC, AC97_RECMUX_MIC);		// select mic in as input 
#endif

#ifdef WOLFSON_MIXER
	Ac97WriteReg(AC97_MIXER_VOL, AC97_MUTE_HI, AC97_MUTE_LO);
#endif
	Ac97WriteReg(AC97_GENERAL_PURPOSE, 0x80, 0);		// select MIC 1, PCM output path: Post-3D			

	Ac97WriteReg(AC97_EXT_AUDIO_CTRL, 0x00, 0x09);		// enable varible input sampling rate
//	Ac97WriteReg(AC97_RECORD_GAIN_MIC, AC97_RECGAIN, AC97_RECGAIN);			// set record gain 
}

void ac97_fifo_init()
{
	// Reset FIFO module of Palm1
//	XBYTE[SYS_RESET2] = 0x3;
//	XBYTE[SYS_RESET2] = 0x0;

	// Set up FIFO's 
	XBYTE[CHN6_START_ADDR] = 0;	// DA 
	XBYTE[CHN6_END_ADDR] = 0x7f;
//	XBYTE[CHN6_HIGH_CHECK] = 0x02;
//	XBYTE[CHN6_LOW_CHECK] = 0x04;
//	XBYTE[CHN6_STOP_CHECK] = 0x06;
	XBYTE[CHN6_HIGH_CHECK] = 0x20;
	XBYTE[CHN6_LOW_CHECK] = 0x40;
	XBYTE[CHN6_STOP_CHECK] = 0x60;

	XBYTE[CHN7_START_ADDR] = 0x0;	// AD 
	XBYTE[CHN7_END_ADDR] = 0x7f;
//	XBYTE[CHN7_HIGH_CHECK] = 0x78;
//	XBYTE[CHN7_LOW_CHECK] = 0x70;
//	XBYTE[CHN7_STOP_CHECK] = 0x68;
	XBYTE[CHN7_HIGH_CHECK] = 0x60;
	XBYTE[CHN7_LOW_CHECK] = 0x40;
	XBYTE[CHN7_STOP_CHECK] = 0x20;

	// ac97 in and usb in shares the phisical fifo with camera in channel 3
	// the input channel might need to be flushed
	XBYTE[CHN3_FLUSH] = 0x01;
}

void Ac97Initialize()
{
//	DSHORT i;

	XBYTE[CODEC_SHARE] = 2;	// select AC97, mono mode, LSB first 
//	XBYTE[CODEC_SHARE] = 6;	// select AC97, mono mode, MSB first 

/*	XBYTE[AC97_WARMWAKE] = 1;
	for (i = 0; i < 512; i ++)
	{
		XBYTE[0x5fff] = 0xff;
	}
	XBYTE[AC97_WARMWAKE] = 0;
*/
	// Enable dma channel 6 (ac97 output) and dma channel 7 (ac97 input) 
	XBYTE[SDRAM_DMA_EN1] = DMA_MASK_AC97_IN;	
//	XBYTE[SDRAM_DMA_EN1] = DMA_MASK_AC97_IN+DMA_MASK_AC97_OUT;	
	XBYTE[SDRAM_DMA_EN2] = 0;	// no high channels 

	// Clear all dma irq
	XBYTE[SDRAM_DMA_INT1] = 0xff;
	XBYTE[SDRAM_DMA_INT2] = 0xff;
	XBYTE[INT_ENABLE] |= INT_MASK_DMA;	// Enable dma interrupt

	ac97_fifo_init();
	ac97_init();

#ifdef _USE_FXS_AM79R70
	// Set SLIC into Standby mode: C1=1, C2=0, C3=1
	XBYTE[IO_LO_ENABLE] |= SLIC_CTRL_OR_MASK;
	XBYTE[IO_LO_DOUT] |= 0x01;	// PIO0 = 1
	XBYTE[IO_LO_DOUT] &= 0xfd;	// PIO1 = 0
	XBYTE[IO_LO_DOUT] |= 0x04;	// PIO2 = 1

	// Set SLIC B2EN to use Vbat1
	XBYTE[IO_HHI_ENABLE] = 1;
	XBYTE[IO_HHI_DOUT] = 1;		// PIO16 = 1
#endif
}

void Ac97Startup()
{
	Dsp_bFinished = TRUE;

	_bWorking = TRUE;
	Ac97StartCodec();
	Ac97Stop();

	_bLoopAlone = FALSE;
	Ac97_bLoopTest = FALSE;
	Ac97_bOutputOnly = FALSE;
	Ac97OnHandsetChange();
}

void Ac97ReStartup()
{
	_bWorking = TRUE;
	Ac97StartCodec();
	Ac97Stop();
}

void Ac97Start()
{
	if (_bWorking)	return;

#ifdef LM386_CTRL
	if (!PIO_bHandsetUp)
	{
		Cam_LedOn(3);
	}
#endif

#ifdef _USE_FXS
	Slic_iCidStatus = 0;
#endif

	if (!_bLoopAlone)
	{
		DspStart();
	}

	_bWorking = TRUE;
	_bNeedBuffer = TRUE;

	if (_bLoopAlone)
	{	// 10ms loop back
		Ac97_iBuf = AC97_LOOP_BUF;
		Ac97StartCodec();
		Ac97Input(0, 240, 0);	
		Ac97Output(0, 240, 0);
	}
	else
	{
		Ac97InitBuffer();
	}
}

void Ac97Stop()
{
	if (_bWorking)
	{
		_bWorking = FALSE;
		Ac97StopCodec();
		XBYTE[SDRAM_STATUS1] = 0x60;	// Stop previous SDRAM transfer, if any
	}
#ifdef LM386_CTRL
	Cam_LedOff(3);
#endif

	Ac97_bLoopTest = FALSE;
	_bLoopAlone = FALSE;
	while (!Dsp_bFinished);
}

void Ac97InputVolHigher()
{
	if (PIO_bHandsetUp)
	{
		if (Ac97_iHandsetInputVol < AC97_MIC_VOL_MAX)
		{
			Ac97_iHandsetInputVol ++;
		}
	}
	else
	{
		if (Ac97_iHandfreeInputVol < AC97_MIC_VOL_MAX)
		{
			Ac97_iHandfreeInputVol ++;
		}
	}
	Ac97SetInputVol();
}

void Ac97InputVolLower()
{
	if (PIO_bHandsetUp)
	{
		if (Ac97_iHandsetInputVol > 0)
		{
			Ac97_iHandsetInputVol --;
		}
	}
	else
	{
		if (Ac97_iHandfreeInputVol > 0)
		{
			Ac97_iHandfreeInputVol --;
		}
	}
	Ac97SetInputVol();
}


void Ac97Test(UCHAR iSelectKey)
{
	DCHAR iKey, iVal;

	iKey = iSelectKey;
	switch (iKey)
	{
	case 0:
		if (!_bLoopAlone)
		{
			DisplayString((PCCHAR)"Ac97 loop test", 0);
			DisplayString(g_cStart, 1);
			Ac97ReStartup();							//Re-startup AC97 codec
			Ac97SetInputSampleRate(FALSE);						//48K bps input
			_bLoopAlone = TRUE;
			Ac97Start();
		}
		break;
	case 1:
		if (_bLoopAlone)
		{
			Ac97Stop();
			DisplayString((PCCHAR)"Ac97 loop test", 0);
			DisplayString(g_cStop, 1);
		}
		break;
	case 2:	// output vol higher
		Ac97OutputVolHigher();
		iVal = PIO_bHandsetUp ? Ac97_iHandsetOutputVol : Ac97_iHandfreeOutputVol;
		DisplayStatus((PCCHAR)"Output Vol", iVal, (iVal+1)>>1);
		break;
	case 3:	// output vol lower
		Ac97OutputVolLower();
		iVal = PIO_bHandsetUp ? Ac97_iHandsetOutputVol : Ac97_iHandfreeOutputVol;
		DisplayStatus((PCCHAR)"Output Vol", iVal, (iVal+1)>>1);
		break;
	case 4:	// input vol higher
		Ac97InputVolHigher();
		iVal = PIO_bHandsetUp ? Ac97_iHandsetInputVol : Ac97_iHandfreeInputVol;
		DisplayStatus((PCCHAR)"Input Vol", iVal, (iVal+1)>>1);
		break;
	case 5:	// input vol lower
		Ac97InputVolLower();
		iVal = PIO_bHandsetUp ? Ac97_iHandsetInputVol : Ac97_iHandfreeInputVol;
		DisplayStatus((PCCHAR)"Input Vol", iVal, (iVal+1)>>1);
		break;
	}
}

void Ac97CodecTest(UCHAR iSelectKey)
{
	DCHAR iKey;

	iKey = iSelectKey;

	if (iKey >= CODEC_NULL)
	{	// stop any test
		if (Ac97_bLoopTest)
		{
			Ac97Stop();
			Ac97_bLoopTest = FALSE;
			Task_bConnected = FALSE;
			SysSetMode(3);		// run in 22Mhz
			DisplayString((PCCHAR)"AC97&DSP test", 0);
			DisplayString(g_cStop, 1);
		}
	}
	else
	{
		if (!Ac97_bLoopTest)
		{
			Ac97_bLoopTest = TRUE;

			Task_bConnected = TRUE;
			Ac97_iDtmfDuration = 0;
			Ac97_iDtmfCode = DTMF_SIGNAL_QUIET;
			Sys_iDtmfType = DTMF_INBAND_AUDIO;

			SysSetMode(6);			// run in 36.75Mhz
			Ac97SetInputSampleRate(TRUE);						//8K bps input
			DisplayString((PCCHAR)"AC97&DSP test", 0);
			DisplayString(g_cStart, 1);
			Dsp_iType = iKey;
			Ac97Start();
		}
	}
}

void Ac97HandleInterrupt()
{
	if (!Ac97CheckIrq())		return;
	if (!_bWorking)			return;

	if (_bLoopAlone)
	{
		// DMA 7, ac97 input
		XBYTE[DMA7_START_ADDR2] = Ac97_iBuf;
		XBYTE[DMA7_START_ADDR3] = AC97_IN_SDRAM_HI;
		XBYTE[DMA7_HIGH] = 0;
		XBYTE[DMA7_START_ADDR1] = 0;		// startup DMA

		Ac97_iBuf = AC97_LOOP_BUF - Ac97_iBuf;

		// DMA 6, ac97 output
		if (XBYTE[SDRAM_STATUS1] & 0x20)
		{
			XBYTE[SDRAM_STATUS1] = 0x20;	// Stop previous SDRAM transfer first
		}
		XBYTE[DMA6_START_ADDR2] = Ac97_iBuf;
		XBYTE[DMA6_START_ADDR3] = AC97_IN_SDRAM_HI;
		XBYTE[DMA6_HIGH] = 0;
		XBYTE[DMA6_START_ADDR1] = 0;		// startup DMA
	}
	else
	{
		Ac97SwapBuffer();
		DspCall();
	}

	_bNeedBuffer = TRUE;
}

void Ac97Run()
{
	if (!_bWorking)		return;

	while (DspHandleEncodeData());

	if (!_bNeedBuffer)	return;

	_bNeedBuffer = FALSE;
}

