xref: /qemu/hw/audio/hda-codec.c (revision 280c1e1cdb24d80ecdfcdfc679ccc5e8ed7af45d)
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"
21*280c1e1cSGerd Hoffmann #include "qemu/atomic.h"
2283c9f4caSPaolo Bonzini #include "hw/hw.h"
2383c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
2447b43a1fSPaolo Bonzini #include "intel-hda.h"
2547b43a1fSPaolo Bonzini #include "intel-hda-defs.h"
26d61a4ce8SGerd Hoffmann #include "audio/audio.h"
27d61a4ce8SGerd Hoffmann 
28d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
29d61a4ce8SGerd Hoffmann 
30d61a4ce8SGerd Hoffmann typedef struct desc_param {
31d61a4ce8SGerd Hoffmann     uint32_t id;
32d61a4ce8SGerd Hoffmann     uint32_t val;
33d61a4ce8SGerd Hoffmann } desc_param;
34d61a4ce8SGerd Hoffmann 
35d61a4ce8SGerd Hoffmann typedef struct desc_node {
36d61a4ce8SGerd Hoffmann     uint32_t nid;
37d61a4ce8SGerd Hoffmann     const char *name;
38d61a4ce8SGerd Hoffmann     const desc_param *params;
39d61a4ce8SGerd Hoffmann     uint32_t nparams;
40d61a4ce8SGerd Hoffmann     uint32_t config;
41d61a4ce8SGerd Hoffmann     uint32_t pinctl;
42d61a4ce8SGerd Hoffmann     uint32_t *conn;
43d61a4ce8SGerd Hoffmann     uint32_t stindex;
44d61a4ce8SGerd Hoffmann } desc_node;
45d61a4ce8SGerd Hoffmann 
46d61a4ce8SGerd Hoffmann typedef struct desc_codec {
47d61a4ce8SGerd Hoffmann     const char *name;
48d61a4ce8SGerd Hoffmann     uint32_t iid;
49d61a4ce8SGerd Hoffmann     const desc_node *nodes;
50d61a4ce8SGerd Hoffmann     uint32_t nnodes;
51d61a4ce8SGerd Hoffmann } desc_codec;
52d61a4ce8SGerd Hoffmann 
53d61a4ce8SGerd Hoffmann static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
54d61a4ce8SGerd Hoffmann {
55d61a4ce8SGerd Hoffmann     int i;
56d61a4ce8SGerd Hoffmann 
57d61a4ce8SGerd Hoffmann     for (i = 0; i < node->nparams; i++) {
58d61a4ce8SGerd Hoffmann         if (node->params[i].id == id) {
59d61a4ce8SGerd Hoffmann             return &node->params[i];
60d61a4ce8SGerd Hoffmann         }
61d61a4ce8SGerd Hoffmann     }
62d61a4ce8SGerd Hoffmann     return NULL;
63d61a4ce8SGerd Hoffmann }
64d61a4ce8SGerd Hoffmann 
65d61a4ce8SGerd Hoffmann static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
66d61a4ce8SGerd Hoffmann {
67d61a4ce8SGerd Hoffmann     int i;
68d61a4ce8SGerd Hoffmann 
69d61a4ce8SGerd Hoffmann     for (i = 0; i < codec->nnodes; i++) {
70d61a4ce8SGerd Hoffmann         if (codec->nodes[i].nid == nid) {
71d61a4ce8SGerd Hoffmann             return &codec->nodes[i];
72d61a4ce8SGerd Hoffmann         }
73d61a4ce8SGerd Hoffmann     }
74d61a4ce8SGerd Hoffmann     return NULL;
75d61a4ce8SGerd Hoffmann }
76d61a4ce8SGerd Hoffmann 
77d61a4ce8SGerd Hoffmann static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
78d61a4ce8SGerd Hoffmann {
79d61a4ce8SGerd Hoffmann     if (format & AC_FMT_TYPE_NON_PCM) {
80d61a4ce8SGerd Hoffmann         return;
81d61a4ce8SGerd Hoffmann     }
82d61a4ce8SGerd Hoffmann 
83d61a4ce8SGerd Hoffmann     as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
84d61a4ce8SGerd Hoffmann 
85d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
86d61a4ce8SGerd Hoffmann     case 1: as->freq *= 2; break;
87d61a4ce8SGerd Hoffmann     case 2: as->freq *= 3; break;
88d61a4ce8SGerd Hoffmann     case 3: as->freq *= 4; break;
89d61a4ce8SGerd Hoffmann     }
90d61a4ce8SGerd Hoffmann 
91d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
92d61a4ce8SGerd Hoffmann     case 1: as->freq /= 2; break;
93d61a4ce8SGerd Hoffmann     case 2: as->freq /= 3; break;
94d61a4ce8SGerd Hoffmann     case 3: as->freq /= 4; break;
95d61a4ce8SGerd Hoffmann     case 4: as->freq /= 5; break;
96d61a4ce8SGerd Hoffmann     case 5: as->freq /= 6; break;
97d61a4ce8SGerd Hoffmann     case 6: as->freq /= 7; break;
98d61a4ce8SGerd Hoffmann     case 7: as->freq /= 8; break;
99d61a4ce8SGerd Hoffmann     }
100d61a4ce8SGerd Hoffmann 
101d61a4ce8SGerd Hoffmann     switch (format & AC_FMT_BITS_MASK) {
102d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_8:  as->fmt = AUD_FMT_S8;  break;
103d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
104d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
105d61a4ce8SGerd Hoffmann     }
106d61a4ce8SGerd Hoffmann 
107d61a4ce8SGerd Hoffmann     as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
108d61a4ce8SGerd Hoffmann }
109d61a4ce8SGerd Hoffmann 
110d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
111d61a4ce8SGerd Hoffmann /*
112d61a4ce8SGerd Hoffmann  * HDA codec descriptions
113d61a4ce8SGerd Hoffmann  */
114d61a4ce8SGerd Hoffmann 
115d61a4ce8SGerd Hoffmann /* some defines */
116d61a4ce8SGerd Hoffmann 
117d61a4ce8SGerd Hoffmann #define QEMU_HDA_ID_VENDOR  0x1af4
118d61a4ce8SGerd Hoffmann #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
119d61a4ce8SGerd Hoffmann                               0x1fc /* 16 -> 96 kHz */)
120d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_NONE    (0)
121d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_STEPS   0x4a
122d61a4ce8SGerd Hoffmann 
1232690e61eSBandan Das #define   PARAM mixemu
1242690e61eSBandan Das #define   HDA_MIXER
1257953793cSBandan Das #include "hda-codec-common.h"
1262690e61eSBandan Das 
1272690e61eSBandan Das #define   PARAM nomixemu
1282690e61eSBandan Das #include  "hda-codec-common.h"
1292690e61eSBandan Das 
130*280c1e1cSGerd Hoffmann #define HDA_TIMER_TICKS (SCALE_MS)
131*280c1e1cSGerd Hoffmann #define MAX_CORR (SCALE_US * 100)
132*280c1e1cSGerd Hoffmann #define B_SIZE sizeof(st->buf)
133*280c1e1cSGerd Hoffmann #define B_MASK (sizeof(st->buf) - 1)
134*280c1e1cSGerd Hoffmann 
135d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
136d61a4ce8SGerd Hoffmann 
137d61a4ce8SGerd Hoffmann static const char *fmt2name[] = {
138d61a4ce8SGerd Hoffmann     [ AUD_FMT_U8  ] = "PCM-U8",
139d61a4ce8SGerd Hoffmann     [ AUD_FMT_S8  ] = "PCM-S8",
140d61a4ce8SGerd Hoffmann     [ AUD_FMT_U16 ] = "PCM-U16",
141d61a4ce8SGerd Hoffmann     [ AUD_FMT_S16 ] = "PCM-S16",
142d61a4ce8SGerd Hoffmann     [ AUD_FMT_U32 ] = "PCM-U32",
143d61a4ce8SGerd Hoffmann     [ AUD_FMT_S32 ] = "PCM-S32",
144d61a4ce8SGerd Hoffmann };
145d61a4ce8SGerd Hoffmann 
146d61a4ce8SGerd Hoffmann typedef struct HDAAudioState HDAAudioState;
147d61a4ce8SGerd Hoffmann typedef struct HDAAudioStream HDAAudioStream;
148d61a4ce8SGerd Hoffmann 
149d61a4ce8SGerd Hoffmann struct HDAAudioStream {
150d61a4ce8SGerd Hoffmann     HDAAudioState *state;
151d61a4ce8SGerd Hoffmann     const desc_node *node;
152d61a4ce8SGerd Hoffmann     bool output, running;
153d61a4ce8SGerd Hoffmann     uint32_t stream;
154d61a4ce8SGerd Hoffmann     uint32_t channel;
155d61a4ce8SGerd Hoffmann     uint32_t format;
156d61a4ce8SGerd Hoffmann     uint32_t gain_left, gain_right;
157d61a4ce8SGerd Hoffmann     bool mute_left, mute_right;
158d61a4ce8SGerd Hoffmann     struct audsettings as;
159d61a4ce8SGerd Hoffmann     union {
160d61a4ce8SGerd Hoffmann         SWVoiceIn *in;
161d61a4ce8SGerd Hoffmann         SWVoiceOut *out;
162d61a4ce8SGerd Hoffmann     } voice;
163*280c1e1cSGerd Hoffmann     uint8_t compat_buf[HDA_BUFFER_SIZE];
164*280c1e1cSGerd Hoffmann     uint32_t compat_bpos;
165*280c1e1cSGerd Hoffmann     uint8_t buf[8192]; /* size must be power of two */
166*280c1e1cSGerd Hoffmann     int64_t rpos;
167*280c1e1cSGerd Hoffmann     int64_t wpos;
168*280c1e1cSGerd Hoffmann     QEMUTimer *buft;
169*280c1e1cSGerd Hoffmann     int64_t buft_start;
170d61a4ce8SGerd Hoffmann };
171d61a4ce8SGerd Hoffmann 
172cd6c8830SGerd Hoffmann #define TYPE_HDA_AUDIO "hda-audio"
173cd6c8830SGerd Hoffmann #define HDA_AUDIO(obj) OBJECT_CHECK(HDAAudioState, (obj), TYPE_HDA_AUDIO)
174cd6c8830SGerd Hoffmann 
175d61a4ce8SGerd Hoffmann struct HDAAudioState {
176d61a4ce8SGerd Hoffmann     HDACodecDevice hda;
177d61a4ce8SGerd Hoffmann     const char *name;
178d61a4ce8SGerd Hoffmann 
179d61a4ce8SGerd Hoffmann     QEMUSoundCard card;
180d61a4ce8SGerd Hoffmann     const desc_codec *desc;
181d61a4ce8SGerd Hoffmann     HDAAudioStream st[4];
182ba43d289SMarc-André Lureau     bool running_compat[16];
183ba43d289SMarc-André Lureau     bool running_real[2 * 16];
184d61a4ce8SGerd Hoffmann 
185d61a4ce8SGerd Hoffmann     /* properties */
186d61a4ce8SGerd Hoffmann     uint32_t debug;
1872690e61eSBandan Das     bool     mixer;
188*280c1e1cSGerd Hoffmann     bool     use_timer;
189d61a4ce8SGerd Hoffmann };
190d61a4ce8SGerd Hoffmann 
191*280c1e1cSGerd Hoffmann static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
192*280c1e1cSGerd Hoffmann {
193*280c1e1cSGerd Hoffmann     return 2 * st->as.nchannels * st->as.freq;
194*280c1e1cSGerd Hoffmann }
195*280c1e1cSGerd Hoffmann 
196*280c1e1cSGerd Hoffmann static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
197*280c1e1cSGerd Hoffmann {
198*280c1e1cSGerd Hoffmann     int64_t corr =
199*280c1e1cSGerd Hoffmann         NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st);
200*280c1e1cSGerd Hoffmann     if (corr > MAX_CORR) {
201*280c1e1cSGerd Hoffmann         corr = MAX_CORR;
202*280c1e1cSGerd Hoffmann     } else if (corr < -MAX_CORR) {
203*280c1e1cSGerd Hoffmann         corr = -MAX_CORR;
204*280c1e1cSGerd Hoffmann     }
205*280c1e1cSGerd Hoffmann     atomic_fetch_add(&st->buft_start, corr);
206*280c1e1cSGerd Hoffmann }
207*280c1e1cSGerd Hoffmann 
208*280c1e1cSGerd Hoffmann static void hda_audio_input_timer(void *opaque)
209*280c1e1cSGerd Hoffmann {
210*280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
211*280c1e1cSGerd Hoffmann 
212*280c1e1cSGerd Hoffmann     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
213*280c1e1cSGerd Hoffmann 
214*280c1e1cSGerd Hoffmann     int64_t buft_start = atomic_fetch_add(&st->buft_start, 0);
215*280c1e1cSGerd Hoffmann     int64_t wpos = atomic_fetch_add(&st->wpos, 0);
216*280c1e1cSGerd Hoffmann     int64_t rpos = atomic_fetch_add(&st->rpos, 0);
217*280c1e1cSGerd Hoffmann 
218*280c1e1cSGerd Hoffmann     int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start)
219*280c1e1cSGerd Hoffmann                           / NANOSECONDS_PER_SECOND;
220*280c1e1cSGerd Hoffmann     wanted_rpos &= -4; /* IMPORTANT! clip to frames */
221*280c1e1cSGerd Hoffmann 
222*280c1e1cSGerd Hoffmann     if (wanted_rpos <= rpos) {
223*280c1e1cSGerd Hoffmann         /* we already transmitted the data */
224*280c1e1cSGerd Hoffmann         goto out_timer;
225*280c1e1cSGerd Hoffmann     }
226*280c1e1cSGerd Hoffmann 
227*280c1e1cSGerd Hoffmann     int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos);
228*280c1e1cSGerd Hoffmann     while (to_transfer) {
229*280c1e1cSGerd Hoffmann         uint32_t start = (rpos & B_MASK);
230*280c1e1cSGerd Hoffmann         uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
231*280c1e1cSGerd Hoffmann         int rc = hda_codec_xfer(
232*280c1e1cSGerd Hoffmann                 &st->state->hda, st->stream, false, st->buf + start, chunk);
233*280c1e1cSGerd Hoffmann         if (!rc) {
234*280c1e1cSGerd Hoffmann             break;
235*280c1e1cSGerd Hoffmann         }
236*280c1e1cSGerd Hoffmann         rpos += chunk;
237*280c1e1cSGerd Hoffmann         to_transfer -= chunk;
238*280c1e1cSGerd Hoffmann         atomic_fetch_add(&st->rpos, chunk);
239*280c1e1cSGerd Hoffmann     }
240*280c1e1cSGerd Hoffmann 
241*280c1e1cSGerd Hoffmann out_timer:
242*280c1e1cSGerd Hoffmann 
243*280c1e1cSGerd Hoffmann     if (st->running) {
244*280c1e1cSGerd Hoffmann         timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
245*280c1e1cSGerd Hoffmann     }
246*280c1e1cSGerd Hoffmann }
247*280c1e1cSGerd Hoffmann 
248d61a4ce8SGerd Hoffmann static void hda_audio_input_cb(void *opaque, int avail)
249d61a4ce8SGerd Hoffmann {
250d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
251d61a4ce8SGerd Hoffmann 
252*280c1e1cSGerd Hoffmann     int64_t wpos = atomic_fetch_add(&st->wpos, 0);
253*280c1e1cSGerd Hoffmann     int64_t rpos = atomic_fetch_add(&st->rpos, 0);
254*280c1e1cSGerd Hoffmann 
255*280c1e1cSGerd Hoffmann     int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail);
256*280c1e1cSGerd Hoffmann 
257*280c1e1cSGerd Hoffmann     hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1)));
258*280c1e1cSGerd Hoffmann 
259*280c1e1cSGerd Hoffmann     while (to_transfer) {
260*280c1e1cSGerd Hoffmann         uint32_t start = (uint32_t) (wpos & B_MASK);
261*280c1e1cSGerd Hoffmann         uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
262*280c1e1cSGerd Hoffmann         uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk);
263*280c1e1cSGerd Hoffmann         wpos += read;
264*280c1e1cSGerd Hoffmann         to_transfer -= read;
265*280c1e1cSGerd Hoffmann         atomic_fetch_add(&st->wpos, read);
266*280c1e1cSGerd Hoffmann         if (chunk != read) {
267d61a4ce8SGerd Hoffmann             break;
268d61a4ce8SGerd Hoffmann         }
269d61a4ce8SGerd Hoffmann     }
270*280c1e1cSGerd Hoffmann }
271*280c1e1cSGerd Hoffmann 
272*280c1e1cSGerd Hoffmann static void hda_audio_output_timer(void *opaque)
273*280c1e1cSGerd Hoffmann {
274*280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
275*280c1e1cSGerd Hoffmann 
276*280c1e1cSGerd Hoffmann     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
277*280c1e1cSGerd Hoffmann 
278*280c1e1cSGerd Hoffmann     int64_t buft_start = atomic_fetch_add(&st->buft_start, 0);
279*280c1e1cSGerd Hoffmann     int64_t wpos = atomic_fetch_add(&st->wpos, 0);
280*280c1e1cSGerd Hoffmann     int64_t rpos = atomic_fetch_add(&st->rpos, 0);
281*280c1e1cSGerd Hoffmann 
282*280c1e1cSGerd Hoffmann     int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start)
283*280c1e1cSGerd Hoffmann                           / NANOSECONDS_PER_SECOND;
284*280c1e1cSGerd Hoffmann     wanted_wpos &= -4; /* IMPORTANT! clip to frames */
285*280c1e1cSGerd Hoffmann 
286*280c1e1cSGerd Hoffmann     if (wanted_wpos <= wpos) {
287*280c1e1cSGerd Hoffmann         /* we already received the data */
288*280c1e1cSGerd Hoffmann         goto out_timer;
289*280c1e1cSGerd Hoffmann     }
290*280c1e1cSGerd Hoffmann 
291*280c1e1cSGerd Hoffmann     int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
292*280c1e1cSGerd Hoffmann     while (to_transfer) {
293*280c1e1cSGerd Hoffmann         uint32_t start = (wpos & B_MASK);
294*280c1e1cSGerd Hoffmann         uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
295*280c1e1cSGerd Hoffmann         int rc = hda_codec_xfer(
296*280c1e1cSGerd Hoffmann                 &st->state->hda, st->stream, true, st->buf + start, chunk);
297d61a4ce8SGerd Hoffmann         if (!rc) {
298d61a4ce8SGerd Hoffmann             break;
299d61a4ce8SGerd Hoffmann         }
300*280c1e1cSGerd Hoffmann         wpos += chunk;
301*280c1e1cSGerd Hoffmann         to_transfer -= chunk;
302*280c1e1cSGerd Hoffmann         atomic_fetch_add(&st->wpos, chunk);
303*280c1e1cSGerd Hoffmann     }
304*280c1e1cSGerd Hoffmann 
305*280c1e1cSGerd Hoffmann out_timer:
306*280c1e1cSGerd Hoffmann 
307*280c1e1cSGerd Hoffmann     if (st->running) {
308*280c1e1cSGerd Hoffmann         timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
309d61a4ce8SGerd Hoffmann     }
310d61a4ce8SGerd Hoffmann }
311d61a4ce8SGerd Hoffmann 
312d61a4ce8SGerd Hoffmann static void hda_audio_output_cb(void *opaque, int avail)
313d61a4ce8SGerd Hoffmann {
314d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
315*280c1e1cSGerd Hoffmann 
316*280c1e1cSGerd Hoffmann     int64_t wpos = atomic_fetch_add(&st->wpos, 0);
317*280c1e1cSGerd Hoffmann     int64_t rpos = atomic_fetch_add(&st->rpos, 0);
318*280c1e1cSGerd Hoffmann 
319*280c1e1cSGerd Hoffmann     int64_t to_transfer = audio_MIN(wpos - rpos, avail);
320*280c1e1cSGerd Hoffmann 
321*280c1e1cSGerd Hoffmann     hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1));
322*280c1e1cSGerd Hoffmann 
323*280c1e1cSGerd Hoffmann     while (to_transfer) {
324*280c1e1cSGerd Hoffmann         uint32_t start = (uint32_t) (rpos & B_MASK);
325*280c1e1cSGerd Hoffmann         uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
326*280c1e1cSGerd Hoffmann         uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
327*280c1e1cSGerd Hoffmann         rpos += written;
328*280c1e1cSGerd Hoffmann         to_transfer -= written;
329*280c1e1cSGerd Hoffmann         atomic_fetch_add(&st->rpos, written);
330*280c1e1cSGerd Hoffmann         if (chunk != written) {
331*280c1e1cSGerd Hoffmann             break;
332*280c1e1cSGerd Hoffmann         }
333*280c1e1cSGerd Hoffmann     }
334*280c1e1cSGerd Hoffmann }
335*280c1e1cSGerd Hoffmann 
336*280c1e1cSGerd Hoffmann static void hda_audio_compat_input_cb(void *opaque, int avail)
337*280c1e1cSGerd Hoffmann {
338*280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
339*280c1e1cSGerd Hoffmann     int recv = 0;
340*280c1e1cSGerd Hoffmann     int len;
341*280c1e1cSGerd Hoffmann     bool rc;
342*280c1e1cSGerd Hoffmann 
343*280c1e1cSGerd Hoffmann     while (avail - recv >= sizeof(st->compat_buf)) {
344*280c1e1cSGerd Hoffmann         if (st->compat_bpos != sizeof(st->compat_buf)) {
345*280c1e1cSGerd Hoffmann             len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos,
346*280c1e1cSGerd Hoffmann                            sizeof(st->compat_buf) - st->compat_bpos);
347*280c1e1cSGerd Hoffmann             st->compat_bpos += len;
348*280c1e1cSGerd Hoffmann             recv += len;
349*280c1e1cSGerd Hoffmann             if (st->compat_bpos != sizeof(st->compat_buf)) {
350*280c1e1cSGerd Hoffmann                 break;
351*280c1e1cSGerd Hoffmann             }
352*280c1e1cSGerd Hoffmann         }
353*280c1e1cSGerd Hoffmann         rc = hda_codec_xfer(&st->state->hda, st->stream, false,
354*280c1e1cSGerd Hoffmann                             st->compat_buf, sizeof(st->compat_buf));
355*280c1e1cSGerd Hoffmann         if (!rc) {
356*280c1e1cSGerd Hoffmann             break;
357*280c1e1cSGerd Hoffmann         }
358*280c1e1cSGerd Hoffmann         st->compat_bpos = 0;
359*280c1e1cSGerd Hoffmann     }
360*280c1e1cSGerd Hoffmann }
361*280c1e1cSGerd Hoffmann 
362*280c1e1cSGerd Hoffmann static void hda_audio_compat_output_cb(void *opaque, int avail)
363*280c1e1cSGerd Hoffmann {
364*280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
365d61a4ce8SGerd Hoffmann     int sent = 0;
366d61a4ce8SGerd Hoffmann     int len;
367d61a4ce8SGerd Hoffmann     bool rc;
368d61a4ce8SGerd Hoffmann 
369*280c1e1cSGerd Hoffmann     while (avail - sent >= sizeof(st->compat_buf)) {
370*280c1e1cSGerd Hoffmann         if (st->compat_bpos == sizeof(st->compat_buf)) {
371d61a4ce8SGerd Hoffmann             rc = hda_codec_xfer(&st->state->hda, st->stream, true,
372*280c1e1cSGerd Hoffmann                                 st->compat_buf, sizeof(st->compat_buf));
373d61a4ce8SGerd Hoffmann             if (!rc) {
374d61a4ce8SGerd Hoffmann                 break;
375d61a4ce8SGerd Hoffmann             }
376*280c1e1cSGerd Hoffmann             st->compat_bpos = 0;
377d61a4ce8SGerd Hoffmann         }
378*280c1e1cSGerd Hoffmann         len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos,
379*280c1e1cSGerd Hoffmann                         sizeof(st->compat_buf) - st->compat_bpos);
380*280c1e1cSGerd Hoffmann         st->compat_bpos += len;
381d61a4ce8SGerd Hoffmann         sent += len;
382*280c1e1cSGerd Hoffmann         if (st->compat_bpos != sizeof(st->compat_buf)) {
383d61a4ce8SGerd Hoffmann             break;
384d61a4ce8SGerd Hoffmann         }
385d61a4ce8SGerd Hoffmann     }
386d61a4ce8SGerd Hoffmann }
387d61a4ce8SGerd Hoffmann 
388d61a4ce8SGerd Hoffmann static void hda_audio_set_running(HDAAudioStream *st, bool running)
389d61a4ce8SGerd Hoffmann {
390d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
391d61a4ce8SGerd Hoffmann         return;
392d61a4ce8SGerd Hoffmann     }
393d61a4ce8SGerd Hoffmann     if (st->running == running) {
394d61a4ce8SGerd Hoffmann         return;
395d61a4ce8SGerd Hoffmann     }
396d61a4ce8SGerd Hoffmann     st->running = running;
397d61a4ce8SGerd Hoffmann     dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
398d61a4ce8SGerd Hoffmann            st->running ? "on" : "off", st->stream);
399*280c1e1cSGerd Hoffmann     if (st->state->use_timer) {
400*280c1e1cSGerd Hoffmann         if (running) {
401*280c1e1cSGerd Hoffmann             int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
402*280c1e1cSGerd Hoffmann             st->rpos = 0;
403*280c1e1cSGerd Hoffmann             st->wpos = 0;
404*280c1e1cSGerd Hoffmann             st->buft_start = now;
405*280c1e1cSGerd Hoffmann             timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
406*280c1e1cSGerd Hoffmann         } else {
407*280c1e1cSGerd Hoffmann             timer_del(st->buft);
408*280c1e1cSGerd Hoffmann         }
409*280c1e1cSGerd Hoffmann     }
410d61a4ce8SGerd Hoffmann     if (st->output) {
411d61a4ce8SGerd Hoffmann         AUD_set_active_out(st->voice.out, st->running);
412d61a4ce8SGerd Hoffmann     } else {
413d61a4ce8SGerd Hoffmann         AUD_set_active_in(st->voice.in, st->running);
414d61a4ce8SGerd Hoffmann     }
415d61a4ce8SGerd Hoffmann }
416d61a4ce8SGerd Hoffmann 
417d61a4ce8SGerd Hoffmann static void hda_audio_set_amp(HDAAudioStream *st)
418d61a4ce8SGerd Hoffmann {
419d61a4ce8SGerd Hoffmann     bool muted;
420d61a4ce8SGerd Hoffmann     uint32_t left, right;
421d61a4ce8SGerd Hoffmann 
422d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
423d61a4ce8SGerd Hoffmann         return;
424d61a4ce8SGerd Hoffmann     }
425d61a4ce8SGerd Hoffmann 
426d61a4ce8SGerd Hoffmann     muted = st->mute_left && st->mute_right;
427d61a4ce8SGerd Hoffmann     left  = st->mute_left  ? 0 : st->gain_left;
428d61a4ce8SGerd Hoffmann     right = st->mute_right ? 0 : st->gain_right;
429d61a4ce8SGerd Hoffmann 
430d61a4ce8SGerd Hoffmann     left = left * 255 / QEMU_HDA_AMP_STEPS;
431d61a4ce8SGerd Hoffmann     right = right * 255 / QEMU_HDA_AMP_STEPS;
432d61a4ce8SGerd Hoffmann 
4334843877eSGerd Hoffmann     if (!st->state->mixer) {
4344843877eSGerd Hoffmann         return;
4354843877eSGerd Hoffmann     }
436d61a4ce8SGerd Hoffmann     if (st->output) {
437d61a4ce8SGerd Hoffmann         AUD_set_volume_out(st->voice.out, muted, left, right);
438d61a4ce8SGerd Hoffmann     } else {
439d61a4ce8SGerd Hoffmann         AUD_set_volume_in(st->voice.in, muted, left, right);
440d61a4ce8SGerd Hoffmann     }
441d61a4ce8SGerd Hoffmann }
442d61a4ce8SGerd Hoffmann 
443d61a4ce8SGerd Hoffmann static void hda_audio_setup(HDAAudioStream *st)
444d61a4ce8SGerd Hoffmann {
445*280c1e1cSGerd Hoffmann     bool use_timer = st->state->use_timer;
446*280c1e1cSGerd Hoffmann     audio_callback_fn cb;
447*280c1e1cSGerd Hoffmann 
448d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
449d61a4ce8SGerd Hoffmann         return;
450d61a4ce8SGerd Hoffmann     }
451d61a4ce8SGerd Hoffmann 
452d61a4ce8SGerd Hoffmann     dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
453d61a4ce8SGerd Hoffmann            st->node->name, st->as.nchannels,
454d61a4ce8SGerd Hoffmann            fmt2name[st->as.fmt], st->as.freq);
455d61a4ce8SGerd Hoffmann 
456d61a4ce8SGerd Hoffmann     if (st->output) {
457*280c1e1cSGerd Hoffmann         if (use_timer) {
458*280c1e1cSGerd Hoffmann             cb = hda_audio_output_cb;
459*280c1e1cSGerd Hoffmann             st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
460*280c1e1cSGerd Hoffmann                                     hda_audio_output_timer, st);
461d61a4ce8SGerd Hoffmann         } else {
462*280c1e1cSGerd Hoffmann             cb = hda_audio_compat_output_cb;
463*280c1e1cSGerd Hoffmann         }
464*280c1e1cSGerd Hoffmann         st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
465*280c1e1cSGerd Hoffmann                                      st->node->name, st, cb, &st->as);
466*280c1e1cSGerd Hoffmann     } else {
467*280c1e1cSGerd Hoffmann         if (use_timer) {
468*280c1e1cSGerd Hoffmann             cb = hda_audio_input_cb;
469*280c1e1cSGerd Hoffmann             st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
470*280c1e1cSGerd Hoffmann                                     hda_audio_input_timer, st);
471*280c1e1cSGerd Hoffmann         } else {
472*280c1e1cSGerd Hoffmann             cb = hda_audio_compat_input_cb;
473*280c1e1cSGerd Hoffmann         }
474d61a4ce8SGerd Hoffmann         st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
475*280c1e1cSGerd Hoffmann                                    st->node->name, st, cb, &st->as);
476d61a4ce8SGerd Hoffmann     }
477d61a4ce8SGerd Hoffmann }
478d61a4ce8SGerd Hoffmann 
479d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
480d61a4ce8SGerd Hoffmann {
481cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
482d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
483d61a4ce8SGerd Hoffmann     const desc_node *node = NULL;
484d61a4ce8SGerd Hoffmann     const desc_param *param;
485d61a4ce8SGerd Hoffmann     uint32_t verb, payload, response, count, shift;
486d61a4ce8SGerd Hoffmann 
487d61a4ce8SGerd Hoffmann     if ((data & 0x70000) == 0x70000) {
488d61a4ce8SGerd Hoffmann         /* 12/8 id/payload */
489d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xfff;
490d61a4ce8SGerd Hoffmann         payload = data & 0x00ff;
491d61a4ce8SGerd Hoffmann     } else {
492d61a4ce8SGerd Hoffmann         /* 4/16 id/payload */
493d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xf00;
494d61a4ce8SGerd Hoffmann         payload = data & 0xffff;
495d61a4ce8SGerd Hoffmann     }
496d61a4ce8SGerd Hoffmann 
497d61a4ce8SGerd Hoffmann     node = hda_codec_find_node(a->desc, nid);
498d61a4ce8SGerd Hoffmann     if (node == NULL) {
499d61a4ce8SGerd Hoffmann         goto fail;
500d61a4ce8SGerd Hoffmann     }
501d61a4ce8SGerd Hoffmann     dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
502a89f364aSAlistair Francis            __func__, nid, node->name, verb, payload);
503d61a4ce8SGerd Hoffmann 
504d61a4ce8SGerd Hoffmann     switch (verb) {
505d61a4ce8SGerd Hoffmann     /* all nodes */
506d61a4ce8SGerd Hoffmann     case AC_VERB_PARAMETERS:
507d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, payload);
508d61a4ce8SGerd Hoffmann         if (param == NULL) {
509d61a4ce8SGerd Hoffmann             goto fail;
510d61a4ce8SGerd Hoffmann         }
511d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, param->val);
512d61a4ce8SGerd Hoffmann         break;
513d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SUBSYSTEM_ID:
514d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, a->desc->iid);
515d61a4ce8SGerd Hoffmann         break;
516d61a4ce8SGerd Hoffmann 
517d61a4ce8SGerd Hoffmann     /* all functions */
518d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONNECT_LIST:
519d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
520d61a4ce8SGerd Hoffmann         count = param ? param->val : 0;
521d61a4ce8SGerd Hoffmann         response = 0;
522d61a4ce8SGerd Hoffmann         shift = 0;
523d61a4ce8SGerd Hoffmann         while (payload < count && shift < 32) {
524d61a4ce8SGerd Hoffmann             response |= node->conn[payload] << shift;
525d61a4ce8SGerd Hoffmann             payload++;
526d61a4ce8SGerd Hoffmann             shift += 8;
527d61a4ce8SGerd Hoffmann         }
528d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
529d61a4ce8SGerd Hoffmann         break;
530d61a4ce8SGerd Hoffmann 
531d61a4ce8SGerd Hoffmann     /* pin widget */
532d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONFIG_DEFAULT:
533d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->config);
534d61a4ce8SGerd Hoffmann         break;
535d61a4ce8SGerd Hoffmann     case AC_VERB_GET_PIN_WIDGET_CONTROL:
536d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->pinctl);
537d61a4ce8SGerd Hoffmann         break;
538d61a4ce8SGerd Hoffmann     case AC_VERB_SET_PIN_WIDGET_CONTROL:
539d61a4ce8SGerd Hoffmann         if (node->pinctl != payload) {
540d61a4ce8SGerd Hoffmann             dprint(a, 1, "unhandled pin control bit\n");
541d61a4ce8SGerd Hoffmann         }
542d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
543d61a4ce8SGerd Hoffmann         break;
544d61a4ce8SGerd Hoffmann 
545d61a4ce8SGerd Hoffmann     /* audio in/out widget */
546d61a4ce8SGerd Hoffmann     case AC_VERB_SET_CHANNEL_STREAMID:
547d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
548d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
549d61a4ce8SGerd Hoffmann             goto fail;
550d61a4ce8SGerd Hoffmann         }
551d61a4ce8SGerd Hoffmann         hda_audio_set_running(st, false);
552d61a4ce8SGerd Hoffmann         st->stream = (payload >> 4) & 0x0f;
553d61a4ce8SGerd Hoffmann         st->channel = payload & 0x0f;
554d61a4ce8SGerd Hoffmann         dprint(a, 2, "%s: stream %d, channel %d\n",
555d61a4ce8SGerd Hoffmann                st->node->name, st->stream, st->channel);
556ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
557d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
558d61a4ce8SGerd Hoffmann         break;
559d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONV:
560d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
561d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
562d61a4ce8SGerd Hoffmann             goto fail;
563d61a4ce8SGerd Hoffmann         }
564d61a4ce8SGerd Hoffmann         response = st->stream << 4 | st->channel;
565d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
566d61a4ce8SGerd Hoffmann         break;
567d61a4ce8SGerd Hoffmann     case AC_VERB_SET_STREAM_FORMAT:
568d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
569d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
570d61a4ce8SGerd Hoffmann             goto fail;
571d61a4ce8SGerd Hoffmann         }
572d61a4ce8SGerd Hoffmann         st->format = payload;
573d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
574d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
575d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
576d61a4ce8SGerd Hoffmann         break;
577d61a4ce8SGerd Hoffmann     case AC_VERB_GET_STREAM_FORMAT:
578d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
579d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
580d61a4ce8SGerd Hoffmann             goto fail;
581d61a4ce8SGerd Hoffmann         }
582d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, st->format);
583d61a4ce8SGerd Hoffmann         break;
584d61a4ce8SGerd Hoffmann     case AC_VERB_GET_AMP_GAIN_MUTE:
585d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
586d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
587d61a4ce8SGerd Hoffmann             goto fail;
588d61a4ce8SGerd Hoffmann         }
589d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_GET_LEFT) {
590d61a4ce8SGerd Hoffmann             response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
591d61a4ce8SGerd Hoffmann         } else {
592d61a4ce8SGerd Hoffmann             response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
593d61a4ce8SGerd Hoffmann         }
594d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
595d61a4ce8SGerd Hoffmann         break;
596d61a4ce8SGerd Hoffmann     case AC_VERB_SET_AMP_GAIN_MUTE:
597d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
598d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
599d61a4ce8SGerd Hoffmann             goto fail;
600d61a4ce8SGerd Hoffmann         }
601d61a4ce8SGerd Hoffmann         dprint(a, 1, "amp (%s): %s%s%s%s index %d  gain %3d %s\n",
602d61a4ce8SGerd Hoffmann                st->node->name,
603d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
604d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INPUT)  ? "i" : "-",
605d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_LEFT)   ? "l" : "-",
606d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_RIGHT)  ? "r" : "-",
607d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
608d61a4ce8SGerd Hoffmann                (payload & AC_AMP_GAIN),
609d61a4ce8SGerd Hoffmann                (payload & AC_AMP_MUTE) ? "muted" : "");
610d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_LEFT) {
611d61a4ce8SGerd Hoffmann             st->gain_left = payload & AC_AMP_GAIN;
612d61a4ce8SGerd Hoffmann             st->mute_left = payload & AC_AMP_MUTE;
613d61a4ce8SGerd Hoffmann         }
614d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_RIGHT) {
615d61a4ce8SGerd Hoffmann             st->gain_right = payload & AC_AMP_GAIN;
616d61a4ce8SGerd Hoffmann             st->mute_right = payload & AC_AMP_MUTE;
617d61a4ce8SGerd Hoffmann         }
618d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
619d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
620d61a4ce8SGerd Hoffmann         break;
621d61a4ce8SGerd Hoffmann 
622d61a4ce8SGerd Hoffmann     /* not supported */
623d61a4ce8SGerd Hoffmann     case AC_VERB_SET_POWER_STATE:
624d61a4ce8SGerd Hoffmann     case AC_VERB_GET_POWER_STATE:
625d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SDI_SELECT:
626d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
627d61a4ce8SGerd Hoffmann         break;
628d61a4ce8SGerd Hoffmann     default:
629d61a4ce8SGerd Hoffmann         goto fail;
630d61a4ce8SGerd Hoffmann     }
631d61a4ce8SGerd Hoffmann     return;
632d61a4ce8SGerd Hoffmann 
633d61a4ce8SGerd Hoffmann fail:
634d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
635a89f364aSAlistair Francis            __func__, nid, node ? node->name : "?", verb, payload);
636d61a4ce8SGerd Hoffmann     hda_codec_response(hda, true, 0);
637d61a4ce8SGerd Hoffmann }
638d61a4ce8SGerd Hoffmann 
639ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
640d61a4ce8SGerd Hoffmann {
641cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
642d61a4ce8SGerd Hoffmann     int s;
643d61a4ce8SGerd Hoffmann 
644ba43d289SMarc-André Lureau     a->running_compat[stnr] = running;
645ba43d289SMarc-André Lureau     a->running_real[output * 16 + stnr] = running;
646d61a4ce8SGerd Hoffmann     for (s = 0; s < ARRAY_SIZE(a->st); s++) {
647d61a4ce8SGerd Hoffmann         if (a->st[s].node == NULL) {
648d61a4ce8SGerd Hoffmann             continue;
649d61a4ce8SGerd Hoffmann         }
650ba43d289SMarc-André Lureau         if (a->st[s].output != output) {
651ba43d289SMarc-André Lureau             continue;
652ba43d289SMarc-André Lureau         }
653d61a4ce8SGerd Hoffmann         if (a->st[s].stream != stnr) {
654d61a4ce8SGerd Hoffmann             continue;
655d61a4ce8SGerd Hoffmann         }
656d61a4ce8SGerd Hoffmann         hda_audio_set_running(&a->st[s], running);
657d61a4ce8SGerd Hoffmann     }
658d61a4ce8SGerd Hoffmann }
659d61a4ce8SGerd Hoffmann 
660d61a4ce8SGerd Hoffmann static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
661d61a4ce8SGerd Hoffmann {
662cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
663d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
664d61a4ce8SGerd Hoffmann     const desc_node *node;
665d61a4ce8SGerd Hoffmann     const desc_param *param;
666d61a4ce8SGerd Hoffmann     uint32_t i, type;
667d61a4ce8SGerd Hoffmann 
668d61a4ce8SGerd Hoffmann     a->desc = desc;
669f79f2bfcSAnthony Liguori     a->name = object_get_typename(OBJECT(a));
670a89f364aSAlistair Francis     dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad);
671d61a4ce8SGerd Hoffmann 
672d61a4ce8SGerd Hoffmann     AUD_register_card("hda", &a->card);
673d61a4ce8SGerd Hoffmann     for (i = 0; i < a->desc->nnodes; i++) {
674d61a4ce8SGerd Hoffmann         node = a->desc->nodes + i;
675d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
6762ab5bf67SGonglei         if (param == NULL) {
677d61a4ce8SGerd Hoffmann             continue;
6782ab5bf67SGonglei         }
679d61a4ce8SGerd Hoffmann         type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
680d61a4ce8SGerd Hoffmann         switch (type) {
681d61a4ce8SGerd Hoffmann         case AC_WID_AUD_OUT:
682d61a4ce8SGerd Hoffmann         case AC_WID_AUD_IN:
683d61a4ce8SGerd Hoffmann             assert(node->stindex < ARRAY_SIZE(a->st));
684d61a4ce8SGerd Hoffmann             st = a->st + node->stindex;
685d61a4ce8SGerd Hoffmann             st->state = a;
686d61a4ce8SGerd Hoffmann             st->node = node;
687d61a4ce8SGerd Hoffmann             if (type == AC_WID_AUD_OUT) {
688d61a4ce8SGerd Hoffmann                 /* unmute output by default */
689d61a4ce8SGerd Hoffmann                 st->gain_left = QEMU_HDA_AMP_STEPS;
690d61a4ce8SGerd Hoffmann                 st->gain_right = QEMU_HDA_AMP_STEPS;
691*280c1e1cSGerd Hoffmann                 st->compat_bpos = sizeof(st->compat_buf);
692d61a4ce8SGerd Hoffmann                 st->output = true;
693d61a4ce8SGerd Hoffmann             } else {
694d61a4ce8SGerd Hoffmann                 st->output = false;
695d61a4ce8SGerd Hoffmann             }
696d61a4ce8SGerd Hoffmann             st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
697d61a4ce8SGerd Hoffmann                 (1 << AC_FMT_CHAN_SHIFT);
698d61a4ce8SGerd Hoffmann             hda_codec_parse_fmt(st->format, &st->as);
699d61a4ce8SGerd Hoffmann             hda_audio_setup(st);
700d61a4ce8SGerd Hoffmann             break;
701d61a4ce8SGerd Hoffmann         }
702d61a4ce8SGerd Hoffmann     }
703d61a4ce8SGerd Hoffmann     return 0;
704d61a4ce8SGerd Hoffmann }
705d61a4ce8SGerd Hoffmann 
7065eaa8e1eSZihan Yang static void hda_audio_exit(HDACodecDevice *hda)
707129dcd2cSGerd Hoffmann {
708cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
709129dcd2cSGerd Hoffmann     HDAAudioStream *st;
710129dcd2cSGerd Hoffmann     int i;
711129dcd2cSGerd Hoffmann 
712a89f364aSAlistair Francis     dprint(a, 1, "%s\n", __func__);
713129dcd2cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
714129dcd2cSGerd Hoffmann         st = a->st + i;
715129dcd2cSGerd Hoffmann         if (st->node == NULL) {
716129dcd2cSGerd Hoffmann             continue;
717129dcd2cSGerd Hoffmann         }
718*280c1e1cSGerd Hoffmann         if (a->use_timer) {
719*280c1e1cSGerd Hoffmann             timer_del(st->buft);
720*280c1e1cSGerd Hoffmann         }
721129dcd2cSGerd Hoffmann         if (st->output) {
722129dcd2cSGerd Hoffmann             AUD_close_out(&a->card, st->voice.out);
723129dcd2cSGerd Hoffmann         } else {
724129dcd2cSGerd Hoffmann             AUD_close_in(&a->card, st->voice.in);
725129dcd2cSGerd Hoffmann         }
726129dcd2cSGerd Hoffmann     }
727129dcd2cSGerd Hoffmann     AUD_remove_card(&a->card);
728129dcd2cSGerd Hoffmann }
729129dcd2cSGerd Hoffmann 
730d61a4ce8SGerd Hoffmann static int hda_audio_post_load(void *opaque, int version)
731d61a4ce8SGerd Hoffmann {
732d61a4ce8SGerd Hoffmann     HDAAudioState *a = opaque;
733d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
734d61a4ce8SGerd Hoffmann     int i;
735d61a4ce8SGerd Hoffmann 
736a89f364aSAlistair Francis     dprint(a, 1, "%s\n", __func__);
737ba43d289SMarc-André Lureau     if (version == 1) {
738ba43d289SMarc-André Lureau         /* assume running_compat[] is for output streams */
739ba43d289SMarc-André Lureau         for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
740ba43d289SMarc-André Lureau             a->running_real[16 + i] = a->running_compat[i];
741ba43d289SMarc-André Lureau     }
742ba43d289SMarc-André Lureau 
743d61a4ce8SGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
744d61a4ce8SGerd Hoffmann         st = a->st + i;
745d61a4ce8SGerd Hoffmann         if (st->node == NULL)
746d61a4ce8SGerd Hoffmann             continue;
747d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
748d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
749d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
750ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
751d61a4ce8SGerd Hoffmann     }
752d61a4ce8SGerd Hoffmann     return 0;
753d61a4ce8SGerd Hoffmann }
754d61a4ce8SGerd Hoffmann 
75539e6a38cSGerd Hoffmann static void hda_audio_reset(DeviceState *dev)
75639e6a38cSGerd Hoffmann {
757cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(dev);
75839e6a38cSGerd Hoffmann     HDAAudioStream *st;
75939e6a38cSGerd Hoffmann     int i;
76039e6a38cSGerd Hoffmann 
76139e6a38cSGerd Hoffmann     dprint(a, 1, "%s\n", __func__);
76239e6a38cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
76339e6a38cSGerd Hoffmann         st = a->st + i;
76439e6a38cSGerd Hoffmann         if (st->node != NULL) {
76539e6a38cSGerd Hoffmann             hda_audio_set_running(st, false);
76639e6a38cSGerd Hoffmann         }
76739e6a38cSGerd Hoffmann     }
76839e6a38cSGerd Hoffmann }
76939e6a38cSGerd Hoffmann 
770*280c1e1cSGerd Hoffmann static bool vmstate_hda_audio_stream_buf_needed(void *opaque)
771*280c1e1cSGerd Hoffmann {
772*280c1e1cSGerd Hoffmann     HDAAudioStream *st = opaque;
773*280c1e1cSGerd Hoffmann     return st->state->use_timer;
774*280c1e1cSGerd Hoffmann }
775*280c1e1cSGerd Hoffmann 
776*280c1e1cSGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream_buf = {
777*280c1e1cSGerd Hoffmann     .name = "hda-audio-stream/buffer",
778*280c1e1cSGerd Hoffmann     .version_id = 1,
779*280c1e1cSGerd Hoffmann     .needed = vmstate_hda_audio_stream_buf_needed,
780*280c1e1cSGerd Hoffmann     .fields = (VMStateField[]) {
781*280c1e1cSGerd Hoffmann         VMSTATE_BUFFER(buf, HDAAudioStream),
782*280c1e1cSGerd Hoffmann         VMSTATE_INT64(rpos, HDAAudioStream),
783*280c1e1cSGerd Hoffmann         VMSTATE_INT64(wpos, HDAAudioStream),
784*280c1e1cSGerd Hoffmann         VMSTATE_TIMER_PTR(buft, HDAAudioStream),
785*280c1e1cSGerd Hoffmann         VMSTATE_INT64(buft_start, HDAAudioStream),
786*280c1e1cSGerd Hoffmann         VMSTATE_END_OF_LIST()
787*280c1e1cSGerd Hoffmann     }
788*280c1e1cSGerd Hoffmann };
789*280c1e1cSGerd Hoffmann 
790d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = {
791d61a4ce8SGerd Hoffmann     .name = "hda-audio-stream",
792d61a4ce8SGerd Hoffmann     .version_id = 1,
793d61a4ce8SGerd Hoffmann     .fields = (VMStateField[]) {
794d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(stream, HDAAudioStream),
795d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(channel, HDAAudioStream),
796d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(format, HDAAudioStream),
797d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_left, HDAAudioStream),
798d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_right, HDAAudioStream),
799d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_left, HDAAudioStream),
800d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_right, HDAAudioStream),
801*280c1e1cSGerd Hoffmann         VMSTATE_UINT32(compat_bpos, HDAAudioStream),
802*280c1e1cSGerd Hoffmann         VMSTATE_BUFFER(compat_buf, HDAAudioStream),
803d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
804*280c1e1cSGerd Hoffmann     },
805*280c1e1cSGerd Hoffmann     .subsections = (const VMStateDescription * []) {
806*280c1e1cSGerd Hoffmann         &vmstate_hda_audio_stream_buf,
807*280c1e1cSGerd Hoffmann         NULL
808d61a4ce8SGerd Hoffmann     }
809d61a4ce8SGerd Hoffmann };
810d61a4ce8SGerd Hoffmann 
811d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio = {
812d61a4ce8SGerd Hoffmann     .name = "hda-audio",
813ba43d289SMarc-André Lureau     .version_id = 2,
814d61a4ce8SGerd Hoffmann     .post_load = hda_audio_post_load,
815d61a4ce8SGerd Hoffmann     .fields = (VMStateField[]) {
816d61a4ce8SGerd Hoffmann         VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
817d61a4ce8SGerd Hoffmann                              vmstate_hda_audio_stream,
818d61a4ce8SGerd Hoffmann                              HDAAudioStream),
819ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
820ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
821d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
822d61a4ce8SGerd Hoffmann     }
823d61a4ce8SGerd Hoffmann };
824d61a4ce8SGerd Hoffmann 
825d61a4ce8SGerd Hoffmann static Property hda_audio_properties[] = {
826d61a4ce8SGerd Hoffmann     DEFINE_PROP_UINT32("debug", HDAAudioState, debug,   0),
8272690e61eSBandan Das     DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer,  true),
828*280c1e1cSGerd Hoffmann     DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, false),
829d61a4ce8SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
830d61a4ce8SGerd Hoffmann };
831d61a4ce8SGerd Hoffmann 
832d61a4ce8SGerd Hoffmann static int hda_audio_init_output(HDACodecDevice *hda)
833d61a4ce8SGerd Hoffmann {
834cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
8352690e61eSBandan Das 
8362690e61eSBandan Das     if (!a->mixer) {
8372690e61eSBandan Das         return hda_audio_init(hda, &output_nomixemu);
8382690e61eSBandan Das     } else {
8392690e61eSBandan Das         return hda_audio_init(hda, &output_mixemu);
8402690e61eSBandan Das     }
841d61a4ce8SGerd Hoffmann }
842d61a4ce8SGerd Hoffmann 
843d61a4ce8SGerd Hoffmann static int hda_audio_init_duplex(HDACodecDevice *hda)
844d61a4ce8SGerd Hoffmann {
845cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
8462690e61eSBandan Das 
8472690e61eSBandan Das     if (!a->mixer) {
8482690e61eSBandan Das         return hda_audio_init(hda, &duplex_nomixemu);
8492690e61eSBandan Das     } else {
8502690e61eSBandan Das         return hda_audio_init(hda, &duplex_mixemu);
8512690e61eSBandan Das     }
852d61a4ce8SGerd Hoffmann }
853d61a4ce8SGerd Hoffmann 
85420110065SGerd Hoffmann static int hda_audio_init_micro(HDACodecDevice *hda)
85520110065SGerd Hoffmann {
856cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
8572690e61eSBandan Das 
8582690e61eSBandan Das     if (!a->mixer) {
8592690e61eSBandan Das         return hda_audio_init(hda, &micro_nomixemu);
8602690e61eSBandan Das     } else {
8612690e61eSBandan Das         return hda_audio_init(hda, &micro_mixemu);
8622690e61eSBandan Das     }
86320110065SGerd Hoffmann }
86420110065SGerd Hoffmann 
865cd6c8830SGerd Hoffmann static void hda_audio_base_class_init(ObjectClass *klass, void *data)
866cd6c8830SGerd Hoffmann {
867cd6c8830SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
868cd6c8830SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
869cd6c8830SGerd Hoffmann 
870cd6c8830SGerd Hoffmann     k->exit = hda_audio_exit;
871cd6c8830SGerd Hoffmann     k->command = hda_audio_command;
872cd6c8830SGerd Hoffmann     k->stream = hda_audio_stream;
873cd6c8830SGerd Hoffmann     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
874cd6c8830SGerd Hoffmann     dc->reset = hda_audio_reset;
875cd6c8830SGerd Hoffmann     dc->vmsd = &vmstate_hda_audio;
876cd6c8830SGerd Hoffmann     dc->props = hda_audio_properties;
877cd6c8830SGerd Hoffmann }
878cd6c8830SGerd Hoffmann 
879cd6c8830SGerd Hoffmann static const TypeInfo hda_audio_info = {
880cd6c8830SGerd Hoffmann     .name          = TYPE_HDA_AUDIO,
881cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_CODEC_DEVICE,
882cd6c8830SGerd Hoffmann     .class_init    = hda_audio_base_class_init,
883cd6c8830SGerd Hoffmann     .abstract      = true,
884cd6c8830SGerd Hoffmann };
885cd6c8830SGerd Hoffmann 
886dbaa7904SAnthony Liguori static void hda_audio_output_class_init(ObjectClass *klass, void *data)
887dbaa7904SAnthony Liguori {
88839bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
889dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
890dbaa7904SAnthony Liguori 
891dbaa7904SAnthony Liguori     k->init = hda_audio_init_output;
89220110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, output-only (line-out)";
893dbaa7904SAnthony Liguori }
894dbaa7904SAnthony Liguori 
8958c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = {
896dbaa7904SAnthony Liguori     .name          = "hda-output",
897cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
89839bffca2SAnthony Liguori     .instance_size = sizeof(HDAAudioState),
899dbaa7904SAnthony Liguori     .class_init    = hda_audio_output_class_init,
900d61a4ce8SGerd Hoffmann };
901d61a4ce8SGerd Hoffmann 
902dbaa7904SAnthony Liguori static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
903dbaa7904SAnthony Liguori {
90439bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
905dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
906dbaa7904SAnthony Liguori 
907dbaa7904SAnthony Liguori     k->init = hda_audio_init_duplex;
90820110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
909dbaa7904SAnthony Liguori }
910dbaa7904SAnthony Liguori 
9118c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = {
912dbaa7904SAnthony Liguori     .name          = "hda-duplex",
913cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
91439bffca2SAnthony Liguori     .instance_size = sizeof(HDAAudioState),
915dbaa7904SAnthony Liguori     .class_init    = hda_audio_duplex_class_init,
916d61a4ce8SGerd Hoffmann };
917d61a4ce8SGerd Hoffmann 
91820110065SGerd Hoffmann static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
91920110065SGerd Hoffmann {
92020110065SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
92120110065SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
92220110065SGerd Hoffmann 
92320110065SGerd Hoffmann     k->init = hda_audio_init_micro;
92420110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
92520110065SGerd Hoffmann }
92620110065SGerd Hoffmann 
9278c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = {
92820110065SGerd Hoffmann     .name          = "hda-micro",
929cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
93020110065SGerd Hoffmann     .instance_size = sizeof(HDAAudioState),
93120110065SGerd Hoffmann     .class_init    = hda_audio_micro_class_init,
93220110065SGerd Hoffmann };
93320110065SGerd Hoffmann 
93483f7d43aSAndreas Färber static void hda_audio_register_types(void)
935d61a4ce8SGerd Hoffmann {
936cd6c8830SGerd Hoffmann     type_register_static(&hda_audio_info);
93739bffca2SAnthony Liguori     type_register_static(&hda_audio_output_info);
93839bffca2SAnthony Liguori     type_register_static(&hda_audio_duplex_info);
93920110065SGerd Hoffmann     type_register_static(&hda_audio_micro_info);
940d61a4ce8SGerd Hoffmann }
94183f7d43aSAndreas Färber 
94283f7d43aSAndreas Färber type_init(hda_audio_register_types)
943