|
1 /* |
|
2 * Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * \bsp\hwip_nec_naviengine\ne1_tb\soundsc\soundsc_channel.cpp |
|
16 * Implementation of the NE1_TBVariant playback and record shared chunk sound physical device driver (PDD). |
|
17 * This file is part of the NE1_TBVariant Base port |
|
18 * Unit is identified by the time of its creation by the DSoundScPddNE1_TB - by specifying TSoundDirection |
|
19 * (either ESoundDirRecord or ESoundDirPlayback) as the constructor parameter. |
|
20 * |
|
21 */ |
|
22 |
|
23 |
|
24 |
|
25 /** |
|
26 @file |
|
27 */ |
|
28 #include <navienginedma.h> |
|
29 #include <i2s.h> |
|
30 #include <nkern.h> |
|
31 #include "soundsc_plat.h" |
|
32 |
|
33 #if _DEBUG |
|
34 static const char KSoundPDDPanicCat[] = "SOUNDSC PDD, line:"; |
|
35 #endif |
|
36 |
|
37 // physical address of the I2S channel 0 Tx register |
|
38 const TUint32 KHwI2S0TxPhys = KHwI2S0Phys + KHoI2STx; |
|
39 |
|
40 // physical address of the I2S channel 0 Rx register |
|
41 const TUint32 KHwI2S0RxPhys = KHwI2S0Phys + KHoI2SRx; |
|
42 |
|
43 /** |
|
44 Constructor for the NE1_TBVariant playback shared chunk sound driver physical device driver (PDD). |
|
45 */ |
|
46 DNE1_TBSoundScPddChannel::DNE1_TBSoundScPddChannel(TSoundDirection aSoundDirection) : |
|
47 iPowerUpDfc(PowerUpCallback, this, 0) |
|
48 { |
|
49 // The data transfer direction for this unit is specified as the constuctor parameter |
|
50 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::DNE1_TBSoundScPddChannel()", aSoundDirection)); |
|
51 |
|
52 iCaps.iDirection = aSoundDirection; |
|
53 |
|
54 // store direction for I2s calls |
|
55 if(aSoundDirection == ESoundDirRecord) |
|
56 { |
|
57 iI2sDirection = I2s::ERx; |
|
58 } |
|
59 else |
|
60 { |
|
61 iI2sDirection = I2s::ETx; |
|
62 } |
|
63 } |
|
64 |
|
65 /** |
|
66 Destructor for the NE1_TBVariant playback shared chunk sound driver physical device driver (PDD). |
|
67 */ |
|
68 DNE1_TBSoundScPddChannel::~DNE1_TBSoundScPddChannel() |
|
69 { |
|
70 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::~DNE1_TBSoundScPddChannel()", iCaps.iDirection)); |
|
71 |
|
72 // Delete the DMA request objects |
|
73 for (TInt i=0; i<KMaxDmaRequests; i++) |
|
74 { |
|
75 delete iDmaRequest[i]; |
|
76 } |
|
77 |
|
78 // Close the DMA channel. |
|
79 if (iDmaChannel) |
|
80 { |
|
81 iDmaChannel->Close(); |
|
82 } |
|
83 } |
|
84 |
|
85 /** |
|
86 Second stage constructor for the NE1_TBVariant playback shared chunk sound driver physical device driver (PDD). |
|
87 Note that this constructor is called before the second stage constructor for the LDD so it is not |
|
88 possible to call methods on the LDD here. |
|
89 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
90 */ |
|
91 TInt DNE1_TBSoundScPddChannel::DoCreate() |
|
92 { |
|
93 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::DoCreate", iCaps.iDirection)); |
|
94 TInt r = KErrNone; |
|
95 |
|
96 // Setup the capabilities of this device. |
|
97 SetCaps(); |
|
98 |
|
99 if (iCaps.iDirection == ESoundDirRecord) |
|
100 { |
|
101 iPowerUpDfc.SetDfcQ(DfcQ(KSoundScRxUnit0)); |
|
102 } |
|
103 else |
|
104 { |
|
105 iPowerUpDfc.SetDfcQ(DfcQ(KSoundScTxUnit0)); |
|
106 } |
|
107 |
|
108 // Setup the DMA channel information for this channel. |
|
109 // note, that the channel type (Playback/Record) is stored in the iDirection |
|
110 // and the I2s::TI2sDirection in iI2sDirection. |
|
111 TDmaChannel::SCreateInfo info; |
|
112 if (iCaps.iDirection == ESoundDirRecord) |
|
113 { |
|
114 info.iCookie = EDMAChannelI2S0RX; |
|
115 } |
|
116 else |
|
117 { |
|
118 info.iCookie = EDMAChannelI2S0TX; |
|
119 } |
|
120 |
|
121 if (iCaps.iDirection == ESoundDirRecord) |
|
122 { |
|
123 info.iDfcQ = DfcQ(KSoundScRxUnit0); |
|
124 } |
|
125 else |
|
126 { |
|
127 info.iDfcQ = DfcQ(KSoundScTxUnit0); |
|
128 } |
|
129 |
|
130 info.iDfcPriority = 0; // and set priority to 0 (the same as for RX channel) |
|
131 info.iDesCount = KMaxDmaRequests; |
|
132 |
|
133 // Try to open the DMA channel for a given direction (Playback or Record specified in iCookie) |
|
134 // If this channel was already opened at this point - the DMA framework will return KErrInUse. |
|
135 r = TDmaChannel::Open(info, iDmaChannel); |
|
136 if (r != KErrNone) |
|
137 { |
|
138 return r; |
|
139 } |
|
140 |
|
141 // Create the DMA request objects for use with the DMA channel. |
|
142 for (TInt i = 0; i < KMaxDmaRequests; i++) |
|
143 { |
|
144 iDmaRequest[i] = new DNE1_TBSoundScDmaRequest(*iDmaChannel,this, 0); |
|
145 if (iDmaRequest[i] == NULL) |
|
146 { |
|
147 return KErrNoMemory; |
|
148 } |
|
149 |
|
150 r = iDmaRequest[i]->CreateMonoBuffer(); |
|
151 if (r != KErrNone) |
|
152 { |
|
153 return r; |
|
154 } |
|
155 } |
|
156 |
|
157 // initialize the hardware FIFO of the I2S bus for this particular direction (iI2sDIrection). |
|
158 // Because on this bus - both channels' (left and right) FIFO can't be enabled separately |
|
159 // it is enough to call EnableFIFO for either of them (I2s::ELeft in this case). |
|
160 r = I2s::EnableFIFO(KI2sChanNum, I2s::ELeft, iI2sDirection); |
|
161 if (r != KErrNone) |
|
162 { |
|
163 return r; |
|
164 } |
|
165 |
|
166 // set the I2S bus hardware FIFO threshold for each channel of a given direction |
|
167 r = I2s::SetFIFOThreshold(KI2sChanNum, I2s::ELeft, iI2sDirection, KFifoThreshold); |
|
168 if (r != KErrNone) |
|
169 { |
|
170 return r; |
|
171 } |
|
172 |
|
173 r = I2s::SetFIFOThreshold(KI2sChanNum, I2s::ERight, iI2sDirection, KFifoThreshold); |
|
174 if (r != KErrNone) |
|
175 { |
|
176 return r; |
|
177 } |
|
178 |
|
179 |
|
180 return KErrNone; |
|
181 } |
|
182 |
|
183 /** |
|
184 Return the DFC queue to be used by this playback device. |
|
185 @return The DFC queue to use. |
|
186 */ |
|
187 TDfcQue* DNE1_TBSoundScPddChannel::DfcQ(TInt /*aUnit*/) |
|
188 { |
|
189 return(iPhysicalDevice->iDfcQ); |
|
190 } |
|
191 |
|
192 /** |
|
193 Called from the LDD to return the shared chunk create information to be used by this play device. |
|
194 @param aChunkCreateInfo A chunk create info. object to be to be filled with the settings |
|
195 required for this device. |
|
196 */ |
|
197 void DNE1_TBSoundScPddChannel::GetChunkCreateInfo(TChunkCreateInfo& aChunkCreateInfo) |
|
198 { |
|
199 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::GetChunkCreateInfo", iCaps.iDirection)); |
|
200 |
|
201 // Setup the shared chunk create information in aChunkCreateInfo for this play device. |
|
202 aChunkCreateInfo.iType = TChunkCreateInfo::ESharedKernelMultiple; |
|
203 aChunkCreateInfo.iMapAttr = EMapAttrFullyBlocking | EMapAttrWriteUser; // not cached, user writable |
|
204 aChunkCreateInfo.iOwnsMemory = ETrue; // Using RAM pages. |
|
205 aChunkCreateInfo.iDestroyedDfc = NULL; // No chunk destroy DFC. |
|
206 } |
|
207 |
|
208 /** |
|
209 Called from the LDD to return the capabilities of this device. |
|
210 @param aCapsBuf A packaged TSoundFormatsSupportedV02 object to be filled with the play |
|
211 capabilities of this device. This descriptor is in kernel memory and can be accessed directly. |
|
212 @see TSoundFormatsSupportedV02. |
|
213 */ |
|
214 void DNE1_TBSoundScPddChannel::Caps(TDes8& aCapsBuf) const |
|
215 { |
|
216 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::Caps", iCaps.iDirection)); |
|
217 |
|
218 // Copy iCaps back. |
|
219 TPtrC8 ptr((const TUint8*)&iCaps,sizeof(iCaps)); |
|
220 aCapsBuf.FillZ(aCapsBuf.MaxLength()); |
|
221 aCapsBuf=ptr.Left(Min(ptr.Length(),aCapsBuf.MaxLength())); |
|
222 } |
|
223 |
|
224 /** |
|
225 Called from the LDD to return the maximum transfer length in bytes that this device can support in a single data transfer. |
|
226 @return The maximum transfer length in bytes. |
|
227 */ |
|
228 TInt DNE1_TBSoundScPddChannel::MaxTransferLen() const |
|
229 { |
|
230 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::MaxTransferLen() %x (%d)", iCaps.iDirection,KMaxDmaTransferLen,KMaxDmaTransferLen)); |
|
231 return(KMaxDmaTransferLen); |
|
232 } |
|
233 |
|
234 /** |
|
235 Called from the LDD to configure or reconfigure the device using the the configuration supplied. |
|
236 @param aConfigBuf A packaged TCurrentSoundFormatV02 object which contains the new configuration settings. |
|
237 This descriptor is in kernel memory and can be accessed directly. |
|
238 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
239 @see TCurrentSoundFormatV02. |
|
240 */ |
|
241 TInt DNE1_TBSoundScPddChannel::SetConfig(const TDesC8& aConfigBuf) |
|
242 { |
|
243 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::SetConfig", iCaps.iDirection)); |
|
244 TInt r=KErrNone; |
|
245 |
|
246 // Read the new configuration from the LDD. |
|
247 TCurrentSoundFormatV02 config; |
|
248 TPtr8 ptr((TUint8*)&config,sizeof(config)); |
|
249 Kern::InfoCopy(ptr,aConfigBuf); |
|
250 |
|
251 // Set the I2S interface as bidirectional and master |
|
252 TI2sConfigV01 i2sconfig = {I2s::EMaster, I2s::EBidirectional}; |
|
253 TPckgBuf<TI2sConfigV01> i2sconf(i2sconfig); |
|
254 |
|
255 r = I2s::ConfigureInterface(KI2sChanNum, &i2sconf); |
|
256 if(r != KErrNone) |
|
257 { |
|
258 return r; |
|
259 } |
|
260 |
|
261 // Apply the specified audio configuration to the audio device. |
|
262 if(config.iChannels > 2) |
|
263 { |
|
264 return KErrNotSupported; |
|
265 } |
|
266 |
|
267 r = I2s::EnableDMA(KI2sChanNum, iI2sDirection); |
|
268 if(r != KErrNone) |
|
269 { |
|
270 return r; |
|
271 } |
|
272 |
|
273 switch (config.iEncoding) |
|
274 { |
|
275 case ESoundEncoding16BitPCM: |
|
276 r = I2s::SetSampleLength(KI2sChanNum, I2s::ELeft, I2s::ESample16Bit); |
|
277 if(r!=KErrNone) |
|
278 { |
|
279 break; |
|
280 } |
|
281 r = I2s::SetFrameLengthAndFormat(KI2sChanNum, I2s::EFrame48Bit, 16); |
|
282 break; |
|
283 default: |
|
284 r = KErrNotSupported; |
|
285 } |
|
286 |
|
287 // might be also be'KErrInUse' here - so we shouldn't continue.. |
|
288 if(r!=KErrNone) |
|
289 { |
|
290 return r; |
|
291 } |
|
292 |
|
293 // BPS = rate * bytes_per_sample * num_of_channels |
|
294 switch(config.iRate) |
|
295 { |
|
296 case ESoundRate11025Hz: |
|
297 r = I2s::SetSamplingRate(KI2sChanNum, I2s::E11_025KHz); |
|
298 break; |
|
299 |
|
300 case ESoundRate22050Hz: |
|
301 r = I2s::SetSamplingRate(KI2sChanNum, I2s::E22_05KHz); |
|
302 break; |
|
303 |
|
304 case ESoundRate44100Hz: |
|
305 r = I2s::SetSamplingRate(KI2sChanNum, I2s::E44_1KHz); |
|
306 break; |
|
307 |
|
308 default: |
|
309 r = KErrNotSupported; |
|
310 } |
|
311 |
|
312 // if we support it - copy the new configuration |
|
313 if(r == KErrNone) |
|
314 { |
|
315 iCurrentConfig = config; |
|
316 } |
|
317 return(r); |
|
318 } |
|
319 |
|
320 /** |
|
321 Called from the LDD to set the play volume. |
|
322 @param aVolume The play volume to be set - a value in the range 0 to 255. The value 255 equates |
|
323 to the maximum volume and each value below this equates to a 0.5dB step below it. |
|
324 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
325 */ |
|
326 TInt DNE1_TBSoundScPddChannel::SetVolume(TInt aVolume) |
|
327 { |
|
328 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::SetVolume", iCaps.iDirection)); |
|
329 TInt r; |
|
330 // Set the specified play volume on the audio device. |
|
331 if (iCaps.iDirection == ESoundDirRecord) |
|
332 { |
|
333 r = iPhysicalDevice->iCodec->SetRecordVolume(aVolume); |
|
334 } |
|
335 else |
|
336 { |
|
337 r = iPhysicalDevice->iCodec->SetPlayVolume(aVolume); |
|
338 } |
|
339 return(r); |
|
340 } |
|
341 |
|
342 /** |
|
343 Called from the LDD to prepare the audio device for playback. |
|
344 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
345 */ |
|
346 |
|
347 TInt DNE1_TBSoundScPddChannel::StartTransfer() |
|
348 { |
|
349 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::StartTransfer", iCaps.iDirection)); |
|
350 TInt r = I2s::Start(KI2sChanNum, iI2sDirection); |
|
351 return(r); |
|
352 } |
|
353 |
|
354 /** |
|
355 Called from the LDD to initiate the playback of a portion of data to the audio device. |
|
356 When the transfer is complete, the PDD signals this event using the LDD function PlayCallback(). |
|
357 @param aTransferID A value assigned by the LDD to allow it to uniquely identify a particular transfer fragment. |
|
358 @param aLinAddr The linear address within the shared chunk of the start of the data to be played. |
|
359 @param aPhysAddr The physical address within the shared chunk of the start of the data to be played. |
|
360 @param aNumBytes The number of bytes to be played. |
|
361 @return KErrNone if the transfer has been initiated successfully; |
|
362 KErrNotReady if the device is unable to accept the transfer for the moment; |
|
363 otherwise one of the other system-wide error codes. |
|
364 */ |
|
365 TInt DNE1_TBSoundScPddChannel::TransferData(TUint aTransferID,TLinAddr aLinAddr,TPhysAddr /*aPhysAddr*/,TInt aNumBytes) |
|
366 { |
|
367 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::TransferData(ID:%xH,Addr:%xH,Len:%d)",iCaps.iDirection,aTransferID,aLinAddr,aNumBytes)); |
|
368 TInt r = KErrNone; |
|
369 |
|
370 // Check that we can accept the request |
|
371 if (iPendingPlay >= KMaxDmaRequests) |
|
372 { |
|
373 return KErrNotReady; |
|
374 } |
|
375 else |
|
376 { |
|
377 // Set the DMA transfer.. |
|
378 // DSoundScDmaRequest, as a friend class checks iChannels and iDirection of the transfer |
|
379 r = iDmaRequest[iFlag]->SetDmaTransfer(aTransferID, aLinAddr, aNumBytes); |
|
380 if (r != KErrNone) |
|
381 { |
|
382 __KTRACE_SND(Kern::Printf("DMA Fragment error (%d), r= %d", iCaps.iDirection, r)); |
|
383 return r; |
|
384 } |
|
385 else |
|
386 { |
|
387 iDmaRequest[iFlag]->Queue(); |
|
388 iPendingPlay++; |
|
389 if ((++iFlag) >= KMaxDmaRequests) |
|
390 iFlag = 0; |
|
391 } |
|
392 } |
|
393 return KErrNone; |
|
394 } |
|
395 |
|
396 /** |
|
397 Called from the LDD to terminate the playback of a data to the device and to release any resources necessary for playback. |
|
398 This is called soon after the last pending play request from the client has been completed. Once this function had been |
|
399 called, the LDD will not issue any further TransferData() commands without first issueing a StartTransfer() command. |
|
400 */ |
|
401 void DNE1_TBSoundScPddChannel::StopTransfer() |
|
402 { |
|
403 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::StopTransfer", iCaps.iDirection)); |
|
404 |
|
405 // Stop the DMA channel. |
|
406 #ifdef _DEBUG |
|
407 TInt r = I2s::Stop(KI2sChanNum, iI2sDirection); |
|
408 __ASSERT_DEBUG(r == KErrNone, Kern::Fault(KSoundPDDPanicCat, __LINE__)); |
|
409 #else |
|
410 I2s::Stop(KI2sChanNum, iI2sDirection); |
|
411 #endif |
|
412 |
|
413 iDmaChannel->CancelAll(); |
|
414 iFlag = 0; |
|
415 iPendingPlay = 0; |
|
416 } |
|
417 |
|
418 /** |
|
419 Called from the LDD to halt the playback of data to the sound device but not to release any resources necessary for |
|
420 playback. |
|
421 If possible, any active transfer should be suspended in such a way that it can be resumed later - starting from next |
|
422 sample following the one last played. |
|
423 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
424 */ |
|
425 TInt DNE1_TBSoundScPddChannel::PauseTransfer() |
|
426 { |
|
427 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PauseTransfer, pending %d", iCaps.iDirection, iPendingPlay)); |
|
428 |
|
429 // Halt transfer on the audio device. |
|
430 TInt r = I2s::Stop(KI2sChanNum, iI2sDirection); |
|
431 if(r != KErrNone) |
|
432 { |
|
433 return r; |
|
434 } |
|
435 |
|
436 if (iCaps.iDirection == ESoundDirRecord) |
|
437 { |
|
438 // for Record, we need to figure out how much data was actually |
|
439 // transfered and provide this to the LDD.. |
|
440 if (iPendingPlay) |
|
441 { |
|
442 iDmaChannel->CancelAll(); |
|
443 TInt byteCount = 0; // Unless dma API is extended.. |
|
444 Ldd()->RecordCallback(0,KErrNone, byteCount); // We can use a NULL transfer ID when pausing. |
|
445 iPendingPlay=0; |
|
446 } |
|
447 iFlag=0; |
|
448 } |
|
449 |
|
450 return(r); |
|
451 } |
|
452 |
|
453 /** |
|
454 Called from the LDD to resume the playback of data to the sound device following a request to halt playback. |
|
455 If possible, any transfer which was active when the device was halted should be resumed - starting from next sample |
|
456 following the one last played. Once complete, it should be reported using PlayCallback() |
|
457 as normal. |
|
458 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
459 */ |
|
460 TInt DNE1_TBSoundScPddChannel::ResumeTransfer() |
|
461 { |
|
462 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::ResumeTransfer, pending %d", iCaps.iDirection, iPendingPlay)); |
|
463 |
|
464 // Resume playback on the audio device. |
|
465 TInt r = I2s::Start(KI2sChanNum, iI2sDirection); |
|
466 return(r); |
|
467 } |
|
468 |
|
469 |
|
470 NFastSemaphore DNE1_TBSoundScPddChannel::iFastSem; |
|
471 |
|
472 void DNE1_TBSoundScPddChannel::PowerUpCallback (TAny *aArg) |
|
473 { |
|
474 DNE1_TBSoundScPddChannel *a= (DNE1_TBSoundScPddChannel*)aArg; |
|
475 __KTRACE_SND(Kern::Printf("powerUpCallback(%d)", a->iCaps.iDirection)); |
|
476 |
|
477 // PowerUp the Codec |
|
478 a->iPowerUpStatus = RCS42AudioCodec::Open(a->iPhysicalDevice->iCodec); |
|
479 |
|
480 // signal will unblock the thread blocked in call to PowerUp() method. |
|
481 NKern::FSSignal(&a->iFastSem); |
|
482 } |
|
483 |
|
484 /** |
|
485 Called from the LDD to power up the sound device when the channel |
|
486 is first opened and if ever the phone is brought out of standby mode. |
|
487 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
488 */ |
|
489 TInt DNE1_TBSoundScPddChannel::PowerUp() |
|
490 { |
|
491 // Power up the audio device. |
|
492 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PowerUp", iCaps.iDirection)); |
|
493 |
|
494 // need to power up the device in the context of the driver's thread |
|
495 // (blocking the calling thread) |
|
496 TDfcQue* dfcQ; |
|
497 if (iCaps.iDirection == ESoundDirRecord) |
|
498 { |
|
499 dfcQ = DfcQ(KSoundScRxUnit0); |
|
500 } |
|
501 else |
|
502 { |
|
503 dfcQ = DfcQ(KSoundScTxUnit0); |
|
504 } |
|
505 |
|
506 if(dfcQ->iThread != NKern::CurrentThread()) |
|
507 { |
|
508 iPowerUpDfc.Enque(); |
|
509 iFastSem.iOwningThread = NKern::CurrentThread(); |
|
510 NKern::FSWait(&iFastSem); |
|
511 } |
|
512 else |
|
513 { |
|
514 iPowerUpStatus = RCS42AudioCodec::Open(iPhysicalDevice->iCodec); |
|
515 } |
|
516 |
|
517 return iPowerUpStatus; |
|
518 } |
|
519 |
|
520 /** |
|
521 Called from the LDD in the context of the driver thread to power down the sound device when the |
|
522 channel is closed and just before the phone powers down when being turned off or going into standby. |
|
523 */ |
|
524 void DNE1_TBSoundScPddChannel::PowerDown() |
|
525 { |
|
526 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PowerDown", iCaps.iDirection)); |
|
527 // Power down the audio device. |
|
528 // note, that reference-counting Codec will be powered-down if this call closes the last instance |
|
529 RCS42AudioCodec::Close(iPhysicalDevice->iCodec); |
|
530 } |
|
531 |
|
532 /** |
|
533 Called from the LDD to handle a custom configuration request. |
|
534 @param aFunction A number identifying the request. |
|
535 @param aParam A 32-bit value passed to the driver. Its meaning depends on the request. |
|
536 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
537 */ |
|
538 TInt DNE1_TBSoundScPddChannel::CustomConfig(TInt /*aFunction*/,TAny* /*aParam*/) |
|
539 { |
|
540 return(KErrNotSupported); |
|
541 } |
|
542 |
|
543 /** |
|
544 Called each time a playback DMA transfer completes - from the DMA callback function in the sound thread's DFC context. |
|
545 @param aTransferID The transfer ID of the DMA transfer. |
|
546 @param aTransferResult The result of the DMA transfer. |
|
547 @param aBytesTransferred The number of bytes transferred. |
|
548 */ |
|
549 void DNE1_TBSoundScPddChannel::PlayCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesTransferred) |
|
550 { |
|
551 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::PlayCallback, ID %x, result %d, pending: %d", iCaps.iDirection, aTransferID, aTransferResult, iPendingPlay)); |
|
552 |
|
553 --iPendingPlay; |
|
554 |
|
555 if(iCaps.iDirection == ESoundDirRecord) |
|
556 { |
|
557 Ldd()->RecordCallback(aTransferID,aTransferResult,aBytesTransferred); |
|
558 } |
|
559 else |
|
560 { |
|
561 Ldd()->PlayCallback(aTransferID,aTransferResult,aBytesTransferred); |
|
562 } |
|
563 } |
|
564 |
|
565 /** |
|
566 Initialise the data member DNE1_TBSoundScPddChannel::iCaps with the play capabilities of this audio playback device. |
|
567 */ |
|
568 void DNE1_TBSoundScPddChannel::SetCaps() |
|
569 { |
|
570 __KTRACE_SND(Kern::Printf("DNE1_TBSoundScPddChannel(%d)::SetCaps", iCaps.iDirection)); |
|
571 |
|
572 // The audio channel configurations supported by this unit |
|
573 // This unit supports both mono and stereo |
|
574 iCaps.iChannels = (KSoundMonoChannel | KSoundStereoChannel); |
|
575 |
|
576 // This unit supports only some of the sample rates offered by Symbian OS |
|
577 iCaps.iRates = (KSoundRate11025Hz | KSoundRate22050Hz | KSoundRate44100Hz); |
|
578 |
|
579 // The encoding formats supported |
|
580 // until we'll be able to set the transfer source/dest lengths for DMA transfers |
|
581 // only support for this one |
|
582 iCaps.iEncodings = KSoundEncoding16BitPCM; |
|
583 |
|
584 // This unit only supports interleaved data format when playing stereo; that is, a PCM data |
|
585 // stream where the left and right channel samples are interleaved as L-R-L-R-L-R etc. |
|
586 iCaps.iDataFormats = KSoundDataFormatInterleaved; |
|
587 |
|
588 // The minimum request size that the device can support. All requests to play or record data must be of a |
|
589 // length that is a multiple of this value. |
|
590 iCaps.iRequestMinSize = 4; |
|
591 |
|
592 // The logarithm to base 2 of the alignment required for request arguments. All requests to play or |
|
593 // record data must specify locations in the shared chunk which conform to this alignment. |
|
594 iCaps.iRequestAlignment = 2; |
|
595 |
|
596 // Indicates whether this unit is capable of detecting changes in its hardware configuration. |
|
597 iCaps.iHwConfigNotificationSupport = EFalse; |
|
598 } |
|
599 |
|
600 /** |
|
601 Constructor for a shared chunk sound driver playback DMA request. |
|
602 */ |
|
603 DNE1_TBSoundScDmaRequest::DNE1_TBSoundScDmaRequest(TDmaChannel& aChannel, DNE1_TBSoundScPddChannel* aPdd, TInt aMaxTransferSize) |
|
604 : DDmaRequest(aChannel,DNE1_TBSoundScDmaRequest::DmaService,this,aMaxTransferSize), |
|
605 iPdd(aPdd) |
|
606 { |
|
607 } |
|
608 |
|
609 DNE1_TBSoundScDmaRequest::~DNE1_TBSoundScDmaRequest() |
|
610 { |
|
611 // release buffer used for mono playback |
|
612 if (iChunk) |
|
613 { |
|
614 iChunk->Close(NULL); |
|
615 } |
|
616 |
|
617 if (iBuffPhys) |
|
618 { |
|
619 Epoc::FreePhysicalRam(iBuffPhys, KMaxDmaTransferLen*2); |
|
620 } |
|
621 } |
|
622 |
|
623 TInt DNE1_TBSoundScDmaRequest::CreateMonoBuffer() |
|
624 { |
|
625 // alloc memory for buffer - we might need to play mono samples.. |
|
626 TInt r = Epoc::AllocPhysicalRam(KMaxDmaTransferLen*2, iBuffPhys); |
|
627 if(r != KErrNone) |
|
628 { |
|
629 return r; |
|
630 } |
|
631 |
|
632 // map this buffer as non-cachable and writtable only by supervisor. |
|
633 r = DPlatChunkHw::New(iChunk, iBuffPhys, KMaxDmaTransferLen*2, |
|
634 EMapAttrSupRw | EMapAttrFullyBlocking); |
|
635 if (r != KErrNone) |
|
636 { |
|
637 return r; |
|
638 } |
|
639 |
|
640 iBufLin = iChunk->LinearAddress(); |
|
641 |
|
642 return KErrNone; |
|
643 } |
|
644 |
|
645 // |
|
646 TInt DNE1_TBSoundScDmaRequest::SetDmaTransfer(TUint aTransferID, TLinAddr aLinAddr, TInt aNumBytes) |
|
647 { |
|
648 __ASSERT_DEBUG(iBufLin != NULL, Kern::Fault(KSoundPDDPanicCat, __LINE__)); |
|
649 TInt r = KErrNone; |
|
650 |
|
651 // store TransferID |
|
652 iTransferID = aTransferID; |
|
653 |
|
654 if (iPdd->iCurrentConfig.iChannels == 1) |
|
655 { |
|
656 // Set the DMA source information - local buffer, which is always |
|
657 // twice as big for mono transfers.. |
|
658 iTransferSize = aNumBytes*2; |
|
659 |
|
660 // Store the original address of the data supplied.. this will be used |
|
661 // as the destination address for recorded mono data.. |
|
662 if (iPdd->iCaps.iDirection == ESoundDirRecord) |
|
663 { |
|
664 // store address of the orginal buffer, |
|
665 // where we need to copy the recorded data back - after the transfer has finished |
|
666 iAddrLinOrig = aLinAddr; |
|
667 |
|
668 r = Fragment(KHwI2S0RxPhys, iBufLin, iTransferSize, |
|
669 KDmaMemDest | KDmaIncDest | KDmaPhysAddrSrc, |
|
670 (TUint32)this); |
|
671 } |
|
672 else // this is a Play (Tx) unit |
|
673 // This is a mono transfer request so we need to copy data to the internal buffer |
|
674 // and transfer it as interleaved stereo - since this is the only format supported |
|
675 // by the I2S bus. |
|
676 { |
|
677 TInt16 *src = (TInt16*)aLinAddr; |
|
678 TInt32 *dst = (TInt32*)iBufLin; |
|
679 |
|
680 // copy data to the local buffer (2 bytes at the time) -to play mono in both channels |
|
681 for (TInt i = 0; i < aNumBytes/2; i++) |
|
682 { |
|
683 *dst++ = TInt32((*src) << 16) | (*src & 0xffff); |
|
684 src++; |
|
685 } |
|
686 |
|
687 r = Fragment(iBufLin, KHwI2S0TxPhys, iTransferSize, |
|
688 KDmaMemSrc | KDmaIncSrc | KDmaPhysAddrDest, |
|
689 (TUint32)this); |
|
690 } |
|
691 } |
|
692 else // it's stereo, interleaved data, which can be transferred directly |
|
693 { |
|
694 // Supply the DMA source information - original data in the shared chunk |
|
695 iTransferSize = aNumBytes; |
|
696 |
|
697 if (iPdd->iCaps.iDirection == ESoundDirRecord) |
|
698 { |
|
699 r = Fragment(KHwI2S0RxPhys, aLinAddr, iTransferSize, |
|
700 KDmaMemDest | KDmaIncDest | KDmaPhysAddrSrc, |
|
701 (TUint32)this); |
|
702 } |
|
703 else // this is a Play (Tx) unit |
|
704 { |
|
705 r = Fragment(aLinAddr, KHwI2S0TxPhys, iTransferSize, |
|
706 KDmaMemSrc | KDmaIncSrc | KDmaPhysAddrDest, |
|
707 (TUint32)this); |
|
708 } |
|
709 } |
|
710 return r; |
|
711 } |
|
712 |
|
713 /** |
|
714 DMA tx service routine. Called in the sound thread's DFC context by the s/w DMA controller. |
|
715 @param aResult Status of DMA transfer. |
|
716 @param aArg Argument passed to DMA controller. |
|
717 */ |
|
718 void DNE1_TBSoundScDmaRequest::DmaService(TResult aResult, TAny* aArg) |
|
719 { |
|
720 DNE1_TBSoundScDmaRequest& req = *(DNE1_TBSoundScDmaRequest*)aArg; |
|
721 __KTRACE_SND( Kern::Printf("DmaService(%d) %d",req.iPdd->iCaps.iDirection, aResult)); |
|
722 |
|
723 TInt res = KErrNone; |
|
724 TInt bytesTransferred = req.iTransferSize; |
|
725 if (aResult!=DDmaRequest::EOk) |
|
726 { |
|
727 res = KErrCorrupt; |
|
728 bytesTransferred = 0; |
|
729 } |
|
730 |
|
731 // if this was mono transfered as stereo.. |
|
732 if (req.iPdd->iCurrentConfig.iChannels == 1) |
|
733 { |
|
734 // adjust back the number of bytes transfered |
|
735 bytesTransferred /= 2; |
|
736 |
|
737 // if this request is a part of record unit |
|
738 // copy data to back to the shared chunk provided by the LDD |
|
739 if (req.iPdd->iCaps.iDirection == ESoundDirRecord) |
|
740 { |
|
741 TInt32 *src = (TInt32*)req.iBufLin; |
|
742 TInt16 *dst = (TInt16*)req.iAddrLinOrig; |
|
743 |
|
744 for (TInt i = 0; i < bytesTransferred; i+=2) |
|
745 { |
|
746 *dst++ = TInt16(*src++); |
|
747 } |
|
748 } |
|
749 } |
|
750 |
|
751 // Inform the LDD of the result of the transfer. |
|
752 req.iPdd->PlayCallback(req.iTransferID,res,bytesTransferred); |
|
753 |
|
754 return; |
|
755 } |
|
756 |