VirtualBox

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

Last change on this file since 82425 was 82406, checked in by vboxsync, 5 years ago

DevHDA: Cleanups. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.6 KB
Line 
1/* $Id: DevHDACommon.cpp 82406 2019-12-05 00:47:02Z vboxsync $ */
2/** @file
3 * DevHDACommon.cpp - Shared HDA device functions.
4 *
5 * @todo r=bird: Shared with whom exactly?
6 */
7
8/*
9 * Copyright (C) 2017-2019 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#include <iprt/assert.h>
25#include <iprt/errcore.h>
26
27#include <VBox/AssertGuest.h>
28
29#define LOG_GROUP LOG_GROUP_DEV_HDA
30#include <VBox/log.h>
31
32#include "DrvAudio.h"
33
34#include "DevHDA.h"
35#include "DevHDACommon.h"
36
37#include "HDAStream.h"
38
39
40/**
41 * Processes (de/asserts) the interrupt according to the HDA's current state.
42 *
43 * @returns IPRT status code.
44 * @param pDevIns The device instance.
45 * @param pThis HDA state.
46 * @param pszSource Caller information.
47 */
48#if defined(LOG_ENABLED) || defined(DOXYGEN_RUNNING)
49int hdaProcessInterrupt(PPDMDEVINS pDevIns, PHDASTATE pThis, const char *pszSource)
50#else
51int hdaProcessInterrupt(PPDMDEVINS pDevIns, PHDASTATE pThis)
52#endif
53{
54 uint32_t uIntSts = hdaGetINTSTS(pThis);
55
56 HDA_REG(pThis, INTSTS) = uIntSts;
57
58 /* NB: It is possible to have GIS set even when CIE/SIEn are all zero; the GIS bit does
59 * not control the interrupt signal. See Figure 4 on page 54 of the HDA 1.0a spec.
60 */
61 /* Global Interrupt Enable (GIE) set? */
62 if ( (HDA_REG(pThis, INTCTL) & HDA_INTCTL_GIE)
63 && (HDA_REG(pThis, INTSTS) & HDA_REG(pThis, INTCTL) & (HDA_INTCTL_CIE | HDA_STRMINT_MASK)))
64 {
65 Log3Func(("Asserted (%s)\n", pszSource));
66
67 PDMDevHlpPCISetIrq(pDevIns, 0, 1 /* Assert */);
68 pThis->u8IRQL = 1;
69
70#ifdef DEBUG
71 pThis->Dbg.IRQ.tsAssertedNs = RTTimeNanoTS();
72 pThis->Dbg.IRQ.tsProcessedLastNs = pThis->Dbg.IRQ.tsAssertedNs;
73#endif
74 }
75 else
76 {
77 Log3Func(("Deasserted (%s)\n", pszSource));
78
79 PDMDevHlpPCISetIrq(pDevIns, 0, 0 /* Deassert */);
80 pThis->u8IRQL = 0;
81 }
82
83 return VINF_SUCCESS;
84}
85
86/**
87 * Retrieves the currently set value for the wall clock.
88 *
89 * @return IPRT status code.
90 * @return Currently set wall clock value.
91 * @param pThis HDA state.
92 *
93 * @remark Operation is atomic.
94 */
95uint64_t hdaWalClkGetCurrent(PHDASTATE pThis)
96{
97 return ASMAtomicReadU64(&pThis->u64WalClk);
98}
99
100#ifdef IN_RING3
101
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 hdaR3WalClkSet(PHDASTATE pThis, uint64_t u64WalClk, bool fForce)
114{
115 const bool fFrontPassed = hdaR3StreamPeriodHasPassedAbsWalClk (&hdaR3GetStreamFromSink(pThis, &pThis->SinkFront)->State.Period,
116 u64WalClk);
117 const uint64_t u64FrontAbsWalClk = hdaR3StreamPeriodGetAbsElapsedWalClk(&hdaR3GetStreamFromSink(pThis, &pThis->SinkFront)->State.Period);
118# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
119# error "Implement me!"
120# endif
121
122 const bool fLineInPassed = hdaR3StreamPeriodHasPassedAbsWalClk (&hdaR3GetStreamFromSink(pThis, &pThis->SinkLineIn)->State.Period, u64WalClk);
123 const uint64_t u64LineInAbsWalClk = hdaR3StreamPeriodGetAbsElapsedWalClk(&hdaR3GetStreamFromSink(pThis, &pThis->SinkLineIn)->State.Period);
124# ifdef VBOX_WITH_HDA_MIC_IN
125 const bool fMicInPassed = hdaR3StreamPeriodHasPassedAbsWalClk (&hdaR3GetStreamFromSink(pThis, &pThis->SinkMicIn)->State.Period, u64WalClk);
126 const uint64_t u64MicInAbsWalClk = hdaR3StreamPeriodGetAbsElapsedWalClk(&hdaR3GetStreamFromSink(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 default (mixer) sink from a given SD#.
189 * Returns NULL if no sink is found.
190 *
191 * @return PHDAMIXERSINK
192 * @param pThis HDA state.
193 * @param uSD SD# to return mixer sink for.
194 * NULL if not found / handled.
195 */
196PHDAMIXERSINK hdaR3GetDefaultSink(PHDASTATE pThis, uint8_t uSD)
197{
198 if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN)
199 {
200 const uint8_t uFirstSDI = 0;
201
202 if (uSD == uFirstSDI) /* First SDI. */
203 return &pThis->SinkLineIn;
204# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
205 if (uSD == uFirstSDI + 1)
206 return &pThis->SinkMicIn;
207# else
208 /* If we don't have a dedicated Mic-In sink, use the always present Line-In sink. */
209 return &pThis->SinkLineIn;
210# endif
211 }
212 else
213 {
214 const uint8_t uFirstSDO = HDA_MAX_SDI;
215
216 if (uSD == uFirstSDO)
217 return &pThis->SinkFront;
218# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
219 if (uSD == uFirstSDO + 1)
220 return &pThis->SinkCenterLFE;
221 if (uSD == uFirstSDO + 2)
222 return &pThis->SinkRear;
223# endif
224 }
225
226 return NULL;
227}
228
229#endif /* IN_RING3 */
230
231/**
232 * Returns the audio direction of a specified stream descriptor.
233 *
234 * The register layout specifies that input streams (SDI) come first,
235 * followed by the output streams (SDO). So every stream ID below HDA_MAX_SDI
236 * is an input stream, whereas everything >= HDA_MAX_SDI is an output stream.
237 *
238 * Note: SDnFMT register does not provide that information, so we have to judge
239 * for ourselves.
240 *
241 * @return Audio direction.
242 */
243PDMAUDIODIR hdaGetDirFromSD(uint8_t uSD)
244{
245 AssertReturn(uSD < HDA_MAX_STREAMS, PDMAUDIODIR_UNKNOWN);
246
247 if (uSD < HDA_MAX_SDI)
248 return PDMAUDIODIR_IN;
249
250 return PDMAUDIODIR_OUT;
251}
252
253/**
254 * Returns the HDA stream of specified stream descriptor number.
255 *
256 * @return Pointer to HDA stream, or NULL if none found.
257 */
258PHDASTREAM hdaGetStreamFromSD(PHDASTATE pThis, uint8_t uSD)
259{
260 AssertPtr(pThis);
261 ASSERT_GUEST_MSG_RETURN(uSD < HDA_MAX_STREAMS, ("uSD=%u (%#x)\n", uSD, uSD), NULL);
262 return &pThis->aStreams[uSD];
263}
264
265#ifdef IN_RING3
266
267/**
268 * Returns the HDA stream of specified HDA sink.
269 *
270 * @return Pointer to HDA stream, or NULL if none found.
271 */
272PHDASTREAM hdaR3GetStreamFromSink(PHDASTATE pThis, PHDAMIXERSINK pSink)
273{
274 AssertPtrReturn(pThis, NULL);
275 AssertPtrReturn(pSink, NULL);
276
277 /** @todo Do something with the channel mapping here? */
278 return pSink->pStream;
279}
280
281/**
282 * Reads DMA data from a given HDA output stream.
283 *
284 * @return IPRT status code.
285 * @param pThis HDA state.
286 * @param pStream HDA output stream to read DMA data from.
287 * @param pvBuf Where to store the read data.
288 * @param cbBuf How much to read in bytes.
289 * @param pcbRead Returns read bytes from DMA. Optional.
290 */
291int hdaR3DMARead(PHDASTATE pThis, PHDASTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
292{
293 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
294 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
295 /* pcbRead is optional. */
296
297 PHDABDLE pBDLE = &pStream->State.BDLE;
298
299 int rc = VINF_SUCCESS;
300
301 uint32_t cbReadTotal = 0;
302 uint32_t cbLeft = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
303
304# ifdef HDA_DEBUG_SILENCE
305 uint64_t csSilence = 0;
306
307 pStream->Dbg.cSilenceThreshold = 100;
308 pStream->Dbg.cbSilenceReadMin = _1M;
309# endif
310
311 RTGCPHYS addrChunk = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
312
313 while (cbLeft)
314 {
315 uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
316
317 rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), addrChunk, (uint8_t *)pvBuf + cbReadTotal, cbChunk);
318 if (RT_FAILURE(rc))
319 break;
320
321# ifdef HDA_DEBUG_SILENCE
322 uint16_t *pu16Buf = (uint16_t *)pvBuf;
323 for (size_t i = 0; i < cbChunk / sizeof(uint16_t); i++)
324 {
325 if (*pu16Buf == 0)
326 csSilence++;
327 else
328 break;
329 pu16Buf++;
330 }
331# endif
332 if (pStream->Dbg.Runtime.fEnabled)
333 DrvAudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMARaw, (uint8_t *)pvBuf + cbReadTotal, cbChunk, 0 /* fFlags */);
334
335 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbChunk);
336 addrChunk = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
337
338 Assert(cbLeft >= cbChunk);
339 cbLeft -= cbChunk;
340
341 cbReadTotal += cbChunk;
342 }
343
344# ifdef HDA_DEBUG_SILENCE
345 if (csSilence)
346 pStream->Dbg.csSilence += csSilence;
347
348 if ( csSilence == 0
349 && pStream->Dbg.csSilence > pStream->Dbg.cSilenceThreshold
350 && pStream->Dbg.cbReadTotal >= pStream->Dbg.cbSilenceReadMin)
351 {
352 LogFunc(("Silent block detected: %RU64 audio samples\n", pStream->Dbg.csSilence));
353 pStream->Dbg.csSilence = 0;
354 }
355# endif
356
357 if (RT_SUCCESS(rc))
358 {
359 if (pcbRead)
360 *pcbRead = cbReadTotal;
361 }
362
363 return rc;
364}
365
366/**
367 * Writes audio data from an HDA input stream's FIFO to its associated DMA area.
368 *
369 * @return IPRT status code.
370 * @param pThis HDA state.
371 * @param pStream HDA input stream to write audio data to.
372 * @param pvBuf Data to write.
373 * @param cbBuf How much (in bytes) to write.
374 * @param pcbWritten Returns written bytes on success. Optional.
375 */
376int hdaR3DMAWrite(PHDASTATE pThis, PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
377{
378 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
379 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
380 /* pcbWritten is optional. */
381
382 PHDABDLE pBDLE = &pStream->State.BDLE;
383
384 int rc = VINF_SUCCESS;
385
386 uint32_t cbWrittenTotal = 0;
387 uint32_t cbLeft = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
388
389 RTGCPHYS addrChunk = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
390
391 while (cbLeft)
392 {
393 uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
394
395 /* Sanity checks. */
396 Assert(cbChunk <= pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
397
398 if (pStream->Dbg.Runtime.fEnabled)
399 DrvAudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMARaw, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk, 0 /* fFlags */);
400
401 rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
402 addrChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
403 if (RT_FAILURE(rc))
404 break;
405
406 STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbChunk);
407 addrChunk = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
408
409 Assert(cbLeft >= cbChunk);
410 cbLeft -= (uint32_t)cbChunk;
411
412 cbWrittenTotal += (uint32_t)cbChunk;
413 }
414
415 if (RT_SUCCESS(rc))
416 {
417 if (pcbWritten)
418 *pcbWritten = cbWrittenTotal;
419 }
420 else
421 LogFunc(("Failed with %Rrc\n", rc));
422
423 return rc;
424}
425
426#endif /* IN_RING3 */
427
428/**
429 * Returns a new INTSTS value based on the current device state.
430 *
431 * @returns Determined INTSTS register value.
432 * @param pThis HDA state.
433 *
434 * @remark This function does *not* set INTSTS!
435 */
436uint32_t hdaGetINTSTS(PHDASTATE pThis)
437{
438 uint32_t intSts = 0;
439
440 /* Check controller interrupts (RIRB, STATEST). */
441 if (HDA_REG(pThis, RIRBSTS) & HDA_REG(pThis, RIRBCTL) & (HDA_RIRBCTL_ROIC | HDA_RIRBCTL_RINTCTL))
442 {
443 intSts |= HDA_INTSTS_CIS; /* Set the Controller Interrupt Status (CIS). */
444 }
445
446 /* Check SDIN State Change Status Flags. */
447 if (HDA_REG(pThis, STATESTS) & HDA_REG(pThis, WAKEEN))
448 {
449 intSts |= HDA_INTSTS_CIS; /* Touch Controller Interrupt Status (CIS). */
450 }
451
452 /* For each stream, check if any interrupt status bit is set and enabled. */
453 for (uint8_t iStrm = 0; iStrm < HDA_MAX_STREAMS; ++iStrm)
454 {
455 if (HDA_STREAM_REG(pThis, STS, iStrm) & HDA_STREAM_REG(pThis, CTL, iStrm) & (HDA_SDCTL_DEIE | HDA_SDCTL_FEIE | HDA_SDCTL_IOCE))
456 {
457 Log3Func(("[SD%d] interrupt status set\n", iStrm));
458 intSts |= RT_BIT(iStrm);
459 }
460 }
461
462 if (intSts)
463 intSts |= HDA_INTSTS_GIS; /* Set the Global Interrupt Status (GIS). */
464
465 Log3Func(("-> 0x%x\n", intSts));
466
467 return intSts;
468}
469
470#ifdef IN_RING3
471
472/**
473 * Converts an HDA stream's SDFMT register into a given PCM properties structure.
474 *
475 * @return IPRT status code.
476 * @param u16SDFMT The HDA stream's SDFMT value to convert.
477 * @param pProps PCM properties structure to hold converted result on success.
478 */
479int hdaR3SDFMTToPCMProps(uint16_t u16SDFMT, PPDMAUDIOPCMPROPS pProps)
480{
481 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
482
483# define EXTRACT_VALUE(v, mask, shift) ((v & ((mask) << (shift))) >> (shift))
484
485 int rc = VINF_SUCCESS;
486
487 uint32_t u32Hz = EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BASE_RATE_MASK, HDA_SDFMT_BASE_RATE_SHIFT)
488 ? 44100 : 48000;
489 uint32_t u32HzMult = 1;
490 uint32_t u32HzDiv = 1;
491
492 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT))
493 {
494 case 0: u32HzMult = 1; break;
495 case 1: u32HzMult = 2; break;
496 case 2: u32HzMult = 3; break;
497 case 3: u32HzMult = 4; break;
498 default:
499 LogFunc(("Unsupported multiplier %x\n",
500 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT)));
501 rc = VERR_NOT_SUPPORTED;
502 break;
503 }
504 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT))
505 {
506 case 0: u32HzDiv = 1; break;
507 case 1: u32HzDiv = 2; break;
508 case 2: u32HzDiv = 3; break;
509 case 3: u32HzDiv = 4; break;
510 case 4: u32HzDiv = 5; break;
511 case 5: u32HzDiv = 6; break;
512 case 6: u32HzDiv = 7; break;
513 case 7: u32HzDiv = 8; break;
514 default:
515 LogFunc(("Unsupported divisor %x\n",
516 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT)));
517 rc = VERR_NOT_SUPPORTED;
518 break;
519 }
520
521 uint8_t cBytes = 0;
522 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT))
523 {
524 case 0:
525 cBytes = 1;
526 break;
527 case 1:
528 cBytes = 2;
529 break;
530 case 4:
531 cBytes = 4;
532 break;
533 default:
534 AssertMsgFailed(("Unsupported bits per sample %x\n",
535 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT)));
536 rc = VERR_NOT_SUPPORTED;
537 break;
538 }
539
540 if (RT_SUCCESS(rc))
541 {
542 RT_BZERO(pProps, sizeof(PDMAUDIOPCMPROPS));
543
544 pProps->cbSample = cBytes;
545 pProps->fSigned = true;
546 pProps->cChannels = (u16SDFMT & 0xf) + 1;
547 pProps->uHz = u32Hz * u32HzMult / u32HzDiv;
548 pProps->cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels);
549 }
550
551# undef EXTRACT_VALUE
552 return rc;
553}
554
555# ifdef LOG_ENABLED
556void hdaR3BDLEDumpAll(PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE)
557{
558 LogFlowFunc(("BDLEs @ 0x%x (%RU16):\n", u64BDLBase, cBDLE));
559 if (!u64BDLBase)
560 return;
561
562 uint32_t cbBDLE = 0;
563 for (uint16_t i = 0; i < cBDLE; i++)
564 {
565 HDABDLEDESC bd;
566 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
567
568 LogFunc(("\t#%03d BDLE(adr:0x%llx, size:%RU32, ioc:%RTbool)\n",
569 i, bd.u64BufAddr, bd.u32BufSize, bd.fFlags & HDA_BDLE_F_IOC));
570
571 cbBDLE += bd.u32BufSize;
572 }
573
574 LogFlowFunc(("Total: %RU32 bytes\n", cbBDLE));
575
576 if (!pThis->u64DPBase) /* No DMA base given? Bail out. */
577 return;
578
579 LogFlowFunc(("DMA counters:\n"));
580
581 for (int i = 0; i < cBDLE; i++)
582 {
583 uint32_t uDMACnt;
584 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), (pThis->u64DPBase & DPBASE_ADDR_MASK) + (i * 2 * sizeof(uint32_t)),
585 &uDMACnt, sizeof(uDMACnt));
586
587 LogFlowFunc(("\t#%03d DMA @ 0x%x\n", i , uDMACnt));
588 }
589}
590# endif /* LOG_ENABLED */
591
592/**
593 * Fetches a Bundle Descriptor List Entry (BDLE) from the DMA engine.
594 *
595 * @param pThis Pointer to HDA state.
596 * @param pBDLE Where to store the fetched result.
597 * @param u64BaseDMA Address base of DMA engine to use.
598 * @param u16Entry BDLE entry to fetch.
599 */
600int hdaR3BDLEFetch(PHDASTATE pThis, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry)
601{
602 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
603 AssertPtrReturn(pBDLE, VERR_INVALID_POINTER);
604 AssertReturn(u64BaseDMA, VERR_INVALID_PARAMETER);
605
606 if (!u64BaseDMA)
607 {
608 LogRel2(("HDA: Unable to fetch BDLE #%RU16 - no base DMA address set (yet)\n", u16Entry));
609 return VERR_NOT_FOUND;
610 }
611 /** @todo Compare u16Entry with LVI. */
612
613 int rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)),
614 &pBDLE->Desc, sizeof(pBDLE->Desc));
615
616 if (RT_SUCCESS(rc))
617 {
618 /* Reset internal state. */
619 RT_ZERO(pBDLE->State);
620 pBDLE->State.u32BDLIndex = u16Entry;
621 }
622
623 Log3Func(("Entry #%d @ 0x%x: %R[bdle], rc=%Rrc\n", u16Entry, u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)), pBDLE, rc));
624
625
626 return VINF_SUCCESS;
627}
628
629/**
630 * Tells whether a given BDLE is complete or not.
631 *
632 * @return true if BDLE is complete, false if not.
633 * @param pBDLE BDLE to retrieve status for.
634 */
635bool hdaR3BDLEIsComplete(PHDABDLE pBDLE)
636{
637 bool fIsComplete = false;
638
639 if ( !pBDLE->Desc.u32BufSize /* There can be BDLEs with 0 size. */
640 || (pBDLE->State.u32BufOff >= pBDLE->Desc.u32BufSize))
641 {
642 Assert(pBDLE->State.u32BufOff == pBDLE->Desc.u32BufSize);
643 fIsComplete = true;
644 }
645
646 Log3Func(("%R[bdle] => %s\n", pBDLE, fIsComplete ? "COMPLETE" : "INCOMPLETE"));
647
648 return fIsComplete;
649}
650
651/**
652 * Tells whether a given BDLE needs an interrupt or not.
653 *
654 * @return true if BDLE needs an interrupt, false if not.
655 * @param pBDLE BDLE to retrieve status for.
656 */
657bool hdaR3BDLENeedsInterrupt(PHDABDLE pBDLE)
658{
659 return (pBDLE->Desc.fFlags & HDA_BDLE_F_IOC);
660}
661
662/**
663 * Sets the virtual device timer to a new expiration time.
664 *
665 * @returns Whether the new expiration time was set or not.
666 * @param pDevIns The device instance.
667 * @param pStream HDA stream to set timer for.
668 * @param tsExpire New (virtual) expiration time to set.
669 * @param fForce Whether to force setting the expiration time or not.
670 * @param tsNow The current clock timestamp if available, 0 if not.
671 *
672 * @remark This function takes all active HDA streams and their
673 * current timing into account. This is needed to make sure
674 * that all streams can match their needed timing.
675 *
676 * To achieve this, the earliest (lowest) timestamp of all
677 * active streams found will be used for the next scheduling slot.
678 *
679 * Forcing a new expiration time will override the above mechanism.
680 */
681bool hdaR3TimerSet(PPDMDEVINS pDevIns, PHDASTREAM pStream, uint64_t tsExpire, bool fForce, uint64_t tsNow)
682{
683 AssertPtr(pStream);
684
685 if (!tsNow)
686 tsNow = PDMDevHlpTimerGet(pDevIns, pStream->hTimer);
687
688 if (!fForce)
689 {
690 /** @todo r=bird: hdaR3StreamTransferIsScheduled() also does a
691 * PDMDevHlpTimerGet(), so, some callers does one, this does, and then we do
692 * right afterwards == very inefficient! */
693 if (hdaR3StreamTransferIsScheduled(pStream, tsNow))
694 {
695 uint64_t const tsNext = hdaR3StreamTransferGetNext(pStream);
696 if (tsExpire > tsNext)
697 tsExpire = tsNext;
698 }
699 }
700
701 /*
702 * Make sure to not go backwards in time, as this will assert in TMTimerSet().
703 * This in theory could happen in hdaR3StreamTransferGetNext() from above.
704 */
705 if (tsExpire < tsNow)
706 tsExpire = tsNow;
707
708 int rc = PDMDevHlpTimerSet(pDevIns, pStream->hTimer, tsExpire);
709 AssertRCReturn(rc, false);
710
711 return true;
712}
713
714#endif /* IN_RING3 */
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