navienginebsp/naviengine_assp/i2s/i2s_psl.cpp
author Ryan Harkin <ryan.harkin@nokia.com>
Tue, 28 Sep 2010 18:00:05 +0100
changeset 0 5de814552237
permissions -rw-r--r--
Initial contribution supporting NaviEngine 1 This package_definition.xml will build support for three memory models - Single (sne1_tb) - Multiple (ne1_tb) - Flexible (fne1_tb)

/*
* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  
* bsp\nwip_nec_naviengine\naviengine_assp\i2s\i2s_psl.cpp
*
*/



#include "navi_i2s.h"
#include <naviengine.h>

#ifdef __SMP__
static TSpinLock I2sLock = TSpinLock(TSpinLock::EOrderGenericIrqLow2);
#endif

// All channels have the same configuration. 
// base for registers are shifted for each channel by 0x400 (1<<10)
#define HW_CHAN_SHIFT 10

// Registers 
// these macros will return register address for a given interface
#define KHwI2SControl(intefaceId)		((KHwBaseI2S0 + KHoI2SCtrl)     + (intefaceId << HW_CHAN_SHIFT))
#define KHwI2SFifoControl(intefaceId) 	((KHwBaseI2S0 + KHoI2SFifoCtrl) + (intefaceId << HW_CHAN_SHIFT))
#define KHwI2SFifoStatus(intefaceId) 	((KHwBaseI2S0 + KHoI2SFifoSts)  + (intefaceId << HW_CHAN_SHIFT))
#define KHwI2SInterruptFlag(intefaceId) ((KHwBaseI2S0 + KHoI2SIntFlg)   + (intefaceId << HW_CHAN_SHIFT))
#define KHwI2SInterruptMask(intefaceId)	((KHwBaseI2S0 + KHoI2SIntMask)  + (intefaceId << HW_CHAN_SHIFT))
#define KHwI2SDataIn(intefaceId) 		((KHwBaseI2S0 + KHoI2SRx)       + (intefaceId << HW_CHAN_SHIFT))
#define KHwI2SDataOut(intefaceId) 		((KHwBaseI2S0 + KHoI2STx)       + (intefaceId << HW_CHAN_SHIFT))

#define AsspIsBitSet(addr, bit)	     (AsspRegister::Read32(addr)& (bit))
#define CLEARMASK(shift,len)         (((1 << len) - 1) << shift)
#define AsspGetBits(w,shift,len)     ((AsspRegister::Read32(w) >> shift) & ((1 << (len)) - 1))
#define AsspSetBits(w,set,shift,len) (AsspRegister::Modify32(w, (CLEARMASK(shift, len)) , (set << shift)))


NONSHARABLE_CLASS(D2sChannelNE1_TB) : public DI2sChannelBase
	{
public:
	D2sChannelNE1_TB(TInt aInterfaceId);
	
	virtual TInt ConfigureInterface(TDes8* aConfig);
	virtual TInt GetInterfaceConfiguration(TDes8& aConfig);
	virtual TInt SetSamplingRate(TInt aSamplingRate);
	virtual TInt GetSamplingRate(TInt& aSamplingRate);
	virtual TInt SetFrameLengthAndFormat(TInt aFrameLength, TInt aLeftFramePhaseLength);
	virtual TInt GetFrameFormat(TInt& aLeftFramePhaseLength, TInt& aRightFramePhaseLength);
	virtual TInt SetSampleLength(TInt aFramePhase, TInt aSampleLength);
	virtual TInt GetSampleLength(TInt aFramePhase, TInt& aSampleLength);
	virtual TInt SetDelayCycles(TInt aFramePhase, TInt aDelayCycles);
	virtual TInt GetDelayCycles(TInt aFramePhase, TInt& aDelayCycles);
	virtual TInt ReadReceiveRegister(TInt aFramePhase, TInt& aData);
	virtual TInt WriteTransmitRegister(TInt aFramePhase, TInt aData);
	virtual TInt ReadTransmitRegister(TInt aFramePhase, TInt& aData);
	virtual TInt ReadRegisterModeStatus(TInt aFramePhase, TInt& aFlags);
	virtual TInt EnableRegisterInterrupts(TInt aFramePhase, TInt aInterrupt);
	virtual TInt DisableRegisterInterrupts(TInt aFramePhase, TInt aInterrupt);
	virtual TInt IsRegisterInterruptEnabled(TInt aFramePhase, TInt& aEnabled);
	virtual TInt EnableFIFO(TInt aFramePhase, TInt aFifoMask);
	virtual TInt DisableFIFO(TInt aFramePhase, TInt aFifoMask);
	virtual TInt IsFIFOEnabled(TInt aFramePhase, TInt& aEnabled);
	virtual TInt SetFIFOThreshold(TInt aFramePhase, TInt aDirection, TInt aThreshold);
	virtual TInt ReadFIFOModeStatus(TInt aFramePhase, TInt& aFlags);
	virtual TInt EnableFIFOInterrupts(TInt aFramePhase, TInt aInterrupt);
	virtual TInt DisableFIFOInterrupts(TInt aFramePhase, TInt aInterrupt);
	virtual TInt IsFIFOInterruptEnabled(TInt aFramePhase, TInt& aEnabled);
	virtual TInt ReadFIFOLevel(TInt aFramePhase, TInt aDirection, TInt& aLevel);
	virtual TInt EnableDMA(TInt aFifoMask);
	virtual TInt DisableDMA(TInt aFifoMask);
	virtual TInt IsDMAEnabled(TInt& aEnabled);
	virtual TInt Start(TInt aDirection);
	virtual TInt Stop(TInt aDirection);
	virtual TInt IsStarted(TInt aDirection, TBool& aStarted);		

private: 
	TBool iConfigured;
	TInt iLastPhaseInWriteFifo;
	TInt iLastPhaseInReadFifo;
	};

