navienginebsp/ne1_tb/specific/cs42l51.cpp
changeset 0 5de814552237
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/navienginebsp/ne1_tb/specific/cs42l51.cpp	Tue Sep 28 18:00:05 2010 +0100
@@ -0,0 +1,375 @@
+/*
+* 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:  
+* ne1_tb\specific\cs42l51.cpp
+*
+*/
+
+
+
+#include <kernel/kern_priv.h>
+#include <naviengine.h>
+#include <naviengine_priv.h>
+#include <gpio.h>
+#include "cs42l51.h"
+
+#define __THREAD_AND_CPU 	Kern::Printf("(Thread %T, CPU: %d)\n", NKern::CurrentThread(), NKern::CurrentCpu())
+//#define __KTRACE_SCODEC(s) s
+#define __KTRACE_SCODEC(s)  __KTRACE_OPT(KSOUND1, s)
+
+
+#if _DEBUG
+static const char KCodecPanicCat[] = "AUDIO CODEC, line:";
+#endif
+
+// other constants
+const TUint KCodecCSIChannel = 0;
+const TInt8 KBufGranularity = 8; // width of a transfer word in bits
+
+// CSI configuration parameters for the data transmission to the Codec.
+const TConfigSpiV01 KCodecSpiV01Config =
+	{
+	ESpiWordWidth_8, //iWordWidth
+	260000,          //iClkSpeed
+	ESpiPolarityHighFallingEdge, //iClkMode
+	40,            // iTimeoutPeriod
+	EBigEndian,    // iEndianness
+	EMsbFirst,     // iBitOrder
+	0,             // iTransactionWaitCycles
+	ESpiCSPinActiveLow //iCsPinActiveMode
+	};
+
+// static members
+RCS42AudioCodec* RCS42AudioCodec::iSelf = 0;
+TInt RCS42AudioCodec::iRefCnt = 0;
+
+// default constructor
+RCS42AudioCodec::RCS42AudioCodec() :
+	iHeaderBuff(KCodecSpiV01Config)
+	{
+	}
+
+TInt RCS42AudioCodec::Create()
+	{
+	__KTRACE_SCODEC(Kern::Printf("\n\nRCS42AudioCodec::Create()"));
+	if (!iSelf)
+		{
+		iSelf = new RCS42AudioCodec;
+		if(!iSelf)
+			{
+			return KErrNoMemory;
+			}
+		}
+	return KErrNone;
+	}
+
+// free resources when the driver is beeing closed.
+void RCS42AudioCodec::Destroy()
+	{
+	__KTRACE_SCODEC(Kern::Printf("RCS42AudioCodec::Destroy()"));
+	__ASSERT_DEBUG(iSelf, Kern::Fault(KCodecPanicCat, __LINE__));
+
+	delete iSelf;
+	iSelf = 0; // static member
+	}
+
+// this static method is called from DNE1_TBSoundScPddChannel::PowerUp()
+// to open an instance to the codec. If this is the first instance
+// beeing opened - the codec is initialized.
+TInt RCS42AudioCodec::Open(RCS42AudioCodec* &aSelf)
+	{
+	__KTRACE_SCODEC(Kern::Printf("RCS42AudioCodec::Open()"));
+
+	TInt r = KErrNone;
+	if(!iSelf)
+		{
+		r = Create();
+		if(r != KErrNone)
+			{
+			return r;
+			}
+		}
+
+	if (iRefCnt == 0)
+		{
+		r = iSelf->Init();
+		if (r != KErrNone)
+			{
+			iSelf->PowerDown();
+			return r;
+			}
+		}
+	// increment the reference counter
+	++iRefCnt;
+
+	// copy object address back to the client
+	aSelf = iSelf;
+
+	return KErrNone;
+	}
+
+// this static method is called from DNE1_TBSoundScPddChannel::PowerDown()
+// to close the reference and power down the codec if the last reference is beeing closed.
+void RCS42AudioCodec::Close(RCS42AudioCodec* &aSelf)
+	{
+	__KTRACE_SCODEC(Kern::Printf("RCS42AudioCodec::Close()"))	;
+
+	if (!aSelf || aSelf != iSelf)
+		{
+		return;
+		}
+
+	// decrement the reference counter
+	--iRefCnt;
+
+	// if closing the last instance - power down the codec
+	if (iRefCnt == 0)
+		{
+		iSelf->PowerDown();
+		aSelf = 0;
+		Destroy();
+		}
+	}
+
+// this method powers down the codec
+void RCS42AudioCodec::PowerDown()
+	{
+	if (iSelf)
+		{
+		iSelf->StartWrite();
+		iSelf->Write(KHwCS42L51DACOutputControl,
+				KHtCS42L51DACOutputControl_DACAB_MUTE);
+
+		// - set PDN bit
+		iSelf->Write(KHwCS42L51PwrCtrl, KHtCS42L51PwrCtrl_PDN);
+
+#ifdef _DEBUG
+		TInt r = iSelf->StopWrite();
+		__ASSERT_DEBUG(r == KErrNone, Kern::Printf("Coulnd't power down the CODEC r=%d ", r));
+		__ASSERT_DEBUG(r == KErrNone, Kern::Fault(KCodecPanicCat, __LINE__));
+#else
+		iSelf->StopWrite();
+#endif
+
+		// put !reset back to low..
+		GPIO::SetOutputState(KCodecResetPin, GPIO::ELow);
+		}
+	}
+
+
+// this is an internal method - to synchronously write the data to the bus. It sets up the transfer
+// and waits for Interrupt - which puts back the CS (Chip Select) pin - to low after the
+// data was sent out of the bus.
+TInt RCS42AudioCodec::DoWrite(TUint16 aRegAddr, TUint16 aData)
+	{
+	__KTRACE_SCODEC(Kern::Printf("RCS42AudioCodec::DoWrite()"));
+	TInt r = KErrNone;
+
+	iTransBuff().iRegister = aRegAddr;
+	iTransBuff().iData = aData;
+
+	// create a transfer object..
+	TIicBusTransfer transfer(TIicBusTransfer::EMasterWrite, KBufGranularity, &iTransBuff);
+
+	// Create transaction
+	TIicBusTransaction transaction(&iHeaderBuff, &transfer);
+
+	// synchronously queue the write transaction
+	r = IicBus::QueueTransaction(iCsiBusConfig, &transaction);
+	return r;
+	}
+
+// to avoid multiple checking for each Write() call - if they are called in a row,
+// precede each block of writes to be checked with StartWrite() which clears the iResult.
+void RCS42AudioCodec::StartWrite()
+	{
+#if _DEBUG
+	iStartWriteCalled = ETrue;
+#endif
+	iResult = KErrNone;
+	}
+
+// After each block calls to Write() check the global status of them by calling this method.
+TInt RCS42AudioCodec::StopWrite()
+	{
+#if _DEBUG
+	iStartWriteCalled = EFalse;
+#endif
+	return iResult;
+	}
+
+// this is an internal method - used to configure the codec. It can be called
+// multiple times - whithout checkin for results. Precondition is - to call StartWrite()
+// and the overall result of multiple calls to this method are gathered using StopWrite()
+void RCS42AudioCodec::Write(TUint16 aRegAddr, TUint16 aData)
+	{
+	__ASSERT_DEBUG(iStartWriteCalled, Kern::Printf("block of multiple Write() calls should be preceded with StartWrite()"));
+	__ASSERT_DEBUG(iStartWriteCalled, Kern::Fault(KCodecPanicCat, __LINE__));
+
+	if (iResult != KErrNone)
+		{
+		return; // there was an error during one of previous write calls, just return
+		}
+	// if all calls proceeding StartWrite() were successful, continue to call the proper write
+	iResult = DoWrite(aRegAddr, aData);
+	}
+
+// This method is used to configure the CSI interface and then - the Codec.
+TInt RCS42AudioCodec::Init()
+	{
+	__KTRACE_SCODEC(Kern::Printf("RCS42AudioCodec::Init()"));
+	TUint32 val;
+
+	// First byte for the Cocec's write request on the CSI bus: (first seven bits-address, 8th-Write idication)
+	iTransBuff().iAddress = KCodecWriteCommand;
+
+	// Enter the BusRealisation config specific to CSI
+	SET_BUS_TYPE(iCsiBusConfig,DIicBusChannel::ESpi); //Bus Type
+	SET_CHAN_NUM(iCsiBusConfig,(TUint8)KCodecCSIChannel); // SPI uses channel numbers 1,2 (CSI0 and CSI1)
+	SET_SLAVE_ADDR(iCsiBusConfig,KCodecCSPin); //  Codec Pin Number
+
+	// enable and configure pin 25 - it is connected to the chip's reset line
+	GPIO::SetPinMode(KCodecResetPin, GPIO::EEnabled);
+	GPIO::SetPinDirection(KCodecResetPin, GPIO::EOutput);
+	GPIO::SetDebounceTime(KCodecResetPin, 0);
+
+	//====================================
+	//configure the CODEC:
+	// put !reset line high to start the power-up sequence..
+	GPIO::SetOutputState(KCodecResetPin, GPIO::EHigh);
+
+	// start multiple Write() block here
+	StartWrite();
+
+	// Write are used here..
+	// power-up sequence..
+	Write(KHwCS42L51PwrCtrl, KHtCS42L51PwrCtrl_PDN);
+	Write(KHwCS42L51PwrCtrl, KHtCS42L51PwrCtrl_PDN_ALL);
+
+	// freeze all registers.. until are set-up..
+	Write(KHwCS42L51DACControl, KHtCS42L51DACControl_FREEZE);
+
+	// Mic power control and speed control (0x03)
+	Write(KHwCS42L51MicPwrSpeed, KHtCS42L51MicPwrSpeed_AUTO
+			| KHtCS42L51MicPwrSpeed_MCLKDIV2); // auto,
+
+	// interface control (0x04) // serial port settings..
+	// I2s, Slave, SDOUT->SDIN internally connected.. Digimix->ON?
+
+	// use I2S format, Slave
+	iInterfaceCtrlVal = KHCS42L51CtrlI2sUpto24bit << KHsCS42L51CtrlFormat;
+
+	// Digital & Mic mix
+	iInterfaceCtrlVal |= KHtCS42L51Ctrl_DIGMIX | KHtCS42L51Ctrl_MICMIX;
+	Write(KHwCS42L51Ctrl, iInterfaceCtrlVal);
+
+	// MIC Control - enable mic pre-amplifierboost (there is also ADC digital boost)
+	// which we can use in addition to this one - but it would- work fine whitout any.
+	val = KHtCS42L51MicCtrl_MICA_BOOST;
+	Write(KHwCS42L51MicCtrl, val);
+
+	// ADCx Input Select, Invert & Mute
+	/*
+	 PDN_PGAx AINx_MUX[1:0] Selected Path to ADC
+	 0 	  00 		AIN1x-->PGAx
+	 0	  01 		AIN2x-->PGAx
+	 0 	  10 		AIN3x/MICINx-->PGAx
+	 0 	  11 		AIN3x/MICINx-->Pre-Amp(+16/+32 dB Gain)-->PGAx
+	 1 	  00 		AIN1x
+	 1 	  01 		AIN2x
+	 1 	  10 		AIN3x/MICINx
+	 1 	  11 		Reserved */
+
+	val = 3 << KHsCS42L51ADCInputMute_AINA_MUX;
+	val |= 3 << KHsCS42L51ADCInputMute_AINB_MUX;
+	Write(KHwCS42L51ADCInputMute, val);
+
+	// DAC output select (0x08)
+	// HP_GAIN2 HP_GAIN1 HP_GAIN0 DAC_SNGVOL INV_PCMB INV_PCMA DACB_MUTE DACA_MUTE
+	Write(KHwCS42L51DACOutputControl, KHtCS42L51DACOutputControl_DAC_SNGVOL);
+
+	// ALCX & PGAX ctrl, A(0x0A), B (0x0B)
+	Write(KHwCS42L51ALC_PGA_A_Control, 0);
+	Write(KHwCS42L51ALC_PGA_B_Control, 0);
+
+	// ADCx Mixer Volume Ctrl A(0x0E), B (0x0F)
+	Write(KHwCS42L51ALC_ADC_A_MixVolume, KHbCS42L51ALC_Volume_Min);
+	Write(KHwCS42L51ALC_ADC_B_MixVolume, KHbCS42L51ALC_Volume_Min);
+
+	// PCMx Volume Ctrl A(0x10), B (0x11)
+	Write(KHwCS42L51ALC_PCM_A_MixVolume, 0x18);
+	Write(KHwCS42L51ALC_PCM_B_MixVolume, 0x18);
+
+	// Volume Control: AOUTA (Address 16h) & AOUTB (Address 17h)
+	Write(KHwCS42L51ALC_Out_A_Volume, KHbCS42L51ALC_Volume_Min);
+	Write(KHwCS42L51ALC_Out_B_Volume, KHbCS42L51ALC_Volume_Min);
+
+	// DAC Control (Address 09h)
+	//  	7	  	 6	   	 5	     4	 	 3		2	   1	 	0
+	// DATA_SEL1 DATA_SEL0 FREEZE Reserved DEEMPH AMUTE DAC_SZC1 DAC_SZC0
+	// DATA_SEL1 DATA_SEL0:
+	// 00 - PCM Serial Port to DAC
+	// 01 - Signal Processing Engine to DAC
+	// 10 - ADC Serial Port to DAC    (11 - Reserved)
+	Write(KHwCS42L51DACControl, 1<<KHsCS42L51DACControl_DATA_SEL); // also clearing freeze bit will update settings..
+
+	// let's rock!! - boost bass and treble a bit ;)
+	// values for nibbles change from 0: +12dB (maximum) boost,
+	// to 15:(minimum) -10.5dB boost. 8: 0dB - play 'as it is'
+	val = (5 << KHsCS42L51ALC_ToneCtrl_TREB) | (6
+			<< KHsCS42L51ALC_ToneCtrl_BASS);
+	Write(KHwCS42L51ALC_ToneCtrl, val);
+
+	// power-up sequence..   - clear PDN ..after loading register settings..
+	Write(KHwCS42L51PwrCtrl, 0);
+
+	// This will return the error status, if any of Write() was not successful or KErrNone otherwise
+	return StopWrite();
+	}
+
+// this method is called from the sound-driver thread context to set the requested playback volume
+TInt RCS42AudioCodec::SetPlayVolume(TInt aVolume)
+	{
+	__KTRACE_SCODEC(Kern::Printf("RCS42AudioCodec::SetPlayVolume(%d)"));
+	// +12 db = 0001 1000 (24)
+	// 0db 	  = 0000 0000 (0)
+	//-0.5db  = 1111 1111 (255)
+	//-102db  = 0001 1001 (25)
+	TUint8 volume = 0xff & (aVolume + KHbCS42L51ALC_Volume_Min);
+
+	StartWrite();
+	Write(KHwCS42L51ALC_Out_A_Volume, volume);
+	Write(KHwCS42L51ALC_Out_B_Volume, volume);
+	TInt r = StopWrite();
+
+	return r;
+	}
+
+// this method is called from the sound-driver thread context to set the requested record volume
+TInt RCS42AudioCodec::SetRecordVolume(TInt aVolume)
+	{
+	__KTRACE_SCODEC(Kern::Printf("RCS42AudioCodec::SetRecordVolume(%d)"));
+	// +12 db = 0001 1000 (24)
+	// 0db 	  = 0000 0000 (0)
+	//-0.5db  = 1111 1111 (255)
+	//-102db  = 0001 1001 (25)
+	TUint8 volume = 0xff & (aVolume + KHbCS42L51ALC_Volume_Min);
+
+	StartWrite();
+	Write(KHwCS42L51ALC_ADC_A_MixVolume, volume);
+	Write(KHwCS42L51ALC_ADC_B_MixVolume, volume);
+	TInt r = StopWrite();
+
+	return r;
+	}