hwctxt.cpp
上传用户:qiulin1960
上传日期:2013-10-16
资源大小:2844k
文件大小:54k
源码类别:

Windows CE

开发平台:

Windows_Unix

  1. //
  2. // Copyright (c) Microsoft Corporation.  All rights reserved.
  3. //
  4. //
  5. // Use of this source code is subject to the terms of the Microsoft end-user
  6. // license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
  7. // If you did not accept the terms of the EULA, you are not authorized to use
  8. // this source code. For a copy of the EULA, please see the LICENSE.RTF on your
  9. // install media.
  10. //
  11. /*++
  12. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  13. ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  14. THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  15. PARTICULAR PURPOSE.
  16.    
  17. Module Name: HWCTXT.CPP
  18. Abstract: Platform dependent code for the mixing audio driver.
  19. Notes: The following file contains all the hardware specific code
  20. for the mixing audio driver.  This code's primary responsibilities
  21. are:
  22. * Initialize audio hardware (including codec chip)
  23. * Schedule DMA operations (move data from/to buffers)
  24. * Handle audio interrupts
  25. All other tasks (mixing, volume control, etc.) are handled by the "upper"
  26. layers of this driver.
  27. ****** IMPORTANT ******
  28. Presently, audio input (i.e. RECORD functionality) IS NOT implemented.  On 
  29. this platform, in order to record the TLV320AIC codec chip needs to be 
  30. configured as MASTER and the CPU's I2S controller configured as SLAVE.  
  31. Unfortunately, a hardware bug in the current Samsung CPU samples prevents 
  32. SLAVE mode from working properly.  As a result, only audio playback is 
  33. supported by configuring the CPU I2S controller as MASTER and the audio
  34. codec chip as SLAVE.  
  35. The final revision of the Samsung SC2440 CPU is expected to fix this 
  36. hardware bug.  Once this issue is resolved, the CPU's I2S controller 
  37. (look at I2S.CPP) should be configured in SLAVE mode and the audio codec 
  38. chip (see InitCodec() below) should be configured as MASTER.  With these
  39. changes, the following routines need to be implemented:
  40. InitInputDMA() - initialize the input DMA channel
  41. StartInputDMA() - starts DMA operations on the input DMA channel
  42. StopInputDMA() - stops any current DMA operations on the input DMA channel
  43. For the SC2440 CPU, DMA channel 2 can be used for both input and output.  In this,
  44. configuration, however, only one type operation (input or output) can execute.  In 
  45. order to implement simultaneous playback and recording, two things must be done:
  46.  
  47. 1) Input DMA should be moved to DMA Channel 1; Output DMA still uses DMA Channel 2.
  48. 2) Step #3 in InterruptThread() needs to be implemented so that the DMA interrupt
  49.    source (input DMA or output DMA?) can be determined.  The interrupt source needs
  50.    to be determined so that the appropriate buffers can be copied (Steps #4,#5...etc.).
  51. Lastly, the m_OutputDMAStatus and m_InputDMAStatus variables shouldn't need to be modified.  
  52. The logic surrounding these drivers is simply used to determine which buffer (A or B) needs
  53. processing.
  54. Environment: PocketPC SMDK2440 Reference Platform:
  55. Hardware - Samsung SC2440 CPU
  56.    TI TLV320AIC audio codec chip where: 
  57. 1) SPI bus is used for control 
  58. 2) I2S bus is used for data (CPU is master
  59.    and codec chip is slave)
  60. Software - Windows CE 3.0 and later.    
  61. -*/
  62. #include "wavemain.h"
  63. #include <p2.h>
  64. #include "s2440.h"
  65. #include "dma.h"
  66. #include "hwctxt.h"
  67. #define DMA_FLAG 1
  68. #define AC97_DEBUG 0
  69. #define AC97MSG(a,b) RETAILMSG(AC97_DEBUG,b)
  70. #define DMA_CH_MIC 2
  71. #define DMA_CH_OUT 1
  72. #define WAIT_DMA_END 0
  73. #define DELAY_COUNT 0x1000
  74. int rec_mode=0;
  75. //-------------------------------- Global Variables --------------------------------------
  76. volatile AC97reg *v_pAC97regs = NULL; // AC97 control registers
  77. volatile IOPreg *v_pIOPregs = NULL; // GPIO registers (needed to enable I2S and SPI)
  78. volatile DMAreg *v_pDMAregs = NULL; // DMA registers (needed for I/O on I2S bus)
  79. volatile CLKPWRreg *g_pCLKPWRreg = NULL; // Clock power registers (needed to enable I2S and SPI clocks)
  80. volatile INTreg  *s2440INT  = NULL;
  81. HANDLE          g_hUTLObject = INVALID_HANDLE_VALUE;
  82. HardwareContext *g_pHWContext = NULL;
  83. void AC97_GPIO_Init();
  84. unsigned int delay_count;
  85. //----------------------------------------------------------------------------------------
  86. /*#ifdef DEBUG
  87. DBGPARAM dpCurSettings = {
  88.     TEXT("CONSOLE"), {
  89.         TEXT("0"),TEXT("1"),TEXT("2"),TEXT("3"),
  90.         TEXT("4"),TEXT("5"),TEXT("6"),TEXT("7"),
  91.         TEXT("8"),TEXT("9"),TEXT("10"),TEXT("11"),
  92.         TEXT("12"),TEXT("Function"),TEXT("Init"),TEXT("Error")},
  93.     0x8000  // Errors only, by default
  94. }; 
  95. #endif
  96. */
  97. // Static Functions
  98. volatile static int loop = S2440FCLK/100000;
  99. static void Delay(USHORT count)
  100. {
  101. volatile int i, j;
  102. for(;count > 0;count--)
  103. for(i=0;i < loop; i++) { j++; }
  104. }
  105. static void WriteCodecRegister(UCHAR Reg, USHORT Val)
  106. {
  107. v_pAC97regs->rAC_CODEC_CMD = (Reg << AC97_CMD_ADDR_SHIFT) | (Val << AC97_CMD_DATA_SHIFT);
  108. Delay(100);
  109. //Sleep(5);
  110. v_pAC97regs->rAC_CODEC_CMD |= (AC97_READ_COMMAND); //To receive SLOTREQ bits when VRA is '1'.
  111. }
  112. static USHORT ReadCodecRegister(UCHAR Reg)
  113. {
  114. USHORT retval;
  115. v_pAC97regs->rAC_CODEC_CMD = AC97_READ_COMMAND | (Reg << AC97_CMD_ADDR_SHIFT);
  116. Delay(100);
  117. //Sleep(5);
  118. retval = v_pAC97regs->rAC_CODEC_STAT & AC97_STAT_DATA_READ_MASK;
  119. return retval;
  120. }
  121. BOOL HardwareContext::CreateHWContext(DWORD Index)
  122. {
  123.     if (g_pHWContext)
  124.     {
  125.         return TRUE;
  126.     }
  127.     g_pHWContext = new HardwareContext;
  128.     if (!g_pHWContext)
  129.     {
  130.         return FALSE;
  131.     }
  132.     return g_pHWContext->Init(Index);
  133. }
  134. HardwareContext::HardwareContext()
  135. : m_InputDeviceContext(), m_OutputDeviceContext()
  136. {
  137.     InitializeCriticalSection(&m_Lock);
  138. }
  139. HardwareContext::~HardwareContext()
  140. {
  141.     DeleteCriticalSection(&m_Lock);
  142. }
  143. BOOL HardwareContext::Init(DWORD Index)
  144. {
  145. BOOL bRet = TRUE;
  146. //----- 1. Initialize the state/status variables -----
  147.     m_DriverIndex = Index;
  148.     m_IntrAudio         = SYSINTR_AUDIO;
  149.     m_InPowerHandler    = FALSE;
  150.     m_InputDMARunning   = FALSE;
  151.     m_OutputDMARunning  = FALSE;
  152. m_InputDMAStatus = DMA_CLEAR;
  153. m_OutputDMAStatus = DMA_CLEAR;
  154.     //----- 2. Map the necessary descriptory channel and control registers into the driver's virtual address space -----
  155. if(!MapRegisters())
  156. {
  157. DEBUGMSG(ZONE_ERROR, (TEXT("WAVEDEV.DLL:HardwareContext::Init() - Failed to map config registers.rn")));
  158. bRet = FALSE;
  159.         goto Exit;
  160. }
  161.     //----- 3. Map the DMA buffers into driver's virtual address space -----
  162.     if(!MapDMABuffers())
  163.     {
  164. DEBUGMSG(ZONE_ERROR, (TEXT("WAVEDEV.DLL:HardwareContext::Init() - Failed to map DMA buffers.rn")));
  165. bRet = FALSE;
  166.         goto Exit;
  167.     }
  168. //---- Configure the AC97 Controller
  169. AC97_Init();
  170.     //----- 4. Configure the Codec -----
  171.     InitCodec();
  172.     
  173. //----- 5. Initialize the interrupt thread -----
  174.     if (!InitInterruptThread())
  175.     {
  176. DEBUGMSG(ZONE_ERROR, (TEXT("WAVEDEV.DLL:HardwareContext::Init() - Failed to initialize interrupt thread.rn")));
  177. bRet = FALSE;
  178.         goto Exit;
  179.     }
  180.     //
  181.     // Power Manager expects us to init in D0.
  182.     // We are normally in D4 unless we are opened for play.
  183.     // Inform the PM.
  184.     //
  185.     m_Dx = D0;
  186.     //DevicePowerNotify(_T("WAV1:"),(_CEDEVICE_POWER_STATE)D4, POWER_NAME);
  187.     RETAILMSG(1,(_T("AC97AUDIO::Init.rn")));    
  188.     
  189. Exit:
  190.     return bRet;
  191. }
  192. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  193. Function: MapRegisters()
  194. Description: Maps the config registers used by both the SPI and
  195. I2S controllers.
  196. Notes: The SPI and I2S controllers both use the GPIO config
  197. registers, so these MUST be initialized FIRST.
  198. Returns: Boolean indicating success
  199. -------------------------------------------------------------------*/
  200. BOOL HardwareContext::MapRegisters()
  201. {
  202.     v_pAC97regs = (volatile AC97reg *)VirtualAlloc(0, sizeof(AC97reg), MEM_RESERVE, PAGE_NOACCESS);
  203. if (!v_pAC97regs)
  204. {
  205. AC97MSG(1, (TEXT("AC97reg: VirtualAlloc failed!rn")));
  206. return(FALSE);
  207. }
  208. if (!VirtualCopy((PVOID)v_pAC97regs, (PVOID)(AC97_BASE), sizeof(AC97reg), PAGE_READWRITE | PAGE_NOCACHE))
  209. {
  210. AC97MSG(1, (TEXT("AC97reg: VirtualCopy failed!rn")));
  211. return(FALSE);
  212. }
  213. v_pIOPregs = (volatile IOPreg *)VirtualAlloc(0,sizeof(IOPreg),MEM_RESERVE, PAGE_NOACCESS);
  214. if (!v_pIOPregs)
  215. {
  216. AC97MSG(1,(TEXT("IOPreg: VirtualAlloc failed!rn")));
  217. return FALSE;
  218. }
  219. if (!VirtualCopy((PVOID)v_pIOPregs,(PVOID)(IOP_BASE),sizeof(IOPreg), PAGE_READWRITE | PAGE_NOCACHE ))
  220. {
  221. AC97MSG(1,(TEXT("IOPreg: VirtualCopy failed!rn")));
  222. return FALSE;
  223. }
  224. v_pDMAregs = (volatile DMAreg *)VirtualAlloc(0,sizeof(DMAreg),MEM_RESERVE, PAGE_NOACCESS);
  225. if (!v_pDMAregs)
  226. {
  227. AC97MSG(1,(TEXT("DMAreg: VirtualAlloc failed!rn")));
  228. return FALSE;
  229. }
  230. if (!VirtualCopy((PVOID)v_pDMAregs,(PVOID)(DMA_BASE),sizeof(DMAreg), PAGE_READWRITE | PAGE_NOCACHE ))
  231. {
  232. AC97MSG(1,(TEXT("DMAreg: VirtualCopy failed!rn")));
  233. return FALSE;
  234. }
  235. s2440INT = (volatile INTreg *)VirtualAlloc(0,sizeof(INTreg),MEM_RESERVE, PAGE_NOACCESS);
  236. if (!v_pDMAregs)
  237. {
  238. AC97MSG(1,(TEXT("INTreg: VirtualAlloc failed!rn")));
  239. return FALSE;
  240. }
  241. if (!VirtualCopy((PVOID)s2440INT,(PVOID)(INT_BASE),sizeof(INTreg), PAGE_READWRITE | PAGE_NOCACHE ))
  242. {
  243. AC97MSG(1,(TEXT("INTreg: VirtualCopy failed!rn")));
  244. return FALSE;
  245. }
  246. g_pCLKPWRreg = (volatile CLKPWRreg *)VirtualAlloc(0,sizeof(CLKPWRreg),MEM_RESERVE, PAGE_NOACCESS);
  247. if (!g_pCLKPWRreg)
  248. {
  249. AC97MSG(1,(TEXT("DMAreg: VirtualAlloc failed!rn")));
  250. return FALSE;
  251. }
  252. if (!VirtualCopy((PVOID)g_pCLKPWRreg,(PVOID)(CLKPWR_BASE),sizeof(CLKPWRreg), PAGE_READWRITE | PAGE_NOCACHE ))
  253. {
  254. AC97MSG(1,(TEXT("DMAreg: VirtualCopy failed!rn")));
  255. return FALSE;
  256. }
  257.    
  258. return TRUE;
  259. }
  260. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  261. Function: Deinit()
  262. Description: Deinitializest the hardware: disables DMA channel(s), 
  263. clears any pending interrupts, powers down the audio
  264. codec chip, etc.
  265. Returns: Boolean indicating success
  266. -------------------------------------------------------------------*/
  267. BOOL HardwareContext::Deinit()
  268. {
  269. //----- 1. Disable the input/output channels -----
  270. // AUDIO_IN_DMA_DISABLE();
  271. AUDIO_OUT_DMA_DISABLE();
  272. //----- 2. Disable/clear DMA input/output interrupts -----
  273. AUDIO_IN_CLEAR_INTERRUPTS();
  274. AUDIO_OUT_CLEAR_INTERRUPTS();
  275. //----- 3. Turn the audio hardware off -----
  276.     AudioMute(DMA_CH_OUT | DMA_CH_MIC, TRUE);
  277.     //----- 4. Unmap the control registers and DMA buffers -----
  278.     UnmapRegisters();
  279. UnmapDMABuffers();
  280.     return TRUE;
  281. }
  282. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  283. Function: UnmapRegisters()
  284. Description: Unmaps the config registers used by both the SPI and
  285. I2S controllers.
  286. Notes: The SPI and I2S controllers both use the GPIO config
  287. registers, so these MUST be deinitialized LAST.
  288. Returns: Boolean indicating success
  289. -------------------------------------------------------------------*/
  290. BOOL HardwareContext::UnmapRegisters()
  291. {
  292. //----- 1. Free the fast driver-->driver calling mechanism object -----
  293. if(g_hUTLObject) 
  294. {
  295.         CloseHandle(g_hUTLObject);
  296. }
  297. return TRUE;
  298. }
  299. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  300. Function: MapDMABuffers()
  301. Description: Maps the DMA buffers used for audio input/output
  302. on the I2S bus.
  303. Returns: Boolean indicating success
  304. -------------------------------------------------------------------*/
  305. BOOL HardwareContext::MapDMABuffers()
  306. {
  307.     BOOL bSuccess=FALSE;
  308. PBYTE pTemp;
  309.     //----- 1. Allocate a block of virtual memory big enough to hold the DMA buffers -----
  310.     if(!(pTemp = (PBYTE)VirtualAlloc(0, AUDIO_DMA_PAGE_SIZE * 4, MEM_RESERVE, PAGE_NOACCESS)))
  311. {
  312. DEBUGMSG(ZONE_ERROR, (TEXT("WAVEDEV.DLL:HardwareContext::MapDMABuffers() - Unable to allocate memory for DMA buffers!rn")));
  313. goto MAP_ERROR;
  314. }
  315.     //----- 2. Map the physical DMA buffer to the virtual address we just allocated -----
  316. if(!VirtualCopy((LPVOID)pTemp, (LPVOID)AUDIO_DMA_BUFFER_BASE, (AUDIO_DMA_PAGE_SIZE * 4), 
  317. PAGE_READWRITE | PAGE_NOCACHE))
  318. {
  319. DEBUGMSG(ZONE_ERROR, (TEXT("WAVEDEV.DLL:HardwareContext::MapDMABuffers() - VirtualCopy() failed when binding DMA buffers.rn")));
  320. goto MAP_ERROR;
  321.     }
  322. //----- 3. Setup the DMA page pointers -----
  323. //    NOTE: Currently, input and output each have two DMA pages; these pages are used in a round-robin
  324. //  fashion so that the OS can read/write one buffer while the audio codec chip read/writes the
  325. //  other buffer.
  326.     m_Output_pbDMA_PAGES[0] = pTemp;
  327.     m_Output_pbDMA_PAGES[1] = pTemp + AUDIO_DMA_PAGE_SIZE;
  328.     m_Input_pbDMA_PAGES[0]  = pTemp + 2*AUDIO_DMA_PAGE_SIZE;
  329.     m_Input_pbDMA_PAGES[1]  = pTemp + 3*AUDIO_DMA_PAGE_SIZE;
  330.     return TRUE;
  331. MAP_ERROR:
  332. if(pTemp)
  333. VirtualFree(pTemp, 0, MEM_RELEASE);
  334. return FALSE;
  335. }
  336. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  337. Function: UnmapDMABuffers()
  338. Description: Unmaps the DMA buffers used for audio input/output
  339. on the I2S bus.
  340. Returns: Boolean indicating success
  341. -------------------------------------------------------------------*/
  342. BOOL HardwareContext::UnmapDMABuffers()
  343. {
  344. if(m_Output_pbDMA_PAGES[0])
  345. {
  346. VirtualFree((PVOID)m_Output_pbDMA_PAGES[0], 0, MEM_RELEASE);
  347. }
  348. return TRUE;
  349. }
  350. void AC97_GPIO_Init()
  351. {
  352. //----- 2. Configure the GPIO pins for AC97 mode -----
  353. //    
  354. //    AC_SDATA_OUT - GPE4
  355. //    AC_SDATA_IN - GPE3
  356. //    AC_nRESET - GPE2
  357. //    AC_BIT_CLK - GPE1
  358. //    AC_SYNC - GPE0
  359. //
  360. // Port Init for AC97
  361. //PG[4:0]=AC_SDATA_OUT:AC_SDATA_IN:AC_nRESET:AC_BIT_CLK:AC_SYNC
  362. v_pIOPregs->rGPECON = ((v_pIOPregs->rGPECON & 0xfffffc00) | 0x3ff);
  363. v_pIOPregs->rGPEUP = (v_pIOPregs->rGPEUP & ~(0x1f)) | 0x1f;
  364. //Delay(10);
  365. //Sleep(5);
  366. }
  367. BOOL HardwareContext::AC97_Init()
  368. {
  369. RETAILMSG(AC97_DEBUG,(_T("WAVDEV_AC97::AC97_Init()++rn")));
  370. //----- 1. IMPORTANT: By default, the internal clock is disabled.  To configure the controller ------
  371. //   we must first enable it.
  372. g_pCLKPWRreg->rCLKCON |= AC97_INTERNAL_CLOCK_ENABLE; // Enable the CPU clock to the AC97 controller
  373. AC97_GPIO_Init();
  374. v_pAC97regs->rAC_GLBCTRL = 0;
  375. Delay(10); //Sleep(5);
  376. // Write into the AC97 Global Control Register
  377. //Cold Reset 
  378. v_pAC97regs->rAC_GLBCTRL = 0x1; 
  379. Delay(10); //Sleep(5);
  380. v_pAC97regs->rAC_GLBCTRL = 0x0;
  381. Delay(10); //Sleep(5);
  382. //AC-link On
  383. v_pAC97regs->rAC_GLBCTRL = (1<<2);
  384. Delay(10); //Sleep(5);
  385. //Transfer data enable using AC-link
  386. v_pAC97regs->rAC_GLBCTRL = v_pAC97regs->rAC_GLBCTRL | (1<<3);
  387. Delay(10); //Sleep(5);
  388. // Disable the Codec ready Interrupt
  389. v_pAC97regs->rAC_GLBCTRL = v_pAC97regs->rAC_GLBCTRL | (1<<22);
  390. Delay(10); //Sleep(5);
  391. //while (!(v_pAC97regs->rAC_GLBSTAT& 0x400000));
  392. RETAILMSG(AC97_DEBUG,(_T("WAVEDEV_AC97::AC97 Codec Ready!rn")));
  393. v_pAC97regs->rAC_GLBCTRL &= ~(0x400000); // codec ready interrupt disable
  394. Delay(10); //Sleep(5);
  395. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3f<<8)) | 0x0000; // PCM_OUT=OFF,PCM_IN=OFF,MIC=OFF;
  396. //v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3f<<8)) | 0x2200; // PCM_OUT=DMA,PCM_IN=OFF,MIC=DMA;
  397. RETAILMSG(AC97_DEBUG,(_T("WAVDEV_AC97::AC97_Init()--rn")));
  398. return TRUE;
  399. }
  400. BOOL HardwareContext::Codec_channel()
  401. {
  402. if( m_InputDMARunning & m_OutputDMARunning )
  403. {
  404. RETAILMSG(AC97_DEBUG,(_T("Codec_channel() - In & Outrn")));
  405.     WriteCodecRegister(AC97_POWER_CONTROL, AC97_PWR_D0); // ADC, DAC power up
  406. }    
  407. else if( m_InputDMARunning )
  408. {
  409. RETAILMSG(AC97_DEBUG,(_T("Codec_channel() - Inrn")));
  410.     WriteCodecRegister(AC97_POWER_CONTROL, AC97_PWR_PR1); // DAC power down
  411. }
  412. else if( m_OutputDMARunning )
  413. {
  414. RETAILMSG(AC97_DEBUG,(_T("Codec_channel() - Outrn")));
  415.     WriteCodecRegister(AC97_POWER_CONTROL, AC97_PWR_PR0); // ADC power down
  416. }
  417. else
  418. {
  419. RETAILMSG(AC97_DEBUG,(_T("Codec_channel() - nonern")));
  420.     WriteCodecRegister(AC97_POWER_CONTROL, AC97_PWR_PR1|AC97_PWR_PR0); // ADC/DAC power down
  421. }
  422.     return(TRUE);
  423. }
  424. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  425. Function: InitCodec()
  426. Description: Initializes the audio codec chip.
  427. Notes: The audio codec chip is intialized for output mode
  428. but powered down.  To conserve battery life, the chip
  429. is only powered up when the user starts playing a 
  430. file.
  431. Specifically, the power_up/down logic is done 
  432. in the AudioMute() function.  If either of the 
  433. audio channels are unmuted, then the chip is powered
  434. up; otherwise the chip is powered own.
  435. Returns: Boolean indicating success
  436. -------------------------------------------------------------------*/
  437. BOOL HardwareContext::InitCodec()
  438. {
  439. USHORT CodecRead;                             
  440. ULONG CodecVendorID, CodecRevision;           
  441. AC97MSG(1, (TEXT("+++InitCodecrn")));
  442.                                                
  443. // write the Codec software reset
  444. // 00h
  445. WriteCodecRegister(AC97_RESET, 0x683F);   
  446. // Enable the VRA
  447. // 2Ah
  448. WriteCodecRegister(AC97_EXT_AUDIO_CONTROL, AC97_ENABLE_VRA);
  449. WriteCodecRegister(AC97_PCM_DAC_RATE, 44100); // Write default Sample rate for DAC
  450. WriteCodecRegister(AC97_PCM_ADC_RATE, 44100); // Write default Sample rate for DAC
  451.   
  452. // get the Vendor ID and revision 
  453. // 7Ch            
  454. CodecVendorID = ULONG(ReadCodecRegister( AC97_VENDOR_ID1 )) << 16;
  455. // 7E
  456. CodecRead = ReadCodecRegister( AC97_VENDOR_ID2 );
  457. CodecVendorID |= ULONG(CodecRead) & 0x0000ff00;
  458. CodecRevision = ULONG(CodecRead) & 0x000000ff;
  459. /*
  460. RETAILMSG(AC97_DEBUG, (TEXT("STAC9766 Codec Vendor ID: %08x rn"), CodecVendorID));
  461. RETAILMSG(AC97_DEBUG, (TEXT("STAC9766 Codec Revision:  %02xrn"), CodecRevision));
  462. if ( !( CodecVendorID == AC97_VENDOR_SIGMATEL && CodecRevision == 0x66)) 
  463. {
  464. // now power down the Analog section of the AC97
  465. // and power it back up.  This forces the Sigmatel
  466. // to calibrate it's analog levels
  467. // 26h 
  468. WriteCodecRegister(AC97_POWER_CONTROL, AC97_PWR_ANLOFF );
  469. Delay(10); //Sleep(5);
  470. RETAILMSG(AC97_DEBUG,(_T("WriteCodecRegister(AC97_POWER_CONTROL, AC97_PWR_ANLOFF)rn")));
  471. }
  472. */
  473. // Turn Power on for sections
  474. // 26h
  475. WriteCodecRegister( AC97_POWER_CONTROL, AC97_PWR_D0 );
  476. // Write the Analog reg
  477. // 6Eh
  478. //WriteCodecRegister( AC97_ANALOG_SPEC, 0x0000 );
  479. // write HP Out Value
  480. // 04h
  481. WriteCodecRegister( AC97_HEADPHONE_VOL, 0x0404 ); //0x0202); //0x0404 );
  482. // Mute unusing analog source except -------------------------------
  483. // PCBEEP_VOL - 0ah
  484. WriteCodecRegister( AC97_PCBEEP_VOL, 0x8000 );
  485. // PHONE_VOL - 0ch
  486. WriteCodecRegister( AC97_PHONE_VOL, 0x8000 );
  487. // LINEIN_VOL - 10h
  488. WriteCodecRegister( AC97_LINEIN_VOL, 0x8000 );
  489. // CD_VOL - 12h
  490. WriteCodecRegister( AC97_CD_VOL, 0x8000 );
  491. // VIDEO_VOL - 14h
  492. WriteCodecRegister( AC97_VIDEO_VOL, 0x8000 );
  493. // AUX_VOL - 16h
  494. WriteCodecRegister( AC97_AUX_VOL, 0x8000 );
  495. // write the wave out volume
  496. // 18h
  497. WriteCodecRegister( AC97_PCMOUT_VOL, 0x0505 ); //0x0000 ); //0x0606 );
  498. // Init MIC-IN configurations
  499. // 20h
  500. WriteCodecRegister( AC97_GENERAL_PURPOSE, 0x0000 ); //MIC1 Selected
  501. // ADC Gain Control, 30dB
  502. //BOOSTEN =1
  503. // tmp = ReadCodecRegister(AC97_MIC_VOL) & ~(0x1<<6);
  504. // WriteCodecRegister(AC97_MIC_VOL, tmp | (0x1<<6));
  505. WriteCodecRegister( AC97_MIC_VOL, (1<<15)|(1<<6) ); // Mute & BOOTEN
  506. //ADC Input Slot => left slot6, right slot9, MIC GAIN VAL=1
  507. WriteCodecRegister( AC97_INTR_PAGE, 0x0 );
  508. WriteCodecRegister( AC97_ANALOG_SPEC, 0x0024 );
  509. //Left, Right => MIC
  510. WriteCodecRegister( AC97_RECORD_SELECT, AC97_RECMUX_MIC );
  511. // set up a default record gain
  512. // 1Ch
  513. WriteCodecRegister( AC97_RECORD_GAIN, AC97_RECORD_GAIN_VAL );
  514. // Now write High Pass Filter Bypass Control Register
  515. // 78h
  516. WriteCodecRegister( AC97_HPF_BYPASS, AC97_HIPASS_DISABLE );
  517. AC97MSG(1, (TEXT("---InitCodecrn")));
  518. return(TRUE);
  519. }
  520. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  521. Function: InitOutputDMA()
  522. Description: Initializes the DMA channel for output.
  523. Notes: DMA Channel 2 is used for transmitting output sound
  524. data from system memory to the I2S controller.
  525. Returns: Boolean indicating success
  526. -------------------------------------------------------------------*/
  527. BOOL HardwareContext::InitOutputDMA()
  528. {
  529. //----- 1. Initialize the DMA channel for output mode and use the first output DMA buffer -----
  530.     v_pDMAregs->rDISRC1 = (int)(PLAY_DMA_BUFFER_PHYS); 
  531.     v_pDMAregs->rDISRCC1 &= ~(SOURCE_PERIPHERAL_BUS | FIXED_SOURCE_ADDRESS); // Source is system bus, increment addr
  532.     //----- 2. Initialize the DMA channel to send data over the I2S bus -----
  533.     v_pDMAregs->rDIDST1 = (int)AC_PCMDATA_PHYS; 
  534.     //v_pDMAregs->rDIDSTC1 |= (DESTINATION_PERIPHERAL_BUS | FIXED_DESTINATION_ADDRESS); // Dest is  periperal bus, fixed addr
  535.     v_pDMAregs->rDIDSTC1 = (DESTINATION_PERIPHERAL_BUS | FIXED_DESTINATION_ADDRESS); // Dest is  periperal bus, fixed addr
  536. //----- 3. Configure the DMA channel's transfer characteristics: handshake, sync PCLK, interrupt, -----
  537. //    single tx, single service, I2SSDO, I2S request, no auto-reload, half-word, tx count
  538. v_pDMAregs->rDCON1 = (  HANDSHAKE_MODE | GENERATE_INTERRUPT | AC97PCMOUT_DMA1 | DMA_TRIGGERED_BY_HARDWARE 
  539. #if DMA_FLAG
  540. // | TRANSFER_HALF_WORD | (AUDIO_DMA_PAGE_SIZE / 2 ) );
  541. | TRANSFER_WORD | (AUDIO_DMA_PAGE_SIZE / 4 ) );
  542. #else    
  543. // | NO_DMA_AUTO_RELOAD | TRANSFER_HALF_WORD | (AUDIO_DMA_PAGE_SIZE / 2) );
  544. | NO_DMA_AUTO_RELOAD | TRANSFER_WORD | (AUDIO_DMA_PAGE_SIZE / 4 ) );
  545. #endif    
  546.      
  547. //----- 4. Reset the playback pointers -----
  548. AUDIO_RESET_PLAYBACK_POINTER();
  549. AC97MSG(1,(TEXT("---InitOutputDMAn")));
  550. return TRUE;
  551. }
  552. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  553. Function: StartOutputDMA()
  554. Description: Starts outputting the sound data to the audio codec
  555. chip via DMA.
  556. Notes: Currently, both playback and record share the same
  557. DMA channel.  Consequently, we can only start this
  558. operation if the input channel isn't using DMA.
  559. Returns: Boolean indicating success
  560. -------------------------------------------------------------------*/
  561. BOOL HardwareContext::StartOutputDMA()
  562. {
  563. ULONG OutputTransferred;
  564. AC97MSG(1,(TEXT("+++StartOutputDMAn")));
  565. if(!m_OutputDMARunning && (m_Dx == D0) )
  566. // if(!m_OutputDMARunning)
  567.     {
  568.         //----- 1. Initialize our buffer counters -----
  569.         m_OutputDMARunning=TRUE;
  570.         m_OutBytes[OUT_BUFFER_A]=m_OutBytes[OUT_BUFFER_B]=0;
  571.         //----- 2. Prime the output buffer with sound data -----
  572. //m_OutputDMAStatus = (DMA_DONEA | DMA_DONEB) & ~DMA_BIU;
  573. //RETAILMSG(1,(_T("dmastat = %xrn"), m_OutputDMAStatus));
  574. m_OutputDMAStatus = (DMA_DONEA | DMA_DONEB) & ~DMA_BIU;
  575. OutputTransferred = TransferOutputBuffers(m_OutputDMAStatus);
  576.         
  577. //----- 3. If we did transfer any data to the DMA buffers, go ahead and enable DMA -----
  578. if(OutputTransferred)
  579.         {
  580. //----- 4. Configure the DMA channel for playback -----
  581. if(!InitOutputDMA())
  582. {
  583. DEBUGMSG(ZONE_ERROR, (TEXT("HardwareContext::StartOutputDMA() - Unable to initialize output DMA channel!rn")));
  584. goto START_ERROR;
  585. }
  586. #if DMA_FLAG
  587. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3<<12)) | (0x2<<12); // 0x2 = DMA
  588. #else
  589. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3<<12)) | (0x1<<12); // 0x1 = PIO
  590. #endif
  591. //----- 5. Make sure the audio isn't muted -----
  592. AudioMute(DMA_CH_OUT, FALSE);
  593. //----- 6. Start the DMA controller -----
  594. AUDIO_RESET_PLAYBACK_POINTER();
  595. SELECT_AUDIO_DMA_OUTPUT_BUFFER_A();
  596. Codec_channel(); // Turn ON output channel
  597. // charlie, start A buffer
  598. AUDIO_OUT_DMA_ENABLE();
  599. #if DMA_FLAG
  600.         // wait for DMA to start.
  601.         delay_count = 0;
  602.         while((v_pDMAregs->rDSTAT1&0xfffff)==0){
  603.          RETAILMSG(1,(_T("1")));
  604. #if WAIT_DMA_END
  605. Sleep(1);
  606. #else
  607.     if( delay_count++ > DELAY_COUNT ) break;
  608. #endif
  609.         }        
  610.         // change the buffer pointer
  611.         SELECT_AUDIO_DMA_OUTPUT_BUFFER_B();
  612.         // Set DMA for B Buffer
  613. #endif         
  614.         }
  615.         else    // We didn't transfer any data, so DMA wasn't enabled
  616.         {
  617.             m_OutputDMARunning=FALSE;
  618.         }
  619.     }
  620.     
  621.     AC97MSG(1,(TEXT("---StartOutputDMAn")));
  622. return TRUE;
  623. START_ERROR:
  624. return FALSE;
  625. }
  626. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  627. Function: StopOutputDMA()
  628. Description: Stops any DMA activity on the output channel.
  629. Returns: Boolean indicating success
  630. -------------------------------------------------------------------*/
  631. void HardwareContext::StopOutputDMA()
  632. {
  633. AC97MSG(1,(_T("StopOutputDMA()++rn")));
  634. // jylee,040220
  635. // you should do this sleep operation for AC-Link operation.
  636. Sleep(1);
  637. //----- 1. If the output DMA is running, stop it -----
  638.     if (m_OutputDMARunning)
  639. {
  640. m_OutputDMAStatus = DMA_CLEAR;
  641. AUDIO_OUT_DMA_DISABLE();
  642. AUDIO_OUT_CLEAR_INTERRUPTS();
  643. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3<<12)) | (0x0<<12); // 0x0 = OFF
  644. AudioMute(DMA_CH_OUT, TRUE);
  645.     }
  646. m_OutputDMARunning = FALSE;
  647. Codec_channel();
  648. /*
  649. // jylee, 040220
  650. // Clear AC97 PCMDATA FIFO.
  651. for (int i=0; i<AC97_PCMDATA_FIFO_LENGTH;i++)
  652. {
  653. v_pAC97regs->rAC_PCMDATA = 0x00000000;
  654. }
  655. */
  656. AC97MSG(1,(_T("StopOutputDMA()--rn")));
  657. }
  658. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  659. Function: InitInputDMA()
  660. Description: Initializes the DMA channel for input.
  661. Notes: ***** NOT IMPLEMENTED *****
  662. The following routine is not implemented due to a
  663. hardware bug in the revision of the Samsung SC2440
  664. CPU this driver was developed on.  See the header
  665. at the top of this file for details.
  666. Returns: Boolean indicating success
  667. -------------------------------------------------------------------*/
  668. BOOL HardwareContext::InitInputDMA()
  669. {
  670. //goto INIT_ERROR;
  671. AC97MSG(1,(TEXT("+++InitInputDMAn")));
  672. //============================ Configure DMA Channel 1 ===========================
  673. //------ On platforms with the revsion of the Samsung SC2440 CPU with the IIS SLAVE bug fix, this -----
  674. //  code can be used to configure DMA channel 1 for input.
  675. //----- 1. Initialize the DMA channel for input mode and use the first input DMA buffer -----
  676. v_pDMAregs->rDISRC2 = (int)AC_MICDATA_PHYS;
  677. v_pDMAregs->rDISRCC2 = (SOURCE_PERIPHERAL_BUS | FIXED_SOURCE_ADDRESS); // Source is periperal bus, fixed addr
  678.     
  679.     //----- 2. Initialize the DMA channel to receive data over the I2S bus -----
  680.     v_pDMAregs->rDIDST2 = (int)(RECORD_DMA_BUFFER_PHYS); //(AUDIO_DMA_BUFFER_PHYS); 
  681.     v_pDMAregs->rDIDSTC2 &= ~(DESTINATION_PERIPHERAL_BUS | FIXED_DESTINATION_ADDRESS); // Destination is system bus, increment addr
  682. //----- 3. Configure the DMA channel's transfer characteristics: handshake, sync PCLK, interrupt, -----
  683. //    single tx, single service, I2SSDI, I2S request, no auto-reload, half-word, tx count
  684. v_pDMAregs->rDCON2 = (  HANDSHAKE_MODE | GENERATE_INTERRUPT | AC97MICIN_DMA2 | DMA_TRIGGERED_BY_HARDWARE 
  685. #if DMA_FLAG
  686. #if (INCHANNELS==2)
  687.    | TRANSFER_WORD | (AUDIO_DMA_PAGE_SIZE / 4) );
  688. #else    
  689.    | TRANSFER_HALF_WORD | (AUDIO_DMA_PAGE_SIZE / 2) );
  690. #endif
  691. #else /* DMA_FLAG */
  692.    | NO_DMA_AUTO_RELOAD | TRANSFER_WORD | (AUDIO_DMA_PAGE_SIZE / 4) );
  693.    //| NO_DMA_AUTO_RELOAD | TRANSFER_HALF_WORD | (AUDIO_DMA_PAGE_SIZE / 2) );
  694. #endif /* DMA_FLAG */
  695. return TRUE;
  696. }
  697. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  698. Function: StartInputDMA()
  699. Description: Starts inputting the recorded sound data from the 
  700. audio codec chip via DMA.
  701. Notes: ***** NOT IMPLEMENTED *****
  702. The following routine is not implemented due to a
  703. hardware bug in the revision of the Samsung SC2440
  704. CPU this driver was developed on.  See the header
  705. at the top of this file for details.
  706. Returns: Boolean indicating success
  707. -------------------------------------------------------------------*/
  708. BOOL HardwareContext::StartInputDMA()
  709. {
  710. //------ On platforms with the revsion of the Samsung SC2440 CPU with the IIS SLAVE bug fix, this -----
  711. //  code can be used to configure DMA channel 1 for input.
  712. AC97MSG(1,(TEXT("+++StartInputDMAn")));
  713. if(!m_InputDMARunning)
  714.     {
  715.         //----- 1. Initialize our buffer counters -----
  716.         m_InputDMARunning=TRUE;
  717. Codec_channel();        // Turn On Input channel
  718.         m_InBytes[IN_BUFFER_A]=m_InBytes[IN_BUFFER_B]=0;
  719.         //----- 2. Prime the output buffer with sound data -----
  720. m_InputDMAStatus = (DMA_DONEA | DMA_DONEB) & ~DMA_BIU;
  721. //----- 3. Configure the DMA channel for record -----
  722. if(!InitInputDMA())
  723. {
  724. DEBUGMSG(ZONE_ERROR, (TEXT("HardwareContext::StartInputDMA() - Unable to initialize input DMA channel!rn")));
  725. goto START_ERROR;
  726. }
  727. #if DMA_FLAG
  728. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3<<8)) | (0x2<<8); // 0x2 = DMA
  729. #else
  730. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3<<8)) | (0x1<<8); // 0x1 = PIO
  731. #endif
  732. //----- 4. Make sure the audio isn't muted -----
  733. AudioMute(DMA_CH_MIC, FALSE);
  734. //----- 5. Start the input DMA -----
  735. AUDIO_RESET_RECORD_POINTER();
  736. SELECT_AUDIO_DMA_INPUT_BUFFER_A();
  737. Codec_channel();        // Turn On Input channel
  738. v_pDMAregs->rDMASKTRIG2 = ENABLE_DMA_CHANNEL;
  739. #if DMA_FLAG
  740.         // wait for DMA to start.
  741.         delay_count = 0;
  742.         while((v_pDMAregs->rDSTAT2&0xfffff)==0){
  743.          RETAILMSG(1,(_T("2")));
  744. #if WAIT_DMA_END
  745. Sleep(1);
  746. #else
  747.          if( delay_count++ > DELAY_COUNT ) break;
  748. #endif
  749.         } 
  750.         // change the buffer pointer
  751.         SELECT_AUDIO_DMA_INPUT_BUFFER_B();
  752. #endif         
  753.     }
  754.     AC97MSG(1,(TEXT("---StartInputDMAn")));
  755. return TRUE;
  756. START_ERROR:
  757. return FALSE;
  758. }
  759. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  760. Function: StopInputDMA()
  761. Description: Stops any DMA activity on the input channel.
  762. Notes: ***** NOT IMPLEMENTED *****
  763. The following routine is not implemented due to a
  764. hardware bug in the revision of the Samsung SC2440
  765. CPU this driver was developed on.  See the header
  766. at the top of this file for details.
  767. Returns: Boolean indicating success
  768. -------------------------------------------------------------------*/
  769. void HardwareContext::StopInputDMA()
  770. {
  771. //------ On platforms with the revsion of the Samsung SC2440 CPU with the IIS SLAVE bug fix, this -----
  772. //  code can be used to configure DMA channel 1 for input.
  773. //----- 1. If the output DMA is running, stop it -----
  774.     if (m_InputDMARunning)
  775. {
  776. AUDIO_IN_DMA_DISABLE(); // jylee
  777. AUDIO_IN_CLEAR_INTERRUPTS();
  778. m_InputDMAStatus = DMA_CLEAR;
  779. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3<<8)) | (0x0<<8); // 0x0 = OFF
  780. AudioMute(DMA_CH_MIC, TRUE);
  781.     }
  782. m_InputDMARunning = FALSE;
  783. Codec_channel();
  784. }
  785. DWORD HardwareContext::GetInterruptThreadPriority()
  786. {
  787.     HKEY hDevKey;
  788.     DWORD dwValType;
  789.     DWORD dwValLen;
  790.     DWORD dwPrio = 249; // Default priority
  791.     hDevKey = OpenDeviceKey((LPWSTR)m_DriverIndex);
  792.     if (hDevKey)
  793.     {
  794.         dwValLen = sizeof(DWORD);
  795.         RegQueryValueEx(
  796.             hDevKey,
  797.             TEXT("Priority256"),
  798.             NULL,
  799.             &dwValType,
  800.             (PUCHAR)&dwPrio,
  801.             &dwValLen);
  802.         RegCloseKey(hDevKey);
  803.     }
  804.     return dwPrio;
  805. }
  806. /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
  807. // SetForceSpeaker is called from the device context to update the state of the
  808. // m_bForceSpeaker variable.
  809. /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
  810. DWORD HardwareContext::ForceSpeaker( BOOL bForceSpeaker )
  811. {
  812.     // If m_NumForcedSpeaker is non-zero, audio should be routed to an
  813.     // external speaker (if hw permits).
  814.     if (bForceSpeaker)
  815.     {
  816.         m_NumForcedSpeaker++;
  817.         if (m_NumForcedSpeaker==1)
  818.         {
  819.             RecalcSpeakerEnable();
  820.         }
  821.     }
  822.     else
  823.     {
  824.         m_NumForcedSpeaker--;
  825.         if (m_NumForcedSpeaker==0)
  826.         {
  827.             RecalcSpeakerEnable();
  828.         }
  829.     }
  830.     return MMSYSERR_NOERROR;
  831. }
  832. // Control the hardware speaker enable
  833. void HardwareContext::SetSpeakerEnable(BOOL bEnable)
  834. {
  835.     // Code to turn speaker on/off here
  836.     return;
  837. }
  838. /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
  839. // RecalcSpeakerEnable decides whether to enable the speaker or not.
  840. // For now, it only looks at the m_bForceSpeaker variable, but it could
  841. // also look at whether the headset is plugged in
  842. // and/or whether we're in a voice call. Some mechanism would
  843. // need to be implemented to inform the wave driver of changes in the state of
  844. // these variables however.
  845. /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
  846. void HardwareContext::RecalcSpeakerEnable()
  847. {
  848.     SetSpeakerEnable(m_NumForcedSpeaker);
  849. }
  850. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  851. Function: InitInterruptThread()
  852. Description: Initializes the IST for handling DMA interrupts.
  853. Returns: Boolean indicating success
  854. -------------------------------------------------------------------*/
  855. BOOL HardwareContext::InitInterruptThread()
  856. {
  857.     BOOL bSuccess;
  858.     
  859.     m_hAudioInterrupt = CreateEvent( NULL, FALSE, FALSE, NULL);
  860.     if (!m_hAudioInterrupt)
  861.     {
  862.         return FALSE;
  863.     }
  864.     bSuccess = InterruptInitialize(m_IntrAudio, m_hAudioInterrupt, NULL, 0);
  865.     m_hAudioInterruptThread  = CreateThread((LPSECURITY_ATTRIBUTES)NULL,
  866.                                             0,
  867.                                             (LPTHREAD_START_ROUTINE)CallInterruptThread,
  868.                                             this,
  869.                                             0,
  870.                                             NULL);
  871.     if (!m_hAudioInterruptThread)
  872.     {
  873.         return FALSE;
  874.     }
  875.     // Bump up the priority since the interrupt must be serviced immediately.
  876. CeSetThreadPriority(m_hAudioInterruptThread, GetInterruptThreadPriority());
  877.     return TRUE;
  878. }
  879. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  880. Function: PowerUp()
  881. Description: Powers up the audio codec chip.
  882. Notes: Currently, this function is unimplemented because
  883. the audio codec chip is ONLY powered up when the 
  884. user wishes to play or record.  The AudioMute() function
  885. handles the powerup sequence.
  886. Returns: Boolean indicating success
  887. -------------------------------------------------------------------*/
  888. void HardwareContext::PowerUp()
  889. {
  890.     RETAILMSG(1,(_T("AC97:PowerUP()rn")));
  891.     AC97_Init();
  892.     //AC97_GPIO_Init();
  893.     
  894.     InitCodec();
  895.     
  896.     //AudioMute(DMA_CH_OUT, FALSE); // 030711
  897.     // setup DMA output
  898.     //InitOutputDMA();
  899.     //AudioMute((DMA_CH_OUT | DMA_CH_MIC), FALSE);
  900.     AudioMute(DMA_CH_OUT, FALSE);
  901. }
  902. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  903. Function: PowerDown()
  904. Description: Powers down the audio codec chip.
  905. Notes: Even if the input/output channels are muted, this
  906. function powers down the audio codec chip in order
  907. to conserve battery power.
  908. Returns: Boolean indicating success
  909. -------------------------------------------------------------------*/
  910. void HardwareContext::PowerDown()
  911. {
  912.     RETAILMSG(1,(_T("AC97:PowerDown()rn")));
  913.     //StopOutputDMA();
  914. m_OutputDMAStatus = DMA_CLEAR;
  915. AUDIO_OUT_DMA_DISABLE();
  916. AUDIO_OUT_CLEAR_INTERRUPTS();
  917. v_pAC97regs->rAC_GLBCTRL = (v_pAC97regs->rAC_GLBCTRL & ~(0x3<<12)) | (0x0<<12); // 0x0 = OFF
  918. //AudioMute(DMA_CH_OUT, TRUE);
  919. m_OutputDMARunning = FALSE;
  920. m_InputDMARunning = FALSE;
  921. AudioMute((DMA_CH_OUT | DMA_CH_MIC), TRUE);
  922. }
  923. //############################################ Helper Functions #############################################
  924. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  925. Function: TransferOutputBuffer()
  926. Description: Retrieves the next "mixed" audio buffer of data to
  927. DMA into the output channel.
  928. Returns: Number of bytes needing to be transferred.
  929. -------------------------------------------------------------------*/
  930. ULONG HardwareContext::TransferOutputBuffer(ULONG NumBuf)
  931. {
  932.     ULONG BytesTransferred = 0;
  933.     PBYTE pBufferStart = m_Output_pbDMA_PAGES[NumBuf];
  934.     PBYTE pBufferEnd = pBufferStart + AUDIO_DMA_PAGE_SIZE;
  935.     PBYTE pBufferLast;
  936. __try
  937. {
  938. pBufferLast = m_OutputDeviceContext.TransferBuffer(pBufferStart, pBufferEnd,NULL);
  939. BytesTransferred = m_OutBytes[NumBuf] = pBufferLast-pBufferStart;
  940. // Enable if you need to clear the rest of the DMA buffer
  941. StreamContext::ClearBuffer(pBufferLast,pBufferEnd);
  942. //memset(pBufferStart, 0, AUDIO_DMA_PAGE_SIZE);
  943. if(NumBuf == OUT_BUFFER_A) // Output Buffer A
  944. {
  945. m_OutputDMAStatus &= ~DMA_DONEA;
  946. m_OutputDMAStatus |= DMA_STRTA;
  947. }
  948. else // Output Buffer B
  949. {
  950. m_OutputDMAStatus &= ~DMA_DONEB;
  951. m_OutputDMAStatus |= DMA_STRTB;
  952. }
  953. }
  954. __except(EXCEPTION_EXECUTE_HANDLER) 
  955. {
  956. DEBUGMSG(ZONE_ERROR, (TEXT("WAVDEV2.DLL:TransferOutputBuffer() - EXCEPTION: %d"), GetExceptionCode()));
  957. }
  958.     return BytesTransferred;
  959. }
  960. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  961. Function: TransferOutputBuffers()
  962. Description: Determines which output buffer (A or B) needs to 
  963. be filled with sound data.  The correct buffer is
  964. then populated with data and ready to DMA to the 
  965. output channel.
  966. Returns: Boolean indicating success
  967. -------------------------------------------------------------------*/
  968. ULONG HardwareContext::TransferOutputBuffers(DWORD dwDCSR)
  969. {
  970.     ULONG BytesTransferred = 0;
  971.     ULONG BytesTotal;
  972.     DWORD Bits = dwDCSR & (DMA_DONEA|DMA_DONEB|DMA_BIU);
  973. switch (Bits)
  974.     {
  975.     case 0:
  976.     case DMA_BIU:
  977.         // No done bits set- must not be my interrupt
  978.         return 0;
  979.     case DMA_DONEA|DMA_DONEB|DMA_BIU:
  980.         // Load B, then A
  981.         BytesTransferred = TransferOutputBuffer(OUT_BUFFER_B);
  982.         // fall through
  983.     case DMA_DONEA: // This should never happen!
  984.     case DMA_DONEA|DMA_BIU:
  985. BytesTransferred += TransferOutputBuffer(OUT_BUFFER_A); // charlie, A => B
  986.         break;
  987.     case DMA_DONEA|DMA_DONEB:
  988.         // Load A, then B
  989.         BytesTransferred = TransferOutputBuffer(OUT_BUFFER_A);
  990. #if DMA_FLAG        
  991.         // charlie
  992.         BytesTransferred += TransferOutputBuffer(OUT_BUFFER_B);
  993.         break; // charlie
  994. #endif        
  995.         // fall through
  996.     case DMA_DONEB|DMA_BIU: // This should never happen!
  997.     case DMA_DONEB:
  998.         // Load B
  999.         BytesTransferred += TransferOutputBuffer(OUT_BUFFER_B); // charlie, B => A
  1000.         break;
  1001.     }
  1002.     // If it was our interrupt, but we weren't able to transfer any bytes
  1003.     // (e.g. no full buffers ready to be emptied)
  1004.     // and all the output DMA buffers are now empty, then stop the output DMA
  1005.     BytesTotal = m_OutBytes[OUT_BUFFER_A]+m_OutBytes[OUT_BUFFER_B];
  1006. if (BytesTotal==0)
  1007.     {
  1008. StopOutputDMA();
  1009.     }
  1010.     return BytesTransferred;
  1011. }
  1012. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1013. Function: TransferInputBuffer()
  1014. Description: Retrieves the chunk of recorded sound data and inputs
  1015. it into an audio buffer for potential "mixing".
  1016. Returns: Number of bytes needing to be transferred.
  1017. -------------------------------------------------------------------*/
  1018. ULONG HardwareContext::TransferInputBuffer(ULONG NumBuf)
  1019. {
  1020.     ULONG BytesTransferred = 0;
  1021.     PBYTE pBufferStart = m_Input_pbDMA_PAGES[NumBuf];
  1022.     PBYTE pBufferEnd = pBufferStart + AUDIO_DMA_PAGE_SIZE;
  1023.     PBYTE pBufferLast;
  1024. __try
  1025. {
  1026. pBufferLast = m_InputDeviceContext.TransferBuffer(pBufferStart, pBufferEnd,NULL);
  1027. BytesTransferred = m_InBytes[NumBuf] = pBufferLast-pBufferStart;
  1028. if(NumBuf == IN_BUFFER_A) // Input Buffer A
  1029. {
  1030. m_InputDMAStatus &= ~DMA_DONEA;
  1031. m_InputDMAStatus |= DMA_STRTA;
  1032. }
  1033. else // Input Buffer B
  1034. {
  1035. m_InputDMAStatus &= ~DMA_DONEB;
  1036. m_InputDMAStatus |= DMA_STRTB;
  1037. }
  1038. }
  1039. __except(EXCEPTION_EXECUTE_HANDLER) 
  1040. {
  1041. DEBUGMSG(ZONE_ERROR, (TEXT("WAVDEV2.DLL:TransferInputBuffer() - EXCEPTION: %d"), GetExceptionCode()));
  1042. }
  1043.     return BytesTransferred;
  1044. }
  1045. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1046. Function: TransferInputBuffers()
  1047. Description: Determines which input buffer (A or B) needs to 
  1048. be filled with recorded sound data.  The correct 
  1049. buffer is then populated with recorded sound data
  1050. from the input channel.
  1051. Returns: Boolean indicating success
  1052. -------------------------------------------------------------------*/
  1053. ULONG HardwareContext::TransferInputBuffers(DWORD dwDCSR)
  1054. {
  1055.     ULONG BytesTransferred=0;
  1056.     DWORD Bits = dwDCSR & (DMA_DONEA|DMA_DONEB|DMA_BIU);
  1057.     switch (Bits)
  1058.     {
  1059.     case 0:
  1060.     case DMA_BIU:
  1061.         // No done bits set- must not be my interrupt
  1062.         return 0;
  1063.     case DMA_DONEA|DMA_DONEB|DMA_BIU:
  1064.         // Load B, then A
  1065.         BytesTransferred = TransferInputBuffer(IN_BUFFER_B);
  1066.         // fall through
  1067.     case DMA_DONEA: // This should never happen!
  1068.     case DMA_DONEA|DMA_BIU:
  1069.         // Load A
  1070.         BytesTransferred += TransferInputBuffer(IN_BUFFER_A);
  1071.         break;
  1072.     case DMA_DONEA|DMA_DONEB:
  1073.         // Load A, then B
  1074.         BytesTransferred = TransferInputBuffer(IN_BUFFER_A);
  1075. #if 0 //DMA_FLAG        
  1076. BytesTransferred += TransferInputBuffer(IN_BUFFER_B);
  1077. break;
  1078. #endif
  1079.         // fall through
  1080.     case DMA_DONEB|DMA_BIU: // This should never happen!
  1081.     case DMA_DONEB:
  1082.         // Load B
  1083.         BytesTransferred += TransferInputBuffer(IN_BUFFER_B);
  1084.         break;
  1085.     }
  1086.     // If it was our interrupt, but we weren't able to transfer any bytes
  1087.     // (e.g. no empty buffers ready to be filled)
  1088.     // Then stop the input DMA
  1089.     if (BytesTransferred==0)
  1090.     {
  1091.         StopInputDMA();
  1092.     }
  1093.     return BytesTransferred;
  1094. }
  1095. void HardwareContext::InterruptThread()
  1096. {
  1097.     ULONG InputTransferred, OutputTransferred;
  1098. BOOL dmaInterruptSource = 0;
  1099.     // Fast way to access embedded pointers in wave headers in other processes.
  1100. SetProcPermissions((DWORD)-1);
  1101.     while(TRUE)
  1102.     {
  1103. WaitForSingleObject(m_hAudioInterrupt, INFINITE);
  1104. AC97MSG(1,(TEXT("0x%xn"),s2440INT->rINTMSK));
  1105. AC97MSG(1,(TEXT("startn")));
  1106. dmaInterruptSource = 0;
  1107. //----- 1. Grab the lock -----
  1108. Lock();
  1109. __try
  1110. {
  1111. AC97MSG(1,(TEXT("tryn")));
  1112. //----- 3. Determine the interrupt source (input DMA operation or output DMA operation?) -----
  1113. //----- NOTE: Often, platforms use two separate DMA channels for input/output operations but
  1114. // have the OAL return SYSINTR_AUDIO as the interrupt source.  If this is the case,
  1115. // then the interrupt source (input or output DMA channel) must be determined in
  1116. // this step.
  1117. // charlie, determine the interrupt source
  1118. // s2440INT->rSRCPND
  1119. if( s2440INT->rINTMSK & BIT_DMA1 ){
  1120. dmaInterruptSource |= DMA_CH_OUT; // Output DMA is supported...
  1121. }
  1122. if( s2440INT->rINTMSK & BIT_DMA2 ){
  1123. dmaInterruptSource |= DMA_CH_MIC; // Input DMA is supported...
  1124. }
  1125. // For determine the interrupt source
  1126. //----- 2. Acknowledge the DMA interrupt -----
  1127. InterruptDone(m_IntrAudio);
  1128. if ( m_Dx != D0 ) continue;
  1129. //----- 4. Handle any interrupts on the input source -----
  1130. //    NOTE: The InterruptDone() call below automatically clears the interrupt.
  1131. // if((m_InputDMARunning) && (dmaInterruptSource == DMA_CH_MIC))
  1132. if((dmaInterruptSource == DMA_CH_MIC))
  1133. {
  1134. //----- Determine which buffer just completed the DMA transfer -----
  1135. if(m_InputDMAStatus & DMA_BIU)
  1136. {
  1137. m_InputDMAStatus &= ~DMA_STRTB; // Buffer B just completed...
  1138. m_InputDMAStatus |= DMA_DONEB;
  1139. m_InputDMAStatus &= ~DMA_BIU; // Buffer A is in use
  1140. #if DMA_FLAG
  1141. SELECT_AUDIO_DMA_INPUT_BUFFER_B();
  1142. #else
  1143. SELECT_AUDIO_DMA_INPUT_BUFFER_A();
  1144. #endif
  1145. AC97MSG(1,(TEXT("1n")));
  1146. }else
  1147. {
  1148. m_InputDMAStatus &= ~DMA_STRTA; // Buffer A just completed...
  1149. m_InputDMAStatus |= DMA_DONEA;
  1150. m_InputDMAStatus |= DMA_BIU; // Buffer B is in use
  1151. #if DMA_FLAG
  1152. SELECT_AUDIO_DMA_INPUT_BUFFER_A();
  1153. #else
  1154. SELECT_AUDIO_DMA_INPUT_BUFFER_B();
  1155. #endif
  1156. AC97MSG(1,(TEXT("2n")));
  1157. }
  1158. #if !DMA_FLAG
  1159. //----- 5. Schedule the next DMA transfer -----
  1160. AUDIO_IN_DMA_ENABLE();
  1161. #endif
  1162. //----- 6. Retrieve the next chunk of recorded data from the non-playing buffer -----
  1163. InputTransferred = TransferInputBuffers(m_InputDMAStatus);
  1164. }
  1165. //----- 7. Handle any interrupts on the output source -----
  1166. //    NOTE: The InterruptDone() call below automatically clears the interrupt.
  1167. // if((m_OutputDMARunning) && (dmaInterruptSource == DMA_CH_OUT))
  1168. // if((dmaInterruptSource == DMA_CH_OUT))
  1169. else
  1170. {
  1171. //----- Determine which buffer just completed the DMA transfer -----
  1172. if(m_OutputDMAStatus & DMA_BIU)
  1173. {
  1174. //RETAILMSG(DMA_CHK,(_T("BBrn")));
  1175. m_OutputDMAStatus &= ~DMA_STRTB; // Buffer A just completed...
  1176. m_OutputDMAStatus |= DMA_DONEB;
  1177. m_OutputDMAStatus &= ~DMA_BIU; // Buffer B is in use
  1178. delay_count = 0;
  1179.     while((v_pDMAregs->rDSTAT1&0xfffff)==0){
  1180.          RETAILMSG(1,(_T("3")));
  1181. #if WAIT_DMA_END
  1182. Sleep(1);
  1183. #else
  1184.          if( delay_count++ > DELAY_COUNT ) break;
  1185. #endif
  1186.          } 
  1187. #if DMA_FLAG
  1188. SELECT_AUDIO_DMA_OUTPUT_BUFFER_B(); // charlie. B => A
  1189. #else
  1190. SELECT_AUDIO_DMA_OUTPUT_BUFFER_A(); // charlie. B => A
  1191. #endif
  1192. }else
  1193. {
  1194. //RETAILMSG(DMA_CHK,(_T("AArn")));
  1195. m_OutputDMAStatus &= ~DMA_STRTA; // Buffer B just completed...
  1196. m_OutputDMAStatus |= DMA_DONEA;
  1197. m_OutputDMAStatus |= DMA_BIU; // Buffer A is in use
  1198. delay_count = 0;
  1199. while((v_pDMAregs->rDSTAT1&0xfffff)==0)
  1200. {
  1201.          RETAILMSG(1,(_T("4")));
  1202. #if WAIT_DMA_END
  1203. Sleep(1);
  1204. #else
  1205.          if( delay_count++ > DELAY_COUNT ) break;
  1206. #endif
  1207. }
  1208. #if DMA_FLAG
  1209. SELECT_AUDIO_DMA_OUTPUT_BUFFER_A(); // charlie. B => A
  1210. #else
  1211. SELECT_AUDIO_DMA_OUTPUT_BUFFER_B(); // charlie. B => A
  1212. #endif
  1213. }
  1214. #if !DMA_FLAG
  1215. //----- 8. Schedule the next DMA transfer -----
  1216. AUDIO_OUT_DMA_ENABLE();
  1217. #endif
  1218. //----- 9. Fill the non-playing buffer with the next chunk of audio data to play -----
  1219. OutputTransferred = TransferOutputBuffers(m_OutputDMAStatus);
  1220. }
  1221. }
  1222. __except(EXCEPTION_EXECUTE_HANDLER) 
  1223. {
  1224. DEBUGMSG(ZONE_ERROR, (TEXT("WAVDEV2.DLL:InterruptThread() - EXCEPTION: %d"), GetExceptionCode()));
  1225. }
  1226. //----- 10. Give up the lock ----- 
  1227. Unlock();
  1228. }  
  1229. }
  1230. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1231. Function: AudioMute()
  1232. Description: Mutes/unmutes the specified audio channel.
  1233. Notes: If both audio channels are MUTED, then the chip 
  1234. is powered down to conserve battery life.  
  1235. Alternatively, if either audio channel is unMUTED, 
  1236. the chip is powered up.
  1237. Returns: Boolean indicating success
  1238. -------------------------------------------------------------------*/
  1239. BOOL HardwareContext::AudioMute(DWORD channel, BOOL bMute)
  1240. {
  1241. static DWORD dwActiveChannel = 0;
  1242. USHORT volume;
  1243. // 
  1244. // Turn off/on mute bit in volume control register.
  1245. // 
  1246. if( (channel & DMA_CH_OUT ) && !m_OutputDMARunning ) // 030711
  1247. {
  1248. if(bMute)
  1249. {
  1250. volume = ReadCodecRegister(AC97_HEADPHONE_VOL);
  1251.       WriteCodecRegister(AC97_HEADPHONE_VOL, volume | 0x8000);
  1252. }
  1253. else
  1254. {
  1255. volume = ReadCodecRegister(AC97_HEADPHONE_VOL);
  1256.       WriteCodecRegister(AC97_HEADPHONE_VOL, volume & ~0x8000);
  1257. }
  1258. }
  1259. if( (channel & DMA_CH_MIC ) && !m_InputDMARunning )
  1260. {
  1261. if(bMute)
  1262. {
  1263. volume = ReadCodecRegister(AC97_MIC_VOL);
  1264.       WriteCodecRegister(AC97_MIC_VOL, volume | 0x8000);
  1265. }
  1266. else
  1267. {
  1268. volume = ReadCodecRegister(AC97_MIC_VOL);
  1269.       WriteCodecRegister(AC97_MIC_VOL, volume & ~0x8000);
  1270. }
  1271. }
  1272. return TRUE;
  1273. }
  1274. void CallInterruptThread(HardwareContext *pHWContext)
  1275. {
  1276.     pHWContext->InterruptThread();
  1277. }
  1278. DWORD 
  1279. HardwareContext::Open(
  1280.     void
  1281.     )
  1282. {
  1283.     DWORD mmErr = MMSYSERR_NOERROR;
  1284.     
  1285.     // Don't allow play when not on, if there is a power constraint upon us.
  1286.     if ( D0 != m_Dx )
  1287.     {
  1288.         // Tell the Power Manager we need to power up.
  1289.         // If there is a power constraint then fail.
  1290.         DWORD dwErr = DevicePowerNotify(_T("WAV1:"), D0, POWER_NAME);
  1291.         if ( ERROR_SUCCESS !=  dwErr ) {
  1292.             RETAILMSG(1, (TEXT("AC97::Open:DevicePowerNotify ERROR: %urn"), dwErr ));
  1293.             mmErr = MMSYSERR_ERROR;
  1294.         }
  1295.     }
  1296.     
  1297.     return mmErr;
  1298. }
  1299. DWORD 
  1300. HardwareContext::Close(
  1301.     void
  1302.     )
  1303. {
  1304.     DWORD mmErr = MMSYSERR_NOERROR;
  1305. /*
  1306.     DWORD dwErr;
  1307.     
  1308.     // we are done so inform Power Manager to power us down, 030711
  1309. //    dwErr = DevicePowerNotify(_T("WAV1:"), (_CEDEVICE_POWER_STATE)D4, POWER_NAME);
  1310.     if ( ERROR_SUCCESS !=  dwErr ) {
  1311.         RETAILMSG(1, (TEXT("AC97::Close:DevicePowerNofify ERROR: %urn"), dwErr ));
  1312.             mmErr = MMSYSERR_ERROR;
  1313.     }
  1314. */
  1315.     
  1316.     return mmErr;
  1317. }
  1318. BOOL 
  1319. HardwareContext::IOControl( 
  1320.     DWORD  dwOpenData,
  1321.     DWORD  dwCode,
  1322.     PBYTE  pBufIn,
  1323.     DWORD  dwLenIn,
  1324.     PBYTE  pBufOut,
  1325.     DWORD  dwLenOut,
  1326.     PDWORD pdwActualOut)
  1327. {
  1328.     DWORD dwErr = ERROR_SUCCESS;    
  1329.     BOOL  bRc = TRUE;
  1330.     UNREFERENCED_PARAMETER(dwOpenData);
  1331.     
  1332.     switch (dwCode) {
  1333.         //
  1334.         // Power Management
  1335.         //
  1336.         case IOCTL_POWER_CAPABILITIES: 
  1337.         {
  1338.             PPOWER_CAPABILITIES ppc;
  1339.             RETAILMSG(1,(_T("IOCTL_POWER_CAPABILITIES++rn")));
  1340.             
  1341.             if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(POWER_CAPABILITIES)) ) {
  1342.                 bRc = FALSE;
  1343.                 dwErr = ERROR_INVALID_PARAMETER;
  1344.                 break;
  1345.             }
  1346.             
  1347.             ppc = (PPOWER_CAPABILITIES)pBufOut;
  1348.             
  1349.             memset(ppc, 0, sizeof(POWER_CAPABILITIES));
  1350.             // support D0, D4
  1351.             ppc->DeviceDx = 0x11;
  1352.             // no wake
  1353.             // no inrush
  1354.             // Report our nominal power consumption in uAmps rather than mWatts.
  1355.             ppc->Flags = POWER_CAP_PREFIX_MICRO | POWER_CAP_UNIT_AMPS;
  1356.             // REVIEW: Do we enable all these for normal playback?
  1357.             // D0: SPI + I2S + CODEC (Playback) + Headphone= 
  1358.             //     0.5 mA + 0.5 mA + (23 mW, into BUGBUG ohms ) + (30 mW, into 32 ohms)
  1359.             //     500 uA + 500 uA + 23000 uA + 32000 uA
  1360.             ppc->Power[D0] = 56000;
  1361.             
  1362.             *pdwActualOut = sizeof(POWER_CAPABILITIES);
  1363.             
  1364.             RETAILMSG(1,(_T("IOCTL_POWER_CAPABILITIES--rn")));
  1365.         } break;
  1366.         case IOCTL_POWER_SET: 
  1367.          bRc = TRUE;
  1368. break;
  1369. /*       
  1370.         {
  1371.             CEDEVICE_POWER_STATE NewDx;
  1372.             RETAILMSG(1,(_T("IOCTL_POWER_SET++rn")));
  1373.             
  1374.             if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)) ) {
  1375.                 bRc = FALSE;
  1376.                 dwErr = ERROR_INVALID_PARAMETER;
  1377.                 break;
  1378.             }
  1379.             
  1380.             NewDx = *(PCEDEVICE_POWER_STATE)pBufOut;
  1381.             if ( VALID_DX(NewDx) ) {
  1382.                 // grab the CS since the normal Xxx_PowerXxx can not.
  1383.                 Lock();
  1384.                 switch ( NewDx ) {
  1385.                     case D0:
  1386.                         if (m_Dx != D0) {
  1387.                             PowerUp();
  1388.                             m_Dx = D0;
  1389.                         }
  1390.                         break;
  1391.                     default:
  1392.                         if (m_Dx != (_CEDEVICE_POWER_STATE)D4) {
  1393.                             PowerDown();
  1394.                             m_Dx = (_CEDEVICE_POWER_STATE)D4;
  1395.                         }
  1396.                         break;
  1397.                 }
  1398.                 
  1399.                 // return our state
  1400.                 *(PCEDEVICE_POWER_STATE)pBufOut = m_Dx;
  1401.                 
  1402.                 RETAILMSG(1, (TEXT("AC97AUDIO: IOCTL_POWER_SET: D%u => D%u rn"), NewDx, m_Dx));
  1403.                 Unlock();
  1404.                 
  1405.                 *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
  1406.             } else {
  1407.                 bRc = FALSE;
  1408.                 dwErr = ERROR_INVALID_PARAMETER;
  1409.             }
  1410.             RETAILMSG(1,(_T("IOCTL_POWER_SET--rn")));
  1411.             
  1412.         } break;
  1413. */
  1414.         case IOCTL_POWER_GET: 
  1415.             RETAILMSG(1,(_T("IOCTL_POWER_GET++rn")));
  1416.             if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)) ) {
  1417.                 bRc = FALSE;
  1418.                 dwErr = ERROR_INVALID_PARAMETER;
  1419.                 break;
  1420.             }
  1421.             *(PCEDEVICE_POWER_STATE)pBufOut = m_Dx;
  1422.             RETAILMSG(1, (TEXT("AC97AUDIO: IOCTL_POWER_GET: D%u rn"), m_Dx));
  1423.             *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
  1424.             RETAILMSG(1,(_T("IOCTL_POWER_GET++rn")));
  1425.             break;
  1426.             
  1427.         default:
  1428.             bRc = FALSE;
  1429.             dwErr = ERROR_INVALID_FUNCTION;
  1430.             DEBUGMSG (ZONE_FUNCTION, (TEXT(" Unsupported ioctl 0x%Xrn"), dwCode));
  1431.             break;            
  1432.     }
  1433.     
  1434.     if ( !bRc ) {
  1435.         SetLastError(dwErr);
  1436.     }
  1437.     
  1438.     return(bRc);
  1439. }