VirtualBox

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

最後變更 在這個檔案從87328是 87328,由 vboxsync 提交於 4 年 前

Audio/HDA: Implemented FIFO watermark support [build fix]. ticketoem2ref:36

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette