1 // Every debug output has "Modulname text"
2 #define STR_MODULENAME "AC97 Stream: "
3
4 #include "shared.h"
5 #include "miniport.h"
6
7 #ifdef _MSC_VER
8 #pragma code_seg("PAGE")
9 #endif
10
11
12 /*****************************************************************************
13 * CMiniportStream::NonDelegatingQueryInterface
14 *****************************************************************************
15 * Obtains an interface. This function works just like a COM QueryInterface
16 * call and is used if the object is not being aggregated.
17 */
NonDelegatingQueryInterface(_In_ REFIID Interface,_COM_Outptr_ PVOID * Object,_In_ REFIID iStream,_In_ PUNKNOWN stream)18 NTSTATUS CMiniportStream::NonDelegatingQueryInterface
19 (
20 _In_ REFIID Interface,
21 _COM_Outptr_ PVOID * Object,
22 _In_ REFIID iStream,
23 _In_ PUNKNOWN stream
24 )
25 {
26 PAGED_CODE ();
27
28 ASSERT (Object);
29
30 DOUT (DBG_PRINT, ("[CMiniportStream::NonDelegatingQueryInterface]"));
31
32 //
33 // Convert for IID_IMiniportXXXStream
34 //
35 if (IsEqualGUIDAligned (Interface, iStream))
36 {
37 *Object = (PVOID)stream;
38 }
39 //
40 // Convert for IID_IDrmAudioStream
41 //
42 else if (IsEqualGUIDAligned (Interface, IID_IDrmAudioStream))
43 {
44 *Object = (PVOID)(PDRMAUDIOSTREAM)this;
45 }
46 //
47 // Convert for IID_IUnknown
48 //
49 else if (IsEqualGUIDAligned (Interface, IID_IUnknown))
50 {
51 *Object = (PVOID)stream;
52 }
53 else
54 {
55 *Object = NULL;
56 return STATUS_INVALID_PARAMETER;
57 }
58
59 ((PUNKNOWN)*Object)->AddRef ();
60 return STATUS_SUCCESS;
61 }
62
63
Init(IN CMiniport * Miniport_,IN PUNKNOWN PortStream_,IN WavePins Pin_,IN BOOLEAN Capture_,IN PKSDATAFORMAT DataFormat_,OUT PSERVICEGROUP * ServiceGroup_)64 NTSTATUS CMiniportStream::Init
65 (
66 IN CMiniport *Miniport_,
67 IN PUNKNOWN PortStream_,
68 IN WavePins Pin_,
69 IN BOOLEAN Capture_,
70 IN PKSDATAFORMAT DataFormat_,
71 OUT PSERVICEGROUP *ServiceGroup_
72 )
73 {
74 PAGED_CODE ();
75
76 DOUT (DBG_PRINT, ("[CMiniportStream::Init]"));
77
78 ASSERT (Miniport_);
79 ASSERT (DataFormat_);
80
81 //
82 // The rule here is that we return when we fail without a cleanup.
83 // The destructor will relase the allocated memory.
84 //
85 NTSTATUS ntStatus = STATUS_SUCCESS;
86
87 //
88 // Save miniport pointer and addref it.
89 //
90 obj_AddRef(Miniport_, (PVOID *)&Miniport);
91
92 //
93 // Save portstream interface pointer and addref it.
94 //
95 obj_AddRef(PortStream_, (PVOID *)&PortStream);
96
97
98 //
99 // Save channel ID and capture flag.
100 //
101 Pin = Pin_;
102 Capture = Capture_;
103
104 //
105 // Save data format and current sample rate.
106 //
107 DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat_;
108 CurrentRate = DataFormat->WaveFormatEx.nSamplesPerSec;
109 NumberOfChannels = DataFormat->WaveFormatEx.nChannels;
110
111
112 if (ServiceGroup_)
113 {
114
115 //
116 // Create a service group (a DPC abstraction/helper) to help with
117 // interrupts.
118 //
119 ntStatus = PcNewServiceGroup (&ServiceGroup, NULL);
120 if (!NT_SUCCESS (ntStatus))
121 {
122 DOUT (DBG_ERROR, ("Failed to create a service group!"));
123 return ntStatus;
124 }
125 }
126
127 //
128 // Store the base address of this DMA engine.
129 //
130 if (Capture)
131 {
132 //
133 // could be PCM or MIC capture
134 //
135 if (Pin == PIN_WAVEIN)
136 {
137 // Base address for DMA registers.
138 m_ulBDAddr = PI_BDBAR;
139 }
140 else
141 {
142 // Base address for DMA registers.
143 m_ulBDAddr = MC_BDBAR;
144 }
145 }
146 else // render
147 {
148 // Base address for DMA registers.
149 m_ulBDAddr = PO_BDBAR;
150 }
151
152 //
153 // Reset the DMA and set the BD list pointer.
154 //
155 ResetDMA ();
156
157 //
158 // Now set the requested sample rate. In case of a failure, the object
159 // gets destroyed and releases all memory etc.
160 //
161 ntStatus = SetFormat (DataFormat_);
162 if (!NT_SUCCESS (ntStatus))
163 {
164 DOUT (DBG_ERROR, ("Stream init SetFormat call failed!"));
165 return ntStatus;
166 }
167
168 //
169 // Initialize the device state.
170 //
171 m_PowerState = PowerDeviceD0;
172
173 //
174 // Call miniport specific init routine
175 //
176 ntStatus = Init_();
177 if (!NT_SUCCESS (ntStatus))
178 {
179 return ntStatus;
180 }
181
182 //
183 // Setup the Buffer Descriptor Base Address (BDBA) register.
184 //
185 WriteReg32(0, BDList_PhysAddr.LowPart);
186
187 //
188 // Pass the ServiceGroup pointer to portcls.
189 //
190 obj_AddRef(ServiceGroup, (PVOID *)ServiceGroup_);
191
192 //
193 // Store the stream pointer, it is used by the ISR.
194 //
195 Miniport->Streams[Pin/2] = this;
196
197 return STATUS_SUCCESS;
198 }
199
200
~CMiniportStream()201 CMiniportStream::~CMiniportStream()
202 {
203 if (Miniport)
204 {
205 //
206 // Disable interrupts and stop DMA just in case.
207 //
208 if (Miniport->AdapterCommon)
209 {
210 WriteReg8 (X_CR, (UCHAR)0);
211
212 //
213 // Update also the topology miniport if this was the render stream.
214 //
215 if (Miniport->AdapterCommon->GetMiniportTopology () &&
216 (Pin == PIN_WAVEOUT))
217 {
218 Miniport->AdapterCommon->GetMiniportTopology ()->SetCopyProtectFlag (FALSE);
219 }
220 }
221
222 //
223 // Remove stream from miniport Streams array.
224 //
225 if (Miniport->Streams[Pin/2] == this)
226 {
227 Miniport->Streams[Pin/2] = NULL;
228 }
229 }
230
231 obj_Release((PVOID *)&Miniport);
232 obj_Release((PVOID *)&ServiceGroup);
233 obj_Release((PVOID *)&PortStream);
234 }
235
236
237 /*****************************************************************************
238 * CMiniportStream::SetContentId
239 *****************************************************************************
240 * This routine gets called by drmk.sys to pass the content to the driver.
241 * The driver has to enforce the rights passed.
242 */
STDMETHODIMP_(NTSTATUS)243 STDMETHODIMP_(NTSTATUS) CMiniportStream::SetContentId
244 (
245 _In_ ULONG contentId,
246 _In_ PCDRMRIGHTS drmRights
247 )
248 {
249 PAGED_CODE ();
250
251 DOUT (DBG_PRINT, ("[CMiniportStream::SetContentId]"));
252
253 UNREFERENCED_PARAMETER(contentId);
254
255 //
256 // If "drmRights->DigitalOutputDisable" is set, we need to disable S/P-DIF.
257 // Currently, we don't have knowledge about the S/P-DIF interface. However,
258 // in case you expanded the driver with S/P-DIF features you need to disable
259 // S/P-DIF or fail SetContentId. If you have HW that has S/P-DIF turned on
260 // by default and you don't know how to turn off (or you cannot do that)
261 // then you must fail SetContentId.
262 //
263 // In our case, we assume the codec has no S/P-DIF or disabled S/P-DIF by
264 // default, so we can ignore the flag.
265 //
266 // Store the copyright flag. We have to disable PCM recording if it's set.
267 //
268 if (!Miniport->AdapterCommon->GetMiniportTopology ())
269 {
270 DOUT (DBG_ERROR, ("Topology pointer not set!"));
271 return STATUS_UNSUCCESSFUL;
272 }
273 else
274 {
275 Miniport->AdapterCommon->GetMiniportTopology ()->
276 SetCopyProtectFlag (drmRights->CopyProtect);
277 }
278
279 //
280 // We assume that if we can enforce the rights, that the old content
281 // will be destroyed. We don't need to store the content id since we
282 // have only one playback channel, so we are finished here.
283 //
284
285 return STATUS_SUCCESS;
286 }
287
PowerChangeNotify(IN POWER_STATE NewState)288 void CMiniportStream::PowerChangeNotify
289 (
290 IN POWER_STATE NewState
291 )
292 {
293 DOUT (DBG_PRINT, ("[CMiniportStream::PowerChangeNotify]"));
294
295 //
296 // We don't have to check the power state, that's already done by the wave
297 // miniport.
298 //
299
300 DOUT (DBG_POWER, ("Changing state to D%d.",
301 (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
302
303 switch (NewState.DeviceState)
304 {
305 case PowerDeviceD0:
306 //
307 // If we are coming from D2 or D3 we have to restore the registers cause
308 // there might have been a power loss.
309 //
310 if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2))
311 {
312 PowerChangeNotify_(NewState);
313 }
314 break;
315
316 case PowerDeviceD1:
317 // Here we do nothing. The device has still enough power to keep all
318 // it's register values.
319 break;
320
321 case PowerDeviceD2:
322 case PowerDeviceD3:
323 //
324 // If we power down to D2 or D3 we might loose power, so we have to be
325 // aware of the DMA engine resetting. In that case a play would start
326 // with scatter gather entry 0 (the current index is read only).
327 // This is fine with the RT port.
328 //
329
330 PowerChangeNotify_(NewState);
331
332 break;
333 }
334
335 //
336 // Save the new state. This local value is used to determine when to
337 // cache property accesses and when to permit the driver from accessing
338 // the hardware.
339 //
340 m_PowerState = NewState.DeviceState;
341 DOUT (DBG_POWER, ("Entering D%d",
342 (ULONG)m_PowerState - (ULONG)PowerDeviceD0));
343
344 }
345
346 /*
347 *****************************************************************************
348 * This routine tests for proper data format (calls wave miniport) and sets
349 * or changes the stream data format.
350 * To figure out if the codec supports the sample rate, we just program the
351 * sample rate and read it back. If it matches we return happy, if not then
352 * we restore the sample rate and return unhappy.
353 * We fail this routine if we are currently running (playing or recording).
354 */
STDMETHODIMP_(NTSTATUS)355 STDMETHODIMP_(NTSTATUS) CMiniportStream::SetFormat
356 (
357 _In_ PKSDATAFORMAT Format
358 )
359 {
360 PAGED_CODE ();
361
362 ASSERT (Format);
363
364 ULONG TempRate;
365 DWORD dwControlReg;
366
367 DOUT (DBG_PRINT, ("[CMiniportStream::SetFormat]"));
368
369 //
370 // Change sample rate when we are in the stop or pause states - not
371 // while running!
372 //
373 if (DMAEngineState == DMA_ENGINE_ON)
374 {
375 return STATUS_UNSUCCESSFUL;
376 }
377
378 //
379 // Ensure format falls in proper range and is supported.
380 //
381 NTSTATUS ntStatus = Miniport->TestDataFormat (Format, Pin);
382 if (!NT_SUCCESS (ntStatus))
383 return ntStatus;
384
385 //
386 // Retrieve wave format portion.
387 //
388 PWAVEFORMATPCMEX waveFormat = (PWAVEFORMATPCMEX)(Format + 1);
389
390 //
391 // Save current rate in this context.
392 //
393 TempRate = waveFormat->Format.nSamplesPerSec;
394
395 //
396 // Check if we have a codec with one sample rate converter and there are streams
397 // already open.
398 //
399 if (Miniport->Streams[PIN_WAVEIN_OFFSET] && Miniport->Streams[PIN_WAVEOUT_OFFSET] &&
400 !Miniport->AdapterCommon->GetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES))
401 {
402 //
403 // Figure out at which sample rate the other stream is running.
404 //
405 ULONG ulFrequency;
406
407 if (Miniport->Streams[PIN_WAVEIN_OFFSET] == this)
408 ulFrequency = Miniport->Streams[PIN_WAVEOUT_OFFSET]->CurrentRate;
409 else
410 ulFrequency = Miniport->Streams[PIN_WAVEIN_OFFSET]->CurrentRate;
411
412 //
413 // Check if this sample rate is requested sample rate.
414 //
415 if (ulFrequency != TempRate)
416 {
417 return STATUS_UNSUCCESSFUL;
418 }
419 }
420
421 //
422 // Program the AC97 to support n channels.
423 //
424 if (Pin == PIN_WAVEOUT)
425 {
426 dwControlReg = Miniport->AdapterCommon->ReadBMControlRegister32 (GLOB_CNT);
427 dwControlReg = (dwControlReg & 0x03F) |
428 (((waveFormat->Format.nChannels >> 1) - 1) * GLOB_CNT_PCM4);
429 Miniport->AdapterCommon->WriteBMControlRegister (GLOB_CNT, dwControlReg);
430 }
431
432 //
433 // Check for rate support by hardware. If it is supported, then update
434 // hardware registers else return not implemented and audio stack will
435 // handle it.
436 //
437 if (Capture)
438 {
439 if (Pin == PIN_WAVEIN)
440 {
441 ntStatus = Miniport->AdapterCommon->
442 ProgramSampleRate (AC97REG_RECORD_SAMPLERATE, TempRate);
443 }
444 else
445 {
446 ntStatus = Miniport->AdapterCommon->
447 ProgramSampleRate (AC97REG_MIC_SAMPLERATE, TempRate);
448 }
449 }
450 else
451 {
452 //
453 // In the playback case we might need to update several DACs
454 // with the new sample rate.
455 //
456 ntStatus = Miniport->AdapterCommon->
457 ProgramSampleRate (AC97REG_FRONT_SAMPLERATE, TempRate);
458
459 if (Miniport->AdapterCommon->GetNodeConfig (NODEC_SURROUND_DAC_PRESENT))
460 {
461 ntStatus = Miniport->AdapterCommon->
462 ProgramSampleRate (AC97REG_SURROUND_SAMPLERATE, TempRate);
463 }
464 if (Miniport->AdapterCommon->GetNodeConfig (NODEC_LFE_DAC_PRESENT))
465 {
466 ntStatus = Miniport->AdapterCommon->
467 ProgramSampleRate (AC97REG_LFE_SAMPLERATE, TempRate);
468 }
469 }
470
471 if (NT_SUCCESS (ntStatus))
472 {
473 //
474 // print information and save the format information.
475 //
476 DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)Format;
477 CurrentRate = TempRate;
478 NumberOfChannels = waveFormat->Format.nChannels;
479 }
480
481 return ntStatus;
482 }
483
484 /*****************************************************************************
485 * Non paged code begins here
486 *****************************************************************************
487 */
488
489 #ifdef _MSC_VER
490 #pragma code_seg()
491 #endif
492
UpdateDMA(void)493 UCHAR CMiniportStream::UpdateDMA (void)
494 {
495 // get X_CR register value
496 UCHAR RegisterValue = ReadReg8(X_CR);
497 UCHAR RegisterValueNew = RegisterValue & ~CR_RPBM;
498 if(DMAEngineState == DMA_ENGINE_ON)
499 RegisterValueNew |= CR_RPBM;
500
501 // write X_CR register value
502 if(RegisterValue != RegisterValueNew)
503 WriteReg8(X_CR, RegisterValueNew);
504 return RegisterValueNew;
505 }
506
507
GetBuffPos(DWORD * buffPos)508 int CMiniportStream::GetBuffPos
509 (
510 DWORD* buffPos
511 )
512 {
513 int nCurrentIndex;
514 DWORD RegisterX_PICB;
515
516 if (DMAEngineState == DMA_ENGINE_OFF)
517 {
518 *buffPos = 0;
519 return 0;
520 }
521
522 //
523 // Repeat this until we get the same reading twice. This will prevent
524 // jumps when we are near the end of the buffer.
525 //
526 do
527 {
528 nCurrentIndex = ReadReg8(X_CIV);
529
530 RegisterX_PICB = ReadReg16 (X_PICB);
531 } while (nCurrentIndex != (int)ReadReg8(X_CIV));
532
533 *buffPos = (BDList[nCurrentIndex].wLength - RegisterX_PICB) * 2;
534 return nCurrentIndex;
535 }
536
537 /*****************************************************************************
538 * CMiniportStream::ResetDMA
539 *****************************************************************************
540 * This routine resets the Run/Pause bit in the control register. In addition, it
541 * resets all DMA registers contents.
542
543 */
ResetDMA(void)544 void CMiniportStream::ResetDMA (void)
545 {
546 DOUT (DBG_PRINT, ("ResetDMA"));
547
548 //
549 // Turn off DMA engine (or make sure it's turned off)
550 //
551 DMAEngineState = DMA_ENGINE_OFF;
552 UCHAR RegisterValue = UpdateDMA();
553
554 //
555 // Reset all register contents.
556 //
557 RegisterValue |= CR_RR;
558 WriteReg8(X_CR, RegisterValue);
559
560 //
561 // Wait until reset condition is cleared by HW; should not take long.
562 //
563 ULONG count = 0;
564 BOOL bTimedOut = TRUE;
565 do
566 {
567 if (!(ReadReg8(X_CR) & CR_RR))
568 {
569 bTimedOut = FALSE;
570 break;
571 }
572 KeStallExecutionProcessor (1);
573 } while (count++ < 10);
574
575 if (bTimedOut)
576 {
577 DOUT (DBG_ERROR, ("ResetDMA TIMEOUT!!"));
578 }
579
580 //
581 // We only want interrupts upon completion.
582 //
583 RegisterValue = CR_IOCE | CR_LVBIE;
584 WriteReg8(X_CR, RegisterValue);
585
586 //
587 // Setup the Buffer Descriptor Base Address (BDBA) register.
588 //
589 WriteReg32(0, BDList_PhysAddr.LowPart);
590 }
591
592 /*****************************************************************************
593 * CMiniportStream::ResumeDMA
594 *****************************************************************************
595 * This routine sets the Run/Pause bit for the particular DMA engine to resume
596 * it after it's been paused. This assumes that DMA registers content have
597 * been preserved.
598 */
ResumeDMA(ULONG state)599 void CMiniportStream::ResumeDMA (ULONG state)
600 {
601 DOUT (DBG_PRINT, ("ResumeDMA"));
602
603 DMAEngineState |= state;
604 UpdateDMA();
605 }
606
607 /*****************************************************************************
608 * CMiniportStream::PauseDMA
609 *****************************************************************************
610 * This routine pauses a hardware stream by reseting the Run/Pause bit in the
611 * control registers, leaving DMA registers content intact so that the stream
612 * can later be resumed.
613 */
PauseDMA(void)614 void CMiniportStream::PauseDMA (void)
615 {
616 DOUT (DBG_PRINT, ("PauseDMA"));
617
618 DMAEngineState &= DMA_ENGINE_PAUSE;
619 UpdateDMA();
620 }
621
622
WriteReg8(ULONG addr,UCHAR data)623 void CMiniportStream::WriteReg8(ULONG addr, UCHAR data) { Miniport->AdapterCommon->
624 WriteBMControlRegister (m_ulBDAddr + addr, data); }
WriteReg16(ULONG addr,USHORT data)625 void CMiniportStream::WriteReg16(ULONG addr, USHORT data) { Miniport->AdapterCommon->
626 WriteBMControlRegister (m_ulBDAddr + addr, data); }
WriteReg32(ULONG addr,ULONG data)627 void CMiniportStream::WriteReg32(ULONG addr, ULONG data) { Miniport->AdapterCommon->
628 WriteBMControlRegister (m_ulBDAddr + addr, data); }
ReadReg8(ULONG addr)629 UCHAR CMiniportStream::ReadReg8(ULONG addr) { return Miniport->AdapterCommon->
630 ReadBMControlRegister8 (m_ulBDAddr + addr); }
ReadReg16(ULONG addr)631 USHORT CMiniportStream::ReadReg16(ULONG addr) { return Miniport->AdapterCommon->
632 ReadBMControlRegister16 (m_ulBDAddr + addr); }
ReadReg32(ULONG addr)633 ULONG CMiniportStream::ReadReg32(ULONG addr) { return Miniport->AdapterCommon->
634 ReadBMControlRegister32 (m_ulBDAddr + addr); }
635