VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevHDACommon.cpp@ 69919

Last change on this file since 69919 was 69919, checked in by vboxsync, 7 years ago

Audio/HDA: Integrated fixes up to r119397 from 5.1-audio-timing branch into trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.9 KB
Line 
1/* $Id: DevHDACommon.cpp 69919 2017-12-04 14:00:05Z vboxsync $ */
2/** @file
3 * DevHDACommon.cpp - Shared HDA device functions.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/assert.h>
23#include <iprt/err.h>
24
25#define LOG_GROUP LOG_GROUP_DEV_HDA
26#include <VBox/log.h>
27
28
29#include "DevHDA.h"
30#include "DevHDACommon.h"
31
32#include "HDAStream.h"
33
34
35#ifndef DEBUG
36/**
37 * Processes (de/asserts) the interrupt according to the HDA's current state.
38 *
39 * @returns IPRT status code.
40 * @param pThis HDA state.
41 */
42int hdaProcessInterrupt(PHDASTATE pThis)
43#else
44/**
45 * Processes (de/asserts) the interrupt according to the HDA's current state.
46 * Debug version.
47 *
48 * @returns IPRT status code.
49 * @param pThis HDA state.
50 * @param pszSource Caller information.
51 */
52int hdaProcessInterrupt(PHDASTATE pThis, const char *pszSource)
53#endif
54{
55 uint32_t uIntSts = hdaGetINTSTS(pThis);
56
57 HDA_REG(pThis, INTSTS) = uIntSts;
58
59 /* NB: It is possible to have GIS set even when CIE/SIEn are all zero; the GIS bit does
60 * not control the interrupt signal. See Figure 4 on page 54 of the HDA 1.0a spec.
61 */
62 /* Global Interrupt Enable (GIE) set? */
63 if ( (HDA_REG(pThis, INTCTL) & HDA_INTCTL_GIE)
64 && (HDA_REG(pThis, INTSTS) & HDA_REG(pThis, INTCTL) & (HDA_INTCTL_CIE | HDA_STRMINT_MASK)))
65 {
66 Log3Func(("Asserted (%s)\n", pszSource));
67
68 PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 1 /* Assert */);
69 pThis->u8IRQL = 1;
70
71#ifdef DEBUG
72 pThis->Dbg.IRQ.tsAssertedNs = RTTimeNanoTS();
73 pThis->Dbg.IRQ.tsProcessedLastNs = pThis->Dbg.IRQ.tsAssertedNs;
74#endif
75 }
76 else
77 {
78 Log3Func(("Deasserted (%s)\n", pszSource));
79
80 PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 0 /* Deassert */);
81 pThis->u8IRQL = 0;
82 }
83
84 return VINF_SUCCESS;
85}
86
87/**
88 * Retrieves the currently set value for the wall clock.
89 *
90 * @return IPRT status code.
91 * @return Currently set wall clock value.
92 * @param pThis HDA state.
93 *
94 * @remark Operation is atomic.
95 */
96uint64_t hdaWalClkGetCurrent(PHDASTATE pThis)
97{
98 return ASMAtomicReadU64(&pThis->u64WalClk);
99}
100
101#ifdef IN_RING3
102/**
103 * Sets the actual WALCLK register to the specified wall clock value.
104 * The specified wall clock value only will be set (unless fForce is set to true) if all
105 * handled HDA streams have passed (in time) that value. This guarantees that the WALCLK
106 * register stays in sync with all handled HDA streams.
107 *
108 * @return true if the WALCLK register has been updated, false if not.
109 * @param pThis HDA state.
110 * @param u64WalClk Wall clock value to set WALCLK register to.
111 * @param fForce Whether to force setting the wall clock value or not.
112 */
113bool hdaWalClkSet(PHDASTATE pThis, uint64_t u64WalClk, bool fForce)
114{
115 const bool fFrontPassed = hdaStreamPeriodHasPassedAbsWalClk (&hdaGetStreamFromSink(pThis, &pThis->SinkFront)->State.Period,
116 u64WalClk);
117 const uint64_t u64FrontAbsWalClk = hdaStreamPeriodGetAbsElapsedWalClk(&hdaGetStreamFromSink(pThis, &pThis->SinkFront)->State.Period);
118#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
119# error "Implement me!"
120#endif
121
122 const bool fLineInPassed = hdaStreamPeriodHasPassedAbsWalClk (&hdaGetStreamFromSink(pThis, &pThis->SinkLineIn)->State.Period, u64WalClk);
123 const uint64_t u64LineInAbsWalClk = hdaStreamPeriodGetAbsElapsedWalClk(&hdaGetStreamFromSink(pThis, &pThis->SinkLineIn)->State.Period);
124#ifdef VBOX_WITH_HDA_MIC_IN
125 const bool fMicInPassed = hdaStreamPeriodHasPassedAbsWalClk (&hdaGetStreamFromSink(pThis, &pThis->SinkMicIn)->State.Period, u64WalClk);
126 const uint64_t u64MicInAbsWalClk = hdaStreamPeriodGetAbsElapsedWalClk(&hdaGetStreamFromSink(pThis, &pThis->SinkMicIn)->State.Period);
127#endif
128
129#ifdef VBOX_STRICT
130 const uint64_t u64WalClkCur = ASMAtomicReadU64(&pThis->u64WalClk);
131#endif
132
133 /* Only drive the WALCLK register forward if all (active) stream periods have passed
134 * the specified point in time given by u64WalClk. */
135 if ( ( fFrontPassed
136#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
137# error "Implement me!"
138#endif
139 && fLineInPassed
140#ifdef VBOX_WITH_HDA_MIC_IN
141 && fMicInPassed
142#endif
143 )
144 || fForce)
145 {
146 if (!fForce)
147 {
148 /* Get the maximum value of all periods we need to handle.
149 * Not the most elegant solution, but works for now ... */
150 u64WalClk = RT_MAX(u64WalClk, u64FrontAbsWalClk);
151#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
152# error "Implement me!"
153#endif
154 u64WalClk = RT_MAX(u64WalClk, u64LineInAbsWalClk);
155#ifdef VBOX_WITH_HDA_MIC_IN
156 u64WalClk = RT_MAX(u64WalClk, u64MicInAbsWalClk);
157#endif
158
159#ifdef VBOX_STRICT
160 AssertMsg(u64WalClk >= u64WalClkCur,
161 ("Setting WALCLK to a value going backwards does not make any sense (old %RU64 vs. new %RU64)\n",
162 u64WalClkCur, u64WalClk));
163 if (u64WalClk == u64WalClkCur) /* Setting a stale value? */
164 {
165 if (pThis->u8WalClkStaleCnt++ > 3)
166 AssertMsgFailed(("Setting WALCLK to a stale value (%RU64) too often isn't a good idea really. "
167 "Good luck with stuck audio stuff.\n", u64WalClk));
168 }
169 else
170 pThis->u8WalClkStaleCnt = 0;
171#endif
172 }
173
174 /* Set the new WALCLK value. */
175 ASMAtomicWriteU64(&pThis->u64WalClk, u64WalClk);
176 }
177
178 const uint64_t u64WalClkNew = hdaWalClkGetCurrent(pThis);
179
180 Log3Func(("Cur: %RU64, New: %RU64 (force %RTbool) -> %RU64 %s\n",
181 u64WalClkCur, u64WalClk, fForce,
182 u64WalClkNew, u64WalClkNew == u64WalClk ? "[OK]" : "[DELAYED]"));
183
184 return (u64WalClkNew == u64WalClk);
185}
186
187/**
188 * Returns the audio direction of a specified stream descriptor.
189 *
190 * The register layout specifies that input streams (SDI) come first,
191 * followed by the output streams (SDO). So every stream ID below HDA_MAX_SDI
192 * is an input stream, whereas everything >= HDA_MAX_SDI is an output stream.
193 *
194 * Note: SDnFMT register does not provide that information, so we have to judge
195 * for ourselves.
196 *
197 * @return Audio direction.
198 */
199PDMAUDIODIR hdaGetDirFromSD(uint8_t uSD)
200{
201 AssertReturn(uSD < HDA_MAX_STREAMS, PDMAUDIODIR_UNKNOWN);
202
203 if (uSD < HDA_MAX_SDI)
204 return PDMAUDIODIR_IN;
205
206 return PDMAUDIODIR_OUT;
207}
208
209/**
210 * Returns the HDA stream of specified stream descriptor number.
211 *
212 * @return Pointer to HDA stream, or NULL if none found.
213 */
214PHDASTREAM hdaGetStreamFromSD(PHDASTATE pThis, uint8_t uSD)
215{
216 AssertPtrReturn(pThis, NULL);
217 AssertReturn(uSD < HDA_MAX_STREAMS, NULL);
218
219 if (uSD >= HDA_MAX_STREAMS)
220 {
221 AssertMsgFailed(("Invalid / non-handled SD%RU8\n", uSD));
222 return NULL;
223 }
224
225 return &pThis->aStreams[uSD];
226}
227
228/**
229 * Returns the HDA stream of specified HDA sink.
230 *
231 * @return Pointer to HDA stream, or NULL if none found.
232 */
233PHDASTREAM hdaGetStreamFromSink(PHDASTATE pThis, PHDAMIXERSINK pSink)
234{
235 AssertPtrReturn(pThis, NULL);
236 AssertPtrReturn(pSink, NULL);
237
238 /** @todo Do something with the channel mapping here? */
239 return hdaGetStreamFromSD(pThis, pSink->uSD);
240}
241
242/**
243 * Reads DMA data from a given HDA output stream.
244 *
245 * @return IPRT status code.
246 * @param pThis HDA state.
247 * @param pStream HDA output stream to read DMA data from.
248 * @param pvBuf Where to store the read data.
249 * @param cbBuf How much to read in bytes.
250 * @param pcbRead Returns read bytes from DMA. Optional.
251 */
252int hdaDMARead(PHDASTATE pThis, PHDASTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
253{
254 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
255 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
256 /* pcbRead is optional. */
257
258 PHDABDLE pBDLE = &pStream->State.BDLE;
259
260 int rc = VINF_SUCCESS;
261
262 uint32_t cbReadTotal = 0;
263 uint32_t cbLeft = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
264
265#ifdef HDA_DEBUG_SILENCE
266 uint64_t csSilence = 0;
267
268 pStream->Dbg.cSilenceThreshold = 100;
269 pStream->Dbg.cbSilenceReadMin = _1M;
270#endif
271
272 RTGCPHYS addrChunk = pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff;
273
274 while (cbLeft)
275 {
276 uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
277
278 rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), addrChunk, (uint8_t *)pvBuf + cbReadTotal, cbChunk);
279 if (RT_FAILURE(rc))
280 break;
281
282#ifdef HDA_DEBUG_SILENCE
283 uint16_t *pu16Buf = (uint16_t *)pvBuf;
284 for (size_t i = 0; i < cbChunk / sizeof(uint16_t); i++)
285 {
286 if (*pu16Buf == 0)
287 {
288 csSilence++;
289 }
290 else
291 break;
292 pu16Buf++;
293 }
294#endif
295
296#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
297 RTFILE fh;
298 int rc2 = RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMARead.pcm",
299 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
300 if (RT_SUCCESS(rc2))
301 {
302 RTFileWrite(fh, (uint8_t *)pvBuf + cbReadTotal, cbChunk, NULL);
303 RTFileClose(fh);
304 }
305 else
306 AssertRC(rc2);
307#endif
308
309#ifdef VBOX_WITH_STATISTICS
310 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbChunk);
311#endif
312 addrChunk = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
313
314 Assert(cbLeft >= cbChunk);
315 cbLeft -= cbChunk;
316
317 cbReadTotal += cbChunk;
318 }
319
320#ifdef HDA_DEBUG_SILENCE
321
322 if (csSilence)
323 pStream->Dbg.csSilence += csSilence;
324
325 if ( csSilence == 0
326 && pStream->Dbg.csSilence > pStream->Dbg.cSilenceThreshold
327 && pStream->Dbg.cbReadTotal >= pStream->Dbg.cbSilenceReadMin)
328 {
329 LogFunc(("Silent block detected: %RU64 audio samples\n", pStream->Dbg.csSilence));
330 pStream->Dbg.csSilence = 0;
331 }
332#endif
333
334 if (RT_SUCCESS(rc))
335 {
336 if (pcbRead)
337 *pcbRead = cbReadTotal;
338 }
339
340 return rc;
341}
342
343/**
344 * Writes audio data from an HDA input stream's FIFO to its associated DMA area.
345 *
346 * @return IPRT status code.
347 * @param pThis HDA state.
348 * @param pStream HDA input stream to write audio data to.
349 * @param pvBuf Data to write.
350 * @param cbBuf How much (in bytes) to write.
351 * @param pcbWritten Returns written bytes on success. Optional.
352 */
353int hdaDMAWrite(PHDASTATE pThis, PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
354{
355 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
356 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
357 /* pcbWritten is optional. */
358
359 PHDABDLE pBDLE = &pStream->State.BDLE;
360
361 int rc = VINF_SUCCESS;
362
363 uint32_t cbWrittenTotal = 0;
364 uint32_t cbLeft = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
365
366 RTGCPHYS addrChunk = pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff;
367
368 while (cbLeft)
369 {
370 uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
371
372 /* Sanity checks. */
373 Assert(cbChunk <= pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
374 Assert(cbChunk % HDA_FRAME_SIZE == 0);
375 Assert((cbChunk >> 1) >= 1);
376
377#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
378 RTFILE fh;
379 RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMAWrite.pcm",
380 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
381 RTFileWrite(fh, pvBuf, cbChunk, NULL);
382 RTFileClose(fh);
383#endif
384 rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
385 addrChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
386 if (RT_FAILURE(rc))
387 break;
388
389#ifdef VBOX_WITH_STATISTICS
390 STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbChunk);
391#endif
392 addrChunk = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
393
394 Assert(cbLeft >= cbChunk);
395 cbLeft -= (uint32_t)cbChunk;
396
397 cbWrittenTotal += (uint32_t)cbChunk;
398 }
399
400 if (RT_SUCCESS(rc))
401 {
402 if (pcbWritten)
403 *pcbWritten = cbWrittenTotal;
404 }
405 else
406 LogFunc(("Failed with %Rrc\n", rc));
407
408 return rc;
409}
410#endif /* IN_RING3 */
411
412/**
413 * Returns a new INTSTS value based on the current device state.
414 *
415 * @returns Determined INTSTS register value.
416 * @param pThis HDA state.
417 *
418 * @remark This function does *not* set INTSTS!
419 */
420uint32_t hdaGetINTSTS(PHDASTATE pThis)
421{
422 uint32_t intSts = 0;
423
424 /* Check controller interrupts (RIRB, STATEST). */
425 if ( (HDA_REG(pThis, RIRBSTS) & HDA_REG(pThis, RIRBCTL) & (HDA_RIRBCTL_ROIC | HDA_RIRBCTL_RINTCTL))
426 /* SDIN State Change Status Flags (SCSF). */
427 || (HDA_REG(pThis, STATESTS) & HDA_STATESTS_SCSF_MASK))
428 {
429 intSts |= HDA_INTSTS_CIS; /* Set the Controller Interrupt Status (CIS). */
430 }
431
432 if (HDA_REG(pThis, STATESTS) & HDA_REG(pThis, WAKEEN))
433 {
434 intSts |= HDA_INTSTS_CIS; /* Touch Controller Interrupt Status (CIS). */
435 }
436
437 /* For each stream, check if any interrupt status bit is set and enabled. */
438 for (uint8_t iStrm = 0; iStrm < HDA_MAX_STREAMS; ++iStrm)
439 {
440 if (HDA_STREAM_REG(pThis, STS, iStrm) & HDA_STREAM_REG(pThis, CTL, iStrm) & (HDA_SDCTL_DEIE | HDA_SDCTL_FEIE | HDA_SDCTL_IOCE))
441 {
442 Log3Func(("[SD%d] interrupt status set\n", iStrm));
443 intSts |= RT_BIT(iStrm);
444 }
445 }
446
447 if (intSts)
448 intSts |= HDA_INTSTS_GIS; /* Set the Global Interrupt Status (GIS). */
449
450 Log3Func(("-> 0x%x\n", intSts));
451
452 return intSts;
453}
454
455/**
456 * Converts an HDA stream's SDFMT register into a given PCM properties structure.
457 *
458 * @return IPRT status code.
459 * @param u32SDFMT The HDA stream's SDFMT value to convert.
460 * @param pProps PCM properties structure to hold converted result on success.
461 */
462int hdaSDFMTToPCMProps(uint32_t u32SDFMT, PPDMAUDIOPCMPROPS pProps)
463{
464 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
465
466# define EXTRACT_VALUE(v, mask, shift) ((v & ((mask) << (shift))) >> (shift))
467
468 int rc = VINF_SUCCESS;
469
470 uint32_t u32Hz = EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_BASE_RATE_MASK, HDA_SDFMT_BASE_RATE_SHIFT)
471 ? 44100 : 48000;
472 uint32_t u32HzMult = 1;
473 uint32_t u32HzDiv = 1;
474
475 switch (EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT))
476 {
477 case 0: u32HzMult = 1; break;
478 case 1: u32HzMult = 2; break;
479 case 2: u32HzMult = 3; break;
480 case 3: u32HzMult = 4; break;
481 default:
482 LogFunc(("Unsupported multiplier %x\n",
483 EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT)));
484 rc = VERR_NOT_SUPPORTED;
485 break;
486 }
487 switch (EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT))
488 {
489 case 0: u32HzDiv = 1; break;
490 case 1: u32HzDiv = 2; break;
491 case 2: u32HzDiv = 3; break;
492 case 3: u32HzDiv = 4; break;
493 case 4: u32HzDiv = 5; break;
494 case 5: u32HzDiv = 6; break;
495 case 6: u32HzDiv = 7; break;
496 case 7: u32HzDiv = 8; break;
497 default:
498 LogFunc(("Unsupported divisor %x\n",
499 EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT)));
500 rc = VERR_NOT_SUPPORTED;
501 break;
502 }
503
504 uint8_t cBits = 0;
505 switch (EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT))
506 {
507 case 0:
508 cBits = 8;
509 break;
510 case 1:
511 cBits = 16;
512 break;
513 case 4:
514 cBits = 32;
515 break;
516 default:
517 AssertMsgFailed(("Unsupported bits per sample %x\n",
518 EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT)));
519 rc = VERR_NOT_SUPPORTED;
520 break;
521 }
522
523 if (RT_SUCCESS(rc))
524 {
525 RT_BZERO(pProps, sizeof(PDMAUDIOPCMPROPS));
526
527 pProps->cBits = cBits;
528 pProps->fSigned = true;
529 pProps->cChannels = (u32SDFMT & 0xf) + 1;
530 pProps->uHz = u32Hz * u32HzMult / u32HzDiv;
531 pProps->cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cBits, pProps->cChannels);
532 }
533
534# undef EXTRACT_VALUE
535 return rc;
536}
537
538#ifdef IN_RING3
539/**
540 * Fetches a Bundle Descriptor List Entry (BDLE) from the DMA engine.
541 *
542 * @param pThis Pointer to HDA state.
543 * @param pBDLE Where to store the fetched result.
544 * @param u64BaseDMA Address base of DMA engine to use.
545 * @param u16Entry BDLE entry to fetch.
546 */
547int hdaBDLEFetch(PHDASTATE pThis, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry)
548{
549 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
550 AssertPtrReturn(pBDLE, VERR_INVALID_POINTER);
551 AssertReturn(u64BaseDMA, VERR_INVALID_PARAMETER);
552
553 if (!u64BaseDMA)
554 {
555 LogRel2(("HDA: Unable to fetch BDLE #%RU16 - no base DMA address set (yet)\n", u16Entry));
556 return VERR_NOT_FOUND;
557 }
558 /** @todo Compare u16Entry with LVI. */
559
560 int rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)),
561 &pBDLE->Desc, sizeof(pBDLE->Desc));
562
563 if (RT_SUCCESS(rc))
564 {
565 /* Reset internal state. */
566 RT_ZERO(pBDLE->State);
567 pBDLE->State.u32BDLIndex = u16Entry;
568 }
569
570 Log3Func(("Entry #%d @ 0x%x: %R[bdle], rc=%Rrc\n", u16Entry, u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)), pBDLE, rc));
571
572
573 return VINF_SUCCESS;
574}
575
576/**
577 * Tells whether a given BDLE is complete or not.
578 *
579 * @return true if BDLE is complete, false if not.
580 * @param pBDLE BDLE to retrieve status for.
581 */
582bool hdaBDLEIsComplete(PHDABDLE pBDLE)
583{
584 bool fIsComplete = false;
585
586 if ( !pBDLE->Desc.u32BufSize /* There can be BDLEs with 0 size. */
587 || (pBDLE->State.u32BufOff >= pBDLE->Desc.u32BufSize))
588 {
589 Assert(pBDLE->State.u32BufOff == pBDLE->Desc.u32BufSize);
590 fIsComplete = true;
591 }
592
593 Log3Func(("%R[bdle] => %s\n", pBDLE, fIsComplete ? "COMPLETE" : "INCOMPLETE"));
594
595 return fIsComplete;
596}
597
598/**
599 * Tells whether a given BDLE needs an interrupt or not.
600 *
601 * @return true if BDLE needs an interrupt, false if not.
602 * @param pBDLE BDLE to retrieve status for.
603 */
604bool hdaBDLENeedsInterrupt(PHDABDLE pBDLE)
605{
606 return (pBDLE->Desc.fFlags & HDA_BDLE_FLAG_IOC);
607}
608
609/**
610 * Sets the virtual device timer to a new expiration time.
611 *
612 * @returns Whether the new expiration time was set or not.
613 * @param pThis HDA state.
614 * @param tsExpire New (virtual) expiration time to set.
615 * @param fForce Whether to force setting the expiration time or not.
616 *
617 * @remark This function takes all active HDA streams and their
618 * current timing into account. This is needed to make sure
619 * that all streams can match their needed timing.
620 *
621 * To achieve this, the earliest (lowest) timestamp of all
622 * active streams found will be used for the next scheduling slot.
623 *
624 * Forcing a new expiration time will override the above mechanism.
625 */
626bool hdaTimerSet(PHDASTATE pThis, uint64_t tsExpire, bool fForce)
627{
628 AssertPtr(pThis->pTimer);
629
630 uint64_t tsExpireMin = tsExpire;
631
632 if (!fForce)
633 {
634 for (uint8_t i = 0; i < HDA_MAX_STREAMS; i++)
635 {
636 PHDASTREAM pStream = &pThis->aStreams[i];
637
638 if (!(HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_RUN))
639 continue;
640
641 if (hdaStreamTransferIsScheduled(pStream))
642 tsExpireMin = RT_MIN(tsExpireMin, hdaStreamTransferGetNext(pStream));
643 }
644 }
645
646 const uint64_t tsNow = TMTimerGet(pThis->pTimer);
647
648 if (tsExpireMin < tsNow) /* Make sure to not go backwards in time. */
649 tsExpireMin = tsNow;
650
651 Log3Func(("u64Epxire=%RU64 -> u64ExpireMin=%RU64, fForce=%RTbool [%s]\n",
652 tsExpire, tsExpireMin, fForce, tsExpireMin == tsExpire ? "OK" : "DELAYED"));
653
654 int rc2 = TMTimerSet(pThis->pTimer, tsExpireMin);
655 AssertRC(rc2);
656
657 return tsExpireMin == tsExpire;
658}
659#endif /* IN_RING3 */
660
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