VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/HDAStreamPeriod.cpp@ 86223

Last change on this file since 86223 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: HDAStreamPeriod.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * HDAStreamPeriod.cpp - Stream period functions for HD Audio.
4 *
5 * Utility functions for handling HDA audio stream periods. Stream period
6 * handling is needed in order to keep track of a stream's timing
7 * and processed audio data.
8 *
9 * As the HDA device only has one bit clock (WALCLK) but audio streams can be
10 * processed at certain points in time, these functions can be used to estimate
11 * and schedule the wall clock (WALCLK) for all streams accordingly.
12 */
13
14/*
15 * Copyright (C) 2017-2020 Oracle Corporation
16 *
17 * This file is part of VirtualBox Open Source Edition (OSE), as
18 * available from http://www.virtualbox.org. This file is free software;
19 * you can redistribute it and/or modify it under the terms of the GNU
20 * General Public License (GPL) as published by the Free Software
21 * Foundation, in version 2 as it comes in the "COPYING" file of the
22 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30#define LOG_GROUP LOG_GROUP_DEV_HDA
31#include <VBox/log.h>
32
33#include <iprt/asm-math.h> /* For ASMMultU64ByU32DivByU32(). */
34
35#include <VBox/vmm/pdmdev.h>
36#include <VBox/vmm/pdmaudioifs.h>
37
38#include "DrvAudio.h"
39#include "HDAStreamPeriod.h"
40
41
42#ifdef IN_RING3 /* entire file currently */
43
44/**
45 * Creates a stream period.
46 *
47 * @return IPRT status code.
48 * @param pPeriod Stream period to initialize.
49 */
50int hdaR3StreamPeriodCreate(PHDASTREAMPERIOD pPeriod)
51{
52 Assert(!(pPeriod->fStatus & HDASTREAMPERIOD_F_VALID));
53
54# ifdef HDA_STREAM_PERIOD_WITH_LOCKING
55 int rc = RTCritSectInit(&pPeriod->CritSect);
56 AssertRCReturnStmt(rc, pPeriod->fStatus = 0, rc);
57# endif
58 pPeriod->fStatus = HDASTREAMPERIOD_F_VALID;
59
60 return VINF_SUCCESS;
61}
62
63/**
64 * Destroys a formerly created stream period.
65 *
66 * @param pPeriod Stream period to destroy.
67 */
68void hdaR3StreamPeriodDestroy(PHDASTREAMPERIOD pPeriod)
69{
70 if (pPeriod->fStatus & HDASTREAMPERIOD_F_VALID)
71 {
72# ifdef HDA_STREAM_PERIOD_WITH_LOCKING
73 RTCritSectDelete(&pPeriod->CritSect);
74# endif
75 pPeriod->fStatus = HDASTREAMPERIOD_F_NONE;
76 }
77}
78
79/**
80 * Initializes a given stream period with needed parameters.
81 *
82 * @return VBox status code.
83 * @param pPeriod Stream period to (re-)initialize. Must be created with hdaR3StreamPeriodCreate() first.
84 * @param u8SD Stream descriptor (serial data #) number to assign this stream period to.
85 * @param u16LVI The HDA stream's LVI value to use for the period calculation.
86 * @param u32CBL The HDA stream's CBL value to use for the period calculation.
87 * @param pStreamCfg Audio stream configuration to use for this period.
88 */
89int hdaR3StreamPeriodInit(PHDASTREAMPERIOD pPeriod,
90 uint8_t u8SD, uint16_t u16LVI, uint32_t u32CBL, PPDMAUDIOSTREAMCFG pStreamCfg)
91{
92 if ( !u16LVI
93 || !u32CBL
94 || !DrvAudioHlpPCMPropsAreValid(&pStreamCfg->Props))
95 {
96 return VERR_INVALID_PARAMETER;
97 }
98
99 /*
100 * Linux guests (at least Ubuntu):
101 * 17632 bytes (CBL) / 4 (frame size) = 4408 frames / 4 (LVI) = 1102 frames per period
102 *
103 * Windows guests (Win10 AU):
104 * 3584 bytes (CBL) / 4 (frame size) = 896 frames / 2 (LVI) = 448 frames per period
105 */
106 unsigned cTotalPeriods = u16LVI + 1;
107
108 if (cTotalPeriods <= 1)
109 cTotalPeriods = 2; /* At least two periods *must* be present (LVI >= 1). */
110
111 uint32_t cFramesToTransfer = (u32CBL / 4 /** @todo Define frame size? */) / cTotalPeriods;
112
113 pPeriod->u8SD = u8SD;
114 pPeriod->u64StartWalClk = 0;
115 pPeriod->u32Hz = pStreamCfg->Props.uHz;
116 pPeriod->u64DurationWalClk = hdaR3StreamPeriodFramesToWalClk(pPeriod, cFramesToTransfer);
117 pPeriod->u64ElapsedWalClk = 0;
118 pPeriod->i64DelayWalClk = 0;
119 pPeriod->cFramesToTransfer = cFramesToTransfer;
120 pPeriod->cFramesTransferred = 0;
121 pPeriod->cIntPending = 0;
122
123 Log3Func(("[SD%RU8] %RU64 long, Hz=%RU32, CBL=%RU32, LVI=%RU16 -> %u periods, %RU32 frames each\n",
124 pPeriod->u8SD, pPeriod->u64DurationWalClk, pPeriod->u32Hz, u32CBL, u16LVI,
125 cTotalPeriods, pPeriod->cFramesToTransfer));
126
127 return VINF_SUCCESS;
128}
129
130/**
131 * Resets a stream period to its initial state.
132 *
133 * @param pPeriod Stream period to reset.
134 */
135void hdaR3StreamPeriodReset(PHDASTREAMPERIOD pPeriod)
136{
137 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
138
139 if (pPeriod->cIntPending)
140 LogRelMax(50, ("HDA: Warning: %RU8 interrupts for stream #%RU8 still pending -- so a period reset might trigger audio hangs\n",
141 pPeriod->cIntPending, pPeriod->u8SD));
142
143 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
144 pPeriod->u64StartWalClk = 0;
145 pPeriod->u64ElapsedWalClk = 0;
146 pPeriod->cFramesTransferred = 0;
147 pPeriod->cIntPending = 0;
148# ifdef LOG_ENABLED
149 pPeriod->Dbg.tsStartNs = 0;
150# endif
151}
152
153/**
154 * Begins a new period life span of a given period.
155 *
156 * @return IPRT status code.
157 * @param pPeriod Stream period to begin new life span for.
158 * @param u64WalClk Wall clock (WALCLK) value to set for the period's starting point.
159 */
160int hdaR3StreamPeriodBegin(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
161{
162 Assert(!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE)); /* No nested calls. */
163
164 pPeriod->fStatus |= HDASTREAMPERIOD_F_ACTIVE;
165 pPeriod->u64StartWalClk = u64WalClk;
166 pPeriod->u64ElapsedWalClk = 0;
167 pPeriod->cFramesTransferred = 0;
168 pPeriod->cIntPending = 0;
169# ifdef LOG_ENABLED
170 pPeriod->Dbg.tsStartNs = RTTimeNanoTS();
171# endif
172
173 Log3Func(("[SD%RU8] Starting @ %RU64 (%RU64 long)\n", pPeriod->u8SD, pPeriod->u64StartWalClk, pPeriod->u64DurationWalClk));
174 return VINF_SUCCESS;
175}
176
177/**
178 * Ends a formerly begun period life span.
179 *
180 * @param pPeriod Stream period to end life span for.
181 */
182void hdaR3StreamPeriodEnd(PHDASTREAMPERIOD pPeriod)
183{
184 Log3Func(("[SD%RU8] Took %zuus\n", pPeriod->u8SD, (RTTimeNanoTS() - pPeriod->Dbg.tsStartNs) / 1000));
185
186 if (!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE))
187 return;
188
189 /* Sanity. */
190 AssertMsg(pPeriod->cIntPending == 0,
191 ("%RU8 interrupts for stream #%RU8 still pending -- so ending a period might trigger audio hangs\n",
192 pPeriod->cIntPending, pPeriod->u8SD));
193 Assert(hdaR3StreamPeriodIsComplete(pPeriod));
194
195 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
196}
197
198/**
199 * Pauses a period. All values remain intact.
200 *
201 * @param pPeriod Stream period to pause.
202 */
203void hdaR3StreamPeriodPause(PHDASTREAMPERIOD pPeriod)
204{
205 AssertMsg((pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE), ("Period %p already in inactive state\n", pPeriod));
206
207 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
208
209 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
210}
211
212/**
213 * Resumes a formerly paused period.
214 *
215 * @param pPeriod Stream period to resume.
216 */
217void hdaR3StreamPeriodResume(PHDASTREAMPERIOD pPeriod)
218{
219 AssertMsg(!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE), ("Period %p already in active state\n", pPeriod));
220
221 pPeriod->fStatus |= HDASTREAMPERIOD_F_ACTIVE;
222
223 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
224}
225
226/**
227 * Locks a stream period for serializing access.
228 *
229 * @returns IPRT status code (safe to ignore, asserted).
230 * @param pPeriod Stream period to lock.
231 */
232int hdaR3StreamPeriodLock(PHDASTREAMPERIOD pPeriod)
233{
234# ifdef HDA_STREAM_PERIOD_WITH_LOCKING
235 int rc = RTCritSectEnter(&pPeriod->CritSect);
236 AssertRC(rc);
237 return rc;
238# else
239 RT_NOREF(pPeriod);
240 return VINF_SUCCESS;
241# endif
242}
243
244/**
245 * Unlocks a formerly locked stream period.
246 *
247 * @param pPeriod Stream period to unlock.
248 */
249void hdaR3StreamPeriodUnlock(PHDASTREAMPERIOD pPeriod)
250{
251# ifdef HDA_STREAM_PERIOD_WITH_LOCKING
252 int rc2 = RTCritSectLeave(&pPeriod->CritSect);
253 AssertRC(rc2);
254# else
255 RT_NOREF(pPeriod);
256# endif
257}
258
259/**
260 * Returns the wall clock (WALCLK) value for a given amount of stream period audio frames.
261 *
262 * @return Calculated wall clock value.
263 * @param pPeriod Stream period to calculate wall clock value for.
264 * @param uFrames Number of audio frames to calculate wall clock value for.
265 *
266 * @remark Calculation depends on the given stream period and assumes a 24 MHz wall clock counter (WALCLK).
267 */
268uint64_t hdaR3StreamPeriodFramesToWalClk(PHDASTREAMPERIOD pPeriod, uint32_t uFrames)
269{
270 /* Prevent division by zero. */
271 const uint32_t uHz = pPeriod->u32Hz ? pPeriod->u32Hz : 1;
272
273 /* 24 MHz wall clock (WALCLK): 42ns resolution. */
274 return ASMMultU64ByU32DivByU32(uFrames, 24000000, uHz);
275}
276
277/**
278 * Returns the absolute wall clock (WALCLK) value for the already elapsed time of
279 * a given stream period.
280 *
281 * @return Absolute elapsed time as wall clock (WALCLK) value.
282 * @param pPeriod Stream period to use.
283 */
284uint64_t hdaR3StreamPeriodGetAbsElapsedWalClk(PHDASTREAMPERIOD pPeriod)
285{
286 return pPeriod->u64StartWalClk
287 + pPeriod->u64ElapsedWalClk
288 + pPeriod->i64DelayWalClk;
289}
290
291/**
292 * Returns the absolute wall clock (WALCLK) value for the calculated end time of
293 * a given stream period.
294 *
295 * @return Absolute end time as wall clock (WALCLK) value.
296 * @param pPeriod Stream period to use.
297 */
298uint64_t hdaR3StreamPeriodGetAbsEndWalClk(PHDASTREAMPERIOD pPeriod)
299{
300 return pPeriod->u64StartWalClk + pPeriod->u64DurationWalClk;
301}
302
303/**
304 * Returns the remaining audio frames to process for a given stream period.
305 *
306 * @return Number of remaining audio frames to process. 0 if all were processed.
307 * @param pPeriod Stream period to return value for.
308 */
309uint32_t hdaR3StreamPeriodGetRemainingFrames(PHDASTREAMPERIOD pPeriod)
310{
311 Assert(pPeriod->cFramesToTransfer >= pPeriod->cFramesTransferred);
312 return pPeriod->cFramesToTransfer - pPeriod->cFramesTransferred;
313}
314
315/**
316 * Tells whether a given stream period has elapsed (time-wise) or not.
317 *
318 * @return true if the stream period has elapsed, false if not.
319 * @param pPeriod Stream period to get status for.
320 */
321bool hdaR3StreamPeriodHasElapsed(PHDASTREAMPERIOD pPeriod)
322{
323 return (pPeriod->u64ElapsedWalClk >= pPeriod->u64DurationWalClk);
324}
325
326/**
327 * Tells whether a given stream period has passed the given absolute wall clock (WALCLK)
328 * time or not
329 *
330 * @return true if the stream period has passed the given time, false if not.
331 * @param pPeriod Stream period to get status for.
332 * @param u64WalClk Absolute wall clock (WALCLK) time to check for.
333 */
334bool hdaR3StreamPeriodHasPassedAbsWalClk(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
335{
336 /* Period not in use? */
337 if (!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE))
338 return true; /* ... implies that it has passed. */
339
340 if (hdaR3StreamPeriodHasElapsed(pPeriod))
341 return true; /* Period already has elapsed. */
342
343 return (pPeriod->u64StartWalClk + pPeriod->u64ElapsedWalClk) >= u64WalClk;
344}
345
346/**
347 * Tells whether a given stream period has some required interrupts pending or not.
348 *
349 * @return true if period has interrupts pending, false if not.
350 * @param pPeriod Stream period to get status for.
351 */
352bool hdaR3StreamPeriodNeedsInterrupt(PHDASTREAMPERIOD pPeriod)
353{
354 return pPeriod->cIntPending > 0;
355}
356
357/**
358 * Acquires (references) an (pending) interrupt for a given stream period.
359 *
360 * @param pPeriod Stream period to acquire interrupt for.
361 *
362 * @remark This routine does not do any actual interrupt processing; it only
363 * keeps track of the required (pending) interrupts for a stream period.
364 */
365void hdaR3StreamPeriodAcquireInterrupt(PHDASTREAMPERIOD pPeriod)
366{
367 uint32_t cIntPending = pPeriod->cIntPending;
368 if (cIntPending)
369 {
370 Log3Func(("[SD%RU8] Already pending\n", pPeriod->u8SD));
371 return;
372 }
373
374 pPeriod->cIntPending++;
375
376 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
377}
378
379/**
380 * Releases (dereferences) a pending interrupt.
381 *
382 * @param pPeriod Stream period to release pending interrupt for.
383 */
384void hdaR3StreamPeriodReleaseInterrupt(PHDASTREAMPERIOD pPeriod)
385{
386 Assert(pPeriod->cIntPending);
387 pPeriod->cIntPending--;
388
389 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
390}
391
392/**
393 * Adds an amount of (processed) audio frames to a given stream period.
394 *
395 * @return IPRT status code.
396 * @param pPeriod Stream period to add audio frames to.
397 * @param framesInc Audio frames to add.
398 */
399void hdaR3StreamPeriodInc(PHDASTREAMPERIOD pPeriod, uint32_t framesInc)
400{
401 pPeriod->cFramesTransferred += framesInc;
402 Assert(pPeriod->cFramesTransferred <= pPeriod->cFramesToTransfer);
403
404 pPeriod->u64ElapsedWalClk = hdaR3StreamPeriodFramesToWalClk(pPeriod, pPeriod->cFramesTransferred);
405 Assert(pPeriod->u64ElapsedWalClk <= pPeriod->u64DurationWalClk);
406
407 Log3Func(("[SD%RU8] cbTransferred=%RU32, u64ElapsedWalClk=%RU64\n",
408 pPeriod->u8SD, pPeriod->cFramesTransferred, pPeriod->u64ElapsedWalClk));
409}
410
411/**
412 * Tells whether a given stream period is considered as complete or not.
413 *
414 * @return true if stream period is complete, false if not.
415 * @param pPeriod Stream period to report status for.
416 *
417 * @remark A stream period is considered complete if it has 1) passed (elapsed) its calculated period time
418 * and 2) processed all required audio frames.
419 */
420bool hdaR3StreamPeriodIsComplete(PHDASTREAMPERIOD pPeriod)
421{
422 const bool fIsComplete = /* Has the period elapsed time-wise? */
423 hdaR3StreamPeriodHasElapsed(pPeriod)
424 /* All frames transferred? */
425 && pPeriod->cFramesTransferred >= pPeriod->cFramesToTransfer;
426# ifdef VBOX_STRICT
427 if (fIsComplete)
428 {
429 Assert(pPeriod->cFramesTransferred == pPeriod->cFramesToTransfer);
430 Assert(pPeriod->u64ElapsedWalClk == pPeriod->u64DurationWalClk);
431 }
432# endif
433
434 Log3Func(("[SD%RU8] Period %s - runtime %RU64 / %RU64 (abs @ %RU64, starts @ %RU64, ends @ %RU64), %RU8 IRQs pending\n",
435 pPeriod->u8SD,
436 fIsComplete ? "COMPLETE" : "NOT COMPLETE YET",
437 pPeriod->u64ElapsedWalClk, pPeriod->u64DurationWalClk,
438 hdaR3StreamPeriodGetAbsElapsedWalClk(pPeriod), pPeriod->u64StartWalClk,
439 hdaR3StreamPeriodGetAbsEndWalClk(pPeriod), pPeriod->cIntPending));
440
441 return fIsComplete;
442}
443
444#endif /* IN_RING3 */
445
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette