Windows NT 4.0 source code leak
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.

459 lines
11 KiB

4 years ago
  1. /*
  2. * stream.c
  3. *
  4. * 32-bit Video Capture driver
  5. * hardware-specific streaming routines for Bravado board.
  6. *
  7. *
  8. *
  9. * Geraint Davies, Feb 93
  10. */
  11. #include <vckernel.h>
  12. #include <bravado.h>
  13. #include "hardware.h"
  14. #include "vidcio.h"
  15. #include "profile.h"
  16. #if DBG
  17. profiling prf_core, prf_line;
  18. #endif
  19. /*
  20. * outline of data streaming interface:
  21. *
  22. *
  23. * StreamInit will be called to initialise for streaming.
  24. * This will be followed by a call to StreamStart to start
  25. * streaming (immediately). Thus any time-consuming setup or cueing should
  26. * be done in the StreamInit call.
  27. *
  28. * After StreamStart has enabled interrupts, our InterruptAck routine
  29. * will be called on each interrupt. This is responsible for:
  30. * -- clearing and re-enabling interrupts
  31. * -- determining if it is time yet to capture a frame (based on the
  32. * microsecs/frame argument to StreamInit).
  33. *
  34. * If InterruptAck returns TRUE to request capture service, then the
  35. * CaptureService function will be called at some later point - IF there is
  36. * a user-supplied buffer to copy into. At this point, the buffer will be
  37. * locked down and the data can be copied in, subject to any conversion.
  38. * If there is no buffer, the capture service routine will be called with
  39. * a NULL buffer pointer, for it to discard any data and re-arm for capture:
  40. * we dont currently use this in the bravado driver.
  41. *
  42. *
  43. * Mutual exclusion outside of this module ensures that only one
  44. * of the init/fini/start/stop functions is called at one time. However,
  45. * there is nothing to prevent the InterruptAcknowledge or the
  46. * CaptureService being called at any time. The interrupt ack routine
  47. * needs to package up all code that needs to be synchronised (in
  48. * our case, access to the PCV index register) in calls to
  49. * VC_SynchronizeExecution.
  50. *
  51. * The capture service routine only accesses the frame buffer, and no other
  52. * routine does that, so there is no contention. If there were any
  53. * contention, we would have to package the code into calls to
  54. * VC_SynchronizeDPC (for sync between the CaptureService and the
  55. * passive-level functions) or VC_SynchronizeExecution (for sync with
  56. * the interrupt routine as well as everything else).
  57. */
  58. VOID HW_EnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
  59. VOID HW_ReEnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
  60. VOID HW_DisableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
  61. /*
  62. * initialise and cue ready for streaming from the framebuffer into
  63. * memory. The argument gives the desired frame rate.
  64. *
  65. * We have very little to do at this point except store the msperframe arg.
  66. *
  67. */
  68. BOOLEAN
  69. HW_StreamInit(PDEVICE_INFO pDevInfo, ULONG microsecperframe)
  70. {
  71. PHWINFO pHw;
  72. pHw = VC_GetHWInfo(pDevInfo);
  73. /* remember the frame rate. Keep it in microsec per frame
  74. * to reduce the rounding errors that would be introduced if we
  75. * did everything in millisecs
  76. */
  77. pHw->dwMicroPerFrame = microsecperframe;
  78. /* check that a format has been set */
  79. if (pHw->Format == FmtInvalid) {
  80. dprintf1(("trying to capture without setting format"));
  81. return(FALSE);
  82. }
  83. return(TRUE);
  84. }
  85. /* clean up after streaming. nothing to do here */
  86. BOOLEAN
  87. HW_StreamFini(PDEVICE_INFO pDevInfo)
  88. {
  89. return (TRUE);
  90. }
  91. /*
  92. * start streaming data into the users buffers.
  93. *
  94. * Reset the counters and flag that capture is in progress.
  95. * Then enable interrupts (per frame) and wait for the right number
  96. * of interrupts.
  97. */
  98. BOOLEAN
  99. HW_StreamStart(PDEVICE_INFO pDevInfo)
  100. {
  101. /* we set the microsecs per vsync interrupt based on the
  102. * PAl/NTSC field rate, and we use this to update the video
  103. * clock every interrupt. When this reaches the next frame time,
  104. * we capture the frame.
  105. *
  106. */
  107. PHWINFO pHw = VC_GetHWInfo(pDevInfo);
  108. pHw->dwNextFrameNr = 0;
  109. pHw->dwMicroPerInterrupt = (pHw->VideoStd == PAL) ? PAL_MICROSPERFRAME : NTSC_MICROSPERFRAME;
  110. pHw->dwMicroPerInterrupt /= 2;
  111. pHw->dwNextFrameTime = 0;
  112. pHw->dwVideoTime = 0;
  113. pHw->dwInterruptCount = 0;
  114. pHw->bVideoIn = TRUE;
  115. pHw->bCapturing = FALSE;
  116. pHw->iNotBusy = 0;
  117. INIT_PROFILING(&prf_core);
  118. INIT_PROFILING(&prf_line);
  119. /* enable acquisition into the buffer */
  120. if (!HW_Capture(pDevInfo, TRUE)) {
  121. return(FALSE);
  122. }
  123. HW_EnableInts(pDevInfo, FALSE);
  124. return(TRUE);
  125. }
  126. /*
  127. * stop streaming - simply disable interrupts on the PCVideo chip.
  128. *
  129. */
  130. BOOLEAN
  131. HW_StreamStop(PDEVICE_INFO pDevInfo)
  132. {
  133. PHWINFO pHw = VC_GetHWInfo(pDevInfo);
  134. pHw->bVideoIn = FALSE;
  135. HW_DisableInts(pDevInfo, FALSE);
  136. #if DBG
  137. if (PROFILING_COUNT(&prf_core) > 1) {
  138. dprintf1(("core loop (%d) %d usecs/frame",
  139. PROFILING_COUNT(&prf_core), PROFILING_TIME(&prf_core)));
  140. dprintf1(("line loop (%d) %d usecs/line",
  141. PROFILING_COUNT(&prf_line), PROFILING_TIME(&prf_line)));
  142. }
  143. #endif
  144. return(TRUE);
  145. }
  146. /*
  147. * get the position - for this we return the number of millisecs
  148. * since capture began, according to the video vsync clock
  149. */
  150. ULONG
  151. HW_StreamGetPosition(PDEVICE_INFO pDevInfo)
  152. {
  153. PHWINFO pHw = VC_GetHWInfo(pDevInfo);
  154. return( (ULONG) pHw->dwVideoTime);
  155. }
  156. /*
  157. * interrupt acknowledge routine. This is called to ack the interrupt
  158. * and re-enable it for next time. It should return TRUE if it is time
  159. * to capture a frame.
  160. *
  161. * If we are not currently capturing (bVideoIn is false), then disable
  162. * interrupts and stop. Inc the interrupt count in any case, since the
  163. * interrupt may have been generated by HW_Init to test the hardware.
  164. */
  165. BOOLEAN
  166. HW_InterruptAcknowledge(PDEVICE_INFO pDevInfo)
  167. {
  168. PHWINFO pHw = VC_GetHWInfo(pDevInfo);
  169. pHw->dwInterruptCount++;
  170. /* clear and re-enable interrupts */
  171. HW_ReEnableInts(pDevInfo, TRUE);
  172. /* are we actually streaming ? */
  173. if (!pHw->bVideoIn) {
  174. HW_DisableInts(pDevInfo, TRUE);
  175. return(FALSE);
  176. }
  177. pHw->dwVideoTime = (pHw->dwInterruptCount * pHw->dwMicroPerInterrupt) / 1000;
  178. // we only capture on even field interrupts
  179. if ((pHw->dwInterruptCount & 1) ||
  180. (pHw->dwVideoTime < pHw->dwNextFrameTime)) {
  181. if (pHw->bCapturing) {
  182. pHw->iNotBusy = 0;
  183. } else {
  184. pHw->iNotBusy++;
  185. }
  186. return(FALSE);
  187. } else {
  188. // check that we are not overrunning
  189. if (pHw->bCapturing) {
  190. //dprintf(("overrun"));
  191. pHw->iNotBusy = 0;
  192. return(FALSE);
  193. }
  194. /* check that we have had enough fields with
  195. * acquire enabled for a frame to be captured
  196. *
  197. * - check the acquisition mode to see if we are capturing
  198. * 1 or 2 fields.
  199. */
  200. if ((pHw->iNotBusy < 1) ||
  201. ((pHw->iNotBusy < 2) && !(pHw->bRegPCVideo[REG_ACQMODE] & 0x4))) {
  202. // we didn't re-enable soon enough to get this frame
  203. return(FALSE);
  204. }
  205. // reset the count of safely captured fields
  206. pHw->iNotBusy = 0;
  207. /* advance next frame time */
  208. pHw->dwNextFrameNr++;
  209. pHw->dwNextFrameTime = (pHw->dwNextFrameNr * pHw->dwMicroPerFrame) / 1000;
  210. return(TRUE);
  211. }
  212. }
  213. /*
  214. * Capture a frame - copy the frame buffer into the buffer passed
  215. * as argument. The user buffer is at this point locked down and
  216. * mapped into system memory.
  217. *
  218. * returns bytes written to buffer PUCHAR (whose length is ULONG)
  219. * called with null if no buffer - nothing to do in this case.
  220. *
  221. * returns the time stamp in pTimeStamp (the millisec count
  222. * since capture began - based on the number of video syncs and the
  223. * video standard (PAL, NTSC...).
  224. */
  225. ULONG
  226. HW_CaptureService(
  227. PDEVICE_INFO pDevInfo,
  228. PUCHAR pBuffer,
  229. PULONG pTimeStamp,
  230. ULONG BufferLength)
  231. {
  232. PHWINFO pHw = VC_GetHWInfo(pDevInfo);
  233. PUCHAR pFrame = VC_GetFrameBuffer(pDevInfo);
  234. ULONG BitmapSize;
  235. /*
  236. * do nothing if no buffer is ready
  237. */
  238. if (pBuffer == NULL) {
  239. return(0);
  240. }
  241. *pTimeStamp = (ULONG) pHw->dwVideoTime;
  242. /*
  243. * disable acquisition: we can't access the frame buffer at the
  244. * same time as acquisition
  245. */
  246. pHw->bCapturing = TRUE;
  247. HW_Capture(pDevInfo, FALSE);
  248. START_PROFILING(&prf_core);
  249. switch(pHw->Format) {
  250. case FmtPal8:
  251. /*
  252. * convert to palettised data (using the translation table) while
  253. * copying out of the frame buffer
  254. */
  255. CopyYUVToPal8(pBuffer,
  256. pFrame,
  257. pHw->pXlate,
  258. pHw->dwWidth,
  259. pHw->dwHeight,
  260. FRAMEBUFFERWIDTH);
  261. BitmapSize = pHw->dwHeight * pHw->dwWidth;
  262. break;
  263. case FmtRGB555:
  264. /*
  265. * convert to 16-bit RGB while copying from the buffer (using
  266. * the translation table)
  267. */
  268. CopyYUVToRGB555(pBuffer,
  269. pFrame,
  270. (PWORD) pHw->pXlate,
  271. pHw->dwWidth,
  272. pHw->dwHeight,
  273. FRAMEBUFFERWIDTH);
  274. BitmapSize = pHw->dwHeight * pHw->dwWidth * 2;
  275. break;
  276. case FmtRGB24:
  277. /*
  278. * convert to 24-bit RGB while copying from the buffer*/
  279. CopyYUVToRGB24(pBuffer,
  280. pFrame,
  281. pHw->dwWidth,
  282. pHw->dwHeight,
  283. FRAMEBUFFERWIDTH);
  284. BitmapSize = pHw->dwHeight * pHw->dwWidth * 3;
  285. break;
  286. case FmtYUV:
  287. /* straight copy of the rectangle. call the generic
  288. * rectangle copy routine - tell it sizes in bytes,
  289. * not pixels
  290. */
  291. CopyRectFromIOMemory(pBuffer,
  292. pFrame,
  293. pHw->dwWidth * 2, // width in bytes, not pixels
  294. pHw->dwHeight,
  295. FRAMEBUFFERWIDTH);
  296. BitmapSize = pHw->dwHeight * pHw->dwWidth * 2;
  297. break;
  298. }
  299. STOP_PROFILING(&prf_core);
  300. /*
  301. * now that we have finished with the frame buffer, re-start
  302. * acquisition
  303. */
  304. HW_Capture(pDevInfo, TRUE);
  305. pHw->bCapturing = FALSE;
  306. return(BitmapSize);
  307. }
  308. /* -- internal functions ----------------------------------------------*/
  309. /*
  310. * enable vertical-sync interrupts. We use one interrupt per field
  311. */
  312. VOID
  313. HW_EnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
  314. {
  315. if (bInIsr) {
  316. SYNC_REG_ARG SyncArg;
  317. SyncArg.pDevInfo = pDevInfo;
  318. SyncArg.bRegister = REG_INTERRUPT;
  319. SyncArg.bData = 0x3;
  320. HW_SetPCVideoReg_Sync(&SyncArg);
  321. } else {
  322. HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0x3);
  323. }
  324. }
  325. /*
  326. * clear an interrupt and re-enable
  327. */
  328. VOID
  329. HW_ReEnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
  330. {
  331. if (bInIsr) {
  332. SYNC_REG_ARG SyncArg;
  333. SyncArg.pDevInfo = pDevInfo;
  334. SyncArg.bRegister = REG_INTERRUPT;
  335. SyncArg.bData = 0x0;
  336. HW_SetPCVideoReg_Sync(&SyncArg);
  337. SyncArg.bData = 0x3;
  338. HW_SetPCVideoReg_Sync(&SyncArg);
  339. } else {
  340. // clear the interrupt
  341. HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0);
  342. // re-enable frame-based interrupts
  343. HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0x3);
  344. }
  345. }
  346. /* disable interrupts on the card */
  347. VOID
  348. HW_DisableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
  349. {
  350. if (bInIsr) {
  351. SYNC_REG_ARG SyncArg;
  352. SyncArg.pDevInfo = pDevInfo;
  353. SyncArg.bRegister = REG_INTERRUPT;
  354. SyncArg.bData = 0x0;
  355. HW_SetPCVideoReg_Sync(&SyncArg);
  356. } else {
  357. HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0);
  358. }
  359. }