navienginebsp/ne1_tb/soundsc/soundsc_channel.cpp
changeset 0 5de814552237
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/navienginebsp/ne1_tb/soundsc/soundsc_channel.cpp	Tue Sep 28 18:00:05 2010 +0100
@@ -0,0 +1,756 @@
+/*
+* 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\hwip_nec_naviengine\ne1_tb\soundsc\soundsc_channel.cpp
+* Implementation of the NE1_TBVariant playback and record shared chunk sound physical device driver (PDD).
+* This file is part of the NE1_TBVariant Base port
+* Unit is identified by the time of its creation by the DSoundScPddNE1_TB - by specifying TSoundDirection
+* (either ESoundDirRecord or ESoundDirPlayback) as the constructor parameter.
+*
+*/
+
+
+
+/**
+ @file
+*/
+#include <navienginedma.h>
+#include <i2s.h>
+#include <nkern.h>
+#include "soundsc_plat.h"
+
+#if _DEBUG
+static const char KSoundPDDPanicCat[] = "SOUNDSC PDD, line:";
+#endif
+
+// physical address of the I2S channel 0 Tx register
+const TUint32 KHwI2S0TxPhys = KHwI2S0Phys + KHoI2STx;
+
+// physical address of the I2S channel 0 Rx register
+const TUint32 KHwI2S0RxPhys = KHwI2S0Phys + KHoI2SRx;
+
+/**
+Constructor for the NE1_TBVariant playback shared chunk sound driver physical device driver (PDD).
+*/
+DNE1_TBSoundScPddChannel::DNE1_TBSoundScPddChannel(TSoundDirection aSoundDirection) :
+	iPowerUpDfc(PowerUpCallback, this, 0)
+	{
+	// The data transfer direction for this unit is specified as the constuctor parameter
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::DNE1_TBSoundScPddChannel()", aSoundDirection));
+
+	iCaps.iDirection = aSoundDirection;
+
+	// store direction for I2s calls
+	if(aSoundDirection == ESoundDirRecord)
+		{
+		iI2sDirection = I2s::ERx;
+		}
+	else
+		{
+		iI2sDirection = I2s::ETx;
+		}
+	}
+
+/**
+Destructor for the NE1_TBVariant playback shared chunk sound driver physical device driver (PDD).
+*/
+DNE1_TBSoundScPddChannel::~DNE1_TBSoundScPddChannel()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::~DNE1_TBSoundScPddChannel()", iCaps.iDirection));
+
+	// Delete the DMA request objects
+	for (TInt i=0; i<KMaxDmaRequests; i++)
+		{
+		delete iDmaRequest[i];
+		}
+
+	// Close the DMA channel.
+	if (iDmaChannel)
+		{
+		iDmaChannel->Close();
+		}
+	}
+
+/**
+Second stage constructor for the NE1_TBVariant playback shared chunk sound driver physical device driver (PDD).
+Note that this constructor is called before the second stage constructor for the LDD so it is not
+possible to call methods on the LDD here.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DNE1_TBSoundScPddChannel::DoCreate()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::DoCreate", iCaps.iDirection));
+	TInt r = KErrNone;
+
+	// Setup the capabilities of this device.
+	SetCaps();
+
+	if (iCaps.iDirection == ESoundDirRecord)
+		{
+		iPowerUpDfc.SetDfcQ(DfcQ(KSoundScRxUnit0));
+		}
+	else
+		{
+		iPowerUpDfc.SetDfcQ(DfcQ(KSoundScTxUnit0));
+		}
+
+	// Setup the DMA channel information for this channel.
+	// note, that the channel type (Playback/Record) is stored in the iDirection
+	// and the I2s::TI2sDirection in iI2sDirection.
+	TDmaChannel::SCreateInfo info;
+	if (iCaps.iDirection == ESoundDirRecord)
+		{
+		info.iCookie = EDMAChannelI2S0RX;
+		}
+	else
+		{
+		info.iCookie = EDMAChannelI2S0TX;
+		}
+
+	if (iCaps.iDirection == ESoundDirRecord)
+		{
+		info.iDfcQ = DfcQ(KSoundScRxUnit0);
+		}
+	else
+		{
+		info.iDfcQ = DfcQ(KSoundScTxUnit0);
+		}
+
+	info.iDfcPriority = 0; // and set priority to 0 (the same as for RX channel)
+	info.iDesCount = KMaxDmaRequests;
+
+	// Try to open the DMA channel for a given direction (Playback or Record specified in iCookie)
+	// If this channel was already opened at this point - the DMA framework will return KErrInUse.
+	r = TDmaChannel::Open(info, iDmaChannel);
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+	// Create the DMA request objects for use with the DMA channel.
+	for (TInt i = 0; i < KMaxDmaRequests; i++)
+		{
+		iDmaRequest[i] = new DNE1_TBSoundScDmaRequest(*iDmaChannel,this, 0);
+		if (iDmaRequest[i] == NULL)
+			{
+			return KErrNoMemory;
+			}
+
+		r = iDmaRequest[i]->CreateMonoBuffer();
+		if (r != KErrNone)
+			{
+			return r;
+			}
+		}
+
+	// initialize the hardware FIFO of the I2S bus for this particular direction (iI2sDIrection).
+	// Because on this bus - both channels' (left and right) FIFO can't be enabled separately
+	// it is enough to call EnableFIFO for either of them (I2s::ELeft in this case).
+	r = I2s::EnableFIFO(KI2sChanNum, I2s::ELeft, iI2sDirection);
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+	// set the I2S bus hardware FIFO threshold for each channel of a given direction
+	r = I2s::SetFIFOThreshold(KI2sChanNum, I2s::ELeft, iI2sDirection, KFifoThreshold);
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+	r = I2s::SetFIFOThreshold(KI2sChanNum, I2s::ERight, iI2sDirection, KFifoThreshold);
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+
+	return KErrNone;
+	}
+
+/**
+Return the DFC queue to be used by this playback device.
+@return The DFC queue to use.
+*/
+TDfcQue* DNE1_TBSoundScPddChannel::DfcQ(TInt /*aUnit*/)
+	{
+	return(iPhysicalDevice->iDfcQ);
+	}
+
+/**
+Called from the LDD to return the shared chunk create information to be used by this play device.
+@param aChunkCreateInfo A chunk create info. object to be to be filled with the settings
+						required for this device.
+*/
+void DNE1_TBSoundScPddChannel::GetChunkCreateInfo(TChunkCreateInfo& aChunkCreateInfo)
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::GetChunkCreateInfo", iCaps.iDirection));
+
+	// Setup the shared chunk create information in aChunkCreateInfo for this play device.
+	aChunkCreateInfo.iType = TChunkCreateInfo::ESharedKernelMultiple;
+	aChunkCreateInfo.iMapAttr = EMapAttrFullyBlocking | EMapAttrWriteUser; // not cached, user writable
+	aChunkCreateInfo.iOwnsMemory = ETrue; 	// Using RAM pages.
+	aChunkCreateInfo.iDestroyedDfc = NULL; 	// No chunk destroy DFC.
+	}
+
+/**
+Called from the LDD to return the capabilities of this device.
+@param aCapsBuf A packaged TSoundFormatsSupportedV02 object to be filled with the play
+				capabilities of this device. This descriptor is in kernel memory and can be accessed directly.
+@see TSoundFormatsSupportedV02.
+*/
+void DNE1_TBSoundScPddChannel::Caps(TDes8& aCapsBuf) const
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::Caps", iCaps.iDirection));
+
+	// Copy iCaps back.
+	TPtrC8 ptr((const TUint8*)&iCaps,sizeof(iCaps));
+	aCapsBuf.FillZ(aCapsBuf.MaxLength());
+	aCapsBuf=ptr.Left(Min(ptr.Length(),aCapsBuf.MaxLength()));
+	}
+
+/**
+Called from the LDD to return the maximum transfer length in bytes that this device can support in a single data transfer.
+@return The maximum transfer length in bytes.
+*/
+TInt DNE1_TBSoundScPddChannel::MaxTransferLen() const
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::MaxTransferLen() %x (%d)", iCaps.iDirection,KMaxDmaTransferLen,KMaxDmaTransferLen));
+	return(KMaxDmaTransferLen);
+	}
+
+/**
+Called from the LDD to configure or reconfigure the device using the the configuration supplied.
+@param aConfigBuf A packaged TCurrentSoundFormatV02 object which contains the new configuration settings.
+				  This descriptor is in kernel memory and can be accessed directly.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@see TCurrentSoundFormatV02.
+*/
+TInt DNE1_TBSoundScPddChannel::SetConfig(const TDesC8& aConfigBuf)
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::SetConfig", iCaps.iDirection));
+	TInt r=KErrNone;
+
+	// Read the new configuration from the LDD.
+	TCurrentSoundFormatV02 config;
+	TPtr8 ptr((TUint8*)&config,sizeof(config));
+	Kern::InfoCopy(ptr,aConfigBuf);
+
+	// Set the I2S interface as bidirectional and master
+	TI2sConfigV01 i2sconfig = {I2s::EMaster, I2s::EBidirectional};
+	TPckgBuf<TI2sConfigV01> i2sconf(i2sconfig);
+
+	r = I2s::ConfigureInterface(KI2sChanNum, &i2sconf);
+	if(r != KErrNone)
+		{
+		return r;
+		}
+
+	// Apply the specified audio configuration to the audio device.
+	if(config.iChannels > 2)
+		{
+		return KErrNotSupported;
+		}
+
+	r = I2s::EnableDMA(KI2sChanNum, iI2sDirection);
+	if(r != KErrNone)
+		{
+		return r;
+		}
+
+	switch (config.iEncoding)
+		{
+		case ESoundEncoding16BitPCM:
+			r = I2s::SetSampleLength(KI2sChanNum, I2s::ELeft, I2s::ESample16Bit);
+			if(r!=KErrNone)
+				{
+				break;
+				}
+			r = I2s::SetFrameLengthAndFormat(KI2sChanNum, I2s::EFrame48Bit,  16);
+			break;
+		default:
+			r =  KErrNotSupported;
+		}
+
+	// might be also be'KErrInUse' here - so we shouldn't continue..
+	if(r!=KErrNone)
+		{
+		return r;
+		}
+
+	// BPS = rate * bytes_per_sample * num_of_channels
+	switch(config.iRate)
+		{
+		case ESoundRate11025Hz:
+			r = I2s::SetSamplingRate(KI2sChanNum, I2s::E11_025KHz);
+			break;
+
+		case ESoundRate22050Hz:
+			r = I2s::SetSamplingRate(KI2sChanNum, I2s::E22_05KHz);
+			break;
+
+		case ESoundRate44100Hz:
+			r = I2s::SetSamplingRate(KI2sChanNum, I2s::E44_1KHz);
+			break;
+
+		default:
+			r = KErrNotSupported;
+		}
+
+	// if we support it - copy the new configuration
+	if(r == KErrNone)
+		{
+		iCurrentConfig = config;
+		}
+	return(r);
+	}
+
+/**
+Called from the LDD to set the play volume.
+@param aVolume The play volume to be set - a value in the range 0 to 255. The value 255 equates
+	to the maximum volume and each value below this equates to a 0.5dB step below it.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DNE1_TBSoundScPddChannel::SetVolume(TInt aVolume)
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::SetVolume", iCaps.iDirection));
+	TInt r;
+	// Set the specified play volume on the audio device.
+	if (iCaps.iDirection == ESoundDirRecord)
+		{
+		r = iPhysicalDevice->iCodec->SetRecordVolume(aVolume);
+		}
+	else
+		{
+		r = iPhysicalDevice->iCodec->SetPlayVolume(aVolume);
+		}
+	return(r);
+	}
+
+/**
+Called from the LDD to prepare the audio device for playback.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+
+TInt DNE1_TBSoundScPddChannel::StartTransfer()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::StartTransfer", iCaps.iDirection));
+	TInt r = I2s::Start(KI2sChanNum, iI2sDirection);
+	return(r);
+	}
+
+/**
+Called from the LDD to initiate the playback of a portion of data to the audio device.
+When the transfer is complete, the PDD signals this event using the LDD function PlayCallback().
+@param aTransferID A value assigned by the LDD to allow it to uniquely identify a particular transfer fragment.
+@param aLinAddr The linear address within the shared chunk of the start of the data to be played.
+@param aPhysAddr The physical address within the shared chunk of the start of the data to be played.
+@param aNumBytes The number of bytes to be played.
+@return KErrNone if the transfer has been initiated successfully;
+  		KErrNotReady if the device is unable to accept the transfer for the moment;
+		otherwise one of the other system-wide error codes.
+*/
+TInt DNE1_TBSoundScPddChannel::TransferData(TUint aTransferID,TLinAddr aLinAddr,TPhysAddr /*aPhysAddr*/,TInt aNumBytes)
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::TransferData(ID:%xH,Addr:%xH,Len:%d)",iCaps.iDirection,aTransferID,aLinAddr,aNumBytes));
+	TInt r = KErrNone;
+
+	// Check that we can accept the request
+	if (iPendingPlay >= KMaxDmaRequests)
+		{
+		return KErrNotReady;
+		}
+	else
+		{
+		// Set the DMA transfer..
+		// DSoundScDmaRequest, as a friend class checks iChannels and iDirection of the transfer
+		r = iDmaRequest[iFlag]->SetDmaTransfer(aTransferID, aLinAddr, aNumBytes);
+		if (r != KErrNone)
+			{
+			__KTRACE_SND(Kern::Printf("DMA Fragment error (%d), r= %d", iCaps.iDirection, r));
+			return r;
+			}
+		else
+			{
+			iDmaRequest[iFlag]->Queue();
+			iPendingPlay++;
+			if ((++iFlag) >= KMaxDmaRequests)
+				iFlag = 0;
+			}
+		}
+	return KErrNone;
+	}
+
+/**
+Called from the LDD to terminate the playback of a data to the device and to release any resources necessary for playback.
+This is called soon after the last pending play request from the client has been completed. Once this function had been
+called, the LDD will not issue any further TransferData() commands without first issueing a StartTransfer() command.
+*/
+void DNE1_TBSoundScPddChannel::StopTransfer()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::StopTransfer", iCaps.iDirection));
+
+	// Stop the DMA channel.
+#ifdef _DEBUG
+	TInt r = I2s::Stop(KI2sChanNum, iI2sDirection);
+	__ASSERT_DEBUG(r == KErrNone, Kern::Fault(KSoundPDDPanicCat, __LINE__));
+#else
+	I2s::Stop(KI2sChanNum, iI2sDirection);
+#endif
+
+	iDmaChannel->CancelAll();
+	iFlag = 0;
+	iPendingPlay = 0;
+	}
+
+/**
+Called from the LDD to halt the playback of data to the sound device but not to release any resources necessary for
+playback.
+If possible, any active transfer should be suspended in such a way that it can be resumed later - starting from next
+sample following the one last played.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DNE1_TBSoundScPddChannel::PauseTransfer()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PauseTransfer, pending %d", iCaps.iDirection, iPendingPlay));
+
+	// Halt transfer on the audio device.
+	TInt r = I2s::Stop(KI2sChanNum, iI2sDirection);
+	if(r != KErrNone)
+		{
+		return r;
+		}
+
+	if (iCaps.iDirection == ESoundDirRecord)
+		{
+	    // for Record, we need to figure out how much data was actually
+		// transfered and provide this to the LDD..
+		if (iPendingPlay)
+			{
+			iDmaChannel->CancelAll();
+			TInt byteCount = 0; // Unless dma API is extended..
+			Ldd()->RecordCallback(0,KErrNone, byteCount);	// We can use a NULL transfer ID when pausing.
+			iPendingPlay=0;
+			}
+	    iFlag=0;
+		}
+
+	return(r);
+	}
+
+/**
+Called from the LDD to resume the playback of data to the sound device following a request to halt playback.
+If possible, any transfer which was active when the device was halted should be resumed - starting from next sample
+following the one last played. Once complete, it should be reported using PlayCallback()
+as normal.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DNE1_TBSoundScPddChannel::ResumeTransfer()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::ResumeTransfer, pending %d", iCaps.iDirection, iPendingPlay));
+
+	// Resume playback on the audio device.
+	TInt r = I2s::Start(KI2sChanNum, iI2sDirection);
+	return(r);
+	}
+
+
+NFastSemaphore DNE1_TBSoundScPddChannel::iFastSem;
+
+void DNE1_TBSoundScPddChannel::PowerUpCallback (TAny *aArg)
+	{
+	DNE1_TBSoundScPddChannel *a= (DNE1_TBSoundScPddChannel*)aArg;
+	__KTRACE_SND(Kern::Printf("powerUpCallback(%d)", a->iCaps.iDirection));
+
+	// PowerUp the Codec
+	a->iPowerUpStatus = RCS42AudioCodec::Open(a->iPhysicalDevice->iCodec);
+
+	// signal will unblock the thread blocked in call to PowerUp() method.
+	NKern::FSSignal(&a->iFastSem);
+	}
+
+/**
+Called from the LDD to power up the sound device when the channel
+is first opened and if ever the phone is brought out of standby mode.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DNE1_TBSoundScPddChannel::PowerUp()
+	{
+	// Power up the audio device.
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PowerUp", iCaps.iDirection));
+
+	// need to power up the device in the context of the driver's thread
+	// (blocking the calling thread)
+	TDfcQue* dfcQ;
+	if (iCaps.iDirection == ESoundDirRecord)
+		{
+		dfcQ = DfcQ(KSoundScRxUnit0);
+		}
+	else
+		{
+		dfcQ = DfcQ(KSoundScTxUnit0);
+		}
+
+	if(dfcQ->iThread != NKern::CurrentThread())
+		{
+		iPowerUpDfc.Enque();
+		iFastSem.iOwningThread = NKern::CurrentThread();
+		NKern::FSWait(&iFastSem);
+		}
+	else
+		{
+		iPowerUpStatus = RCS42AudioCodec::Open(iPhysicalDevice->iCodec);
+		}
+
+	return iPowerUpStatus;
+	}
+
+/**
+Called from the LDD in the context of the driver thread to power down the sound device when the
+channel is closed and just before the phone powers down when being turned off or going into standby.
+*/
+void DNE1_TBSoundScPddChannel::PowerDown()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PowerDown", iCaps.iDirection));
+	// Power down the audio device.
+	// note, that reference-counting Codec will be powered-down if this call closes the last instance
+	RCS42AudioCodec::Close(iPhysicalDevice->iCodec);
+	}
+
+/**
+Called from the LDD to handle a custom configuration request.
+@param aFunction A number identifying the request.
+@param aParam A 32-bit value passed to the driver. Its meaning depends on the request.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DNE1_TBSoundScPddChannel::CustomConfig(TInt /*aFunction*/,TAny* /*aParam*/)
+	{
+	return(KErrNotSupported);
+	}
+
+/**
+Called each time a playback DMA transfer completes - from the DMA callback function in the sound thread's DFC context.
+@param aTransferID The transfer ID of the DMA transfer.
+@param aTransferResult The result of the DMA transfer.
+@param aBytesTransferred The number of bytes transferred.
+*/
+void DNE1_TBSoundScPddChannel::PlayCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesTransferred)
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PlayCallback, ID %x, result %d, pending: %d", iCaps.iDirection, aTransferID, aTransferResult, iPendingPlay));
+
+	--iPendingPlay;
+
+	if(iCaps.iDirection == ESoundDirRecord)
+		{
+		Ldd()->RecordCallback(aTransferID,aTransferResult,aBytesTransferred);
+		}
+	else
+		{
+		Ldd()->PlayCallback(aTransferID,aTransferResult,aBytesTransferred);
+		}
+	}
+
+/**
+Initialise the data member DNE1_TBSoundScPddChannel::iCaps with the play capabilities of this audio playback device.
+*/
+void DNE1_TBSoundScPddChannel::SetCaps()
+	{
+	__KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::SetCaps", iCaps.iDirection));
+
+	// The audio channel configurations supported by this unit
+	// This unit supports both mono and stereo
+	iCaps.iChannels = (KSoundMonoChannel | KSoundStereoChannel);
+
+	// This unit supports only some of the sample rates offered by Symbian OS
+	iCaps.iRates = (KSoundRate11025Hz | KSoundRate22050Hz | KSoundRate44100Hz);
+
+	// The encoding formats supported
+	// until we'll be able to set the transfer source/dest lengths for DMA transfers
+	// only support for this one
+	iCaps.iEncodings = KSoundEncoding16BitPCM;
+
+	// This unit only supports interleaved data format when playing stereo;  that is, a PCM data
+	// stream where the left and right channel samples are interleaved as L-R-L-R-L-R etc.
+	iCaps.iDataFormats = KSoundDataFormatInterleaved;
+
+	// The minimum request size that the device can support. All requests to play or record data must be of a
+	//	length that is a multiple of this value.
+	iCaps.iRequestMinSize = 4;
+
+	// The logarithm to base 2 of the alignment required for request arguments. All requests to play or
+	//	record data must specify locations in the shared chunk which conform to this alignment.
+	iCaps.iRequestAlignment = 2;
+
+	// Indicates whether this unit is capable of detecting changes in its hardware configuration.
+	iCaps.iHwConfigNotificationSupport = EFalse;
+	}
+
+/**
+Constructor for a shared chunk sound driver playback DMA request.
+*/
+DNE1_TBSoundScDmaRequest::DNE1_TBSoundScDmaRequest(TDmaChannel& aChannel, DNE1_TBSoundScPddChannel* aPdd, TInt aMaxTransferSize)
+	: DDmaRequest(aChannel,DNE1_TBSoundScDmaRequest::DmaService,this,aMaxTransferSize),
+	  iPdd(aPdd)
+	{
+	}
+
+DNE1_TBSoundScDmaRequest::~DNE1_TBSoundScDmaRequest()
+	{
+	// release buffer used for mono playback
+	if (iChunk)
+		{
+		iChunk->Close(NULL);
+		}
+
+	if (iBuffPhys)
+		{
+		Epoc::FreePhysicalRam(iBuffPhys, KMaxDmaTransferLen*2);
+		}
+	}
+
+TInt DNE1_TBSoundScDmaRequest::CreateMonoBuffer()
+	{
+	// alloc memory for buffer - we might need to play mono samples..
+	TInt r = Epoc::AllocPhysicalRam(KMaxDmaTransferLen*2, iBuffPhys);
+	if(r != KErrNone)
+		{
+		return r;
+		}
+
+	// map this buffer as non-cachable and writtable only by supervisor.
+	r = DPlatChunkHw::New(iChunk, iBuffPhys, KMaxDmaTransferLen*2,
+							  EMapAttrSupRw | EMapAttrFullyBlocking);
+	if (r != KErrNone)
+		{
+		return r;
+		}
+
+	iBufLin = iChunk->LinearAddress();
+
+	return KErrNone;
+	}
+
+//
+TInt DNE1_TBSoundScDmaRequest::SetDmaTransfer(TUint aTransferID, TLinAddr aLinAddr, TInt aNumBytes)
+	{
+	__ASSERT_DEBUG(iBufLin != NULL, Kern::Fault(KSoundPDDPanicCat, __LINE__));
+	TInt r = KErrNone;
+
+	// store TransferID
+	iTransferID = aTransferID;
+
+	if (iPdd->iCurrentConfig.iChannels == 1)
+		{
+		// Set the DMA source information - local buffer, which is always
+		// twice as big for mono transfers..
+		iTransferSize = aNumBytes*2;
+
+		// Store the original address of the data supplied.. this will be used
+		// as the destination address for recorded mono data..
+		if (iPdd->iCaps.iDirection == ESoundDirRecord)
+			{
+			// store address of the orginal buffer,
+			// where we need to copy the recorded data back - after the transfer has finished
+			iAddrLinOrig = aLinAddr;
+
+			r = Fragment(KHwI2S0RxPhys, iBufLin, iTransferSize,
+					KDmaMemDest | KDmaIncDest | KDmaPhysAddrSrc,
+					(TUint32)this);
+			}
+		else // this is a Play (Tx) unit
+		// This is a mono transfer request so we need to copy data to the internal buffer
+		// and transfer it as interleaved stereo - since this is the only format supported
+		// by the I2S bus.
+			{
+			TInt16 *src = (TInt16*)aLinAddr;
+			TInt32 *dst = (TInt32*)iBufLin;
+
+			// copy data to the local buffer (2 bytes at the time) -to play mono in both channels
+			for (TInt i = 0; i < aNumBytes/2; i++)
+				{
+				*dst++ = TInt32((*src) << 16) | (*src & 0xffff);
+				src++;
+				}
+
+			r = Fragment(iBufLin, KHwI2S0TxPhys, iTransferSize,
+					KDmaMemSrc | KDmaIncSrc | KDmaPhysAddrDest,
+					(TUint32)this);
+			}
+		}
+	else // it's stereo, interleaved data, which can be transferred directly
+		{
+		// Supply the DMA source information - original data in the shared chunk
+		iTransferSize = aNumBytes;
+
+		if (iPdd->iCaps.iDirection == ESoundDirRecord)
+			{
+			r = Fragment(KHwI2S0RxPhys, aLinAddr, iTransferSize,
+					KDmaMemDest | KDmaIncDest | KDmaPhysAddrSrc,
+					(TUint32)this);
+			}
+		else // this is a Play (Tx) unit
+			{
+			r = Fragment(aLinAddr, KHwI2S0TxPhys, iTransferSize,
+					KDmaMemSrc | KDmaIncSrc | KDmaPhysAddrDest,
+					(TUint32)this);
+			}
+		}
+	return r;
+	}
+
+/**
+DMA tx service routine. Called in the sound thread's DFC context by the s/w DMA controller.
+@param aResult Status of DMA transfer.
+@param aArg Argument passed to DMA controller.
+*/
+void DNE1_TBSoundScDmaRequest::DmaService(TResult aResult, TAny* aArg)
+	{
+	DNE1_TBSoundScDmaRequest& req = *(DNE1_TBSoundScDmaRequest*)aArg;
+	__KTRACE_SND( Kern::Printf("DmaService(%d) %d",req.iPdd->iCaps.iDirection, aResult));
+
+	TInt res = KErrNone;
+	TInt bytesTransferred = req.iTransferSize;
+	if (aResult!=DDmaRequest::EOk)
+		{
+		res = KErrCorrupt;
+		bytesTransferred = 0;
+		}
+
+	// if this was mono transfered as stereo..
+	if (req.iPdd->iCurrentConfig.iChannels == 1)
+		{
+		// adjust back the number of bytes transfered
+		bytesTransferred /= 2;
+
+		// if this request is a part of record unit
+		// copy data to back to the shared chunk provided by the LDD
+		if (req.iPdd->iCaps.iDirection == ESoundDirRecord)
+			{
+			TInt32 *src = (TInt32*)req.iBufLin;
+			TInt16 *dst = (TInt16*)req.iAddrLinOrig;
+
+			for (TInt i = 0; i < bytesTransferred; i+=2)
+				{
+				*dst++ = TInt16(*src++);
+				}
+			}
+		}
+
+	// Inform the LDD of the result of the transfer.
+	req.iPdd->PlayCallback(req.iTransferID,res,bytesTransferred);
+
+	return;
+	}
+