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