Index: /trunk/src/VBox/Devices/Audio/DevHDA.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DevHDA.cpp	(revision 69918)
+++ /trunk/src/VBox/Devices/Audio/DevHDA.cpp	(revision 69919)
@@ -229,5 +229,7 @@
 static int hdaRegWriteCORBRP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
 static int hdaRegWriteCORBCTL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int hdaRegWriteCORBSIZE(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
 static int hdaRegWriteCORBSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int hdaRegWriteRINTCNT(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
 static int hdaRegWriteRIRBWP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
 static int hdaRegWriteRIRBSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
@@ -281,8 +283,6 @@
  * @{
  */
-#if !defined(VBOX_WITH_AUDIO_HDA_CALLBACKS) && defined(IN_RING3)
-static int           hdaTimerMaybeStart(PHDASTATE pThis);
-static int           hdaTimerMaybeStop(PHDASTATE pThis);
-static void          hdaTimerMain(PHDASTATE pThis);
+#ifdef IN_RING3
+static void hdaTimerMain(PHDASTATE pThis);
 #endif
 /** @} */
@@ -351,12 +351,12 @@
     { 0x00044, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, HDA_RD_FLAG_NONE, hdaRegReadU32   , hdaRegWriteBase    , HDA_REG_IDX(CORBUBASE)    }, /* CORB Upper Base Address */
     { 0x00048, 0x00002, 0x000000FF, 0x000000FF, HDA_RD_FLAG_NONE, hdaRegReadU16   , hdaRegWriteCORBWP  , HDA_REG_IDX(CORBWP)       }, /* CORB Write Pointer */
-    { 0x0004A, 0x00002, 0x000080FF, 0x000080FF, HDA_RD_FLAG_NONE, hdaRegReadU16   , hdaRegWriteCORBRP  , HDA_REG_IDX(CORBRP)       }, /* CORB Read Pointer */
+    { 0x0004A, 0x00002, 0x000080FF, 0x00008000, HDA_RD_FLAG_NONE, hdaRegReadU16   , hdaRegWriteCORBRP  , HDA_REG_IDX(CORBRP)       }, /* CORB Read Pointer */
     { 0x0004C, 0x00001, 0x00000003, 0x00000003, HDA_RD_FLAG_NONE, hdaRegReadU8    , hdaRegWriteCORBCTL , HDA_REG_IDX(CORBCTL)      }, /* CORB Control */
     { 0x0004D, 0x00001, 0x00000001, 0x00000001, HDA_RD_FLAG_NONE, hdaRegReadU8    , hdaRegWriteCORBSTS , HDA_REG_IDX(CORBSTS)      }, /* CORB Status */
-    { 0x0004E, 0x00001, 0x000000F3, 0x00000000, HDA_RD_FLAG_NONE, hdaRegReadU8    , hdaRegWriteUnimpl  , HDA_REG_IDX(CORBSIZE)     }, /* CORB Size */
+    { 0x0004E, 0x00001, 0x000000F3, 0x00000003, HDA_RD_FLAG_NONE, hdaRegReadU8    , hdaRegWriteCORBSIZE, HDA_REG_IDX(CORBSIZE)     }, /* CORB Size */
     { 0x00050, 0x00004, 0xFFFFFF80, 0xFFFFFF80, HDA_RD_FLAG_NONE, hdaRegReadU32   , hdaRegWriteBase    , HDA_REG_IDX(RIRBLBASE)    }, /* RIRB Lower Base Address */
     { 0x00054, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, HDA_RD_FLAG_NONE, hdaRegReadU32   , hdaRegWriteBase    , HDA_REG_IDX(RIRBUBASE)    }, /* RIRB Upper Base Address */
     { 0x00058, 0x00002, 0x000000FF, 0x00008000, HDA_RD_FLAG_NONE, hdaRegReadU8    , hdaRegWriteRIRBWP  , HDA_REG_IDX(RIRBWP)       }, /* RIRB Write Pointer */
-    { 0x0005A, 0x00002, 0x000000FF, 0x000000FF, HDA_RD_FLAG_NONE, hdaRegReadU16   , hdaRegWriteU16     , HDA_REG_IDX(RINTCNT)      }, /* Response Interrupt Count */
+    { 0x0005A, 0x00002, 0x000000FF, 0x000000FF, HDA_RD_FLAG_NONE, hdaRegReadU16   , hdaRegWriteRINTCNT , HDA_REG_IDX(RINTCNT)      }, /* Response Interrupt Count */
     { 0x0005C, 0x00001, 0x00000007, 0x00000007, HDA_RD_FLAG_NONE, hdaRegReadU8    , hdaRegWriteU8      , HDA_REG_IDX(RIRBCTL)      }, /* RIRB Control */
     { 0x0005D, 0x00001, 0x00000005, 0x00000005, HDA_RD_FLAG_NONE, hdaRegReadU8    , hdaRegWriteRIRBSTS , HDA_REG_IDX(RIRBSTS)      }, /* RIRB Status */
@@ -436,5 +436,5 @@
     SSMFIELD_ENTRY(HDASTREAMSTATE, uCurBDLE),
     SSMFIELD_ENTRY(HDASTREAMSTATE, fInReset),
-    SSMFIELD_ENTRY(HDASTREAMSTATE, uTimerTS),
+    SSMFIELD_ENTRY(HDASTREAMSTATE, tsTransferNext),
     SSMFIELD_ENTRY_TERM()
 };
@@ -723,19 +723,25 @@
     if (fLocal)
     {
-        Assert((HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA));
-        Assert(pThis->u64CORBBase);
-        AssertPtr(pThis->pu32CorbBuf);
-        Assert(pThis->cbCorbBuf);
-
-        rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), pThis->u64CORBBase, pThis->pu32CorbBuf, pThis->cbCorbBuf);
-        if (RT_FAILURE(rc))
-            AssertRCReturn(rc, rc);
+        if (pThis->u64CORBBase)
+        {
+            AssertPtr(pThis->pu32CorbBuf);
+            Assert(pThis->cbCorbBuf);
+
+            rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), pThis->u64CORBBase, pThis->pu32CorbBuf, pThis->cbCorbBuf);
+            if (RT_FAILURE(rc))
+                AssertRCReturn(rc, rc);
+        }
     }
     else
     {
-        Assert((HDA_REG(pThis, RIRBCTL) & HDA_RIRBCTL_RDMAEN));
-        rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), pThis->u64RIRBBase, pThis->pu64RirbBuf, pThis->cbRirbBuf);
-        if (RT_FAILURE(rc))
-            AssertRCReturn(rc, rc);
+        if (pThis->u64RIRBBase)
+        {
+            AssertPtr(pThis->pu64RirbBuf);
+            Assert(pThis->cbRirbBuf);
+
+            rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), pThis->u64RIRBBase, pThis->pu64RirbBuf, pThis->cbRirbBuf);
+            if (RT_FAILURE(rc))
+                AssertRCReturn(rc, rc);
+        }
     }
 
@@ -791,7 +797,4 @@
 static int hdaCORBCmdProcess(PHDASTATE pThis)
 {
-    int rc = hdaCmdSync(pThis, true /* Sync from guest */);
-    AssertRCReturn(rc, rc);
-
     uint8_t corbRp = HDA_REG(pThis, CORBRP);
     uint8_t corbWp = HDA_REG(pThis, CORBWP);
@@ -800,10 +803,29 @@
     Log3Func(("CORB(RP:%x, WP:%x) RIRBWP:%x\n", corbRp, corbWp, rirbWp));
 
+    if (!(HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA))
+    {
+        LogFunc(("CORB DMA not active, skipping\n"));
+        return VINF_SUCCESS;
+    }
+
+    Assert(pThis->cbCorbBuf);
+
+    int rc = hdaCmdSync(pThis, true /* Sync from guest */);
+    AssertRCReturn(rc, rc);
+
+    uint16_t cIntCnt = HDA_REG(pThis, RINTCNT) & 0xff;
+
+    if (!cIntCnt) /* 0 means 256 interrupts. */
+        cIntCnt = HDA_MAX_RINTCNT;
+
+    Log3Func(("START CORB(RP:%x, WP:%x) RIRBWP:%x, RINTCNT:%RU8/%RU8\n",
+              corbRp, corbWp, rirbWp, pThis->u16RespIntCnt, cIntCnt));
+
     while (corbRp != corbWp)
     {
-        corbRp = (corbRp + 1) % HDA_CORB_SIZE; /* Advance +1 as the first command(s) are at CORBWP + 1. */
-
+        corbRp = (corbRp + 1) % (pThis->cbCorbBuf / HDA_CORB_ELEMENT_SIZE); /* Advance +1 as the first command(s) are at CORBWP + 1. */
+
+        uint32_t uCmd  = pThis->pu32CorbBuf[corbRp];
         uint64_t uResp = 0;
-        uint32_t uCmd = pThis->pu32CorbBuf[corbRp];
 
         rc = pThis->pCodec->pfnLookup(pThis->pCodec, HDA_CODEC_CMD(uCmd, 0 /* Codec index */), &uResp);
@@ -811,5 +833,5 @@
             LogFunc(("Codec lookup failed with rc=%Rrc\n", rc));
 
-        LogFunc(("verb:%08x -> %016lx\n", uCmd, uResp));
+        Log3Func(("Codec verb %08x -> response %016lx\n", uCmd, uResp));
 
         if (   (uResp & CODEC_RESPONSE_UNSOLICITED)
@@ -828,11 +850,38 @@
 
         pThis->u16RespIntCnt++;
-        if (pThis->u16RespIntCnt > HDA_MAX_RINTCNT) /* Make sure that the guest can't hang the host. */
-        {
-            LogRel(("HDA: Maximum response interrupt count (%d) reached, bailing out\n", HDA_MAX_RINTCNT));
-            pThis->u16RespIntCnt = HDA_MAX_RINTCNT;
-            break;
-        }
-    }
+
+        bool fSendInterrupt = false;
+
+        if (pThis->u16RespIntCnt == cIntCnt) /* Response interrupt count reached? */
+        {
+            pThis->u16RespIntCnt = 0; /* Reset internal interrupt response counter. */
+
+            Log3Func(("Response interrupt count reached (%RU16)\n", pThis->u16RespIntCnt));
+            fSendInterrupt = true;
+
+        }
+        else if (corbRp == corbWp) /* Did we reach the end of the current command buffer? */
+        {
+            Log3Func(("Command buffer empty\n"));
+            fSendInterrupt = true;
+        }
+
+        if (fSendInterrupt)
+        {
+            if (HDA_REG(pThis, RIRBCTL) & HDA_RIRBCTL_RINTCTL) /* Response Interrupt Control (RINTCTL) enabled? */
+            {
+                HDA_REG(pThis, RIRBSTS) |= HDA_RIRBSTS_RINTFL;
+
+#ifndef DEBUG
+                rc = hdaProcessInterrupt(pThis);
+#else
+                rc = hdaProcessInterrupt(pThis, __FUNCTION__);
+#endif
+            }
+        }
+    }
+
+    Log3Func(("END CORB(RP:%x, WP:%x) RIRBWP:%x, RINTCNT:%RU8/%RU8\n",
+              corbRp, corbWp, rirbWp, pThis->u16RespIntCnt, cIntCnt));
 
     HDA_REG(pThis, CORBRP) = corbRp;
@@ -841,24 +890,4 @@
     rc = hdaCmdSync(pThis, false /* Sync to guest */);
     AssertRCReturn(rc, rc);
-
-    Log3Func(("CORB(RP:%x, WP:%x) RIRBWP:%x, uRespIntCnt=%RU16\n", corbRp, corbWp, rirbWp, pThis->u16RespIntCnt));
-
-    if (pThis->u16RespIntCnt)
-    {
-        if (HDA_REG(pThis, RIRBCTL) & HDA_RIRBCTL_RINTCTL) /* Response Interrupt Control (RINTCTL) enabled? */
-        {
-            HDA_REG(pThis, RIRBSTS) |= HDA_RIRBSTS_RINTFL;
-            HDA_REG(pThis, RINTCNT)  = RT_LO_U8(pThis->u16RespIntCnt);
-
-#ifndef DEBUG
-            rc = hdaProcessInterrupt(pThis);
-#else
-            rc = hdaProcessInterrupt(pThis, __FUNCTION__);
-#endif
-            pThis->u16RespIntCnt--;
-        }
-        else /* Not enabled -- just reset our internal counter. */
-            pThis->u16RespIntCnt = 0;
-    }
 
     if (RT_FAILURE(rc))
@@ -1089,10 +1118,21 @@
 static int hdaRegWriteCORBRP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
 {
-    RT_NOREF_PV(iReg);
+    RT_NOREF(iReg);
 
     DEVHDA_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
 
     if (u32Value & HDA_CORBRP_RST)
+    {
+        /* Do a CORB reset. */
+        if (pThis->cbCorbBuf)
+        {
+            Assert(pThis->pu32CorbBuf);
+            RT_BZERO((void *)pThis->pu32CorbBuf, pThis->cbCorbBuf);
+        }
+
+        LogRel2(("HDA: CORB reset\n"));
+
         HDA_REG(pThis, CORBRP) = HDA_CORBRP_RST;    /* Clears the pointer. */
+    }
     else
         HDA_REG(pThis, CORBRP) &= ~HDA_CORBRP_RST;  /* Only CORBRP_RST bit is writable. */
@@ -1105,20 +1145,91 @@
 {
 #ifdef IN_RING3
+    DEVHDA_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
+
     int rc = hdaRegWriteU8(pThis, iReg, u32Value);
     AssertRC(rc);
 
-    DEVHDA_LOCK(pThis);
-
-    if (   (uint8_t)HDA_REG(pThis, CORBWP) != (uint8_t)HDA_REG(pThis, CORBRP)
-        && (HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA))
+    if (HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA) /* Start DMA engine. */
     {
         rc = hdaCORBCmdProcess(pThis);
     }
+    else
+        LogFunc(("CORB DMA not running, skipping\n"));
 
     DEVHDA_UNLOCK(pThis);
-
     return rc;
 #else
-    RT_NOREF_PV(pThis); RT_NOREF_PV(iReg); RT_NOREF_PV(u32Value);
+    RT_NOREF(pThis, iReg, u32Value);
+    return VINF_IOM_R3_MMIO_WRITE;
+#endif
+}
+
+static int hdaRegWriteCORBSIZE(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+#ifdef IN_RING3
+    RT_NOREF(iReg);
+
+    DEVHDA_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
+
+    if (HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA) /* Ignore request if CORB DMA engine is (still) running. */
+    {
+        LogFunc(("CORB DMA is (still) running, skipping\n"));
+
+        DEVHDA_UNLOCK(pThis);
+        return VINF_SUCCESS;
+    }
+
+    u32Value = (u32Value & HDA_CORBSIZE_SZ);
+
+    uint16_t cEntries = HDA_CORB_SIZE; /* Set default. */
+
+    switch (u32Value)
+    {
+        case 0: /* 8 byte; 2 entries. */
+            cEntries = 2;
+            break;
+
+        case 1: /* 64 byte; 16 entries. */
+            cEntries = 16;
+            break;
+
+        case 2: /* 1 KB; 256 entries. */
+            /* Use default size. */
+            break;
+
+        default:
+            LogRel(("HDA: Guest tried to set an invalid CORB size (0x%x), keeping default\n", u32Value));
+            u32Value = 2;
+            /* Use default size. */
+            break;
+    }
+
+    uint32_t cbCorbBuf = cEntries * sizeof(uint32_t);
+
+    if (cbCorbBuf != pThis->cbCorbBuf)
+    {
+        if (pThis->pu32CorbBuf)
+        {
+            RTMemFree(pThis->pu32CorbBuf);
+            pThis->pu32CorbBuf = NULL;
+        }
+
+        if (cbCorbBuf)
+        {
+            Assert(cbCorbBuf % sizeof(uint32_t) == 0);
+
+            pThis->pu32CorbBuf = (uint32_t *)RTMemAllocZ(cbCorbBuf);
+            pThis->cbCorbBuf   = cbCorbBuf;
+        }
+    }
+
+    LogFunc(("CORB buffer size is now %RU32 bytes (%u entries)\n", pThis->cbCorbBuf, pThis->cbCorbBuf / HDA_CORB_ELEMENT_SIZE));
+
+    HDA_REG(pThis, CORBSIZE) = u32Value;
+
+    DEVHDA_UNLOCK(pThis);
+    return VINF_SUCCESS;
+#else
+    RT_NOREF(pThis, iReg, u32Value);
     return VINF_IOM_R3_MMIO_WRITE;
 #endif
@@ -1135,5 +1246,4 @@
 
     DEVHDA_UNLOCK(pThis);
-
     return VINF_SUCCESS;
 }
@@ -1142,30 +1252,18 @@
 {
 #ifdef IN_RING3
+    DEVHDA_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
+
     int rc = hdaRegWriteU16(pThis, iReg, u32Value);
-    AssertRCReturn(rc, rc);
-
-    DEVHDA_LOCK(pThis);
-
-    if ((uint8_t)HDA_REG(pThis, CORBWP) == (uint8_t)HDA_REG(pThis, CORBRP))
-    {
-        DEVHDA_UNLOCK(pThis);
-        return VINF_SUCCESS;
-    }
-
-    if (!(HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA))
-    {
-        DEVHDA_UNLOCK(pThis);
-        return VINF_SUCCESS;
-    }
+    if (RT_FAILURE(rc))
+        AssertRCReturn(rc, rc);
 
     rc = hdaCORBCmdProcess(pThis);
 
     DEVHDA_UNLOCK(pThis);
-
     return rc;
-#else  /* !IN_RING3 */
-    RT_NOREF_PV(pThis); RT_NOREF_PV(iReg); RT_NOREF_PV(u32Value);
+#else
+    RT_NOREF(pThis, iReg, u32Value);
     return VINF_IOM_R3_MMIO_WRITE;
-#endif /* IN_RING3 */
+#endif
 }
 
@@ -1306,8 +1404,12 @@
 
             /* Enable/disable the stream. */
-            hdaStreamEnable(pStream, fRun /* fEnable */);
+            rc2 = hdaStreamEnable(pStream, fRun /* fEnable */);
+            AssertRC(rc2);
 
             if (fRun)
             {
+                /* Keep track of running streams. */
+                pThis->cStreamsActive++;
+
                 /* (Re-)init the stream's period. */
                 hdaStreamPeriodInit(&pStream->State.Period,
@@ -1316,8 +1418,16 @@
                 /* Begin a new period for this stream. */
                 rc2 = hdaStreamPeriodBegin(&pStream->State.Period, hdaWalClkGetCurrent(pThis)/* Use current wall clock time */);
+                AssertRC(rc2);
+
+                rc2 = hdaTimerSet(pThis, TMTimerGet(pThis->pTimer) + pStream->State.cTransferTicks, false /* fForce */);
                 AssertRC(rc2);
             }
             else
             {
+                /* Keep track of running streams. */
+                Assert(pThis->cStreamsActive);
+                if (pThis->cStreamsActive)
+                    pThis->cStreamsActive--;
+
                 /* Make sure to (re-)schedule outstanding (delayed) interrupts. */
                 hdaReschedulePendingInterrupts(pThis);
@@ -1332,12 +1442,4 @@
             /* Make sure to leave the lock before (eventually) starting the timer. */
             hdaStreamUnlock(pStream);
-
-# ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
-            /* See if we need to start or stop the timer. */
-            if (!fRun)
-                hdaTimerMaybeStop(pThis);
-            else
-                hdaTimerMaybeStart(pThis);
-# endif
         }
     }
@@ -1358,5 +1460,5 @@
 {
 #ifdef IN_RING3
-    DEVHDA_LOCK(pThis);
+    DEVHDA_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
 
     PHDASTREAM pStream = hdaGetStreamFromSD(pThis, HDA_SD_NUM_FROM_REG(pThis, STS, iReg));
@@ -1366,5 +1468,5 @@
                          HDA_SD_NUM_FROM_REG(pThis, STS, iReg), u32Value));
 
-        DEVHDA_UNLOCK(pThis);
+        DEVHDA_UNLOCK_BOTH(pThis);
         return hdaRegWriteU16(pThis, iReg, u32Value);
     }
