xref: /qemu/hw/audio/hda-codec.c (revision cb94ff5f80c537e43c04fa4f071f1df784255310)
1d61a4ce8SGerd Hoffmann /*
2d61a4ce8SGerd Hoffmann  * Copyright (C) 2010 Red Hat, Inc.
3d61a4ce8SGerd Hoffmann  *
4d61a4ce8SGerd Hoffmann  * written by Gerd Hoffmann <kraxel@redhat.com>
5d61a4ce8SGerd Hoffmann  *
6d61a4ce8SGerd Hoffmann  * This program is free software; you can redistribute it and/or
7d61a4ce8SGerd Hoffmann  * modify it under the terms of the GNU General Public License as
8d61a4ce8SGerd Hoffmann  * published by the Free Software Foundation; either version 2 or
9d61a4ce8SGerd Hoffmann  * (at your option) version 3 of the License.
10d61a4ce8SGerd Hoffmann  *
11d61a4ce8SGerd Hoffmann  * This program is distributed in the hope that it will be useful,
12d61a4ce8SGerd Hoffmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13d61a4ce8SGerd Hoffmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14d61a4ce8SGerd Hoffmann  * GNU General Public License for more details.
15d61a4ce8SGerd Hoffmann  *
16d61a4ce8SGerd Hoffmann  * You should have received a copy of the GNU General Public License
17d61a4ce8SGerd Hoffmann  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18d61a4ce8SGerd Hoffmann  */
19d61a4ce8SGerd Hoffmann 
206086a565SPeter Maydell #include "qemu/osdep.h"
2183c9f4caSPaolo 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"
250b8fa32fSMarkus Armbruster #include "qemu/module.h"
2647b43a1fSPaolo Bonzini #include "intel-hda-defs.h"
27d61a4ce8SGerd Hoffmann #include "audio/audio.h"
280a373bb3SGerd Hoffmann #include "trace.h"
29db1015e9SEduardo Habkost #include "qom/object.h"
30d61a4ce8SGerd Hoffmann 
31d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
32d61a4ce8SGerd Hoffmann 
33d61a4ce8SGerd Hoffmann typedef struct desc_param {
34d61a4ce8SGerd Hoffmann     uint32_t id;
35d61a4ce8SGerd Hoffmann     uint32_t val;
36d61a4ce8SGerd Hoffmann } desc_param;
37d61a4ce8SGerd Hoffmann 
38d61a4ce8SGerd Hoffmann typedef struct desc_node {
39d61a4ce8SGerd Hoffmann     uint32_t nid;
40d61a4ce8SGerd Hoffmann     const char *name;
41d61a4ce8SGerd Hoffmann     const desc_param *params;
42d61a4ce8SGerd Hoffmann     uint32_t nparams;
43d61a4ce8SGerd Hoffmann     uint32_t config;
44d61a4ce8SGerd Hoffmann     uint32_t pinctl;
45d61a4ce8SGerd Hoffmann     uint32_t *conn;
46d61a4ce8SGerd Hoffmann     uint32_t stindex;
47d61a4ce8SGerd Hoffmann } desc_node;
48d61a4ce8SGerd Hoffmann 
49d61a4ce8SGerd Hoffmann typedef struct desc_codec {
50d61a4ce8SGerd Hoffmann     const char *name;
51d61a4ce8SGerd Hoffmann     uint32_t iid;
52d61a4ce8SGerd Hoffmann     const desc_node *nodes;
53d61a4ce8SGerd Hoffmann     uint32_t nnodes;
54d61a4ce8SGerd Hoffmann } desc_codec;
55d61a4ce8SGerd Hoffmann 
56d61a4ce8SGerd Hoffmann static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
57d61a4ce8SGerd Hoffmann {
58d61a4ce8SGerd Hoffmann     int i;
59d61a4ce8SGerd Hoffmann 
60d61a4ce8SGerd Hoffmann     for (i = 0; i < node->nparams; i++) {
61d61a4ce8SGerd Hoffmann         if (node->params[i].id == id) {
62d61a4ce8SGerd Hoffmann             return &node->params[i];
63d61a4ce8SGerd Hoffmann         }
64d61a4ce8SGerd Hoffmann     }
65d61a4ce8SGerd Hoffmann     return NULL;
66d61a4ce8SGerd Hoffmann }
67d61a4ce8SGerd Hoffmann 
68d61a4ce8SGerd Hoffmann static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
69d61a4ce8SGerd Hoffmann {
70d61a4ce8SGerd Hoffmann     int i;
71d61a4ce8SGerd Hoffmann 
72d61a4ce8SGerd Hoffmann     for (i = 0; i < codec->nnodes; i++) {
73d61a4ce8SGerd Hoffmann         if (codec->nodes[i].nid == nid) {
74d61a4ce8SGerd Hoffmann             return &codec->nodes[i];
75d61a4ce8SGerd Hoffmann         }
76d61a4ce8SGerd Hoffmann     }
77d61a4ce8SGerd Hoffmann     return NULL;
78d61a4ce8SGerd Hoffmann }
79d61a4ce8SGerd Hoffmann 
80d61a4ce8SGerd Hoffmann static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
81d61a4ce8SGerd Hoffmann {
82d61a4ce8SGerd Hoffmann     if (format & AC_FMT_TYPE_NON_PCM) {
83d61a4ce8SGerd Hoffmann         return;
84d61a4ce8SGerd Hoffmann     }
85d61a4ce8SGerd Hoffmann 
86d61a4ce8SGerd Hoffmann     as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
87d61a4ce8SGerd Hoffmann 
88d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
89d61a4ce8SGerd Hoffmann     case 1: as->freq *= 2; break;
90d61a4ce8SGerd Hoffmann     case 2: as->freq *= 3; break;
91d61a4ce8SGerd Hoffmann     case 3: as->freq *= 4; break;
92d61a4ce8SGerd Hoffmann     }
93d61a4ce8SGerd Hoffmann 
94d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
95d61a4ce8SGerd Hoffmann     case 1: as->freq /= 2; break;
96d61a4ce8SGerd Hoffmann     case 2: as->freq /= 3; break;
97d61a4ce8SGerd Hoffmann     case 3: as->freq /= 4; break;
98d61a4ce8SGerd Hoffmann     case 4: as->freq /= 5; break;
99d61a4ce8SGerd Hoffmann     case 5: as->freq /= 6; break;
100d61a4ce8SGerd Hoffmann     case 6: as->freq /= 7; break;
101d61a4ce8SGerd Hoffmann     case 7: as->freq /= 8; break;
102d61a4ce8SGerd Hoffmann     }
103d61a4ce8SGerd Hoffmann 
104d61a4ce8SGerd Hoffmann     switch (format & AC_FMT_BITS_MASK) {
10585bc5852SKővágó, Zoltán     case AC_FMT_BITS_8:  as->fmt = AUDIO_FORMAT_S8;  break;
10685bc5852SKővágó, Zoltán     case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break;
10785bc5852SKővágó, Zoltán     case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break;
108d61a4ce8SGerd Hoffmann     }
109d61a4ce8SGerd Hoffmann 
110d61a4ce8SGerd Hoffmann     as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
111d61a4ce8SGerd Hoffmann }
112d61a4ce8SGerd Hoffmann 
113d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
114d61a4ce8SGerd Hoffmann /*
115d61a4ce8SGerd Hoffmann  * HDA codec descriptions
116d61a4ce8SGerd Hoffmann  */
117d61a4ce8SGerd Hoffmann 
118d61a4ce8SGerd Hoffmann /* some defines */
119d61a4ce8SGerd Hoffmann 
120d61a4ce8SGerd Hoffmann #define QEMU_HDA_ID_VENDOR  0x1af4
121d61a4ce8SGerd Hoffmann #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
122d61a4ce8SGerd Hoffmann                               0x1fc /* 16 -> 96 kHz */)
123d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_NONE    (0)
124d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_STEPS   0x4a
125d61a4ce8SGerd Hoffmann 
1262690e61eSBandan Das #define   PARAM mixemu
1272690e61eSBandan Das #define   HDA_MIXER
1287953793cSBandan Das #include "hda-codec-common.h"
1292690e61eSBandan Das 
1302690e61eSBandan Das #define   PARAM nomixemu
1312690e61eSBandan Das #include  "hda-codec-common.h"
1322690e61eSBandan Das 
133280c1e1cSGerd Hoffmann #define HDA_TIMER_TICKS (SCALE_MS)
134280c1e1cSGerd Hoffmann #define B_SIZE sizeof(st->buf)
135280c1e1cSGerd Hoffmann #define B_MASK (sizeof(st->buf) - 1)
136280c1e1cSGerd Hoffmann 
137d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
138d61a4ce8SGerd Hoffmann 
139d61a4ce8SGerd Hoffmann static const char *fmt2name[] = {
14085bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_U8  ] = "PCM-U8",
14185bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_S8  ] = "PCM-S8",
14285bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_U16 ] = "PCM-U16",
14385bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_S16 ] = "PCM-S16",
14485bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_U32 ] = "PCM-U32",
14585bc5852SKővágó, Zoltán     [ AUDIO_FORMAT_S32 ] = "PCM-S32",
146d61a4ce8SGerd Hoffmann };
147d61a4ce8SGerd Hoffmann 
148acab7d60SPhilippe Mathieu-Daudé #define TYPE_HDA_AUDIO "hda-audio"
149acab7d60SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO)
150acab7d60SPhilippe Mathieu-Daudé 
151d61a4ce8SGerd Hoffmann typedef struct HDAAudioStream HDAAudioStream;
152d61a4ce8SGerd Hoffmann 
153d61a4ce8SGerd Hoffmann struct HDAAudioStream {
154d61a4ce8SGerd Hoffmann     HDAAudioState *state;
155d61a4ce8SGerd Hoffmann     const desc_node *node;
156d61a4ce8SGerd Hoffmann     bool output, running;
157d61a4ce8SGerd Hoffmann     uint32_t stream;
158d61a4ce8SGerd Hoffmann     uint32_t channel;
159d61a4ce8SGerd Hoffmann     uint32_t format;
160d61a4ce8SGerd Hoffmann     uint32_t gain_left, gain_right;
161d61a4ce8SGerd Hoffmann     bool mute_left, mute_right;
162d61a4ce8SGerd Hoffmann     struct audsettings as;
163d61a4ce8SGerd Hoffmann     union {
164d61a4ce8SGerd Hoffmann         SWVoiceIn *in;
165d61a4ce8SGerd Hoffmann         SWVoiceOut *out;
166d61a4ce8SGerd Hoffmann     } voice;
167280c1e1cSGerd Hoffmann     uint8_t compat_buf[HDA_BUFFER_SIZE];
168280c1e1cSGerd Hoffmann     uint32_t compat_bpos;
169280c1e1cSGerd Hoffmann     uint8_t buf[8192]; /* size must be power of two */
170280c1e1cSGerd Hoffmann     int64_t rpos;
171280c1e1cSGerd Hoffmann     int64_t wpos;
172280c1e1cSGerd Hoffmann     QEMUTimer *buft;
173280c1e1cSGerd Hoffmann     int64_t buft_start;
174d61a4ce8SGerd Hoffmann };
175d61a4ce8SGerd Hoffmann 
176d61a4ce8SGerd Hoffmann struct HDAAudioState {
177d61a4ce8SGerd Hoffmann     HDACodecDevice hda;
178d61a4ce8SGerd Hoffmann     const char *name;
179d61a4ce8SGerd Hoffmann 
180d61a4ce8SGerd Hoffmann     QEMUSoundCard card;
181d61a4ce8SGerd Hoffmann     const desc_codec *desc;
182d61a4ce8SGerd Hoffmann     HDAAudioStream st[4];
183ba43d289SMarc-André Lureau     bool running_compat[16];
184ba43d289SMarc-André Lureau     bool running_real[2 * 16];
185d61a4ce8SGerd Hoffmann 
186d61a4ce8SGerd Hoffmann     /* properties */
187d61a4ce8SGerd Hoffmann     uint32_t debug;
1882690e61eSBandan Das     bool     mixer;
189280c1e1cSGerd Hoffmann     bool     use_timer;
190d61a4ce8SGerd Hoffmann };
191d61a4ce8SGerd Hoffmann 
192280c1e1cSGerd Hoffmann static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
193280c1e1cSGerd Hoffmann {
194cabedbc2SGerd Hoffmann     return 2LL * st->as.nchannels * st->as.freq;
195280c1e1cSGerd Hoffmann }
196280c1e1cSGerd Hoffmann 
197280c1e1cSGerd Hoffmann static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
198280c1e1cSGerd Hoffmann {
1998ced0669SGerd Hoffmann     int64_t limit = B_SIZE / 8;
2008ced0669SGerd Hoffmann     int64_t corr = 0;
2018ced0669SGerd Hoffmann 
2028ced0669SGerd Hoffmann     if (target_pos > limit) {
2038ced0669SGerd Hoffmann         corr = HDA_TIMER_TICKS;
204280c1e1cSGerd Hoffmann     }
2058ced0669SGerd Hoffmann     if (target_pos < -limit) {
2068ced0669SGerd Hoffmann         corr = -HDA_TIMER_TICKS;
2078ced0669SGerd Hoffmann     }
2089d340f67SGerd Hoffmann     if (target_pos < -(2 * limit)) {
2099d340f67SGerd Hoffmann         corr = -(4 * HDA_TIMER_TICKS);
2109d340f67SGerd Hoffmann     }
2118ced0669SGerd Hoffmann     if (corr == 0) {
2128ced0669SGerd Hoffmann         return;
2138ced0669SGerd Hoffmann     }
2148ced0669SGerd Hoffmann 
2158ced0669SGerd Hoffmann     trace_hda_audio_adjust(st->node->name, target_pos);
2163b84967cSGerd Hoffmann     st->buft_start += corr;
217280c1e1cSGerd Hoffmann }
218280c1e1cSGerd Hoffmann 
219280c1e1cSGerd Hoffmann static void hda_audio_input_timer(void *opaque)
220280c1e1cSGerd Hoffmann {
221280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
222280c1e1cSGerd Hoffmann 
223280c1e1cSGerd Hoffmann     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
224280c1e1cSGerd Hoffmann 
2253b84967cSGerd Hoffmann     int64_t buft_start = st->buft_start;
2263b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
2273b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
228280c1e1cSGerd Hoffmann 
229280c1e1cSGerd Hoffmann     int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start)
230280c1e1cSGerd Hoffmann                           / NANOSECONDS_PER_SECOND;
231280c1e1cSGerd Hoffmann     wanted_rpos &= -4; /* IMPORTANT! clip to frames */
232280c1e1cSGerd Hoffmann 
233280c1e1cSGerd Hoffmann     if (wanted_rpos <= rpos) {
234280c1e1cSGerd Hoffmann         /* we already transmitted the data */
235280c1e1cSGerd Hoffmann         goto out_timer;
236280c1e1cSGerd Hoffmann     }
237280c1e1cSGerd Hoffmann 
23858935915SKővágó, Zoltán     int64_t to_transfer = MIN(wpos - rpos, wanted_rpos - rpos);
239280c1e1cSGerd Hoffmann     while (to_transfer) {
240280c1e1cSGerd Hoffmann         uint32_t start = (rpos & B_MASK);
24158935915SKővágó, Zoltán         uint32_t chunk = MIN(B_SIZE - start, to_transfer);
242280c1e1cSGerd Hoffmann         int rc = hda_codec_xfer(
243280c1e1cSGerd Hoffmann                 &st->state->hda, st->stream, false, st->buf + start, chunk);
244280c1e1cSGerd Hoffmann         if (!rc) {
245280c1e1cSGerd Hoffmann             break;
246280c1e1cSGerd Hoffmann         }
247280c1e1cSGerd Hoffmann         rpos += chunk;
248280c1e1cSGerd Hoffmann         to_transfer -= chunk;
2493b84967cSGerd Hoffmann         st->rpos += chunk;
250280c1e1cSGerd Hoffmann     }
251280c1e1cSGerd Hoffmann 
252280c1e1cSGerd Hoffmann out_timer:
253280c1e1cSGerd Hoffmann 
254280c1e1cSGerd Hoffmann     if (st->running) {
255280c1e1cSGerd Hoffmann         timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
256280c1e1cSGerd Hoffmann     }
257280c1e1cSGerd Hoffmann }
258280c1e1cSGerd Hoffmann 
259d61a4ce8SGerd Hoffmann static void hda_audio_input_cb(void *opaque, int avail)
260d61a4ce8SGerd Hoffmann {
261d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
262d61a4ce8SGerd Hoffmann 
2633b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
2643b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
265280c1e1cSGerd Hoffmann 
26658935915SKővágó, Zoltán     int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), avail);
267280c1e1cSGerd Hoffmann 
268280c1e1cSGerd Hoffmann     while (to_transfer) {
269280c1e1cSGerd Hoffmann         uint32_t start = (uint32_t) (wpos & B_MASK);
27058935915SKővágó, Zoltán         uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer);
271280c1e1cSGerd Hoffmann         uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk);
272280c1e1cSGerd Hoffmann         wpos += read;
273280c1e1cSGerd Hoffmann         to_transfer -= read;
2743b84967cSGerd Hoffmann         st->wpos += read;
275280c1e1cSGerd Hoffmann         if (chunk != read) {
276d61a4ce8SGerd Hoffmann             break;
277d61a4ce8SGerd Hoffmann         }
278d61a4ce8SGerd Hoffmann     }
279c435fea7SVolker Rümelin 
280c435fea7SVolker Rümelin     hda_timer_sync_adjust(st, -((wpos - rpos) - (B_SIZE >> 1)));
281280c1e1cSGerd Hoffmann }
282280c1e1cSGerd Hoffmann 
283280c1e1cSGerd Hoffmann static void hda_audio_output_timer(void *opaque)
284280c1e1cSGerd Hoffmann {
285280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
286280c1e1cSGerd Hoffmann 
287280c1e1cSGerd Hoffmann     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
288280c1e1cSGerd Hoffmann 
2893b84967cSGerd Hoffmann     int64_t buft_start = st->buft_start;
2903b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
2913b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
292280c1e1cSGerd Hoffmann 
293280c1e1cSGerd Hoffmann     int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start)
294280c1e1cSGerd Hoffmann                           / NANOSECONDS_PER_SECOND;
295280c1e1cSGerd Hoffmann     wanted_wpos &= -4; /* IMPORTANT! clip to frames */
296280c1e1cSGerd Hoffmann 
297280c1e1cSGerd Hoffmann     if (wanted_wpos <= wpos) {
298280c1e1cSGerd Hoffmann         /* we already received the data */
299280c1e1cSGerd Hoffmann         goto out_timer;
300280c1e1cSGerd Hoffmann     }
301280c1e1cSGerd Hoffmann 
30258935915SKővágó, Zoltán     int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
303280c1e1cSGerd Hoffmann     while (to_transfer) {
304280c1e1cSGerd Hoffmann         uint32_t start = (wpos & B_MASK);
30558935915SKővágó, Zoltán         uint32_t chunk = MIN(B_SIZE - start, to_transfer);
306280c1e1cSGerd Hoffmann         int rc = hda_codec_xfer(
307280c1e1cSGerd Hoffmann                 &st->state->hda, st->stream, true, st->buf + start, chunk);
308d61a4ce8SGerd Hoffmann         if (!rc) {
309d61a4ce8SGerd Hoffmann             break;
310d61a4ce8SGerd Hoffmann         }
311280c1e1cSGerd Hoffmann         wpos += chunk;
312280c1e1cSGerd Hoffmann         to_transfer -= chunk;
3133b84967cSGerd Hoffmann         st->wpos += chunk;
314280c1e1cSGerd Hoffmann     }
315280c1e1cSGerd Hoffmann 
316280c1e1cSGerd Hoffmann out_timer:
317280c1e1cSGerd Hoffmann 
318280c1e1cSGerd Hoffmann     if (st->running) {
319280c1e1cSGerd Hoffmann         timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
320d61a4ce8SGerd Hoffmann     }
321d61a4ce8SGerd Hoffmann }
322d61a4ce8SGerd Hoffmann 
323d61a4ce8SGerd Hoffmann static void hda_audio_output_cb(void *opaque, int avail)
324d61a4ce8SGerd Hoffmann {
325d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
326280c1e1cSGerd Hoffmann 
3273b84967cSGerd Hoffmann     int64_t wpos = st->wpos;
3283b84967cSGerd Hoffmann     int64_t rpos = st->rpos;
329280c1e1cSGerd Hoffmann 
33058935915SKővágó, Zoltán     int64_t to_transfer = MIN(wpos - rpos, avail);
331280c1e1cSGerd Hoffmann 
3324501ee16SGerd Hoffmann     if (wpos - rpos == B_SIZE) {
3334501ee16SGerd Hoffmann         /* drop buffer, reset timer adjust */
3344501ee16SGerd Hoffmann         st->rpos = 0;
3354501ee16SGerd Hoffmann         st->wpos = 0;
3364501ee16SGerd Hoffmann         st->buft_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
3374501ee16SGerd Hoffmann         trace_hda_audio_overrun(st->node->name);
3384501ee16SGerd Hoffmann         return;
3394501ee16SGerd Hoffmann     }
3404501ee16SGerd Hoffmann 
341280c1e1cSGerd Hoffmann     while (to_transfer) {
342280c1e1cSGerd Hoffmann         uint32_t start = (uint32_t) (rpos & B_MASK);
34358935915SKővágó, Zoltán         uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer);
344280c1e1cSGerd Hoffmann         uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
345280c1e1cSGerd Hoffmann         rpos += written;
346280c1e1cSGerd Hoffmann         to_transfer -= written;
3473b84967cSGerd Hoffmann         st->rpos += written;
348280c1e1cSGerd Hoffmann         if (chunk != written) {
349280c1e1cSGerd Hoffmann             break;
350280c1e1cSGerd Hoffmann         }
351280c1e1cSGerd Hoffmann     }
352df016a19SVolker Rümelin 
353df016a19SVolker Rümelin     hda_timer_sync_adjust(st, (wpos - rpos) - (B_SIZE >> 1));
354280c1e1cSGerd Hoffmann }
355280c1e1cSGerd Hoffmann 
356280c1e1cSGerd Hoffmann static void hda_audio_compat_input_cb(void *opaque, int avail)
357280c1e1cSGerd Hoffmann {
358280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
359280c1e1cSGerd Hoffmann     int recv = 0;
360280c1e1cSGerd Hoffmann     int len;
361280c1e1cSGerd Hoffmann     bool rc;
362280c1e1cSGerd Hoffmann 
363280c1e1cSGerd Hoffmann     while (avail - recv >= sizeof(st->compat_buf)) {
364280c1e1cSGerd Hoffmann         if (st->compat_bpos != sizeof(st->compat_buf)) {
365280c1e1cSGerd Hoffmann             len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos,
366280c1e1cSGerd Hoffmann                            sizeof(st->compat_buf) - st->compat_bpos);
367280c1e1cSGerd Hoffmann             st->compat_bpos += len;
368280c1e1cSGerd Hoffmann             recv += len;
369280c1e1cSGerd Hoffmann             if (st->compat_bpos != sizeof(st->compat_buf)) {
370280c1e1cSGerd Hoffmann                 break;
371280c1e1cSGerd Hoffmann             }
372280c1e1cSGerd Hoffmann         }
373280c1e1cSGerd Hoffmann         rc = hda_codec_xfer(&st->state->hda, st->stream, false,
374280c1e1cSGerd Hoffmann                             st->compat_buf, sizeof(st->compat_buf));
375280c1e1cSGerd Hoffmann         if (!rc) {
376280c1e1cSGerd Hoffmann             break;
377280c1e1cSGerd Hoffmann         }
378280c1e1cSGerd Hoffmann         st->compat_bpos = 0;
379280c1e1cSGerd Hoffmann     }
380280c1e1cSGerd Hoffmann }
381280c1e1cSGerd Hoffmann 
382280c1e1cSGerd Hoffmann static void hda_audio_compat_output_cb(void *opaque, int avail)
383280c1e1cSGerd Hoffmann {
384280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
385d61a4ce8SGerd Hoffmann     int sent = 0;
386d61a4ce8SGerd Hoffmann     int len;
387d61a4ce8SGerd Hoffmann     bool rc;
388d61a4ce8SGerd Hoffmann 
389280c1e1cSGerd Hoffmann     while (avail - sent >= sizeof(st->compat_buf)) {
390280c1e1cSGerd Hoffmann         if (st->compat_bpos == sizeof(st->compat_buf)) {
391d61a4ce8SGerd Hoffmann             rc = hda_codec_xfer(&st->state->hda, st->stream, true,
392280c1e1cSGerd Hoffmann                                 st->compat_buf, sizeof(st->compat_buf));
393d61a4ce8SGerd Hoffmann             if (!rc) {
394d61a4ce8SGerd Hoffmann                 break;
395d61a4ce8SGerd Hoffmann             }
396280c1e1cSGerd Hoffmann             st->compat_bpos = 0;
397d61a4ce8SGerd Hoffmann         }
398280c1e1cSGerd Hoffmann         len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos,
399280c1e1cSGerd Hoffmann                         sizeof(st->compat_buf) - st->compat_bpos);
400280c1e1cSGerd Hoffmann         st->compat_bpos += len;
401d61a4ce8SGerd Hoffmann         sent += len;
402280c1e1cSGerd Hoffmann         if (st->compat_bpos != sizeof(st->compat_buf)) {
403d61a4ce8SGerd Hoffmann             break;
404d61a4ce8SGerd Hoffmann         }
405d61a4ce8SGerd Hoffmann     }
406d61a4ce8SGerd Hoffmann }
407d61a4ce8SGerd Hoffmann 
408d61a4ce8SGerd Hoffmann static void hda_audio_set_running(HDAAudioStream *st, bool running)
409d61a4ce8SGerd Hoffmann {
410d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
411d61a4ce8SGerd Hoffmann         return;
412d61a4ce8SGerd Hoffmann     }
413d61a4ce8SGerd Hoffmann     if (st->running == running) {
414d61a4ce8SGerd Hoffmann         return;
415d61a4ce8SGerd Hoffmann     }
416d61a4ce8SGerd Hoffmann     st->running = running;
4170a373bb3SGerd Hoffmann     trace_hda_audio_running(st->node->name, st->stream, st->running);
418280c1e1cSGerd Hoffmann     if (st->state->use_timer) {
419280c1e1cSGerd Hoffmann         if (running) {
420280c1e1cSGerd Hoffmann             int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
421280c1e1cSGerd Hoffmann             st->rpos = 0;
422280c1e1cSGerd Hoffmann             st->wpos = 0;
423280c1e1cSGerd Hoffmann             st->buft_start = now;
424280c1e1cSGerd Hoffmann             timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
425280c1e1cSGerd Hoffmann         } else {
426280c1e1cSGerd Hoffmann             timer_del(st->buft);
427280c1e1cSGerd Hoffmann         }
428280c1e1cSGerd Hoffmann     }
429d61a4ce8SGerd Hoffmann     if (st->output) {
430d61a4ce8SGerd Hoffmann         AUD_set_active_out(st->voice.out, st->running);
431d61a4ce8SGerd Hoffmann     } else {
432d61a4ce8SGerd Hoffmann         AUD_set_active_in(st->voice.in, st->running);
433d61a4ce8SGerd Hoffmann     }
434d61a4ce8SGerd Hoffmann }
435d61a4ce8SGerd Hoffmann 
436d61a4ce8SGerd Hoffmann static void hda_audio_set_amp(HDAAudioStream *st)
437d61a4ce8SGerd Hoffmann {
438d61a4ce8SGerd Hoffmann     bool muted;
439d61a4ce8SGerd Hoffmann     uint32_t left, right;
440d61a4ce8SGerd Hoffmann 
441d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
442d61a4ce8SGerd Hoffmann         return;
443d61a4ce8SGerd Hoffmann     }
444d61a4ce8SGerd Hoffmann 
445d61a4ce8SGerd Hoffmann     muted = st->mute_left && st->mute_right;
446d61a4ce8SGerd Hoffmann     left  = st->mute_left  ? 0 : st->gain_left;
447d61a4ce8SGerd Hoffmann     right = st->mute_right ? 0 : st->gain_right;
448d61a4ce8SGerd Hoffmann 
449d61a4ce8SGerd Hoffmann     left = left * 255 / QEMU_HDA_AMP_STEPS;
450d61a4ce8SGerd Hoffmann     right = right * 255 / QEMU_HDA_AMP_STEPS;
451d61a4ce8SGerd Hoffmann 
4524843877eSGerd Hoffmann     if (!st->state->mixer) {
4534843877eSGerd Hoffmann         return;
4544843877eSGerd Hoffmann     }
455d61a4ce8SGerd Hoffmann     if (st->output) {
456d61a4ce8SGerd Hoffmann         AUD_set_volume_out(st->voice.out, muted, left, right);
457d61a4ce8SGerd Hoffmann     } else {
458d61a4ce8SGerd Hoffmann         AUD_set_volume_in(st->voice.in, muted, left, right);
459d61a4ce8SGerd Hoffmann     }
460d61a4ce8SGerd Hoffmann }
461d61a4ce8SGerd Hoffmann 
462d61a4ce8SGerd Hoffmann static void hda_audio_setup(HDAAudioStream *st)
463d61a4ce8SGerd Hoffmann {
464280c1e1cSGerd Hoffmann     bool use_timer = st->state->use_timer;
465280c1e1cSGerd Hoffmann     audio_callback_fn cb;
466280c1e1cSGerd Hoffmann 
467d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
468d61a4ce8SGerd Hoffmann         return;
469d61a4ce8SGerd Hoffmann     }
470d61a4ce8SGerd Hoffmann 
4710a373bb3SGerd Hoffmann     trace_hda_audio_format(st->node->name, st->as.nchannels,
472d61a4ce8SGerd Hoffmann                            fmt2name[st->as.fmt], st->as.freq);
473d61a4ce8SGerd Hoffmann 
474d61a4ce8SGerd Hoffmann     if (st->output) {
475280c1e1cSGerd Hoffmann         if (use_timer) {
476280c1e1cSGerd Hoffmann             cb = hda_audio_output_cb;
477280c1e1cSGerd Hoffmann             st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
478280c1e1cSGerd Hoffmann                                     hda_audio_output_timer, st);
479d61a4ce8SGerd Hoffmann         } else {
480280c1e1cSGerd Hoffmann             cb = hda_audio_compat_output_cb;
481280c1e1cSGerd Hoffmann         }
482280c1e1cSGerd Hoffmann         st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
483280c1e1cSGerd Hoffmann                                      st->node->name, st, cb, &st->as);
484280c1e1cSGerd Hoffmann     } else {
485280c1e1cSGerd Hoffmann         if (use_timer) {
486280c1e1cSGerd Hoffmann             cb = hda_audio_input_cb;
487280c1e1cSGerd Hoffmann             st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
488280c1e1cSGerd Hoffmann                                     hda_audio_input_timer, st);
489280c1e1cSGerd Hoffmann         } else {
490280c1e1cSGerd Hoffmann             cb = hda_audio_compat_input_cb;
491280c1e1cSGerd Hoffmann         }
492d61a4ce8SGerd Hoffmann         st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
493280c1e1cSGerd Hoffmann                                    st->node->name, st, cb, &st->as);
494d61a4ce8SGerd Hoffmann     }
495d61a4ce8SGerd Hoffmann }
496d61a4ce8SGerd Hoffmann 
497d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
498d61a4ce8SGerd Hoffmann {
499cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
500d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
501d61a4ce8SGerd Hoffmann     const desc_node *node = NULL;
502d61a4ce8SGerd Hoffmann     const desc_param *param;
503d61a4ce8SGerd Hoffmann     uint32_t verb, payload, response, count, shift;
504d61a4ce8SGerd Hoffmann 
505d61a4ce8SGerd Hoffmann     if ((data & 0x70000) == 0x70000) {
506d61a4ce8SGerd Hoffmann         /* 12/8 id/payload */
507d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xfff;
508d61a4ce8SGerd Hoffmann         payload = data & 0x00ff;
509d61a4ce8SGerd Hoffmann     } else {
510d61a4ce8SGerd Hoffmann         /* 4/16 id/payload */
511d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xf00;
512d61a4ce8SGerd Hoffmann         payload = data & 0xffff;
513d61a4ce8SGerd Hoffmann     }
514d61a4ce8SGerd Hoffmann 
515d61a4ce8SGerd Hoffmann     node = hda_codec_find_node(a->desc, nid);
516d61a4ce8SGerd Hoffmann     if (node == NULL) {
517d61a4ce8SGerd Hoffmann         goto fail;
518d61a4ce8SGerd Hoffmann     }
519d61a4ce8SGerd Hoffmann     dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
520a89f364aSAlistair Francis            __func__, nid, node->name, verb, payload);
521d61a4ce8SGerd Hoffmann 
522d61a4ce8SGerd Hoffmann     switch (verb) {
523d61a4ce8SGerd Hoffmann     /* all nodes */
524d61a4ce8SGerd Hoffmann     case AC_VERB_PARAMETERS:
525d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, payload);
526d61a4ce8SGerd Hoffmann         if (param == NULL) {
527d61a4ce8SGerd Hoffmann             goto fail;
528d61a4ce8SGerd Hoffmann         }
529d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, param->val);
530d61a4ce8SGerd Hoffmann         break;
531d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SUBSYSTEM_ID:
532d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, a->desc->iid);
533d61a4ce8SGerd Hoffmann         break;
534d61a4ce8SGerd Hoffmann 
535d61a4ce8SGerd Hoffmann     /* all functions */
536d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONNECT_LIST:
537d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
538d61a4ce8SGerd Hoffmann         count = param ? param->val : 0;
539d61a4ce8SGerd Hoffmann         response = 0;
540d61a4ce8SGerd Hoffmann         shift = 0;
541d61a4ce8SGerd Hoffmann         while (payload < count && shift < 32) {
542d61a4ce8SGerd Hoffmann             response |= node->conn[payload] << shift;
543d61a4ce8SGerd Hoffmann             payload++;
544d61a4ce8SGerd Hoffmann             shift += 8;
545d61a4ce8SGerd Hoffmann         }
546d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
547d61a4ce8SGerd Hoffmann         break;
548d61a4ce8SGerd Hoffmann 
549d61a4ce8SGerd Hoffmann     /* pin widget */
550d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONFIG_DEFAULT:
551d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->config);
552d61a4ce8SGerd Hoffmann         break;
553d61a4ce8SGerd Hoffmann     case AC_VERB_GET_PIN_WIDGET_CONTROL:
554d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->pinctl);
555d61a4ce8SGerd Hoffmann         break;
556d61a4ce8SGerd Hoffmann     case AC_VERB_SET_PIN_WIDGET_CONTROL:
557d61a4ce8SGerd Hoffmann         if (node->pinctl != payload) {
558d61a4ce8SGerd Hoffmann             dprint(a, 1, "unhandled pin control bit\n");
559d61a4ce8SGerd Hoffmann         }
560d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
561d61a4ce8SGerd Hoffmann         break;
562d61a4ce8SGerd Hoffmann 
563d61a4ce8SGerd Hoffmann     /* audio in/out widget */
564d61a4ce8SGerd Hoffmann     case AC_VERB_SET_CHANNEL_STREAMID:
565d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
566d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
567d61a4ce8SGerd Hoffmann             goto fail;
568d61a4ce8SGerd Hoffmann         }
569d61a4ce8SGerd Hoffmann         hda_audio_set_running(st, false);
570d61a4ce8SGerd Hoffmann         st->stream = (payload >> 4) & 0x0f;
571d61a4ce8SGerd Hoffmann         st->channel = payload & 0x0f;
572d61a4ce8SGerd Hoffmann         dprint(a, 2, "%s: stream %d, channel %d\n",
573d61a4ce8SGerd Hoffmann                st->node->name, st->stream, st->channel);
574ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
575d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
576d61a4ce8SGerd Hoffmann         break;
577d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONV:
578d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
579d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
580d61a4ce8SGerd Hoffmann             goto fail;
581d61a4ce8SGerd Hoffmann         }
582d61a4ce8SGerd Hoffmann         response = st->stream << 4 | st->channel;
583d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
584d61a4ce8SGerd Hoffmann         break;
585d61a4ce8SGerd Hoffmann     case AC_VERB_SET_STREAM_FORMAT:
586d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
587d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
588d61a4ce8SGerd Hoffmann             goto fail;
589d61a4ce8SGerd Hoffmann         }
590d61a4ce8SGerd Hoffmann         st->format = payload;
591d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
592d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
593d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
594d61a4ce8SGerd Hoffmann         break;
595d61a4ce8SGerd Hoffmann     case AC_VERB_GET_STREAM_FORMAT:
596d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
597d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
598d61a4ce8SGerd Hoffmann             goto fail;
599d61a4ce8SGerd Hoffmann         }
600d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, st->format);
601d61a4ce8SGerd Hoffmann         break;
602d61a4ce8SGerd Hoffmann     case AC_VERB_GET_AMP_GAIN_MUTE:
603d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
604d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
605d61a4ce8SGerd Hoffmann             goto fail;
606d61a4ce8SGerd Hoffmann         }
607d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_GET_LEFT) {
608d61a4ce8SGerd Hoffmann             response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
609d61a4ce8SGerd Hoffmann         } else {
610d61a4ce8SGerd Hoffmann             response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
611d61a4ce8SGerd Hoffmann         }
612d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
613d61a4ce8SGerd Hoffmann         break;
614d61a4ce8SGerd Hoffmann     case AC_VERB_SET_AMP_GAIN_MUTE:
615d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
616d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
617d61a4ce8SGerd Hoffmann             goto fail;
618d61a4ce8SGerd Hoffmann         }
619d61a4ce8SGerd Hoffmann         dprint(a, 1, "amp (%s): %s%s%s%s index %d  gain %3d %s\n",
620d61a4ce8SGerd Hoffmann                st->node->name,
621d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
622d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INPUT)  ? "i" : "-",
623d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_LEFT)   ? "l" : "-",
624d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_RIGHT)  ? "r" : "-",
625d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
626d61a4ce8SGerd Hoffmann                (payload & AC_AMP_GAIN),
627d61a4ce8SGerd Hoffmann                (payload & AC_AMP_MUTE) ? "muted" : "");
628d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_LEFT) {
629d61a4ce8SGerd Hoffmann             st->gain_left = payload & AC_AMP_GAIN;
630d61a4ce8SGerd Hoffmann             st->mute_left = payload & AC_AMP_MUTE;
631d61a4ce8SGerd Hoffmann         }
632d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_RIGHT) {
633d61a4ce8SGerd Hoffmann             st->gain_right = payload & AC_AMP_GAIN;
634d61a4ce8SGerd Hoffmann             st->mute_right = payload & AC_AMP_MUTE;
635d61a4ce8SGerd Hoffmann         }
636d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
637d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
638d61a4ce8SGerd Hoffmann         break;
639d61a4ce8SGerd Hoffmann 
640d61a4ce8SGerd Hoffmann     /* not supported */
641d61a4ce8SGerd Hoffmann     case AC_VERB_SET_POWER_STATE:
642d61a4ce8SGerd Hoffmann     case AC_VERB_GET_POWER_STATE:
643d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SDI_SELECT:
644d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
645d61a4ce8SGerd Hoffmann         break;
646d61a4ce8SGerd Hoffmann     default:
647d61a4ce8SGerd Hoffmann         goto fail;
648d61a4ce8SGerd Hoffmann     }
649d61a4ce8SGerd Hoffmann     return;
650d61a4ce8SGerd Hoffmann 
651d61a4ce8SGerd Hoffmann fail:
652d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
653a89f364aSAlistair Francis            __func__, nid, node ? node->name : "?", verb, payload);
654d61a4ce8SGerd Hoffmann     hda_codec_response(hda, true, 0);
655d61a4ce8SGerd Hoffmann }
656d61a4ce8SGerd Hoffmann 
657ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
658d61a4ce8SGerd Hoffmann {
659cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
660d61a4ce8SGerd Hoffmann     int s;
661d61a4ce8SGerd Hoffmann 
662ba43d289SMarc-André Lureau     a->running_compat[stnr] = running;
663ba43d289SMarc-André Lureau     a->running_real[output * 16 + stnr] = running;
664d61a4ce8SGerd Hoffmann     for (s = 0; s < ARRAY_SIZE(a->st); s++) {
665d61a4ce8SGerd Hoffmann         if (a->st[s].node == NULL) {
666d61a4ce8SGerd Hoffmann             continue;
667d61a4ce8SGerd Hoffmann         }
668ba43d289SMarc-André Lureau         if (a->st[s].output != output) {
669ba43d289SMarc-André Lureau             continue;
670ba43d289SMarc-André Lureau         }
671d61a4ce8SGerd Hoffmann         if (a->st[s].stream != stnr) {
672d61a4ce8SGerd Hoffmann             continue;
673d61a4ce8SGerd Hoffmann         }
674d61a4ce8SGerd Hoffmann         hda_audio_set_running(&a->st[s], running);
675d61a4ce8SGerd Hoffmann     }
676d61a4ce8SGerd Hoffmann }
677d61a4ce8SGerd Hoffmann 
678b7639b7dSMartin Kletzander static void hda_audio_init(HDACodecDevice *hda,
679b7639b7dSMartin Kletzander                            const struct desc_codec *desc,
680b7639b7dSMartin Kletzander                            Error **errp)
681d61a4ce8SGerd Hoffmann {
682cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
683d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
684d61a4ce8SGerd Hoffmann     const desc_node *node;
685d61a4ce8SGerd Hoffmann     const desc_param *param;
686d61a4ce8SGerd Hoffmann     uint32_t i, type;
687d61a4ce8SGerd Hoffmann 
688*cb94ff5fSMartin Kletzander     if (!AUD_register_card("hda", &a->card, errp)) {
689*cb94ff5fSMartin Kletzander         return;
690*cb94ff5fSMartin Kletzander     }
691*cb94ff5fSMartin Kletzander 
692d61a4ce8SGerd Hoffmann     a->desc = desc;
693f79f2bfcSAnthony Liguori     a->name = object_get_typename(OBJECT(a));
694a89f364aSAlistair Francis     dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad);
695d61a4ce8SGerd Hoffmann 
696d61a4ce8SGerd Hoffmann     for (i = 0; i < a->desc->nnodes; i++) {
697d61a4ce8SGerd Hoffmann         node = a->desc->nodes + i;
698d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
6992ab5bf67SGonglei         if (param == NULL) {
700d61a4ce8SGerd Hoffmann             continue;
7012ab5bf67SGonglei         }
702d61a4ce8SGerd Hoffmann         type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
703d61a4ce8SGerd Hoffmann         switch (type) {
704d61a4ce8SGerd Hoffmann         case AC_WID_AUD_OUT:
705d61a4ce8SGerd Hoffmann         case AC_WID_AUD_IN:
706d61a4ce8SGerd Hoffmann             assert(node->stindex < ARRAY_SIZE(a->st));
707d61a4ce8SGerd Hoffmann             st = a->st + node->stindex;
708d61a4ce8SGerd Hoffmann             st->state = a;
709d61a4ce8SGerd Hoffmann             st->node = node;
710d61a4ce8SGerd Hoffmann             if (type == AC_WID_AUD_OUT) {
711d61a4ce8SGerd Hoffmann                 /* unmute output by default */
712d61a4ce8SGerd Hoffmann                 st->gain_left = QEMU_HDA_AMP_STEPS;
713d61a4ce8SGerd Hoffmann                 st->gain_right = QEMU_HDA_AMP_STEPS;
714280c1e1cSGerd Hoffmann                 st->compat_bpos = sizeof(st->compat_buf);
715d61a4ce8SGerd Hoffmann                 st->output = true;
716d61a4ce8SGerd Hoffmann             } else {
717d61a4ce8SGerd Hoffmann                 st->output = false;
718d61a4ce8SGerd Hoffmann             }
719d61a4ce8SGerd Hoffmann             st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
720d61a4ce8SGerd Hoffmann                 (1 << AC_FMT_CHAN_SHIFT);
721d61a4ce8SGerd Hoffmann             hda_codec_parse_fmt(st->format, &st->as);
722d61a4ce8SGerd Hoffmann             hda_audio_setup(st);
723d61a4ce8SGerd Hoffmann             break;
724d61a4ce8SGerd Hoffmann         }
725d61a4ce8SGerd Hoffmann     }
726d61a4ce8SGerd Hoffmann }
727d61a4ce8SGerd Hoffmann 
7285eaa8e1eSZihan Yang static void hda_audio_exit(HDACodecDevice *hda)
729129dcd2cSGerd Hoffmann {
730cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
731129dcd2cSGerd Hoffmann     HDAAudioStream *st;
732129dcd2cSGerd Hoffmann     int i;
733129dcd2cSGerd Hoffmann 
734a89f364aSAlistair Francis     dprint(a, 1, "%s\n", __func__);
735129dcd2cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
736129dcd2cSGerd Hoffmann         st = a->st + i;
737129dcd2cSGerd Hoffmann         if (st->node == NULL) {
738129dcd2cSGerd Hoffmann             continue;
739129dcd2cSGerd Hoffmann         }
740280c1e1cSGerd Hoffmann         if (a->use_timer) {
741280c1e1cSGerd Hoffmann             timer_del(st->buft);
742280c1e1cSGerd Hoffmann         }
743129dcd2cSGerd Hoffmann         if (st->output) {
744129dcd2cSGerd Hoffmann             AUD_close_out(&a->card, st->voice.out);
745129dcd2cSGerd Hoffmann         } else {
746129dcd2cSGerd Hoffmann             AUD_close_in(&a->card, st->voice.in);
747129dcd2cSGerd Hoffmann         }
748129dcd2cSGerd Hoffmann     }
749129dcd2cSGerd Hoffmann     AUD_remove_card(&a->card);
750129dcd2cSGerd Hoffmann }
751129dcd2cSGerd Hoffmann 
752d61a4ce8SGerd Hoffmann static int hda_audio_post_load(void *opaque, int version)
753d61a4ce8SGerd Hoffmann {
754d61a4ce8SGerd Hoffmann     HDAAudioState *a = opaque;
755d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
756d61a4ce8SGerd Hoffmann     int i;
757d61a4ce8SGerd Hoffmann 
758a89f364aSAlistair Francis     dprint(a, 1, "%s\n", __func__);
759ba43d289SMarc-André Lureau     if (version == 1) {
760ba43d289SMarc-André Lureau         /* assume running_compat[] is for output streams */
761ba43d289SMarc-André Lureau         for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
762ba43d289SMarc-André Lureau             a->running_real[16 + i] = a->running_compat[i];
763ba43d289SMarc-André Lureau     }
764ba43d289SMarc-André Lureau 
765d61a4ce8SGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
766d61a4ce8SGerd Hoffmann         st = a->st + i;
767d61a4ce8SGerd Hoffmann         if (st->node == NULL)
768d61a4ce8SGerd Hoffmann             continue;
769d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
770d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
771d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
772ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
773d61a4ce8SGerd Hoffmann     }
774d61a4ce8SGerd Hoffmann     return 0;
775d61a4ce8SGerd Hoffmann }
776d61a4ce8SGerd Hoffmann 
77739e6a38cSGerd Hoffmann static void hda_audio_reset(DeviceState *dev)
77839e6a38cSGerd Hoffmann {
779cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(dev);
78039e6a38cSGerd Hoffmann     HDAAudioStream *st;
78139e6a38cSGerd Hoffmann     int i;
78239e6a38cSGerd Hoffmann 
78339e6a38cSGerd Hoffmann     dprint(a, 1, "%s\n", __func__);
78439e6a38cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
78539e6a38cSGerd Hoffmann         st = a->st + i;
78639e6a38cSGerd Hoffmann         if (st->node != NULL) {
78739e6a38cSGerd Hoffmann             hda_audio_set_running(st, false);
78839e6a38cSGerd Hoffmann         }
78939e6a38cSGerd Hoffmann     }
79039e6a38cSGerd Hoffmann }
79139e6a38cSGerd Hoffmann 
792280c1e1cSGerd Hoffmann static bool vmstate_hda_audio_stream_buf_needed(void *opaque)
793280c1e1cSGerd Hoffmann {
794280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
79567fa1f57SDr. David Alan Gilbert     return st->state && st->state->use_timer;
796280c1e1cSGerd Hoffmann }
797280c1e1cSGerd Hoffmann 
798280c1e1cSGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream_buf = {
799280c1e1cSGerd Hoffmann     .name = "hda-audio-stream/buffer",
800280c1e1cSGerd Hoffmann     .version_id = 1,
801280c1e1cSGerd Hoffmann     .needed = vmstate_hda_audio_stream_buf_needed,
802280c1e1cSGerd Hoffmann     .fields = (VMStateField[]) {
803280c1e1cSGerd Hoffmann         VMSTATE_BUFFER(buf, HDAAudioStream),
804280c1e1cSGerd Hoffmann         VMSTATE_INT64(rpos, HDAAudioStream),
805280c1e1cSGerd Hoffmann         VMSTATE_INT64(wpos, HDAAudioStream),
806280c1e1cSGerd Hoffmann         VMSTATE_TIMER_PTR(buft, HDAAudioStream),
807280c1e1cSGerd Hoffmann         VMSTATE_INT64(buft_start, HDAAudioStream),
808280c1e1cSGerd Hoffmann         VMSTATE_END_OF_LIST()
809280c1e1cSGerd Hoffmann     }
810280c1e1cSGerd Hoffmann };
811280c1e1cSGerd Hoffmann 
812d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = {
813d61a4ce8SGerd Hoffmann     .name = "hda-audio-stream",
814d61a4ce8SGerd Hoffmann     .version_id = 1,
815d61a4ce8SGerd Hoffmann     .fields = (VMStateField[]) {
816d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(stream, HDAAudioStream),
817d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(channel, HDAAudioStream),
818d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(format, HDAAudioStream),
819d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_left, HDAAudioStream),
820d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_right, HDAAudioStream),
821d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_left, HDAAudioStream),
822d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_right, HDAAudioStream),
823280c1e1cSGerd Hoffmann         VMSTATE_UINT32(compat_bpos, HDAAudioStream),
824280c1e1cSGerd Hoffmann         VMSTATE_BUFFER(compat_buf, HDAAudioStream),
825d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
826280c1e1cSGerd Hoffmann     },
827280c1e1cSGerd Hoffmann     .subsections = (const VMStateDescription * []) {
828280c1e1cSGerd Hoffmann         &vmstate_hda_audio_stream_buf,
829280c1e1cSGerd Hoffmann         NULL
830d61a4ce8SGerd Hoffmann     }
831d61a4ce8SGerd Hoffmann };
832d61a4ce8SGerd Hoffmann 
833d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio = {
834d61a4ce8SGerd Hoffmann     .name = "hda-audio",
835ba43d289SMarc-André Lureau     .version_id = 2,
836d61a4ce8SGerd Hoffmann     .post_load = hda_audio_post_load,
837d61a4ce8SGerd Hoffmann     .fields = (VMStateField[]) {
838d61a4ce8SGerd Hoffmann         VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
839d61a4ce8SGerd Hoffmann                              vmstate_hda_audio_stream,
840d61a4ce8SGerd Hoffmann                              HDAAudioStream),
841ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
842ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
843d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
844d61a4ce8SGerd Hoffmann     }
845d61a4ce8SGerd Hoffmann };
846d61a4ce8SGerd Hoffmann 
847d61a4ce8SGerd Hoffmann static Property hda_audio_properties[] = {
84888e47b9aSKővágó, Zoltán     DEFINE_AUDIO_PROPERTIES(HDAAudioState, card),
849d61a4ce8SGerd Hoffmann     DEFINE_PROP_UINT32("debug", HDAAudioState, debug,   0),
8502690e61eSBandan Das     DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer,  true),
851bc753dc0SGerd Hoffmann     DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer,  true),
852d61a4ce8SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
853d61a4ce8SGerd Hoffmann };
854d61a4ce8SGerd Hoffmann 
855b7639b7dSMartin Kletzander static void hda_audio_init_output(HDACodecDevice *hda, Error **errp)
856d61a4ce8SGerd Hoffmann {
857cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
858b7639b7dSMartin Kletzander     const struct desc_codec *desc = &output_nomixemu;
8592690e61eSBandan Das 
8602690e61eSBandan Das     if (!a->mixer) {
861b7639b7dSMartin Kletzander         desc = &output_mixemu;
862d61a4ce8SGerd Hoffmann     }
863d61a4ce8SGerd Hoffmann 
864b7639b7dSMartin Kletzander     hda_audio_init(hda, desc, errp);
865b7639b7dSMartin Kletzander }
866b7639b7dSMartin Kletzander 
867b7639b7dSMartin Kletzander static void hda_audio_init_duplex(HDACodecDevice *hda, Error **errp)
868d61a4ce8SGerd Hoffmann {
869cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
870b7639b7dSMartin Kletzander     const struct desc_codec *desc = &duplex_nomixemu;
8712690e61eSBandan Das 
8722690e61eSBandan Das     if (!a->mixer) {
873b7639b7dSMartin Kletzander         desc = &duplex_mixemu;
874d61a4ce8SGerd Hoffmann     }
875d61a4ce8SGerd Hoffmann 
876b7639b7dSMartin Kletzander     hda_audio_init(hda, desc, errp);
877b7639b7dSMartin Kletzander }
878b7639b7dSMartin Kletzander 
879b7639b7dSMartin Kletzander static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp)
88020110065SGerd Hoffmann {
881cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
882b7639b7dSMartin Kletzander     const struct desc_codec *desc = &micro_nomixemu;
8832690e61eSBandan Das 
8842690e61eSBandan Das     if (!a->mixer) {
885b7639b7dSMartin Kletzander         desc = &micro_mixemu;
8862690e61eSBandan Das     }
887b7639b7dSMartin Kletzander 
888b7639b7dSMartin Kletzander     hda_audio_init(hda, desc, errp);
88920110065SGerd Hoffmann }
89020110065SGerd Hoffmann 
891cd6c8830SGerd Hoffmann static void hda_audio_base_class_init(ObjectClass *klass, void *data)
892cd6c8830SGerd Hoffmann {
893cd6c8830SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
894cd6c8830SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
895cd6c8830SGerd Hoffmann 
896cd6c8830SGerd Hoffmann     k->exit = hda_audio_exit;
897cd6c8830SGerd Hoffmann     k->command = hda_audio_command;
898cd6c8830SGerd Hoffmann     k->stream = hda_audio_stream;
899cd6c8830SGerd Hoffmann     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
900cd6c8830SGerd Hoffmann     dc->reset = hda_audio_reset;
901cd6c8830SGerd Hoffmann     dc->vmsd = &vmstate_hda_audio;
9024f67d30bSMarc-André Lureau     device_class_set_props(dc, hda_audio_properties);
903cd6c8830SGerd Hoffmann }
904cd6c8830SGerd Hoffmann 
905cd6c8830SGerd Hoffmann static const TypeInfo hda_audio_info = {
906cd6c8830SGerd Hoffmann     .name          = TYPE_HDA_AUDIO,
907cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_CODEC_DEVICE,
908edf632eeSEduardo Habkost     .instance_size = sizeof(HDAAudioState),
909cd6c8830SGerd Hoffmann     .class_init    = hda_audio_base_class_init,
910cd6c8830SGerd Hoffmann     .abstract      = true,
911cd6c8830SGerd Hoffmann };
912cd6c8830SGerd Hoffmann 
913dbaa7904SAnthony Liguori static void hda_audio_output_class_init(ObjectClass *klass, void *data)
914dbaa7904SAnthony Liguori {
91539bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
916dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
917dbaa7904SAnthony Liguori 
918dbaa7904SAnthony Liguori     k->init = hda_audio_init_output;
91920110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, output-only (line-out)";
920dbaa7904SAnthony Liguori }
921dbaa7904SAnthony Liguori 
9228c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = {
923dbaa7904SAnthony Liguori     .name          = "hda-output",
924cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
925dbaa7904SAnthony Liguori     .class_init    = hda_audio_output_class_init,
926d61a4ce8SGerd Hoffmann };
927d61a4ce8SGerd Hoffmann 
928dbaa7904SAnthony Liguori static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
929dbaa7904SAnthony Liguori {
93039bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
931dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
932dbaa7904SAnthony Liguori 
933dbaa7904SAnthony Liguori     k->init = hda_audio_init_duplex;
93420110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
935dbaa7904SAnthony Liguori }
936dbaa7904SAnthony Liguori 
9378c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = {
938dbaa7904SAnthony Liguori     .name          = "hda-duplex",
939cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
940dbaa7904SAnthony Liguori     .class_init    = hda_audio_duplex_class_init,
941d61a4ce8SGerd Hoffmann };
942d61a4ce8SGerd Hoffmann 
94320110065SGerd Hoffmann static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
94420110065SGerd Hoffmann {
94520110065SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
94620110065SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
94720110065SGerd Hoffmann 
94820110065SGerd Hoffmann     k->init = hda_audio_init_micro;
94920110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
95020110065SGerd Hoffmann }
95120110065SGerd Hoffmann 
9528c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = {
95320110065SGerd Hoffmann     .name          = "hda-micro",
954cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
95520110065SGerd Hoffmann     .class_init    = hda_audio_micro_class_init,
95620110065SGerd Hoffmann };
95720110065SGerd Hoffmann 
95883f7d43aSAndreas Färber static void hda_audio_register_types(void)
959d61a4ce8SGerd Hoffmann {
960cd6c8830SGerd Hoffmann     type_register_static(&hda_audio_info);
96139bffca2SAnthony Liguori     type_register_static(&hda_audio_output_info);
96239bffca2SAnthony Liguori     type_register_static(&hda_audio_duplex_info);
96320110065SGerd Hoffmann     type_register_static(&hda_audio_micro_info);
964d61a4ce8SGerd Hoffmann }
96583f7d43aSAndreas Färber 
96683f7d43aSAndreas Färber type_init(hda_audio_register_types)
967