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.

1268 lines
46 KiB

  1. /******************************Module*Header*********************************\
  2. *
  3. * ***************
  4. * * SAMPLE CODE *
  5. * ***************
  6. *
  7. * Module Name: Permedia.c
  8. *
  9. * Content: This module implements basic access to the Permedia chip and
  10. * DMA transport. It shows also how to implement synchronization
  11. * between a display driver and the miniport interrupt by using
  12. * a shared buffer.
  13. *
  14. *
  15. *
  16. * Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
  17. * Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved.
  18. \*****************************************************************************/
  19. #include "precomp.h"
  20. #define ALLOC_TAG ALLOC_TAG_EP2P
  21. //----------------------------------------------------------------------------
  22. //
  23. // here some notes to the transport of data to the Permedia Fifo
  24. // via standard CPU writes or DMA:
  25. //
  26. // The Permedia 2 chip allows to download data via three methods:
  27. // 1. program registers by writing once to a specific address
  28. // 2. writing an address and a data tag to a special area on the chip
  29. // 3. writing an address and a data tag to a DMA buffer, then
  30. // download via DMA
  31. //
  32. // The third method is preferred, because the CPU writes fastest to memory
  33. // and the DMA does not stall the CPU. Also many commands can be queued
  34. // in a buffer while the graphic processor continues to render
  35. // independently. Methods one and two need to read the space in the Input
  36. // Fifo before data can be written to the Fifo. The disconnect mode of the
  37. // chip should not be used, because it can stall the CPU in PCI Disconnect/Retry
  38. // cycles, where the CPU is not even able to acknoledge an interrupt.
  39. // On the other hand writing to a DMA buffer introduces a latency compared
  40. // to write directly to the chip registers. The more data is queued in the
  41. // DMA buffer, the higher will be the latency.
  42. //
  43. // Methods one and two force the CPU to access the chip, which costs more
  44. // PCI/AGP bus bandwidth than a DMA burst. Also sequential writes using
  45. // method one are less efficient, because only accesses to consecutive
  46. // addresses can be combined to a burst.
  47. // The special FIFO area on the chip which is used for method two is 2kb
  48. // wide and can be written by using a memory copy. These copies can be
  49. // combined to bursts by the PCI-Bridge. On processors implementing writeback
  50. // caches also normal writes to this area are combined to bursts.
  51. // (in this driver the "Fifo" memory area on the
  52. // chip is not marked as write combined, because writes to the Fifo
  53. // need to preserve the order). Also the data
  54. // format which is written to the chip is exactly the same as in the DMA case.
  55. // For that reason a very simple fallback mechanism can be implemented in case
  56. // the DMA doesn't work on the target system. This could be due to low memory,
  57. // problems in sharing interrupts, incompatible PCI devices etc.
  58. //
  59. // here is a typical piece of code sending some data to the chip:
  60. //
  61. // RESERVEDMAPTR(2); // wait until two entries are left in Fifo
  62. // LD_INPUT_FIFO(__Permedia2TagFogMode,0); // write data
  63. // LD_INPUT_FIFO(__Permedia2TagScissorMode,0);
  64. // COMMITDMAPTR(); // commit write pointer for next DMA flush
  65. // FLUSHDMA(); // do the actual flush (optional)
  66. //
  67. // Here is a brief description of the DMA memory model:
  68. //
  69. // There is one huge DMA buffer. It is organized as a ring and is typically
  70. // between 32kb and 256kb big. There are three main pointers and one helper
  71. // handling the DMA operation. They reside in the shared memory section
  72. // (nonpaged) of the interrupt handler and the display driver.
  73. //
  74. //
  75. // pulDMAPrevStart; // start address of previous DMA
  76. // pulDMANextStart; // start address of next DMA
  77. // pulDMAWritePos; // address of current write pointer
  78. //
  79. // pulDMAWriteEnd; // helper address for reserve function
  80. //
  81. // In the idle case all three pointers have the same value. In the above sample
  82. // the write pointer is incremented by two and the execute command would start
  83. // a 2 command long DMA and setting NextStart to the current value of WritePos and
  84. // PrevStart to the previous NextStart. Since there can only be one DMA active
  85. // at a time, a check is necessary if subsequent DMAs have finished before
  86. // starting a new one. As long as there are no unfinished DMAs pending, the
  87. // current implementation does not use interrupts to save CPU time.
  88. // In the case there is still a DMA pending, a mechanism for flushing the buffer
  89. // is necessary without stalling the CPU. Interrupts are enabled in this case to
  90. // ensure the buffer flush. The interrupt handler in the miniport can also access
  91. // the current pointer positions in the shared memory area. Updates to these
  92. // pointers have to be done carefully and synchronization between the interrupt
  93. // thread and the display driver thread is necessary for some operations.
  94. // On multiprocessor systems, special care has to be taken to handle cases where
  95. // both CPUs access the shared memory area at the same time.
  96. //
  97. // The access to the shared memory area is secured by calls to
  98. // InterlockedExchange on a variable in this area. Pointer updates like
  99. // the "CommitDMAPtr", which are only done one at a time by one thread
  100. // need not to be secured (as long as they are atomic)
  101. // Since the call to InterlockedExchange in the kernel
  102. // is also very expensive, different versions of the FlushDMA function are
  103. // provided for single processor and multiprocessor environments.
  104. //
  105. //-------------------------------------------------------------------------
  106. //-------------------------------------------------------------------------
  107. //
  108. // here are some hints of how to vary parameters of the CPermedia class:
  109. //
  110. // the DMA buffer size can be changed between
  111. // 8kb and 256kb by setting:
  112. //
  113. // #define DMA_BUFFERSIZE 0x40000 // set size between 8kb and 256kb
  114. //
  115. // The 256kb allocation limit is set by VideoPortGetCommonBuffer.
  116. // Also the Permedia2 can only transfer 256 kb in one piece.
  117. // On the Alpha processor we have a limit of 8kb, because some alpha
  118. // machines cannot handle DMAs which pass a 8kb page limit.
  119. //
  120. //-------------------------------------------------------------------------
  121. //-------------------------------------------------------------------------
  122. // on x86 machines we need to call InterlockedExchange in ntoskrnl, but
  123. // the display driver is only allowed to import EngXXX functions. So the
  124. // VideoPort maps the function for us and we call it directly. On other
  125. // platforms InterlockedExchange is implemented as inline. (in fact we
  126. // are calling VideoPortInterlockedExchange)
  127. //
  128. #if defined(_X86_)
  129. #define InterlockedExchange(a,b) (*pP2dma->pInterlockedExchange)(a, b)
  130. #endif
  131. //----------------------------------------------------------------------------
  132. //
  133. // vFree()
  134. //
  135. // frees allocated DMA buffer, instance count to DMA buffer will be
  136. // decremented by one. if usage counts gets down to zero,
  137. // the DMA buffer(s) will be freed.
  138. //
  139. //----------------------------------------------------------------------------
  140. VOID vFree(P2DMA *pP2dma)
  141. {
  142. ULONG MagicNum;
  143. pP2dma->uiInstances--;
  144. if (pP2dma->uiInstances==0)
  145. {
  146. ASSERTDD(pP2dma->bEnabled == FALSE,
  147. "vFree: Trying to free enabled DMA");
  148. if (pP2dma->pSharedDMABuffer != NULL)
  149. {
  150. FreeDMABuffer(pP2dma->hDriver, pP2dma->pSharedDMABuffer);
  151. }
  152. if (pP2dma->pEmulatedDMABuffer != NULL)
  153. {
  154. FreeEmulatedDMABuffer(pP2dma->hDriver, pP2dma->pEmulatedDMABuffer);
  155. }
  156. // Back to zeroed state retaining magic number
  157. MagicNum = pP2dma->ICB.ulMagicNo;
  158. RtlZeroMemory(pP2dma, sizeof(P2DMA));
  159. pP2dma->ICB.ulMagicNo = MagicNum;
  160. }
  161. }
  162. //----------------------------------------------------------------------------
  163. //
  164. // bInitializeP2DMA
  165. //
  166. // Initialize chip registers for use with display driver and decide if we
  167. // will use DMA. DMA will only be used if:
  168. // - the acceleration level is zero (full acc.)
  169. // - the miniport can map at least 8kb of DMA memory for us
  170. // - we get the receipt from the IRQ handler after starting a DMA
  171. // - x86 only: if we get the pointer to the InterlockedExchange function
  172. // in the videoport
  173. //
  174. // TODO: parameters
  175. //
  176. //----------------------------------------------------------------------------
  177. BOOL bInitializeP2DMA(P2DMA *pP2dma,
  178. HANDLE hDriver,
  179. ULONG *pChipBase,
  180. DWORD dwAccelLevel,
  181. BOOL NewReference
  182. )
  183. {
  184. ASSERTDD(pP2dma->bEnabled == FALSE,
  185. "bInitializeP2DMA: DMA already enabled");
  186. if (NewReference)
  187. {
  188. // increment usage count
  189. // we rely here on the fact that the videport initializes the shared
  190. // memory section to zero at start of day
  191. pP2dma->uiInstances++;
  192. if (pP2dma->uiInstances == 1)
  193. {
  194. ASSERTDD(pP2dma->pSharedDMABuffer == NULL,
  195. "Shared DMA Buffer already allocated");
  196. ASSERTDD(pP2dma->pEmulatedDMABuffer == NULL,
  197. "Emulated DMA Buffer already allocated");
  198. }
  199. }
  200. else
  201. {
  202. ASSERTDD(pP2dma->uiInstances != 0, "bInitializeP2DMA: DMA hasn't been initialized");
  203. }
  204. // save pointers to Permedia 2 registers for later use
  205. //
  206. pP2dma->pCtrlBase = pChipBase+CTRLBASE/sizeof(ULONG);
  207. pP2dma->pGPFifo = pChipBase+GPFIFO/sizeof(ULONG);
  208. DISPDBG((5, "Initialize: pCtrlBase=0x%p\n", pP2dma->pCtrlBase));
  209. DISPDBG((5, "Initialize: pGPFifo=0x%p\n", pP2dma->pGPFifo));
  210. BOOL bUseDMA=FALSE;
  211. // read number of processors we are running on:
  212. // If we are on a multiprocessing environment we have to take special care
  213. // about the synchronization of the interrupt
  214. // service routine and the display driver
  215. ULONG ulNumberOfProcessors = 1; // Init to 1 by default.
  216. if(!g_bOnNT40)
  217. EngQuerySystemAttribute(EngNumberOfProcessors,
  218. (ULONG *)&ulNumberOfProcessors);
  219. DISPDBG((1,"running on %ld processor machine",
  220. ulNumberOfProcessors));
  221. //
  222. // Allow DMA initialization only at full acceleration level (0) on NT5.0
  223. // and when the magic number of the miniport is the same as ours
  224. // Otherwise the miniport could use a different version of data structures
  225. // where the synchronization would probably fail. The magic no. is the
  226. // first entry in the shared memory data structure.
  227. //
  228. if ( dwAccelLevel==0 &&
  229. (pP2dma->ICB.ulMagicNo==P2_ICB_MAGICNUMBER) &&
  230. !g_bOnNT40)
  231. {
  232. bUseDMA=TRUE;
  233. }
  234. pP2dma->hDriver=hDriver;
  235. //
  236. // On x86 machines the InterlockedExchange routine is implemented different
  237. // in the single- and multiprocessor versions of the kernel. So we have to
  238. // make sure we call the same function as the interrupt service routine in
  239. // the miniport.
  240. // The miniport returns us a pointer to his InterlockedExchange function,
  241. // which is implemented as __fastcall. Otherwise the lock could also be
  242. // implemented using an x86 assembler xchg instruction, which is
  243. // multiprocessor safe.
  244. //
  245. // On the Alpha architecture the compiler generates inline code for
  246. // InterlockedExchange and the pointer to this function is not needed.
  247. //
  248. #if defined(_X86_)
  249. // get pointer to InterlockedExchange in kernel
  250. pP2dma->pInterlockedExchange=
  251. (PInterlockedExchange) GetPInterlockedExchange(hDriver);
  252. if (pP2dma->pInterlockedExchange==NULL)
  253. {
  254. bUseDMA=FALSE;
  255. }
  256. #endif
  257. // set DMA control status to default
  258. //
  259. WRITE_CTRL_REG(PREG_DMACONTROL,0);
  260. // disable all interrupts
  261. //
  262. WRITE_CTRL_REG(PREG_INTENABLE, 0);
  263. // We turn the register on by default, so no entries written to the Fifo can
  264. // be lost. But the code checks the number of available entries anyway,
  265. // because when the CPU ends up in a PCI Disconnect-Retry cycle because of an
  266. // Fifo overflow, it would not even allow an interrupt to come through.
  267. WRITE_CTRL_REG(PREG_FIFODISCON, DISCONNECT_INPUT_FIFO_ENABLE);
  268. pP2dma->bDMAEmulation=FALSE;
  269. pP2dma->lDMABufferSize=0;
  270. pP2dma->ICB.pDMAActualBufferEnd =
  271. pP2dma->ICB.pDMAWriteEnd =
  272. pP2dma->ICB.pDMAPrevStart=
  273. pP2dma->ICB.pDMANextStart=
  274. pP2dma->ICB.pDMAWritePos = NULL;
  275. pP2dma->ICB.pDMABufferEnd =
  276. pP2dma->ICB.pDMABufferStart=NULL;
  277. //
  278. // the following code first tries to allocate a reasonably sized DMA
  279. // buffer, does some initialization and fires off a DMA transfer to see
  280. // if the systems responds as expected. If the system doesn't, it falls
  281. // back to DMA emulation.
  282. //
  283. if (bUseDMA)
  284. {
  285. //
  286. // preset flush and Check function pointers first
  287. //
  288. //@@BEGIN_DDKSPLIT
  289. #if !MULTITHREADED
  290. //@@END_DDKSPLIT
  291. if (ulNumberOfProcessors==1)
  292. {
  293. pP2dma->pgfnFlushDMA= vFlushDMA;
  294. pP2dma->pgfnCheckEOB= vCheckForEOB;
  295. } else
  296. //@@BEGIN_DDKSPLIT
  297. #endif !MULTITHREADED
  298. //@@END_DDKSPLIT
  299. {
  300. pP2dma->pgfnFlushDMA= vFlushDMAMP;
  301. pP2dma->pgfnCheckEOB= vCheckForEOBMP;
  302. }
  303. // Allocate the DMA buffer shared with videoport
  304. // if we haven't previously allocated one.
  305. if (pP2dma->pSharedDMABuffer == NULL)
  306. {
  307. // allocate a buffer between 8kb and 256kb
  308. pP2dma->lSharedDMABufferSize = DMACMDSIZE;
  309. //
  310. // allocate the DMA buffer in the videoport
  311. //
  312. if (AllocateDMABuffer( pP2dma->hDriver,
  313. &pP2dma->lSharedDMABufferSize,
  314. &pP2dma->pSharedDMABuffer,
  315. &pP2dma->ICB.liDMAPhysAddr))
  316. {
  317. // for now we limit DMA Buffer size on alpha to 8kb, because
  318. // of hardware problems on some Miata machines
  319. #if defined(_ALPHA_)
  320. ASSERTDD(pP2dma->lSharedDMABufferSize<=0x2000,
  321. "DMA Buffer too big for alpha, fix constants!");
  322. #endif
  323. if (pP2dma->lSharedDMABufferSize < DMACMDMINSIZE)
  324. {
  325. DISPDBG((0,"allocated %ld bytes for DMA, not enough! No DMA!",
  326. pP2dma->lSharedDMABufferSize));
  327. FreeDMABuffer( pP2dma->hDriver,
  328. pP2dma->pSharedDMABuffer);
  329. pP2dma->pSharedDMABuffer = NULL;
  330. }
  331. }
  332. else
  333. {
  334. DISPDBG((0,"couldn't allocate memory for DMA"));
  335. pP2dma->pSharedDMABuffer = NULL;
  336. }
  337. }
  338. // Make sure we have a shared DMA buffer
  339. if (pP2dma->pSharedDMABuffer == NULL)
  340. {
  341. bUseDMA=FALSE;
  342. }
  343. else
  344. {
  345. // we always do "ULONG" arithmetics in the DMA routines
  346. pP2dma->lDMABufferSize=pP2dma->lSharedDMABufferSize/sizeof(ULONG);
  347. pP2dma->ICB.ulControl=0;
  348. pP2dma->ICB.pDMABufferStart = pP2dma->pSharedDMABuffer;
  349. pP2dma->ICB.pDMAActualBufferEnd =
  350. pP2dma->ICB.pDMABufferEnd =
  351. pP2dma->ICB.pDMABufferStart+
  352. pP2dma->lDMABufferSize;
  353. pP2dma->ICB.pDMAWriteEnd =
  354. pP2dma->ICB.pDMABufferEnd;
  355. pP2dma->ICB.pDMAPrevStart=
  356. pP2dma->ICB.pDMANextStart=
  357. pP2dma->ICB.pDMAWritePos =
  358. pP2dma->ICB.pDMABufferStart;
  359. // check if we get an interrupt...
  360. // clear the flags before we check for a DMA
  361. WRITE_CTRL_REG( PREG_ERRORFLAGS, 0xffffffffl);
  362. //
  363. // clear DMA, VSync and Error interrupt flags
  364. //
  365. WRITE_CTRL_REG( PREG_INTFLAGS, PREG_INTFLAGS_DMA|
  366. PREG_INTFLAGS_VS|
  367. PREG_INTFLAGS_ERROR);
  368. //
  369. // enable DMA interrupts
  370. //
  371. WRITE_CTRL_REG( PREG_INTENABLE, PREG_INTFLAGS_DMA);
  372. BOOL bIRQsOk=FALSE;
  373. DWORD dwTimeOut=5;
  374. // send a small sequence and see if we get a response
  375. // by the interrupt handler
  376. //
  377. pP2dma->bEnabled = TRUE;
  378. PULONG pTmp=ReserveDMAPtr(pP2dma,10);
  379. LD_INPUT_FIFO(__Permedia2TagDeltaMode, 0);
  380. LD_INPUT_FIFO(__Permedia2TagColorDDAMode, 0);
  381. LD_INPUT_FIFO(__Permedia2TagScissorMode, 0);
  382. LD_INPUT_FIFO(__Permedia2TagTextureColorMode, 0);
  383. LD_INPUT_FIFO(__Permedia2TagFogMode, 0);
  384. CommitDMAPtr(pP2dma,pTmp);
  385. vFlushDMAMP(pP2dma);
  386. pP2dma->bEnabled = FALSE;
  387. //
  388. // The videoport IRQ service routine marks ulControl
  389. // on a DMA Interrupt
  390. //
  391. while (!(pP2dma->ICB.ulControl & DMA_INTERRUPT_AVAILABLE))
  392. {
  393. // wait for some Vsyncs here, then continue
  394. //
  395. if (READ_CTRL_REG( PREG_INTFLAGS) & PREG_INTFLAGS_VS)
  396. {
  397. WRITE_CTRL_REG( PREG_INTFLAGS, PREG_INTFLAGS_VS);
  398. if (--dwTimeOut==0)
  399. break;
  400. }
  401. }
  402. // interrupt service is ok if the IRQ handler marked the flag
  403. //
  404. bIRQsOk=pP2dma->ICB.ulControl & DMA_INTERRUPT_AVAILABLE;
  405. if (!bIRQsOk)
  406. {
  407. // disable IRQs and go back to emulation...
  408. //
  409. WRITE_CTRL_REG( PREG_INTENABLE, 0);
  410. bUseDMA=FALSE;
  411. pP2dma->lDMABufferSize=0;
  412. pP2dma->ICB.pDMAActualBufferEnd =
  413. pP2dma->ICB.pDMAWriteEnd =
  414. pP2dma->ICB.pDMAPrevStart=
  415. pP2dma->ICB.pDMANextStart=
  416. pP2dma->ICB.pDMAWritePos = NULL;
  417. pP2dma->ICB.pDMABufferEnd =
  418. pP2dma->ICB.pDMABufferStart=NULL;
  419. DISPDBG((0,"no interrupts available...no DMA available"));
  420. }
  421. else
  422. {
  423. // VS IRQs can be turned off for now.
  424. // but enable DMA and Error interrupts
  425. pP2dma->ulIntFlags=PREG_INTFLAGS_DMA|PREG_INTFLAGS_ERROR;
  426. WRITE_CTRL_REG(PREG_INTENABLE, pP2dma->ulIntFlags);
  427. WRITE_CTRL_REG(PREG_INTFLAGS, PREG_INTFLAGS_ERROR);
  428. DISPDBG((2,"allocated %ld bytes for DMA, interrupts ok",
  429. pP2dma->lDMABufferSize*4));
  430. }
  431. }
  432. }
  433. if (!bUseDMA)
  434. {
  435. // DMA didn't work, then try to allocate memory for DMA emulation
  436. pP2dma->pgfnFlushDMA= vFlushDMAEmulation;
  437. pP2dma->pgfnCheckEOB= vCheckForEOBEmulation;
  438. if (pP2dma->pEmulatedDMABuffer == NULL)
  439. {
  440. pP2dma->lEmulatedDMABufferSize=DMACMDMINSIZE;
  441. pP2dma->pEmulatedDMABuffer=
  442. AllocateEmulatedDMABuffer( pP2dma->hDriver,
  443. pP2dma->lEmulatedDMABufferSize,
  444. ALLOC_TAG);
  445. if (pP2dma->pEmulatedDMABuffer == NULL)
  446. {
  447. DISPDBG((0,"failed to run in DMA emulation mode"));
  448. return FALSE;
  449. }
  450. }
  451. DISPDBG((0,"running in DMA emulation mode"));
  452. pP2dma->bDMAEmulation=TRUE;
  453. pP2dma->lDMABufferSize = pP2dma->lEmulatedDMABufferSize/sizeof(ULONG);
  454. pP2dma->ICB.pDMABufferStart = pP2dma->pEmulatedDMABuffer;
  455. pP2dma->ICB.pDMAActualBufferEnd =
  456. pP2dma->ICB.pDMABufferEnd =
  457. pP2dma->ICB.pDMABufferStart+
  458. pP2dma->lDMABufferSize;
  459. pP2dma->ICB.pDMAWriteEnd =
  460. pP2dma->ICB.pDMABufferEnd;
  461. pP2dma->ICB.pDMAPrevStart=
  462. pP2dma->ICB.pDMANextStart=
  463. pP2dma->ICB.pDMAWritePos =
  464. pP2dma->ICB.pDMABufferStart;
  465. }
  466. pP2dma->bEnabled = TRUE;
  467. return TRUE;
  468. }
  469. //----------------------------------------------------------------------------
  470. //
  471. // vSyncWithPermedia
  472. //
  473. // Send a sync tag through the Permedia and make sure all pending reads and
  474. // writes are flushed from the graphics pipeline.
  475. //
  476. // MUST be called before accessing the Frame Buffer directly
  477. //
  478. //----------------------------------------------------------------------------
  479. VOID vSyncWithPermedia(P2DMA *pP2dma)
  480. {
  481. PULONG pTmp; // pointer for pTmp in macros
  482. ASSERTDD(pP2dma->bEnabled, "vSyncWithPermedia: not enabled");
  483. pTmp=ReserveDMAPtr(pP2dma,6);
  484. // let the filter tag walk through the whole core
  485. // by setting the filter mode to passthrough
  486. //
  487. LD_INPUT_FIFO(__Permedia2TagFilterMode, 0x400);
  488. LD_INPUT_FIFO(__Permedia2TagSync, 0L);
  489. LD_INPUT_FIFO(__Permedia2TagFilterMode, 0x0);
  490. CommitDMAPtr(pP2dma,pTmp);
  491. (pP2dma->pgfnFlushDMA)(pP2dma);
  492. vWaitDMAComplete(pP2dma);
  493. ULONG ulSync;
  494. //
  495. // now wait until the sync tag has walked through the
  496. // graphic core and shows up at the output
  497. //
  498. do {
  499. if (lWaitOutputFifoReady(pP2dma)==0) break;
  500. ulSync=READ_CTRL_REG(PREG_FIFOINTERFACE);
  501. } while (ulSync != __Permedia2TagSync);
  502. }
  503. //----------------------------------------------------------------------------
  504. //
  505. // vWaitDMAComplete
  506. //
  507. // Flush the DMA Buffer and wait until all data is at least sent to the chip.
  508. // Does not wait until the graphics pipeline is idle.
  509. //
  510. //----------------------------------------------------------------------------
  511. VOID vWaitDMAComplete(P2DMA *pP2dma)
  512. {
  513. while ( READ_CTRL_REG(PREG_INDMACOUNT)!=0 ||
  514. pP2dma->ICB.pDMAWritePos!=pP2dma->ICB.pDMANextStart ||
  515. pP2dma->ICB.pDMAPrevStart!=pP2dma->ICB.pDMANextStart)
  516. {
  517. if (READ_CTRL_REG(PREG_INDMACOUNT)!=0)
  518. {
  519. // stall for 1 us
  520. // we shouldn't access the P2 chip here too often, because
  521. // reading from the DMA register too often would stall an
  522. // ongoing DMA transfer. So we better wait for a microsecond.
  523. // Also we eat up less PCI bus bandwidth by polling only every
  524. // 1 microsecond.
  525. //
  526. StallExecution( pP2dma->hDriver, 1);
  527. }
  528. (pP2dma->pgfnFlushDMA)(pP2dma);
  529. }
  530. }
  531. //----------------------------------------------------------------------------
  532. //
  533. // vBlockLoadInputFifo
  534. //
  535. // pP2dma-----shared
  536. // uiTag------register tag to write the data to
  537. // pImage-----pointer to data
  538. // lWords-----number of pixels to transfer
  539. //
  540. // download a block of data with lWords pixels
  541. // to register uiTag from buffer at pImage. The size of the source pixels
  542. // are DWORDS.
  543. //
  544. //----------------------------------------------------------------------------
  545. VOID vBlockLoadInputFifo( P2DMA *pP2dma, ULONG uiTag, ULONG *pImage, LONG lWords)
  546. {
  547. ASSERTDD(pP2dma->bEnabled, "vBlockLoadInputFifo: not enabled");
  548. while (lWords>0)
  549. {
  550. PULONG pTmp=ReserveDMAPtr(pP2dma,MAXINPUTFIFOLENGTH);
  551. LONG lBufferEntries=GetFreeEntries(pP2dma)-1;
  552. if (lWords < lBufferEntries)
  553. {
  554. lBufferEntries = lWords;
  555. }
  556. *pTmp++ = uiTag | ((lBufferEntries-1) << 16);
  557. lWords -= lBufferEntries;
  558. while (lBufferEntries--)
  559. {
  560. *pTmp++=*pImage++;
  561. }
  562. CommitDMAPtr(pP2dma,pTmp);
  563. (pP2dma->pgfnFlushDMA)(pP2dma);
  564. }
  565. }
  566. //----------------------------------------------------------------------------
  567. //
  568. // lWaitOutputFifoReady
  569. //
  570. // return---number of words ready in output fifo
  571. //
  572. // Wait until some data appears at the output Fifo of the P2. Flush DMA
  573. // if necessary.
  574. //
  575. //----------------------------------------------------------------------------
  576. LONG lWaitOutputFifoReady(P2DMA *pP2dma)
  577. {
  578. ULONG x=1000000L; // equals a timeout of 1s
  579. ULONG uiResult;
  580. while ((uiResult=READ_CTRL_REG(PREG_OUTFIFOWORDS)) == 0)
  581. {
  582. if (x-- == 0)
  583. {
  584. // we will end up here if nothing shows up at the output
  585. // Usually a download operation did not provide the right
  586. // amount of data if we end up here
  587. ASSERTDD( FALSE, "chip output fifo timed out");
  588. break;
  589. }
  590. // Make sure we do not read from the control register too often
  591. // when waiting. Permanent reading from the chip can stall DMA
  592. // downloads
  593. if (READ_CTRL_REG(PREG_INDMACOUNT)!=0)
  594. StallExecution( pP2dma->hDriver, 1); // stall 1us if DMA still busy
  595. else
  596. (pP2dma->pgfnFlushDMA)(pP2dma); // make sure buffer is flushed
  597. }
  598. return uiResult;
  599. }
  600. //----------------------------------------------------------------------------
  601. //
  602. // vFlushDMA
  603. //
  604. // single processor version of FlushDMA
  605. //
  606. // vFlushDMAMP
  607. //
  608. // multiprocessor version of FlushDMA
  609. //
  610. // vFlushDMAEmulation
  611. //
  612. // buffer flush using DMA emulation, where the normal DMA doesn't work
  613. //
  614. // This routine really kicks off DMAs and handles synchronization with the
  615. // miniport interrupt service routine.
  616. //
  617. // several scenarios can happen:
  618. // 1.) DMA is inactive, then just kick off the data currently in the
  619. // buffer
  620. // a) WritePos > NextStart, kick off DMA
  621. // a) otherwise we wrap around, just flush to buffer end
  622. //
  623. // 2.) DMA still active, make sure interrupts are started and let
  624. // the interrupt handler
  625. //
  626. // The synchronization between this routine and the miniport is essential
  627. // for our DMA model to work on Multiprocessor machines. The display driver
  628. // is single threaded, but the miniport interrupt handler can be called
  629. // any time and be processed by another CPU. For that reason we loop with
  630. // InterlockedExchange until we get the lock. The interrupt handler behaves
  631. // a bit different. Since we don't want an interrupt being stalled, it just
  632. // falls through doing nothing when it cannot get the lock, since then the
  633. // DMA start will be handled by the display driver anyway.
  634. //
  635. // For the single processor case InterlockedExchange needs not to be called.
  636. // A simple assignment instead of the lock is enough.
  637. //
  638. //----------------------------------------------------------------------------
  639. //----------------------------------------------------------------------------
  640. //
  641. // VOID vFlushDMAMP()
  642. //
  643. // multiprocessor safe version of FlushDMA. Its basically the same as the single
  644. // processor version, but we are calling here the expensive InterlockedExchange
  645. // functions to lock the shared memory section
  646. //
  647. //----------------------------------------------------------------------------
  648. VOID vFlushDMAMP(P2DMA *pP2dma)
  649. {
  650. ASSERTDD(pP2dma->bEnabled, "vFlushDMAMP: not enabled");
  651. ASSERTDD(!pP2dma->bDMAEmulation, "FlushDMA called with DMA mode disabled");
  652. ASSERTDD(pP2dma->ICB.pDMAWritePos<=
  653. pP2dma->ICB.pDMABufferEnd,"Index exceeds buffer limit");
  654. ASSERTDD(pP2dma->ICB.pDMANextStart<=
  655. pP2dma->ICB.pDMABufferEnd,"NextStart exceeds buffer limit!");
  656. // lock the access to the shared memory section first
  657. while (InterlockedExchange((PLONG)&pP2dma->ICB.ulICBLock,TRUE))
  658. ;
  659. // check if DMA channel is still busy, count is zero if not
  660. if (READ_CTRL_REG(PREG_INDMACOUNT)==0)
  661. {
  662. // this code is called frequently. To help the processors branch
  663. // prediction the most common case should be reached
  664. // without a cond. jump
  665. if (pP2dma->ICB.pDMAWritePos>pP2dma->ICB.pDMANextStart)
  666. {
  667. // This is the most common case for DMA start
  668. // set Permedia 2 DMA unit to fire the DMA
  669. WRITE_CTRL_REG( PREG_INDMAADDRESS, (ULONG)
  670. (pP2dma->ICB.liDMAPhysAddr.LowPart+
  671. (pP2dma->ICB.pDMANextStart-
  672. pP2dma->ICB.pDMABufferStart)*sizeof(ULONG)));
  673. WRITE_CTRL_REG( PREG_INDMACOUNT, (ULONG)
  674. (pP2dma->ICB.pDMAWritePos-
  675. pP2dma->ICB.pDMANextStart));
  676. // in this case we always continue to fill to buffer end,
  677. // iterate the other pointers
  678. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMABufferEnd;
  679. pP2dma->ICB.pDMAPrevStart=pP2dma->ICB.pDMANextStart;
  680. pP2dma->ICB.pDMANextStart=pP2dma->ICB.pDMAWritePos;
  681. // free the shared memory lock
  682. InterlockedExchange((PLONG)&pP2dma->ICB.ulICBLock,FALSE);
  683. return;
  684. } else if (pP2dma->ICB.pDMAWritePos<pP2dma->ICB.pDMANextStart)
  685. {
  686. // wraparound case: the write pointer already wrapped around
  687. // to the beginning and we finish up to the end of the buffer.
  688. WRITE_CTRL_REG( PREG_INDMAADDRESS, (ULONG)
  689. (pP2dma->ICB.liDMAPhysAddr.LowPart+
  690. (pP2dma->ICB.pDMANextStart-
  691. pP2dma->ICB.pDMABufferStart)*sizeof(ULONG)));
  692. WRITE_CTRL_REG( PREG_INDMACOUNT, (ULONG)
  693. (pP2dma->ICB.pDMAActualBufferEnd-
  694. pP2dma->ICB.pDMANextStart));
  695. // reset buffer size back to full length for next round
  696. pP2dma->ICB.pDMAActualBufferEnd=pP2dma->ICB.pDMABufferEnd;
  697. // in this case we don't want the write pointer
  698. // to catch up to last start...
  699. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMANextStart-1;
  700. // iterate last and next start pointer:
  701. pP2dma->ICB.pDMAPrevStart=pP2dma->ICB.pDMANextStart;
  702. pP2dma->ICB.pDMANextStart=pP2dma->ICB.pDMABufferStart;
  703. // free the shared memory lock
  704. InterlockedExchange((PLONG)&pP2dma->ICB.ulICBLock,FALSE);
  705. return;
  706. } else // nothing to do
  707. {
  708. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMABufferEnd;
  709. pP2dma->ICB.pDMAPrevStart=pP2dma->ICB.pDMANextStart;
  710. }
  711. // free the shared memory lock
  712. InterlockedExchange((PLONG)&pP2dma->ICB.ulICBLock,FALSE);
  713. return;
  714. } else
  715. {
  716. // the index pointer has been passed to IRQ service routine, nothing more to do..
  717. //
  718. // unlock shared section,
  719. InterlockedExchange((PLONG)&pP2dma->ICB.ulICBLock,FALSE);
  720. // now we are filling the DMA buffer faster than the hardware
  721. // can follow up and we want to make sure that the DMA channel
  722. // keeps being busy and start the interrupt handler
  723. WRITE_CTRL_REG( PREG_INTFLAGS, PREG_INTFLAGS_DMA);
  724. WRITE_CTRL_REG( PREG_INTENABLE, pP2dma->ulIntFlags );
  725. return;
  726. }
  727. }
  728. //----------------------------------------------------------------------------
  729. //
  730. // VOID vFlushDMA()
  731. //
  732. // single processor version of FlushDMA.
  733. //
  734. //----------------------------------------------------------------------------
  735. VOID vFlushDMA(P2DMA *pP2dma)
  736. {
  737. ASSERTDD(pP2dma->bEnabled, "vFlushDMA: not enabled");
  738. ASSERTDD(!pP2dma->bDMAEmulation, "FlushDMA called with DMA mode disabled");
  739. ASSERTDD(pP2dma->ICB.pDMAWritePos<=
  740. pP2dma->ICB.pDMABufferEnd,"Index exceeds buffer limit");
  741. ASSERTDD(pP2dma->ICB.pDMANextStart<=
  742. pP2dma->ICB.pDMABufferEnd,"NextStart exceeds buffer limit!");
  743. // lock the access to the shared memory section first
  744. pP2dma->ICB.ulICBLock=TRUE;
  745. // check if DMA channel is still busy, count is zero if not
  746. if (READ_CTRL_REG(PREG_INDMACOUNT)==0)
  747. {
  748. // this code is called frequently. To help the processors branch
  749. // prediction the most common case should be reached
  750. // without a cond. jump
  751. if (pP2dma->ICB.pDMAWritePos>pP2dma->ICB.pDMANextStart)
  752. {
  753. // This is the most common case for DMA start
  754. // set Permedia 2 DMA unit to fire the DMA
  755. WRITE_CTRL_REG( PREG_INDMAADDRESS, (ULONG)
  756. (pP2dma->ICB.liDMAPhysAddr.LowPart+
  757. (pP2dma->ICB.pDMANextStart-
  758. pP2dma->ICB.pDMABufferStart)*sizeof(ULONG)));
  759. WRITE_CTRL_REG( PREG_INDMACOUNT, (ULONG)
  760. (pP2dma->ICB.pDMAWritePos-
  761. pP2dma->ICB.pDMANextStart));
  762. // in this case we always continue to fill to buffer end,
  763. // iterate the other pointers
  764. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMABufferEnd;
  765. pP2dma->ICB.pDMAPrevStart=pP2dma->ICB.pDMANextStart;
  766. pP2dma->ICB.pDMANextStart=pP2dma->ICB.pDMAWritePos;
  767. // free the shared memory lock
  768. pP2dma->ICB.ulICBLock=FALSE;
  769. return;
  770. } else if (pP2dma->ICB.pDMAWritePos<pP2dma->ICB.pDMANextStart)
  771. {
  772. // wraparound case: the write pointer already wrapped around
  773. // to the beginning and we finish up to the end of the buffer.
  774. WRITE_CTRL_REG( PREG_INDMAADDRESS, (ULONG)
  775. (pP2dma->ICB.liDMAPhysAddr.LowPart+
  776. (pP2dma->ICB.pDMANextStart-
  777. pP2dma->ICB.pDMABufferStart)*sizeof(ULONG)));
  778. WRITE_CTRL_REG( PREG_INDMACOUNT, (ULONG)
  779. (pP2dma->ICB.pDMAActualBufferEnd-
  780. pP2dma->ICB.pDMANextStart));
  781. // reset buffer size back to full length for next round
  782. pP2dma->ICB.pDMAActualBufferEnd=pP2dma->ICB.pDMABufferEnd;
  783. // in this case we don't want the write pointer
  784. // to catch up to last start...
  785. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMANextStart-1;
  786. // iterate last and next start pointer:
  787. pP2dma->ICB.pDMAPrevStart=pP2dma->ICB.pDMANextStart;
  788. pP2dma->ICB.pDMANextStart=pP2dma->ICB.pDMABufferStart;
  789. // free the shared memory lock
  790. pP2dma->ICB.ulICBLock=FALSE;
  791. return;
  792. } else // nothing to do
  793. {
  794. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMABufferEnd;
  795. pP2dma->ICB.pDMAPrevStart=pP2dma->ICB.pDMANextStart;
  796. }
  797. // free the shared memory lock
  798. pP2dma->ICB.ulICBLock=FALSE;
  799. return;
  800. } else
  801. {
  802. // the index pointer has been passed to IRQ service routine, nothing more to do..
  803. //
  804. // unlock shared section,
  805. pP2dma->ICB.ulICBLock=FALSE;
  806. // now we are filling the DMA buffer faster than the hardware
  807. // can follow up and we want to make sure that the DMA channel
  808. // keeps being busy and start the interrupt handler
  809. WRITE_CTRL_REG( PREG_INTFLAGS, PREG_INTFLAGS_DMA);
  810. WRITE_CTRL_REG( PREG_INTENABLE, pP2dma->ulIntFlags );
  811. return;
  812. }
  813. }
  814. //----------------------------------------------------------------------------
  815. //
  816. // vFlushDMAEmulation
  817. //
  818. // this version of FlushDMA emulates the DMA copy and
  819. // lets the CPU copy the data
  820. //
  821. //----------------------------------------------------------------------------
  822. VOID vFlushDMAEmulation(P2DMA *pP2dma)
  823. {
  824. ASSERTDD(pP2dma->bEnabled, "vFlushDMAEmulation: not enabled");
  825. DISPDBG((10,"Emu::FlushDMA: Write: %04lx Next: %04lx Prev: %04lx End: %04lx",
  826. pP2dma->ICB.pDMAWritePos, pP2dma->ICB.pDMANextStart,
  827. pP2dma->ICB.pDMAPrevStart, pP2dma->ICB.pDMABufferEnd));
  828. ASSERTDD(pP2dma->bDMAEmulation, "FlushDMA called with DMA mode disabled");
  829. ULONG *pData=pP2dma->ICB.pDMABufferStart;
  830. ULONG *pDst;
  831. LONG lWords=(LONG)(pP2dma->ICB.pDMAWritePos-pP2dma->ICB.pDMABufferStart);
  832. while (lWords > 0)
  833. {
  834. LONG lFifoSpace=(LONG)READ_CTRL_REG(PREG_INFIFOSPACE);
  835. if (lWords<lFifoSpace) lFifoSpace=lWords;
  836. lWords -= lFifoSpace;
  837. pDst = pP2dma->pGPFifo;
  838. while (lFifoSpace--)
  839. {
  840. WRITE_REGISTER_ULONG(pDst++,*pData++);
  841. MEMORY_BARRIER();
  842. }
  843. }
  844. pP2dma->ICB.pDMAWritePos=pP2dma->ICB.pDMANextStart=
  845. pP2dma->ICB.pDMAPrevStart=pP2dma->ICB.pDMABufferStart;
  846. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMABufferEnd;
  847. }
  848. //----------------------------------------------------------------------------
  849. //
  850. // bDrawEngineBusy
  851. //
  852. // check if P2 is still busy drawing.
  853. //
  854. // return---- TRUE P2 is still busy
  855. // FALSE P2 has finished drawing and is not busy anymore
  856. //
  857. //----------------------------------------------------------------------------
  858. BOOL bDrawEngineBusy(P2DMA *pP2dma)
  859. {
  860. if (READ_CTRL_REG(PREG_INDMACOUNT)!=0) return TRUE;
  861. if (READ_CTRL_REG(PREG_FIFODISCON) & PREG_FIFODISCON_GPACTIVE)
  862. {
  863. return TRUE;
  864. }
  865. return FALSE;
  866. }
  867. //----------------------------------------------------------------------------
  868. //
  869. // bInVerticalRetrace
  870. //
  871. // Return----- TRUE if beam position is within current vertical sync.
  872. // FALSE otherwise
  873. //
  874. //----------------------------------------------------------------------------
  875. BOOL bInVerticalRetrace(PPDev ppdev)
  876. {
  877. return P2_READ_CTRL_REG(PREG_LINECOUNT) < P2_READ_CTRL_REG(PREG_VBEND);
  878. }
  879. //----------------------------------------------------------------------------
  880. //
  881. // lCurrentLine
  882. //
  883. // returns current line of beam on display
  884. //
  885. //----------------------------------------------------------------------------
  886. LONG lCurrentLine(PPDev ppdev)
  887. {
  888. LONG lScanline=P2_READ_CTRL_REG(PREG_LINECOUNT)-P2_READ_CTRL_REG(PREG_VBEND);
  889. if (lScanline<0) return 0;
  890. return lScanline;
  891. }
  892. //----------------------------------------------------------------------------
  893. //
  894. // vCheckFOREOB (End of Buffer)
  895. //
  896. // Check if buffer end would be overrun and adjust actual buffer size.
  897. // The buffer size will be restored when the DMA handler passes the wrap
  898. // around.
  899. //
  900. //----------------------------------------------------------------------------
  901. VOID vCheckForEOBEmulation( P2DMA *pP2dma, LONG lEntries)
  902. {
  903. vFlushDMAEmulation(pP2dma);
  904. }
  905. //
  906. // multiprocessor safe version of vCheckForEOB
  907. //
  908. VOID vCheckForEOBMP( P2DMA *pP2dma, LONG lEntries)
  909. {
  910. // check for overrun condition over the buffer end:
  911. // if we would exceed the current buffer size,
  912. // LastStart has already wrapped around (LastStart<=writepos)
  913. // but is not at the wraparound position
  914. // and the buffer size was already reset to the full size
  915. if (pP2dma->ICB.pDMAWritePos+lEntries >= pP2dma->ICB.pDMABufferEnd &&
  916. pP2dma->ICB.pDMAPrevStart<=pP2dma->ICB.pDMAWritePos &&
  917. pP2dma->ICB.pDMAPrevStart!=pP2dma->ICB.pDMABufferStart)
  918. {
  919. DISPDBG((10,"wrap condition before: %04lx %04lx %04lx",
  920. pP2dma->ICB.pDMAWritePos,
  921. pP2dma->ICB.pDMANextStart,
  922. pP2dma->ICB.pDMAPrevStart));
  923. while (InterlockedExchange((PLONG)&pP2dma->ICB.ulICBLock,TRUE))
  924. ;
  925. if (pP2dma->ICB.pDMAWritePos==pP2dma->ICB.pDMANextStart)
  926. {
  927. // special case one:
  928. // NextStart equals LastStart, so we just reset Index and Next
  929. // to the buffer start and see if we have enough space
  930. pP2dma->ICB.pDMANextStart=pP2dma->ICB.pDMABufferStart;
  931. } else
  932. {
  933. // index exceeds buffer end on the next block, but there is
  934. // a DMA pending to the current position of Index. Set Buffer
  935. // end temporarily to the current index.
  936. pP2dma->ICB.pDMAActualBufferEnd = pP2dma->ICB.pDMAWritePos;
  937. }
  938. // wrap index around and see if there are enought free entries
  939. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMAPrevStart-1;
  940. pP2dma->ICB.pDMAWritePos=pP2dma->ICB.pDMABufferStart;
  941. InterlockedExchange((PLONG)&pP2dma->ICB.ulICBLock,FALSE);
  942. DISPDBG((10,"wrap condition after: %04lx %04lx %04lx",
  943. pP2dma->ICB.pDMAWritePos,
  944. pP2dma->ICB.pDMANextStart,
  945. pP2dma->ICB.pDMAPrevStart));
  946. }
  947. vFlushDMAMP(pP2dma);
  948. }
  949. VOID vCheckForEOB( P2DMA *pP2dma, LONG lEntries)
  950. {
  951. // check for overrun condition over the buffer end:
  952. // if we would exceed the current buffer size,
  953. // LastStart has already wrapped around (LastStart<=writepos)
  954. // but is not at the wraparound position
  955. // and the buffer size was already reset to the full size
  956. if (pP2dma->ICB.pDMAWritePos+lEntries >= pP2dma->ICB.pDMABufferEnd &&
  957. pP2dma->ICB.pDMAPrevStart<=pP2dma->ICB.pDMAWritePos &&
  958. pP2dma->ICB.pDMAPrevStart!=pP2dma->ICB.pDMABufferStart)
  959. {
  960. DISPDBG((10,"wrap condition before: %04lx %04lx %04lx",
  961. pP2dma->ICB.pDMAWritePos,
  962. pP2dma->ICB.pDMANextStart,
  963. pP2dma->ICB.pDMAPrevStart));
  964. pP2dma->ICB.ulICBLock=TRUE;
  965. if (pP2dma->ICB.pDMAWritePos==pP2dma->ICB.pDMANextStart)
  966. {
  967. // special case one:
  968. // NextStart equals LastStart, so we just reset Index and Next
  969. // to the buffer start and see if we have enough space
  970. pP2dma->ICB.pDMANextStart=pP2dma->ICB.pDMABufferStart;
  971. } else
  972. {
  973. // index exceeds buffer end on the next block, but there is
  974. // a DMA pending to the current position of Index. Set Buffer
  975. // end temporarily to the current index.
  976. pP2dma->ICB.pDMAActualBufferEnd = pP2dma->ICB.pDMAWritePos;
  977. }
  978. // wrap index around and see if there are enought free entries
  979. pP2dma->ICB.pDMAWriteEnd=pP2dma->ICB.pDMAPrevStart-1;
  980. pP2dma->ICB.pDMAWritePos=pP2dma->ICB.pDMABufferStart;
  981. pP2dma->ICB.ulICBLock=FALSE;
  982. DISPDBG((10,"wrap condition after: %04lx %04lx %04lx",
  983. pP2dma->ICB.pDMAWritePos,
  984. pP2dma->ICB.pDMANextStart,
  985. pP2dma->ICB.pDMAPrevStart));
  986. }
  987. vFlushDMA(pP2dma);
  988. }
  989. #if DBG
  990. //----------------------------------------------------------------------------
  991. //
  992. // ReserveDMAPtr
  993. //
  994. // return a pointer to current position in DMA buffer. The function guarantees
  995. // that there are at least lEntries available in the buffer.
  996. // Otherwise the caller can ask GetFreeEntries and adjust the download to
  997. // batch more entries. The caller MUST call CommitDMAPtr after a call to
  998. // to ReserveDMAPtr to readjust the Index pointer.
  999. //
  1000. //----------------------------------------------------------------------------
  1001. ULONG *ReserveDMAPtr(P2DMA *pP2dma, const LONG lEntries)
  1002. {
  1003. ASSERTDD(pP2dma->bEnabled, "ReserveDMAPtr: not enabled");
  1004. ASSERTDD(pP2dma->lDBGState==0,
  1005. "ReserveDMAPtr called, but previous called was not closed");
  1006. //@@BEGIN_DDKSPLIT
  1007. #if MULTITHREADED
  1008. ASSERTDD(pP2dma->ppdev != NULL, "ReserveDMAPtr: pP2dma->ppdev = NULL");
  1009. #endif
  1010. ASSERTLOCK(pP2dma->ppdev, ReserveDMAPtr);
  1011. //@@END_DDKSPLIT
  1012. pP2dma->lDBGState=2;
  1013. while (pP2dma->ICB.pDMAWritePos+lEntries>=
  1014. pP2dma->ICB.pDMAWriteEnd)
  1015. {
  1016. (*pP2dma->pgfnCheckEOB)(pP2dma,lEntries);
  1017. }
  1018. if (lEntries<MAXINPUTFIFOLENGTH)
  1019. pP2dma->pDBGReservedEntries=
  1020. (ULONG *)(lEntries+pP2dma->ICB.pDMAWritePos);
  1021. else
  1022. pP2dma->pDBGReservedEntries=NULL;
  1023. return (ULONG *)pP2dma->ICB.pDMAWritePos;
  1024. }
  1025. //----------------------------------------------------------------------------
  1026. //
  1027. // CommitDMAPtr
  1028. //
  1029. // pDMAPtr----DMA buffer address to which the caller has written to.
  1030. //
  1031. // Readjust write pointer after being reserved by ReserveDMAPtr.
  1032. // By committing the pointer a DMA to the committed position could already
  1033. // be started by interrupt handler!
  1034. //
  1035. //----------------------------------------------------------------------------
  1036. VOID CommitDMAPtr(P2DMA *pP2dma,ULONG *pDMAPtr)
  1037. {
  1038. ASSERTDD(pP2dma->bEnabled, "CommitDMAPtr: not enabled");
  1039. ASSERTDD(pP2dma->lDBGState==2,
  1040. "CommitDMAPtr called, but previous without calling Reserve before");
  1041. pP2dma->lDBGState=0;
  1042. if (pDMAPtr==NULL) return;
  1043. pP2dma->ICB.pDMAWritePos=pDMAPtr;
  1044. ASSERTDD(pP2dma->ICB.pDMAWritePos<=
  1045. pP2dma->ICB.pDMABufferEnd,"CommitDMAPtr: DMA buffer overrun");
  1046. if (pP2dma->pDBGReservedEntries!=NULL)
  1047. {
  1048. ASSERTDD(pP2dma->ICB.pDMAWritePos<=pP2dma->pDBGReservedEntries,
  1049. "reserved not enough entries in ReserveDMAPtr");
  1050. }
  1051. }
  1052. //----------------------------------------------------------------------------
  1053. //
  1054. // GetFreeEntries
  1055. //
  1056. // Get free entries available for consecutive writing to the DMA buffer.
  1057. // The maximum number of returned entries is now MAXBLKSIZE.
  1058. //
  1059. // returns---number of available entries in ULONGS
  1060. //
  1061. //----------------------------------------------------------------------------
  1062. LONG GetFreeEntries(P2DMA *pP2dma)
  1063. {
  1064. LONG EntriesAvailable;
  1065. ASSERTDD(pP2dma->bEnabled, "GetFreeEntries: not enabled");
  1066. EntriesAvailable = (LONG)(pP2dma->ICB.pDMAWriteEnd - pP2dma->ICB.pDMAWritePos);
  1067. return min(MAXBLKSIZE,EntriesAvailable);
  1068. }
  1069. #endif