@@ -1391,4 +1493,10 @@
         if (hdaStreamPeriodIsComplete(pPeriod))
         {
+            /* Make sure to try to update the WALCLK register if a period is complete.
+             * Use the maximum WALCLK value all (active) streams agree to. */
+            const uint64_t uWalClkMax = hdaWalClkGetMax(pThis);
+            if (uWalClkMax > hdaWalClkGetCurrent(pThis))
+                hdaWalClkSet(pThis, uWalClkMax, false /* fForce */);
+
             hdaStreamPeriodEnd(pPeriod);
 
@@ -1398,16 +1506,51 @@
 
         hdaStreamPeriodUnlock(pPeriod); /* Unlock before processing interrupt. */
-
-        if (fNeedsInterrupt)
-        {
+    }
+
 #ifndef DEBUG
-            hdaProcessInterrupt(pThis);
+    hdaProcessInterrupt(pThis);
 #else
-            hdaProcessInterrupt(pThis, __FUNCTION__);
-#endif
-        }
-    }
-
-    DEVHDA_UNLOCK(pThis);
+    hdaProcessInterrupt(pThis, __FUNCTION__);
+#endif
+
+    const uint64_t tsNow = TMTimerGet(pThis->pTimer);
+    Assert(tsNow >= pStream->State.tsTransferLast);
+
+    const uint64_t cTicksElapsed     = tsNow - pStream->State.tsTransferLast;
+    const uint64_t cTicksTransferred = pStream->State.cbTransferProcessed * pStream->State.cTicksPerByte;
+
+    uint64_t cTicksToNext = pStream->State.cTransferTicks;
+
+    Log3Func(("[SD%RU8] cTicksElapsed=%RU64, cTicksTransferred=%RU64, cTicksToNext=%RU64\n",
+              pStream->u8SD, cTicksElapsed, cTicksTransferred, cTicksToNext));
+
+    Log3Func(("[SD%RU8] cbTransferProcessed=%RU32, cbTransferChunk=%RU32, cbTransferSize=%RU32\n",
+              pStream->u8SD, pStream->State.cbTransferProcessed, pStream->State.cbTransferChunk, pStream->State.cbTransferSize));
+
+    if (cTicksElapsed <= cTicksToNext)
+    {
+        cTicksToNext = cTicksToNext - cTicksElapsed;
+    }
+    else /* Catch up. */
+    {
+        Log3Func(("[SD%RU8] Warning: Lagging behind (%RU64 ticks elapsed, maximum allowed is %RU64)\n",
+                 pStream->u8SD, cTicksElapsed, cTicksToNext));
+
+        LogRelMax2(64, ("HDA: Stream #%RU8 interrupt lagging behind (expected %uus, got %uus), trying to catch up ...\n",
+                        pStream->u8SD,
+                        (TMTimerGetFreq(pThis->pTimer) / pThis->u16TimerHz) / 1000, (tsNow - pStream->State.tsTransferLast) / 1000));
+
+        cTicksToNext = 0;
+    }
+
+    Log3Func(("[SD%RU8] -> cTicksToNext=%RU64\n", pStream->u8SD, cTicksToNext));
+
+    /* Reset processed data counter. */
+    pStream->State.cbTransferProcessed = 0;
+
+    /* Re-arm the timer. */
+    hdaTimerSet(pThis, tsNow + cTicksToNext, false /* fForce */);
+
+    DEVHDA_UNLOCK_BOTH(pThis);
     return VINF_SUCCESS;
 #else /* IN_RING3 */
@@ -1998,10 +2141,29 @@
 static int hdaRegWriteRIRBWP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
 {
-    RT_NOREF_PV(iReg);
+    RT_NOREF(iReg);
 
     DEVHDA_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
 
+    if (HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA) /* Ignore request if CORB DMA engine is (still) running. */
+    {
+        LogFunc(("CORB DMA (still) running, skipping\n"));
+
+        DEVHDA_UNLOCK(pThis);
+        return VINF_SUCCESS;
+    }
+
     if (u32Value & HDA_RIRBWP_RST)
+    {
+        /* Do a RIRB reset. */
+        if (pThis->cbRirbBuf)
+        {
+            Assert(pThis->pu64RirbBuf);
+            RT_BZERO((void *)pThis->pu64RirbBuf, pThis->cbRirbBuf);
+        }
+
+        LogRel2(("HDA: RIRB reset\n"));
+
         HDA_REG(pThis, RIRBWP) = 0;
+    }
 
     DEVHDA_UNLOCK(pThis);
@@ -2009,4 +2171,27 @@
     /* The remaining bits are O, see 6.2.22. */
     return VINF_SUCCESS;
+}
+
+static int hdaRegWriteRINTCNT(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    DEVHDA_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
+
+    if (HDA_REG(pThis, CORBCTL) & HDA_CORBCTL_DMA) /* Ignore request if CORB DMA engine is (still) running. */
+    {
+        LogFunc(("CORB DMA is (still) running, skipping\n"));
+
+        DEVHDA_UNLOCK(pThis);
+        return VINF_SUCCESS;
+    }
+
+    RT_NOREF(iReg);
+
+    int rc = hdaRegWriteU16(pThis, iReg, u32Value);
+    AssertRC(rc);
+
+    LogFunc(("Response interrupt count is now %RU8\n", HDA_REG(pThis, RINTCNT) & 0xFF));
+
+    DEVHDA_UNLOCK(pThis);
+    return rc;
 }
 
@@ -2515,114 +2700,4 @@
 }
 
-#ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
-/**
- * Starts the internal audio device timer.
- *
- * @return  IPRT status code.
- * @param   pThis               HDA state.
- */
-static int hdaTimerStart(PHDASTATE pThis)
-{
-    LogFlowFuncEnter();
-
-    DEVHDA_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
-
-    AssertPtr(pThis->pTimer);
-
-    if (!pThis->fTimerActive)
-    {
-        LogRel2(("HDA: Starting transfers\n"));
-
-        pThis->fTimerActive  = true;
-        pThis->tsTimerExpire = TMTimerGet(pThis->pTimer) + pThis->cTimerTicks; /* Update current time timestamp. */
-
-        /* Start transfers. */
-        hdaTimerMain(pThis);
-    }
-
-    DEVHDA_UNLOCK_BOTH(pThis);
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Starts the internal audio device timer (if not started yet).
- *
- * @return  IPRT status code.
- * @param   pThis               HDA state.
- */
-static int hdaTimerMaybeStart(PHDASTATE pThis)
-{
-    LogFlowFuncEnter();
-
-    if (!pThis->pTimer)
-        return VERR_WRONG_ORDER;
-
-    pThis->cStreamsActive++;
-
-    /* Only start the timer at the first active stream. */
-    if (pThis->cStreamsActive == 1)
-        return hdaTimerStart(pThis);
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Stops the internal audio device timer.
- *
- * @return  IPRT status code.
- * @param   pThis               HDA state.
- */
-static int hdaTimerStop(PHDASTATE pThis)
-{
-    LogFlowFuncEnter();
-
-    if (!pThis->pTimer) /* Only can happen on device construction time, so no locking needed here. */
-        return VINF_SUCCESS;
-
-    DEVHDA_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
-
-    if (pThis->fTimerActive)
-    {
-        LogRel2(("HDA: Stopping transfers ...\n"));
-
-        pThis->fTimerActive = false;
-
-        /* Note: Do not stop the timer via TMTimerStop() here, as there still might
-         *       be queued audio data which needs to be handled (e.g. played back) first
-         *       before actually stopping the timer for good. */
-    }
-
-    DEVHDA_UNLOCK_BOTH(pThis);
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Decreases the active HDA streams count by one and
- * then checks if the internal audio device timer can be
- * stopped.
- *
- * @return  IPRT status code.
- * @param   pThis               HDA state.
- */
-static int hdaTimerMaybeStop(PHDASTATE pThis)
-{
-    LogFlowFuncEnter();
-
-    if (!pThis->pTimer)
-        return VERR_WRONG_ORDER;
-
-    if (pThis->cStreamsActive) /* Disable can be called mupltiple times. */
-    {
-        pThis->cStreamsActive--;
-
-        if (pThis->cStreamsActive == 0)
-            return hdaTimerStop(pThis);
-    }
-
-    return VINF_SUCCESS;
-}
-
 /**
  * Main routine for the device timer.
@@ -2638,9 +2713,10 @@
     DEVHDA_LOCK_BOTH_RETURN_VOID(pThis);
 
+    /* Do all transfers from/to DMA. */
+    hdaDoTransfers(pThis);
+
     /* Flag indicating whether to kick the timer again for a
      * new data processing round. */
-    bool fKickTimer = false;
-
-    hdaDoTransfers(pThis);
+    bool fSinksActive = false;
 
     /* Do we need to kick the timer again? */
@@ -2656,17 +2732,24 @@
         )
     {
-        fKickTimer = true;
-    }
-
-    if (   ASMAtomicReadBool(&pThis->fTimerActive)
-        || fKickTimer)
-    {
-        /* Kick the timer again. */
-        pThis->tsTimerExpire += pThis->cTimerTicks;
-
-        TMTimerSet(pThis->pTimer, pThis->tsTimerExpire);
-    }
-    else
-        LogRel2(("HDA: Stopped transfers\n"));
+        fSinksActive = true;
+    }
+
+    bool fTimerScheduled = false;
+    if (   hdaStreamTransferIsScheduled(hdaGetStreamFromSink(pThis, &pThis->SinkFront))
+#ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
+        || hdaStreamTransferIsScheduled(hdaGetStreamFromSink(pThis, &pThis->SinkMicIn))
+#endif
+        || hdaStreamTransferIsScheduled(hdaGetStreamFromSink(pThis, &pThis->SinkLineIn)))
+    {
+        fTimerScheduled = true;
+    }
+
+    Log3Func(("fSinksActive=%RTbool, fTimerScheduled=%RTbool\n", fSinksActive, fTimerScheduled));
+
+    if (    fSinksActive
+        && !fTimerScheduled)
+    {
+        hdaTimerSet(pThis, TMTimerGet(pThis->pTimer) + TMTimerGetFreq(pThis->pTimer) / pThis->u16TimerHz, true /* fForce */);
+    }
 
     DEVHDA_UNLOCK_BOTH(pThis);
@@ -2727,9 +2810,9 @@
             uint64_t cbWrittenHz = ASMAtomicReadU64(&pStreamDbg->cbWrittenHz);
 
-            if (tsElapsedMs >= (1000 / HDA_TIMER_HZ))
+            if (tsElapsedMs >= (1000 / HDA_TIMER_HZ_DEFAULT))
             {
                 LogFunc(("[SD%RU8] %RU32ms elapsed, cbWritten=%RU64, cWritten=%RU64 -- %RU32 bytes on average per time slot (%zums)\n",
                          pStream->u8SD, tsElapsedMs, cbWrittenHz, cWritesHz,
-                         ASMDivU64ByU32RetU32(cbWrittenHz, cWritesHz ? cWritesHz : 1), 1000 / HDA_TIMER_HZ));
+                         ASMDivU64ByU32RetU32(cbWrittenHz, cWritesHz ? cWritesHz : 1), 1000 / HDA_TIMER_HZ_DEFAULT));
 
                 pStreamDbg->tsWriteSlotBegin = tsNowNs;
@@ -2817,31 +2900,19 @@
     LogFlowFuncEnter();
 
-# ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
-    /*
-     * Stop the timer, if any.
-     */
-    hdaTimerStop(pThis);
-
     pThis->cStreamsActive = 0;
-# endif
-
-    memset(pThis->au32Regs, 0, sizeof(pThis->au32Regs));
-    /* See 6.2.1. */
-    HDA_REG(pThis, GCAP)     = HDA_MAKE_GCAP(HDA_MAX_SDO /* Ouput streams */,
-                                             HDA_MAX_SDI /* Input streams */,
-                                             0           /* Bidirectional output streams */,
-                                             0           /* Serial data out signals */,
-                                             1           /* 64-bit */);
-    HDA_REG(pThis, VMIN)     = 0x00;                     /* see 6.2.2 */
-    HDA_REG(pThis, VMAJ)     = 0x01;                     /* see 6.2.3 */
-    /* Announce the full 60 words output payload. */
-    HDA_REG(pThis, OUTPAY)   = 0x003C;                   /* see 6.2.4 */
-    /* Announce the full 29 words input payload. */
-    HDA_REG(pThis, INPAY)    = 0x001D;                   /* see 6.2.5 */
-    HDA_REG(pThis, CORBSIZE) = 0x42;                     /* see 6.2.1 */
-    HDA_REG(pThis, RIRBSIZE) = 0x42;                     /* see 6.2.1 */
+
+    HDA_REG(pThis, GCAP)     = HDA_MAKE_GCAP(HDA_MAX_SDO, HDA_MAX_SDI, 0, 0, 1); /* see 6.2.1 */
+    HDA_REG(pThis, VMIN)     = 0x00;                                             /* see 6.2.2 */
+    HDA_REG(pThis, VMAJ)     = 0x01;                                             /* see 6.2.3 */
+    HDA_REG(pThis, OUTPAY)   = 0x003C;                                           /* see 6.2.4 */
+    HDA_REG(pThis, INPAY)    = 0x001D;                                           /* see 6.2.5 */
+    HDA_REG(pThis, CORBSIZE) = 0x42; /* Up to 256 CORB entries                      see 6.2.1 */
+    HDA_REG(pThis, RIRBSIZE) = 0x42; /* Up to 256 RIRB entries                      see 6.2.1 */
     HDA_REG(pThis, CORBRP)   = 0x0;
+    HDA_REG(pThis, CORBWP)   = 0x0;
     HDA_REG(pThis, RIRBWP)   = 0x0;
-    HDA_REG(pThis, RINTCNT)  = 0x0;
+    /* Some guests (like Haiku) don't set RINTCNT explicitly but expect an interrupt after each
+     * RIRB response -- so initialize RINTCNT to 1 by default. */
+    HDA_REG(pThis, RINTCNT)  = 0x1;
 
     /*
@@ -2872,5 +2943,5 @@
     }
 
-    /*
+       /*
      * Set some sensible defaults for which HDA sinks
      * are connected to which stream number.
@@ -2923,5 +2994,4 @@
 }
 
-
 /**
  * Timer callback which handles the audio data transfers on a periodic basis.
@@ -2942,54 +3012,4 @@
 }
 
-#else /* VBOX_WITH_AUDIO_HDA_CALLBACKS */
-
-static DECLCALLBACK(int) hdaCallbackInput(PDMAUDIOBACKENDCBTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser)
-{
-    Assert(enmType == PDMAUDIOCALLBACKTYPE_INPUT);
-    AssertPtrReturn(pvCtx,  VERR_INVALID_POINTER);
-    AssertReturn(cbCtx,     VERR_INVALID_PARAMETER);
-    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
-    AssertReturn(cbUser,    VERR_INVALID_PARAMETER);
-
-    PHDACALLBACKCTX pCtx = (PHDACALLBACKCTX)pvCtx;
-    AssertReturn(cbCtx == sizeof(HDACALLBACKCTX), VERR_INVALID_PARAMETER);
-
-    PPDMAUDIOCBDATA_DATA_INPUT pData = (PPDMAUDIOCBDATA_DATA_INPUT)pvUser;
-    AssertReturn(cbUser == sizeof(PDMAUDIOCBDATA_DATA_INPUT), VERR_INVALID_PARAMETER);
-
-    return hdaStreamDoDMA(pCtx->pThis, PI_INDEX, UINT32_MAX, &pData->cbOutRead);
-}
-
-static DECLCALLBACK(int) hdaCallbackOutput(PDMAUDIOBACKENDCBTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser)
-{
-    Assert(enmType == PDMAUDIOCALLBACKTYPE_OUTPUT);
-    AssertPtrReturn(pvCtx,  VERR_INVALID_POINTER);
-    AssertReturn(cbCtx,     VERR_INVALID_PARAMETER);
-    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
-    AssertReturn(cbUser,    VERR_INVALID_PARAMETER);
-
-    PHDACALLBACKCTX pCtx = (PHDACALLBACKCTX)pvCtx;
-    AssertReturn(cbCtx == sizeof(HDACALLBACKCTX), VERR_INVALID_PARAMETER);
-
-    PPDMAUDIOCBDATA_DATA_OUTPUT pData = (PPDMAUDIOCBDATA_DATA_OUTPUT)pvUser;
-    AssertReturn(cbUser == sizeof(PDMAUDIOCBDATA_DATA_OUTPUT), VERR_INVALID_PARAMETER);
-
-    PHDASTATE pThis = pCtx->pThis;
-
-    int rc = hdaStreamDoDMA(pCtx->pThis, PO_INDEX, UINT32_MAX, &pData->cbOutWritten);
-    if (   RT_SUCCESS(rc)
-        && pData->cbOutWritten)
-    {
-        PHDADRIVER pDrv;
-        RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
-        {
-            uint32_t cFramesPlayed;
-            int rc2 = pDrv->pConnector->pfnPlay(pDrv->pConnector, &cFramesPlayed);
-            LogFlowFunc(("LUN#%RU8: cFramesPlayed=%RU32, rc=%Rrc\n", pDrv->uLUN, cFramesPlayed, rc2));
-        }
-    }
-}
-#endif /* VBOX_WITH_AUDIO_HDA_CALLBACKS */
-
 /**
  * Main routine to perform the actual audio data transfers from the HDA streams
@@ -3005,7 +3025,4 @@
 #endif
     PHDASTREAM pStreamFront   = hdaGetStreamFromSink(pThis, &pThis->SinkFront);
-#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
-    /** @todo See note below. */
-#endif
 
     hdaStreamUpdate(pStreamFront,  true /* fInTimer */);
@@ -3534,5 +3551,5 @@
     int rc = VINF_SUCCESS;
 
-    bool fStartTimer = false; /* Whether to resume the device timer. */
+    uint64_t tsExpire = 0; /* Timestamp of new timer expiration time / Whether to resume the device timer. */
 
     /*
@@ -3557,8 +3574,4 @@
                 hdaStreamAsyncIOEnable(pStream, true /* fEnable */);
 #endif
-                /* (Re-)initialize the stream with current values. */
-                rc2 = hdaStreamInit(pStream, pStream->u8SD);
-                AssertRC(rc2);
-
                 /* Resume the stream's period. */
                 hdaStreamPeriodResume(&pStream->State.Period);
@@ -3576,14 +3589,21 @@
                 hdaStreamRegisterDMAHandlers(pThis, pStream);
 #endif
-                fStartTimer = true;
+                /* Determine the earliest timing slot we need to use. */
+                if (tsExpire)
+                    tsExpire = RT_MIN(tsExpire, hdaStreamTransferGetNext(pStream));
+                else
+                    tsExpire = hdaStreamTransferGetNext(pStream);
+
+                Log2Func(("[SD%RU8] tsExpire=%RU64\n", pStream->u8SD, tsExpire));
             }
         }
     }
 
-#ifndef VBOX_WITH_AUDIO_CALLBACKS
     /* Start the timer if one of the above streams were active during taking the saved state. */
-    if (fStartTimer)
-        hdaTimerMaybeStart(pThis);
-#endif
+    if (tsExpire)
+    {
+        LogFunc(("Resuming timer at %RU64\n", tsExpire));
+        hdaTimerSet(pThis, tsExpire, true /* fForce */);
+    }
 
     LogFlowFuncLeaveRC(rc);
@@ -3973,7 +3993,4 @@
         }
 
-        /*
-         * Load BDLEs (Buffer Descriptor List Entries) and DMA counters.
-         */
         rc = SSMR3GetStructEx(pSSM, &pStrm->State, sizeof(HDASTREAMSTATE),
                               0 /* fFlags */, g_aSSMStreamStateFields7,
@@ -3981,4 +3998,7 @@
         AssertRC(rc);
 
+        /*
+         * Load BDLEs (Buffer Descriptor List Entries) and DMA counters.
+         */
         rc = SSMR3GetStructEx(pSSM, &pStrm->State.BDLE.Desc, sizeof(HDABDLEDESC),
                               0 /* fFlags */, g_aSSMBDLEDescFields7, NULL);
@@ -4009,5 +4029,4 @@
          * Load internal (FIFO) buffer.
          */
-
         uint32_t cbCircBufSize = 0;
         rc = SSMR3GetU32(pSSM, &cbCircBufSize); /* cbCircBuf */
@@ -4731,7 +4750,11 @@
     if (!CFGMR3AreValuesValid(pCfg, "R0Enabled\0"
                                     "RCEnabled\0"
-                                    "TimerHz\0"))
+                                    "TimerHz\0"
+                                    "PosAdjustEnabled\0"
+                                    "PosAdjustFrames\0"))
+    {
         return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
                                 N_ ("Invalid configuration for the Intel HDA device"));
+    }
 
     int rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &pThis->fRCEnabled, false);
@@ -4743,11 +4766,28 @@
         return PDMDEV_SET_ERROR(pDevIns, rc,
                                 N_("HDA configuration error: failed to read R0Enabled as boolean"));
