VirtualBox

Changeset 67416 in vbox for trunk/src/VBox/Devices/Audio


Ignore:
Timestamp:
Jun 15, 2017 9:04:51 AM (8 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
116142
Message:

Audio/DevHDA: Forward ported / integrated stream interrupt / period handling. WIP.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DevHDA.cpp

    r67411 r67416  
    105105/** Default timer frequency (in Hz). */
    106106#define HDA_TIMER_HZ            200
     107
     108/** HDA's (fixed) audio frame size in bytes.
     109 *  We only support 16-bit stereo frames at the moment. */
     110#define HDA_FRAME_SIZE          4
    107111
    108112/**
     
    10131017#ifdef IN_RING3
    10141018static void          hdaStreamDestroy(PHDASTATE pThis, PHDASTREAM pStream);
    1015 static int           hdaStreamDoDMA(PHDASTATE pThis, PHDASTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t cbToProcess, uint32_t *pcbProcessed);
    10161019static int           hdaStreamEnable(PHDASTATE pThis, PHDASTREAM pStream, bool fEnable);
    1017 static int           hdaStreamUpdate(PHDASTATE pThis, PHDASTREAM pStream);
     1020uint32_t             hdaStreamGetDataSize(PHDASTREAM pStream);
     1021static int           hdaStreamTransfer(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToProcessMax);
    10181022DECLINLINE(uint32_t) hdaStreamUpdateLPIB(PHDASTATE pThis, PHDASTREAM pStream, uint32_t u32LPIB);
    10191023static void          hdaStreamLock(PHDASTREAM pStream);
     
    13791383}
    13801384
    1381 
     1385#if 0
    13821386/**
    13831387 * Fetches the next BDLE to use for a stream.
     
    14311435    return rc;
    14321436}
     1437#endif
    14331438
    14341439
     
    20672072    if (fEnable)
    20682073    {
     2074        /* (Re-)initialize the stream with current values. */
     2075        int rc2 = hdaStreamInit(pThis, pStream, pStream->u8SD);
     2076        AssertRC(rc2);
     2077
    20692078        /* Begin a new period for this stream. */
    2070         int rc2 = hdaStreamPeriodBegin(&pStream->State.Period, hdaWalClkGetCurrent(pThis)/* Use current wall clock time */);
     2079        rc2 = hdaStreamPeriodBegin(&pStream->State.Period, hdaWalClkGetCurrent(pThis)/* Use current wall clock time */);
    20712080        AssertRC(rc2);
    20722081    }
     
    24162425/**
    24172426 * Sets the actual WALCLK register to the specified wall clock value.
    2418  * The specified wall clock value only will be set (unless fForce is set to \c @true) if all
     2427 * The specified wall clock value only will be set (unless fForce is set to true) if all
    24192428 * handled HDA streams have passed (in time) that value. This guarantees that the WALCLK
    24202429 * register stays in sync with all handled HDA streams.
    24212430 *
    2422  * @return  \c @true if the WALCLK register has been updated, \c @false if not.
     2431 * @return  true if the WALCLK register has been updated, false if not.
    24232432 * @param   pThis               HDA state.
    24242433 * @param   u64WalClk           Wall clock value to set WALCLK register to.
     
    27212730            LogFunc(("[SD%RU8]: State changed (fRun=%RTbool)\n", pStream->u8SD, fRun));
    27222731
    2723             if (fRun)
    2724             {
    2725                 /* Make sure to first fetch the current BDLE before enabling the stream below. */
    2726                 int rc2 = hdaBDLEFetch(pThis, &pStream->State.BDLE, pStream->u64BDLBase, pStream->State.uCurBDLE);
    2727                 AssertRC(rc2);
    2728             }
    2729 
    27302732            hdaStreamEnable(pThis, pStream, fRun /* fEnable */);
    27312733
     
    27512753{
    27522754#ifdef IN_RING3
    2753     uint32_t v = HDA_REG_IND(pThis, iReg);
    2754 
    27552755    PHDASTREAM pStream = hdaStreamGetFromSD(pThis, HDA_SD_NUM_FROM_REG(pThis, STS, iReg));
    27562756    if (!pStream)
     
    27612761    }
    27622762
     2763    uint32_t v = HDA_REG_IND(pThis, iReg);
     2764
    27632765    /* Clear (zero) FIFOE, DESE and BCIS bits when writing 1 to it (6.2.33). */
    27642766    HDA_REG_IND(pThis, iReg) &= ~(u32Value & v);
     
    27662768    /* Some guests tend to write SDnSTS even if the stream is not running.
    27672769     * So make sure to check if the RUN bit is set first. */
    2768     const bool fInRun = RT_BOOL(HDA_REG_IND(pThis, iReg) & HDA_SDCTL_RUN);
     2770    const bool fInRun = RT_BOOL(HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_RUN);
    27692771
    27702772    Log3Func(("[SD%RU8] fRun=%RTbool %R[sdsts]\n", pStream->u8SD, fInRun, v));
     
    32003202{
    32013203#ifdef IN_RING3
    3202     PDMAUDIOSTREAMCFG strmCfg;
    3203     RT_ZERO(strmCfg);
    3204 
    3205     int rc = hdaSDFMTToStrmCfg(u32Value, &strmCfg);
    3206     if (RT_FAILURE(rc))
    3207         return VINF_SUCCESS; /* Always return success to the MMIO handler. */
    3208 
    32093204    PHDASTREAM pStream = hdaStreamGetFromSD(pThis, HDA_SD_NUM_FROM_REG(pThis, FMT, iReg));
    32103205    if (!pStream)
     
    32153210    }
    32163211
     3212    PPDMAUDIOSTREAMCFG pCfg = &pStream->State.strmCfg;
     3213
     3214    int rc = hdaSDFMTToStrmCfg(u32Value, pCfg);
     3215    if (RT_FAILURE(rc))
     3216        return VINF_SUCCESS; /* Always return success to the MMIO handler. */
     3217
    32173218    LogFunc(("[SD%RU8]: Hz=%RU32, Channels=%RU8, cBits=%RU8\n",
    3218              pStream->u8SD, strmCfg.Props.uHz, strmCfg.Props.cChannels, strmCfg.Props.cBits));
     3219             pStream->u8SD, pCfg->Props.uHz, pCfg->Props.cChannels, pCfg->Props.cBits));
    32193220
    32203221    /* Set audio direction. */
    3221     strmCfg.enmDir = hdaGetDirFromSD(pStream->u8SD);
    3222     switch (strmCfg.enmDir)
     3222    pCfg->enmDir = hdaGetDirFromSD(pStream->u8SD);
     3223    switch (pCfg->enmDir)
    32233224    {
    32243225        case PDMAUDIODIR_IN:
     
    32263227#  error "Implement me!"
    32273228# else
    3228             strmCfg.DestSource.Source = PDMAUDIORECSOURCE_LINE;
    3229             RTStrCopy(strmCfg.szName, sizeof(strmCfg.szName), "Line In");
     3229            pCfg->DestSource.Source = PDMAUDIORECSOURCE_LINE;
     3230            RTStrCopy(pCfg->szName, sizeof(pCfg->szName), "Line In");
    32303231# endif
    32313232            break;
     
    32543255     * the supported channels within a single audio stream, e.g. mono/stereo.
    32553256     *
    3256      * In other words, the stream mapping *always* knowns the real
     3257     * In other words, the stream mapping *always* knows the real
    32573258     * number of channels in a single audio stream.
    32583259     */
    32593260    if (RT_SUCCESS(rc))
    32603261    {
    3261         rc = hdaStreamMapInit(&pStream->State.Mapping, &strmCfg);
     3262        rc = hdaStreamMapInit(&pStream->State.Mapping, pCfg);
    32623263        AssertRC(rc);
    32633264    }
     
    32693270        {
    32703271            int rc2;
    3271             switch (strmCfg.enmDir)
     3272            switch (pCfg->enmDir)
    32723273            {
    32733274                case PDMAUDIODIR_OUT:
    3274                     rc2 = hdaAddStreamOut(pThis, &strmCfg);
     3275                    rc2 = hdaAddStreamOut(pThis, pCfg);
    32753276                    break;
    32763277
    32773278                case PDMAUDIODIR_IN:
    3278                     rc2 = hdaAddStreamIn(pThis, &strmCfg);
     3279                    rc2 = hdaAddStreamIn(pThis, pCfg);
    32793280                    break;
    32803281
     
    35033504 * Registers access handlers for a stream's BDLE DMA accesses.
    35043505 *
    3505  * @returns \c @true if registration was successful, \c @false if not.
     3506 * @returns true if registration was successful, false if not.
    35063507 * @param   pThis               HDA state.
    35073508 * @param   pStream             HDA stream to register BDLE access handlers for.
     
    37073708    }
    37083709}
    3709 #endif
     3710#endif /* LOG_ENABLED */
    37103711
    37113712/**
     
    37433744
    37443745/**
     3746 * Tells whether a given BDLE is complete or not.
     3747 *
     3748 * @return  @true if BDLE is complete, @false if not.
     3749 * @param   pBDLE               BDLE to retrieve status for.
     3750 */
     3751static bool hdaBDLEIsComplete(PHDABDLE pBDLE)
     3752{
     3753    bool fIsComplete = false;
     3754
     3755    if (   !pBDLE->Desc.u32BufSize /* There can be BDLEs with 0 size. */
     3756        || (pBDLE->State.u32BufOff >= pBDLE->Desc.u32BufSize))
     3757    {
     3758        Assert(pBDLE->State.u32BufOff == pBDLE->Desc.u32BufSize);
     3759        fIsComplete = true;
     3760    }
     3761
     3762    Log3Func(("%R[bdle] => %s\n", pBDLE, fIsComplete ? "COMPLETE" : "INCOMPLETE"));
     3763
     3764    return fIsComplete;
     3765}
     3766
     3767/**
     3768 * Tells whether a given BDLE needs an interrupt or not.
     3769 *
     3770 * @return  @true if BDLE needs an interrupt, @false if not.
     3771 * @param   pBDLE               BDLE to retrieve status for.
     3772 */
     3773static bool hdaBDLENeedsInterrupt(PHDABDLE pBDLE)
     3774{
     3775    return (pBDLE->Desc.fFlags & HDA_BDLE_FLAG_IOC);
     3776}
     3777
     3778/**
    37453779 * Returns the number of outstanding stream data bytes which need to be processed
    37463780 * by the DMA engine assigned to this stream.
     
    37893823}
    37903824
     3825#if 0
    37913826DECLINLINE(void) hdaBDLEUpdate(PHDABDLE pBDLE, uint32_t cbData, uint32_t cbProcessed)
    37923827{
     
    38243859    LogFlowFunc(("cbData=%RU32, cbProcessed=%RU32, %R[bdle]\n", cbData, cbProcessed, pBDLE));
    38253860}
     3861#endif
    38263862
    38273863#ifdef IN_RING3
     
    39343970#endif /* IN_RING3 */
    39353971
     3972#if 0
    39363973DECLINLINE(bool) hdaStreamNeedsNextBDLE(PHDASTATE pThis, PHDASTREAM pStream)
    39373974{
     
    39573994
    39583995    return fNeedsNextBDLE;
     3996}
     3997#endif
     3998
     3999/**
     4000 * Returns the number of outstanding stream data bytes which need to be processed
     4001 * by the DMA engine assigned to this stream.
     4002 *
     4003 * @return Number of bytes for the DMA engine to process.
     4004 */
     4005DECLINLINE(uint32_t) hdaStreamGetTransferSize(PHDASTATE pThis, PHDASTREAM pStream)
     4006{
     4007    AssertPtrReturn(pThis, 0);
     4008    AssertPtrReturn(pStream, 0);
     4009
     4010    if (!RT_BOOL(HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_RUN))
     4011    {
     4012        AssertFailed(); /* Should never happen. */
     4013        return 0;
     4014    }
     4015
     4016    PHDABDLE pBDLE = &pStream->State.BDLE;
     4017
     4018    uint32_t cbFree = RT_MIN(pBDLE->Desc.u32BufSize, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
     4019
     4020    Log3Func(("[SD%RU8] LPIB=%RU32 CBL=%RU32 -> cbFree=%RU32 %R[bdle]\n", pStream->u8SD,
     4021              HDA_STREAM_REG(pThis, LPIB, pStream->u8SD), pStream->u32CBL, cbFree, pBDLE));
     4022    return cbFree;
     4023}
     4024
     4025DECLINLINE(void) hdaStreamTransferInc(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbInc)
     4026{
     4027    AssertPtrReturnVoid(pThis);
     4028    AssertPtrReturnVoid(pStream);
     4029
     4030    if (!cbInc)
     4031        return;
     4032
     4033    const uint32_t u32LPIB = HDA_STREAM_REG(pThis, LPIB, pStream->u8SD);
     4034
     4035    Log3Func(("[SD%RU8] %RU32 + %RU32 -> %RU32, CBL=%RU32\n",
     4036              pStream->u8SD, u32LPIB, cbInc, u32LPIB + cbInc, pStream->u32CBL));
     4037
     4038    hdaStreamUpdateLPIB(pThis, pStream, u32LPIB + cbInc);
    39594039}
    39604040
     
    39844064}
    39854065
     4066#if 0
    39864067static bool hdaBDLEIsComplete(PHDABDLE pBDLE, bool *pfInterrupt)
    39874068{
     
    40154096    return fIsComplete;
    40164097}
     4098#endif
    40174099
    40184100/**
     
    46334715
    46344716/**
     4717 * Reads DMA data from a given HDA output stream into its associated FIFO buffer.
     4718 *
     4719 * @return  IPRT status code.
     4720 * @param   pThis               HDA state.
     4721 * @param   pStream             HDA output stream to read DMA data from.
     4722 * @param   cbToRead            How much (in bytes) to read from DMA.
     4723 * @param   pcbRead             Returns read bytes from DMA. Optional.
     4724 */
     4725static int hdaDMARead(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToRead, uint32_t *pcbRead)
     4726{
     4727    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
     4728    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     4729    /* pcbRead is optional. */
     4730
     4731    int rc = VINF_SUCCESS;
     4732
     4733    uint32_t cbReadTotal = 0;
     4734
     4735    PHDABDLE   pBDLE     = &pStream->State.BDLE;
     4736    PRTCIRCBUF pCircBuf  = pStream->State.pCircBuf;
     4737    AssertPtr(pCircBuf);
     4738
     4739#ifdef HDA_DEBUG_SILENCE
     4740    uint64_t   csSilence = 0;
     4741
     4742    pStream->Dbg.cSilenceThreshold = 100;
     4743    pStream->Dbg.cbSilenceReadMin  = _1M;
     4744#endif
     4745
     4746    while (cbToRead)
     4747    {
     4748        /* Make sure we only copy as much as the stream's FIFO can hold (SDFIFOS, 18.2.39). */
     4749        void *pvBuf;
     4750        size_t cbBuf;
     4751        RTCircBufAcquireWriteBlock(pCircBuf, RT_MIN(cbToRead, pStream->u16FIFOS), &pvBuf, &cbBuf);
     4752
     4753        if (cbBuf)
     4754        {
     4755            /*
     4756             * Read from the current BDLE's DMA buffer.
     4757             */
     4758            int rc2 = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns),
     4759                                        pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff + cbReadTotal, pvBuf, cbBuf);
     4760            AssertRC(rc2);
     4761
     4762#ifdef HDA_DEBUG_SILENCE
     4763            uint16_t *pu16Buf = (uint16_t *)pvBuf;
     4764            for (size_t i = 0; i < cbBuf / sizeof(uint16_t); i++)
     4765            {
     4766                if (*pu16Buf == 0)
     4767                {
     4768                    csSilence++;
     4769                }
     4770                else
     4771                    break;
     4772                pu16Buf++;
     4773            }
     4774#endif
     4775
     4776#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
     4777            if (cbBuf)
     4778            {
     4779                RTFILE fh;
     4780                rc2 = RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMARead.pcm",
     4781                                 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
     4782                if (RT_SUCCESS(rc2))
     4783                {
     4784                    RTFileWrite(fh, pvBuf, cbBuf, NULL);
     4785                    RTFileClose(fh);
     4786                }
     4787                else
     4788                    AssertRC(rc2);
     4789            }
     4790#endif
     4791
     4792#if 0
     4793            pStream->Dbg.cbReadTotal += cbBuf;
     4794            const uint64_t cbWritten = ASMAtomicReadU64(&pStream->Dbg.cbWrittenTotal);
     4795            Log3Func(("cbRead=%RU64, cbWritten=%RU64 -> %RU64 bytes %s\n",
     4796                      pStream->Dbg.cbReadTotal, cbWritten,
     4797                      pStream->Dbg.cbReadTotal >= cbWritten ? pStream->Dbg.cbReadTotal - cbWritten : cbWritten - pStream->Dbg.cbReadTotal,
     4798                      pStream->Dbg.cbReadTotal > cbWritten ? "too much" : "too little"));
     4799#endif
     4800
     4801#ifdef VBOX_WITH_STATISTICS
     4802            STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBuf);
     4803#endif
     4804        }
     4805
     4806        RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
     4807
     4808        if (!cbBuf)
     4809        {
     4810            rc = VERR_BUFFER_OVERFLOW;
     4811            break;
     4812        }
     4813
     4814        cbReadTotal += (uint32_t)cbBuf;
     4815        Assert(pBDLE->State.u32BufOff + cbReadTotal <= pBDLE->Desc.u32BufSize);
     4816
     4817        Assert(cbToRead >= cbBuf);
     4818        cbToRead    -= (uint32_t)cbBuf;
     4819    }
     4820
     4821#ifdef HDA_DEBUG_SILENCE
     4822
     4823    if (csSilence)
     4824        pStream->Dbg.csSilence += csSilence;
     4825
     4826    if (   csSilence == 0
     4827        && pStream->Dbg.csSilence   >  pStream->Dbg.cSilenceThreshold
     4828        && pStream->Dbg.cbReadTotal >= pStream->Dbg.cbSilenceReadMin)
     4829    {
     4830        LogFunc(("Silent block detected: %RU64 audio samples\n", pStream->Dbg.csSilence));
     4831        pStream->Dbg.csSilence = 0;
     4832    }
     4833#endif
     4834
     4835    if (RT_SUCCESS(rc))
     4836    {
     4837        if (pcbRead)
     4838            *pcbRead = cbReadTotal;
     4839    }
     4840
     4841    return rc;
     4842}
     4843
     4844/**
     4845 * Writes audio data from an HDA input stream's FIFO to its associated DMA area.
     4846 *
     4847 * @return  IPRT status code.
     4848 * @param   pThis               HDA state.
     4849 * @param   pStream             HDA input stream to write audio data to.
     4850 * @param   cbToWrite           How much (in bytes) to write.
     4851 * @param   pcbWritten          Returns written bytes on success. Optional.
     4852 */
     4853static int hdaDMAWrite(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToWrite, uint32_t *pcbWritten)
     4854{
     4855    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
     4856    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     4857    /* pcbWritten is optional. */
     4858
     4859    PHDABDLE   pBDLE    = &pStream->State.BDLE;
     4860    PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
     4861    AssertPtr(pCircBuf);
     4862
     4863    int rc = VINF_SUCCESS;
     4864
     4865    uint32_t cbWrittenTotal = 0;
     4866
     4867    void *pvBuf  = NULL;
     4868    size_t cbBuf = 0;
     4869
     4870    uint8_t abSilence[HDA_FIFO_MAX + 1] = { 0 };
     4871
     4872    while (cbToWrite)
     4873    {
     4874        size_t cbChunk = RT_MIN(cbToWrite, pStream->u16FIFOS);
     4875
     4876        size_t cbBlock = 0;
     4877        RTCircBufAcquireReadBlock(pCircBuf, cbChunk, &pvBuf, &cbBlock);
     4878
     4879        if (cbBlock)
     4880        {
     4881            cbBuf = cbBlock;
     4882        }
     4883        else /* No audio data available? Send silence. */
     4884        {
     4885            pvBuf = &abSilence;
     4886            cbBuf = cbChunk;
     4887        }
     4888
     4889        /* Sanity checks. */
     4890        Assert(cbBuf <= pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
     4891        Assert(cbBuf % HDA_FRAME_SIZE == 0);
     4892        Assert((cbBuf >> 1) >= 1);
     4893
     4894#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
     4895        RTFILE fh;
     4896        RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMAWrite.pcm",
     4897                   RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
     4898        RTFileWrite(fh, pvBuf, cbBuf, NULL);
     4899        RTFileClose(fh);
     4900#endif
     4901        int rc2 = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
     4902                                        pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff + cbWrittenTotal,
     4903                                        pvBuf, cbBuf);
     4904        AssertRC(rc2);
     4905
     4906#ifdef VBOX_WITH_STATISTICS
     4907        STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbBuf);
     4908#endif
     4909        if (cbBlock)
     4910            RTCircBufReleaseReadBlock(pCircBuf, cbBlock);
     4911
     4912        Assert(cbToWrite >= cbBuf);
     4913        cbToWrite -= (uint32_t)cbBuf;
     4914
     4915        cbWrittenTotal += (uint32_t)cbBuf;
     4916    }
     4917
     4918    if (RT_SUCCESS(rc))
     4919    {
     4920        if (pcbWritten)
     4921            *pcbWritten = cbWrittenTotal;
     4922    }
     4923    else
     4924        LogFunc(("Failed with %Rrc\n", rc));
     4925
     4926    return rc;
     4927}
     4928
     4929/**
    46354930 * Timer callback which handles the audio data transfers on a periodic basis.
    46364931 *
     
    47085003static void hdaDoTransfers(PHDASTATE pThis)
    47095004{
    4710     PHDASTREAM pStreamLineIn  = hdaSinkGetStream(pThis, &pThis->SinkLineIn);
     5005    //PHDASTREAM pStreamLineIn  = hdaSinkGetStream(pThis, &pThis->SinkLineIn);
    47115006#ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
    47125007    PHDASTREAM pStreamMicIn   = hdaSinkGetStream(pThis, &pThis->SinkMicIn);
     
    47175012#endif
    47185013
    4719     hdaStreamUpdate(pThis, pStreamLineIn);
     5014#if 0
     5015    uint32_t cbWritten = 0;
     5016    int rc2 = hdaStreamWrite(pThis, pStreamLineIn, &cbWritten);
     5017    if (   RT_SUCCESS(rc2)
     5018        && cbWritten)
     5019    {
     5020        rc2 = hdaStreamTransfer(pThis, pStreamLineIn, cbWritten);
     5021        AssertRC(rc2);
     5022    }
     5023
    47205024#ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
    4721     hdaStreamUpdate(pThis, pStreamMicIn);
    4722 #endif
    4723     hdaStreamUpdate(pThis, pStreamFront);
    4724 
     5025    rc2 = hdaStreamWrite(pThis, pStreamMicIn, &cbWritten);
     5026    if (   RT_SUCCESS(rc2)
     5027        && cbWritten)
     5028    {
     5029        rc2 = hdaStreamTransfer(pThis, pStreamMicIn, cbWritten);
     5030        AssertRC(rc2);
     5031    }
     5032#endif
     5033#endif
     5034
     5035    int rc2;
     5036
     5037    /* Is the sink ready to be written to? If so, how much? */
     5038    uint32_t cbToWrite = AudioMixerSinkGetWritable(pThis->SinkFront.pMixSink);
     5039    if (cbToWrite)
     5040    {
     5041        rc2 = hdaStreamTransfer(pThis, pStreamFront, cbToWrite);
     5042        AssertRC(rc2);
     5043
     5044        if (hdaStreamGetDataSize(pStreamFront))
     5045        {
     5046#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
     5047            /* Let the asynchronous thread know that there is some new data to process. */
     5048            hdaStreamAsyncIONotify(pThis, pStreamFront);
     5049#else
     5050            /* Read audio data from the HDA stream and write to the backends. */
     5051            rc2 = hdaStreamRead(pThis, pStream, cbToWrite, NULL /* pcbRead */);
     5052            AssertRC(rc2);
     5053#endif
     5054        }
     5055    }
    47255056
    47265057#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
    4727     int rc2 = AudioMixerSinkUpdate(pThis->SinkCenterLFE.pMixSink);
     5058    rc2 = AudioMixerSinkUpdate(pThis->SinkCenterLFE.pMixSink);
    47285059    AssertRC(rc2);
    47295060
     
    47445075#endif
    47455076
     5077#if 0
    47465078/**
    47475079 * Does a single DMA transfer for a specific HDA stream (SDI/SDO).
     
    49405272    return rc;
    49415273}
     5274#endif
     5275
     5276/**
     5277 * Retrieves the available size of (buffered) audio data (in bytes) of a given HDA stream.
     5278 *
     5279 * @returns Data size (in bytes).
     5280 * @param   pStream             HDA stream to retrieve size for.
     5281 */
     5282uint32_t hdaStreamGetDataSize(PHDASTREAM pStream)
     5283{
     5284    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     5285
     5286    if (!pStream->State.pCircBuf)
     5287        return 0;
     5288
     5289    return (uint32_t)RTCircBufSize(pStream->State.pCircBuf);
     5290}
    49425291
    49435292/**
     
    52935642#endif /* VBOX_WITH_AUDIO_HDA_ASYNC_IO */
    52945643
    5295 /**
    5296  * Updates a HDA stream according to its usage (input / output).
     5644static uint32_t hdaStreamTransferGetElapsed(PHDASTATE pThis, PHDASTREAM pStream)
     5645{
     5646    const uint64_t cTicksNow     = TMTimerGet(pThis->pTimer);
     5647    const uint64_t cTicksPerSec  = TMTimerGetFreq(pThis->pTimer);
     5648
     5649    const uint64_t cTicksElapsed = cTicksNow - pStream->State.uTimerTS;
     5650#ifdef DEBUG
     5651    const uint64_t cMsElapsed    = cTicksElapsed / (cTicksPerSec / 1000);
     5652#endif
     5653
     5654    AssertPtr(pThis->pCodec);
     5655
     5656    PPDMAUDIOSTREAMCFG pCfg = &pStream->State.strmCfg;
     5657
     5658    /* A stream *always* runs with 48 kHz device-wise, regardless of the actual stream input/output format (Hz) being set. */
     5659    uint32_t csPerPeriod = (int)((pCfg->Props.cChannels * cTicksElapsed * 48000 /* Hz */ + cTicksPerSec) / cTicksPerSec / 2);
     5660    uint32_t cbPerPeriod = csPerPeriod << pCfg->Props.cShift;
     5661
     5662    Log3Func(("[SD%RU8] %RU64ms (%zu samples, %zu bytes) elapsed\n", pStream->u8SD, cMsElapsed, csPerPeriod, cbPerPeriod));
     5663
     5664    return cbPerPeriod;
     5665}
     5666
     5667/**
     5668 * Transfers data of an HDA stream according to its usage (input / output).
    52975669 *
    52985670 * For an SDO (output) stream this means reading DMA data from the device to
    5299  * the connected audio sink(s).
    5300  *
    5301  * For an SDI (input) stream this is reading audio data from the connected
    5302  * audio sink(s) and writing it as DMA data to the device.
     5671 * the HDA stream's internal FIFO buffer.
     5672 *
     5673 * For an SDI (input) stream this is reading audio data from the HDA stream's
     5674 * internal FIFO buffer and writing it as DMA data to the device.
    53035675 *
    53045676 * @returns IPRT status code.
    53055677 * @param   pThis               HDA state.
    53065678 * @param   pStream             HDA stream to update.
    5307  */
    5308 static int hdaStreamUpdate(PHDASTATE pThis, PHDASTREAM pStream)
    5309 {
    5310     AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
    5311     AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     5679 * @param   cbToProcessMax      Maximum of data (in bytes) to process.
     5680 */
     5681static int hdaStreamTransfer(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToProcessMax)
     5682{
     5683    AssertPtrReturn(pThis,          VERR_INVALID_POINTER);
     5684    AssertPtrReturn(pStream,        VERR_INVALID_POINTER);
     5685    AssertReturn(cbToProcessMax,    VERR_INVALID_PARAMETER);
    53125686
    53135687    hdaStreamLock(pStream);
    53145688
    5315     PHDAMIXERSINK pSink  = pStream->pMixSink;
    5316     AssertPtr(pSink);
    5317 
    5318     PRTCIRCBUF pCircBuf  = pStream->State.pCircBuf;
    5319     AssertPtr(pCircBuf);
    5320 
    5321     if (!AudioMixerSinkIsActive(pSink->pMixSink))
    5322     {
     5689    PHDASTREAMPERIOD pPeriod = &pStream->State.Period;
     5690    int rc = hdaStreamPeriodLock(pPeriod);
     5691    AssertRC(rc);
     5692
     5693    bool fProceed = true;
     5694
     5695    /* Stream not running? */
     5696    if (!(HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_RUN))
     5697    {
     5698        Log3Func(("[SD%RU8] RUN bit not set\n", pStream->u8SD));
     5699        fProceed = false;
     5700    }
     5701    /* Period complete? */
     5702    else if (hdaStreamPeriodIsComplete(pPeriod))
     5703    {
     5704        Log3Func(("[SD%RU8] Period is complete, nothing to do\n", pStream->u8SD));
     5705        fProceed = false;
     5706    }
     5707
     5708    if (!fProceed)
     5709    {
     5710        hdaStreamPeriodUnlock(pPeriod);
    53235711        hdaStreamUnlock(pStream);
    53245712        return VINF_SUCCESS;
    53255713    }
    53265714
     5715    /* Sanity checks. */
     5716    Assert(pStream->u8SD <= HDA_MAX_STREAMS);
     5717    Assert(pStream->u64BDLBase);
     5718
     5719    /* State sanity checks. */
     5720    Assert(ASMAtomicReadBool(&pStream->State.fInReset) == false);
     5721
     5722    /* Fetch first / next BDL entry. */
     5723    PHDABDLE pBDLE = &pStream->State.BDLE;
     5724    if (hdaBDLEIsComplete(pBDLE))
     5725    {
     5726        rc = hdaBDLEFetch(pThis, pBDLE, pStream->u64BDLBase, pStream->State.uCurBDLE);
     5727        AssertRC(rc);
     5728    }
     5729
     5730    const uint32_t cbPeriodRemaining = hdaStreamPeriodGetRemainingFrames(pPeriod) * HDA_FRAME_SIZE;
     5731    Assert(cbPeriodRemaining); /* Paranoia. */
     5732
     5733    const uint32_t cbElapsed         = hdaStreamTransferGetElapsed(pThis, pStream);
     5734
     5735    /* Limit the data to read, as this routine could be delayed and therefore
     5736     * report wrong (e.g. too much) cbElapsed bytes. */
     5737    uint32_t cbLeft                  = RT_MIN(RT_MIN(cbPeriodRemaining, cbElapsed), cbToProcessMax);
     5738    Assert(cbLeft % HDA_FRAME_SIZE == 0); /* Paranoia. */
     5739
     5740    Log3Func(("[SD%RU8] cbPeriodRemaining=%RU32, cbElapsed=%RU32, cbToProcessMax=%RU32 -> cbLeft=%RU32\n",
     5741              pStream->u8SD, cbPeriodRemaining, cbElapsed, cbToProcessMax, cbLeft));
     5742
    53275743    Log2Func(("[SD%RU8]\n", pStream->u8SD));
    53285744
    5329     bool fDone = false;
    5330     uint8_t cTransfers = 0;
    5331 
    5332     while (!fDone)
    5333     {
    5334         int rc2;
    5335         uint32_t cbDMA = 0;
    5336 
     5745    while (cbLeft >> 1) /** @todo Define frame size? */
     5746    {
     5747        uint32_t cbChunk = RT_MIN(hdaStreamGetTransferSize(pThis, pStream), cbLeft);
     5748        uint32_t cbDMA   = 0;
     5749#if 1
     5750        if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
     5751        {
     5752            STAM_PROFILE_START(&pThis->StatOut, a);
     5753
     5754            rc = hdaDMARead(pThis, pStream, cbChunk, &cbDMA /* pcbRead */);
     5755            if (RT_FAILURE(rc))
     5756                LogRel(("HDA: Reading from stream #%RU8 DMA failed with %Rrc\n", pStream->u8SD, rc));
     5757
     5758            STAM_PROFILE_STOP(&pThis->StatOut, a);
     5759        }
     5760        else if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN) /* Input (SDI). */
     5761        {
     5762            STAM_PROFILE_START(&pThis->StatIn, a);
     5763
     5764            rc = hdaDMAWrite(pThis, pStream, cbChunk, &cbDMA /* pcbWritten */);
     5765            if (RT_FAILURE(rc))
     5766                LogRel(("HDA: Writing to stream #%RU8 DMA failed with %Rrc\n", pStream->u8SD, rc));
     5767
     5768            STAM_PROFILE_STOP(&pThis->StatIn, a);
     5769        }
     5770        else /** @todo Handle duplex streams? */
     5771            AssertFailed();
     5772
     5773        if (cbDMA)
     5774        {
     5775            Assert(cbDMA % HDA_FRAME_SIZE == 0);
     5776
     5777            /* We always increment the position of DMA buffer counter because we're always reading
     5778             * into an intermediate buffer. */
     5779            pBDLE->State.u32BufOff += (uint32_t)cbDMA;
     5780            Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
     5781
     5782            hdaStreamTransferInc(pThis, pStream, cbDMA);
     5783
     5784            uint32_t framesDMA = cbDMA / 4; /** @todo Define frame size? */
     5785
     5786            /* Add the transferred frames to the period. */
     5787            hdaStreamPeriodInc(pPeriod, framesDMA);
     5788
     5789            /* Save the timestamp of when the last successful DMA transfer has been for this stream. */
     5790            pStream->State.uTimerTS = TMTimerGet(pThis->pTimer);
     5791
     5792            Assert(cbLeft >= cbDMA);
     5793            cbLeft        -= cbDMA;
     5794        }
     5795
     5796        if (hdaBDLEIsComplete(pBDLE))
     5797        {
     5798            Log3Func(("[SD%RU8] Complete: %R[bdle]\n", pStream->u8SD, pBDLE));
     5799
     5800            if (hdaBDLENeedsInterrupt(pBDLE))
     5801            {
     5802                /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL register is set
     5803                 * we need to generate an interrupt.
     5804                 */
     5805                if (HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_IOCE)
     5806                    hdaStreamPeriodAcquireInterrupt(pPeriod);
     5807            }
     5808
     5809            if (pStream->State.uCurBDLE == pStream->u16LVI)
     5810            {
     5811                Assert(pStream->u32CBL == HDA_STREAM_REG(pThis, LPIB, pStream->u8SD));
     5812
     5813                pStream->State.uCurBDLE = 0;
     5814                hdaStreamUpdateLPIB(pThis, pStream, 0 /* LPIB */);
     5815            }
     5816            else
     5817                pStream->State.uCurBDLE++;
     5818
     5819            hdaBDLEFetch(pThis, pBDLE, pStream->u64BDLBase, pStream->State.uCurBDLE);
     5820
     5821            Log3Func(("[SD%RU8] Fetching: %R[bdle]\n", pStream->u8SD, pBDLE));
     5822        }
     5823
     5824        if (RT_FAILURE(rc))
     5825            break;
     5826    }
     5827
     5828    if (hdaStreamPeriodIsComplete(pPeriod))
     5829    {
     5830        Log3Func(("[SD%RU8] Period complete -- Current: %R[bdle]\n", pStream->u8SD, &pStream->State.BDLE));
     5831
     5832        /* Set the stream's BCIS bit.
     5833         *
     5834         * Note: This only must be done if the whole period is complete, and not if only
     5835         * one specific BDL entry is complete (if it has the IOC bit set).
     5836         *
     5837         * This will otherwise confuses the guest when it 1) deasserts the interrupt,
     5838         * 2) reads SDSTS (with BCIS set) and then 3) too early reads a (wrong) WALCLK value.
     5839         *
     5840         * snd_hda_intel on Linux will tell. */
     5841        HDA_STREAM_REG(pThis, STS, pStream->u8SD) |= HDA_SDSTS_BCIS;
     5842
     5843        /* Try updating the wall clock. */
     5844        const uint64_t u64WalClk  = hdaStreamPeriodGetAbsElapsedWalClk(pPeriod);
     5845        const bool     fWalClkSet = hdaWalClkSet(pThis, u64WalClk, false /* fForce */);
     5846
     5847        /* Does the period have any interrupts outstanding? */
     5848        if (hdaStreamPeriodNeedsInterrupt(pPeriod))
     5849        {
     5850            if (fWalClkSet)
     5851            {
     5852                Log3Func(("[SD%RU8] Set WALCLK to %RU64, triggering interrupt\n", pStream->u8SD, u64WalClk));
     5853
     5854                /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
     5855                 * ending / beginning a period. */
     5856#ifndef DEBUG
     5857                hdaProcessInterrupt(pThis);
     5858#else
     5859                hdaProcessInterrupt(pThis, __FUNCTION__);
     5860#endif
     5861            }
     5862        }
     5863        else
     5864        {
     5865            /* End the period first ... */
     5866            hdaStreamPeriodEnd(pPeriod);
     5867
     5868            /* ... and immediately begin the next one. */
     5869            hdaStreamPeriodBegin(pPeriod, hdaWalClkGetCurrent(pThis));
     5870        }
     5871    }
     5872
     5873    hdaStreamPeriodUnlock(pPeriod);
     5874#else
    53375875        if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
    53385876        {
     
    54595997        if (++cTransfers == UINT8_MAX) /* Failsafe counter. */
    54605998            fDone = true;
    5461 
    54625999    } /* while !fDone */
    54636000
    54646001#ifdef VBOX_STRICT
    54656002    AssertMsg(cTransfers < UINT8_MAX, ("HDA: Update for SD#%RU8 ran for too long\n", pStream->u8SD));
     6003#endif
    54666004#endif
    54676005
Note: See TracChangeset for help on using the changeset viewer.

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