[intel-hda] Update SRST bit behavior in the stream control register.

According to the documentation, the SRST bit in the {IOB}SDnCTL
register is sticky bit.  Software is supposed to write a 1 to this bit
in order to begin the process or resetting the stream hardware.
Hardware is supposed to report a 1 once it has run the complete reset
cycle.  At this point, software is supposed to write a 0 and (once
again) wait until the HW ack acknowledged a release from reset by
reporting a 0 in the readback of this bit.

Previously, QEMU's emulation of the hardware would reset the contents
of the Status and Control registers, but it would not report a 1 in
the reset bit.

Now, the emulated hardware will immediately acknowledge the reset by
reporting a 1 in the SRST bit and reset the values contained in all of
the stream descriptor registers.  Subsequent writes to the register
will be ignored until the SRST bit has been cleared.

See section 3.3.35 of the Intel High Definition Audio Specification
Rev 1.0a dated June 17, 2010

Change-Id: I4d4bfc6a7b75a014cc6145fc457a54f604d44a66
diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
index 6aa9097..16bcaab 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -486,6 +486,16 @@
     st->bp    = 0;
 }
 
+static void intel_hda_reset_bdl(IntelHDAState *d, IntelHDAStream *st)
+{
+    g_free(st->bpl);
+    st->bpl      = NULL;
+    st->bentries = 0;
+    st->bsize    = 0;
+    st->be       = 0;
+    st->bp       = 0;
+}
+
 static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running, bool output)
 {
     BusChild *kid;
@@ -575,27 +585,55 @@
     bool output = reg->stream >= 4;
     IntelHDAStream *st = d->st + reg->stream;
 
-    if (st->ctl & 0x01) {
-        /* reset */
-        dprint(d, 1, "st #%d: reset\n", reg->stream);
-        st->ctl = SD_STS_FIFO_READY << 24;
-    }
-    if ((st->ctl & 0x02) != (old & 0x02)) {
-        uint32_t stnr = (st->ctl >> 20) & 0x0f;
-        /* run bit flipped */
-        if (st->ctl & 0x02) {
-            /* start */
-            dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n",
-                   reg->stream, stnr, st->cbl);
-            intel_hda_parse_bdl(d, st);
-            intel_hda_notify_codecs(d, stnr, true, output);
-        } else {
-            /* stop */
-            dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr);
-            intel_hda_notify_codecs(d, stnr, false, output);
+    if ((st->ctl & 0x01) && (old & 0x01)) {
+        /* If the stream descriptor was in the reset state, and this update did
+         * not clear the reset bit, ignore the register write */
+        st->ctl = old;
+    } else {
+        if (st->ctl & 0x01) {
+            /* reset */
+            dprint(d, 1, "st #%d: reset\n", reg->stream);
+
+            /* Reset all writable register bits to their power on defaults.
+             * Mark the SRST bit as being set in the CTL register */
+            uint32_t stnr  = (st->ctl >> 20) & 0x0f;
+            st->ctl        = (SD_STS_FIFO_READY << 24) | 0x01;
+            st->lpib       = 0;
+            st->cbl        = 0;
+            st->lvi        = 0;
+            st->fmt        = 0;
+            st->bdlp_lbase = 0;
+            st->bdlp_ubase = 0;
+
+            /* In theory, no one should place the stream into reset while the
+             * RUN bit is set.  Docs say "The RUN bit must be cleared before
+             * SRST is asserted".  Behavior in this situation is undefined.
+             * QEMU's implementation chooses to 'magicically' stop all streams.
+             * */
+            if (old & 0x02) {
+                dprint(d, 1, "st #%d: WARNING SRST asserted while RUN set\n", reg->stream);
+                intel_hda_notify_codecs(d, stnr, false, output);
+                intel_hda_reset_bdl(d, st);
+            }
+        } else if ((st->ctl & 0x02) != (old & 0x02)) {
+            uint32_t stnr = (st->ctl >> 20) & 0x0f;
+            /* run bit flipped */
+            if (st->ctl & 0x02) {
+                /* start */
+                dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n",
+                       reg->stream, stnr, st->cbl);
+                intel_hda_parse_bdl(d, st);
+                intel_hda_notify_codecs(d, stnr, true, output);
+            } else {
+                /* stop */
+                dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr);
+                intel_hda_notify_codecs(d, stnr, false, output);
+                intel_hda_reset_bdl(d, st);
+            }
         }
+
+        intel_hda_update_irq(d);
     }
-    intel_hda_update_irq(d);
 }
 
 /* --------------------------------------------------------------------- */