-#ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
-    uint16_t uTimerHz;
-    rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, HDA_TIMER_HZ /* Default value, if not set. */);
+
+    rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &pThis->u16TimerHz, HDA_TIMER_HZ_DEFAULT /* Default value, if not set. */);
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc,
                                 N_("HDA configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
-#endif
+
+     if (pThis->u16TimerHz != HDA_TIMER_HZ_DEFAULT)
+         LogRel(("HDA: Using custom device timer rate (%RU16Hz)\n", pThis->u16TimerHz));
+
+     rc = CFGMR3QueryBoolDef(pCfg, "PosAdjustEnabled", &pThis->fPosAdjustEnabled, true);
+     if (RT_FAILURE(rc))
+         return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("HDA configuration error: failed to read position adjustment enabled as boolean"));
+
+     if (!pThis->fPosAdjustEnabled)
+         LogRel(("HDA: Position adjustment is disabled\n"));
+
+     rc = CFGMR3QueryU16Def(pCfg, "PosAdjustFrames", &pThis->cPosAdjustFrames, HDA_POS_ADJUST_DEFAULT);
+     if (RT_FAILURE(rc))
+         return PDMDEV_SET_ERROR(pDevIns, rc,
+                                 N_("HDA configuration error: failed to read position adjustment frames as unsigned integer"));
+
+     if (pThis->cPosAdjustFrames)
+         LogRel(("HDA: Using custom position adjustment (%RU16 audio frames)\n", pThis->cPosAdjustFrames));
 
     /*
@@ -5174,5 +5214,4 @@
     }
 
-# ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
     if (RT_SUCCESS(rc))
     {
@@ -5191,39 +5230,5 @@
         rc = TMR3TimerSetCritSect(pThis->pTimer, &pThis->CritSect);
         AssertRCReturn(rc, rc);
-
-        pThis->cTimerTicks = TMTimerGetFreq(pThis->pTimer) / uTimerHz;
-        LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTimerTicks, uTimerHz));
-    }
-# else
-    if (RT_SUCCESS(rc))
-    {
-        PHDADRIVER pDrv;
-        RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
-        {
-            /* Only register primary driver.
-             * The device emulation does the output multiplexing then. */
-            if (pDrv->fFlags != PDMAUDIODRVFLAGS_PRIMARY)
-                continue;
-
-            PDMAUDIOCBRECORD AudioCallbacks[2];
-
-            HDACALLBACKCTX Ctx = { pThis, pDrv };
-
-            AudioCallbacks[0].enmType     = PDMAUDIOCALLBACKTYPE_INPUT;
-            AudioCallbacks[0].pfnCallback = hdaCallbackInput;
-            AudioCallbacks[0].pvCtx       = &Ctx;
-            AudioCallbacks[0].cbCtx       = sizeof(HDACALLBACKCTX);
-
-            AudioCallbacks[1].enmType     = PDMAUDIOCALLBACKTYPE_OUTPUT;
-            AudioCallbacks[1].pfnCallback = hdaCallbackOutput;
-            AudioCallbacks[1].pvCtx       = &Ctx;
-            AudioCallbacks[1].cbCtx       = sizeof(HDACALLBACKCTX);
-
-            rc = pDrv->pConnector->pfnRegisterCallbacks(pDrv->pConnector, AudioCallbacks, RT_ELEMENTS(AudioCallbacks));
-            if (RT_FAILURE(rc))
-                break;
-        }
-    }
-# endif
+    }
 
 # ifdef VBOX_WITH_STATISTICS
@@ -5233,7 +5238,5 @@
          * Register statistics.
          */
-#  ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
         PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer,            STAMTYPE_PROFILE, "/Devices/HDA/Timer",             STAMUNIT_TICKS_PER_CALL, "Profiling hdaTimer.");
-#  endif
         PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIn,               STAMTYPE_PROFILE, "/Devices/HDA/Input",             STAMUNIT_TICKS_PER_CALL, "Profiling input.");
         PDMDevHlpSTAMRegister(pDevIns, &pThis->StatOut,              STAMTYPE_PROFILE, "/Devices/HDA/Output",            STAMUNIT_TICKS_PER_CALL, "Profiling output.");
Index: /trunk/src/VBox/Devices/Audio/DevHDA.h
===================================================================
--- /trunk/src/VBox/Devices/Audio/DevHDA.h	(revision 69918)
+++ /trunk/src/VBox/Devices/Audio/DevHDA.h	(revision 69919)
@@ -154,19 +154,8 @@
     /** Number of active (running) SDn streams. */
     uint8_t                            cStreamsActive;
-#ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
     /** The timer for pumping data thru the attached LUN drivers. */
     PTMTIMERR3                         pTimer;
-    /** Flag indicating whether the timer is active or not. */
-    bool                               fTimerActive;
-    uint8_t                            u8Padding1[7];
-    /** Timer ticks per Hz. */
-    uint64_t                           cTimerTicks;
-    /** The current timer expire time (in timer ticks). */
-    uint64_t                           tsTimerExpire;
-#endif
 #ifdef VBOX_WITH_STATISTICS
-# ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS
     STAMPROFILE                        StatTimer;
-# endif
     STAMPROFILE                        StatIn;
     STAMPROFILE                        StatOut;
@@ -205,21 +194,28 @@
     /** Response Interrupt Count (RINTCNT). */
     uint16_t                           u16RespIntCnt;
+    /** Position adjustment (in audio frames).
+     *
+     *  This is not an official feature of the HDA specs, but used by
+     *  certain OS drivers (e.g. snd_hda_intel) to work around certain
+     *  quirks by "real" HDA hardware implementations.
+     *
+     *  The position adjustment specifies how many audio frames
+     *  a stream is ahead from its actual reading/writing position when
+     *  starting a stream.
+     */
+    uint16_t                           cPosAdjustFrames;
+    /** Whether the position adjustment is enabled or not. */
+    bool                               fPosAdjustEnabled;
+    uint8_t                            Padding1[3];
     /** Current IRQ level. */
     uint8_t                            u8IRQL;
+    /** The device timer Hz rate. Defaults to HDA_TIMER_HZ_DEFAULT. */
+    uint16_t                           u16TimerHz;
     /** Padding for alignment. */
-    uint8_t                            au8Padding3[5];
+    uint8_t                            au8Padding3[3];
 #ifdef DEBUG
     HDASTATEDBGINFO                    Dbg;
 #endif
 } HDASTATE, *PHDASTATE;
-
-#ifdef VBOX_WITH_AUDIO_HDA_CALLBACKS
-typedef struct HDACALLBACKCTX
-{
-    PHDASTATE  pThis;
-    PHDADRIVER pDriver;
-} HDACALLBACKCTX, *PHDACALLBACKCTX;
-#endif
-
 #endif /* !DEV_HDA_H */
 
Index: /trunk/src/VBox/Devices/Audio/DevHDACommon.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DevHDACommon.cpp	(revision 69918)
+++ /trunk/src/VBox/Devices/Audio/DevHDACommon.cpp	(revision 69919)
@@ -53,87 +53,31 @@
 #endif
 {
-    HDA_REG(pThis, INTSTS) = hdaGetINTSTS(pThis);
-
-    Log3Func(("IRQL=%RU8\n", pThis->u8IRQL));
+    uint32_t uIntSts = hdaGetINTSTS(pThis);
+
+    HDA_REG(pThis, INTSTS) = uIntSts;
 
     /* NB: It is possible to have GIS set even when CIE/SIEn are all zero; the GIS bit does
      * not control the interrupt signal. See Figure 4 on page 54 of the HDA 1.0a spec.
      */
-
-    /* If global interrupt enable (GIE) is set, check if any enabled interrupts are set. */
+    /* Global Interrupt Enable (GIE) set? */
     if (   (HDA_REG(pThis, INTCTL) & HDA_INTCTL_GIE)
         && (HDA_REG(pThis, INTSTS) & HDA_REG(pThis, INTCTL) & (HDA_INTCTL_CIE | HDA_STRMINT_MASK)))
     {
-        if (!pThis->u8IRQL)
-        {
+        Log3Func(("Asserted (%s)\n", pszSource));
+
+        PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 1 /* Assert */);
+        pThis->u8IRQL = 1;
+
 #ifdef DEBUG
-            if (!pThis->Dbg.IRQ.tsProcessedLastNs)
-                pThis->Dbg.IRQ.tsProcessedLastNs = RTTimeNanoTS();
-
-            const uint64_t tsLastElapsedNs = RTTimeNanoTS() - pThis->Dbg.IRQ.tsProcessedLastNs;
-
-            if (!pThis->Dbg.IRQ.tsAssertedNs)
-                pThis->Dbg.IRQ.tsAssertedNs = RTTimeNanoTS();
-
-            const uint64_t tsAssertedElapsedNs = RTTimeNanoTS() - pThis->Dbg.IRQ.tsAssertedNs;
-
-            pThis->Dbg.IRQ.cAsserted++;
-            pThis->Dbg.IRQ.tsAssertedTotalNs += tsAssertedElapsedNs;
-
-            const uint64_t avgAssertedUs = (pThis->Dbg.IRQ.tsAssertedTotalNs / pThis->Dbg.IRQ.cAsserted) / 1000;
-
-            if (avgAssertedUs > (1000 / HDA_TIMER_HZ) /* ms */ * 1000) /* Exceeds time slot? */
-                Log3Func(("Asserted (%s): %zuus elapsed (%zuus on average) -- %zuus alternation delay\n",
-                          pszSource, tsAssertedElapsedNs / 1000,
-                          avgAssertedUs,
-                          (pThis->Dbg.IRQ.tsDeassertedNs - pThis->Dbg.IRQ.tsAssertedNs) / 1000));
-#endif
-            Log3Func(("Asserted (%s): %RU64us between alternation (WALCLK=%RU64)\n",
-                      pszSource, tsLastElapsedNs / 1000, pThis->u64WalClk));
-
-            PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 1 /* Assert */);
-            pThis->u8IRQL = 1;
-
-#ifdef DEBUG
-            pThis->Dbg.IRQ.tsAssertedNs = RTTimeNanoTS();
-            pThis->Dbg.IRQ.tsProcessedLastNs = pThis->Dbg.IRQ.tsAssertedNs;
-#endif
-        }
+        pThis->Dbg.IRQ.tsAssertedNs = RTTimeNanoTS();
+        pThis->Dbg.IRQ.tsProcessedLastNs = pThis->Dbg.IRQ.tsAssertedNs;
+#endif
     }
     else
     {
-        if (pThis->u8IRQL)
-        {
-#ifdef DEBUG
-            if (!pThis->Dbg.IRQ.tsProcessedLastNs)
-                pThis->Dbg.IRQ.tsProcessedLastNs = RTTimeNanoTS();
-
-            const uint64_t tsLastElapsedNs = RTTimeNanoTS() - pThis->Dbg.IRQ.tsProcessedLastNs;
-
-            if (!pThis->Dbg.IRQ.tsDeassertedNs)
-                pThis->Dbg.IRQ.tsDeassertedNs = RTTimeNanoTS();
-
-            const uint64_t tsDeassertedElapsedNs = RTTimeNanoTS() - pThis->Dbg.IRQ.tsDeassertedNs;
-
-            pThis->Dbg.IRQ.cDeasserted++;
-            pThis->Dbg.IRQ.tsDeassertedTotalNs += tsDeassertedElapsedNs;
-
-            const uint64_t avgDeassertedUs = (pThis->Dbg.IRQ.tsDeassertedTotalNs / pThis->Dbg.IRQ.cDeasserted) / 1000;
-
-            if (avgDeassertedUs > (1000 / HDA_TIMER_HZ) /* ms */ * 1000) /* Exceeds time slot? */
-                Log3Func(("Deasserted (%s): %zuus elapsed (%zuus on average)\n",
-                          pszSource, tsDeassertedElapsedNs / 1000, avgDeassertedUs));
-
-            Log3Func(("Deasserted (%s): %RU64us between alternation (WALCLK=%RU64)\n",
-                      pszSource, tsLastElapsedNs / 1000, pThis->u64WalClk));
-#endif
-            PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 0 /* Deassert */);
-            pThis->u8IRQL = 0;
-
-#ifdef DEBUG
-            pThis->Dbg.IRQ.tsDeassertedNs    = RTTimeNanoTS();
-            pThis->Dbg.IRQ.tsProcessedLastNs = pThis->Dbg.IRQ.tsDeassertedNs;
-#endif
-        }
+        Log3Func(("Deasserted (%s)\n", pszSource));
+
+        PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 0 /* Deassert */);
+        pThis->u8IRQL = 0;
     }
 
@@ -184,7 +128,6 @@
 
 #ifdef VBOX_STRICT
-    const uint64_t u64WalClkCur = ASMAtomicReadU64(&pThis->u64WalClk);
-#endif
-          uint64_t u64WalClkSet = u64WalClk;
+    const uint64_t u64WalClkCur       = ASMAtomicReadU64(&pThis->u64WalClk);
+#endif
 
     /* Only drive the WALCLK register forward if all (active) stream periods have passed
@@ -205,22 +148,22 @@
             /* Get the maximum value of all periods we need to handle.
              * Not the most elegant solution, but works for now ... */
-            u64WalClk = RT_MAX(u64WalClkSet, u64FrontAbsWalClk);
+            u64WalClk = RT_MAX(u64WalClk, u64FrontAbsWalClk);
 #ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
 # error "Implement me!"
 #endif
-            u64WalClk = RT_MAX(u64WalClkSet, u64LineInAbsWalClk);
+            u64WalClk = RT_MAX(u64WalClk, u64LineInAbsWalClk);
 #ifdef VBOX_WITH_HDA_MIC_IN
-            u64WalClk = RT_MAX(u64WalClkSet, u64MicInAbsWalClk);
+            u64WalClk = RT_MAX(u64WalClk, u64MicInAbsWalClk);
 #endif
 
 #ifdef VBOX_STRICT
-            AssertMsg(u64WalClkSet >= u64WalClkCur,
+            AssertMsg(u64WalClk >= u64WalClkCur,
                       ("Setting WALCLK to a value going backwards does not make any sense (old %RU64 vs. new %RU64)\n",
-                       u64WalClkCur, u64WalClkSet));
-            if (u64WalClkSet == u64WalClkCur)     /* Setting a stale value? */
+                       u64WalClkCur, u64WalClk));
+            if (u64WalClk == u64WalClkCur)     /* Setting a stale value? */
             {
                 if (pThis->u8WalClkStaleCnt++ > 3)
                     AssertMsgFailed(("Setting WALCLK to a stale value (%RU64) too often isn't a good idea really. "
-                                     "Good luck with stuck audio stuff.\n", u64WalClkSet));
+                                     "Good luck with stuck audio stuff.\n", u64WalClk));
             }
             else
@@ -230,5 +173,5 @@
 
         /* Set the new WALCLK value. */
-        ASMAtomicWriteU64(&pThis->u64WalClk, u64WalClkSet);
+        ASMAtomicWriteU64(&pThis->u64WalClk, u64WalClk);
     }
 
@@ -298,13 +241,14 @@
 
 /**
- * Reads DMA data from a given HDA output stream into its associated FIFO buffer.
+ * Reads DMA data from a given HDA output stream.
  *
  * @return  IPRT status code.
  * @param   pThis               HDA state.
  * @param   pStream             HDA output stream to read DMA data from.
- * @param   cbToRead            How much (in bytes) to read from DMA.
+ * @param   pvBuf               Where to store the read data.
+ * @param   cbBuf               How much to read in bytes.
  * @param   pcbRead             Returns read bytes from DMA. Optional.
  */
