VirtualBox

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

Last change on this file since 87809 was 87809, checked in by vboxsync, 4 years ago

HDA: Got rid of the stream period's additional locking; not needed, as it's part of the stream's own lock already. ticketoem2ref:36

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: HDAStreamPeriod.cpp 87809 2021-02-19 16:05:53Z 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 =
112 (u32CBL / (pStreamCfg->Props.cbSample * pStreamCfg->Props.cChannels /* Frame size */)) / cTotalPeriods;
113
114 pPeriod->u8SD = u8SD;
115 pPeriod->u64StartWalClk = 0;
116 pPeriod->u32Hz = pStreamCfg->Props.uHz;
117 pPeriod->u64DurationWalClk = hdaR3StreamPeriodFramesToWalClk(pPeriod, cFramesToTransfer);
118 pPeriod->u64ElapsedWalClk = 0;
119 pPeriod->i64DelayWalClk = 0;
120 pPeriod->cFramesToTransfer = cFramesToTransfer;
121 pPeriod->cFramesTransferred = 0;
122 pPeriod->cIntPending = 0;
123
124 Log3Func(("[SD%RU8] %RU64 long, Hz=%RU32, CBL=%RU32, LVI=%RU16 -> %u periods, %RU32 frames each\n",
125 pPeriod->u8SD, pPeriod->u64DurationWalClk, pPeriod->u32Hz, u32CBL, u16LVI,
126 cTotalPeriods, pPeriod->cFramesToTransfer));
127
128 return VINF_SUCCESS;
129}
130
131/**
132 * Resets a stream period to its initial state.
133 *
134 * @param pPeriod Stream period to reset.
135 */
136void hdaR3StreamPeriodReset(PHDASTREAMPERIOD pPeriod)
137{
138 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
139
140 if (pPeriod->cIntPending)
141 LogRelMax(50, ("HDA: Warning: %RU8 interrupts for stream #%RU8 still pending -- so a period reset might trigger audio hangs\n",
142 pPeriod->cIntPending, pPeriod->u8SD));
143
144 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
145 pPeriod->u64StartWalClk = 0;
146 pPeriod->u64ElapsedWalClk = 0;
147 pPeriod->cFramesTransferred = 0;
148 pPeriod->cIntPending = 0;
149# ifdef LOG_ENABLED
150 pPeriod->Dbg.tsStartNs = 0;
151# endif
152}
153
154/**
155 * Begins a new period life span of a given period.
156 *
157 * @return IPRT status code.
158 * @param pPeriod Stream period to begin new life span for.
159 * @param u64WalClk Wall clock (WALCLK) value to set for the period's starting point.
160 */
161int hdaR3StreamPeriodBegin(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
162{
163 Assert(!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE)); /* No nested calls. */
164
165 pPeriod->fStatus |= HDASTREAMPERIOD_F_ACTIVE;
166 pPeriod->u64StartWalClk = u64WalClk;
167 pPeriod->u64ElapsedWalClk = 0;
168 pPeriod->cFramesTransferred = 0;
169 pPeriod->cIntPending = 0;
170# ifdef LOG_ENABLED
171 pPeriod->Dbg.tsStartNs = RTTimeNanoTS();
172# endif
173
174 Log3Func(("[SD%RU8] Starting @ %RU64 (%RU64 long)\n", pPeriod->u8SD, pPeriod->u64StartWalClk, pPeriod->u64DurationWalClk));
175 return VINF_SUCCESS;
176}
177
178/**
179 * Ends a formerly begun period life span.
180 *
181 * @param pPeriod Stream period to end life span for.
182 */
183void hdaR3StreamPeriodEnd(PHDASTREAMPERIOD pPeriod)
184{
185 Log3Func(("[SD%RU8] Took %zuus\n", pPeriod->u8SD, (RTTimeNanoTS() - pPeriod->Dbg.tsStartNs) / 1000));
186
187 if (!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE))
188 return;
189
190 /* Sanity. */
191 AssertMsg(pPeriod->cIntPending == 0,
192 ("%RU8 interrupts for stream #%RU8 still pending -- so ending a period might trigger audio hangs\n",
193 pPeriod->cIntPending, pPeriod->u8SD));
194 Assert(hdaR3StreamPeriodIsComplete(pPeriod));
195
196 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
197}
198
199/**
200 * Pauses a period. All values remain intact.
201 *
202 * @param pPeriod Stream period to pause.
203 */
204void hdaR3StreamPeriodPause(PHDASTREAMPERIOD pPeriod)
205{
206 AssertMsg((pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE), ("Period %p already in inactive state\n", pPeriod));
207
208 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
209
210 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
211}
212
213/**
214 * Resumes a formerly paused period.
215 *
216 * @param pPeriod Stream period to resume.
217 */
218void hdaR3StreamPeriodResume(PHDASTREAMPERIOD pPeriod)
219{
220 AssertMsg(!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE), ("Period %p already in active state\n", pPeriod));
221
222 pPeriod->fStatus |= HDASTREAMPERIOD_F_ACTIVE;
223
224 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
225}
226
227/**
228 * Returns the wall clock (WALCLK) value for a given amount of stream period audio frames.
229 *
230 * @return Calculated wall clock value.
231 * @param pPeriod Stream period to calculate wall clock value for.
232 * @param uFrames Number of audio frames to calculate wall clock value for.
233 *
234 * @remark Calculation depends on the given stream period and assumes a 24 MHz wall clock counter (WALCLK).
235 */
236uint64_t hdaR3StreamPeriodFramesToWalClk(PHDASTREAMPERIOD pPeriod, uint32_t uFrames)
237{
238 /* Prevent division by zero. */
239 const uint32_t uHz = pPeriod->u32Hz ? pPeriod->u32Hz : 1;
240
241 /* 24 MHz wall clock (WALCLK): 42ns resolution. */
242 return ASMMultU64ByU32DivByU32(uFrames, 24000000, uHz);
243}
244
245/**
246 * Returns the absolute wall clock (WALCLK) value for the already elapsed time of
247 * a given stream period.
248 *
249 * @return Absolute elapsed time as wall clock (WALCLK) value.
250 * @param pPeriod Stream period to use.
251 */
252uint64_t hdaR3StreamPeriodGetAbsElapsedWalClk(PHDASTREAMPERIOD pPeriod)
253{
254 return pPeriod->u64StartWalClk
255 + pPeriod->u64ElapsedWalClk
256 + pPeriod->i64DelayWalClk;
257}
258
259/**
260 * Returns the absolute wall clock (WALCLK) value for the calculated end time of
261 * a given stream period.
262 *
263 * @return Absolute end time as wall clock (WALCLK) value.
264 * @param pPeriod Stream period to use.
265 */
266uint64_t hdaR3StreamPeriodGetAbsEndWalClk(PHDASTREAMPERIOD pPeriod)
267{
268 return pPeriod->u64StartWalClk + pPeriod->u64DurationWalClk;
269}
270
271/**
272 * Returns the remaining audio frames to process for a given stream period.
273 *
274 * @return Number of remaining audio frames to process. 0 if all were processed.
275 * @param pPeriod Stream period to return value for.
276 */
277uint32_t hdaR3StreamPeriodGetRemainingFrames(PHDASTREAMPERIOD pPeriod)
278{
279 Assert(pPeriod->cFramesToTransfer >= pPeriod->cFramesTransferred);
280 return pPeriod->cFramesToTransfer - pPeriod->cFramesTransferred;
281}
282
283/**
284 * Tells whether a given stream period has elapsed (time-wise) or not.
285 *
286 * @return true if the stream period has elapsed, false if not.
287 * @param pPeriod Stream period to get status for.
288 */
289bool hdaR3StreamPeriodHasElapsed(PHDASTREAMPERIOD pPeriod)
290{
291 return (pPeriod->u64ElapsedWalClk >= pPeriod->u64DurationWalClk);
292}
293
294/**
295 * Tells whether a given stream period has passed the given absolute wall clock (WALCLK)
296 * time or not
297 *
298 * @return true if the stream period has passed the given time, false if not.
299 * @param pPeriod Stream period to get status for.
300 * @param u64WalClk Absolute wall clock (WALCLK) time to check for.
301 */
302bool hdaR3StreamPeriodHasPassedAbsWalClk(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
303{
304 /* Period not in use? */
305 if (!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE))
306 return true; /* ... implies that it has passed. */
307
308 if (hdaR3StreamPeriodHasElapsed(pPeriod))
309 return true; /* Period already has elapsed. */
310
311 return (pPeriod->u64StartWalClk + pPeriod->u64ElapsedWalClk) >= u64WalClk;
312}
313
314/**
315 * Tells whether a given stream period has some required interrupts pending or not.
316 *
317 * @return true if period has interrupts pending, false if not.
318 * @param pPeriod Stream period to get status for.
319 */
320bool hdaR3StreamPeriodNeedsInterrupt(PHDASTREAMPERIOD pPeriod)
321{
322 return pPeriod->cIntPending > 0;
323}
324
325/**
326 * Acquires (references) an (pending) interrupt for a given stream period.
327 *
328 * @param pPeriod Stream period to acquire interrupt for.
329 *
330 * @remark This routine does not do any actual interrupt processing; it only
331 * keeps track of the required (pending) interrupts for a stream period.
332 */
333void hdaR3StreamPeriodAcquireInterrupt(PHDASTREAMPERIOD pPeriod)
334{
335 uint32_t cIntPending = pPeriod->cIntPending;
336 if (cIntPending)
337 {
338 Log3Func(("[SD%RU8] Already pending\n", pPeriod->u8SD));
339 return;
340 }
341
342 pPeriod->cIntPending++;
343
344 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
345}
346
347/**
348 * Releases (dereferences) a pending interrupt.
349 *
350 * @param pPeriod Stream period to release pending interrupt for.
351 */
352void hdaR3StreamPeriodReleaseInterrupt(PHDASTREAMPERIOD pPeriod)
353{
354 Assert(pPeriod->cIntPending);
355 pPeriod->cIntPending--;
356
357 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
358}
359
360/**
361 * Adds an amount of (processed) audio frames to a given stream period.
362 *
363 * @return IPRT status code.
364 * @param pPeriod Stream period to add audio frames to.
365 * @param framesInc Audio frames to add.
366 */
367void hdaR3StreamPeriodInc(PHDASTREAMPERIOD pPeriod, uint32_t framesInc)
368{
369 pPeriod->cFramesTransferred += framesInc;
370 Assert(pPeriod->cFramesTransferred <= pPeriod->cFramesToTransfer);
371
372 pPeriod->u64ElapsedWalClk = hdaR3StreamPeriodFramesToWalClk(pPeriod, pPeriod->cFramesTransferred);
373 Assert(pPeriod->u64ElapsedWalClk <= pPeriod->u64DurationWalClk);
374
375 Log3Func(("[SD%RU8] cbTransferred=%RU32, u64ElapsedWalClk=%RU64\n",
376 pPeriod->u8SD, pPeriod->cFramesTransferred, pPeriod->u64ElapsedWalClk));
377}
378
379/**
380 * Tells whether a given stream period is considered as complete or not.
381 *
382 * @return true if stream period is complete, false if not.
383 * @param pPeriod Stream period to report status for.
384 *
385 * @remark A stream period is considered complete if it has 1) passed (elapsed) its calculated period time
386 * and 2) processed all required audio frames.
387 */
388bool hdaR3StreamPeriodIsComplete(PHDASTREAMPERIOD pPeriod)
389{
390 const bool fIsComplete = /* Has the period elapsed time-wise? */
391 hdaR3StreamPeriodHasElapsed(pPeriod)
392 /* All frames transferred? */
393 && pPeriod->cFramesTransferred >= pPeriod->cFramesToTransfer;
394# ifdef VBOX_STRICT
395 if (fIsComplete)
396 {
397 Assert(pPeriod->cFramesTransferred == pPeriod->cFramesToTransfer);
398 Assert(pPeriod->u64ElapsedWalClk == pPeriod->u64DurationWalClk);
399 }
400# endif
401
402 Log3Func(("[SD%RU8] Period %s - runtime %RU64 / %RU64 (abs @ %RU64, starts @ %RU64, ends @ %RU64), %RU8 IRQs pending\n",
403 pPeriod->u8SD,
404 fIsComplete ? "COMPLETE" : "NOT COMPLETE YET",
405 pPeriod->u64ElapsedWalClk, pPeriod->u64DurationWalClk,
406 hdaR3StreamPeriodGetAbsElapsedWalClk(pPeriod), pPeriod->u64StartWalClk,
407 hdaR3StreamPeriodGetAbsEndWalClk(pPeriod), pPeriod->cIntPending));
408
409 return fIsComplete;
410}
411
412#endif /* IN_RING3 */
413
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