]> Git Repo - qemu.git/blobdiff - hw/audio/hda-codec.c
Merge remote-tracking branch 'remotes/berrange/tags/qcrypto-next-pull-request' into...
[qemu.git] / hw / audio / hda-codec.c
index 9550c97e65e8b9a4d843a5436e5dfc6b4f96e181..617a1c1016b8ea06403493b2848cac69ff46ba16 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "qemu/osdep.h"
 #include "hw/hw.h"
 #include "hw/pci/pci.h"
 #include "intel-hda.h"
 #include "intel-hda-defs.h"
 #include "audio/audio.h"
+#include "trace.h"
 
 /* -------------------------------------------------------------------------- */
 
@@ -118,428 +120,16 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
 #define QEMU_HDA_AMP_NONE    (0)
 #define QEMU_HDA_AMP_STEPS   0x4a
 
-#ifdef CONFIG_MIXEMU
-# define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x12)
-# define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x22)
-# define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x32)
-# define QEMU_HDA_AMP_CAPS                                              \
-    (AC_AMPCAP_MUTE |                                                   \
-     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT)    |                \
-     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) |                \
-     (3                  << AC_AMPCAP_STEP_SIZE_SHIFT))
-#else
-# define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x11)
-# define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x21)
-# define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x31)
-# define QEMU_HDA_AMP_CAPS   QEMU_HDA_AMP_NONE
-#endif
-
-/* common: audio output widget */
-static const desc_param common_params_audio_dac[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_FORMAT_OVRD |
-                AC_WCAP_AMP_OVRD |
-                AC_WCAP_OUT_AMP |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_CAPS,
-    },
-};
-
-/* common: audio input widget */
-static const desc_param common_params_audio_adc[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_CONN_LIST |
-                AC_WCAP_FORMAT_OVRD |
-                AC_WCAP_AMP_OVRD |
-                AC_WCAP_IN_AMP |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_CONNLIST_LEN,
-        .val = 1,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_CAPS,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },
-};
-
-/* common: pin widget (line-out) */
-static const desc_param common_params_audio_lineout[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_CONN_LIST |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_PIN_CAP,
-        .val = AC_PINCAP_OUT,
-    },{
-        .id  = AC_PAR_CONNLIST_LEN,
-        .val = 1,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },
-};
-
-/* common: pin widget (line-in) */
-static const desc_param common_params_audio_linein[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_PIN_CAP,
-        .val = AC_PINCAP_IN,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },
-};
-
-/* output: root node */
-static const desc_param output_params_root[] = {
-    {
-        .id  = AC_PAR_VENDOR_ID,
-        .val = QEMU_HDA_ID_OUTPUT,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_OUTPUT,
-    },{
-        .id  = AC_PAR_REV_ID,
-        .val = 0x00100101,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00010001,
-    },
-};
-
-/* output: audio function */
-static const desc_param output_params_audio_func[] = {
-    {
-        .id  = AC_PAR_FUNCTION_TYPE,
-        .val = AC_GRP_AUDIO_FUNCTION,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_OUTPUT,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00020002,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_GPIO_CAP,
-        .val = 0,
-    },{
-        .id  = AC_PAR_AUDIO_FG_CAP,
-        .val = 0x00000808,
-    },{
-        .id  = AC_PAR_POWER_STATE,
-        .val = 0,
-    },
-};
-
-/* output: nodes */
-static const desc_node output_nodes[] = {
-    {
-        .nid     = AC_NODE_ROOT,
-        .name    = "root",
-        .params  = output_params_root,
-        .nparams = ARRAY_SIZE(output_params_root),
-    },{
-        .nid     = 1,
-        .name    = "func",
-        .params  = output_params_audio_func,
-        .nparams = ARRAY_SIZE(output_params_audio_func),
-    },{
-        .nid     = 2,
-        .name    = "dac",
-        .params  = common_params_audio_dac,
-        .nparams = ARRAY_SIZE(common_params_audio_dac),
-        .stindex = 0,
-    },{
-        .nid     = 3,
-        .name    = "out",
-        .params  = common_params_audio_lineout,
-        .nparams = ARRAY_SIZE(common_params_audio_lineout),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x10),
-        .pinctl  = AC_PINCTL_OUT_EN,
-        .conn    = (uint32_t[]) { 2 },
-    }
-};
-
-/* output: codec */
-static const desc_codec output = {
-    .name   = "output",
-    .iid    = QEMU_HDA_ID_OUTPUT,
-    .nodes  = output_nodes,
-    .nnodes = ARRAY_SIZE(output_nodes),
-};
-
-/* duplex: root node */
-static const desc_param duplex_params_root[] = {
-    {
-        .id  = AC_PAR_VENDOR_ID,
-        .val = QEMU_HDA_ID_DUPLEX,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_DUPLEX,
-    },{
-        .id  = AC_PAR_REV_ID,
-        .val = 0x00100101,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00010001,
-    },
-};
-
-/* duplex: audio function */
-static const desc_param duplex_params_audio_func[] = {
-    {
-        .id  = AC_PAR_FUNCTION_TYPE,
-        .val = AC_GRP_AUDIO_FUNCTION,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_DUPLEX,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00020004,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_GPIO_CAP,
-        .val = 0,
-    },{
-        .id  = AC_PAR_AUDIO_FG_CAP,
-        .val = 0x00000808,
-    },{
-        .id  = AC_PAR_POWER_STATE,
-        .val = 0,
-    },
-};
-
-/* duplex: nodes */
-static const desc_node duplex_nodes[] = {
-    {
-        .nid     = AC_NODE_ROOT,
-        .name    = "root",
-        .params  = duplex_params_root,
-        .nparams = ARRAY_SIZE(duplex_params_root),
-    },{
-        .nid     = 1,
-        .name    = "func",
-        .params  = duplex_params_audio_func,
-        .nparams = ARRAY_SIZE(duplex_params_audio_func),
-    },{
-        .nid     = 2,
-        .name    = "dac",
-        .params  = common_params_audio_dac,
-        .nparams = ARRAY_SIZE(common_params_audio_dac),
-        .stindex = 0,
-    },{
-        .nid     = 3,
-        .name    = "out",
-        .params  = common_params_audio_lineout,
-        .nparams = ARRAY_SIZE(common_params_audio_lineout),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x10),
-        .pinctl  = AC_PINCTL_OUT_EN,
-        .conn    = (uint32_t[]) { 2 },
-    },{
-        .nid     = 4,
-        .name    = "adc",
-        .params  = common_params_audio_adc,
-        .nparams = ARRAY_SIZE(common_params_audio_adc),
-        .stindex = 1,
-        .conn    = (uint32_t[]) { 5 },
-    },{
-        .nid     = 5,
-        .name    = "in",
-        .params  = common_params_audio_linein,
-        .nparams = ARRAY_SIZE(common_params_audio_linein),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_LINE_IN      << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x20),
-        .pinctl  = AC_PINCTL_IN_EN,
-    }
-};
-
-/* duplex: codec */
-static const desc_codec duplex = {
-    .name   = "duplex",
-    .iid    = QEMU_HDA_ID_DUPLEX,
-    .nodes  = duplex_nodes,
-    .nnodes = ARRAY_SIZE(duplex_nodes),
-};
-
-/* micro: root node */
-static const desc_param micro_params_root[] = {
-    {
-        .id  = AC_PAR_VENDOR_ID,
-        .val = QEMU_HDA_ID_MICRO,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_MICRO,
-    },{
-        .id  = AC_PAR_REV_ID,
-        .val = 0x00100101,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00010001,
-    },
-};
+#define   PARAM mixemu
+#define   HDA_MIXER
+#include "hda-codec-common.h"
 