-int hdaDMARead(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToRead, uint32_t *pcbRead)
+int hdaDMARead(PHDASTATE pThis, PHDASTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
 {
     AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
@@ -312,11 +256,10 @@
     /* pcbRead is optional. */
 
+    PHDABDLE pBDLE       = &pStream->State.BDLE;
+
     int rc = VINF_SUCCESS;
 
     uint32_t cbReadTotal = 0;
-
-    PHDABDLE   pBDLE     = &pStream->State.BDLE;
-    PRTCIRCBUF pCircBuf  = pStream->State.pCircBuf;
-    AssertPtr(pCircBuf);
+    uint32_t cbLeft      = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
 
 #ifdef HDA_DEBUG_SILENCE
@@ -327,77 +270,50 @@
 #endif
 
-    while (cbToRead)
-    {
-        /* Make sure we only copy as much as the stream's FIFO can hold (SDFIFOS, 18.2.39). */
-        void *pvBuf;
-        size_t cbBuf;
-        RTCircBufAcquireWriteBlock(pCircBuf, RT_MIN(cbToRead, pStream->u16FIFOS), &pvBuf, &cbBuf);
-
-        if (cbBuf)
+    RTGCPHYS addrChunk = pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff;
+
+    while (cbLeft)
+    {
+        uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
+
+        rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), addrChunk, (uint8_t *)pvBuf + cbReadTotal, cbChunk);
+        if (RT_FAILURE(rc))
+            break;
+
+#ifdef HDA_DEBUG_SILENCE
+        uint16_t *pu16Buf = (uint16_t *)pvBuf;
+        for (size_t i = 0; i < cbChunk / sizeof(uint16_t); i++)
         {
-            /*
-             * Read from the current BDLE's DMA buffer.
-             */
-            int rc2 = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns),
-                                        pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff + cbReadTotal, pvBuf, cbBuf);
+            if (*pu16Buf == 0)
+            {
+                csSilence++;
+            }
+            else
+                break;
+            pu16Buf++;
+        }
+#endif
+
+#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
+        RTFILE fh;
+        int rc2 = RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMARead.pcm",
+                             RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+        if (RT_SUCCESS(rc2))
+        {
+            RTFileWrite(fh, (uint8_t *)pvBuf + cbReadTotal, cbChunk, NULL);
+            RTFileClose(fh);
+        }
+        else
             AssertRC(rc2);
-
-#ifdef HDA_DEBUG_SILENCE
-            uint16_t *pu16Buf = (uint16_t *)pvBuf;
-            for (size_t i = 0; i < cbBuf / sizeof(uint16_t); i++)
-            {
-                if (*pu16Buf == 0)
-                {
-                    csSilence++;
-                }
-                else
-                    break;
-                pu16Buf++;
-            }
-#endif
-
-#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
-            if (cbBuf)
-            {
-                RTFILE fh;
-                rc2 = RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMARead.pcm",
-                                 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
-                if (RT_SUCCESS(rc2))
-                {
-                    RTFileWrite(fh, pvBuf, cbBuf, NULL);
-                    RTFileClose(fh);
-                }
-                else
-                    AssertRC(rc2);
-            }
-#endif
-
-#if 0
-            pStream->Dbg.cbReadTotal += cbBuf;
-            const uint64_t cbWritten = ASMAtomicReadU64(&pStream->Dbg.cbWrittenTotal);
-            Log3Func(("cbRead=%RU64, cbWritten=%RU64 -> %RU64 bytes %s\n",
-                      pStream->Dbg.cbReadTotal, cbWritten,
-                      pStream->Dbg.cbReadTotal >= cbWritten ? pStream->Dbg.cbReadTotal - cbWritten : cbWritten - pStream->Dbg.cbReadTotal,
-                      pStream->Dbg.cbReadTotal > cbWritten ? "too much" : "too little"));
 #endif
 
 #ifdef VBOX_WITH_STATISTICS
-            STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBuf);
-#endif
-        }
-
-        RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
-
-        if (!cbBuf)
-        {
-            rc = VERR_BUFFER_OVERFLOW;
-            break;
-        }
-
-        cbReadTotal += (uint32_t)cbBuf;
-        Assert(pBDLE->State.u32BufOff + cbReadTotal <= pBDLE->Desc.u32BufSize);
-
-        Assert(cbToRead >= cbBuf);
-        cbToRead    -= (uint32_t)cbBuf;
+        STAM_COUNTER_ADD(&pThis->StatBytesRead, cbChunk);
+#endif
+        addrChunk         = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
+
+        Assert(cbLeft    >= cbChunk);
+        cbLeft           -= cbChunk;
+
+        cbReadTotal      += cbChunk;
     }
 
@@ -431,8 +347,9 @@
  * @param   pThis               HDA state.
  * @param   pStream             HDA input stream to write audio data to.
- * @param   cbToWrite           How much (in bytes) to write.
+ * @param   pvBuf               Data to write.
+ * @param   cbBuf               How much (in bytes) to write.
  * @param   pcbWritten          Returns written bytes on success. Optional.
  */
-int hdaDMAWrite(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToWrite, uint32_t *pcbWritten)
+int hdaDMAWrite(PHDASTATE pThis, PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
 {
     AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
@@ -440,38 +357,21 @@
     /* pcbWritten is optional. */
 
-    PHDABDLE   pBDLE    = &pStream->State.BDLE;
-    PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
-    AssertPtr(pCircBuf);
+    PHDABDLE pBDLE  = &pStream->State.BDLE;
 
     int rc = VINF_SUCCESS;
 
     uint32_t cbWrittenTotal = 0;
-
-    void *pvBuf  = NULL;
-    size_t cbBuf = 0;
-
-    uint8_t abSilence[HDA_FIFO_MAX + 1] = { 0 };
-
-    while (cbToWrite)
-    {
-        size_t cbChunk = RT_MIN(cbToWrite, pStream->u16FIFOS);
-
-        size_t cbBlock = 0;
-        RTCircBufAcquireReadBlock(pCircBuf, cbChunk, &pvBuf, &cbBlock);
-
-        if (cbBlock)
-        {
-            cbBuf = cbBlock;
-        }
-        else /* No audio data available? Send silence. */
-        {
-            pvBuf = &abSilence;
-            cbBuf = cbChunk;
-        }
+    uint32_t cbLeft = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
+
+    RTGCPHYS addrChunk = pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff;
+
+    while (cbLeft)
+    {
+        uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
 
         /* Sanity checks. */
-        Assert(cbBuf <= pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
-        Assert(cbBuf % HDA_FRAME_SIZE == 0);
-        Assert((cbBuf >> 1) >= 1);
+        Assert(cbChunk <= pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
+        Assert(cbChunk % HDA_FRAME_SIZE == 0);
+        Assert((cbChunk >> 1) >= 1);
 
 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
@@ -479,22 +379,21 @@
         RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMAWrite.pcm",
                    RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
-        RTFileWrite(fh, pvBuf, cbBuf, NULL);
+        RTFileWrite(fh, pvBuf, cbChunk, NULL);
         RTFileClose(fh);
 #endif
-        int rc2 = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
-                                        pBDLE->Desc.u64BufAdr + pBDLE->State.u32BufOff + cbWrittenTotal,
-                                        pvBuf, cbBuf);
-        AssertRC(rc2);
+        rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
+                                   addrChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
+        if (RT_FAILURE(rc))
+            break;
 
 #ifdef VBOX_WITH_STATISTICS
-        STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbBuf);
-#endif
-        if (cbBlock)
-            RTCircBufReleaseReadBlock(pCircBuf, cbBlock);
-
-        Assert(cbToWrite >= cbBuf);
-        cbToWrite -= (uint32_t)cbBuf;
-
-        cbWrittenTotal += (uint32_t)cbBuf;
+        STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbChunk);
+#endif
+        addrChunk       = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
+
+        Assert(cbLeft  >= cbChunk);
+        cbLeft -= (uint32_t)cbChunk;
+
+        cbWrittenTotal += (uint32_t)cbChunk;
     }
 
@@ -707,4 +606,55 @@
     return (pBDLE->Desc.fFlags & HDA_BDLE_FLAG_IOC);
 }
+
+/**
+ * Sets the virtual device timer to a new expiration time.
+ *
+ * @returns Whether the new expiration time was set or not.
+ * @param   pThis               HDA state.
+ * @param   tsExpire            New (virtual) expiration time to set.
+ * @param   fForce              Whether to force setting the expiration time or not.
+ *
+ * @remark  This function takes all active HDA streams and their
+ *          current timing into account. This is needed to make sure
+ *          that all streams can match their needed timing.
+ *
+ *          To achieve this, the earliest (lowest) timestamp of all
+ *          active streams found will be used for the next scheduling slot.
+ *
+ *          Forcing a new expiration time will override the above mechanism.
+ */
+bool hdaTimerSet(PHDASTATE pThis, uint64_t tsExpire, bool fForce)
+{
+    AssertPtr(pThis->pTimer);
+
+    uint64_t tsExpireMin = tsExpire;
+
+    if (!fForce)
+    {
+        for (uint8_t i = 0; i < HDA_MAX_STREAMS; i++)
+        {
+            PHDASTREAM pStream = &pThis->aStreams[i];
+
+            if (!(HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_RUN))
+                continue;
+
+            if (hdaStreamTransferIsScheduled(pStream))
+                tsExpireMin = RT_MIN(tsExpireMin, hdaStreamTransferGetNext(pStream));
+        }
+    }
+
+    const uint64_t tsNow = TMTimerGet(pThis->pTimer);
+
+    if (tsExpireMin < tsNow) /* Make sure to not go backwards in time. */
+        tsExpireMin = tsNow;
+
+    Log3Func(("u64Epxire=%RU64 -> u64ExpireMin=%RU64, fForce=%RTbool [%s]\n",
+              tsExpire, tsExpireMin, fForce, tsExpireMin == tsExpire ? "OK" : "DELAYED"));
+
+    int rc2 = TMTimerSet(pThis->pTimer, tsExpireMin);
+    AssertRC(rc2);
+
+    return tsExpireMin == tsExpire;
+}
 #endif /* IN_RING3 */
 
Index: /trunk/src/VBox/Devices/Audio/DevHDACommon.h
===================================================================
--- /trunk/src/VBox/Devices/Audio/DevHDACommon.h	(revision 69918)
+++ /trunk/src/VBox/Devices/Audio/DevHDACommon.h	(revision 69919)
@@ -89,11 +89,24 @@
 /** Default timer frequency (in Hz).
  *
- *  Note: Keep in mind that the Hz rate has nothing to do with samples rates
- *        or DMA / interrupt timing -- it's purely needed in order to drive
- *        the data flow at a constant (and sufficient) rate.
- *
- *        Lowering this value can ask for trouble, as backends then can run
- *        into data underruns. */
-#define HDA_TIMER_HZ                200
+ * Lowering this value can ask for trouble, as backends then can run
+ * into data underruns. */
+#define HDA_TIMER_HZ_DEFAULT        100
+
+/** Default position adjustment (in audio samples).
+ *
+ * For snd_hda_intel (Linux guests), the first BDL entry always is being used as
+ * so-called BDL adjustment, which can vary, and is being used for chipsets which
+ * misbehave and/or are incorrectly implemented.
+ *
+ * The BDL adjustment entry *always* has the IOC (Interrupt on Completion) bit set.
+ *
+ * For Intel Baytrail / Braswell implementations the BDL default adjustment is 32 frames, whereas
+ * for ICH / PCH it's only one (1) frame.
+ *
+ * See default_bdl_pos_adj() and snd_hdac_stream_setup_periods() for more information.
+ *
+ * By default we apply some simple heuristics in hdaStreamInit().
+ */
+#define HDA_POS_ADJUST_DEFAULT      0
 
 /** HDA's (fixed) audio frame size in bytes.
@@ -231,9 +244,15 @@
 #define HDA_REG_CORBSIZE            21          /* 0x4E */
 #define HDA_RMX_CORBSIZE            19
+#define HDA_CORBSIZE_SZ_CAP         0xF0
+#define HDA_CORBSIZE_SZ             0x3
 
 /** Number of CORB buffer entries. */
 #define HDA_CORB_SIZE               256
+/** CORB element size (in bytes). */
+#define HDA_CORB_ELEMENT_SIZE       4
 /** Number of RIRB buffer entries. */
 #define HDA_RIRB_SIZE               256
+/** RIRB element size (in bytes). */
+#define HDA_RIRB_ELEMENT_SIZE       8
 
 #define HDA_REG_RIRBLBASE           22          /* 0x50 */
@@ -586,6 +605,6 @@
  * @{
  */
-int           hdaDMARead(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToRead, uint32_t *pcbRead);
-int           hdaDMAWrite(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToWrite, uint32_t *pcbWritten);
+int           hdaDMARead(PHDASTATE pThis, PHDASTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead);
+int           hdaDMAWrite(PHDASTATE pThis, PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten);
 /** @} */
 
@@ -609,4 +628,12 @@
 /** @} */
 
+/** @name Device timer functions.
+ * @{
+ */
+#ifdef IN_RING3
+bool          hdaTimerSet(PHDASTATE pThis, uint64_t u64Expire, bool fForce);
+#endif /* IN_RING3 */
+/** @} */
+
 #endif /* !DEV_HDA_H_COMMON */
 
Index: /trunk/src/VBox/Devices/Audio/HDAStream.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/HDAStream.cpp	(revision 69918)
+++ /trunk/src/VBox/Devices/Audio/HDAStream.cpp	(revision 69919)
@@ -92,6 +92,4 @@
     rc2 = hdaStreamAsyncIODestroy(pStream);
     AssertRC(rc2);
-#else
-    RT_NOREF(pThis);
 #endif
 
@@ -137,5 +135,5 @@
 
     /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
-    hdaStreamUpdateLPIB(pStream, HDA_STREAM_REG(pThis, LPIB, pStream->u8SD));
+    hdaStreamSetPosition(pStream, HDA_STREAM_REG(pThis, LPIB, pStream->u8SD));
 
     PPDMAUDIOSTREAMCFG pCfg = &pStream->State.Cfg;
@@ -187,4 +185,99 @@
     LogFunc(("[SD%RU8] DMA @ 0x%x (%RU32 bytes), LVI=%RU16, FIFOS=%RU16, rc=%Rrc\n",
              pStream->u8SD, pStream->u64BDLBase, pStream->u32CBL, pStream->u16LVI, pStream->u16FIFOS, rc));
+
+    if (   pStream->u32CBL
+        && pStream->u16LVI)
+    {
+        /* Make sure that the chosen Hz rate dividable by the stream's rate. */
+        if (pStream->State.Cfg.Props.uHz % pThis->u16TimerHz != 0)
+            LogRel(("HDA: Device timer (%RU32) does not fit to stream #RU8 timing (%RU32)\n",
+                    pThis->u16TimerHz, pStream->State.Cfg.Props.uHz));
+
+        /** @todo Use a more dynamic fragment size? */
+        uint8_t cFragments = pStream->u16LVI;
+        if (cFragments <= 1)
+            cFragments = 2; /* At least two fragments (BDLEs) must be present. */
+
+        /* Calculate the fragment size the guest OS expects interrupt delivery at. */
+        pStream->State.cbTransferSize = pStream->u32CBL / cFragments;
+        Assert(pStream->State.cbTransferSize);
+        Assert(pStream->State.cbTransferSize % HDA_FRAME_SIZE == 0);
+
+        /* Calculate the bytes we need to transfer to / from the stream's DMA per iteration.
+         * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects. */
+        pStream->State.cbTransferChunk = (pStream->State.Cfg.Props.uHz / pThis->u16TimerHz) * HDA_FRAME_SIZE;
+        Assert(pStream->State.cbTransferChunk);
+        Assert(pStream->State.cbTransferChunk % HDA_FRAME_SIZE == 0);
+
+        /* Make sure that the transfer chunk does not exceed the overall transfer size. */
+        if (pStream->State.cbTransferChunk > pStream->State.cbTransferSize)
+            pStream->State.cbTransferChunk = pStream->State.cbTransferSize;
+
+        pStream->State.cbTransferProcessed        = 0;
+        pStream->State.cTransferPendingInterrupts = 0;
+
+        const uint64_t cTicksPerHz = TMTimerGetFreq(pThis->pTimer) / pThis->u16TimerHz;
+
+        /* Calculate the timer ticks per byte for this stream. */
+        pStream->State.cTicksPerByte = cTicksPerHz / pStream->State.cbTransferChunk;
+        Assert(pStream->State.cTicksPerByte);
+
+        /* Calculate timer ticks per transfer. */
+        pStream->State.cTransferTicks     = pStream->State.cbTransferChunk * pStream->State.cTicksPerByte;
+
+        /* Initialize the transfer timestamps. */
+        pStream->State.tsTransferLast     = 0;
+        pStream->State.tsTransferNext     = 0;
+
+        LogFunc(("[SD%RU8] Timer %uHz (%RU64 ticks per Hz), ticks per byte: %RU64 (%RU64 bytes per iteration = %RU64 ticks)," \
+                 " transfer size = %RU64\n",
+                 pStream->u8SD, pThis->u16TimerHz, cTicksPerHz, pStream->State.cTicksPerByte,
+                 pStream->State.cbTransferChunk, pStream->State.cTransferTicks, pStream->State.cbTransferSize));
+        /*
+         * Handle the stream's position adjustment.
+         */
+        uint32_t cfPosAdjust = 0;
+
+        if (pThis->fPosAdjustEnabled) /* Is the position adjustment enabled at all? */
+        {
+            /* If no custom set position adjustment is set, apply some
+             * simple heuristics to detect the appropriate position adjustment. */
+            if (   pStream->u64BDLBase
+                && !pThis->cPosAdjustFrames)
+            {
+                HDABDLE BDLE;
+                int rc2 = hdaBDLEFetch(pThis, &BDLE, pStream->u64BDLBase, 0 /* Entry */);
+                AssertRC(rc2);
+
+                /** @todo Implement / use a (dynamic) table once this gets more complicated. */
+
+#ifdef VBOX_WITH_INTEL_HDA
+                /* Intel ICH / PCH: 1 frame. */
+                if (BDLE.Desc.u32BufSize == 1 * HDA_FRAME_SIZE)
+                {
+                    cfPosAdjust = 1;
+                }
+                /* Intel Baytrail / Braswell: 32 frames. */
+                else if (BDLE.Desc.u32BufSize == 32 * HDA_FRAME_SIZE)
+                {
+                    cfPosAdjust = 32;
+                }
+                else
+#endif
+                    cfPosAdjust = pThis->cPosAdjustFrames;
+            }
+            else /* Go with the set default. */
+                cfPosAdjust = pThis->cPosAdjustFrames;
+
+            if (cfPosAdjust)
+            {
+                /* Initialize position adjustment counter. */
+                pStream->State.cPosAdjustFramesLeft = cfPosAdjust;
+                LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n", pStream->u8SD, cfPosAdjust));
+            }
+        }
+
+        LogFunc(("[SD%RU8] cfPosAdjust=%RU32\n", pStream->u8SD, cfPosAdjust));
+    }
 
     return rc;
@@ -239,4 +332,7 @@
 #endif
 
+    pStream->State.tsTransferLast = 0;
+    pStream->State.tsTransferNext = 0;
+
     RT_ZERO(pStream->State.BDLE);
     pStream->State.uCurBDLE = 0;
@@ -304,63 +400,37 @@
 }
 
-/**
- * Returns the number of outstanding stream data bytes which need to be processed
- * by the DMA engine assigned to this stream.
- *
- * @return Number of bytes for the DMA engine to process.
- */
-uint32_t hdaStreamGetTransferSize(PHDASTATE pThis, PHDASTREAM pStream)
-{
-    AssertPtrReturn(pThis, 0);
-    AssertPtrReturn(pStream, 0);
-
-    if (!RT_BOOL(HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_RUN))
-    {
-        AssertFailed(); /* Should never happen. */
-        return 0;
-    }
-
-    /* Determine how much for the current BDL entry we have left to transfer. */
-    PHDABDLE pBDLE  = &pStream->State.BDLE;
-    const uint32_t cbBDLE = RT_MIN(pBDLE->Desc.u32BufSize, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
-
-    /* Determine how much we (still) can stuff in the stream's internal FIFO.  */
-    const uint32_t cbCircBuf   = (uint32_t)RTCircBufFree(pStream->State.pCircBuf);
-
-    uint32_t cbToTransfer = cbBDLE;
-
-    /* Make sure that we don't transfer more than our FIFO can hold at the moment.
-     * As the host sets the overall pace it needs to process some of the FIFO data first before
-     * we can issue a new DMA data transfer. */
-    if (cbToTransfer > cbCircBuf)
-        cbToTransfer = cbCircBuf;
-
-    Log3Func(("[SD%RU8] LPIB=%RU32 CBL=%RU32 cbCircBuf=%RU32, -> cbToTransfer=%RU32 %R[bdle]\n", pStream->u8SD,
-              HDA_STREAM_REG(pThis, LPIB, pStream->u8SD), pStream->u32CBL, cbCircBuf, cbToTransfer, pBDLE));
-    return cbToTransfer;
-}
-
-/**
- * Increases the amount of transferred (audio) data of an HDA stream and
- * reports this as needed to the guest.
- *
- * @param  pStream              HDA stream to increase amount for.
- * @param  cbInc                Amount (in bytes) to increase.
- */
-void hdaStreamTransferInc(PHDASTREAM pStream, uint32_t cbInc)
+uint32_t hdaStreamGetPosition(PHDASTATE pThis, PHDASTREAM pStream)
+{
+    return HDA_STREAM_REG(pThis, LPIB, pStream->u8SD);
+}
+
+/**
+ * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
+ * updating its associated LPIB register and DMA position buffer (if enabled).
+ *
+ * @param   pThis               HDA state.
+ * @param   pStream             HDA stream to update read / write position for.
+ * @param   u32LPIB             Absolute position (in bytes) to set current read / write position to.
+ */
+void hdaStreamSetPosition(PHDASTREAM pStream, uint32_t u32LPIB)
 {
     AssertPtrReturnVoid(pStream);
 
-    if (!cbInc)
-        return;
-
-    const PHDASTATE pThis  = pStream->pHDAState;
-
-    const uint32_t u32LPIB = HDA_STREAM_REG(pThis, LPIB, pStream->u8SD);
-
-    Log3Func(("[SD%RU8] %RU32 + %RU32 -> %RU32, CBL=%RU32\n",
-              pStream->u8SD, u32LPIB, cbInc, u32LPIB + cbInc, pStream->u32CBL));
-
-    hdaStreamUpdateLPIB(pStream, u32LPIB + cbInc);
+    Assert(u32LPIB % HDA_FRAME_SIZE == 0);
+
+    Log3Func(("[SD%RU8] LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n",
+              pStream->u8SD, u32LPIB, pStream->pHDAState->fDMAPosition));
+
+    /* Update LPIB in any case. */
+    HDA_STREAM_REG(pStream->pHDAState, LPIB, pStream->u8SD) = u32LPIB;
+
+    /* Do we need to tell the current DMA position? */
+    if (pStream->pHDAState->fDMAPosition)
+    {
+        int rc2 = PDMDevHlpPCIPhysWrite(pStream->pHDAState->CTX_SUFF(pDevIns),
+                                        pStream->pHDAState->u64DPBase + (pStream->u8SD * 2 * sizeof(uint32_t)),
+                                        (void *)&u32LPIB, sizeof(uint32_t));
+        AssertRC(rc2);
+    }
 }
 
@@ -397,4 +467,33 @@
 }
 
+/**
+ * Returns whether a next transfer for a given stream is scheduled or not.
+ *
+ * @returns True if a next transfer is scheduled, false if not.
+ * @param   pStream             HDA stream to retrieve schedule status for.
+ */
+bool hdaStreamTransferIsScheduled(PHDASTREAM pStream)
+{
+    AssertPtrReturn(pStream,            false);
+    AssertPtrReturn(pStream->pHDAState, false);
+
+    const bool fScheduled = pStream->State.tsTransferNext > TMTimerGet(pStream->pHDAState->pTimer);
+
+    Log3Func(("[SD%RU8] %RU64 -> %RTbool\n", pStream->u8SD, pStream->State.tsTransferNext, fScheduled));
+
+    return fScheduled;
+}
+
+/**
+ * Returns the (virtual) clock timestamp of the next transfer, if any.
+ * Will return 0 if no new transfer is scheduled.
+ *
+ * @returns The (virtual) clock timestamp of the next transfer.
+ * @param   pStream             HDA stream to retrieve timestamp for.
+ */
+uint64_t hdaStreamTransferGetNext(PHDASTREAM pStream)
+{
+    return pStream->State.tsTransferNext;
+}
 
 /**
@@ -403,22 +502,15 @@
  * @returns IPRT status code.
  * @param   pStream             HDA stream to write to.
- * @param   cbToWrite           Number of bytes to write.
+ * @param   pvBuf               Data buffer to write.
+ *                              If NULL, silence will be written.
+ * @param   cbBuf               Number of bytes of data buffer to write.
  * @param   pcbWritten          Number of bytes written. Optional.
  */
-int hdaStreamWrite(PHDASTREAM pStream, uint32_t cbToWrite, uint32_t *pcbWritten)
+int hdaStreamWrite(PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
 {
     AssertPtrReturn(pStream, VERR_INVALID_POINTER);
-    AssertReturn(cbToWrite,  VERR_INVALID_PARAMETER);
+    /* pvBuf is optional. */
+    AssertReturn(cbBuf,      VERR_INVALID_PARAMETER);
     /* pcbWritten is optional. */
-
-    PHDAMIXERSINK pSink = pStream->pMixSink;
-    if (!pSink)
-    {
-        AssertMsgFailed(("[SD%RU8]: Can't write to a stream with no sink attached\n", pStream->u8SD));
-
-        if (pcbWritten)
-            *pcbWritten = 0;
-        return VINF_SUCCESS;
-    }
 
     PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
@@ -428,5 +520,5 @@
 
     uint32_t cbWrittenTotal = 0;
-    uint32_t cbLeft         = RT_MIN(cbToWrite, (uint32_t)RTCircBufFree(pCircBuf));
+    uint32_t cbLeft         = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
 
     while (cbLeft)
@@ -435,15 +527,18 @@
         size_t cbDst;
 
-        uint32_t cbRead = 0;
-
         RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, &pvDst, &cbDst);
 
         if (cbDst)
         {
-            rc = AudioMixerSinkRead(pSink->pMixSink, AUDMIXOP_COPY, pvDst, (uint32_t)cbDst, &cbRead);
-            AssertRC(rc);
-
-            Assert(cbDst >= cbRead);
-            Log2Func(("[SD%RU8]: %RU32/%zu bytes read\n", pStream->u8SD, cbRead, cbDst));
+            if (pvBuf)
+            {
+                memcpy(pvDst, (uint8_t *)pvBuf + cbWrittenTotal, cbDst);
+            }
+            else /* Send silence. */
+            {
+                /** @todo Use a sample spec for "silence" based on the PCM parameters.
+                 *        For now we ASSUME that silence equals NULLing the data. */
+                RT_BZERO(pvDst, cbDst);
+            }
 
 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
@@ -451,18 +546,18 @@
             RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaStreamWrite.pcm",
                        RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
-            RTFileWrite(fh, pvDst, cbRead, NULL);
+            RTFileWrite(fh, pvDst, cbDst, NULL);
             RTFileClose(fh);
 #endif
         }
 
-        RTCircBufReleaseWriteBlock(pCircBuf, cbRead);
+        RTCircBufReleaseWriteBlock(pCircBuf, cbDst);
 
         if (RT_FAILURE(rc))
             break;
 
-        Assert(cbLeft  >= cbRead);
-        cbLeft         -= cbRead;
-
-        cbWrittenTotal += cbRead;
+        Assert(cbLeft  >= cbDst);
+        cbLeft         -= cbDst;
+
+        cbWrittenTotal += cbDst;
     }
 
@@ -548,28 +643,4 @@
 }
 
