diff options
Diffstat (limited to 'src/VBox/Devices/Audio/DevHdaStream.h')
-rw-r--r-- | src/VBox/Devices/Audio/DevHdaStream.h | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/src/VBox/Devices/Audio/DevHdaStream.h b/src/VBox/Devices/Audio/DevHdaStream.h new file mode 100644 index 00000000..0d1065e2 --- /dev/null +++ b/src/VBox/Devices/Audio/DevHdaStream.h @@ -0,0 +1,330 @@ +/* $Id: DevHdaStream.h $ */ +/** @file + * Intel HD Audio Controller Emulation - Streams. + */ + +/* + * Copyright (C) 2017-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_Audio_DevHdaStream_h +#define VBOX_INCLUDED_SRC_Audio_DevHdaStream_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#ifndef VBOX_INCLUDED_SRC_Audio_DevHda_h +# error "Only include DevHda.h!" +#endif + + +/** + * Structure containing HDA stream debug stuff, configurable at runtime. + */ +typedef struct HDASTREAMDEBUGRT +{ + /** Whether debugging is enabled or not. */ + bool fEnabled; + uint8_t Padding[7]; + /** File for dumping stream reads / writes. + * For input streams, this dumps data being written to the device FIFO, + * whereas for output streams this dumps data being read from the device FIFO. */ + R3PTRTYPE(PAUDIOHLPFILE) pFileStream; + /** File for dumping raw DMA reads / writes. + * For input streams, this dumps data being written to the device DMA, + * whereas for output streams this dumps data being read from the device DMA. */ + R3PTRTYPE(PAUDIOHLPFILE) pFileDMARaw; + /** File for dumping mapped (that is, extracted) DMA reads / writes. */ + R3PTRTYPE(PAUDIOHLPFILE) pFileDMAMapped; +} HDASTREAMDEBUGRT; + +/** + * Structure containing HDA stream debug information. + */ +typedef struct HDASTREAMDEBUG +{ + /** Runtime debug info. */ + HDASTREAMDEBUGRT Runtime; + uint64_t au64Alignment[2]; +} HDASTREAMDEBUG; + +/** + * Internal state of a HDA stream. + */ +typedef struct HDASTREAMSTATE +{ + /** Flag indicating whether this stream currently is + * in reset mode and therefore not acccessible by the guest. */ + volatile bool fInReset; + /** Flag indicating if the stream is in running state or not. */ + volatile bool fRunning; + /** How many interrupts are pending due to + * BDLE interrupt-on-completion (IOC) bits set. */ + uint8_t cTransferPendingInterrupts; + /** Input streams only: Set when we switch from feeding the guest silence and + * commits to proving actual audio input bytes. */ + bool fInputPreBuffered; + /** Input streams only: The number of bytes we need to prebuffer. */ + uint32_t cbInputPreBuffer; + /** Timestamp (absolute, in timer ticks) of the last DMA data transfer. + * @note This is used for wall clock (WALCLK) calculations. */ + uint64_t volatile tsTransferLast; + /** The stream's current configuration (matches SDnFMT). */ + PDMAUDIOSTREAMCFG Cfg; + /** Timestamp (real time, in ns) of last DMA transfer. */ + uint64_t tsLastTransferNs; + /** Timestamp (real time, in ns) of last stream read (to backends). + * When running in async I/O mode, this differs from \a tsLastTransferNs, + * because reading / processing will be done in a separate stream. */ + uint64_t tsLastReadNs; + + /** The start time for the playback (on the timer clock). */ + uint64_t tsStart; + + /** @name DMA engine + * @{ */ + /** Timestamp (absolute, in timer ticks) of the next DMA data transfer. + * Next for determining the next scheduling window. + * Can be 0 if no next transfer is scheduled. */ + uint64_t tsTransferNext; + /** The size of the current DMA transfer period. */ + uint32_t cbCurDmaPeriod; + /** The size of an average transfer. */ + uint32_t cbAvgTransfer; + + /** Current circular buffer read offset (for tracing & logging). */ + uint64_t offRead; + /** Current circular buffer write offset (for tracing & logging). */ + uint64_t offWrite; + + /** The offset into the current BDLE. */ + uint32_t offCurBdle; + /** LVI + 1 */ + uint16_t cBdles; + /** The index of the current BDLE. + * This is the entry which period is currently "running" on the DMA timer. */ + uint8_t idxCurBdle; + /** The number of prologue scheduling steps. + * This is used when the tail BDLEs doesn't have IOC set. */ + uint8_t cSchedulePrologue; + /** Number of scheduling steps. */ + uint16_t cSchedule; + /** Current scheduling step. */ + uint16_t idxSchedule; + /** Current loop number within the current scheduling step. */ + uint32_t idxScheduleLoop; + + /** Buffer descriptors and additional timer scheduling state. + * (Same as HDABDLEDESC, with more sensible naming.) */ + struct + { + /** The buffer address. */ + uint64_t GCPhys; + /** The buffer size (guest bytes). */ + uint32_t cb; + /** The flags (only bit 0 is defined). */ + uint32_t fFlags; + } aBdl[256]; + /** Scheduling steps. */ + struct + { + /** Number of timer ticks per period. + * ASSUMES that we don't need a full second and that the timer resolution + * isn't much higher than nanoseconds. */ + uint32_t cPeriodTicks; + /** The period length in host bytes. */ + uint32_t cbPeriod; + /** Number of times to repeat the period. */ + uint32_t cLoops; + /** The BDL index of the first entry. */ + uint8_t idxFirst; + /** The number of BDL entries. */ + uint8_t cEntries; + uint8_t abPadding[2]; + } aSchedule[512+8]; + +#ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA + /** Number of valid bytes in abDma. + * @note Volatile to prevent the compiler from re-reading it after we've + * validated the value in ring-0. */ + uint32_t volatile cbDma; + /** Total number of bytes going via abDma this timer period. */ + uint32_t cbDmaTotal; + /** DMA bounce buffer for ring-0 register reads (LPIB). */ + uint8_t abDma[2048 - 8]; +#endif + /** @} */ +} HDASTREAMSTATE; +AssertCompileSizeAlignment(HDASTREAMSTATE, 16); +AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 8); +AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 16); +AssertCompileMemberAlignment(HDASTREAMSTATE, aSchedule, 16); + +/** + * An HDA stream (SDI / SDO) - shared. + * + * @note This HDA stream has nothing to do with a regular audio stream handled + * by the audio connector or the audio mixer. This HDA stream is a serial + * data in/out stream (SDI/SDO) defined in hardware and can contain + * multiple audio streams in one single SDI/SDO (interleaving streams). + * + * Contains only register values which do *not* change until a stream reset + * occurs. + */ +typedef struct HDASTREAM +{ + /** Internal state of this stream. */ + HDASTREAMSTATE State; + + /** Stream descriptor number (SDn). */ + uint8_t u8SD; + /** Current channel index. + * For a stereo stream, this is u8Channel + 1. */ + uint8_t u8Channel; + /** FIFO Watermark (checked + translated in bytes, FIFOW). + * This will be update from hdaRegWriteSDFIFOW() and also copied + * hdaR3StreamInit() for some reason. */ + uint8_t u8FIFOW; + + /** @name Register values at stream setup. + * These will all be copied in hdaR3StreamInit(). + * @{ */ + /** FIFO Size (checked + translated in bytes, FIFOS). + * This is supposedly the max number of bytes we'll be DMA'ing in one chunk + * and correspondingly the LPIB & wall clock update jumps. However, we're + * not at all being honest with the guest about this. */ + uint8_t u8FIFOS; + /** Cyclic Buffer Length (SDnCBL) - Represents the size of the ring buffer. */ + uint32_t u32CBL; + /** Last Valid Index (SDnLVI). */ + uint16_t u16LVI; + /** Format (SDnFMT). */ + uint16_t u16FMT; + uint8_t abPadding[4]; + /** DMA base address (SDnBDPU - SDnBDPL). */ + uint64_t u64BDLBase; + /** @} */ + + /** The timer for pumping data thru the attached LUN drivers. */ + TMTIMERHANDLE hTimer; + + /** Pad the structure size to a 64 byte alignment. */ + uint64_t au64Padding1[2]; +} HDASTREAM; +AssertCompileMemberAlignment(HDASTREAM, State.aBdl, 16); +AssertCompileMemberAlignment(HDASTREAM, State.aSchedule, 16); +AssertCompileSizeAlignment(HDASTREAM, 64); +/** Pointer to an HDA stream (SDI / SDO). */ +typedef HDASTREAM *PHDASTREAM; + + +/** + * An HDA stream (SDI / SDO) - ring-3 bits. + */ +typedef struct HDASTREAMR3 +{ + /** Stream descriptor number (SDn). */ + uint8_t u8SD; + uint8_t abPadding[7]; + /** The shared state for the parent HDA device. */ + R3PTRTYPE(PHDASTATE) pHDAStateShared; + /** The ring-3 state for the parent HDA device. */ + R3PTRTYPE(PHDASTATER3) pHDAStateR3; + /** Pointer to HDA sink this stream is attached to. */ + R3PTRTYPE(PHDAMIXERSINK) pMixSink; + /** Internal state of this stream. */ + struct + { + /** Circular buffer (FIFO) for holding DMA'ed data. */ + R3PTRTYPE(PRTCIRCBUF) pCircBuf; + /** The mixer sink this stream has registered AIO update callback with. + * This is NULL till we register it, typically in hdaR3StreamEnable. + * (The problem with following the pMixSink assignment is that hdaR3StreamReset + * sets it without updating the HDA sink structure, so things get out of + * wack in hdaR3MixerControl later in the initial device reset.) */ + PAUDMIXSINK pAioRegSink; + + /** Size of the DMA buffer (pCircBuf) in bytes. */ + uint32_t StatDmaBufSize; + /** Number of used bytes in the DMA buffer (pCircBuf). */ + uint32_t StatDmaBufUsed; + /** Counter for all under/overflows problems. */ + STAMCOUNTER StatDmaFlowProblems; + /** Counter for unresovled under/overflows problems. */ + STAMCOUNTER StatDmaFlowErrors; + /** Number of bytes involved in unresolved flow errors. */ + STAMCOUNTER StatDmaFlowErrorBytes; + /** DMA skipped because buffer interrupt pending. */ + STAMCOUNTER StatDmaSkippedPendingBcis; + + STAMPROFILE StatStart; + STAMPROFILE StatReset; + STAMPROFILE StatStop; + } State; + /** Debug bits. */ + HDASTREAMDEBUG Dbg; + uint64_t au64Alignment[3]; +} HDASTREAMR3; +AssertCompileSizeAlignment(HDASTREAMR3, 64); +/** Pointer to an HDA stream (SDI / SDO). */ +typedef HDASTREAMR3 *PHDASTREAMR3; + +/** @name Stream functions (all contexts). + * @{ + */ +VBOXSTRICTRC hdaStreamDoOnAccessDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, + uint64_t tsNow, uint32_t cbToTransfer); +VBOXSTRICTRC hdaStreamMaybeDoOnAccessDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, + PHDASTREAM pStreamShared, uint64_t tsNow); +/** @} */ + +#ifdef IN_RING3 + +/** @name Stream functions (ring-3). + * @{ + */ +int hdaR3StreamConstruct(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, PHDASTATE pThis, + PHDASTATER3 pThisCC, uint8_t uSD); +void hdaR3StreamDestroy(PHDASTREAMR3 pStreamR3); +int hdaR3StreamSetUp(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, + PHDASTREAMR3 pStreamR3, uint8_t uSD); +void hdaR3StreamReset(PHDASTATE pThis, PHDASTATER3 pThisCC, + PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, uint8_t uSD); +int hdaR3StreamEnable(PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, bool fEnable); +void hdaR3StreamMarkStarted(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint64_t tsNow); +void hdaR3StreamMarkStopped(PHDASTREAM pStreamShared); + +uint64_t hdaR3StreamTimerMain(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, + PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3); +DECLCALLBACK(void) hdaR3StreamUpdateAsyncIoJob(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser); +/** @} */ + +/** @name Helper functions associated with the stream code. + * @{ */ +int hdaR3SDFMTToPCMProps(uint16_t u16SDFMT, PPDMAUDIOPCMPROPS pProps); +# ifdef LOG_ENABLED +void hdaR3BDLEDumpAll(PPDMDEVINS pDevIns, PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE); +# endif +/** @} */ + +#endif /* IN_RING3 */ +#endif /* !VBOX_INCLUDED_SRC_Audio_DevHdaStream_h */ + |