VirtualBox

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

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

Audio/HDA: Experimental change to lower more DPC calls within the guest by avoiding re-scheduling to R3 when the guest acknowledges an interrupt via hdaRegWriteSDSTS(). Only tested on Ubuntu 20.10, Win7 + Win10 20H2 so far. ticketoem2ref:36

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