-/* micro: audio function */
-static const desc_param micro_params_audio_func[] = {
-    {
-        .id  = AC_PAR_FUNCTION_TYPE,
-        .val = AC_GRP_AUDIO_FUNCTION,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_MICRO,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00020004,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_GPIO_CAP,
-        .val = 0,
-    },{
-        .id  = AC_PAR_AUDIO_FG_CAP,
-        .val = 0x00000808,
-    },{
-        .id  = AC_PAR_POWER_STATE,
-        .val = 0,
-    },
-};
-
-/* micro: nodes */
-static const desc_node micro_nodes[] = {
-    {
-        .nid     = AC_NODE_ROOT,
-        .name    = "root",
-        .params  = micro_params_root,
-        .nparams = ARRAY_SIZE(micro_params_root),
-    },{
-        .nid     = 1,
-        .name    = "func",
-        .params  = micro_params_audio_func,
-        .nparams = ARRAY_SIZE(micro_params_audio_func),
-    },{
-        .nid     = 2,
-        .name    = "dac",
-        .params  = common_params_audio_dac,
-        .nparams = ARRAY_SIZE(common_params_audio_dac),
-        .stindex = 0,
-    },{
-        .nid     = 3,
-        .name    = "out",
-        .params  = common_params_audio_lineout,
-        .nparams = ARRAY_SIZE(common_params_audio_lineout),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_SPEAKER      << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x10),
-        .pinctl  = AC_PINCTL_OUT_EN,
-        .conn    = (uint32_t[]) { 2 },
-    },{
-        .nid     = 4,
-        .name    = "adc",
-        .params  = common_params_audio_adc,
-        .nparams = ARRAY_SIZE(common_params_audio_adc),
-        .stindex = 1,
-        .conn    = (uint32_t[]) { 5 },
-    },{
-        .nid     = 5,
-        .name    = "in",
-        .params  = common_params_audio_linein,
-        .nparams = ARRAY_SIZE(common_params_audio_linein),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_MIC_IN       << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x20),
-        .pinctl  = AC_PINCTL_IN_EN,
-    }
-};
+#define   PARAM nomixemu
+#include  "hda-codec-common.h"
 
