VirtualBox

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

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

Audio: More cleanups (missing keywords, incorrect #endif docs, stuff)

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