Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
14 KiB

  1. /***************************************************************************\
  2. *
  3. * ************************
  4. * * MINIPORT SAMPLE CODE *
  5. * ************************
  6. *
  7. * Module Name:
  8. *
  9. * interupt.c
  10. *
  11. * Abstract:
  12. *
  13. * This module contains code to control interrupts for Permedia 3.
  14. *
  15. * Environment:
  16. *
  17. * Kernel mode
  18. *
  19. *
  20. * Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
  21. * Copyright (c) 1995-2003 Microsoft Corporation. All Rights Reserved.
  22. *
  23. \***************************************************************************/
  24. #include "perm3.h"
  25. #pragma alloc_text(PAGE,Perm3InitializeInterruptBlock)
  26. BOOLEAN
  27. Perm3VideoInterrupt(
  28. PVOID HwDeviceExtension
  29. )
  30. /*++
  31. Routine Description:
  32. Permedia3 interrupt service routine.
  33. THIS ROUTINE CANNOT BE PAGED
  34. Arguments:
  35. HwDeviceExtension
  36. Supplies a pointer to the miniport's device extension.
  37. Return Value:
  38. Return FALSE if it is not our interrupt. Otherwise, we'll dismiss the
  39. interrupt on Permedia 3 before returning TRUE.
  40. --*/
  41. {
  42. PHW_DEVICE_EXTENSION hwDeviceExtension = HwDeviceExtension;
  43. PINTERRUPT_CONTROL_BLOCK pBlock;
  44. ULONG intrFlags;
  45. ULONG enableFlags;
  46. ULONG backIndex;
  47. ULONG bHaveCommandBlockMutex = FALSE;
  48. ULONG errFlags = 0;
  49. pPerm3ControlRegMap pCtrlRegs = hwDeviceExtension->ctrlRegBase[0];
  50. pBlock = &hwDeviceExtension->InterruptControl.ControlBlock;
  51. if(!hwDeviceExtension->InterruptControl.bInterruptsInitialized) {
  52. //
  53. // This is not our interrupt since we don't generate interrupt
  54. // before the interrpt block got initialized
  55. //
  56. return(FALSE);
  57. }
  58. if (hwDeviceExtension->PreviousPowerState != VideoPowerOn) {
  59. //
  60. // We reach here because we are sharing IRQ with other devices
  61. // and another device on the chain is in D0 and functioning
  62. //
  63. return(FALSE);
  64. }
  65. //
  66. // Find out what caused the interrupt. We AND with the enabled interrupts
  67. // since the flags are set if the event occurred even though no interrupt
  68. // was enabled.
  69. //
  70. intrFlags = VideoPortReadRegisterUlong(INT_FLAGS);
  71. enableFlags = VideoPortReadRegisterUlong(INT_ENABLE);
  72. intrFlags &= enableFlags;
  73. if (intrFlags == 0) {
  74. return FALSE;
  75. }
  76. //
  77. // Clear the interrupts we detected.
  78. //
  79. VideoPortWriteRegisterUlong(INT_FLAGS, intrFlags);
  80. VideoPortReadRegisterUlong(INT_FLAGS);
  81. if((pBlock->Control & PXRX_CHECK_VFIFO_IN_VBLANK) ||
  82. (intrFlags & INTR_ERROR_SET)) {
  83. errFlags = VideoPortReadRegisterUlong(ERROR_FLAGS);
  84. //
  85. // Keep a record of the errors. It will help us to debug
  86. // hardware issues
  87. //
  88. if (errFlags & ERROR_VFIFO_UNDERRUN) {
  89. hwDeviceExtension->UnderflowErrors++;
  90. }
  91. if (errFlags & ERROR_OUT_FIFO) {
  92. hwDeviceExtension->OutputFifoErrors++;
  93. }
  94. if (errFlags & ERROR_IN_FIFO) {
  95. hwDeviceExtension->InputFifoErrors++;
  96. }
  97. if (errFlags) {
  98. hwDeviceExtension->TotalErrors++;
  99. }
  100. }
  101. //
  102. // Handle VBLANK interrupt
  103. //
  104. if (intrFlags & INTR_VBLANK_SET) {
  105. //
  106. // Need this only on very first interrupt but it's not a big thing.
  107. //
  108. pBlock->Control |= VBLANK_INTERRUPT_AVAILABLE;
  109. //
  110. // Get General Mutex
  111. //
  112. REQUEST_INTR_CMD_BLOCK_MUTEX((&(pBlock->General)), bHaveCommandBlockMutex);
  113. if(bHaveCommandBlockMutex) {
  114. ULONG ulValue;
  115. //
  116. // DirectDraw needs to have it's VBLANK flag set, when it
  117. // sets the DIRECTDRAW_VBLANK_ENABLED bit.
  118. //
  119. if( pBlock->Control & (DIRECTDRAW_VBLANK_ENABLED | PXRX_SEND_ON_VBLANK_ENABLED | PXRX_CHECK_VFIFO_IN_VBLANK) ) {
  120. if( pBlock->Control & DIRECTDRAW_VBLANK_ENABLED ) {
  121. pBlock->DDRAW_VBLANK = TRUE;
  122. }
  123. //
  124. // Don't need to do anything here. The actual processing is
  125. // lower down, outside of the VBlank mutex.
  126. //
  127. } else {
  128. //
  129. // Disable VBLANK interrupts. DD enables them when it needs to
  130. //
  131. VideoPortWriteRegisterUlong(INT_ENABLE, (enableFlags & ~INTR_ENABLE_VBLANK));
  132. }
  133. //
  134. // If DMA was suspended till VBLANK then simulate a DMA interrupt
  135. // to start it off again.
  136. //
  137. if (pBlock->Control & SUSPEND_DMA_TILL_VBLANK) {
  138. pBlock->Control &= ~SUSPEND_DMA_TILL_VBLANK;
  139. //
  140. // execute the DMA interrupt code
  141. //
  142. intrFlags |= INTR_ERROR_SET;
  143. }
  144. RELEASE_INTR_CMD_BLOCK_MUTEX((&(pBlock->General)));
  145. }
  146. if( (pBlock->Control & PXRX_CHECK_VFIFO_IN_VBLANK) &&
  147. (--hwDeviceExtension->VideoFifoControlCountdown == 0) ) {
  148. //
  149. // It's time to check the error flags for an underrun (we don't
  150. // keep the error interrupt turned on for long because Perm3
  151. // generates a lot of spurious host-in DMA errors)
  152. //
  153. if(enableFlags & INTR_ERROR_SET) {
  154. //
  155. // Turn off the error interrupts now and rely on the periodic VBLANK check
  156. //
  157. enableFlags &= ~INTR_ERROR_SET;
  158. VideoPortWriteRegisterUlong(INT_ENABLE, enableFlags);
  159. }
  160. //
  161. // Set-up counter for our periodic check
  162. //
  163. hwDeviceExtension->VideoFifoControlCountdown = NUM_VBLANKS_BETWEEN_VFIFO_CHECKS;
  164. if(errFlags & ERROR_VFIFO_UNDERRUN) {
  165. if((enableFlags & INTR_ERROR_SET) == 0) {
  166. //
  167. // We've got a video FIFO underrun error: turn on error
  168. // interrupts for a little while in order to catch any
  169. // other errors ASAP
  170. //
  171. hwDeviceExtension->VideoFifoControlCountdown = NUM_VBLANKS_AFTER_VFIFO_ERROR;
  172. VideoPortWriteRegisterUlong(INT_ENABLE,
  173. enableFlags | INTR_ERROR_SET);
  174. }
  175. }
  176. }
  177. }
  178. //
  179. // Handle underrun error
  180. //
  181. if(errFlags & ERROR_VFIFO_UNDERRUN) {
  182. ULONG highWater, lowWater;
  183. //
  184. // Clear the error
  185. //
  186. VideoPortWriteRegisterUlong(ERROR_FLAGS, ERROR_VFIFO_UNDERRUN);
  187. //
  188. // Lower the video FIFO thresholds. If the new upper threshold is 0
  189. // (indicating the thresholds are currently both 1) then we can't
  190. // go any lower, so just leave the underrun bit set (that way at
  191. // least we won't get any more error interrupts)
  192. //
  193. highWater = ((hwDeviceExtension->VideoFifoControl >> 8) & 0xff) - 1;
  194. if(highWater) {
  195. //
  196. // Load up the new FIFO control and clear the underrun bit.
  197. // The lower threshold is set to 8 if the upper threshold
  198. // is >= 15, otherwise it's set to 1/2 the upper threshold
  199. //
  200. lowWater = highWater >= 15 ? 8 : ((highWater + 1) >> 1);
  201. hwDeviceExtension->VideoFifoControl = (1 << 16) |
  202. (highWater << 8) |
  203. lowWater;
  204. do {
  205. VideoPortWriteRegisterUlong(VIDEO_FIFO_CTL,
  206. hwDeviceExtension->VideoFifoControl);
  207. } while(VideoPortReadRegisterUlong(VIDEO_FIFO_CTL) & (1 << 16));
  208. VideoDebugPrint((3, "Perm3: Setting new Video Fifo thresholds to %d and %d\n", highWater, lowWater));
  209. }
  210. }
  211. //
  212. // Handle outfifo error
  213. //
  214. if(errFlags & ERROR_OUT_FIFO) {
  215. //
  216. // If we got here by generating an OutputFIFO error, clear it
  217. //
  218. VideoPortWriteRegisterUlong(ERROR_FLAGS, ERROR_OUT_FIFO);
  219. #ifdef MASK_OUTFIFO_ERROR_INTERRUPT
  220. enableFlags &= ~INTR_ERROR_SET;
  221. VideoPortWriteRegisterUlong(INT_ENABLE, enableFlags);
  222. #endif
  223. }
  224. //
  225. // The error interrupt occurs each time the display driver adds an entry
  226. // to the queue. We treat it exactly as though we had received a DMA
  227. // interrupt.
  228. //
  229. if (intrFlags & (INTR_DMA_SET | INTR_ERROR_SET)) {
  230. //
  231. // if suspended till VBLANK we can't do any DMA.
  232. //
  233. if (pBlock->Control & SUSPEND_DMA_TILL_VBLANK) {
  234. VideoDebugPrint(( 1, "Perm3: DMA suspended till VBLANK\n"));
  235. return(TRUE);
  236. }
  237. //
  238. // If the previous DMA has not completed we can't do anything. We've
  239. // cleared the interrupt flag for this interrupt so even if the DMA
  240. // completes before we return, we'll immediately get another
  241. // interrupt. Since we will be getting another interrupt we do not
  242. // have to clear the InterruptPending flag.
  243. //
  244. if (VideoPortReadRegisterUlong(DMA_COUNT) != 0) {
  245. //
  246. // DMA in progress, leaving
  247. //
  248. return(TRUE);
  249. }
  250. //
  251. // We may return without starting any DMA and hence not expecting any
  252. // interrupt so clear the InterruptPending flag. This will force the
  253. // display driver to wake us. MUST DO THIS BEFORE CHECKING THE QUEUE.
  254. // Since the display driver adds entries and then checks the flag, we
  255. // must do it in the reverse order.
  256. //
  257. pBlock->InterruptPending = 0;
  258. //
  259. // if the DMA queue is empty then we have nothing to do
  260. //
  261. backIndex = pBlock->backIndex;
  262. if (pBlock->frontIndex == backIndex) {
  263. //
  264. // Queue is empty, leaving.
  265. //
  266. return(TRUE);
  267. }
  268. //
  269. // Since we know we'll get a DMA interrupt, we don't need a wakeup so
  270. // set the InterruptPending flag to true.
  271. //
  272. pBlock->InterruptPending = 1;
  273. //
  274. // kick off DMA for the next Q entry and remove it. DO NOT remove from
  275. // the queue first because on a multi-processor machine the display
  276. // driver could modify the now free queue entry before we read it.
  277. //
  278. VideoPortWriteRegisterUlong(DMA_ADDRESS, pBlock->dmaQueue[backIndex].address);
  279. VideoPortWriteRegisterUlong(DMA_COUNT, pBlock->dmaQueue[backIndex].count);
  280. //
  281. // Keep track of where the last DMA to start was from:
  282. //
  283. pBlock->lastAddr = pBlock->dmaQueue[backIndex].address;
  284. //
  285. // now remove the entry from the queue
  286. //
  287. if (++backIndex == pBlock->endIndex)
  288. backIndex = 0;
  289. pBlock->backIndex = backIndex;
  290. }
  291. return TRUE;
  292. }
  293. BOOLEAN
  294. Perm3InitializeInterruptBlock(
  295. PHW_DEVICE_EXTENSION hwDeviceExtension
  296. )
  297. /*++
  298. Routine Description:
  299. Do any initialization needed for interrupts, such as allocating the shared
  300. memory control block.
  301. Arguments:
  302. HwDeviceExtension - Supplies a pointer to the miniport's device extension.
  303. Return Value:
  304. TRUE
  305. --*/
  306. {
  307. PVOID HwDeviceExtension = (PVOID)hwDeviceExtension;
  308. PINTERRUPT_CONTROL_BLOCK pBlock;
  309. PVOID SavedPtr;
  310. PVOID pkdpc;
  311. //
  312. // This is set to zero since it is on longer used
  313. //
  314. hwDeviceExtension->InterruptControl.PhysAddress.LowPart =
  315. hwDeviceExtension->InterruptControl.PhysAddress.HighPart = 0;
  316. //
  317. // Set up the control block
  318. //
  319. pBlock = &hwDeviceExtension->InterruptControl.ControlBlock;
  320. //
  321. // Initialize the circular DMA queue
  322. //
  323. pBlock->frontIndex = pBlock->backIndex = 0;
  324. pBlock->maximumIndex = MAX_DMA_QUEUE_ENTRIES - 1;
  325. //
  326. // The size of the queue we actually use is dynamically configurable but
  327. // initialize it to be as small as possible. This default size will work
  328. // for all interrupt driven DMA buffers regardless of how many buffers
  329. // are actually available.
  330. //
  331. pBlock->endIndex = 2;
  332. //
  333. // Initially no interrupts are available. Later we try to enable the
  334. // interrupts and if they happen the interrupt handler will set the
  335. // available bits in this word. So it's a sort of auto-sensing mechanism.
  336. //
  337. pBlock->Control = 0;
  338. pBlock->InterruptPending = 0;
  339. //
  340. // Initialize the VBLANK interrupt command field
  341. //
  342. pBlock->VBCommand = NO_COMMAND;
  343. //
  344. // Initialize the General update in VBLANK fields (only used by P2)
  345. //
  346. pBlock->General.bDisplayDriverHasAccess = FALSE;
  347. pBlock->General.bMiniportHasAccess = FALSE;
  348. VideoPortZeroMemory( &pBlock->pxrxDMA, sizeof(pBlock->pxrxDMA) );
  349. hwDeviceExtension->InterruptControl.bInterruptsInitialized = TRUE;
  350. hwDeviceExtension->OutputFifoErrors = 0;
  351. hwDeviceExtension->InputFifoErrors = 0;
  352. hwDeviceExtension->UnderflowErrors = 0;
  353. hwDeviceExtension->TotalErrors = 0;
  354. return(TRUE);
  355. }