-/* micro: codec */
-static const desc_codec micro = {
-    .name   = "micro",
-    .iid    = QEMU_HDA_ID_MICRO,
-    .nodes  = micro_nodes,
-    .nnodes = ARRAY_SIZE(micro_nodes),
-};
+#define HDA_TIMER_TICKS (SCALE_MS)
+#define B_SIZE sizeof(st->buf)
+#define B_MASK (sizeof(st->buf) - 1)
 
 /* -------------------------------------------------------------------------- */
 
@@ -569,10 +159,18 @@ struct HDAAudioStream {
         SWVoiceIn *in;
         SWVoiceOut *out;
     } voice;
-    uint8_t buf[HDA_BUFFER_SIZE];
-    uint32_t bpos;
+    uint8_t compat_buf[HDA_BUFFER_SIZE];
+    uint32_t compat_bpos;
+    uint8_t buf[8192]; /* size must be power of two */
+    int64_t rpos;
+    int64_t wpos;
+    QEMUTimer *buft;
+    int64_t buft_start;
 };
 
+#define TYPE_HDA_AUDIO "hda-audio"
+#define HDA_AUDIO(obj) OBJECT_CHECK(HDAAudioState, (obj), TYPE_HDA_AUDIO)
+
 struct HDAAudioState {
     HDACodecDevice hda;
     const char *name;
@@ -585,55 +183,221 @@ struct HDAAudioState {
 
     /* properties */
     uint32_t debug;
+    bool     mixer;
+    bool     use_timer;
 };
 
+static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
+{
+    return 2LL * st->as.nchannels * st->as.freq;
+}
+
+static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
+{
+    int64_t limit = B_SIZE / 8;
+    int64_t corr = 0;
+
+    if (target_pos > limit) {
+        corr = HDA_TIMER_TICKS;
+    }
+    if (target_pos < -limit) {
+        corr = -HDA_TIMER_TICKS;
+    }
+    if (target_pos < -(2 * limit)) {
+        corr = -(4 * HDA_TIMER_TICKS);
+    }
+    if (corr == 0) {
+        return;
+    }
+
+    trace_hda_audio_adjust(st->node->name, target_pos);
+    st->buft_start += corr;
+}
+
+static void hda_audio_input_timer(void *opaque)
+{
+    HDAAudioStream *st = opaque;
+
+    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+    int64_t buft_start = st->buft_start;
+    int64_t wpos = st->wpos;
+    int64_t rpos = st->rpos;
+
+    int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start)
+                          / NANOSECONDS_PER_SECOND;
+    wanted_rpos &= -4; /* IMPORTANT! clip to frames */
+
+    if (wanted_rpos <= rpos) {
+        /* we already transmitted the data */
+        goto out_timer;
+    }
+
+    int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos);
+    while (to_transfer) {
+        uint32_t start = (rpos & B_MASK);
+        uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
+        int rc = hda_codec_xfer(
+                &st->state->hda, st->stream, false, st->buf + start, chunk);
+        if (!rc) {
+            break;
+        }
+        rpos += chunk;
+        to_transfer -= chunk;
+        st->rpos += chunk;
+    }
+
+out_timer:
+
+    if (st->running) {
+        timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
+    }
+}
+
 static void hda_audio_input_cb(void *opaque, int avail)
