xref: /openbmc/qemu/hw/audio/hda-codec.c (revision a7b7953d8de6f01833e759d8c19a862f438caa89)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * Copyright (C) 2010 Red Hat, Inc.
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * written by Gerd Hoffmann <kraxel@redhat.com>
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * This program is free software; you can redistribute it and/or
749ab747fSPaolo Bonzini  * modify it under the terms of the GNU General Public License as
849ab747fSPaolo Bonzini  * published by the Free Software Foundation; either version 2 or
949ab747fSPaolo Bonzini  * (at your option) version 3 of the License.
1049ab747fSPaolo Bonzini  *
1149ab747fSPaolo Bonzini  * This program is distributed in the hope that it will be useful,
1249ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1349ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1449ab747fSPaolo Bonzini  * GNU General Public License for more details.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * You should have received a copy of the GNU General Public License
1749ab747fSPaolo Bonzini  * along with this program; if not, see <http://www.gnu.org/licenses/>.
1849ab747fSPaolo Bonzini  */
1949ab747fSPaolo Bonzini 
206086a565SPeter Maydell #include "qemu/osdep.h"
2149ab747fSPaolo Bonzini #include "hw/pci/pci.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
2347b43a1fSPaolo Bonzini #include "intel-hda.h"
24d6454270SMarkus Armbruster #include "migration/vmstate.h"
2574e8593eSVolker Rümelin #include "qemu/host-utils.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
2747b43a1fSPaolo Bonzini #include "intel-hda-defs.h"
2849ab747fSPaolo Bonzini #include "audio/audio.h"
290a373bb3SGerd Hoffmann #include "trace.h"
30db1015e9SEduardo Habkost #include "qom/object.h"
3149ab747fSPaolo Bonzini 
3249ab747fSPaolo Bonzini /* -------------------------------------------------------------------------- */
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini typedef struct desc_param {
3549ab747fSPaolo Bonzini     uint32_t id;
3649ab747fSPaolo Bonzini     uint32_t val;
3749ab747fSPaolo Bonzini } desc_param;
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini typedef struct desc_node {
4049ab747fSPaolo Bonzini     uint32_t nid;
4149ab747fSPaolo Bonzini     const char *name;
4249ab747fSPaolo Bonzini     const desc_param *params;
4349ab747fSPaolo Bonzini     uint32_t nparams;
4449ab747fSPaolo Bonzini     uint32_t config;
4549ab747fSPaolo Bonzini     uint32_t pinctl;
4649ab747fSPaolo Bonzini     uint32_t *conn;
4749ab747fSPaolo Bonzini     uint32_t stindex;
4849ab747fSPaolo Bonzini } desc_node;
4949ab747fSPaolo Bonzini 
5049ab747fSPaolo Bonzini typedef struct desc_codec {
5149ab747fSPaolo Bonzini     const char *name;
5249ab747fSPaolo Bonzini     uint32_t iid;
5349ab747fSPaolo Bonzini     const desc_node *nodes;
5449ab747fSPaolo Bonzini     uint32_t nnodes;
5549ab747fSPaolo Bonzini } desc_codec;
5649ab747fSPaolo Bonzini 
hda_codec_find_param(const desc_node * node,uint32_t id)5749ab747fSPaolo Bonzini static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
5849ab747fSPaolo Bonzini {
5949ab747fSPaolo Bonzini     int i;
6049ab747fSPaolo Bonzini 
6149ab747fSPaolo Bonzini     for (i = 0; i < node->nparams; i++) {
6249ab747fSPaolo Bonzini         if (node->params[i].id == id) {
6349ab747fSPaolo Bonzini             return &node->params[i];
6449ab747fSPaolo Bonzini         }
6549ab747fSPaolo Bonzini     }
6649ab747fSPaolo Bonzini     return NULL;
6749ab747fSPaolo Bonzini }
6849ab747fSPaolo Bonzini 
hda_codec_find_node(const desc_codec * codec,uint32_t nid)6949ab747fSPaolo Bonzini static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
7049ab747fSPaolo Bonzini {
7149ab747fSPaolo Bonzini     int i;
7249ab747fSPaolo Bonzini 
7349ab747fSPaolo Bonzini     for (i = 0; i < codec->nnodes; i++) {
7449ab747fSPaolo Bonzini         if (codec->nodes[i].nid == nid) {
7549ab747fSPaolo Bonzini             return &codec->nodes[i];
7649ab747fSPaolo Bonzini         }
7749ab747fSPaolo Bonzini     }
7849ab747fSPaolo Bonzini     return NULL;
7949ab747fSPaolo Bonzini }
8049ab747fSPaolo Bonzini 
hda_codec_parse_fmt(uint32_t format,struct audsettings * as)8149ab747fSPaolo Bonzini static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
8249ab747fSPaolo Bonzini {
8349ab747fSPaolo Bonzini     if (format & AC_FMT_TYPE_NON_PCM) {
8449ab747fSPaolo Bonzini         return;
8549ab747fSPaolo Bonzini     }
8649ab747fSPaolo Bonzini 
8749ab747fSPaolo Bonzini     as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
8849ab747fSPaolo Bonzini 
8949ab747fSPaolo Bonzini     switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
9049ab747fSPaolo Bonzini     case 1: as->freq *= 2; break;
9149ab747fSPaolo Bonzini     case 2: as->freq *= 3; break;
9249ab747fSPaolo Bonzini     case 3: as->freq *= 4; break;
9349ab747fSPaolo Bonzini     }
9449ab747fSPaolo Bonzini 
9549ab747fSPaolo Bonzini     switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
9649ab747fSPaolo Bonzini     case 1: as->freq /= 2; break;
9749ab747fSPaolo Bonzini     case 2: as->freq /= 3; break;
9849ab747fSPaolo Bonzini     case 3: as->freq /= 4; break;
9949ab747fSPaolo Bonzini     case 4: as->freq /= 5; break;
10049ab747fSPaolo Bonzini     case 5: as->freq /= 6; break;
10149ab747fSPaolo Bonzini     case 6: as->freq /= 7; break;
10249ab747fSPaolo Bonzini     case 7: as->freq /= 8; break;
10349ab747fSPaolo Bonzini     }
10449ab747fSPaolo Bonzini 
10549ab747fSPaolo Bonzini     switch (format & AC_FMT_BITS_MASK) {
10685bc5852SKővágó, Zoltán     case AC_FMT_BITS_8:  as->fmt = AUDIO_FORMAT_S8;  break;
10785bc5852SKővágó, Zoltán     case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break;
10885bc5852SKővágó, Zoltán     case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break;
10949ab747fSPaolo Bonzini     }
11049ab747fSPaolo Bonzini 
11149ab747fSPaolo Bonzini     as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
11249ab747fSPaolo Bonzini }
11349ab747fSPaolo Bonzini 
11449ab747fSPaolo Bonzini /* -------------------------------------------------------------------------- */
11549ab747fSPaolo Bonzini /*
11649ab747fSPaolo Bonzini  * HDA codec descriptions
11749ab747fSPaolo Bonzini  */
11849ab747fSPaolo Bonzini 
11949ab747fSPaolo Bonzini /* some defines */
12049ab747fSPaolo Bonzini 
12149ab747fSPaolo Bonzini #define QEMU_HDA_ID_VENDOR  0x1af4
12249ab747fSPaolo Bonzini #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
12349ab747fSPaolo Bonzini                               0x1fc /* 16 -> 96 kHz */)
12449ab747fSPaolo Bonzini #define QEMU_HDA_AMP_NONE    (0)
12549ab747fSPaolo Bonzini #define QEMU_HDA_AMP_STEPS   0x4a
12649ab747fSPaolo Bonzini 
1272690e61eSBandan Das #define   PARAM mixemu
1282690e61eSBandan Das #define   HDA_MIXER
1297953793cSBandan Das #include "hda-codec-common.h"
1302690e61eSBandan Das 
1312690e61eSBandan Das #define   PARAM nomixemu
1322690e61eSBandan Das #include  "hda-codec-common.h"
1332690e61eSBandan Das 
134280c1e1cSGerd Hoffmann #define HDA_TIMER_TICKS (SCALE_MS)
135280c1e1cSGerd Hoffmann #define B_SIZE sizeof(st->buf)
136280c1e1cSGerd Hoffmann #define B_MASK (sizeof(st->buf) - 1)
137280c1e1cSGerd Hoffmann 
13849ab747fSPaolo Bonzini /* -------------------------------------------------------------------------- */
13949ab747fSPaolo Bonzini 
14049ab747fSPaolo Bonzini static const char *fmt2name[] = {
14185bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_U8  ] = "PCM-U8",
14285bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_S8  ] = "PCM-S8",
14385bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_U16 ] = "PCM-U16",
14485bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_S16 ] = "PCM-S16",
14585bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_U32 ] = "PCM-U32",
14685bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_S32 ] = "PCM-S32",
14749ab747fSPaolo Bonzini };
14849ab747fSPaolo Bonzini 
149acab7d60SPhilippe Mathieu-Daudé #define TYPE_HDA_AUDIO "hda-audio"
150acab7d60SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO)
151acab7d60SPhilippe Mathieu-Daudé 
15249ab747fSPaolo Bonzini typedef struct HDAAudioStream HDAAudioStream;
15349ab747fSPaolo Bonzini 
15449ab747fSPaolo Bonzini struct HDAAudioStream {
15549ab747fSPaolo Bonzini     HDAAudioState *state;
15649ab747fSPaolo Bonzini     const desc_node *node;
15749ab747fSPaolo Bonzini     bool output, running;
15849ab747fSPaolo Bonzini     uint32_t stream;
15949ab747fSPaolo Bonzini     uint32_t channel;
16049ab747fSPaolo Bonzini     uint32_t format;
16149ab747fSPaolo Bonzini     uint32_t gain_left, gain_right;
16249ab747fSPaolo Bonzini     bool mute_left, mute_right;
16349ab747fSPaolo Bonzini     struct audsettings as;
16449ab747fSPaolo Bonzini     union {
16549ab747fSPaolo Bonzini         SWVoiceIn *in;
16649ab747fSPaolo Bonzini         SWVoiceOut *out;
16749ab747fSPaolo Bonzini     } voice;
168280c1e1cSGerd Hoffmann     uint8_t compat_buf[HDA_BUFFER_SIZE];
169280c1e1cSGerd Hoffmann     uint32_t compat_bpos;
170280c1e1cSGerd Hoffmann     uint8_t buf[8192]; /* size must be power of two */
171280c1e1cSGerd Hoffmann     int64_t rpos;
172280c1e1cSGerd Hoffmann     int64_t wpos;
173280c1e1cSGerd Hoffmann     QEMUTimer *buft;
174280c1e1cSGerd Hoffmann     int64_t buft_start;
17549ab747fSPaolo Bonzini };
17649ab747fSPaolo Bonzini 
17749ab747fSPaolo Bonzini struct HDAAudioState {
17849ab747fSPaolo Bonzini     HDACodecDevice hda;
17949ab747fSPaolo Bonzini     const char *name;
18049ab747fSPaolo Bonzini 
18149ab747fSPaolo Bonzini     QEMUSoundCard card;
18249ab747fSPaolo Bonzini     const desc_codec *desc;
18349ab747fSPaolo Bonzini     HDAAudioStream st[4];
18449ab747fSPaolo Bonzini     bool running_compat[16];
18549ab747fSPaolo Bonzini     bool running_real[2 * 16];
18649ab747fSPaolo Bonzini 
18749ab747fSPaolo Bonzini     /* properties */
18849ab747fSPaolo Bonzini     uint32_t debug;
1892690e61eSBandan Das     bool     mixer;
190280c1e1cSGerd Hoffmann     bool     use_timer;
19149ab747fSPaolo Bonzini };
19249ab747fSPaolo Bonzini 
hda_bytes_per_second(HDAAudioStream * st)19374e8593eSVolker Rümelin static inline uint32_t hda_bytes_per_second(HDAAudioStream *st)
194280c1e1cSGerd Hoffmann {
19574e8593eSVolker Rümelin     return 2 * (uint32_t)st->as.nchannels * (uint32_t)st->as.freq;
196280c1e1cSGerd Hoffmann }
197280c1e1cSGerd Hoffmann 
hda_timer_sync_adjust(HDAAudioStream * st,int64_t target_pos)198280c1e1cSGerd Hoffmann static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
199280c1e1cSGerd Hoffmann {
2008ced0669SGerd Hoffmann     int64_t limit = B_SIZE / 8;
2018ced0669SGerd Hoffmann     int64_t corr = 0;
2028ced0669SGerd Hoffmann 
2038ced0669SGerd Hoffmann     if (target_pos > limit) {
2048ced0669SGerd Hoffmann         corr = HDA_TIMER_TICKS;
205280c1e1cSGerd Hoffmann     }
2068ced0669SGerd Hoffmann     if (target_pos < -limit) {
2078ced0669SGerd Hoffmann         corr = -HDA_TIMER_TICKS;
2088ced0669SGerd Hoffmann     }
2099d340f67SGerd Hoffmann     if (target_pos < -(2 * limit)) {
2109d340f67SGerd Hoffmann         corr = -(4 * HDA_TIMER_TICKS);
2119d340f67SGerd Hoffmann     }
2128ced0669SGerd Hoffmann     if (corr == 0) {
2138ced0669SGerd Hoffmann         return;
2148ced0669SGerd Hoffmann     }
2158ced0669SGerd Hoffmann 
2168ced0669SGerd Hoffmann     trace_hda_audio_adjust(st->node->name, target_pos);
2173b84967cSGerd Hoffmann     st->buft_start += corr;
218280c1e1cSGerd Hoffmann }
219280c1e1cSGerd Hoffmann 
hda_audio_input_timer(void * opaque)220280c1e1cSGerd Hoffmann static void hda_audio_input_timer(void *opaque)
221280c1e1cSGerd Hoffmann {
222280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
223280c1e1cSGerd Hoffmann 
224280c1e1cSGerd Hoffmann     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
225280c1e1cSGerd Hoffmann 
22674e8593eSVolker Rümelin     int64_t uptime = now - st->buft_start;
2273b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
2283b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
22974e8593eSVolker Rümelin     int64_t wanted_rpos;
230280c1e1cSGerd Hoffmann 
23174e8593eSVolker Rümelin     if (uptime <= 0) {
23274e8593eSVolker Rümelin         /* wanted_rpos <= 0 */
23374e8593eSVolker Rümelin         goto out_timer;
23474e8593eSVolker Rümelin     }
23574e8593eSVolker Rümelin 
23674e8593eSVolker Rümelin     wanted_rpos = muldiv64(uptime, hda_bytes_per_second(st),
23774e8593eSVolker Rümelin                            NANOSECONDS_PER_SECOND);
238280c1e1cSGerd Hoffmann     wanted_rpos &= -4; /* IMPORTANT! clip to frames */
239280c1e1cSGerd Hoffmann 
240280c1e1cSGerd Hoffmann     if (wanted_rpos <= rpos) {
241280c1e1cSGerd Hoffmann         /* we already transmitted the data */
242280c1e1cSGerd Hoffmann         goto out_timer;
243280c1e1cSGerd Hoffmann     }
244280c1e1cSGerd Hoffmann 
24558935915SKővágó, Zoltán     int64_t to_transfer = MIN(wpos - rpos, wanted_rpos - rpos);
246280c1e1cSGerd Hoffmann     while (to_transfer) {
247280c1e1cSGerd Hoffmann         uint32_t start = (rpos & B_MASK);
24858935915SKővágó, Zoltán         uint32_t chunk = MIN(B_SIZE - start, to_transfer);
249280c1e1cSGerd Hoffmann         int rc = hda_codec_xfer(
250280c1e1cSGerd Hoffmann                 &st->state->hda, st->stream, false, st->buf + start, chunk);
251280c1e1cSGerd Hoffmann         if (!rc) {
252280c1e1cSGerd Hoffmann             break;
253280c1e1cSGerd Hoffmann         }
254280c1e1cSGerd Hoffmann         rpos += chunk;
255280c1e1cSGerd Hoffmann         to_transfer -= chunk;
2563b84967cSGerd Hoffmann         st->rpos += chunk;
257280c1e1cSGerd Hoffmann     }
258280c1e1cSGerd Hoffmann 
259280c1e1cSGerd Hoffmann out_timer:
260280c1e1cSGerd Hoffmann 
261280c1e1cSGerd Hoffmann     if (st->running) {
262280c1e1cSGerd Hoffmann         timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
263280c1e1cSGerd Hoffmann     }
264280c1e1cSGerd Hoffmann }
265280c1e1cSGerd Hoffmann 
hda_audio_input_cb(void * opaque,int avail)26649ab747fSPaolo Bonzini static void hda_audio_input_cb(void *opaque, int avail)
26749ab747fSPaolo Bonzini {
26849ab747fSPaolo Bonzini     HDAAudioStream *st = opaque;
26949ab747fSPaolo Bonzini 
2703b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
2713b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
272280c1e1cSGerd Hoffmann 
27358935915SKővágó, Zoltán     int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), avail);
274280c1e1cSGerd Hoffmann 
275280c1e1cSGerd Hoffmann     while (to_transfer) {
276280c1e1cSGerd Hoffmann         uint32_t start = (uint32_t) (wpos & B_MASK);
27758935915SKővágó, Zoltán         uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer);
278280c1e1cSGerd Hoffmann         uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk);
279280c1e1cSGerd Hoffmann         wpos += read;
280280c1e1cSGerd Hoffmann         to_transfer -= read;
2813b84967cSGerd Hoffmann         st->wpos += read;
282280c1e1cSGerd Hoffmann         if (chunk != read) {
28349ab747fSPaolo Bonzini             break;
28449ab747fSPaolo Bonzini         }
28549ab747fSPaolo Bonzini     }
286c435fea7SVolker Rümelin 
287c435fea7SVolker Rümelin     hda_timer_sync_adjust(st, -((wpos - rpos) - (B_SIZE >> 1)));
288280c1e1cSGerd Hoffmann }
289280c1e1cSGerd Hoffmann 
hda_audio_output_timer(void * opaque)290280c1e1cSGerd Hoffmann static void hda_audio_output_timer(void *opaque)
291280c1e1cSGerd Hoffmann {
292280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
293280c1e1cSGerd Hoffmann 
294280c1e1cSGerd Hoffmann     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
295280c1e1cSGerd Hoffmann 
29674e8593eSVolker Rümelin     int64_t uptime = now - st->buft_start;
2973b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
2983b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
29974e8593eSVolker Rümelin     int64_t wanted_wpos;
300280c1e1cSGerd Hoffmann 
30174e8593eSVolker Rümelin     if (uptime <= 0) {
30274e8593eSVolker Rümelin         /* wanted_wpos <= 0 */
30374e8593eSVolker Rümelin         goto out_timer;
30474e8593eSVolker Rümelin     }
30574e8593eSVolker Rümelin 
30674e8593eSVolker Rümelin     wanted_wpos = muldiv64(uptime, hda_bytes_per_second(st),
30774e8593eSVolker Rümelin                            NANOSECONDS_PER_SECOND);
308280c1e1cSGerd Hoffmann     wanted_wpos &= -4; /* IMPORTANT! clip to frames */
309280c1e1cSGerd Hoffmann 
310280c1e1cSGerd Hoffmann     if (wanted_wpos <= wpos) {
311280c1e1cSGerd Hoffmann         /* we already received the data */
312280c1e1cSGerd Hoffmann         goto out_timer;
313280c1e1cSGerd Hoffmann     }
314280c1e1cSGerd Hoffmann 
31558935915SKővágó, Zoltán     int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
316280c1e1cSGerd Hoffmann     while (to_transfer) {
317280c1e1cSGerd Hoffmann         uint32_t start = (wpos & B_MASK);
31858935915SKővágó, Zoltán         uint32_t chunk = MIN(B_SIZE - start, to_transfer);
319280c1e1cSGerd Hoffmann         int rc = hda_codec_xfer(
320280c1e1cSGerd Hoffmann                 &st->state->hda, st->stream, true, st->buf + start, chunk);
32149ab747fSPaolo Bonzini         if (!rc) {
32249ab747fSPaolo Bonzini             break;
32349ab747fSPaolo Bonzini         }
324280c1e1cSGerd Hoffmann         wpos += chunk;
325280c1e1cSGerd Hoffmann         to_transfer -= chunk;
3263b84967cSGerd Hoffmann         st->wpos += chunk;
327280c1e1cSGerd Hoffmann     }
328280c1e1cSGerd Hoffmann 
329280c1e1cSGerd Hoffmann out_timer:
330280c1e1cSGerd Hoffmann 
331280c1e1cSGerd Hoffmann     if (st->running) {
332280c1e1cSGerd Hoffmann         timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
33349ab747fSPaolo Bonzini     }
33449ab747fSPaolo Bonzini }
33549ab747fSPaolo Bonzini 
hda_audio_output_cb(void * opaque,int avail)33649ab747fSPaolo Bonzini static void hda_audio_output_cb(void *opaque, int avail)
33749ab747fSPaolo Bonzini {
33849ab747fSPaolo Bonzini     HDAAudioStream *st = opaque;
339280c1e1cSGerd Hoffmann 
3403b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
3413b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
342280c1e1cSGerd Hoffmann 
34358935915SKővágó, Zoltán     int64_t to_transfer = MIN(wpos - rpos, avail);
344280c1e1cSGerd Hoffmann 
3454501ee16SGerd Hoffmann     if (wpos - rpos == B_SIZE) {
3464501ee16SGerd Hoffmann         /* drop buffer, reset timer adjust */
3474501ee16SGerd Hoffmann         st->rpos = 0;
3484501ee16SGerd Hoffmann         st->wpos = 0;
3494501ee16SGerd Hoffmann         st->buft_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
3504501ee16SGerd Hoffmann         trace_hda_audio_overrun(st->node->name);
3514501ee16SGerd Hoffmann         return;
3524501ee16SGerd Hoffmann     }
3534501ee16SGerd Hoffmann 
354280c1e1cSGerd Hoffmann     while (to_transfer) {
355280c1e1cSGerd Hoffmann         uint32_t start = (uint32_t) (rpos & B_MASK);
35658935915SKővágó, Zoltán         uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer);
357280c1e1cSGerd Hoffmann         uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
358280c1e1cSGerd Hoffmann         rpos += written;
359280c1e1cSGerd Hoffmann         to_transfer -= written;
3603b84967cSGerd Hoffmann         st->rpos += written;
361280c1e1cSGerd Hoffmann         if (chunk != written) {
362280c1e1cSGerd Hoffmann             break;
363280c1e1cSGerd Hoffmann         }
364280c1e1cSGerd Hoffmann     }
365df016a19SVolker Rümelin 
366df016a19SVolker Rümelin     hda_timer_sync_adjust(st, (wpos - rpos) - (B_SIZE >> 1));
367280c1e1cSGerd Hoffmann }
368280c1e1cSGerd Hoffmann 
hda_audio_compat_input_cb(void * opaque,int avail)369280c1e1cSGerd Hoffmann static void hda_audio_compat_input_cb(void *opaque, int avail)
370280c1e1cSGerd Hoffmann {
371280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
372280c1e1cSGerd Hoffmann     int recv = 0;
373280c1e1cSGerd Hoffmann     int len;
374280c1e1cSGerd Hoffmann     bool rc;
375280c1e1cSGerd Hoffmann 
376280c1e1cSGerd Hoffmann     while (avail - recv >= sizeof(st->compat_buf)) {
377280c1e1cSGerd Hoffmann         if (st->compat_bpos != sizeof(st->compat_buf)) {
378280c1e1cSGerd Hoffmann             len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos,
379280c1e1cSGerd Hoffmann                            sizeof(st->compat_buf) - st->compat_bpos);
380280c1e1cSGerd Hoffmann             st->compat_bpos += len;
381280c1e1cSGerd Hoffmann             recv += len;
382280c1e1cSGerd Hoffmann             if (st->compat_bpos != sizeof(st->compat_buf)) {
383280c1e1cSGerd Hoffmann                 break;
384280c1e1cSGerd Hoffmann             }
385280c1e1cSGerd Hoffmann         }
386280c1e1cSGerd Hoffmann         rc = hda_codec_xfer(&st->state->hda, st->stream, false,
387280c1e1cSGerd Hoffmann                             st->compat_buf, sizeof(st->compat_buf));
388280c1e1cSGerd Hoffmann         if (!rc) {
389280c1e1cSGerd Hoffmann             break;
390280c1e1cSGerd Hoffmann         }
391280c1e1cSGerd Hoffmann         st->compat_bpos = 0;
392280c1e1cSGerd Hoffmann     }
393280c1e1cSGerd Hoffmann }
394280c1e1cSGerd Hoffmann 
hda_audio_compat_output_cb(void * opaque,int avail)395280c1e1cSGerd Hoffmann static void hda_audio_compat_output_cb(void *opaque, int avail)
396280c1e1cSGerd Hoffmann {
397280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
39849ab747fSPaolo Bonzini     int sent = 0;
39949ab747fSPaolo Bonzini     int len;
40049ab747fSPaolo Bonzini     bool rc;
40149ab747fSPaolo Bonzini 
402280c1e1cSGerd Hoffmann     while (avail - sent >= sizeof(st->compat_buf)) {
403280c1e1cSGerd Hoffmann         if (st->compat_bpos == sizeof(st->compat_buf)) {
40449ab747fSPaolo Bonzini             rc = hda_codec_xfer(&st->state->hda, st->stream, true,
405280c1e1cSGerd Hoffmann                                 st->compat_buf, sizeof(st->compat_buf));
40649ab747fSPaolo Bonzini             if (!rc) {
40749ab747fSPaolo Bonzini                 break;
40849ab747fSPaolo Bonzini             }
409280c1e1cSGerd Hoffmann             st->compat_bpos = 0;
41049ab747fSPaolo Bonzini         }
411280c1e1cSGerd Hoffmann         len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos,
412280c1e1cSGerd Hoffmann                         sizeof(st->compat_buf) - st->compat_bpos);
413280c1e1cSGerd Hoffmann         st->compat_bpos += len;
41449ab747fSPaolo Bonzini         sent += len;
415280c1e1cSGerd Hoffmann         if (st->compat_bpos != sizeof(st->compat_buf)) {
41649ab747fSPaolo Bonzini             break;
41749ab747fSPaolo Bonzini         }
41849ab747fSPaolo Bonzini     }
41949ab747fSPaolo Bonzini }
42049ab747fSPaolo Bonzini 
hda_audio_set_running(HDAAudioStream * st,bool running)42149ab747fSPaolo Bonzini static void hda_audio_set_running(HDAAudioStream *st, bool running)
42249ab747fSPaolo Bonzini {
42349ab747fSPaolo Bonzini     if (st->node == NULL) {
42449ab747fSPaolo Bonzini         return;
42549ab747fSPaolo Bonzini     }
42649ab747fSPaolo Bonzini     if (st->running == running) {
42749ab747fSPaolo Bonzini         return;
42849ab747fSPaolo Bonzini     }
42949ab747fSPaolo Bonzini     st->running = running;
4300a373bb3SGerd Hoffmann     trace_hda_audio_running(st->node->name, st->stream, st->running);
431280c1e1cSGerd Hoffmann     if (st->state->use_timer) {
432280c1e1cSGerd Hoffmann         if (running) {
433280c1e1cSGerd Hoffmann             int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
434280c1e1cSGerd Hoffmann             st->rpos = 0;
435280c1e1cSGerd Hoffmann             st->wpos = 0;
436280c1e1cSGerd Hoffmann             st->buft_start = now;
437280c1e1cSGerd Hoffmann             timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
438280c1e1cSGerd Hoffmann         } else {
439280c1e1cSGerd Hoffmann             timer_del(st->buft);
440280c1e1cSGerd Hoffmann         }
441280c1e1cSGerd Hoffmann     }
44249ab747fSPaolo Bonzini     if (st->output) {
44349ab747fSPaolo Bonzini         AUD_set_active_out(st->voice.out, st->running);
44449ab747fSPaolo Bonzini     } else {
44549ab747fSPaolo Bonzini         AUD_set_active_in(st->voice.in, st->running);
44649ab747fSPaolo Bonzini     }
44749ab747fSPaolo Bonzini }
44849ab747fSPaolo Bonzini 
hda_audio_set_amp(HDAAudioStream * st)44949ab747fSPaolo Bonzini static void hda_audio_set_amp(HDAAudioStream *st)
45049ab747fSPaolo Bonzini {
45149ab747fSPaolo Bonzini     bool muted;
45249ab747fSPaolo Bonzini     uint32_t left, right;
45349ab747fSPaolo Bonzini 
45449ab747fSPaolo Bonzini     if (st->node == NULL) {
45549ab747fSPaolo Bonzini         return;
45649ab747fSPaolo Bonzini     }
45749ab747fSPaolo Bonzini 
45849ab747fSPaolo Bonzini     muted = st->mute_left && st->mute_right;
45949ab747fSPaolo Bonzini     left  = st->mute_left  ? 0 : st->gain_left;
46049ab747fSPaolo Bonzini     right = st->mute_right ? 0 : st->gain_right;
46149ab747fSPaolo Bonzini 
46249ab747fSPaolo Bonzini     left = left * 255 / QEMU_HDA_AMP_STEPS;
46349ab747fSPaolo Bonzini     right = right * 255 / QEMU_HDA_AMP_STEPS;
46449ab747fSPaolo Bonzini 
4654843877eSGerd Hoffmann     if (!st->state->mixer) {
4664843877eSGerd Hoffmann         return;
4674843877eSGerd Hoffmann     }
46849ab747fSPaolo Bonzini     if (st->output) {
46949ab747fSPaolo Bonzini         AUD_set_volume_out(st->voice.out, muted, left, right);
47049ab747fSPaolo Bonzini     } else {
47149ab747fSPaolo Bonzini         AUD_set_volume_in(st->voice.in, muted, left, right);
47249ab747fSPaolo Bonzini     }
47349ab747fSPaolo Bonzini }
47449ab747fSPaolo Bonzini 
hda_audio_setup(HDAAudioStream * st)47549ab747fSPaolo Bonzini static void hda_audio_setup(HDAAudioStream *st)
47649ab747fSPaolo Bonzini {
477280c1e1cSGerd Hoffmann     bool use_timer = st->state->use_timer;
478280c1e1cSGerd Hoffmann     audio_callback_fn cb;
479280c1e1cSGerd Hoffmann 
48049ab747fSPaolo Bonzini     if (st->node == NULL) {
48149ab747fSPaolo Bonzini         return;
48249ab747fSPaolo Bonzini     }
48349ab747fSPaolo Bonzini 
4840a373bb3SGerd Hoffmann     trace_hda_audio_format(st->node->name, st->as.nchannels,
48549ab747fSPaolo Bonzini                            fmt2name[st->as.fmt], st->as.freq);
48649ab747fSPaolo Bonzini 
48749ab747fSPaolo Bonzini     if (st->output) {
488280c1e1cSGerd Hoffmann         if (use_timer) {
489280c1e1cSGerd Hoffmann             cb = hda_audio_output_cb;
490*a7b7953dSPaolo Bonzini             timer_del(st->buft);
49149ab747fSPaolo Bonzini         } else {
492280c1e1cSGerd Hoffmann             cb = hda_audio_compat_output_cb;
493280c1e1cSGerd Hoffmann         }
494280c1e1cSGerd Hoffmann         st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
495280c1e1cSGerd Hoffmann                                      st->node->name, st, cb, &st->as);
496280c1e1cSGerd Hoffmann     } else {
497280c1e1cSGerd Hoffmann         if (use_timer) {
498280c1e1cSGerd Hoffmann             cb = hda_audio_input_cb;
499*a7b7953dSPaolo Bonzini             timer_del(st->buft);
500280c1e1cSGerd Hoffmann         } else {
501280c1e1cSGerd Hoffmann             cb = hda_audio_compat_input_cb;
502280c1e1cSGerd Hoffmann         }
50349ab747fSPaolo Bonzini         st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
504280c1e1cSGerd Hoffmann                                    st->node->name, st, cb, &st->as);
50549ab747fSPaolo Bonzini     }
50649ab747fSPaolo Bonzini }
50749ab747fSPaolo Bonzini 
hda_audio_command(HDACodecDevice * hda,uint32_t nid,uint32_t data)50849ab747fSPaolo Bonzini static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
50949ab747fSPaolo Bonzini {
510cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
51149ab747fSPaolo Bonzini     HDAAudioStream *st;
51249ab747fSPaolo Bonzini     const desc_node *node = NULL;
51349ab747fSPaolo Bonzini     const desc_param *param;
51449ab747fSPaolo Bonzini     uint32_t verb, payload, response, count, shift;
51549ab747fSPaolo Bonzini 
51649ab747fSPaolo Bonzini     if ((data & 0x70000) == 0x70000) {
51749ab747fSPaolo Bonzini         /* 12/8 id/payload */
51849ab747fSPaolo Bonzini         verb = (data >> 8) & 0xfff;
51949ab747fSPaolo Bonzini         payload = data & 0x00ff;
52049ab747fSPaolo Bonzini     } else {
52149ab747fSPaolo Bonzini         /* 4/16 id/payload */
52249ab747fSPaolo Bonzini         verb = (data >> 8) & 0xf00;
52349ab747fSPaolo Bonzini         payload = data & 0xffff;
52449ab747fSPaolo Bonzini     }
52549ab747fSPaolo Bonzini 
52649ab747fSPaolo Bonzini     node = hda_codec_find_node(a->desc, nid);
52749ab747fSPaolo Bonzini     if (node == NULL) {
52849ab747fSPaolo Bonzini         goto fail;
52949ab747fSPaolo Bonzini     }
53049ab747fSPaolo Bonzini     dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
531a89f364aSAlistair Francis            __func__, nid, node->name, verb, payload);
53249ab747fSPaolo Bonzini 
53349ab747fSPaolo Bonzini     switch (verb) {
53449ab747fSPaolo Bonzini     /* all nodes */
53549ab747fSPaolo Bonzini     case AC_VERB_PARAMETERS:
53649ab747fSPaolo Bonzini         param = hda_codec_find_param(node, payload);
53749ab747fSPaolo Bonzini         if (param == NULL) {
53849ab747fSPaolo Bonzini             goto fail;
53949ab747fSPaolo Bonzini         }
54049ab747fSPaolo Bonzini         hda_codec_response(hda, true, param->val);
54149ab747fSPaolo Bonzini         break;
54249ab747fSPaolo Bonzini     case AC_VERB_GET_SUBSYSTEM_ID:
54349ab747fSPaolo Bonzini         hda_codec_response(hda, true, a->desc->iid);
54449ab747fSPaolo Bonzini         break;
54549ab747fSPaolo Bonzini 
54649ab747fSPaolo Bonzini     /* all functions */
54749ab747fSPaolo Bonzini     case AC_VERB_GET_CONNECT_LIST:
54849ab747fSPaolo Bonzini         param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
54949ab747fSPaolo Bonzini         count = param ? param->val : 0;
55049ab747fSPaolo Bonzini         response = 0;
55149ab747fSPaolo Bonzini         shift = 0;
55249ab747fSPaolo Bonzini         while (payload < count && shift < 32) {
55349ab747fSPaolo Bonzini             response |= node->conn[payload] << shift;
55449ab747fSPaolo Bonzini             payload++;
55549ab747fSPaolo Bonzini             shift += 8;
55649ab747fSPaolo Bonzini         }
55749ab747fSPaolo Bonzini         hda_codec_response(hda, true, response);
55849ab747fSPaolo Bonzini         break;
55949ab747fSPaolo Bonzini 
56049ab747fSPaolo Bonzini     /* pin widget */
56149ab747fSPaolo Bonzini     case AC_VERB_GET_CONFIG_DEFAULT:
56249ab747fSPaolo Bonzini         hda_codec_response(hda, true, node->config);
56349ab747fSPaolo Bonzini         break;
56449ab747fSPaolo Bonzini     case AC_VERB_GET_PIN_WIDGET_CONTROL:
56549ab747fSPaolo Bonzini         hda_codec_response(hda, true, node->pinctl);
56649ab747fSPaolo Bonzini         break;
56749ab747fSPaolo Bonzini     case AC_VERB_SET_PIN_WIDGET_CONTROL:
56849ab747fSPaolo Bonzini         if (node->pinctl != payload) {
56949ab747fSPaolo Bonzini             dprint(a, 1, "unhandled pin control bit\n");
57049ab747fSPaolo Bonzini         }
57149ab747fSPaolo Bonzini         hda_codec_response(hda, true, 0);
57249ab747fSPaolo Bonzini         break;
57349ab747fSPaolo Bonzini 
57449ab747fSPaolo Bonzini     /* audio in/out widget */
57549ab747fSPaolo Bonzini     case AC_VERB_SET_CHANNEL_STREAMID:
57649ab747fSPaolo Bonzini         st = a->st + node->stindex;
57749ab747fSPaolo Bonzini         if (st->node == NULL) {
57849ab747fSPaolo Bonzini             goto fail;
57949ab747fSPaolo Bonzini         }
58049ab747fSPaolo Bonzini         hda_audio_set_running(st, false);
58149ab747fSPaolo Bonzini         st->stream = (payload >> 4) & 0x0f;
58249ab747fSPaolo Bonzini         st->channel = payload & 0x0f;
58349ab747fSPaolo Bonzini         dprint(a, 2, "%s: stream %d, channel %d\n",
58449ab747fSPaolo Bonzini                st->node->name, st->stream, st->channel);
58549ab747fSPaolo Bonzini         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
58649ab747fSPaolo Bonzini         hda_codec_response(hda, true, 0);
58749ab747fSPaolo Bonzini         break;
58849ab747fSPaolo Bonzini     case AC_VERB_GET_CONV:
58949ab747fSPaolo Bonzini         st = a->st + node->stindex;
59049ab747fSPaolo Bonzini         if (st->node == NULL) {
59149ab747fSPaolo Bonzini             goto fail;
59249ab747fSPaolo Bonzini         }
59349ab747fSPaolo Bonzini         response = st->stream << 4 | st->channel;
59449ab747fSPaolo Bonzini         hda_codec_response(hda, true, response);
59549ab747fSPaolo Bonzini         break;
59649ab747fSPaolo Bonzini     case AC_VERB_SET_STREAM_FORMAT:
59749ab747fSPaolo Bonzini         st = a->st + node->stindex;
59849ab747fSPaolo Bonzini         if (st->node == NULL) {
59949ab747fSPaolo Bonzini             goto fail;
60049ab747fSPaolo Bonzini         }
60149ab747fSPaolo Bonzini         st->format = payload;
60249ab747fSPaolo Bonzini         hda_codec_parse_fmt(st->format, &st->as);
60349ab747fSPaolo Bonzini         hda_audio_setup(st);
60449ab747fSPaolo Bonzini         hda_codec_response(hda, true, 0);
60549ab747fSPaolo Bonzini         break;
60649ab747fSPaolo Bonzini     case AC_VERB_GET_STREAM_FORMAT:
60749ab747fSPaolo Bonzini         st = a->st + node->stindex;
60849ab747fSPaolo Bonzini         if (st->node == NULL) {
60949ab747fSPaolo Bonzini             goto fail;
61049ab747fSPaolo Bonzini         }
61149ab747fSPaolo Bonzini         hda_codec_response(hda, true, st->format);
61249ab747fSPaolo Bonzini         break;
61349ab747fSPaolo Bonzini     case AC_VERB_GET_AMP_GAIN_MUTE:
61449ab747fSPaolo Bonzini         st = a->st + node->stindex;
61549ab747fSPaolo Bonzini         if (st->node == NULL) {
61649ab747fSPaolo Bonzini             goto fail;
61749ab747fSPaolo Bonzini         }
61849ab747fSPaolo Bonzini         if (payload & AC_AMP_GET_LEFT) {
61949ab747fSPaolo Bonzini             response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
62049ab747fSPaolo Bonzini         } else {
62149ab747fSPaolo Bonzini             response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
62249ab747fSPaolo Bonzini         }
62349ab747fSPaolo Bonzini         hda_codec_response(hda, true, response);
62449ab747fSPaolo Bonzini         break;
62549ab747fSPaolo Bonzini     case AC_VERB_SET_AMP_GAIN_MUTE:
62649ab747fSPaolo Bonzini         st = a->st + node->stindex;
62749ab747fSPaolo Bonzini         if (st->node == NULL) {
62849ab747fSPaolo Bonzini             goto fail;
62949ab747fSPaolo Bonzini         }
63049ab747fSPaolo Bonzini         dprint(a, 1, "amp (%s): %s%s%s%s index %d  gain %3d %s\n",
63149ab747fSPaolo Bonzini                st->node->name,
63249ab747fSPaolo Bonzini                (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
63349ab747fSPaolo Bonzini                (payload & AC_AMP_SET_INPUT)  ? "i" : "-",
63449ab747fSPaolo Bonzini                (payload & AC_AMP_SET_LEFT)   ? "l" : "-",
63549ab747fSPaolo Bonzini                (payload & AC_AMP_SET_RIGHT)  ? "r" : "-",
63649ab747fSPaolo Bonzini                (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
63749ab747fSPaolo Bonzini                (payload & AC_AMP_GAIN),
63849ab747fSPaolo Bonzini                (payload & AC_AMP_MUTE) ? "muted" : "");
63949ab747fSPaolo Bonzini         if (payload & AC_AMP_SET_LEFT) {
64049ab747fSPaolo Bonzini             st->gain_left = payload & AC_AMP_GAIN;
64149ab747fSPaolo Bonzini             st->mute_left = payload & AC_AMP_MUTE;
64249ab747fSPaolo Bonzini         }
64349ab747fSPaolo Bonzini         if (payload & AC_AMP_SET_RIGHT) {
64449ab747fSPaolo Bonzini             st->gain_right = payload & AC_AMP_GAIN;
64549ab747fSPaolo Bonzini             st->mute_right = payload & AC_AMP_MUTE;
64649ab747fSPaolo Bonzini         }
64749ab747fSPaolo Bonzini         hda_audio_set_amp(st);
64849ab747fSPaolo Bonzini         hda_codec_response(hda, true, 0);
64949ab747fSPaolo Bonzini         break;
65049ab747fSPaolo Bonzini 
65149ab747fSPaolo Bonzini     /* not supported */
65249ab747fSPaolo Bonzini     case AC_VERB_SET_POWER_STATE:
65349ab747fSPaolo Bonzini     case AC_VERB_GET_POWER_STATE:
65449ab747fSPaolo Bonzini     case AC_VERB_GET_SDI_SELECT:
65549ab747fSPaolo Bonzini         hda_codec_response(hda, true, 0);
65649ab747fSPaolo Bonzini         break;
65749ab747fSPaolo Bonzini     default:
65849ab747fSPaolo Bonzini         goto fail;
65949ab747fSPaolo Bonzini     }
66049ab747fSPaolo Bonzini     return;
66149ab747fSPaolo Bonzini 
66249ab747fSPaolo Bonzini fail:
66349ab747fSPaolo Bonzini     dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
664a89f364aSAlistair Francis            __func__, nid, node ? node->name : "?", verb, payload);
66549ab747fSPaolo Bonzini     hda_codec_response(hda, true, 0);
66649ab747fSPaolo Bonzini }
66749ab747fSPaolo Bonzini 
hda_audio_stream(HDACodecDevice * hda,uint32_t stnr,bool running,bool output)66849ab747fSPaolo Bonzini static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
66949ab747fSPaolo Bonzini {
670cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
67149ab747fSPaolo Bonzini     int s;
67249ab747fSPaolo Bonzini 
67349ab747fSPaolo Bonzini     a->running_compat[stnr] = running;
67449ab747fSPaolo Bonzini     a->running_real[output * 16 + stnr] = running;
67549ab747fSPaolo Bonzini     for (s = 0; s < ARRAY_SIZE(a->st); s++) {
67649ab747fSPaolo Bonzini         if (a->st[s].node == NULL) {
67749ab747fSPaolo Bonzini             continue;
67849ab747fSPaolo Bonzini         }
67949ab747fSPaolo Bonzini         if (a->st[s].output != output) {
68049ab747fSPaolo Bonzini             continue;
68149ab747fSPaolo Bonzini         }
68249ab747fSPaolo Bonzini         if (a->st[s].stream != stnr) {
68349ab747fSPaolo Bonzini             continue;
68449ab747fSPaolo Bonzini         }
68549ab747fSPaolo Bonzini         hda_audio_set_running(&a->st[s], running);
68649ab747fSPaolo Bonzini     }
68749ab747fSPaolo Bonzini }
68849ab747fSPaolo Bonzini 
hda_audio_init(HDACodecDevice * hda,const struct desc_codec * desc,Error ** errp)689b7639b7dSMartin Kletzander static void hda_audio_init(HDACodecDevice *hda,
690b7639b7dSMartin Kletzander                            const struct desc_codec *desc,
691b7639b7dSMartin Kletzander                            Error **errp)
69249ab747fSPaolo Bonzini {
693cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
69449ab747fSPaolo Bonzini     HDAAudioStream *st;
69549ab747fSPaolo Bonzini     const desc_node *node;
69649ab747fSPaolo Bonzini     const desc_param *param;
69749ab747fSPaolo Bonzini     uint32_t i, type;
69849ab747fSPaolo Bonzini 
699cb94ff5fSMartin Kletzander     if (!AUD_register_card("hda", &a->card, errp)) {
700cb94ff5fSMartin Kletzander         return;
701cb94ff5fSMartin Kletzander     }
702cb94ff5fSMartin Kletzander 
70349ab747fSPaolo Bonzini     a->desc = desc;
70449ab747fSPaolo Bonzini     a->name = object_get_typename(OBJECT(a));
705a89f364aSAlistair Francis     dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad);
70649ab747fSPaolo Bonzini 
70749ab747fSPaolo Bonzini     for (i = 0; i < a->desc->nnodes; i++) {
70849ab747fSPaolo Bonzini         node = a->desc->nodes + i;
70949ab747fSPaolo Bonzini         param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
7102ab5bf67SGonglei         if (param == NULL) {
71149ab747fSPaolo Bonzini             continue;
7122ab5bf67SGonglei         }
71349ab747fSPaolo Bonzini         type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
71449ab747fSPaolo Bonzini         switch (type) {
71549ab747fSPaolo Bonzini         case AC_WID_AUD_OUT:
71649ab747fSPaolo Bonzini         case AC_WID_AUD_IN:
71749ab747fSPaolo Bonzini             assert(node->stindex < ARRAY_SIZE(a->st));
71849ab747fSPaolo Bonzini             st = a->st + node->stindex;
71949ab747fSPaolo Bonzini             st->state = a;
72049ab747fSPaolo Bonzini             st->node = node;
72149ab747fSPaolo Bonzini             if (type == AC_WID_AUD_OUT) {
72249ab747fSPaolo Bonzini                 /* unmute output by default */
72349ab747fSPaolo Bonzini                 st->gain_left = QEMU_HDA_AMP_STEPS;
72449ab747fSPaolo Bonzini                 st->gain_right = QEMU_HDA_AMP_STEPS;
725280c1e1cSGerd Hoffmann                 st->compat_bpos = sizeof(st->compat_buf);
72649ab747fSPaolo Bonzini                 st->output = true;
727*a7b7953dSPaolo Bonzini                 st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
728*a7b7953dSPaolo Bonzini                                         hda_audio_output_timer, st);
72949ab747fSPaolo Bonzini             } else {
73049ab747fSPaolo Bonzini                 st->output = false;
731*a7b7953dSPaolo Bonzini                 st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
732*a7b7953dSPaolo Bonzini                                         hda_audio_input_timer, st);
73349ab747fSPaolo Bonzini             }
73449ab747fSPaolo Bonzini             st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
73549ab747fSPaolo Bonzini                 (1 << AC_FMT_CHAN_SHIFT);
73649ab747fSPaolo Bonzini             hda_codec_parse_fmt(st->format, &st->as);
73749ab747fSPaolo Bonzini             hda_audio_setup(st);
73849ab747fSPaolo Bonzini             break;
73949ab747fSPaolo Bonzini         }
74049ab747fSPaolo Bonzini     }
74149ab747fSPaolo Bonzini }
74249ab747fSPaolo Bonzini 
hda_audio_exit(HDACodecDevice * hda)7435eaa8e1eSZihan Yang static void hda_audio_exit(HDACodecDevice *hda)
74449ab747fSPaolo Bonzini {
745cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
7468d9c6f6fSPaolo Bonzini     HDAAudioStream *st;
74749ab747fSPaolo Bonzini     int i;
74849ab747fSPaolo Bonzini 
749a89f364aSAlistair Francis     dprint(a, 1, "%s\n", __func__);
75049ab747fSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
7518d9c6f6fSPaolo Bonzini         st = a->st + i;
7528d9c6f6fSPaolo Bonzini         if (st->node == NULL) {
7538d9c6f6fSPaolo Bonzini             continue;
7548d9c6f6fSPaolo Bonzini         }
7558d9c6f6fSPaolo Bonzini         timer_free(st->buft);
7568d9c6f6fSPaolo Bonzini         if (st->output) {
7578d9c6f6fSPaolo Bonzini             AUD_close_out(&a->card, st->voice.out);
7588d9c6f6fSPaolo Bonzini         } else {
7598d9c6f6fSPaolo Bonzini             AUD_close_in(&a->card, st->voice.in);
7608d9c6f6fSPaolo Bonzini         }
76149ab747fSPaolo Bonzini     }
76249ab747fSPaolo Bonzini     AUD_remove_card(&a->card);
76349ab747fSPaolo Bonzini }
76449ab747fSPaolo Bonzini 
hda_audio_post_load(void * opaque,int version)76549ab747fSPaolo Bonzini static int hda_audio_post_load(void *opaque, int version)
76649ab747fSPaolo Bonzini {
76749ab747fSPaolo Bonzini     HDAAudioState *a = opaque;
76849ab747fSPaolo Bonzini     HDAAudioStream *st;
76949ab747fSPaolo Bonzini     int i;
77049ab747fSPaolo Bonzini 
771a89f364aSAlistair Francis     dprint(a, 1, "%s\n", __func__);
77249ab747fSPaolo Bonzini     if (version == 1) {
77349ab747fSPaolo Bonzini         /* assume running_compat[] is for output streams */
77449ab747fSPaolo Bonzini         for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
77549ab747fSPaolo Bonzini             a->running_real[16 + i] = a->running_compat[i];
77649ab747fSPaolo Bonzini     }
77749ab747fSPaolo Bonzini 
77849ab747fSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
77949ab747fSPaolo Bonzini         st = a->st + i;
78049ab747fSPaolo Bonzini         if (st->node == NULL)
78149ab747fSPaolo Bonzini             continue;
78249ab747fSPaolo Bonzini         hda_codec_parse_fmt(st->format, &st->as);
78349ab747fSPaolo Bonzini         hda_audio_setup(st);
78449ab747fSPaolo Bonzini         hda_audio_set_amp(st);
78549ab747fSPaolo Bonzini         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
78649ab747fSPaolo Bonzini     }
78749ab747fSPaolo Bonzini     return 0;
78849ab747fSPaolo Bonzini }
78949ab747fSPaolo Bonzini 
hda_audio_reset(DeviceState * dev)79039e6a38cSGerd Hoffmann static void hda_audio_reset(DeviceState *dev)
79139e6a38cSGerd Hoffmann {
792cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(dev);
79339e6a38cSGerd Hoffmann     HDAAudioStream *st;
79439e6a38cSGerd Hoffmann     int i;
79539e6a38cSGerd Hoffmann 
79639e6a38cSGerd Hoffmann     dprint(a, 1, "%s\n", __func__);
79739e6a38cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
79839e6a38cSGerd Hoffmann         st = a->st + i;
79939e6a38cSGerd Hoffmann         if (st->node != NULL) {
80039e6a38cSGerd Hoffmann             hda_audio_set_running(st, false);
80139e6a38cSGerd Hoffmann         }
80239e6a38cSGerd Hoffmann     }
80339e6a38cSGerd Hoffmann }
80439e6a38cSGerd Hoffmann 
vmstate_hda_audio_stream_buf_needed(void * opaque)805280c1e1cSGerd Hoffmann static bool vmstate_hda_audio_stream_buf_needed(void *opaque)
806280c1e1cSGerd Hoffmann {
807280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
80867fa1f57SDr. David Alan Gilbert     return st->state && st->state->use_timer;
809280c1e1cSGerd Hoffmann }
810280c1e1cSGerd Hoffmann 
811280c1e1cSGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream_buf = {
812280c1e1cSGerd Hoffmann     .name = "hda-audio-stream/buffer",
813280c1e1cSGerd Hoffmann     .version_id = 1,
814280c1e1cSGerd Hoffmann     .needed = vmstate_hda_audio_stream_buf_needed,
815856a6fe4SRichard Henderson     .fields = (const VMStateField[]) {
816280c1e1cSGerd Hoffmann         VMSTATE_BUFFER(buf, HDAAudioStream),
817280c1e1cSGerd Hoffmann         VMSTATE_INT64(rpos, HDAAudioStream),
818280c1e1cSGerd Hoffmann         VMSTATE_INT64(wpos, HDAAudioStream),
819280c1e1cSGerd Hoffmann         VMSTATE_TIMER_PTR(buft, HDAAudioStream),
820280c1e1cSGerd Hoffmann         VMSTATE_INT64(buft_start, HDAAudioStream),
821280c1e1cSGerd Hoffmann         VMSTATE_END_OF_LIST()
822280c1e1cSGerd Hoffmann     }
823280c1e1cSGerd Hoffmann };
824280c1e1cSGerd Hoffmann 
82549ab747fSPaolo Bonzini static const VMStateDescription vmstate_hda_audio_stream = {
82649ab747fSPaolo Bonzini     .name = "hda-audio-stream",
82749ab747fSPaolo Bonzini     .version_id = 1,
828856a6fe4SRichard Henderson     .fields = (const VMStateField[]) {
82949ab747fSPaolo Bonzini         VMSTATE_UINT32(stream, HDAAudioStream),
83049ab747fSPaolo Bonzini         VMSTATE_UINT32(channel, HDAAudioStream),
83149ab747fSPaolo Bonzini         VMSTATE_UINT32(format, HDAAudioStream),
83249ab747fSPaolo Bonzini         VMSTATE_UINT32(gain_left, HDAAudioStream),
83349ab747fSPaolo Bonzini         VMSTATE_UINT32(gain_right, HDAAudioStream),
83449ab747fSPaolo Bonzini         VMSTATE_BOOL(mute_left, HDAAudioStream),
83549ab747fSPaolo Bonzini         VMSTATE_BOOL(mute_right, HDAAudioStream),
836280c1e1cSGerd Hoffmann         VMSTATE_UINT32(compat_bpos, HDAAudioStream),
837280c1e1cSGerd Hoffmann         VMSTATE_BUFFER(compat_buf, HDAAudioStream),
83849ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
839280c1e1cSGerd Hoffmann     },
840856a6fe4SRichard Henderson     .subsections = (const VMStateDescription * const []) {
841280c1e1cSGerd Hoffmann         &vmstate_hda_audio_stream_buf,
842280c1e1cSGerd Hoffmann         NULL
84349ab747fSPaolo Bonzini     }
84449ab747fSPaolo Bonzini };
84549ab747fSPaolo Bonzini 
84649ab747fSPaolo Bonzini static const VMStateDescription vmstate_hda_audio = {
84749ab747fSPaolo Bonzini     .name = "hda-audio",
84849ab747fSPaolo Bonzini     .version_id = 2,
84949ab747fSPaolo Bonzini     .post_load = hda_audio_post_load,
850856a6fe4SRichard Henderson     .fields = (const VMStateField[]) {
85149ab747fSPaolo Bonzini         VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
85249ab747fSPaolo Bonzini                              vmstate_hda_audio_stream,
85349ab747fSPaolo Bonzini                              HDAAudioStream),
85449ab747fSPaolo Bonzini         VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
85549ab747fSPaolo Bonzini         VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
85649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
85749ab747fSPaolo Bonzini     }
85849ab747fSPaolo Bonzini };
85949ab747fSPaolo Bonzini 
86049ab747fSPaolo Bonzini static Property hda_audio_properties[] = {
86188e47b9aSKővágó, Zoltán     DEFINE_AUDIO_PROPERTIES(HDAAudioState, card),
86249ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("debug", HDAAudioState, debug,   0),
8632690e61eSBandan Das     DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer,  true),
864bc753dc0SGerd Hoffmann     DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer,  true),
86549ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
86649ab747fSPaolo Bonzini };
86749ab747fSPaolo Bonzini 
hda_audio_init_output(HDACodecDevice * hda,Error ** errp)868b7639b7dSMartin Kletzander static void hda_audio_init_output(HDACodecDevice *hda, Error **errp)
86949ab747fSPaolo Bonzini {
870cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
871e60bdfb7SVolker Rümelin     const struct desc_codec *desc = &output_mixemu;
8722690e61eSBandan Das 
8732690e61eSBandan Das     if (!a->mixer) {
874e60bdfb7SVolker Rümelin         desc = &output_nomixemu;
87549ab747fSPaolo Bonzini     }
87649ab747fSPaolo Bonzini 
877b7639b7dSMartin Kletzander     hda_audio_init(hda, desc, errp);
878b7639b7dSMartin Kletzander }
879b7639b7dSMartin Kletzander 
hda_audio_init_duplex(HDACodecDevice * hda,Error ** errp)880b7639b7dSMartin Kletzander static void hda_audio_init_duplex(HDACodecDevice *hda, Error **errp)
88149ab747fSPaolo Bonzini {
882cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
883e60bdfb7SVolker Rümelin     const struct desc_codec *desc = &duplex_mixemu;
8842690e61eSBandan Das 
8852690e61eSBandan Das     if (!a->mixer) {
886e60bdfb7SVolker Rümelin         desc = &duplex_nomixemu;
88749ab747fSPaolo Bonzini     }
88849ab747fSPaolo Bonzini 
889b7639b7dSMartin Kletzander     hda_audio_init(hda, desc, errp);
890b7639b7dSMartin Kletzander }
891b7639b7dSMartin Kletzander 
hda_audio_init_micro(HDACodecDevice * hda,Error ** errp)892b7639b7dSMartin Kletzander static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp)
89349ab747fSPaolo Bonzini {
894cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
895e60bdfb7SVolker Rümelin     const struct desc_codec *desc = &micro_mixemu;
8962690e61eSBandan Das 
8972690e61eSBandan Das     if (!a->mixer) {
898e60bdfb7SVolker Rümelin         desc = &micro_nomixemu;
8992690e61eSBandan Das     }
900b7639b7dSMartin Kletzander 
901b7639b7dSMartin Kletzander     hda_audio_init(hda, desc, errp);
90249ab747fSPaolo Bonzini }
90349ab747fSPaolo Bonzini 
hda_audio_base_class_init(ObjectClass * klass,void * data)904cd6c8830SGerd Hoffmann static void hda_audio_base_class_init(ObjectClass *klass, void *data)
905cd6c8830SGerd Hoffmann {
906cd6c8830SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
907cd6c8830SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
908cd6c8830SGerd Hoffmann 
909cd6c8830SGerd Hoffmann     k->exit = hda_audio_exit;
910cd6c8830SGerd Hoffmann     k->command = hda_audio_command;
911cd6c8830SGerd Hoffmann     k->stream = hda_audio_stream;
912cd6c8830SGerd Hoffmann     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
913cd6c8830SGerd Hoffmann     device_class_set_legacy_reset(dc, hda_audio_reset);
914cd6c8830SGerd Hoffmann     dc->vmsd = &vmstate_hda_audio;
9154f67d30bSMarc-André Lureau     device_class_set_props(dc, hda_audio_properties);
916cd6c8830SGerd Hoffmann }
917cd6c8830SGerd Hoffmann 
918cd6c8830SGerd Hoffmann static const TypeInfo hda_audio_info = {
919cd6c8830SGerd Hoffmann     .name          = TYPE_HDA_AUDIO,
920cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_CODEC_DEVICE,
921edf632eeSEduardo Habkost     .instance_size = sizeof(HDAAudioState),
922cd6c8830SGerd Hoffmann     .class_init    = hda_audio_base_class_init,
923cd6c8830SGerd Hoffmann     .abstract      = true,
924cd6c8830SGerd Hoffmann };
925cd6c8830SGerd Hoffmann 
hda_audio_output_class_init(ObjectClass * klass,void * data)92649ab747fSPaolo Bonzini static void hda_audio_output_class_init(ObjectClass *klass, void *data)
92749ab747fSPaolo Bonzini {
92849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
92949ab747fSPaolo Bonzini     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
93049ab747fSPaolo Bonzini 
93149ab747fSPaolo Bonzini     k->init = hda_audio_init_output;
93249ab747fSPaolo Bonzini     dc->desc = "HDA Audio Codec, output-only (line-out)";
93349ab747fSPaolo Bonzini }
93449ab747fSPaolo Bonzini 
93549ab747fSPaolo Bonzini static const TypeInfo hda_audio_output_info = {
93649ab747fSPaolo Bonzini     .name          = "hda-output",
937cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
93849ab747fSPaolo Bonzini     .class_init    = hda_audio_output_class_init,
93949ab747fSPaolo Bonzini };
94049ab747fSPaolo Bonzini 
hda_audio_duplex_class_init(ObjectClass * klass,void * data)94149ab747fSPaolo Bonzini static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
94249ab747fSPaolo Bonzini {
94349ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
94449ab747fSPaolo Bonzini     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
94549ab747fSPaolo Bonzini 
94649ab747fSPaolo Bonzini     k->init = hda_audio_init_duplex;
94749ab747fSPaolo Bonzini     dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
94849ab747fSPaolo Bonzini }
94949ab747fSPaolo Bonzini 
95049ab747fSPaolo Bonzini static const TypeInfo hda_audio_duplex_info = {
95149ab747fSPaolo Bonzini     .name          = "hda-duplex",
952cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
95349ab747fSPaolo Bonzini     .class_init    = hda_audio_duplex_class_init,
95449ab747fSPaolo Bonzini };
95549ab747fSPaolo Bonzini 
hda_audio_micro_class_init(ObjectClass * klass,void * data)95649ab747fSPaolo Bonzini static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
95749ab747fSPaolo Bonzini {
95849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
95949ab747fSPaolo Bonzini     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
96049ab747fSPaolo Bonzini 
96149ab747fSPaolo Bonzini     k->init = hda_audio_init_micro;
96249ab747fSPaolo Bonzini     dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
96349ab747fSPaolo Bonzini }
96449ab747fSPaolo Bonzini 
96549ab747fSPaolo Bonzini static const TypeInfo hda_audio_micro_info = {
96649ab747fSPaolo Bonzini     .name          = "hda-micro",
967cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
96849ab747fSPaolo Bonzini     .class_init    = hda_audio_micro_class_init,
96949ab747fSPaolo Bonzini };
97049ab747fSPaolo Bonzini 
hda_audio_register_types(void)97149ab747fSPaolo Bonzini static void hda_audio_register_types(void)
97249ab747fSPaolo Bonzini {
973cd6c8830SGerd Hoffmann     type_register_static(&hda_audio_info);
97449ab747fSPaolo Bonzini     type_register_static(&hda_audio_output_info);
97549ab747fSPaolo Bonzini     type_register_static(&hda_audio_duplex_info);
97649ab747fSPaolo Bonzini     type_register_static(&hda_audio_micro_info);
97749ab747fSPaolo Bonzini }
97849ab747fSPaolo Bonzini 
97949ab747fSPaolo Bonzini type_init(hda_audio_register_types)
980