// this static method, creates the DI2sChannelBase corresponding
// to the interfaceId passed and return the channel
// NB: if each channel was implemented independently (e.g. on a separate file)
// this function would have to be provided spearately and know how to map
// the interface ID to the appropriate channel object to be created
// (e.g. each channel would have a different implementation D2sChannelXXX
// and this function call the appropriate constructor for each interface Id)

TInt DI2sChannelBase::CreateChannels(DI2sChannelBase*& aChannel, TInt aInterfaceId)
	{
	DI2sChannelBase* chan = new D2sChannelNE1_TB(aInterfaceId);
	if (!chan)
		{
		return KErrNoMemory;
		}

	aChannel = chan;
	
	return KErrNone;
	}


// Default constructor.
D2sChannelNE1_TB::D2sChannelNE1_TB(TInt aInterfaceId):
	iConfigured(EFalse), 
	iLastPhaseInWriteFifo(I2s::ERight), // the first phase in write fifo should be left, so initialize to right 
	iLastPhaseInReadFifo(I2s::ERight) // the first phase in read fifo should be left, so initialize to right
	{
	iInterfaceId=aInterfaceId;
	}

TInt D2sChannelNE1_TB::ConfigureInterface(TDes8 *aConfig)
	{
	__KTRACE_OPT(KDLL, Kern::Printf("DI2sChannelNE1_TB::ConfigureInterfaceInterface (Id: %d)", iInterfaceId));

	if(AsspIsBitSet(KHwI2SControl(iInterfaceId), KHtI2SCtrl_TEN | KHtI2SCtrl_REN))
		{
		return KErrInUse;
		}
	
	TI2sConfigBufV01 &conf = ((TI2sConfigBufV01&)*aConfig);

	// this interface doesn's support EController mode
	if (conf().iType == I2s::EController)
		{
		return KErrNotSupported;
		}
	
	if(conf().iRole == I2s::EMaster)
		{
		AsspRegister::Modify32(KHwI2SControl(iInterfaceId), 0, KHtI2SCtrl_MSMODE);
		}
		
	// copy configuration.. it will be used in Start/Stop
	iConfig = conf();		// ok, thread context only and one client thread per channel
	
	 // select I2S format
	AsspSetBits(KHwI2SControl(iInterfaceId), 4, KHsI2SCtrl_FORMAT, 3);

	iConfigured = ETrue;	// this API can only be called in thread context and we assume that a channel is for use of a single thread
	
	return KErrNone;
	}

TInt D2sChannelNE1_TB::GetInterfaceConfiguration(TDes8 &aConfig)
	{
	if (!iConfigured)	// this API can only be called in thread context and we assume that a channel is for use of a single thread
		{
		aConfig.SetLength(0); //no configuration  present yet..
		}
	else
		{
		TPckgBuf<TI2sConfigV01> conf(iConfig);
		aConfig.Copy(conf);
		}
	return KErrNone;
	}