+{
+    HDAAudioStream *st = opaque;
+
+    int64_t wpos = st->wpos;
+    int64_t rpos = st->rpos;
+
+    int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail);
+
+    hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1)));
+
+    while (to_transfer) {
+        uint32_t start = (uint32_t) (wpos & B_MASK);
+        uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
+        uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk);
+        wpos += read;
+        to_transfer -= read;
+        st->wpos += read;
+        if (chunk != read) {
+            break;
+        }
+    }
+}
+
+static void hda_audio_output_timer(void *opaque)
+{
+    HDAAudioStream *st = opaque;
+
+    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+    int64_t buft_start = st->buft_start;
+    int64_t wpos = st->wpos;
+    int64_t rpos = st->rpos;
+
+    int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start)
+                          / NANOSECONDS_PER_SECOND;
+    wanted_wpos &= -4; /* IMPORTANT! clip to frames */
+
+    if (wanted_wpos <= wpos) {
+        /* we already received the data */
+        goto out_timer;
+    }
+
+    int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
+    while (to_transfer) {
+        uint32_t start = (wpos & B_MASK);
+        uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
+        int rc = hda_codec_xfer(
+                &st->state->hda, st->stream, true, st->buf + start, chunk);
+        if (!rc) {
+            break;
+        }
+        wpos += chunk;
+        to_transfer -= chunk;
+        st->wpos += chunk;
+    }
+
+out_timer:
+
+    if (st->running) {
+        timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
+    }
+}
+
+static void hda_audio_output_cb(void *opaque, int avail)
+{
+    HDAAudioStream *st = opaque;
+
+    int64_t wpos = st->wpos;
+    int64_t rpos = st->rpos;
+
+    int64_t to_transfer = audio_MIN(wpos - rpos, avail);
+
+    if (wpos - rpos == B_SIZE) {
+        /* drop buffer, reset timer adjust */
+        st->rpos = 0;
+        st->wpos = 0;
+        st->buft_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        trace_hda_audio_overrun(st->node->name);
+        return;
+    }
+
+    hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1));
+
+    while (to_transfer) {
+        uint32_t start = (uint32_t) (rpos & B_MASK);
+        uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
+        uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
+        rpos += written;
+        to_transfer -= written;
+        st->rpos += written;
+        if (chunk != written) {
+            break;
+        }
+    }
+}
+
+static void hda_audio_compat_input_cb(void *opaque, int avail)
 {
     HDAAudioStream *st = opaque;
     int recv = 0;
     int len;
     bool rc;
 
-    while (avail - recv >= sizeof(st->buf)) {
-        if (st->bpos != sizeof(st->buf)) {
-            len = AUD_read(st->voice.in, st->buf + st->bpos,
-                           sizeof(st->buf) - st->bpos);
-            st->bpos += len;
+    while (avail - recv >= sizeof(st->compat_buf)) {
+        if (st->compat_bpos != sizeof(st->compat_buf)) {
+            len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos,
+                           sizeof(st->compat_buf) - st->compat_bpos);
+            st->compat_bpos += len;
             recv += len;
-            if (st->bpos != sizeof(st->buf)) {
+            if (st->compat_bpos != sizeof(st->compat_buf)) {
                 break;
             }
         }
         rc = hda_codec_xfer(&st->state->hda, st->stream, false,
-                            st->buf, sizeof(st->buf));
+                            st->compat_buf, sizeof(st->compat_buf));
         if (!rc) {
             break;
         }
-        st->bpos = 0;
+        st->compat_bpos = 0;
     }
 }
 
-static void hda_audio_output_cb(void *opaque, int avail)
+static void hda_audio_compat_output_cb(void *opaque, int avail)
 {
     HDAAudioStream *st = opaque;
     int sent = 0;
     int len;
     bool rc;
 
-    while (avail - sent >= sizeof(st->buf)) {
-        if (st->bpos == sizeof(st->buf)) {
+    while (avail - sent >= sizeof(st->compat_buf)) {
+        if (st->compat_bpos == sizeof(st->compat_buf)) {
             rc = hda_codec_xfer(&st->state->hda, st->stream, true,
-                                st->buf, sizeof(st->buf));
+                                st->compat_buf, sizeof(st->compat_buf));
             if (!rc) {
                 break;
             }
-            st->bpos = 0;
+            st->compat_bpos = 0;
         }
-        len = AUD_write(st->voice.out, st->buf + st->bpos,
-                        sizeof(st->buf) - st->bpos);
-        st->bpos += len;
+        len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos,
+                        sizeof(st->compat_buf) - st->compat_bpos);
+        st->compat_bpos += len;
         sent += len;
-        if (st->bpos != sizeof(st->buf)) {
+        if (st->compat_bpos != sizeof(st->compat_buf)) {
             break;
         }
     }
@@ -648,8 +412,18 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running)
         return;
     }
     st->running = running;
-    dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
-           st->running ? "on" : "off", st->stream);
+    trace_hda_audio_running(st->node->name, st->stream, st->running);
+    if (st->state->use_timer) {
+        if (running) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            st->rpos = 0;
+            st->wpos = 0;
+            st->buft_start = now;
+            timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
+        } else {
+            timer_del(st->buft);
+        }
+    }
     if (st->output) {
         AUD_set_active_out(st->voice.out, st->running);
     } else {
@@ -673,6 +447,9 @@ static void hda_audio_set_amp(HDAAudioStream *st)
     left = left * 255 / QEMU_HDA_AMP_STEPS;
     right = right * 255 / QEMU_HDA_AMP_STEPS;
 
+    if (!st->state->mixer) {
+        return;
+    }
     if (st->output) {
         AUD_set_volume_out(st->voice.out, muted, left, right);
     } else {
@@ -682,28 +459,42 @@ static void hda_audio_set_amp(HDAAudioStream *st)
 
 static void hda_audio_setup(HDAAudioStream *st)
 {
+    bool use_timer = st->state->use_timer;
+    audio_callback_fn cb;
+
     if (st->node == NULL) {
         return;
     }
 
-    dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
-           st->node->name, st->as.nchannels,
-           fmt2name[st->as.fmt], st->as.freq);
+    trace_hda_audio_format(st->node->name, st->as.nchannels,
+                           fmt2name[st->as.fmt], st->as.freq);
 
     if (st->output) {
+        if (use_timer) {
+            cb = hda_audio_output_cb;
+            st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                    hda_audio_output_timer, st);
+        } else {
+            cb = hda_audio_compat_output_cb;
+        }
         st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
-                                     st->node->name, st,
-                                     hda_audio_output_cb, &st->as);
+                                     st->node->name, st, cb, &st->as);
     } else {
+        if (use_timer) {
+            cb = hda_audio_input_cb;
+            st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                    hda_audio_input_timer, st);
+        } else {
+            cb = hda_audio_compat_input_cb;
+        }
         st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