-uint32_t hdaStreamTransferGetElapsed(PHDASTREAM pStream)
-{
-    AssertPtr(pStream->pHDAState->pTimer);
-    const uint64_t cTicksNow     = TMTimerGet(pStream->pHDAState->pTimer);
-    const uint64_t cTicksPerSec  = TMTimerGetFreq(pStream->pHDAState->pTimer);
-
-    const uint64_t cTicksElapsed = cTicksNow - pStream->State.uTimerTS;
-#ifdef DEBUG
-    const uint64_t cMsElapsed    = cTicksElapsed / (cTicksPerSec / 1000);
-#endif
-
-    AssertPtr(pStream->pHDAState->pCodec);
-
-    PPDMAUDIOSTREAMCFG pCfg = &pStream->State.Cfg;
-
-    /* A stream *always* runs with 48 kHz device-wise, regardless of the actual stream input/output format (Hz) being set. */
-    uint32_t csPerPeriod = (int)((pCfg->Props.cChannels * cTicksElapsed * 48000 /* Hz */ + cTicksPerSec) / cTicksPerSec / 2);
-    uint32_t cbPerPeriod = csPerPeriod << pCfg->Props.cShift;
-
-    Log3Func(("[SD%RU8] %RU64ms (%zu samples, %zu bytes) elapsed\n", pStream->u8SD, cMsElapsed, csPerPeriod, cbPerPeriod));
-
-    return cbPerPeriod;
-}
-
 /**
  * Transfers data of an HDA stream according to its usage (input / output).
@@ -583,10 +654,8 @@
  * @returns IPRT status code.
  * @param   pStream             HDA stream to update.
- * @param   cbToProcessMax      Maximum of data (in bytes) to process.
  */
 int hdaStreamTransfer(PHDASTREAM pStream, uint32_t cbToProcessMax)
 {
-    AssertPtrReturn(pStream,        VERR_INVALID_POINTER);
-    AssertReturn(cbToProcessMax,    VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
 
     hdaStreamLock(pStream);
@@ -607,8 +676,7 @@
         fProceed = false;
     }
-    /* Period complete? */
-    else if (hdaStreamPeriodIsComplete(pPeriod))
-    {
-        Log3Func(("[SD%RU8] Period is complete, nothing to do\n", pStream->u8SD));
+    else if (HDA_STREAM_REG(pThis, STS, pStream->u8SD) & HDA_SDSTS_BCIS)
+    {
+        Log3Func(("[SD%RU8] BCIS bit set\n", pStream->u8SD));
         fProceed = false;
     }
@@ -620,4 +688,17 @@
         return VINF_SUCCESS;
     }
+
+    const uint64_t tsNow = TMTimerGet(pThis->pTimer);
+
+    if (!pStream->State.tsTransferLast)
+        pStream->State.tsTransferLast = tsNow;
+
+#ifdef DEBUG
+    const int64_t iTimerDelta = tsNow - pStream->State.tsTransferLast;
+    Log3Func(("[SD%RU8] Time now=%RU64, last=%RU64 -> %RI64 ticks delta\n",
+              pStream->u8SD, tsNow, pStream->State.tsTransferLast, iTimerDelta));
+#endif
+
+    pStream->State.tsTransferLast = tsNow;
 
     /* Sanity checks. */
@@ -625,4 +706,5 @@
     Assert(pStream->u64BDLBase);
     Assert(pStream->u32CBL);
+    Assert(pStream->u16FIFOS);
 
     /* State sanity checks. */
@@ -637,47 +719,120 @@
     }
 
-    const uint32_t cbPeriodRemaining = hdaStreamPeriodGetRemainingFrames(pPeriod) * HDA_FRAME_SIZE;
-    Assert(cbPeriodRemaining); /* Paranoia. */
-
-    const uint32_t cbElapsed         = hdaStreamTransferGetElapsed(pStream);
-    Assert(cbElapsed);         /* Paranoia. */
-
-    /* Limit the data to read, as this routine could be delayed and therefore
-     * report wrong (e.g. too much) cbElapsed bytes. */
-    uint32_t cbLeft                  = RT_MIN(RT_MIN(cbPeriodRemaining, cbElapsed), cbToProcessMax);
-
-    Log3Func(("[SD%RU8] cbPeriodRemaining=%RU32, cbElapsed=%RU32, cbToProcessMax=%RU32 -> cbLeft=%RU32\n",
-              pStream->u8SD, cbPeriodRemaining, cbElapsed, cbToProcessMax, cbLeft));
-
-    Assert(cbLeft % HDA_FRAME_SIZE == 0); /* Paranoia. */
-
+    uint32_t cbToProcess = RT_MIN(pStream->State.cbTransferSize - pStream->State.cbTransferProcessed,
+                                  pStream->State.cbTransferChunk);
+    uint32_t cbProcessed = 0;
+    uint32_t cbLeft      = cbToProcess;
+    Assert(cbLeft % HDA_FRAME_SIZE == 0);
+
+    if (cbToProcess > cbToProcessMax)
+        LogRel2(("HDA: More data to process\n"));
+
+    Log3Func(("[SD%RU8] cbToProcess=%RU32\n", pStream->u8SD, cbToProcess));
+
+    uint8_t abChunk[HDA_FIFO_MAX + 1];
     while (cbLeft)
     {
-        uint32_t cbChunk = RT_MIN(hdaStreamGetTransferSize(pThis, pStream), cbLeft);
+        /* Limit the chunk to the stream's FIFO size and what's left to process. */
+        uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
+
+        /* Limit the chunk to the remaining data of the current BDLE. */
+        cbChunk = RT_MIN(cbChunk, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
+
+        /* If there are position adjustment frames left to be processed,
+         * make sure that we process them first as a whole. */
+        if (pStream->State.cPosAdjustFramesLeft)
+            cbChunk = RT_MIN(cbChunk, pStream->State.cPosAdjustFramesLeft * HDA_FRAME_SIZE);
+
+        Log3Func(("[SD%RU8] cbChunk=%RU32, cPosAdjustFramesLeft=%RU16\n",
+                  pStream->u8SD, cbChunk, pStream->State.cPosAdjustFramesLeft));
+
         if (!cbChunk)
             break;
 
-        uint32_t cbDMA   = 0;
-
-        if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
+        uint32_t   cbDMA    = 0;
+        PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
+
+        if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN) /* Input (SDI). */
+        {
+            STAM_PROFILE_START(&pThis->StatIn, a);
+
+            uint32_t cbDMAWritten = 0;
+            uint32_t cbDMAToWrite = cbChunk;
+
+            while (cbDMAToWrite)
+            {
+                void *pvBuf; size_t cbBuf;
+                RTCircBufAcquireReadBlock(pCircBuf, cbDMAToWrite, &pvBuf, &cbBuf);
+
+                if (   !cbBuf
+                    && !RTCircBufUsed(pCircBuf))
+                    break;
+
+                memcpy(abChunk + cbDMAWritten, pvBuf, cbBuf);
+
+                RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
+
+                Assert(cbDMAToWrite >= cbBuf);
+                cbDMAToWrite -= (uint32_t)cbBuf;
+                cbDMAWritten += (uint32_t)cbBuf;
+                Assert(cbDMAWritten <= cbChunk);
+            }
+
+            if (cbDMAToWrite)
+            {
+                LogRel2(("HDA: FIFO underflow for stream #%RU8 (%RU32 bytes outstanding)\n", pStream->u8SD, cbDMAToWrite));
+
+                Assert(cbChunk == cbDMAWritten + cbDMAToWrite);
+                memset((uint8_t *)abChunk + cbDMAWritten, 0, cbDMAToWrite);
+                cbDMAWritten = cbChunk;
+            }
+
+            rc = hdaDMAWrite(pThis, pStream, abChunk, cbDMAWritten, &cbDMA /* pcbWritten */);
+            if (RT_FAILURE(rc))
+                LogRel(("HDA: Writing to stream #%RU8 DMA failed with %Rrc\n", pStream->u8SD, rc));
+
+            STAM_PROFILE_STOP(&pThis->StatIn, a);
+        }
+        else if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
         {
             STAM_PROFILE_START(&pThis->StatOut, a);
 
-            rc = hdaDMARead(pThis, pStream, cbChunk, &cbDMA /* pcbRead */);
-            if (RT_FAILURE(rc))
+            rc = hdaDMARead(pThis, pStream, abChunk, cbChunk, &cbDMA /* pcbRead */);
+            if (RT_SUCCESS(rc))
+            {
+                uint32_t cbDMAWritten = 0;
+                uint32_t cbDMALeft    = cbDMA;
+
+                if (cbDMALeft > RTCircBufFree(pCircBuf))
+                {
+                    LogRel2(("HDA: FIFO overflow for stream #%RU8 (%RU32 bytes outstanding)\n",
+                             pStream->u8SD, cbDMALeft - RTCircBufFree(pCircBuf)));
+
+                    /* Try to read as much as possible. */
+                    cbDMALeft = (uint32_t)RTCircBufFree(pCircBuf);
+
+                    rc = VERR_BUFFER_OVERFLOW;
+                }
+
+                while (cbDMALeft)
+                {
+                    void *pvBuf; size_t cbBuf;
+                    RTCircBufAcquireWriteBlock(pCircBuf, cbDMALeft, &pvBuf, &cbBuf);
+
+                    if (cbBuf)
+                        memcpy(pvBuf, abChunk + cbDMAWritten, cbBuf);
+
+                    RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
+
+                    cbDMALeft    -= (uint32_t)cbBuf;
+                    cbDMAWritten += (uint32_t)cbBuf;
+                }
+            }
+            else
                 LogRel(("HDA: Reading from stream #%RU8 DMA failed with %Rrc\n", pStream->u8SD, rc));
 
             STAM_PROFILE_STOP(&pThis->StatOut, a);
         }
-        else if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN) /* Input (SDI). */
-        {
-            STAM_PROFILE_START(&pThis->StatIn, a);
-
-            rc = hdaDMAWrite(pThis, pStream, cbChunk, &cbDMA /* pcbWritten */);
-            if (RT_FAILURE(rc))
-                LogRel(("HDA: Writing to stream #%RU8 DMA failed with %Rrc\n", pStream->u8SD, rc));
-
-            STAM_PROFILE_STOP(&pThis->StatIn, a);
-        }
+
         else /** @todo Handle duplex streams? */
             AssertFailed();
@@ -692,16 +847,24 @@
             Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
 
-            hdaStreamTransferInc(pStream, cbDMA);
-
-            uint32_t framesDMA = cbDMA / HDA_FRAME_SIZE;
-
-            /* Add the transferred frames to the period. */
-            hdaStreamPeriodInc(pPeriod, framesDMA);
-
-            /* Save the timestamp of when the last successful DMA transfer has been for this stream. */
-            pStream->State.uTimerTS = TMTimerGet(pThis->pTimer);
-
-            Assert(cbLeft >= cbDMA);
-            cbLeft        -= cbDMA;
+            /* Are we done doing the position adjustment?
+             * Only then do the transfer accounting .*/
+            if (pStream->State.cPosAdjustFramesLeft == 0)
+            {
+                Assert(cbLeft >= cbDMA);
+                cbLeft        -= cbDMA;
+
+                cbProcessed   += cbDMA;
+            }
+
+            /**
+             * Update the stream's current position.
+             * Do this as accurate and close to the actual data transfer as possible.
+             * All guetsts rely on this, depending on the mechanism they use (LPIB register or DMA counters).
+             */
+            uint32_t cbStreamPos = hdaStreamGetPosition(pThis, pStream);
+            if (cbStreamPos == pStream->u32CBL)
+                cbStreamPos = 0;
+
+            hdaStreamSetPosition(pStream, cbStreamPos + cbDMA);
         }
 
@@ -710,5 +873,12 @@
             Log3Func(("[SD%RU8] Complete: %R[bdle]\n", pStream->u8SD, pBDLE));
 
-            if (hdaBDLENeedsInterrupt(pBDLE))
+                /* Does the current BDLE require an interrupt to be sent? */
+            if (   hdaBDLENeedsInterrupt(pBDLE)
+                /* Are we done doing the position adjustment?
+                 * It can happen that a BDLE which is handled while doing the
+                 * position adjustment requires an interrupt on completion (IOC) being set.
+                 *
+                 * In such a case we need to skip such an interrupt and just move on. */
+                && pStream->State.cPosAdjustFramesLeft == 0)
             {
                 /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL register is set
@@ -716,21 +886,26 @@
                  */
                 if (HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_IOCE)
-                    hdaStreamPeriodAcquireInterrupt(pPeriod);
+                {
+                    pStream->State.cTransferPendingInterrupts++;
+
+                    AssertMsg(pStream->State.cTransferPendingInterrupts <= 32,
+                              ("Too many pending interrupts (%RU8) for stream #%RU8\n",
+                               pStream->State.cTransferPendingInterrupts, pStream->u8SD));
+                }
             }
 
             if (pStream->State.uCurBDLE == pStream->u16LVI)
             {
-                Assert(pStream->u32CBL == HDA_STREAM_REG(pThis, LPIB, pStream->u8SD));
-
                 pStream->State.uCurBDLE = 0;
-                hdaStreamUpdateLPIB(pStream, 0 /* LPIB */);
             }
             else
                 pStream->State.uCurBDLE++;
 
+            /* Fetch the next BDLE entry. */
             hdaBDLEFetch(pThis, pBDLE, pStream->u64BDLBase, pStream->State.uCurBDLE);
-
-            Log3Func(("[SD%RU8] Fetching: %R[bdle]\n", pStream->u8SD, pBDLE));
         }
+
+        /* Do the position adjustment accounting. */
+        pStream->State.cPosAdjustFramesLeft -= RT_MIN(pStream->State.cPosAdjustFramesLeft, cbDMA / HDA_FRAME_SIZE);
 
         if (RT_FAILURE(rc))
@@ -738,9 +913,57 @@
     }
 
