Sync to trunk head (r47736)
[reactos.git] / drivers / wdm / audio / drivers / CMIDriver / common.cpp
1 /*
2 Copyright (c) 2006-2007 dogbert <dogber1@gmail.com>
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "common.hpp"
29
30 #ifdef _MSC_VER
31 #pragma code_seg("PAGE") /* warning - ignored by GCC compiler */
32 #endif
33
34 NTSTATUS
35 NewCMIAdapter(
36 PUNKNOWN *Unknown,
37 REFCLSID,
38 PUNKNOWN UnknownOuter,
39 POOL_TYPE PoolType)
40 {
41 #if 1
42 //PAGED_CODE();
43 DBGPRINT(("NewCMIAdapter()"));
44 ASSERT (Unknown);
45 #endif
46 STD_CREATE_BODY_(CCMIAdapter, Unknown, UnknownOuter, PoolType, PCMIADAPTER);
47 }
48
49
50 STDMETHODIMP_(NTSTATUS) CCMIAdapter::init(PRESOURCELIST ResourceList, PDEVICE_OBJECT aDeviceObject)
51 {
52 #if 0
53 //PAGED_CODE();
54 //ASSERT(ResourceList);
55 //ASSERT(aDeviceObject);
56 //ASSERT(ResourceList->FindTranslatedPort(0));
57 DBGPRINT(("CCMIAdapter[%p]::init()", this));
58 #endif
59
60 NTSTATUS ntStatus = STATUS_SUCCESS;
61
62 RtlFillMemory(&mixerCache, 0xFF, 0xFF);
63 RtlFillMemory(&cm, sizeof(cm), 0x00);
64
65 DeviceObject = aDeviceObject;
66
67 cm.IOBase = 0;
68 for ( UINT i=0; i < ResourceList->NumberOfPorts(); i++ ) {
69 if (ResourceList->FindTranslatedPort(i)->u.Port.Length == 0x100) {
70 cm.IOBase = (UInt32*)ResourceList->FindTranslatedPort(i)->u.Port.Start.QuadPart;
71 }
72 }
73
74 if (cm.IOBase == 0) {
75 return STATUS_INSUFFICIENT_RESOURCES;
76 }
77 cm.MPUBase = 0;
78
79 #ifdef WAVERT
80 INFOPRINT(("Driver Version: %s-WAVERT", CMIVERSION));
81 #else
82 INFOPRINT(("Driver Version: %s", CMIVERSION));
83 #endif
84 INFOPRINT(("Configuration:"));
85 INFOPRINT((" IO Base: 0x%X", cm.IOBase));
86 INFOPRINT((" MPU Base: 0x%X", cm.MPUBase));
87
88 if (!queryChip()) {
89 return STATUS_INSUFFICIENT_RESOURCES;
90 }
91
92 INFOPRINT((" Chip Version: %d", cm.chipVersion));
93 INFOPRINT((" Max Channels: %d", cm.maxChannels));
94 INFOPRINT((" CanAC3HW: %d", cm.canAC3HW));
95
96 resetController();
97
98 ntStatus = PcNewInterruptSync(&(InterruptSync), NULL, ResourceList, 0, InterruptSyncModeNormal);
99 if (!NT_SUCCESS(ntStatus) || !(InterruptSync)) {
100 DBGPRINT(("Failed to create an interrupt sync!"));
101 return STATUS_INSUFFICIENT_RESOURCES;
102 }
103 ntStatus = InterruptSync->RegisterServiceRoutine(InterruptServiceRoutine, (PVOID)this, FALSE);
104 if (!NT_SUCCESS(ntStatus)) {
105 DBGPRINT(("Failed to register ISR!"));
106 return ntStatus;
107 }
108
109 ntStatus = InterruptSync->Connect();
110 if (!NT_SUCCESS(ntStatus)) {
111 DBGPRINT(("Failed to connect the ISR with InterruptSync!"));
112 return ntStatus;
113 }
114
115 // Initialize the device state.
116 CurrentPowerState = PowerDeviceD0;
117
118 return ntStatus;
119 }
120
121
122 CCMIAdapter::~CCMIAdapter()
123 {
124 //PAGED_CODE ();
125 DBGPRINT(("CCMIAdapter[%p]::~CCMIAdapter()", this));
126
127 if (InterruptSync) {
128 InterruptSync->Disconnect();
129 InterruptSync->Release();
130 InterruptSync = NULL;
131 }
132 }
133
134 STDMETHODIMP_(NTSTATUS) CCMIAdapter::QueryInterface(REFIID Interface, PVOID* Object)
135 {
136 //PAGED_CODE();
137
138 DBGPRINT(("CCMIAdapter[%p]::NonDelegatingQueryInterface()", this));
139
140 //ASSERT(Object);
141
142 // Is it IID_IUnknown?
143 if (IsEqualGUIDAligned (Interface, IID_IUnknown)) {
144 *Object = (PVOID)(PUNKNOWN)(PCMIADAPTER)this;
145 }
146 else
147 // or IID_IAdapterCommon ...
148 if (IsEqualGUIDAligned (Interface, IID_ICMIAdapter)) {
149 *Object = (PVOID)(PCMIADAPTER)this;
150 } else
151 // or IID_IAdapterPowerManagement ...
152 if (IsEqualGUIDAligned (Interface, IID_IAdapterPowerManagement)) {
153 *Object = (PVOID)(PADAPTERPOWERMANAGEMENT)this;
154 } else {
155 // nothing found, must be an unknown interface.
156 *Object = NULL;
157 return STATUS_INVALID_PARAMETER;
158 }
159
160 //
161 // We reference the interface for the caller.
162 //
163 ((PUNKNOWN)*Object)->AddRef();
164 return STATUS_SUCCESS;
165 }
166
167 bool CCMIAdapter::queryChip()
168 {
169 //PAGED_CODE();
170 DBGPRINT(("CCMIAdapter[%p]::queryChip()", this));
171
172 UInt32 version = readUInt32(REG_INTHLDCLR) & VERSION_MASK;
173 if (version == 0xFFFFFFFF) {
174 return false;
175 }
176 if (version) {
177 if (version & VERSION_68) {
178 cm.chipVersion = 68;
179 cm.maxChannels = 8;
180 cm.canAC3HW = true;
181 cm.hasDualDAC = true;
182 cm.canMultiChannel = true;
183 return true;
184 }
185 if (version & VERSION_55) {
186 cm.chipVersion = 55;
187 cm.maxChannels = 6;
188 cm.canAC3HW = true;
189 cm.hasDualDAC = true;
190 cm.canMultiChannel = true;
191 return true;
192 }
193 if (version & VERSION_39) {
194 cm.chipVersion = 39;
195 if (version & VERSION_39_6) {
196 cm.maxChannels = 6;
197 } else {
198 cm.maxChannels = 4;
199 }
200 cm.canAC3HW = true;
201 cm.hasDualDAC = true;
202 cm.canMultiChannel = true;
203 return true;
204 }
205 } else {
206 version = readUInt32(REG_CHFORMAT) & VERSION_37;
207 if (!version) {
208 cm.chipVersion = 33;
209 cm.maxChannels = 2;
210 if (cm.doAC3SW) {
211 cm.canAC3SW = true;
212 } else {
213 cm.canAC3HW = true;
214 }
215 cm.hasDualDAC = true;
216 return true;
217 } else {
218 cm.chipVersion = 37;
219 cm.maxChannels = 2;
220 cm.canAC3HW = true;
221 cm.hasDualDAC = 1;
222 return true;
223 }
224 }
225 return false;
226 }
227
228 void CCMIAdapter::resetMixer()
229 {
230 //PAGED_CODE();
231 DBGPRINT(("CCMIAdapter[%p]::resetMixer()", this));
232
233 writeMixer(0, 0);
234 setUInt8Bit(REG_MIXER1, EN_SPDI2DAC);
235 }
236
237 void CCMIAdapter::resetController()
238 {
239 #if 0
240 //PAGED_CODE();
241 DBGPRINT(("CCMIAdapter[%p]::resetController()", this));
242 #endif
243
244 writeUInt32(REG_INTHLDCLR, 0);
245
246 #if OUT_CHANNEL == 1
247 writeUInt32(REG_FUNCTRL0, ADC_CH0 | (RST_CH0 | RST_CH1));
248 writeUInt32(REG_FUNCTRL0, ADC_CH0 & ~(RST_CH0 | RST_CH1));
249 #else
250 writeUInt32(REG_FUNCTRL0, ADC_CH1 | (RST_CH0 | RST_CH1));
251 writeUInt32(REG_FUNCTRL0, ADC_CH1 & ~(RST_CH0 | RST_CH1));
252 #endif
253 KeStallExecutionProcessor(100L);
254
255 writeUInt32(REG_FUNCTRL0, 0);
256 writeUInt32(REG_FUNCTRL1, 0);
257
258 writeUInt32(REG_CHFORMAT, 0);
259 writeUInt32(REG_MISCCTRL, EN_DBLDAC);
260 #if OUT_CHANNEL == 1
261 setUInt32Bit(REG_MISCCTRL, XCHG_DAC);
262 #endif
263
264 setUInt32Bit(REG_FUNCTRL1, BREQ);
265
266 writeMixer(0, 0);
267
268 return;
269 }
270
271
272 STDMETHODIMP_(NTSTATUS) CCMIAdapter::activateMPU(ULONG* MPUBase)
273 {
274 UInt32 LegacyCtrl;
275 #if 0
276 //PAGED_CODE();
277 DBGPRINT(("CCMIAdapter[%p]::activateMPU(%X)", this, MPUBase));
278 #endif
279
280 switch ((LONGLONG)MPUBase) {
281 case 0x300: LegacyCtrl = UART_300; break;
282 case 0x310: LegacyCtrl = UART_310; break;
283 case 0x320: LegacyCtrl = UART_320; break;
284 case 0x330: LegacyCtrl = UART_330; break; // UART_330 == 0
285 default: LegacyCtrl = 0xFFFFFFFF; break;
286 }
287 if (LegacyCtrl < 0xFFFFFFFF) {
288 cm.MPUBase = MPUBase;
289 setUInt32Bit(REG_FUNCTRL1, EN_UART);
290 writeUInt32(REG_LEGACY, LegacyCtrl);
291 return STATUS_SUCCESS;
292 }
293
294 return STATUS_UNSUCCESSFUL;
295 }
296
297 // "The code for this method must reside in paged memory.", IID_IAdapterPowerManagement.PowerChangeState() docs
298 // XP's order of power states when going to hibernate: D3 -> D0, waking up: D0 -> D3.
299 STDMETHODIMP_(void) CCMIAdapter::PowerChangeState(POWER_STATE NewState)
300 {
301 #if 0
302 //PAGED_CODE();
303 DBGPRINT(("CCMIAdapter[%p]::PowerChangeState(%p)", this, NewState));
304 #endif
305
306 if (NewState.DeviceState == CurrentPowerState ) {
307 return;
308 }
309
310 switch (NewState.DeviceState) {
311 case PowerDeviceD0: // powering up, hardware access allowed
312 clearUInt32Bit(REG_MISCCTRL, PWD_CHIP);
313 cm.WaveMiniport->powerUp();
314 CurrentPowerState = NewState.DeviceState;
315 break;
316
317 case PowerDeviceD1: // powering down, hardware access still allowed
318 setUInt32Bit(REG_MISCCTRL, PWD_CHIP);
319 CurrentPowerState = NewState.DeviceState;
320 break;
321
322 case PowerDeviceD2: // sleep state - hardware access not allowed
323 case PowerDeviceD3: // hibernation state - hardware access not allowed
324 if (CurrentPowerState == PowerDeviceD0) {
325 cm.WaveMiniport->powerDown();
326 setUInt32Bit(REG_MISCCTRL, PWD_CHIP);
327 }
328 CurrentPowerState = NewState.DeviceState;
329 break;
330 default: // unknown power state
331 break;
332 }
333 }
334
335 STDMETHODIMP_(NTSTATUS) CCMIAdapter::QueryPowerChangeState(POWER_STATE NewStateQuery)
336 {
337 #if 0
338 //PAGED_CODE();
339 DBGPRINT(("CCMIAdapter[%p]::QueryPowerChangeState(%p)", this, NewStateQuery));
340 #endif
341 return STATUS_SUCCESS;
342 }
343
344 STDMETHODIMP_(NTSTATUS) CCMIAdapter::QueryDeviceCapabilities(PDEVICE_CAPABILITIES PowerDeviceCaps)
345 {
346 #if 0
347 //PAGED_CODE();
348 DBGPRINT(("CCMIAdapter[%p]::QueryDeviceCapabilities(%p)", this, PowerDeviceCaps));
349 #endif
350 return STATUS_SUCCESS;
351 }
352
353 STDMETHODIMP_(NTSTATUS) CCMIAdapter::loadSBMixerFromMemory()
354 {
355 UInt8 sbIndex[] = { 0x04, 0x0A, 0x22, 0x28, 0x2E, 0x30, 0x31, 0x32, 0x33, 0x36, 0x37, 0x38,
356 0x39, 0x3A, 0x3C, 0x3D, 0x3E, 0xF0 };
357
358 #if 0
359 //PAGED_CODE();
360 DBGPRINT(("CCMIAdapter[%p]::loadSBMixerFromMemory()", this));
361 #endif
362 for ( UINT i = 0; i < (sizeof(sbIndex)/sizeof(sbIndex[0])); i++ ) {
363 writeUInt8(REG_SBINDEX, sbIndex[i]);
364 writeUInt8(REG_SBDATA, mixerCache[i]);
365 }
366
367 return STATUS_SUCCESS;
368 }
369
370 /*
371 ** non-paged code below
372 */
373 #ifdef _MSC_VER
374 #pragma code_seg() /* warning - ignored by GCC compiler */
375 #endif
376
377 STDMETHODIMP_(UInt8) CCMIAdapter::readUInt8(UInt8 reg)
378 {
379 return READ_PORT_UCHAR((PUCHAR)(reinterpret_cast<PUCHAR>(cm.IOBase) + reg));
380 }
381
382 STDMETHODIMP_(void) CCMIAdapter::writeUInt8(UInt8 cmd, UInt8 value)
383 {
384 WRITE_PORT_UCHAR((PUCHAR)(reinterpret_cast<PUCHAR>(cm.IOBase) + cmd), value);
385 }
386
387 STDMETHODIMP_(void) CCMIAdapter::setUInt8Bit(UInt8 reg, UInt8 flag)
388 {
389 writeUInt8(reg, readUInt8(reg) | flag);
390 }
391
392 STDMETHODIMP_(void) CCMIAdapter::clearUInt8Bit(UInt8 reg, UInt8 flag)
393 {
394 writeUInt8(reg, readUInt8(reg) & ~flag);
395 }
396
397 STDMETHODIMP_(UInt16) CCMIAdapter::readUInt16(UInt8 reg)
398 {
399 return READ_PORT_USHORT((PUSHORT)(reinterpret_cast<PUCHAR>(cm.IOBase) + reg));
400 }
401
402 STDMETHODIMP_(void) CCMIAdapter::writeUInt16(UInt8 cmd, UInt16 value)
403 {
404 WRITE_PORT_USHORT((PUSHORT)(reinterpret_cast<PUCHAR>(cm.IOBase) + cmd), value);
405 }
406
407
408 STDMETHODIMP_(UInt32) CCMIAdapter::readUInt32(UInt8 reg)
409 {
410 return READ_PORT_ULONG((PULONG)(reinterpret_cast<PUCHAR>(cm.IOBase) + reg));
411 }
412
413 STDMETHODIMP_(void) CCMIAdapter::writeUInt32(UInt8 cmd, UInt32 value)
414 {
415 WRITE_PORT_ULONG((PULONG)(reinterpret_cast<PUCHAR>(cm.IOBase) + cmd), value);
416 }
417
418 STDMETHODIMP_(void) CCMIAdapter::setUInt32Bit(UInt8 reg, UInt32 flag)
419 {
420 writeUInt32(reg, readUInt32(reg) | flag);
421 }
422
423 STDMETHODIMP_(void) CCMIAdapter::clearUInt32Bit(UInt8 reg, UInt32 flag)
424 {
425 writeUInt32(reg, readUInt32(reg) & ~flag);
426 }
427
428 STDMETHODIMP_(UInt8) CCMIAdapter::readMixer(UInt8 index)
429 {
430 if (mixerCache[index] == 0xFF) {
431 writeUInt8(REG_SBINDEX, index);
432 mixerCache[index] = readUInt8(REG_SBDATA);
433 }
434 return mixerCache[index];
435 }
436
437 STDMETHODIMP_(void) CCMIAdapter::writeMixer(UInt8 index, UInt8 value)
438 {
439 if (value != mixerCache[index]) {
440 mixerCache[index] = value;
441 writeUInt8(REG_SBINDEX, index);
442 writeUInt8(REG_SBDATA, value);
443 }
444 }
445
446 STDMETHODIMP_(void) CCMIAdapter::setMixerBit(UInt8 index, UInt8 flag)
447 {
448 writeMixer(index, readMixer(index) | flag);
449 }
450
451 STDMETHODIMP_(void) CCMIAdapter::clearMixerBit(UInt8 index, UInt8 flag)
452 {
453 writeMixer(index, readMixer(index) & ~flag);
454 }
455
456 NTSTATUS NTAPI CCMIAdapter::InterruptServiceRoutine(PINTERRUPTSYNC InterruptSync, PVOID DynamicContext)
457 {
458 UInt32 status, mask = 0;
459
460 CCMIAdapter *CMIAdapter = (CCMIAdapter *)DynamicContext;
461
462 #if 0
463 //ASSERT(InterruptSync);
464 //ASSERT(DynamicContext);
465 #endif
466
467 if (!(CMIAdapter->cm.WaveMiniport)) {
468 return STATUS_UNSUCCESSFUL;
469 }
470
471 status = CMIAdapter->readUInt32(REG_INT_STAT);
472
473 if ((!(status & INT_PENDING)) || (status == 0xFFFFFFFF)) {
474 return STATUS_UNSUCCESSFUL;
475 }
476
477 if (status & INT_CH0) {
478 mask |= EN_CH0_INT;
479 #if OUT_CHANNEL == 0
480 CMIAdapter->cm.WaveMiniport->ServiceWaveISR(PCM_OUT_STREAM);
481 #endif
482 #if IN_CHANNEL == 0
483 CMIAdapter->cm.WaveMiniport->ServiceWaveISR(PCM_IN_STREAM);
484 #endif
485 }
486 if (status & INT_CH1) {
487 mask |= EN_CH1_INT;
488 #if OUT_CHANNEL == 1
489 CMIAdapter->cm.WaveMiniport->ServiceWaveISR(PCM_OUT_STREAM);
490 #endif
491 #if IN_CHANNEL == 1
492 CMIAdapter->cm.WaveMiniport->ServiceWaveISR(PCM_IN_STREAM);
493 #endif
494 }
495 #ifdef UART
496 if (status & INT_UART) {
497 // the UART miniport should catch / have caught the interrupt
498 return STATUS_UNSUCCESSFUL;
499 }
500 #endif
501
502 CMIAdapter->clearUInt32Bit(REG_INTHLDCLR, mask);
503 CMIAdapter->setUInt32Bit(REG_INTHLDCLR, mask);
504
505 return STATUS_SUCCESS;
506 }