VirtualBox

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

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