-                                   st->node->name, st,
-                                   hda_audio_input_cb, &st->as);
+                                   st->node->name, st, cb, &st->as);
     }
 }
 
 static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
 {
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    HDAAudioState *a = HDA_AUDIO(hda);
     HDAAudioStream *st;
     const desc_node *node = NULL;
     const desc_param *param;
@@ -724,7 +515,7 @@ static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
         goto fail;
     }
     dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
-           __FUNCTION__, nid, node->name, verb, payload);
+           __func__, nid, node->name, verb, payload);
 
     switch (verb) {
     /* all nodes */
@@ -857,13 +648,13 @@ static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
 
 fail:
     dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
-           __FUNCTION__, nid, node ? node->name : "?", verb, payload);
+           __func__, nid, node ? node->name : "?", verb, payload);
     hda_codec_response(hda, true, 0);
 }
 
 static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
 {
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    HDAAudioState *a = HDA_AUDIO(hda);
     int s;
 
     a->running_compat[stnr] = running;
@@ -884,7 +675,7 @@ static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, b
 
 static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
 {
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    HDAAudioState *a = HDA_AUDIO(hda);
     HDAAudioStream *st;
     const desc_node *node;
     const desc_param *param;
@@ -892,14 +683,15 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
 
     a->desc = desc;
     a->name = object_get_typename(OBJECT(a));
-    dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
+    dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad);
 
     AUD_register_card("hda", &a->card);
     for (i = 0; i < a->desc->nnodes; i++) {
         node = a->desc->nodes + i;
         param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
-        if (NULL == param)
+        if (param == NULL) {
             continue;
+        }
         type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
         switch (type) {
         case AC_WID_AUD_OUT:
@@ -912,7 +704,7 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
                 /* unmute output by default */
                 st->gain_left = QEMU_HDA_AMP_STEPS;
                 st->gain_right = QEMU_HDA_AMP_STEPS;
-                st->bpos = sizeof(st->buf);
+                st->compat_bpos = sizeof(st->compat_buf);
                 st->output = true;
             } else {
                 st->output = false;
@@ -927,18 +719,21 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
     return 0;
 }
 
-static int hda_audio_exit(HDACodecDevice *hda)
+static void hda_audio_exit(HDACodecDevice *hda)
 {
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    HDAAudioState *a = HDA_AUDIO(hda);
     HDAAudioStream *st;
     int i;
 
-    dprint(a, 1, "%s\n", __FUNCTION__);
+    dprint(a, 1, "%s\n", __func__);
     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
         st = a->st + i;
         if (st->node == NULL) {
             continue;
         }
+        if (a->use_timer) {
+            timer_del(st->buft);
+        }
         if (st->output) {
             AUD_close_out(&a->card, st->voice.out);
         } else {
@@ -946,7 +741,6 @@ static int hda_audio_exit(HDACodecDevice *hda)
         }
     }
     AUD_remove_card(&a->card);
-    return 0;
 }
 
 static int hda_audio_post_load(void *opaque, int version)
@@ -955,7 +749,7 @@ static int hda_audio_post_load(void *opaque, int version)
     HDAAudioStream *st;
     int i;
 
-    dprint(a, 1, "%s\n", __FUNCTION__);
+    dprint(a, 1, "%s\n", __func__);
     if (version == 1) {
         /* assume running_compat[] is for output streams */
         for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
@@ -974,10 +768,45 @@ static int hda_audio_post_load(void *opaque, int version)
     return 0;
 }
 
+static void hda_audio_reset(DeviceState *dev)
+{
+    HDAAudioState *a = HDA_AUDIO(dev);
+    HDAAudioStream *st;
+    int i;
+
+    dprint(a, 1, "%s\n", __func__);
+    for (i = 0; i < ARRAY_SIZE(a->st); i++) {
+        st = a->st + i;
+        if (st->node != NULL) {
+            hda_audio_set_running(st, false);
+        }
+    }
+}
+
+static bool vmstate_hda_audio_stream_buf_needed(void *opaque)
+{
+    HDAAudioStream *st = opaque;
+    return st->state && st->state->use_timer;
+}
+
+static const VMStateDescription vmstate_hda_audio_stream_buf = {
+    .name = "hda-audio-stream/buffer",
+    .version_id = 1,
+    .needed = vmstate_hda_audio_stream_buf_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_BUFFER(buf, HDAAudioStream),
+        VMSTATE_INT64(rpos, HDAAudioStream),
+        VMSTATE_INT64(wpos, HDAAudioStream),
+        VMSTATE_TIMER_PTR(buft, HDAAudioStream),
+        VMSTATE_INT64(buft_start, HDAAudioStream),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_hda_audio_stream = {
     .name = "hda-audio-stream",
     .version_id = 1,
-    .fields = (VMStateField []) {
+    .fields = (VMStateField[]) {
         VMSTATE_UINT32(stream, HDAAudioStream),
         VMSTATE_UINT32(channel, HDAAudioStream),
         VMSTATE_UINT32(format, HDAAudioStream),
@@ -985,9 +814,13 @@ static const VMStateDescription vmstate_hda_audio_stream = {
         VMSTATE_UINT32(gain_right, HDAAudioStream),
         VMSTATE_BOOL(mute_left, HDAAudioStream),
         VMSTATE_BOOL(mute_right, HDAAudioStream),
-        VMSTATE_UINT32(bpos, HDAAudioStream),
-        VMSTATE_BUFFER(buf, HDAAudioStream),
+        VMSTATE_UINT32(compat_bpos, HDAAudioStream),
+        VMSTATE_BUFFER(compat_buf, HDAAudioStream),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &vmstate_hda_audio_stream_buf,
+        NULL
     }
 };
 
@@ -995,7 +828,7 @@ static const VMStateDescription vmstate_hda_audio = {
     .name = "hda-audio",
     .version_id = 2,
     .post_load = hda_audio_post_load,
-    .fields = (VMStateField []) {
+    .fields = (VMStateField[]) {
         VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
                              vmstate_hda_audio_stream,
                              HDAAudioStream),
@@ -1006,43 +839,78 @@ static const VMStateDescription vmstate_hda_audio = {
 };
 
 static Property hda_audio_properties[] = {
-    DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
+    DEFINE_PROP_UINT32("debug", HDAAudioState, debug,   0),
+    DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer,  true),
+    DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer,  true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
 static int hda_audio_init_output(HDACodecDevice *hda)
 {
-    return hda_audio_init(hda, &output);
+    HDAAudioState *a = HDA_AUDIO(hda);
+
+    if (!a->mixer) {
+        return hda_audio_init(hda, &output_nomixemu);
+    } else {
+        return hda_audio_init(hda, &output_mixemu);
+    }
 }
 
 static int hda_audio_init_duplex(HDACodecDevice *hda)
 {
-    return hda_audio_init(hda, &duplex);
+    HDAAudioState *a = HDA_AUDIO(hda);
+
+    if (!a->mixer) {
+        return hda_audio_init(hda, &duplex_nomixemu);
+    } else {
+        return hda_audio_init(hda, &duplex_mixemu);
+    }
 }
 
 static int hda_audio_init_micro(HDACodecDevice *hda)
 {
-    return hda_audio_init(hda, &micro);
+    HDAAudioState *a = HDA_AUDIO(hda);
+
+    if (!a->mixer) {
+        return hda_audio_init(hda, &micro_nomixemu);
+    } else {
+        return hda_audio_init(hda, &micro_mixemu);
+    }
 }
 
-static void hda_audio_output_class_init(ObjectClass *klass, void *data)
+static void hda_audio_base_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
 
-    k->init = hda_audio_init_output;
     k->exit = hda_audio_exit;
     k->command = hda_audio_command;
     k->stream = hda_audio_stream;
     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
-    dc->desc = "HDA Audio Codec, output-only (line-out)";
+    dc->reset = hda_audio_reset;
     dc->vmsd = &vmstate_hda_audio;
     dc->props = hda_audio_properties;
 }
 
+static const TypeInfo hda_audio_info = {
+    .name          = TYPE_HDA_AUDIO,
+    .parent        = TYPE_HDA_CODEC_DEVICE,
+    .class_init    = hda_audio_base_class_init,
+    .abstract      = true,
+};
+
+static void hda_audio_output_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
+
+    k->init = hda_audio_init_output;
+    dc->desc = "HDA Audio Codec, output-only (line-out)";
+}
+
 static const TypeInfo hda_audio_output_info = {
     .name          = "hda-output",
-    .parent        = TYPE_HDA_CODEC_DEVICE,
+    .parent        = TYPE_HDA_AUDIO,
     .instance_size = sizeof(HDAAudioState),
     .class_init    = hda_audio_output_class_init,
 };
@@ -1053,18 +921,12 @@ static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
 
     k->init = hda_audio_init_duplex;
-    k->exit = hda_audio_exit;
-    k->command = hda_audio_command;
-    k->stream = hda_audio_stream;
-    set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
-    dc->vmsd = &vmstate_hda_audio;
-    dc->props = hda_audio_properties;
 }
 
 static const TypeInfo hda_audio_duplex_info = {
     .name          = "hda-duplex",
-    .parent        = TYPE_HDA_CODEC_DEVICE,
+    .parent        = TYPE_HDA_AUDIO,
     .instance_size = sizeof(HDAAudioState),
     .class_init    = hda_audio_duplex_class_init,
 };
@@ -1075,24 +937,19 @@ static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
 
     k->init = hda_audio_init_micro;
-    k->exit = hda_audio_exit;
-    k->command = hda_audio_command;
-    k->stream = hda_audio_stream;
-    set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
-    dc->vmsd = &vmstate_hda_audio;
-    dc->props = hda_audio_properties;
 }
 
 static const TypeInfo hda_audio_micro_info = {
     .name          = "hda-micro",
-    .parent        = TYPE_HDA_CODEC_DEVICE,
+    .parent        = TYPE_HDA_AUDIO,
     .instance_size = sizeof(HDAAudioState),
     .class_init    = hda_audio_micro_class_init,
 };
 
 static void hda_audio_register_types(void)
 {
+    type_register_static(&hda_audio_info);
     type_register_static(&hda_audio_output_info);
     type_register_static(&hda_audio_duplex_info);
     type_register_static(&hda_audio_micro_info);
This page took 0.057693 seconds and 4 git commands to generate.