VirtualBox

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

Last change on this file since 69111 was 68674, checked in by vboxsync, 7 years ago

Audio/HDA: Refined assertion.

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