VirtualBox

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

Last change on this file since 87875 was 87810, 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: 13.8 KB
Line 
1/* $Id: HDAStreamPeriod.cpp 87810 2021-02-19 16:08:22Z 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 pPeriod->fStatus = HDASTREAMPERIOD_F_VALID;
55
56 return VINF_SUCCESS;
57}
58
59/**
60 * Destroys a formerly created stream period.
61 *
62 * @param pPeriod Stream period to destroy.
63 */
64void hdaR3StreamPeriodDestroy(PHDASTREAMPERIOD pPeriod)
65{
66 if (pPeriod->fStatus & HDASTREAMPERIOD_F_VALID)
67 pPeriod->fStatus = HDASTREAMPERIOD_F_NONE;
68}
69
70/**
71 * Initializes a given stream period with needed parameters.
72 *
73 * @return VBox status code.
74 * @param pPeriod Stream period to (re-)initialize. Must be created with hdaR3StreamPeriodCreate() first.
75 * @param u8SD Stream descriptor (serial data #) number to assign this stream period to.
76 * @param u16LVI The HDA stream's LVI value to use for the period calculation.
77 * @param u32CBL The HDA stream's CBL value to use for the period calculation.
78 * @param pStreamCfg Audio stream configuration to use for this period.
79 */
80int hdaR3StreamPeriodInit(PHDASTREAMPERIOD pPeriod,
81 uint8_t u8SD, uint16_t u16LVI, uint32_t u32CBL, PPDMAUDIOSTREAMCFG pStreamCfg)
82{
83 if ( !u16LVI
84 || !u32CBL
85 || !DrvAudioHlpPCMPropsAreValid(&pStreamCfg->Props))
86 {
87 return VERR_INVALID_PARAMETER;
88 }
89
90 /*
91 * Linux guests (at least Ubuntu):
92 * 17632 bytes (CBL) / 4 (frame size) = 4408 frames / 4 (LVI) = 1102 frames per period
93 *
94 * Windows guests (Win10 AU):
95 * 3584 bytes (CBL) / 4 (frame size) = 896 frames / 2 (LVI) = 448 frames per period
96 */
97 unsigned cTotalPeriods = u16LVI + 1;
98
99 if (cTotalPeriods <= 1)
100 cTotalPeriods = 2; /* At least two periods *must* be present (LVI >= 1). */
101
102 uint32_t cFramesToTransfer =
103 (u32CBL / (pStreamCfg->Props.cbSample * pStreamCfg->Props.cChannels /* Frame size */)) / cTotalPeriods;
104
105 pPeriod->u8SD = u8SD;
106 pPeriod->u64StartWalClk = 0;
107 pPeriod->u32Hz = pStreamCfg->Props.uHz;
108 pPeriod->u64DurationWalClk = hdaR3StreamPeriodFramesToWalClk(pPeriod, cFramesToTransfer);
109 pPeriod->u64ElapsedWalClk = 0;
110 pPeriod->i64DelayWalClk = 0;
111 pPeriod->cFramesToTransfer = cFramesToTransfer;
112 pPeriod->cFramesTransferred = 0;
113 pPeriod->cIntPending = 0;
114
115 Log3Func(("[SD%RU8] %RU64 long, Hz=%RU32, CBL=%RU32, LVI=%RU16 -> %u periods, %RU32 frames each\n",
116 pPeriod->u8SD, pPeriod->u64DurationWalClk, pPeriod->u32Hz, u32CBL, u16LVI,
117 cTotalPeriods, pPeriod->cFramesToTransfer));
118
119 return VINF_SUCCESS;
120}
121
122/**
123 * Resets a stream period to its initial state.
124 *
125 * @param pPeriod Stream period to reset.
126 */
127void hdaR3StreamPeriodReset(PHDASTREAMPERIOD pPeriod)
128{
129 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
130
131 if (pPeriod->cIntPending)
132 LogRelMax(50, ("HDA: Warning: %RU8 interrupts for stream #%RU8 still pending -- so a period reset might trigger audio hangs\n",
133 pPeriod->cIntPending, pPeriod->u8SD));
134
135 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
136 pPeriod->u64StartWalClk = 0;
137 pPeriod->u64ElapsedWalClk = 0;
138 pPeriod->cFramesTransferred = 0;
139 pPeriod->cIntPending = 0;
140# ifdef LOG_ENABLED
141 pPeriod->Dbg.tsStartNs = 0;
142# endif
143}
144
145/**
146 * Begins a new period life span of a given period.
147 *
148 * @return IPRT status code.
149 * @param pPeriod Stream period to begin new life span for.
150 * @param u64WalClk Wall clock (WALCLK) value to set for the period's starting point.
151 */
152int hdaR3StreamPeriodBegin(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
153{
154 Assert(!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE)); /* No nested calls. */
155
156 pPeriod->fStatus |= HDASTREAMPERIOD_F_ACTIVE;
157 pPeriod->u64StartWalClk = u64WalClk;
158 pPeriod->u64ElapsedWalClk = 0;
159 pPeriod->cFramesTransferred = 0;
160 pPeriod->cIntPending = 0;
161# ifdef LOG_ENABLED
162 pPeriod->Dbg.tsStartNs = RTTimeNanoTS();
163# endif
164
165 Log3Func(("[SD%RU8] Starting @ %RU64 (%RU64 long)\n", pPeriod->u8SD, pPeriod->u64StartWalClk, pPeriod->u64DurationWalClk));
166 return VINF_SUCCESS;
167}
168
169/**
170 * Ends a formerly begun period life span.
171 *
172 * @param pPeriod Stream period to end life span for.
173 */
174void hdaR3StreamPeriodEnd(PHDASTREAMPERIOD pPeriod)
175{
176 Log3Func(("[SD%RU8] Took %zuus\n", pPeriod->u8SD, (RTTimeNanoTS() - pPeriod->Dbg.tsStartNs) / 1000));
177
178 if (!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE))
179 return;
180
181 /* Sanity. */
182 AssertMsg(pPeriod->cIntPending == 0,
183 ("%RU8 interrupts for stream #%RU8 still pending -- so ending a period might trigger audio hangs\n",
184 pPeriod->cIntPending, pPeriod->u8SD));
185 Assert(hdaR3StreamPeriodIsComplete(pPeriod));
186
187 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
188}
189
190/**
191 * Pauses a period. All values remain intact.
192 *
193 * @param pPeriod Stream period to pause.
194 */
195void hdaR3StreamPeriodPause(PHDASTREAMPERIOD pPeriod)
196{
197 AssertMsg((pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE), ("Period %p already in inactive state\n", pPeriod));
198
199 pPeriod->fStatus &= ~HDASTREAMPERIOD_F_ACTIVE;
200
201 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
202}
203
204/**
205 * Resumes a formerly paused period.
206 *
207 * @param pPeriod Stream period to resume.
208 */
209void hdaR3StreamPeriodResume(PHDASTREAMPERIOD pPeriod)
210{
211 AssertMsg(!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE), ("Period %p already in active state\n", pPeriod));
212
213 pPeriod->fStatus |= HDASTREAMPERIOD_F_ACTIVE;
214
215 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
216}
217
218/**
219 * Returns the wall clock (WALCLK) value for a given amount of stream period audio frames.
220 *
221 * @return Calculated wall clock value.
222 * @param pPeriod Stream period to calculate wall clock value for.
223 * @param uFrames Number of audio frames to calculate wall clock value for.
224 *
225 * @remark Calculation depends on the given stream period and assumes a 24 MHz wall clock counter (WALCLK).
226 */
227uint64_t hdaR3StreamPeriodFramesToWalClk(PHDASTREAMPERIOD pPeriod, uint32_t uFrames)
228{
229 /* Prevent division by zero. */
230 const uint32_t uHz = pPeriod->u32Hz ? pPeriod->u32Hz : 1;
231
232 /* 24 MHz wall clock (WALCLK): 42ns resolution. */
233 return ASMMultU64ByU32DivByU32(uFrames, 24000000, uHz);
234}
235
236/**
237 * Returns the absolute wall clock (WALCLK) value for the already elapsed time of
238 * a given stream period.
239 *
240 * @return Absolute elapsed time as wall clock (WALCLK) value.
241 * @param pPeriod Stream period to use.
242 */
243uint64_t hdaR3StreamPeriodGetAbsElapsedWalClk(PHDASTREAMPERIOD pPeriod)
244{
245 return pPeriod->u64StartWalClk
246 + pPeriod->u64ElapsedWalClk
247 + pPeriod->i64DelayWalClk;
248}
249
250/**
251 * Returns the absolute wall clock (WALCLK) value for the calculated end time of
252 * a given stream period.
253 *
254 * @return Absolute end time as wall clock (WALCLK) value.
255 * @param pPeriod Stream period to use.
256 */
257uint64_t hdaR3StreamPeriodGetAbsEndWalClk(PHDASTREAMPERIOD pPeriod)
258{
259 return pPeriod->u64StartWalClk + pPeriod->u64DurationWalClk;
260}
261
262/**
263 * Returns the remaining audio frames to process for a given stream period.
264 *
265 * @return Number of remaining audio frames to process. 0 if all were processed.
266 * @param pPeriod Stream period to return value for.
267 */
268uint32_t hdaR3StreamPeriodGetRemainingFrames(PHDASTREAMPERIOD pPeriod)
269{
270 Assert(pPeriod->cFramesToTransfer >= pPeriod->cFramesTransferred);
271 return pPeriod->cFramesToTransfer - pPeriod->cFramesTransferred;
272}
273
274/**
275 * Tells whether a given stream period has elapsed (time-wise) or not.
276 *
277 * @return true if the stream period has elapsed, false if not.
278 * @param pPeriod Stream period to get status for.
279 */
280bool hdaR3StreamPeriodHasElapsed(PHDASTREAMPERIOD pPeriod)
281{
282 return (pPeriod->u64ElapsedWalClk >= pPeriod->u64DurationWalClk);
283}
284
285/**
286 * Tells whether a given stream period has passed the given absolute wall clock (WALCLK)
287 * time or not
288 *
289 * @return true if the stream period has passed the given time, false if not.
290 * @param pPeriod Stream period to get status for.
291 * @param u64WalClk Absolute wall clock (WALCLK) time to check for.
292 */
293bool hdaR3StreamPeriodHasPassedAbsWalClk(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
294{
295 /* Period not in use? */
296 if (!(pPeriod->fStatus & HDASTREAMPERIOD_F_ACTIVE))
297 return true; /* ... implies that it has passed. */
298
299 if (hdaR3StreamPeriodHasElapsed(pPeriod))
300 return true; /* Period already has elapsed. */
301
302 return (pPeriod->u64StartWalClk + pPeriod->u64ElapsedWalClk) >= u64WalClk;
303}
304
305/**
306 * Tells whether a given stream period has some required interrupts pending or not.
307 *
308 * @return true if period has interrupts pending, false if not.
309 * @param pPeriod Stream period to get status for.
310 */
311bool hdaR3StreamPeriodNeedsInterrupt(PHDASTREAMPERIOD pPeriod)
312{
313 return pPeriod->cIntPending > 0;
314}
315
316/**
317 * Acquires (references) an (pending) interrupt for a given stream period.
318 *
319 * @param pPeriod Stream period to acquire interrupt for.
320 *
321 * @remark This routine does not do any actual interrupt processing; it only
322 * keeps track of the required (pending) interrupts for a stream period.
323 */
324void hdaR3StreamPeriodAcquireInterrupt(PHDASTREAMPERIOD pPeriod)
325{
326 uint32_t cIntPending = pPeriod->cIntPending;
327 if (cIntPending)
328 {
329 Log3Func(("[SD%RU8] Already pending\n", pPeriod->u8SD));
330 return;
331 }
332
333 pPeriod->cIntPending++;
334
335 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
336}
337
338/**
339 * Releases (dereferences) a pending interrupt.
340 *
341 * @param pPeriod Stream period to release pending interrupt for.
342 */
343void hdaR3StreamPeriodReleaseInterrupt(PHDASTREAMPERIOD pPeriod)
344{
345 Assert(pPeriod->cIntPending);
346 pPeriod->cIntPending--;
347
348 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
349}
350
351/**
352 * Adds an amount of (processed) audio frames to a given stream period.
353 *
354 * @return IPRT status code.
355 * @param pPeriod Stream period to add audio frames to.
356 * @param framesInc Audio frames to add.
357 */
358void hdaR3StreamPeriodInc(PHDASTREAMPERIOD pPeriod, uint32_t framesInc)
359{
360 pPeriod->cFramesTransferred += framesInc;
361 Assert(pPeriod->cFramesTransferred <= pPeriod->cFramesToTransfer);
362
363 pPeriod->u64ElapsedWalClk = hdaR3StreamPeriodFramesToWalClk(pPeriod, pPeriod->cFramesTransferred);
364 Assert(pPeriod->u64ElapsedWalClk <= pPeriod->u64DurationWalClk);
365
366 Log3Func(("[SD%RU8] cbTransferred=%RU32, u64ElapsedWalClk=%RU64\n",
367 pPeriod->u8SD, pPeriod->cFramesTransferred, pPeriod->u64ElapsedWalClk));
368}
369
370/**
371 * Tells whether a given stream period is considered as complete or not.
372 *
373 * @return true if stream period is complete, false if not.
374 * @param pPeriod Stream period to report status for.
375 *
376 * @remark A stream period is considered complete if it has 1) passed (elapsed) its calculated period time
377 * and 2) processed all required audio frames.
378 */
379bool hdaR3StreamPeriodIsComplete(PHDASTREAMPERIOD pPeriod)
380{
381 const bool fIsComplete = /* Has the period elapsed time-wise? */
382 hdaR3StreamPeriodHasElapsed(pPeriod)
383 /* All frames transferred? */
384 && pPeriod->cFramesTransferred >= pPeriod->cFramesToTransfer;
385# ifdef VBOX_STRICT
386 if (fIsComplete)
387 {
388 Assert(pPeriod->cFramesTransferred == pPeriod->cFramesToTransfer);
389 Assert(pPeriod->u64ElapsedWalClk == pPeriod->u64DurationWalClk);
390 }
391# endif
392
393 Log3Func(("[SD%RU8] Period %s - runtime %RU64 / %RU64 (abs @ %RU64, starts @ %RU64, ends @ %RU64), %RU8 IRQs pending\n",
394 pPeriod->u8SD,
395 fIsComplete ? "COMPLETE" : "NOT COMPLETE YET",
396 pPeriod->u64ElapsedWalClk, pPeriod->u64DurationWalClk,
397 hdaR3StreamPeriodGetAbsElapsedWalClk(pPeriod), pPeriod->u64StartWalClk,
398 hdaR3StreamPeriodGetAbsEndWalClk(pPeriod), pPeriod->cIntPending));
399
400 return fIsComplete;
401}
402
403#endif /* IN_RING3 */
404
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