TInt D2sChannelNE1_TB::SetSamplingRate(TInt aSamplingRate)
	{
	if (iConfig.iRole == I2s::ESlave)
		{
		return KErrNotSupported; 
		}
	
	if (AsspIsBitSet(KHwI2SControl(iInterfaceId), KHtI2SCtrl_TEN | KHtI2SCtrl_REN))
		{
		return KErrInUse;
		}
	
	TUint32 val = 0, div = 0;
	
	switch(aSamplingRate)
		{
		case I2s::E8KHz: 		// 0000: 8 kHz
			div = 1; 			// MCLK = 24.5760 MHz(1*)
			break;

		case I2s::E11_025KHz:	// 1000: 11.025 kHz
			val = 8;
			div = 4; 			// MCLK = 33.8688 MHz(4*) or 22.5792(5*) or 16.9344 MHz(6)
			break;

		case I2s::E12KHz:		// 0001: 12 kHz
			val = 1;
			div = 1; 			// MCLK = 24.5760 MHz(1*) or 18.432MHz(2)
			break;

		case I2s::E16KHz:		// 0010: 16 kHz
			val = 2;
			div = 1; 			// MCLK = 24.5760 MHz(1*)
			break;

		case I2s::E22_05KHz:	// 1001: 22.05 kHz
			val = 9;
			div = 4; 			// MCLK = 33.8688 MHz(4*) or 22.5792(5*) or 16.9344 MHz(6)  
			break;
			
		case I2s::E24KHz:		// 0011: 24 kHz
			val = 3;
			div = 1; 			// MCLK = 24.5760 MHz(1*) or 18.432MHz(2)
			break;
			
		case I2s::E32KHz:		// 	0100: 32 kHz
			val = 4;
			div = 1; 			// MCLK = 24.5760 MHz
			break;
			
		case I2s::E44_1KHz:		// 1010: 44.1 kHz
			val = 10;
			div = 4;			// MCLK = 33.8688 MHz(4*) or 22.5792(5*) or 16.9344 MHz(6)
			break;
			
		case I2s::E48KHz:		// 0101: 48 kHz
			val = 5;
			div = 2; 			// MCLK = 24.5760 MHz(1*) or 18.432MHz(2)
			break;

		default:
			return KErrNotSupported;
		}

	TInt irq=__SPIN_LOCK_IRQSAVE(I2sLock);		// seems that we must guarantee the following sequence is uninterrupted...
	// before changing FSCLKSEL and/or FSMODE - mask I2S bit
	// MSK_I2Sx (x=0:3): bits 18:21 in MaskCtrl Register of the System Ctrl Unit 
	AsspRegister::Modify32(KHwBaseSCU + KHoSCUClockMaskCtrl, 0, (1<<iInterfaceId)<<18);
	
	// change the divide I2SCLK ctrl value for this channel..
	AsspRegister::Modify32(KHwSystemCtrlBase+KHoSCUDivideI2SCLKCtrl, 0xf<<(iInterfaceId<<3), div<<(iInterfaceId<<3));  

	// update the KHwI2SControl register
	AsspSetBits(KHwI2SControl(iInterfaceId), val, KHsI2SCtrl_FCKLKSEL, 4); 

	// after changing FSCLKSEL and FSMODE - clear MSK_I2Sx mask bit
	AsspRegister::Modify32(KHwBaseSCU + KHoSCUClockMaskCtrl, (1<<iInterfaceId)<<18, 0);
	__SPIN_UNLOCK_IRQRESTORE(I2sLock,irq);

	return KErrNone;
	}

TInt D2sChannelNE1_TB::GetSamplingRate(TInt& aSamplingRate)
	{
	if (iConfig.iRole == I2s::ESlave)
		{
		return KErrNotSupported; 
		}
	
	TUint32 val = (AsspRegister::Read32(KHwI2SControl(iInterfaceId))>>KHsI2SCtrl_FCKLKSEL) & 0xf;
	
	switch(val)
		{
		case 0:
			aSamplingRate = I2s::E8KHz;		// 0000: 8 kHz
			break;

		case 8: 
			aSamplingRate = I2s::E11_025KHz;	// 1000: 11.025 kHz
			break;

		case 1:
			aSamplingRate = I2s::E12KHz;		// 0001: 12 kHz
			break;

		case 2:
			aSamplingRate = I2s::E16KHz;		// 0010: 16 kHz
			break;

		case 9:
			aSamplingRate = I2s::E22_05KHz;		// 1001: 22.05 kHz
			break;
			
		case 3:
			aSamplingRate = I2s::E24KHz;		// 0011: 24 kHz
			break;
			
		case 4:
			aSamplingRate = I2s::E32KHz;		// 	0100: 32 kHz
			break;
			
		case 10:
			aSamplingRate = I2s::E44_1KHz;		// 1010: 44.1 kHz
			break;
			
		case 5:
			aSamplingRate = I2s::E48KHz;		// 0101: 48 kHz
			break;
		}
	
	return KErrNone;
	}