-    if (hdaStreamPeriodIsComplete(pPeriod))
-    {
-        Log3Func(("[SD%RU8] Period complete -- Current: %R[bdle]\n", pStream->u8SD, &pStream->State.BDLE));
-
-        /* Set the stream's BCIS bit.
+    Log3Func(("[SD%RU8] cbToProcess=%RU32, cbProcessed=%RU32, cbLeft=%RU32, %R[bdle], rc=%Rrc\n",
+              pStream->u8SD, cbToProcess, cbProcessed, cbLeft, pBDLE, rc));
+
+    /* Sanity. */
+    Assert(cbProcessed % HDA_FRAME_SIZE == 0);
+    Assert(cbProcessed == cbToProcess);
+    Assert(cbLeft      == 0);
+
+    /* Only do the data accounting if we don't have to do any position
+     * adjustment anymore. */
+    if (pStream->State.cPosAdjustFramesLeft == 0)
+    {
+        hdaStreamPeriodInc(pPeriod, RT_MIN(cbProcessed / HDA_FRAME_SIZE, hdaStreamPeriodGetRemainingFrames(pPeriod)));
+
+        pStream->State.cbTransferProcessed += cbProcessed;
+    }
+
+    /* Make sure that we never report more stuff processed than initially announced. */
+    if (pStream->State.cbTransferProcessed > pStream->State.cbTransferSize)
+        pStream->State.cbTransferProcessed = pStream->State.cbTransferSize;
+
+    uint32_t cbTransferLeft     = pStream->State.cbTransferSize - pStream->State.cbTransferProcessed;
+    bool     fTransferComplete  = !cbTransferLeft;
+    uint64_t tsTransferNext     = 0;
+
+    if (fTransferComplete)
+    {
+        /**
+         * Try updating the wall clock.
+         *
+         * Note 1) Only certain guests (like Linux' snd_hda_intel) rely on the WALCLK register
+         *         in order to determine the correct timing of the sound device. Other guests
+         *         like Windows 7 + 10 (or even more exotic ones like Haiku) will completely
+         *         ignore this.
+         *
+         * Note 2) When updating the WALCLK register too often / early (or even in a non-monotonic
+         *         fashion) this *will* upset guest device drivers and will completely fuck up the
+         *         sound output. Running VLC on the guest will tell!
+         */
+        const bool fWalClkSet = hdaWalClkSet(pThis,
+                                               hdaWalClkGetCurrent(pThis)
+                                             + hdaStreamPeriodFramesToWalClk(pPeriod, pStream->State.cbTransferProcessed / HDA_FRAME_SIZE),
+                                             false /* fForce */);
+        RT_NOREF(fWalClkSet);
+    }
+
+    /* Does the period have any interrupts outstanding? */
+    if (pStream->State.cTransferPendingInterrupts)
+    {
+        Log3Func(("[SD%RU8] Scheduling interrupt\n", pStream->u8SD));
+
+        /**
+         * Set the stream's BCIS bit.
          *
          * Note: This only must be done if the whole period is complete, and not if only
@@ -750,44 +973,56 @@
          * 2) reads SDSTS (with BCIS set) and then 3) too early reads a (wrong) WALCLK value.
          *
-         * snd_hda_intel on Linux will tell. */
+         * snd_hda_intel on Linux will tell.
+         */
         HDA_STREAM_REG(pThis, STS, pStream->u8SD) |= HDA_SDSTS_BCIS;
 
-        /* Try updating the wall clock. */
-        const uint64_t u64WalClk  = hdaStreamPeriodGetAbsElapsedWalClk(pPeriod);
-        const bool     fWalClkSet = hdaWalClkSet(pThis, u64WalClk, false /* fForce */);
-
-        /* Does the period have any interrupts outstanding? */
-        if (hdaStreamPeriodNeedsInterrupt(pPeriod))
-        {
-            if (fWalClkSet)
-            {
-                Log3Func(("[SD%RU8] Set WALCLK to %RU64, triggering interrupt\n", pStream->u8SD, u64WalClk));
-
-                /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
-                 * ending / beginning a period. */
+        /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
+         * ending / beginning a period. */
 #ifndef DEBUG
-                hdaProcessInterrupt(pThis);
+        hdaProcessInterrupt(pThis);
 #else
-                hdaProcessInterrupt(pThis, __FUNCTION__);
+        hdaProcessInterrupt(pThis, __FUNCTION__);
 #endif
-            }
+        pStream->State.cTransferPendingInterrupts--;
+    }
+    else /* Transfer still in-flight -- schedule the next timing slot. */
+    {
+        uint32_t cbTransferNext = cbTransferLeft;
+
+        /* No data left to transfer anymore or do we have more data left
+         * than we can transfer per timing slot? Clamp. */
+        if (   !cbTransferNext
+            || cbTransferNext > pStream->State.cbTransferChunk)
+        {
+            cbTransferNext = pStream->State.cbTransferChunk;
         }
-        else
-        {
-            /* End the period first ... */
-            hdaStreamPeriodEnd(pPeriod);
-
-            /* ... and immediately begin the next one. */
-            hdaStreamPeriodBegin(pPeriod, hdaWalClkGetCurrent(pThis));
-        }
-    }
+
+        tsTransferNext = tsNow + (cbTransferNext * pStream->State.cTicksPerByte);
+    }
+
+    /* If we need to do another transfer, (re-)arm the device timer.  */
+    if (tsTransferNext) /* Can be 0 if no next transfer is needed. */
+    {
+        Log3Func(("[SD%RU8] Scheduling timer\n", pStream->u8SD));
+
+        TMTimerUnlock(pThis->pTimer);
+
+        hdaTimerSet(pThis, tsTransferNext, false /* fForce */);
+
+        TMTimerLock(pThis->pTimer, VINF_SUCCESS);
+
+        pStream->State.tsTransferNext = tsTransferNext;
+    }
+
+    pStream->State.tsTransferLast = tsNow;
+
+    Log3Func(("[SD%RU8] cbTransferLeft=%RU32 -- %RU64/%RU64\n",
+              pStream->u8SD, cbTransferLeft, pStream->State.cbTransferProcessed, pStream->State.cbTransferSize));
+    Log3Func(("[SD%RU8] fTransferComplete=%RTbool, cTransferPendingInterrupts=%RU8\n",
+              pStream->u8SD, fTransferComplete, pStream->State.cTransferPendingInterrupts));
+    Log3Func(("[SD%RU8] tsNow=%RU64, tsTransferNext=%RU64 (in %RU64 ticks)\n",
+              pStream->u8SD, tsNow, tsTransferNext, tsTransferNext - tsNow));
 
     hdaStreamPeriodUnlock(pPeriod);
-
-    Log3Func(("[SD%RU8] Returning %Rrc ==========================================\n", pStream->u8SD, rc));
-
-    if (RT_FAILURE(rc))
-        LogFunc(("[SD%RU8] Failed with rc=%Rrcc\n", pStream->u8SD, rc));
-
     hdaStreamUnlock(pStream);
 
@@ -862,5 +1097,5 @@
 
             /* When running synchronously, update the associated sink here.
-             * Otherwise this will be done in the device timer. */
+             * Otherwise this will be done in the stream's dedicated async I/O thread. */
             rc2 = AudioMixerSinkUpdate(pSink);
             AssertRC(rc2);
@@ -875,9 +1110,4 @@
         if (fInTimer)
         {
-            rc2 = hdaStreamAsyncIONotify(pStream);
-            AssertRC(rc2);
-        }
-        else
-        {
 #endif
             rc2 = AudioMixerSinkUpdate(pSink);
@@ -885,40 +1115,49 @@
 
             /* Is the sink ready to be read (host input data) from? If so, by how much? */
-            const uint32_t cbReadable = AudioMixerSinkGetReadable(pSink);
-
-            /* How much (guest input) data is free at the moment? */
-            uint32_t cbFree = hdaStreamGetFree(pStream);
-
-            Log3Func(("[SD%RU8] cbReadable=%RU32, cbFree=%RU32\n", pStream->u8SD, cbReadable, cbFree));
-
-            /* Do not read more than the sink can provide at the moment.
-             * The host sets the overall pace. */
-            if (cbFree > cbReadable)
-                cbFree = cbReadable;
-
-            if (cbFree)
-            {
-                /* Write (guest input) data to the stream which was read from stream's sink before. */
-                rc2 = hdaStreamWrite(pStream, cbFree, NULL /* pcbWritten */);
-                AssertRC(rc2);
-            }
-#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
-        }
-#endif
-
-#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
-        if (fInTimer)
-        {
-#endif
+            uint32_t cbReadable = AudioMixerSinkGetReadable(pSink);
+
+            Log3Func(("[SD%RU8] cbReadable=%RU32\n", pStream->u8SD, cbReadable));
+
+            if (cbReadable)
+            {
+                uint8_t abFIFO[HDA_FIFO_MAX + 1];
+                while (cbReadable)
+                {
+                    uint32_t cbRead;
+                    rc2 = AudioMixerSinkRead(pSink, AUDMIXOP_COPY,
+                                             abFIFO, RT_MIN(cbReadable, (uint32_t)sizeof(abFIFO)), &cbRead);
+                    AssertRCBreak(rc2);
+
+                    /* Write (guest input) data to the stream which was read from stream's sink before. */
+                    rc2 = hdaStreamWrite(pStream, abFIFO, cbRead, NULL /* pcbWritten */);
+                    AssertRCBreak(rc2);
+
+                    Assert(cbReadable >= cbRead);
+                    cbReadable -= cbRead;
+                }
+            }
+        #if 0
+            else /* Send silence as input. */
+            {
+                cbReadable = pStream->State.cbTransferSize - pStream->State.cbTransferProcessed;
+
+                Log3Func(("[SD%RU8] Sending silence (%RU32 bytes)\n", pStream->u8SD, cbReadable));
+
+                if (cbReadable)
+                {
+                    rc2 = hdaStreamWrite(pStream, NULL /* Silence */, cbReadable, NULL /* pcbWritten */);
+                    AssertRC(rc2);
+                }
+            }
+        #endif
+
             const uint32_t cbToTransfer = hdaStreamGetUsed(pStream);
             if (cbToTransfer)
             {
-                /* When running synchronously, do the DMA data transfers here.
-                 * Otherwise this will be done in the stream's async I/O thread. */
                 rc2 = hdaStreamTransfer(pStream, cbToTransfer);
                 AssertRC(rc2);
             }
 #ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
-        }
+        } /* fInTimer */
 #endif
     }
Index: /trunk/src/VBox/Devices/Audio/HDAStream.h
===================================================================
--- /trunk/src/VBox/Devices/Audio/HDAStream.h	(revision 69918)
+++ /trunk/src/VBox/Devices/Audio/HDAStream.h	(revision 69919)
@@ -109,7 +109,31 @@
     /** Circular buffer (FIFO) for holding DMA'ed data. */
     R3PTRTYPE(PRTCIRCBUF)   pCircBuf;
-    /** Timestamp of the last success DMA data transfer.
-     *  Used to calculate the time actually elapsed between two transfers. */
-    uint64_t                uTimerTS;
+    /** Timestamp of the last DMA data transfer. */
+    uint64_t                tsTransferLast;
+    /** Timestamp 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;
+    /** Total transfer size (in bytes) of a transfer period. */
+    uint32_t                cbTransferSize;
+    /** Transfer chunk size (in bytes) of a transfer period. */
+    uint32_t                cbTransferChunk;
+    /** How many bytes already have been processed in within
+     *  the current transfer period. */
+    uint32_t                cbTransferProcessed;
+    /** How many interrupts are pending due to
+     *  BDLE interrupt-on-completion (IOC) bits set. */
+    uint8_t                 cTransferPendingInterrupts;
+    uint8_t                 Padding1[4];
+    /** How many audio data frames are left to be processed
+     *  for the position adjustment handling.
+     *
+     *  0 if position adjustment handling is done or inactive. */
+    uint16_t                cPosAdjustFramesLeft;
+    uint8_t                 Padding2[2];
+    /** (Virtual) clock ticks per byte. */
+    uint64_t                cTicksPerByte;
+    /** (Virtual) clock ticks per transfer. */
+    uint64_t                cTransferTicks;
     /** The stream's period. Need for timing. */
     HDASTREAMPERIOD         Period;
@@ -122,5 +146,5 @@
 #endif
     /** Unused, padding. */
-    uint8_t                 Padding1[3];
+    uint8_t                 Padding3[3];
 } HDASTREAMSTATE, *PHDASTREAMSTATE;
 
@@ -193,12 +217,15 @@
 void              hdaStreamReset(PHDASTATE pThis, PHDASTREAM pStream, uint8_t uSD);
 int               hdaStreamEnable(PHDASTREAM pStream, bool fEnable);
+uint32_t          hdaStreamGetPosition(PHDASTATE pThis, PHDASTREAM pStream);
+void              hdaStreamSetPosition(PHDASTREAM pStream, uint32_t u32LPIB);
+uint32_t          hdaStreamGetFree(PHDASTREAM pStream);
 uint32_t          hdaStreamGetUsed(PHDASTREAM pStream);
-uint32_t          hdaStreamGetFree(PHDASTREAM pStream);
+bool              hdaStreamTransferIsScheduled(PHDASTREAM pStream);
+uint64_t          hdaStreamTransferGetNext(PHDASTREAM pStream);
 int               hdaStreamTransfer(PHDASTREAM pStream, uint32_t cbToProcessMax);
-uint32_t          hdaStreamUpdateLPIB(PHDASTREAM pStream, uint32_t u32LPIB);
 void              hdaStreamLock(PHDASTREAM pStream);
 void              hdaStreamUnlock(PHDASTREAM pStream);
 int               hdaStreamRead(PHDASTREAM pStream, uint32_t cbToRead, uint32_t *pcbRead);
-int               hdaStreamWrite(PHDASTREAM pStream, uint32_t cbToWrite, uint32_t *pcbWritten);
+int               hdaStreamWrite(PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten);
 void              hdaStreamUpdate(PHDASTREAM pStream, bool fAsync);
 # ifdef HDA_USE_DMA_ACCESS_HANDLER
Index: /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp
===================================================================
--- /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 69918)
+++ /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 69919)
@@ -1877,5 +1877,4 @@
 #ifndef VBOX_WITH_AUDIO_CALLBACKS
     GEN_CHECK_OFF(HDASTATE, pTimer);
-    GEN_CHECK_OFF(HDASTATE, cTimerTicks);
 #endif
 #ifdef VBOX_WITH_STATISTICS
@@ -1900,4 +1899,5 @@
     GEN_CHECK_OFF(HDASTATE, u64WalClk);
     GEN_CHECK_OFF(HDASTATE, u16RespIntCnt);
+    GEN_CHECK_OFF(HDASTATE, cPosAdjustFrames);
     GEN_CHECK_OFF(HDASTATE, u8IRQL);
 