TInt D2sChannelNE1_TB::SetFrameLengthAndFormat(TInt aFrameLength, TInt /*aLeftFramePhaseLength*/)
	{
	if (AsspIsBitSet(KHwI2SControl(iInterfaceId), KHtI2SCtrl_TEN | KHtI2SCtrl_REN))
		{
		return KErrInUse;
		}
	
	TUint32 val=0;
	
	switch(aFrameLength)
		{
		case I2s::EFrame32Bit:
			val = 0;
			break;
		
		case I2s::EFrame48Bit: 
			val = 1;
			break;
		
		case I2s::EFrame64Bit:
			val = 2;
			break;

		case I2s::EFrame128Bit:
			val = 3;
			break;

		default:
			return KErrNotSupported;
		};
	
	TInt irq=__SPIN_LOCK_IRQSAVE(I2sLock);		// seems that we must guarantee the following sequence is uninterrupted
	// before changing FSCLKSEL and/or FSMODE - mask I2S bit
	// MSK_I2Sx (x=0:3): bits 18:21 in MaskCtrl Register of the System Ctrl Unit 
	AsspRegister::Modify32(KHwBaseSCU + KHoSCUClockMaskCtrl, 0, (1<<iInterfaceId)<<18);
	
	// update the register (3 bits at KHsI2SCtrl_FSMODE)
	AsspSetBits(KHwI2SControl(iInterfaceId), val, KHsI2SCtrl_FSMODE, 3);

	// after changing FSCLKSEL and FSMODE - clear MSK_I2Sx mask bit
	AsspRegister::Modify32(KHwBaseSCU + KHoSCUClockMaskCtrl, (1<<iInterfaceId)<<18, 0);
	__SPIN_UNLOCK_IRQRESTORE(I2sLock,irq);
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::GetFrameFormat(TInt& aLeftFramePhaseLength, TInt& aRightFramePhaseLength)
	{
	TInt frameLength=0;
	switch(AsspGetBits(KHwI2SControl(iInterfaceId), KHsI2SCtrl_FSMODE, 3))
		{
		case 0:
			frameLength = I2s::EFrame32Bit;
			break;
		
		case 1:
			frameLength = I2s::EFrame48Bit;
			break;
		
		case 2: 
			frameLength = I2s::EFrame64Bit;
			break;

		case 3:
			frameLength = I2s::EFrame128Bit;
			break;

		default:
			return KErrGeneral; //unexpected value??
		};

	aLeftFramePhaseLength=aRightFramePhaseLength=frameLength/2;	// on NaviEngine frames are symmetrical

	return KErrNone; 
	}

TInt D2sChannelNE1_TB::SetSampleLength(TInt /*aFramePhase*/, TInt aSampleLength)
	{
	if (AsspIsBitSet(KHwI2SControl(iInterfaceId), KHtI2SCtrl_TEN | KHtI2SCtrl_REN))
		{
		return KErrInUse;
		}

	TUint32 val=0;
	
	switch(aSampleLength)
		{
		case I2s::ESample8Bit:
			val = 0x8;
			break;
			
		case I2s::ESample16Bit:
			val = 0x10;
			break;
		
		case I2s::ESample24Bit:
			val = 0x18;
			break;

		case I2s::ESample12Bit:
		case I2s::ESample32Bit:
			return KErrNotSupported;
		}
	
	// update the register.. 
	AsspSetBits(KHwI2SControl(iInterfaceId), val, KHsI2SCtrl_DLENGTH,5); // [4:0] sampling data length);
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::GetSampleLength(TInt /*aFramePhase*/, TInt& aSampleLength)
	{
	// sample length can't be configured separately for left/right channels..
	// .. in this chip, so aFramePhase is ignored
	
	TUint32 val=AsspRegister::Read32(KHwI2SControl(iInterfaceId)) & 0x1F;
	
	switch(val)
		{
		case 0x8:
			aSampleLength = I2s::ESample8Bit;
			break;
			
		case 0x10: 
			aSampleLength = I2s::ESample16Bit;
			break;
		
		case 0x18: 
			aSampleLength = I2s::ESample24Bit;
			break;
		}
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::SetDelayCycles(TInt aFramePhase, TInt aDelayCycles)
	{
	return KErrNotSupported; 
	}

TInt D2sChannelNE1_TB::GetDelayCycles(TInt aFramePhase, TInt& aDelayCycles)
	{
	return KErrNotSupported; 
	}

TInt D2sChannelNE1_TB::ReadReceiveRegister(TInt aFramePhase, TInt& aData)
	{
	// should check here, if sample length was configured, 
	// but once the interface is properly configured, this check would add unnecessary overhead.
	
	// since there is only one fifo for both channels in this chip, 
	// we need to read Left and then Right channel data in sequence. 

	TInt oldFP=__e32_atomic_swp_ord32(&iLastPhaseInReadFifo,aFramePhase);	// atomic as this may be used in ISR too
	if(aFramePhase == oldFP)
		{
		return KErrNotSupported;
		}

	// and get the current data from the fifo register
	aData = AsspRegister::Read32(KHwI2SDataIn(iInterfaceId));
	return KErrNone; 	
	}

TInt D2sChannelNE1_TB::WriteTransmitRegister(TInt aFramePhase, TInt aData)
	{
	// should check here, if sample length was configured, 
	// but once the interface is properly configured, this check would add unnecessary overhead.
	
	// since there is only one fifo for both channels in this chip, 
	// we need to write Left and then right channel data in sequence. 

	TInt oldFP=__e32_atomic_swp_ord32(&iLastPhaseInWriteFifo,aFramePhase);	// atomic as this may be used in ISR too
	if(aFramePhase == oldFP)
		{
		return KErrNotSupported;
		}

	// and update the fifo register
	AsspRegister::Write32(KHwI2SDataOut(iInterfaceId), aData);

	return KErrNone; 
	}

TInt D2sChannelNE1_TB::ReadTransmitRegister(TInt aFramePhase, TInt& aData)
	{
	// since there is only one fifo for both channels in this chip, 
	// we can only read the data for the last written phase (either left or right) 

	TInt curFp=__e32_atomic_load_acq32(&iLastPhaseInWriteFifo);
	if(aFramePhase != curFp)
		{
		return KErrArgument;
		}
	
	// read the register
	aData = AsspRegister::Read32(KHwI2SDataOut(iInterfaceId));
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::ReadRegisterModeStatus(TInt aFramePhase, TInt& aFlags)
	{
	return KErrNotSupported; // register PIO mode not supported (FIFO always present)
	}

TInt D2sChannelNE1_TB::EnableRegisterInterrupts(TInt aFramePhase, TInt aInterrupt)
	{
	return KErrNotSupported; // register PIO mode not supported (FIFO always present)
	}

TInt D2sChannelNE1_TB::DisableRegisterInterrupts(TInt aFramePhase, TInt aInterrupt)
	{
	return KErrNotSupported; // register PIO mode not supported (FIFO always present)
	}

TInt D2sChannelNE1_TB::IsRegisterInterruptEnabled(TInt aFramePhase, TInt& aEnabled)
	{
	return KErrNotSupported; // register PIO mode not supported (FIFO always present)
	}

TInt D2sChannelNE1_TB::EnableFIFO(TInt /*aFramePhase*/, TInt aFifoMask)
	{
	TInt val=0;
	
	// Set and clear fifo init bits for transmit/receive
	// tere are only two FIFOs - not separated to phases..
	if(aFifoMask & I2s::ETx) 
		val = KHtI2SFifoCtrl_TFIFOCLR;

	if(aFifoMask & I2s::ERx) 
		val |= KHtI2SFifoCtrl_RFIFOCLR;
			
	AsspRegister::Modify32(KHwI2SFifoControl(iInterfaceId), 0,  val);
	AsspRegister::Modify32(KHwI2SFifoControl(iInterfaceId), val, 0);

	return KErrNone; 
	}

TInt D2sChannelNE1_TB::DisableFIFO(TInt aFramePhase, TInt aFifoMask)
	{
	// fifo is always enabled in this chip.. 
	return KErrNotSupported; 
	}

TInt D2sChannelNE1_TB::IsFIFOEnabled(TInt /*aFramePhase*/, TInt& aEnabled)
	{
	// fifo is always enabled in this chip.. 
	aEnabled = I2s::ERx|I2s::ETx;
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::SetFIFOThreshold(TInt aFramePhase, TInt aDirection, TInt aThreshold)
	{
	// supported threshold values for this chip: 
	// 011b - 16-word space available
	// 010b - 8-word space available
	// 001b - 4-word space available
	// 000b - 2-word space available
	if (aThreshold < 0)
		{
		return KErrNotSupported;
		}

	// Determine, what value was specified, and adjust it down to one  
	// of the possible values
	TInt i=15;
	while(i>1)
		{
		if ( aThreshold!=(aThreshold & i))
			break;
		i>>=1;
		}
	aThreshold = i+1; //this will now contain one of possible values (2-16);
	
	// now any of 16/8/4/2, shifted right until == 0 will give us requested register_value+2
	// (instead of using 'switch(aThreshold)') 
	i=0;
	while(aThreshold)
		{
		aThreshold>>=1;
		++i;
		}
	aThreshold = i-2 >= 0 ? i-2 : 0; //if i-2 gives <0 (e.g.for aThreshold<4) -adjust to 0;
	
	if (AsspIsBitSet(KHwI2SControl(iInterfaceId), KHtI2SCtrl_TEN | KHtI2SCtrl_REN))
		{
		return KErrInUse;
		}

	TUint32 clr, set;
	if(aDirection & I2s::ERx)  // receive fifo..
		{
		if (aFramePhase == I2s::ELeft)   	
			{
			clr = CLEARMASK(KHsI2SFifoCtrl_RFIFOLT, 3);
			set = (aThreshold << KHsI2SFifoCtrl_RFIFOLT);
			}

		else //if (aFramePhase == I2s::ERight)
			{
			clr = CLEARMASK(KHsI2SFifoCtrl_RFIFORT, 3);
			set = (aThreshold << KHsI2SFifoCtrl_RFIFORT);
			}
		}
	else 	// transmit fifo..
		{
		if (aFramePhase == I2s::ELeft)  
			{
			clr = CLEARMASK(KHsI2SFifoCtrl_TFIFOLT, 3);
			set = (aThreshold << KHsI2SFifoCtrl_TFIFOLT);

			}
		else // if (aFramePhase == I2s::ERight)
			{
			clr = CLEARMASK(KHsI2SFifoCtrl_TFIFORT, 3);
			set = (aThreshold << KHsI2SFifoCtrl_TFIFORT);
			}
		}
	// updating register with value will also clear FIFO initialization bits.. 
	AsspRegister::Modify32(KHwI2SFifoControl(iInterfaceId), clr, set);

	return KErrNone; 
	}

TInt D2sChannelNE1_TB::ReadFIFOModeStatus(TInt aFramePhase, TInt& aFlags)
	{
	aFlags = 0;

	TInt irq=__SPIN_LOCK_IRQSAVE(I2sLock);		// (read and clear), can be used in ISR
	TUint32 flags = AsspRegister::Read32(KHwI2SInterruptFlag(iInterfaceId));
	AsspRegister::Write32(KHwI2SInterruptFlag(iInterfaceId), flags);	// clear the status flags (after reading)
	__SPIN_UNLOCK_IRQRESTORE(I2sLock,irq);
	
	if (aFramePhase == I2s::ELeft)
		{
		if(flags & KHtI2SIntFlg_TFLURINT)	// L-ch transmit FIFO underrun
			aFlags |= I2s::ETxUnderrun;

		if(flags & KHtI2SIntFlg_TFLEINT)	// L-ch transmit FIFO reached the empty trigger level
			aFlags |= I2s::ETxEmpty;

		if(flags & KHtI2SIntFlg_RFLORINT)	// L-ch receive FIFO overrun
			aFlags |= I2s::ERxOverrun;
		
		if(flags & KHtI2SIntFlg_RFLFINT)	// L-ch receive FIFO reached the full trigger level
			aFlags |= I2s::ERxFull;
		}
	else
		{
		if(flags & KHtI2SIntFlg_TFRURINT)	// R-ch transmit FIFO underrun
			aFlags |= I2s::ETxUnderrun;

		if(flags & KHtI2SIntFlg_TFREINT)	// R-ch transmit FIFO reached the empty trigger level
			aFlags |= I2s::ETxEmpty;

		if(flags & KHtI2SIntFlg_RFRORINT)	// R-ch receive FIFO overrun
			aFlags |= I2s::ERxOverrun;
		
		if(flags & KHtI2SIntFlg_RFRFINT)	// R-ch receive FIFO reached the full trigger level
			aFlags |= I2s::ERxFull;
		}
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::EnableFIFOInterrupts(TInt aFramePhase, TInt aInterrupt)
	{
	TUint32 val=0;
	
	if(aInterrupt & I2s::ERxFull)
		{
		if (aFramePhase == I2s::ELeft)
			val |= KHtI2SIntMask_RFLFINT;

		if (aFramePhase == I2s::ERight)
			val |= KHtI2SIntMask_RFRFINT;
		}

	if(aInterrupt & I2s::ETxEmpty)
		{
		if (aFramePhase == I2s::ELeft)
			val |= KHtI2SIntMask_TFLEINT;

		if (aFramePhase == I2s::ERight)
			val |= KHtI2SIntMask_TFREINT;
		}

	if(aInterrupt & I2s::ERxOverrun)
		{
		if (aFramePhase == I2s::ELeft)
			val |= KHtI2SIntMask_RFLORINT;

		if (aFramePhase == I2s::ERight)
			val |= KHtI2SIntMask_RFRORINT;
		}

	if(aInterrupt & I2s::ETxUnderrun)
		{
		if (aFramePhase == I2s::ELeft)
			val |= KHtI2SIntMask_TFLURINT;

		if (aFramePhase == I2s::ERight)
			val |= KHtI2SIntMask_TFRURINT;
		}
	
	if(aInterrupt & I2s::EFramingError)
		{
		// not supported, do nothing
		}

	if (val)
		{
		// update the register
		AsspRegister::Write32(KHwI2SInterruptMask(iInterfaceId), val);
		}
	
	return KErrNone;
	}

TInt D2sChannelNE1_TB::DisableFIFOInterrupts(TInt aFramePhase, TInt aInterrupt)
	{
	TUint32 val = KHmI2SIntMask_ALL;
	
	if(aInterrupt & I2s::ERxFull)
		{
		if (aFramePhase == I2s::ELeft)
			val &= ~KHtI2SIntMask_RFLFINT;

		if (aFramePhase == I2s::ERight)
			val &= ~KHtI2SIntMask_RFRFINT;
		}

	if(aInterrupt & I2s::ETxEmpty)
		{
		if (aFramePhase == I2s::ELeft)
			val &= ~KHtI2SIntMask_TFLEINT;

		if (aFramePhase == I2s::ERight)
			val &= ~KHtI2SIntMask_TFREINT;
		}

	if(aInterrupt & I2s::ERxOverrun)
		{
		if (aFramePhase == I2s::ELeft)
			val &= ~KHtI2SIntMask_RFLORINT;

		if (aFramePhase == I2s::ERight)
			val &= ~KHtI2SIntMask_RFRORINT;
		}

	if(aInterrupt & I2s::ETxUnderrun)
		{
		if (aFramePhase == I2s::ELeft)
			val &= ~KHtI2SIntMask_TFLURINT;

		if (aFramePhase == I2s::ERight)
			val &= ~KHtI2SIntMask_TFRURINT;
		}
	
	if(aInterrupt & I2s::EFramingError)
		{
		// not supported, do nothing
		}

	TInt irq=__SPIN_LOCK_IRQSAVE(I2sLock);		// (read and clear), not used from ISR but made safe nevertheless...
	TUint32 oldVal = AsspRegister::Read32(KHwI2SInterruptMask(iInterfaceId));
	if (val!=oldVal)
		{
		// update the register
		AsspRegister::Write32(KHwI2SInterruptMask(iInterfaceId), val);
		}
	__SPIN_UNLOCK_IRQRESTORE(I2sLock,irq);
	
	return KErrNone;
	}

TInt D2sChannelNE1_TB::IsFIFOInterruptEnabled(TInt aFramePhase, TInt& aEnabled)
	{
	// check, if any interrupt is enabled.. 
	TUint32 val = AsspRegister::Read32(KHwI2SInterruptMask(iInterfaceId));

	aEnabled=0;
	if(aFramePhase== I2s::ELeft)
		{
		if(val & KHtI2SIntMask_TFLURINT)// L-ch transmit FIFO underrun int enable
			aEnabled |= I2s::ETxUnderrun;
		if(val & KHtI2SIntMask_TFLEINT)// L-ch transmit FIFO reached the empty trigger level int enable
			aEnabled |= I2s::ETxEmpty;
		if(val & KHtI2SIntMask_RFLORINT)// L-ch receive FIFO overrun int enable
			aEnabled |= I2s::ERxOverrun;
		if(val & KHtI2SIntMask_RFLFINT)// L-ch receive FIFO reached the full trigger level int enable
			aEnabled |= I2s::ERxFull;
		}
	else
		{
		if(val & KHtI2SIntMask_TFRURINT)// R-ch transmit FIFO underrun int enable
			aEnabled |= I2s::ETxUnderrun;
		if(val & KHtI2SIntMask_TFREINT)// R-ch transmit FIFO reached the empty trigger level int enable
			aEnabled |= I2s::ETxEmpty;
		if(val & KHtI2SIntMask_RFRORINT)// R-ch receive FIFO overrun int enable
			aEnabled |= I2s::ERxOverrun;
		if(val & KHtI2SIntMask_RFRFINT)// R-ch receive FIFO reached the full trigger level int enable
			aEnabled |= I2s::ERxFull;
		}
		
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::ReadFIFOLevel(TInt aFramePhase, TInt aDirection, TInt& aLevel)
	{
	// This device only allows us to see, if the receive FIFO or transmit FIFO is either 
	// empty or full, so return: 
	// TX FIFO: 1-FULL,  0-EMPTY, 
	// RX FIFO: 1-EMPTY, 0-FULL
	
	TUint32 val = AsspRegister::Read32(KHwI2SFifoStatus(iInterfaceId));

	if(aDirection & I2s::ETx)
		{
		if (aFramePhase == I2s::ELeft)
			{
			aLevel = (val & KHtI2SFifoSts_TFL_FULL) >> 17;  // L-ch transmit FIFO full (bit 17)
			}
		else // I2s::ERight
			{
			aLevel = (val & KHtI2SFifoSts_TFR_FULL) >> 16;  // R-ch transmit FIFO full (bit 16)
			}
		}
	else  // I2s::ERx
		{
		if (aFramePhase == I2s::ELeft)
			{
			aLevel = (val & KHtI2SFifoSts_RFL_EMPTY) >> 1;  // L-ch receive FIFO empty (bit 1)
			}
		else // I2s::ERight
			{
			aLevel = val & KHtI2SFifoSts_RFR_EMPTY;  // R-ch receive FIFO empty (bit 0)
			}
		}
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::EnableDMA(TInt aFifoMask)
	{
	TUint32 val=0;

	if(aFifoMask & I2s::ETx)
		val |= KHtI2SFifoCtrl_TDMAEN;

	if(aFifoMask & I2s::ERx)
		val |= KHtI2SFifoCtrl_RDMAEN;
	
	AsspRegister::Modify32(KHwI2SFifoControl(iInterfaceId), 0, val);

	return KErrNone; 
	}

TInt D2sChannelNE1_TB::DisableDMA(TInt aFifoMask)
	{
	TUint32 val=0;

	if(aFifoMask & I2s::ETx)
		val |= KHtI2SFifoCtrl_TDMAEN;

	if(aFifoMask & I2s::ERx)
		val |= KHtI2SFifoCtrl_RDMAEN;
	
	AsspRegister::Modify32(KHwI2SFifoControl(iInterfaceId), val, 0);

	return KErrNone; 
	}

TInt D2sChannelNE1_TB::IsDMAEnabled(TInt& aEnabled)
	{ 
	TUint32 val = AsspRegister::Read32(KHwI2SFifoControl(iInterfaceId));
	aEnabled = 0;
	
	if(val & KHtI2SFifoCtrl_TDMAEN)
		aEnabled |= I2s::ETx;

	if(val & KHtI2SFifoCtrl_RDMAEN)
		aEnabled |= I2s::ERx;
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::Start(TInt aDirection)
	{
	TUint32 val=0;
	
	if(aDirection & I2s::ERx)  
		{
		// check, if the interface was configured for reception.. 
		if (iConfig.iType != I2s::EReceiver && iConfig.iType != I2s::EBidirectional)
			{
			return KErrNotSupported;
			}

		__KTRACE_OPT(KDLL, Kern::Printf("I2S channel %d: Start Rx", iInterfaceId));
		val |= KHtI2SCtrl_REN;	
		}
	else
		{
		// check, if the interface was configured for transmission.. 
		if (iConfig.iType != I2s::ETransmitter && iConfig.iType != I2s::EBidirectional)
			{
			return KErrNotSupported;
			}
		
		__KTRACE_OPT(KDLL, Kern::Printf("I2S channel %d: Start Tx", iInterfaceId));
		val |= KHtI2SCtrl_TEN;
		}

	// update the register
	AsspRegister::Modify32(KHwI2SControl(iInterfaceId), 0, val);

	return KErrNone; 
	}

TInt D2sChannelNE1_TB::Stop(TInt aDirection)
	{
	TUint32 val=0;
	
	if(aDirection & I2s::ERx)  
		{
		__KTRACE_OPT(KDLL, Kern::Printf("I2S channel %d: Stop Rx", iInterfaceId));
		val = KHtI2SCtrl_REN;	
		}
	else
		{
		__KTRACE_OPT(KDLL, Kern::Printf("I2S channel %d: Stop Rx", iInterfaceId));
		val = KHtI2SCtrl_TEN;
		}

	// update the register
	AsspRegister::Modify32(KHwI2SControl(iInterfaceId), val, 0);
	
	return KErrNone; 
	}

TInt D2sChannelNE1_TB::IsStarted(TInt aDirection, TBool& aStarted)
	{
	TUint32 val=AsspRegister::Read32(KHwI2SControl(iInterfaceId));
	
	if(aDirection & I2s::ERx)
		{
		aStarted = (val & KHtI2SCtrl_REN) >> 24; // bit 24
		}
	else
		{
		aStarted = (val & KHtI2SCtrl_TEN) >> 25; // bit 25
		}
	return KErrNone; 
	}