xref: /qemu/hw/audio/hda-codec.c (revision 125ee0ed9cad04307498ac2b7b0d51ad8a807360)
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 
2083c9f4caSPaolo Bonzini #include "hw/hw.h"
2183c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
2247b43a1fSPaolo Bonzini #include "intel-hda.h"
2347b43a1fSPaolo Bonzini #include "intel-hda-defs.h"
24d61a4ce8SGerd Hoffmann #include "audio/audio.h"
25d61a4ce8SGerd Hoffmann 
26d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
27d61a4ce8SGerd Hoffmann 
28d61a4ce8SGerd Hoffmann typedef struct desc_param {
29d61a4ce8SGerd Hoffmann     uint32_t id;
30d61a4ce8SGerd Hoffmann     uint32_t val;
31d61a4ce8SGerd Hoffmann } desc_param;
32d61a4ce8SGerd Hoffmann 
33d61a4ce8SGerd Hoffmann typedef struct desc_node {
34d61a4ce8SGerd Hoffmann     uint32_t nid;
35d61a4ce8SGerd Hoffmann     const char *name;
36d61a4ce8SGerd Hoffmann     const desc_param *params;
37d61a4ce8SGerd Hoffmann     uint32_t nparams;
38d61a4ce8SGerd Hoffmann     uint32_t config;
39d61a4ce8SGerd Hoffmann     uint32_t pinctl;
40d61a4ce8SGerd Hoffmann     uint32_t *conn;
41d61a4ce8SGerd Hoffmann     uint32_t stindex;
42d61a4ce8SGerd Hoffmann } desc_node;
43d61a4ce8SGerd Hoffmann 
44d61a4ce8SGerd Hoffmann typedef struct desc_codec {
45d61a4ce8SGerd Hoffmann     const char *name;
46d61a4ce8SGerd Hoffmann     uint32_t iid;
47d61a4ce8SGerd Hoffmann     const desc_node *nodes;
48d61a4ce8SGerd Hoffmann     uint32_t nnodes;
49d61a4ce8SGerd Hoffmann } desc_codec;
50d61a4ce8SGerd Hoffmann 
51d61a4ce8SGerd Hoffmann static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
52d61a4ce8SGerd Hoffmann {
53d61a4ce8SGerd Hoffmann     int i;
54d61a4ce8SGerd Hoffmann 
55d61a4ce8SGerd Hoffmann     for (i = 0; i < node->nparams; i++) {
56d61a4ce8SGerd Hoffmann         if (node->params[i].id == id) {
57d61a4ce8SGerd Hoffmann             return &node->params[i];
58d61a4ce8SGerd Hoffmann         }
59d61a4ce8SGerd Hoffmann     }
60d61a4ce8SGerd Hoffmann     return NULL;
61d61a4ce8SGerd Hoffmann }
62d61a4ce8SGerd Hoffmann 
63d61a4ce8SGerd Hoffmann static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
64d61a4ce8SGerd Hoffmann {
65d61a4ce8SGerd Hoffmann     int i;
66d61a4ce8SGerd Hoffmann 
67d61a4ce8SGerd Hoffmann     for (i = 0; i < codec->nnodes; i++) {
68d61a4ce8SGerd Hoffmann         if (codec->nodes[i].nid == nid) {
69d61a4ce8SGerd Hoffmann             return &codec->nodes[i];
70d61a4ce8SGerd Hoffmann         }
71d61a4ce8SGerd Hoffmann     }
72d61a4ce8SGerd Hoffmann     return NULL;
73d61a4ce8SGerd Hoffmann }
74d61a4ce8SGerd Hoffmann 
75d61a4ce8SGerd Hoffmann static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
76d61a4ce8SGerd Hoffmann {
77d61a4ce8SGerd Hoffmann     if (format & AC_FMT_TYPE_NON_PCM) {
78d61a4ce8SGerd Hoffmann         return;
79d61a4ce8SGerd Hoffmann     }
80d61a4ce8SGerd Hoffmann 
81d61a4ce8SGerd Hoffmann     as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
82d61a4ce8SGerd Hoffmann 
83d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
84d61a4ce8SGerd Hoffmann     case 1: as->freq *= 2; break;
85d61a4ce8SGerd Hoffmann     case 2: as->freq *= 3; break;
86d61a4ce8SGerd Hoffmann     case 3: as->freq *= 4; break;
87d61a4ce8SGerd Hoffmann     }
88d61a4ce8SGerd Hoffmann 
89d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
90d61a4ce8SGerd Hoffmann     case 1: as->freq /= 2; break;
91d61a4ce8SGerd Hoffmann     case 2: as->freq /= 3; break;
92d61a4ce8SGerd Hoffmann     case 3: as->freq /= 4; break;
93d61a4ce8SGerd Hoffmann     case 4: as->freq /= 5; break;
94d61a4ce8SGerd Hoffmann     case 5: as->freq /= 6; break;
95d61a4ce8SGerd Hoffmann     case 6: as->freq /= 7; break;
96d61a4ce8SGerd Hoffmann     case 7: as->freq /= 8; break;
97d61a4ce8SGerd Hoffmann     }
98d61a4ce8SGerd Hoffmann 
99d61a4ce8SGerd Hoffmann     switch (format & AC_FMT_BITS_MASK) {
100d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_8:  as->fmt = AUD_FMT_S8;  break;
101d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
102d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
103d61a4ce8SGerd Hoffmann     }
104d61a4ce8SGerd Hoffmann 
105d61a4ce8SGerd Hoffmann     as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
106d61a4ce8SGerd Hoffmann }
107d61a4ce8SGerd Hoffmann 
108d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
109d61a4ce8SGerd Hoffmann /*
110d61a4ce8SGerd Hoffmann  * HDA codec descriptions
111d61a4ce8SGerd Hoffmann  */
112d61a4ce8SGerd Hoffmann 
113d61a4ce8SGerd Hoffmann /* some defines */
114d61a4ce8SGerd Hoffmann 
115d61a4ce8SGerd Hoffmann #define QEMU_HDA_ID_VENDOR  0x1af4
116d61a4ce8SGerd Hoffmann #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
117d61a4ce8SGerd Hoffmann                               0x1fc /* 16 -> 96 kHz */)
118d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_NONE    (0)
119d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_STEPS   0x4a
120d61a4ce8SGerd Hoffmann 
121d61a4ce8SGerd Hoffmann #ifdef CONFIG_MIXEMU
122ec4a8047SGerd Hoffmann # define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x12)
123ec4a8047SGerd Hoffmann # define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x22)
124ec4a8047SGerd Hoffmann # define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x32)
125d61a4ce8SGerd Hoffmann # define QEMU_HDA_AMP_CAPS                                              \
126d61a4ce8SGerd Hoffmann     (AC_AMPCAP_MUTE |                                                   \
127d61a4ce8SGerd Hoffmann      (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT)    |                \
128d61a4ce8SGerd Hoffmann      (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) |                \
129d61a4ce8SGerd Hoffmann      (3                  << AC_AMPCAP_STEP_SIZE_SHIFT))
130d61a4ce8SGerd Hoffmann #else
131ec4a8047SGerd Hoffmann # define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x11)
132ec4a8047SGerd Hoffmann # define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x21)
133ec4a8047SGerd Hoffmann # define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x31)
134d61a4ce8SGerd Hoffmann # define QEMU_HDA_AMP_CAPS   QEMU_HDA_AMP_NONE
135d61a4ce8SGerd Hoffmann #endif
136d61a4ce8SGerd Hoffmann 
137d61a4ce8SGerd Hoffmann /* common: audio output widget */
138d61a4ce8SGerd Hoffmann static const desc_param common_params_audio_dac[] = {
139d61a4ce8SGerd Hoffmann     {
140d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AUDIO_WIDGET_CAP,
141d61a4ce8SGerd Hoffmann         .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) |
142d61a4ce8SGerd Hoffmann                 AC_WCAP_FORMAT_OVRD |
143d61a4ce8SGerd Hoffmann                 AC_WCAP_AMP_OVRD |
144d61a4ce8SGerd Hoffmann                 AC_WCAP_OUT_AMP |
145d61a4ce8SGerd Hoffmann                 AC_WCAP_STEREO),
146d61a4ce8SGerd Hoffmann     },{
147d61a4ce8SGerd Hoffmann         .id  = AC_PAR_PCM,
148d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_PCM_FORMATS,
149d61a4ce8SGerd Hoffmann     },{
150d61a4ce8SGerd Hoffmann         .id  = AC_PAR_STREAM,
151d61a4ce8SGerd Hoffmann         .val = AC_SUPFMT_PCM,
152d61a4ce8SGerd Hoffmann     },{
153d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_IN_CAP,
154d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
155d61a4ce8SGerd Hoffmann     },{
156d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_OUT_CAP,
157d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_CAPS,
158d61a4ce8SGerd Hoffmann     },
159d61a4ce8SGerd Hoffmann };
160d61a4ce8SGerd Hoffmann 
161d0c2bbb9SGerd Hoffmann /* common: audio input widget */
162d0c2bbb9SGerd Hoffmann static const desc_param common_params_audio_adc[] = {
163d0c2bbb9SGerd Hoffmann     {
164d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_AUDIO_WIDGET_CAP,
165d0c2bbb9SGerd Hoffmann         .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) |
166d0c2bbb9SGerd Hoffmann                 AC_WCAP_CONN_LIST |
167d0c2bbb9SGerd Hoffmann                 AC_WCAP_FORMAT_OVRD |
168d0c2bbb9SGerd Hoffmann                 AC_WCAP_AMP_OVRD |
169d0c2bbb9SGerd Hoffmann                 AC_WCAP_IN_AMP |
170d0c2bbb9SGerd Hoffmann                 AC_WCAP_STEREO),
171d0c2bbb9SGerd Hoffmann     },{
172d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_CONNLIST_LEN,
173d0c2bbb9SGerd Hoffmann         .val = 1,
174d0c2bbb9SGerd Hoffmann     },{
175d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_PCM,
176d0c2bbb9SGerd Hoffmann         .val = QEMU_HDA_PCM_FORMATS,
177d0c2bbb9SGerd Hoffmann     },{
178d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_STREAM,
179d0c2bbb9SGerd Hoffmann         .val = AC_SUPFMT_PCM,
180d0c2bbb9SGerd Hoffmann     },{
181d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_AMP_IN_CAP,
182d0c2bbb9SGerd Hoffmann         .val = QEMU_HDA_AMP_CAPS,
183d0c2bbb9SGerd Hoffmann     },{
184d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_AMP_OUT_CAP,
185d0c2bbb9SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
186d0c2bbb9SGerd Hoffmann     },
187d0c2bbb9SGerd Hoffmann };
188d0c2bbb9SGerd Hoffmann 
189d61a4ce8SGerd Hoffmann /* common: pin widget (line-out) */
190d61a4ce8SGerd Hoffmann static const desc_param common_params_audio_lineout[] = {
191d61a4ce8SGerd Hoffmann     {
192d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AUDIO_WIDGET_CAP,
193d61a4ce8SGerd Hoffmann         .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
194d61a4ce8SGerd Hoffmann                 AC_WCAP_CONN_LIST |
195d61a4ce8SGerd Hoffmann                 AC_WCAP_STEREO),
196d61a4ce8SGerd Hoffmann     },{
197d61a4ce8SGerd Hoffmann         .id  = AC_PAR_PIN_CAP,
198d61a4ce8SGerd Hoffmann         .val = AC_PINCAP_OUT,
199d61a4ce8SGerd Hoffmann     },{
200d61a4ce8SGerd Hoffmann         .id  = AC_PAR_CONNLIST_LEN,
201d61a4ce8SGerd Hoffmann         .val = 1,
202d61a4ce8SGerd Hoffmann     },{
203d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_IN_CAP,
204d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
205d61a4ce8SGerd Hoffmann     },{
206d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_OUT_CAP,
207d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
208d61a4ce8SGerd Hoffmann     },
209d61a4ce8SGerd Hoffmann };
210d61a4ce8SGerd Hoffmann 
211d0c2bbb9SGerd Hoffmann /* common: pin widget (line-in) */
212d0c2bbb9SGerd Hoffmann static const desc_param common_params_audio_linein[] = {
213d0c2bbb9SGerd Hoffmann     {
214d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_AUDIO_WIDGET_CAP,
215d0c2bbb9SGerd Hoffmann         .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
216d0c2bbb9SGerd Hoffmann                 AC_WCAP_STEREO),
217d0c2bbb9SGerd Hoffmann     },{
218d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_PIN_CAP,
219d0c2bbb9SGerd Hoffmann         .val = AC_PINCAP_IN,
220d0c2bbb9SGerd Hoffmann     },{
221d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_AMP_IN_CAP,
222d0c2bbb9SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
223d0c2bbb9SGerd Hoffmann     },{
224d0c2bbb9SGerd Hoffmann         .id  = AC_PAR_AMP_OUT_CAP,
225d0c2bbb9SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
226d0c2bbb9SGerd Hoffmann     },
227d0c2bbb9SGerd Hoffmann };
228d0c2bbb9SGerd Hoffmann 
229d61a4ce8SGerd Hoffmann /* output: root node */
230d61a4ce8SGerd Hoffmann static const desc_param output_params_root[] = {
231d61a4ce8SGerd Hoffmann     {
232d61a4ce8SGerd Hoffmann         .id  = AC_PAR_VENDOR_ID,
233d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_ID_OUTPUT,
234d61a4ce8SGerd Hoffmann     },{
235d61a4ce8SGerd Hoffmann         .id  = AC_PAR_SUBSYSTEM_ID,
236d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_ID_OUTPUT,
237d61a4ce8SGerd Hoffmann     },{
238d61a4ce8SGerd Hoffmann         .id  = AC_PAR_REV_ID,
239d61a4ce8SGerd Hoffmann         .val = 0x00100101,
240d61a4ce8SGerd Hoffmann     },{
241d61a4ce8SGerd Hoffmann         .id  = AC_PAR_NODE_COUNT,
242d61a4ce8SGerd Hoffmann         .val = 0x00010001,
243d61a4ce8SGerd Hoffmann     },
244d61a4ce8SGerd Hoffmann };
245d61a4ce8SGerd Hoffmann 
246d61a4ce8SGerd Hoffmann /* output: audio function */
247d61a4ce8SGerd Hoffmann static const desc_param output_params_audio_func[] = {
248d61a4ce8SGerd Hoffmann     {
249d61a4ce8SGerd Hoffmann         .id  = AC_PAR_FUNCTION_TYPE,
250d61a4ce8SGerd Hoffmann         .val = AC_GRP_AUDIO_FUNCTION,
251d61a4ce8SGerd Hoffmann     },{
252d61a4ce8SGerd Hoffmann         .id  = AC_PAR_SUBSYSTEM_ID,
253d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_ID_OUTPUT,
254d61a4ce8SGerd Hoffmann     },{
255d61a4ce8SGerd Hoffmann         .id  = AC_PAR_NODE_COUNT,
256d61a4ce8SGerd Hoffmann         .val = 0x00020002,
257d61a4ce8SGerd Hoffmann     },{
258d61a4ce8SGerd Hoffmann         .id  = AC_PAR_PCM,
259d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_PCM_FORMATS,
260d61a4ce8SGerd Hoffmann     },{
261d61a4ce8SGerd Hoffmann         .id  = AC_PAR_STREAM,
262d61a4ce8SGerd Hoffmann         .val = AC_SUPFMT_PCM,
263d61a4ce8SGerd Hoffmann     },{
264d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_IN_CAP,
265d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
266d61a4ce8SGerd Hoffmann     },{
267d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_OUT_CAP,
268d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
269d61a4ce8SGerd Hoffmann     },{
270d61a4ce8SGerd Hoffmann         .id  = AC_PAR_GPIO_CAP,
271d61a4ce8SGerd Hoffmann         .val = 0,
272d61a4ce8SGerd Hoffmann     },{
273d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AUDIO_FG_CAP,
274d61a4ce8SGerd Hoffmann         .val = 0x00000808,
275d61a4ce8SGerd Hoffmann     },{
276d61a4ce8SGerd Hoffmann         .id  = AC_PAR_POWER_STATE,
277d61a4ce8SGerd Hoffmann         .val = 0,
278d61a4ce8SGerd Hoffmann     },
279d61a4ce8SGerd Hoffmann };
280d61a4ce8SGerd Hoffmann 
281d61a4ce8SGerd Hoffmann /* output: nodes */
282d61a4ce8SGerd Hoffmann static const desc_node output_nodes[] = {
283d61a4ce8SGerd Hoffmann     {
284d61a4ce8SGerd Hoffmann         .nid     = AC_NODE_ROOT,
285d61a4ce8SGerd Hoffmann         .name    = "root",
286d61a4ce8SGerd Hoffmann         .params  = output_params_root,
287d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(output_params_root),
288d61a4ce8SGerd Hoffmann     },{
289d61a4ce8SGerd Hoffmann         .nid     = 1,
290d61a4ce8SGerd Hoffmann         .name    = "func",
291d61a4ce8SGerd Hoffmann         .params  = output_params_audio_func,
292d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(output_params_audio_func),
293d61a4ce8SGerd Hoffmann     },{
294d61a4ce8SGerd Hoffmann         .nid     = 2,
295d61a4ce8SGerd Hoffmann         .name    = "dac",
296d61a4ce8SGerd Hoffmann         .params  = common_params_audio_dac,
297d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_dac),
298d61a4ce8SGerd Hoffmann         .stindex = 0,
299d61a4ce8SGerd Hoffmann     },{
300d61a4ce8SGerd Hoffmann         .nid     = 3,
301d61a4ce8SGerd Hoffmann         .name    = "out",
302d61a4ce8SGerd Hoffmann         .params  = common_params_audio_lineout,
303d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_lineout),
304d61a4ce8SGerd Hoffmann         .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
305d61a4ce8SGerd Hoffmann                     (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
306d61a4ce8SGerd Hoffmann                     (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
307d61a4ce8SGerd Hoffmann                     (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
308d61a4ce8SGerd Hoffmann                     0x10),
309d61a4ce8SGerd Hoffmann         .pinctl  = AC_PINCTL_OUT_EN,
310d61a4ce8SGerd Hoffmann         .conn    = (uint32_t[]) { 2 },
311d61a4ce8SGerd Hoffmann     }
312d61a4ce8SGerd Hoffmann };
313d61a4ce8SGerd Hoffmann 
314d61a4ce8SGerd Hoffmann /* output: codec */
315d61a4ce8SGerd Hoffmann static const desc_codec output = {
316d61a4ce8SGerd Hoffmann     .name   = "output",
317d61a4ce8SGerd Hoffmann     .iid    = QEMU_HDA_ID_OUTPUT,
318d61a4ce8SGerd Hoffmann     .nodes  = output_nodes,
319d61a4ce8SGerd Hoffmann     .nnodes = ARRAY_SIZE(output_nodes),
320d61a4ce8SGerd Hoffmann };
321d61a4ce8SGerd Hoffmann 
322d61a4ce8SGerd Hoffmann /* duplex: root node */
323d61a4ce8SGerd Hoffmann static const desc_param duplex_params_root[] = {
324d61a4ce8SGerd Hoffmann     {
325d61a4ce8SGerd Hoffmann         .id  = AC_PAR_VENDOR_ID,
326d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_ID_DUPLEX,
327d61a4ce8SGerd Hoffmann     },{
328d61a4ce8SGerd Hoffmann         .id  = AC_PAR_SUBSYSTEM_ID,
329d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_ID_DUPLEX,
330d61a4ce8SGerd Hoffmann     },{
331d61a4ce8SGerd Hoffmann         .id  = AC_PAR_REV_ID,
332d61a4ce8SGerd Hoffmann         .val = 0x00100101,
333d61a4ce8SGerd Hoffmann     },{
334d61a4ce8SGerd Hoffmann         .id  = AC_PAR_NODE_COUNT,
335d61a4ce8SGerd Hoffmann         .val = 0x00010001,
336d61a4ce8SGerd Hoffmann     },
337d61a4ce8SGerd Hoffmann };
338d61a4ce8SGerd Hoffmann 
339d61a4ce8SGerd Hoffmann /* duplex: audio function */
340d61a4ce8SGerd Hoffmann static const desc_param duplex_params_audio_func[] = {
341d61a4ce8SGerd Hoffmann     {
342d61a4ce8SGerd Hoffmann         .id  = AC_PAR_FUNCTION_TYPE,
343d61a4ce8SGerd Hoffmann         .val = AC_GRP_AUDIO_FUNCTION,
344d61a4ce8SGerd Hoffmann     },{
345d61a4ce8SGerd Hoffmann         .id  = AC_PAR_SUBSYSTEM_ID,
346d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_ID_DUPLEX,
347d61a4ce8SGerd Hoffmann     },{
348d61a4ce8SGerd Hoffmann         .id  = AC_PAR_NODE_COUNT,
349d61a4ce8SGerd Hoffmann         .val = 0x00020004,
350d61a4ce8SGerd Hoffmann     },{
351d61a4ce8SGerd Hoffmann         .id  = AC_PAR_PCM,
352d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_PCM_FORMATS,
353d61a4ce8SGerd Hoffmann     },{
354d61a4ce8SGerd Hoffmann         .id  = AC_PAR_STREAM,
355d61a4ce8SGerd Hoffmann         .val = AC_SUPFMT_PCM,
356d61a4ce8SGerd Hoffmann     },{
357d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_IN_CAP,
358d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
359d61a4ce8SGerd Hoffmann     },{
360d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AMP_OUT_CAP,
361d61a4ce8SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
362d61a4ce8SGerd Hoffmann     },{
363d61a4ce8SGerd Hoffmann         .id  = AC_PAR_GPIO_CAP,
364d61a4ce8SGerd Hoffmann         .val = 0,
365d61a4ce8SGerd Hoffmann     },{
366d61a4ce8SGerd Hoffmann         .id  = AC_PAR_AUDIO_FG_CAP,
367d61a4ce8SGerd Hoffmann         .val = 0x00000808,
368d61a4ce8SGerd Hoffmann     },{
369d61a4ce8SGerd Hoffmann         .id  = AC_PAR_POWER_STATE,
370d61a4ce8SGerd Hoffmann         .val = 0,
371d61a4ce8SGerd Hoffmann     },
372d61a4ce8SGerd Hoffmann };
373d61a4ce8SGerd Hoffmann 
374d61a4ce8SGerd Hoffmann /* duplex: nodes */
375d61a4ce8SGerd Hoffmann static const desc_node duplex_nodes[] = {
376d61a4ce8SGerd Hoffmann     {
377d61a4ce8SGerd Hoffmann         .nid     = AC_NODE_ROOT,
378d61a4ce8SGerd Hoffmann         .name    = "root",
379d61a4ce8SGerd Hoffmann         .params  = duplex_params_root,
380d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(duplex_params_root),
381d61a4ce8SGerd Hoffmann     },{
382d61a4ce8SGerd Hoffmann         .nid     = 1,
383d61a4ce8SGerd Hoffmann         .name    = "func",
384d61a4ce8SGerd Hoffmann         .params  = duplex_params_audio_func,
385d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(duplex_params_audio_func),
386d61a4ce8SGerd Hoffmann     },{
387d61a4ce8SGerd Hoffmann         .nid     = 2,
388d61a4ce8SGerd Hoffmann         .name    = "dac",
389d61a4ce8SGerd Hoffmann         .params  = common_params_audio_dac,
390d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_dac),
391d61a4ce8SGerd Hoffmann         .stindex = 0,
392d61a4ce8SGerd Hoffmann     },{
393d61a4ce8SGerd Hoffmann         .nid     = 3,
394d61a4ce8SGerd Hoffmann         .name    = "out",
395d61a4ce8SGerd Hoffmann         .params  = common_params_audio_lineout,
396d61a4ce8SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_lineout),
397d61a4ce8SGerd Hoffmann         .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
398d61a4ce8SGerd Hoffmann                     (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
399d61a4ce8SGerd Hoffmann                     (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
400d61a4ce8SGerd Hoffmann                     (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
401d61a4ce8SGerd Hoffmann                     0x10),
402d61a4ce8SGerd Hoffmann         .pinctl  = AC_PINCTL_OUT_EN,
403d61a4ce8SGerd Hoffmann         .conn    = (uint32_t[]) { 2 },
404d61a4ce8SGerd Hoffmann     },{
405d61a4ce8SGerd Hoffmann         .nid     = 4,
406d61a4ce8SGerd Hoffmann         .name    = "adc",
407d0c2bbb9SGerd Hoffmann         .params  = common_params_audio_adc,
408d0c2bbb9SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_adc),
409d61a4ce8SGerd Hoffmann         .stindex = 1,
410d61a4ce8SGerd Hoffmann         .conn    = (uint32_t[]) { 5 },
411d61a4ce8SGerd Hoffmann     },{
412d61a4ce8SGerd Hoffmann         .nid     = 5,
413d61a4ce8SGerd Hoffmann         .name    = "in",
414d0c2bbb9SGerd Hoffmann         .params  = common_params_audio_linein,
415d0c2bbb9SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_linein),
416d61a4ce8SGerd Hoffmann         .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
417d61a4ce8SGerd Hoffmann                     (AC_JACK_LINE_IN      << AC_DEFCFG_DEVICE_SHIFT)    |
418d61a4ce8SGerd Hoffmann                     (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
419d61a4ce8SGerd Hoffmann                     (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
420d61a4ce8SGerd Hoffmann                     0x20),
421d61a4ce8SGerd Hoffmann         .pinctl  = AC_PINCTL_IN_EN,
422d61a4ce8SGerd Hoffmann     }
423d61a4ce8SGerd Hoffmann };
424d61a4ce8SGerd Hoffmann 
425d61a4ce8SGerd Hoffmann /* duplex: codec */
426d61a4ce8SGerd Hoffmann static const desc_codec duplex = {
427d61a4ce8SGerd Hoffmann     .name   = "duplex",
428d61a4ce8SGerd Hoffmann     .iid    = QEMU_HDA_ID_DUPLEX,
429d61a4ce8SGerd Hoffmann     .nodes  = duplex_nodes,
430d61a4ce8SGerd Hoffmann     .nnodes = ARRAY_SIZE(duplex_nodes),
431d61a4ce8SGerd Hoffmann };
432d61a4ce8SGerd Hoffmann 
43320110065SGerd Hoffmann /* micro: root node */
43420110065SGerd Hoffmann static const desc_param micro_params_root[] = {
43520110065SGerd Hoffmann     {
43620110065SGerd Hoffmann         .id  = AC_PAR_VENDOR_ID,
43720110065SGerd Hoffmann         .val = QEMU_HDA_ID_MICRO,
43820110065SGerd Hoffmann     },{
43920110065SGerd Hoffmann         .id  = AC_PAR_SUBSYSTEM_ID,
44020110065SGerd Hoffmann         .val = QEMU_HDA_ID_MICRO,
44120110065SGerd Hoffmann     },{
44220110065SGerd Hoffmann         .id  = AC_PAR_REV_ID,
44320110065SGerd Hoffmann         .val = 0x00100101,
44420110065SGerd Hoffmann     },{
44520110065SGerd Hoffmann         .id  = AC_PAR_NODE_COUNT,
44620110065SGerd Hoffmann         .val = 0x00010001,
44720110065SGerd Hoffmann     },
44820110065SGerd Hoffmann };
44920110065SGerd Hoffmann 
45020110065SGerd Hoffmann /* micro: audio function */
45120110065SGerd Hoffmann static const desc_param micro_params_audio_func[] = {
45220110065SGerd Hoffmann     {
45320110065SGerd Hoffmann         .id  = AC_PAR_FUNCTION_TYPE,
45420110065SGerd Hoffmann         .val = AC_GRP_AUDIO_FUNCTION,
45520110065SGerd Hoffmann     },{
45620110065SGerd Hoffmann         .id  = AC_PAR_SUBSYSTEM_ID,
45720110065SGerd Hoffmann         .val = QEMU_HDA_ID_MICRO,
45820110065SGerd Hoffmann     },{
45920110065SGerd Hoffmann         .id  = AC_PAR_NODE_COUNT,
46020110065SGerd Hoffmann         .val = 0x00020004,
46120110065SGerd Hoffmann     },{
46220110065SGerd Hoffmann         .id  = AC_PAR_PCM,
46320110065SGerd Hoffmann         .val = QEMU_HDA_PCM_FORMATS,
46420110065SGerd Hoffmann     },{
46520110065SGerd Hoffmann         .id  = AC_PAR_STREAM,
46620110065SGerd Hoffmann         .val = AC_SUPFMT_PCM,
46720110065SGerd Hoffmann     },{
46820110065SGerd Hoffmann         .id  = AC_PAR_AMP_IN_CAP,
46920110065SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
47020110065SGerd Hoffmann     },{
47120110065SGerd Hoffmann         .id  = AC_PAR_AMP_OUT_CAP,
47220110065SGerd Hoffmann         .val = QEMU_HDA_AMP_NONE,
47320110065SGerd Hoffmann     },{
47420110065SGerd Hoffmann         .id  = AC_PAR_GPIO_CAP,
47520110065SGerd Hoffmann         .val = 0,
47620110065SGerd Hoffmann     },{
47720110065SGerd Hoffmann         .id  = AC_PAR_AUDIO_FG_CAP,
47820110065SGerd Hoffmann         .val = 0x00000808,
47920110065SGerd Hoffmann     },{
48020110065SGerd Hoffmann         .id  = AC_PAR_POWER_STATE,
48120110065SGerd Hoffmann         .val = 0,
48220110065SGerd Hoffmann     },
48320110065SGerd Hoffmann };
48420110065SGerd Hoffmann 
48520110065SGerd Hoffmann /* micro: nodes */
48620110065SGerd Hoffmann static const desc_node micro_nodes[] = {
48720110065SGerd Hoffmann     {
48820110065SGerd Hoffmann         .nid     = AC_NODE_ROOT,
48920110065SGerd Hoffmann         .name    = "root",
49020110065SGerd Hoffmann         .params  = micro_params_root,
49120110065SGerd Hoffmann         .nparams = ARRAY_SIZE(micro_params_root),
49220110065SGerd Hoffmann     },{
49320110065SGerd Hoffmann         .nid     = 1,
49420110065SGerd Hoffmann         .name    = "func",
49520110065SGerd Hoffmann         .params  = micro_params_audio_func,
49620110065SGerd Hoffmann         .nparams = ARRAY_SIZE(micro_params_audio_func),
49720110065SGerd Hoffmann     },{
49820110065SGerd Hoffmann         .nid     = 2,
49920110065SGerd Hoffmann         .name    = "dac",
50020110065SGerd Hoffmann         .params  = common_params_audio_dac,
50120110065SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_dac),
50220110065SGerd Hoffmann         .stindex = 0,
50320110065SGerd Hoffmann     },{
50420110065SGerd Hoffmann         .nid     = 3,
50520110065SGerd Hoffmann         .name    = "out",
50620110065SGerd Hoffmann         .params  = common_params_audio_lineout,
50720110065SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_lineout),
50820110065SGerd Hoffmann         .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
50920110065SGerd Hoffmann                     (AC_JACK_SPEAKER      << AC_DEFCFG_DEVICE_SHIFT)    |
51020110065SGerd Hoffmann                     (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
51120110065SGerd Hoffmann                     (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
51220110065SGerd Hoffmann                     0x10),
51320110065SGerd Hoffmann         .pinctl  = AC_PINCTL_OUT_EN,
51420110065SGerd Hoffmann         .conn    = (uint32_t[]) { 2 },
51520110065SGerd Hoffmann     },{
51620110065SGerd Hoffmann         .nid     = 4,
51720110065SGerd Hoffmann         .name    = "adc",
51820110065SGerd Hoffmann         .params  = common_params_audio_adc,
51920110065SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_adc),
52020110065SGerd Hoffmann         .stindex = 1,
52120110065SGerd Hoffmann         .conn    = (uint32_t[]) { 5 },
52220110065SGerd Hoffmann     },{
52320110065SGerd Hoffmann         .nid     = 5,
52420110065SGerd Hoffmann         .name    = "in",
52520110065SGerd Hoffmann         .params  = common_params_audio_linein,
52620110065SGerd Hoffmann         .nparams = ARRAY_SIZE(common_params_audio_linein),
52720110065SGerd Hoffmann         .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
52820110065SGerd Hoffmann                     (AC_JACK_MIC_IN       << AC_DEFCFG_DEVICE_SHIFT)    |
52920110065SGerd Hoffmann                     (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
53020110065SGerd Hoffmann                     (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
53120110065SGerd Hoffmann                     0x20),
53220110065SGerd Hoffmann         .pinctl  = AC_PINCTL_IN_EN,
53320110065SGerd Hoffmann     }
53420110065SGerd Hoffmann };
53520110065SGerd Hoffmann 
53620110065SGerd Hoffmann /* micro: codec */
53720110065SGerd Hoffmann static const desc_codec micro = {
53820110065SGerd Hoffmann     .name   = "micro",
53920110065SGerd Hoffmann     .iid    = QEMU_HDA_ID_MICRO,
54020110065SGerd Hoffmann     .nodes  = micro_nodes,
54120110065SGerd Hoffmann     .nnodes = ARRAY_SIZE(micro_nodes),
54220110065SGerd Hoffmann };
54320110065SGerd Hoffmann 
544d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
545d61a4ce8SGerd Hoffmann 
546d61a4ce8SGerd Hoffmann static const char *fmt2name[] = {
547d61a4ce8SGerd Hoffmann     [ AUD_FMT_U8  ] = "PCM-U8",
548d61a4ce8SGerd Hoffmann     [ AUD_FMT_S8  ] = "PCM-S8",
549d61a4ce8SGerd Hoffmann     [ AUD_FMT_U16 ] = "PCM-U16",
550d61a4ce8SGerd Hoffmann     [ AUD_FMT_S16 ] = "PCM-S16",
551d61a4ce8SGerd Hoffmann     [ AUD_FMT_U32 ] = "PCM-U32",
552d61a4ce8SGerd Hoffmann     [ AUD_FMT_S32 ] = "PCM-S32",
553d61a4ce8SGerd Hoffmann };
554d61a4ce8SGerd Hoffmann 
555d61a4ce8SGerd Hoffmann typedef struct HDAAudioState HDAAudioState;
556d61a4ce8SGerd Hoffmann typedef struct HDAAudioStream HDAAudioStream;
557d61a4ce8SGerd Hoffmann 
558d61a4ce8SGerd Hoffmann struct HDAAudioStream {
559d61a4ce8SGerd Hoffmann     HDAAudioState *state;
560d61a4ce8SGerd Hoffmann     const desc_node *node;
561d61a4ce8SGerd Hoffmann     bool output, running;
562d61a4ce8SGerd Hoffmann     uint32_t stream;
563d61a4ce8SGerd Hoffmann     uint32_t channel;
564d61a4ce8SGerd Hoffmann     uint32_t format;
565d61a4ce8SGerd Hoffmann     uint32_t gain_left, gain_right;
566d61a4ce8SGerd Hoffmann     bool mute_left, mute_right;
567d61a4ce8SGerd Hoffmann     struct audsettings as;
568d61a4ce8SGerd Hoffmann     union {
569d61a4ce8SGerd Hoffmann         SWVoiceIn *in;
570d61a4ce8SGerd Hoffmann         SWVoiceOut *out;
571d61a4ce8SGerd Hoffmann     } voice;
572d61a4ce8SGerd Hoffmann     uint8_t buf[HDA_BUFFER_SIZE];
573d61a4ce8SGerd Hoffmann     uint32_t bpos;
574d61a4ce8SGerd Hoffmann };
575d61a4ce8SGerd Hoffmann 
576d61a4ce8SGerd Hoffmann struct HDAAudioState {
577d61a4ce8SGerd Hoffmann     HDACodecDevice hda;
578d61a4ce8SGerd Hoffmann     const char *name;
579d61a4ce8SGerd Hoffmann 
580d61a4ce8SGerd Hoffmann     QEMUSoundCard card;
581d61a4ce8SGerd Hoffmann     const desc_codec *desc;
582d61a4ce8SGerd Hoffmann     HDAAudioStream st[4];
583ba43d289SMarc-André Lureau     bool running_compat[16];
584ba43d289SMarc-André Lureau     bool running_real[2 * 16];
585d61a4ce8SGerd Hoffmann 
586d61a4ce8SGerd Hoffmann     /* properties */
587d61a4ce8SGerd Hoffmann     uint32_t debug;
588d61a4ce8SGerd Hoffmann };
589d61a4ce8SGerd Hoffmann 
590d61a4ce8SGerd Hoffmann static void hda_audio_input_cb(void *opaque, int avail)
591d61a4ce8SGerd Hoffmann {
592d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
593d61a4ce8SGerd Hoffmann     int recv = 0;
594d61a4ce8SGerd Hoffmann     int len;
595d61a4ce8SGerd Hoffmann     bool rc;
596d61a4ce8SGerd Hoffmann 
597d61a4ce8SGerd Hoffmann     while (avail - recv >= sizeof(st->buf)) {
598d61a4ce8SGerd Hoffmann         if (st->bpos != sizeof(st->buf)) {
599d61a4ce8SGerd Hoffmann             len = AUD_read(st->voice.in, st->buf + st->bpos,
600d61a4ce8SGerd Hoffmann                            sizeof(st->buf) - st->bpos);
601d61a4ce8SGerd Hoffmann             st->bpos += len;
602d61a4ce8SGerd Hoffmann             recv += len;
603d61a4ce8SGerd Hoffmann             if (st->bpos != sizeof(st->buf)) {
604d61a4ce8SGerd Hoffmann                 break;
605d61a4ce8SGerd Hoffmann             }
606d61a4ce8SGerd Hoffmann         }
607d61a4ce8SGerd Hoffmann         rc = hda_codec_xfer(&st->state->hda, st->stream, false,
608d61a4ce8SGerd Hoffmann                             st->buf, sizeof(st->buf));
609d61a4ce8SGerd Hoffmann         if (!rc) {
610d61a4ce8SGerd Hoffmann             break;
611d61a4ce8SGerd Hoffmann         }
612d61a4ce8SGerd Hoffmann         st->bpos = 0;
613d61a4ce8SGerd Hoffmann     }
614d61a4ce8SGerd Hoffmann }
615d61a4ce8SGerd Hoffmann 
616d61a4ce8SGerd Hoffmann static void hda_audio_output_cb(void *opaque, int avail)
617d61a4ce8SGerd Hoffmann {
618d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
619d61a4ce8SGerd Hoffmann     int sent = 0;
620d61a4ce8SGerd Hoffmann     int len;
621d61a4ce8SGerd Hoffmann     bool rc;
622d61a4ce8SGerd Hoffmann 
623d61a4ce8SGerd Hoffmann     while (avail - sent >= sizeof(st->buf)) {
624d61a4ce8SGerd Hoffmann         if (st->bpos == sizeof(st->buf)) {
625d61a4ce8SGerd Hoffmann             rc = hda_codec_xfer(&st->state->hda, st->stream, true,
626d61a4ce8SGerd Hoffmann                                 st->buf, sizeof(st->buf));
627d61a4ce8SGerd Hoffmann             if (!rc) {
628d61a4ce8SGerd Hoffmann                 break;
629d61a4ce8SGerd Hoffmann             }
630d61a4ce8SGerd Hoffmann             st->bpos = 0;
631d61a4ce8SGerd Hoffmann         }
632d61a4ce8SGerd Hoffmann         len = AUD_write(st->voice.out, st->buf + st->bpos,
633d61a4ce8SGerd Hoffmann                         sizeof(st->buf) - st->bpos);
634d61a4ce8SGerd Hoffmann         st->bpos += len;
635d61a4ce8SGerd Hoffmann         sent += len;
636d61a4ce8SGerd Hoffmann         if (st->bpos != sizeof(st->buf)) {
637d61a4ce8SGerd Hoffmann             break;
638d61a4ce8SGerd Hoffmann         }
639d61a4ce8SGerd Hoffmann     }
640d61a4ce8SGerd Hoffmann }
641d61a4ce8SGerd Hoffmann 
642d61a4ce8SGerd Hoffmann static void hda_audio_set_running(HDAAudioStream *st, bool running)
643d61a4ce8SGerd Hoffmann {
644d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
645d61a4ce8SGerd Hoffmann         return;
646d61a4ce8SGerd Hoffmann     }
647d61a4ce8SGerd Hoffmann     if (st->running == running) {
648d61a4ce8SGerd Hoffmann         return;
649d61a4ce8SGerd Hoffmann     }
650d61a4ce8SGerd Hoffmann     st->running = running;
651d61a4ce8SGerd Hoffmann     dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
652d61a4ce8SGerd Hoffmann            st->running ? "on" : "off", st->stream);
653d61a4ce8SGerd Hoffmann     if (st->output) {
654d61a4ce8SGerd Hoffmann         AUD_set_active_out(st->voice.out, st->running);
655d61a4ce8SGerd Hoffmann     } else {
656d61a4ce8SGerd Hoffmann         AUD_set_active_in(st->voice.in, st->running);
657d61a4ce8SGerd Hoffmann     }
658d61a4ce8SGerd Hoffmann }
659d61a4ce8SGerd Hoffmann 
660d61a4ce8SGerd Hoffmann static void hda_audio_set_amp(HDAAudioStream *st)
661d61a4ce8SGerd Hoffmann {
662d61a4ce8SGerd Hoffmann     bool muted;
663d61a4ce8SGerd Hoffmann     uint32_t left, right;
664d61a4ce8SGerd Hoffmann 
665d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
666d61a4ce8SGerd Hoffmann         return;
667d61a4ce8SGerd Hoffmann     }
668d61a4ce8SGerd Hoffmann 
669d61a4ce8SGerd Hoffmann     muted = st->mute_left && st->mute_right;
670d61a4ce8SGerd Hoffmann     left  = st->mute_left  ? 0 : st->gain_left;
671d61a4ce8SGerd Hoffmann     right = st->mute_right ? 0 : st->gain_right;
672d61a4ce8SGerd Hoffmann 
673d61a4ce8SGerd Hoffmann     left = left * 255 / QEMU_HDA_AMP_STEPS;
674d61a4ce8SGerd Hoffmann     right = right * 255 / QEMU_HDA_AMP_STEPS;
675d61a4ce8SGerd Hoffmann 
676d61a4ce8SGerd Hoffmann     if (st->output) {
677d61a4ce8SGerd Hoffmann         AUD_set_volume_out(st->voice.out, muted, left, right);
678d61a4ce8SGerd Hoffmann     } else {
679d61a4ce8SGerd Hoffmann         AUD_set_volume_in(st->voice.in, muted, left, right);
680d61a4ce8SGerd Hoffmann     }
681d61a4ce8SGerd Hoffmann }
682d61a4ce8SGerd Hoffmann 
683d61a4ce8SGerd Hoffmann static void hda_audio_setup(HDAAudioStream *st)
684d61a4ce8SGerd Hoffmann {
685d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
686d61a4ce8SGerd Hoffmann         return;
687d61a4ce8SGerd Hoffmann     }
688d61a4ce8SGerd Hoffmann 
689d61a4ce8SGerd Hoffmann     dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
690d61a4ce8SGerd Hoffmann            st->node->name, st->as.nchannels,
691d61a4ce8SGerd Hoffmann            fmt2name[st->as.fmt], st->as.freq);
692d61a4ce8SGerd Hoffmann 
693d61a4ce8SGerd Hoffmann     if (st->output) {
694d61a4ce8SGerd Hoffmann         st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
695d61a4ce8SGerd Hoffmann                                      st->node->name, st,
696d61a4ce8SGerd Hoffmann                                      hda_audio_output_cb, &st->as);
697d61a4ce8SGerd Hoffmann     } else {
698d61a4ce8SGerd Hoffmann         st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
699d61a4ce8SGerd Hoffmann                                    st->node->name, st,
700d61a4ce8SGerd Hoffmann                                    hda_audio_input_cb, &st->as);
701d61a4ce8SGerd Hoffmann     }
702d61a4ce8SGerd Hoffmann }
703d61a4ce8SGerd Hoffmann 
704d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
705d61a4ce8SGerd Hoffmann {
706d61a4ce8SGerd Hoffmann     HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
707d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
708d61a4ce8SGerd Hoffmann     const desc_node *node = NULL;
709d61a4ce8SGerd Hoffmann     const desc_param *param;
710d61a4ce8SGerd Hoffmann     uint32_t verb, payload, response, count, shift;
711d61a4ce8SGerd Hoffmann 
712d61a4ce8SGerd Hoffmann     if ((data & 0x70000) == 0x70000) {
713d61a4ce8SGerd Hoffmann         /* 12/8 id/payload */
714d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xfff;
715d61a4ce8SGerd Hoffmann         payload = data & 0x00ff;
716d61a4ce8SGerd Hoffmann     } else {
717d61a4ce8SGerd Hoffmann         /* 4/16 id/payload */
718d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xf00;
719d61a4ce8SGerd Hoffmann         payload = data & 0xffff;
720d61a4ce8SGerd Hoffmann     }
721d61a4ce8SGerd Hoffmann 
722d61a4ce8SGerd Hoffmann     node = hda_codec_find_node(a->desc, nid);
723d61a4ce8SGerd Hoffmann     if (node == NULL) {
724d61a4ce8SGerd Hoffmann         goto fail;
725d61a4ce8SGerd Hoffmann     }
726d61a4ce8SGerd Hoffmann     dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
727d61a4ce8SGerd Hoffmann            __FUNCTION__, nid, node->name, verb, payload);
728d61a4ce8SGerd Hoffmann 
729d61a4ce8SGerd Hoffmann     switch (verb) {
730d61a4ce8SGerd Hoffmann     /* all nodes */
731d61a4ce8SGerd Hoffmann     case AC_VERB_PARAMETERS:
732d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, payload);
733d61a4ce8SGerd Hoffmann         if (param == NULL) {
734d61a4ce8SGerd Hoffmann             goto fail;
735d61a4ce8SGerd Hoffmann         }
736d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, param->val);
737d61a4ce8SGerd Hoffmann         break;
738d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SUBSYSTEM_ID:
739d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, a->desc->iid);
740d61a4ce8SGerd Hoffmann         break;
741d61a4ce8SGerd Hoffmann 
742d61a4ce8SGerd Hoffmann     /* all functions */
743d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONNECT_LIST:
744d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
745d61a4ce8SGerd Hoffmann         count = param ? param->val : 0;
746d61a4ce8SGerd Hoffmann         response = 0;
747d61a4ce8SGerd Hoffmann         shift = 0;
748d61a4ce8SGerd Hoffmann         while (payload < count && shift < 32) {
749d61a4ce8SGerd Hoffmann             response |= node->conn[payload] << shift;
750d61a4ce8SGerd Hoffmann             payload++;
751d61a4ce8SGerd Hoffmann             shift += 8;
752d61a4ce8SGerd Hoffmann         }
753d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
754d61a4ce8SGerd Hoffmann         break;
755d61a4ce8SGerd Hoffmann 
756d61a4ce8SGerd Hoffmann     /* pin widget */
757d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONFIG_DEFAULT:
758d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->config);
759d61a4ce8SGerd Hoffmann         break;
760d61a4ce8SGerd Hoffmann     case AC_VERB_GET_PIN_WIDGET_CONTROL:
761d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->pinctl);
762d61a4ce8SGerd Hoffmann         break;
763d61a4ce8SGerd Hoffmann     case AC_VERB_SET_PIN_WIDGET_CONTROL:
764d61a4ce8SGerd Hoffmann         if (node->pinctl != payload) {
765d61a4ce8SGerd Hoffmann             dprint(a, 1, "unhandled pin control bit\n");
766d61a4ce8SGerd Hoffmann         }
767d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
768d61a4ce8SGerd Hoffmann         break;
769d61a4ce8SGerd Hoffmann 
770d61a4ce8SGerd Hoffmann     /* audio in/out widget */
771d61a4ce8SGerd Hoffmann     case AC_VERB_SET_CHANNEL_STREAMID:
772d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
773d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
774d61a4ce8SGerd Hoffmann             goto fail;
775d61a4ce8SGerd Hoffmann         }
776d61a4ce8SGerd Hoffmann         hda_audio_set_running(st, false);
777d61a4ce8SGerd Hoffmann         st->stream = (payload >> 4) & 0x0f;
778d61a4ce8SGerd Hoffmann         st->channel = payload & 0x0f;
779d61a4ce8SGerd Hoffmann         dprint(a, 2, "%s: stream %d, channel %d\n",
780d61a4ce8SGerd Hoffmann                st->node->name, st->stream, st->channel);
781ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
782d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
783d61a4ce8SGerd Hoffmann         break;
784d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONV:
785d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
786d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
787d61a4ce8SGerd Hoffmann             goto fail;
788d61a4ce8SGerd Hoffmann         }
789d61a4ce8SGerd Hoffmann         response = st->stream << 4 | st->channel;
790d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
791d61a4ce8SGerd Hoffmann         break;
792d61a4ce8SGerd Hoffmann     case AC_VERB_SET_STREAM_FORMAT:
793d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
794d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
795d61a4ce8SGerd Hoffmann             goto fail;
796d61a4ce8SGerd Hoffmann         }
797d61a4ce8SGerd Hoffmann         st->format = payload;
798d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
799d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
800d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
801d61a4ce8SGerd Hoffmann         break;
802d61a4ce8SGerd Hoffmann     case AC_VERB_GET_STREAM_FORMAT:
803d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
804d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
805d61a4ce8SGerd Hoffmann             goto fail;
806d61a4ce8SGerd Hoffmann         }
807d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, st->format);
808d61a4ce8SGerd Hoffmann         break;
809d61a4ce8SGerd Hoffmann     case AC_VERB_GET_AMP_GAIN_MUTE:
810d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
811d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
812d61a4ce8SGerd Hoffmann             goto fail;
813d61a4ce8SGerd Hoffmann         }
814d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_GET_LEFT) {
815d61a4ce8SGerd Hoffmann             response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
816d61a4ce8SGerd Hoffmann         } else {
817d61a4ce8SGerd Hoffmann             response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
818d61a4ce8SGerd Hoffmann         }
819d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
820d61a4ce8SGerd Hoffmann         break;
821d61a4ce8SGerd Hoffmann     case AC_VERB_SET_AMP_GAIN_MUTE:
822d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
823d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
824d61a4ce8SGerd Hoffmann             goto fail;
825d61a4ce8SGerd Hoffmann         }
826d61a4ce8SGerd Hoffmann         dprint(a, 1, "amp (%s): %s%s%s%s index %d  gain %3d %s\n",
827d61a4ce8SGerd Hoffmann                st->node->name,
828d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
829d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INPUT)  ? "i" : "-",
830d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_LEFT)   ? "l" : "-",
831d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_RIGHT)  ? "r" : "-",
832d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
833d61a4ce8SGerd Hoffmann                (payload & AC_AMP_GAIN),
834d61a4ce8SGerd Hoffmann                (payload & AC_AMP_MUTE) ? "muted" : "");
835d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_LEFT) {
836d61a4ce8SGerd Hoffmann             st->gain_left = payload & AC_AMP_GAIN;
837d61a4ce8SGerd Hoffmann             st->mute_left = payload & AC_AMP_MUTE;
838d61a4ce8SGerd Hoffmann         }
839d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_RIGHT) {
840d61a4ce8SGerd Hoffmann             st->gain_right = payload & AC_AMP_GAIN;
841d61a4ce8SGerd Hoffmann             st->mute_right = payload & AC_AMP_MUTE;
842d61a4ce8SGerd Hoffmann         }
843d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
844d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
845d61a4ce8SGerd Hoffmann         break;
846d61a4ce8SGerd Hoffmann 
847d61a4ce8SGerd Hoffmann     /* not supported */
848d61a4ce8SGerd Hoffmann     case AC_VERB_SET_POWER_STATE:
849d61a4ce8SGerd Hoffmann     case AC_VERB_GET_POWER_STATE:
850d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SDI_SELECT:
851d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
852d61a4ce8SGerd Hoffmann         break;
853d61a4ce8SGerd Hoffmann     default:
854d61a4ce8SGerd Hoffmann         goto fail;
855d61a4ce8SGerd Hoffmann     }
856d61a4ce8SGerd Hoffmann     return;
857d61a4ce8SGerd Hoffmann 
858d61a4ce8SGerd Hoffmann fail:
859d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
860d61a4ce8SGerd Hoffmann            __FUNCTION__, nid, node ? node->name : "?", verb, payload);
861d61a4ce8SGerd Hoffmann     hda_codec_response(hda, true, 0);
862d61a4ce8SGerd Hoffmann }
863d61a4ce8SGerd Hoffmann 
864ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
865d61a4ce8SGerd Hoffmann {
866d61a4ce8SGerd Hoffmann     HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
867d61a4ce8SGerd Hoffmann     int s;
868d61a4ce8SGerd Hoffmann 
869ba43d289SMarc-André Lureau     a->running_compat[stnr] = running;
870ba43d289SMarc-André Lureau     a->running_real[output * 16 + stnr] = running;
871d61a4ce8SGerd Hoffmann     for (s = 0; s < ARRAY_SIZE(a->st); s++) {
872d61a4ce8SGerd Hoffmann         if (a->st[s].node == NULL) {
873d61a4ce8SGerd Hoffmann             continue;
874d61a4ce8SGerd Hoffmann         }
875ba43d289SMarc-André Lureau         if (a->st[s].output != output) {
876ba43d289SMarc-André Lureau             continue;
877ba43d289SMarc-André Lureau         }
878d61a4ce8SGerd Hoffmann         if (a->st[s].stream != stnr) {
879d61a4ce8SGerd Hoffmann             continue;
880d61a4ce8SGerd Hoffmann         }
881d61a4ce8SGerd Hoffmann         hda_audio_set_running(&a->st[s], running);
882d61a4ce8SGerd Hoffmann     }
883d61a4ce8SGerd Hoffmann }
884d61a4ce8SGerd Hoffmann 
885d61a4ce8SGerd Hoffmann static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
886d61a4ce8SGerd Hoffmann {
887d61a4ce8SGerd Hoffmann     HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
888d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
889d61a4ce8SGerd Hoffmann     const desc_node *node;
890d61a4ce8SGerd Hoffmann     const desc_param *param;
891d61a4ce8SGerd Hoffmann     uint32_t i, type;
892d61a4ce8SGerd Hoffmann 
893d61a4ce8SGerd Hoffmann     a->desc = desc;
894f79f2bfcSAnthony Liguori     a->name = object_get_typename(OBJECT(a));
895d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
896d61a4ce8SGerd Hoffmann 
897d61a4ce8SGerd Hoffmann     AUD_register_card("hda", &a->card);
898d61a4ce8SGerd Hoffmann     for (i = 0; i < a->desc->nnodes; i++) {
899d61a4ce8SGerd Hoffmann         node = a->desc->nodes + i;
900d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
901d61a4ce8SGerd Hoffmann         if (NULL == param)
902d61a4ce8SGerd Hoffmann             continue;
903d61a4ce8SGerd Hoffmann         type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
904d61a4ce8SGerd Hoffmann         switch (type) {
905d61a4ce8SGerd Hoffmann         case AC_WID_AUD_OUT:
906d61a4ce8SGerd Hoffmann         case AC_WID_AUD_IN:
907d61a4ce8SGerd Hoffmann             assert(node->stindex < ARRAY_SIZE(a->st));
908d61a4ce8SGerd Hoffmann             st = a->st + node->stindex;
909d61a4ce8SGerd Hoffmann             st->state = a;
910d61a4ce8SGerd Hoffmann             st->node = node;
911d61a4ce8SGerd Hoffmann             if (type == AC_WID_AUD_OUT) {
912d61a4ce8SGerd Hoffmann                 /* unmute output by default */
913d61a4ce8SGerd Hoffmann                 st->gain_left = QEMU_HDA_AMP_STEPS;
914d61a4ce8SGerd Hoffmann                 st->gain_right = QEMU_HDA_AMP_STEPS;
915d61a4ce8SGerd Hoffmann                 st->bpos = sizeof(st->buf);
916d61a4ce8SGerd Hoffmann                 st->output = true;
917d61a4ce8SGerd Hoffmann             } else {
918d61a4ce8SGerd Hoffmann                 st->output = false;
919d61a4ce8SGerd Hoffmann             }
920d61a4ce8SGerd Hoffmann             st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
921d61a4ce8SGerd Hoffmann                 (1 << AC_FMT_CHAN_SHIFT);
922d61a4ce8SGerd Hoffmann             hda_codec_parse_fmt(st->format, &st->as);
923d61a4ce8SGerd Hoffmann             hda_audio_setup(st);
924d61a4ce8SGerd Hoffmann             break;
925d61a4ce8SGerd Hoffmann         }
926d61a4ce8SGerd Hoffmann     }
927d61a4ce8SGerd Hoffmann     return 0;
928d61a4ce8SGerd Hoffmann }
929d61a4ce8SGerd Hoffmann 
930129dcd2cSGerd Hoffmann static int hda_audio_exit(HDACodecDevice *hda)
931129dcd2cSGerd Hoffmann {
932129dcd2cSGerd Hoffmann     HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
933129dcd2cSGerd Hoffmann     HDAAudioStream *st;
934129dcd2cSGerd Hoffmann     int i;
935129dcd2cSGerd Hoffmann 
936129dcd2cSGerd Hoffmann     dprint(a, 1, "%s\n", __FUNCTION__);
937129dcd2cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
938129dcd2cSGerd Hoffmann         st = a->st + i;
939129dcd2cSGerd Hoffmann         if (st->node == NULL) {
940129dcd2cSGerd Hoffmann             continue;
941129dcd2cSGerd Hoffmann         }
942129dcd2cSGerd Hoffmann         if (st->output) {
943129dcd2cSGerd Hoffmann             AUD_close_out(&a->card, st->voice.out);
944129dcd2cSGerd Hoffmann         } else {
945129dcd2cSGerd Hoffmann             AUD_close_in(&a->card, st->voice.in);
946129dcd2cSGerd Hoffmann         }
947129dcd2cSGerd Hoffmann     }
948129dcd2cSGerd Hoffmann     AUD_remove_card(&a->card);
949129dcd2cSGerd Hoffmann     return 0;
950129dcd2cSGerd Hoffmann }
951129dcd2cSGerd Hoffmann 
952d61a4ce8SGerd Hoffmann static int hda_audio_post_load(void *opaque, int version)
953d61a4ce8SGerd Hoffmann {
954d61a4ce8SGerd Hoffmann     HDAAudioState *a = opaque;
955d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
956d61a4ce8SGerd Hoffmann     int i;
957d61a4ce8SGerd Hoffmann 
958d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s\n", __FUNCTION__);
959ba43d289SMarc-André Lureau     if (version == 1) {
960ba43d289SMarc-André Lureau         /* assume running_compat[] is for output streams */
961ba43d289SMarc-André Lureau         for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
962ba43d289SMarc-André Lureau             a->running_real[16 + i] = a->running_compat[i];
963ba43d289SMarc-André Lureau     }
964ba43d289SMarc-André Lureau 
965d61a4ce8SGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
966d61a4ce8SGerd Hoffmann         st = a->st + i;
967d61a4ce8SGerd Hoffmann         if (st->node == NULL)
968d61a4ce8SGerd Hoffmann             continue;
969d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
970d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
971d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
972ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
973d61a4ce8SGerd Hoffmann     }
974d61a4ce8SGerd Hoffmann     return 0;
975d61a4ce8SGerd Hoffmann }
976d61a4ce8SGerd Hoffmann 
977d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = {
978d61a4ce8SGerd Hoffmann     .name = "hda-audio-stream",
979d61a4ce8SGerd Hoffmann     .version_id = 1,
980d61a4ce8SGerd Hoffmann     .fields = (VMStateField []) {
981d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(stream, HDAAudioStream),
982d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(channel, HDAAudioStream),
983d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(format, HDAAudioStream),
984d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_left, HDAAudioStream),
985d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_right, HDAAudioStream),
986d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_left, HDAAudioStream),
987d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_right, HDAAudioStream),
988d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(bpos, HDAAudioStream),
989d61a4ce8SGerd Hoffmann         VMSTATE_BUFFER(buf, HDAAudioStream),
990d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
991d61a4ce8SGerd Hoffmann     }
992d61a4ce8SGerd Hoffmann };
993d61a4ce8SGerd Hoffmann 
994d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio = {
995d61a4ce8SGerd Hoffmann     .name = "hda-audio",
996ba43d289SMarc-André Lureau     .version_id = 2,
997d61a4ce8SGerd Hoffmann     .post_load = hda_audio_post_load,
998d61a4ce8SGerd Hoffmann     .fields = (VMStateField []) {
999d61a4ce8SGerd Hoffmann         VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
1000d61a4ce8SGerd Hoffmann                              vmstate_hda_audio_stream,
1001d61a4ce8SGerd Hoffmann                              HDAAudioStream),
1002ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
1003ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
1004d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
1005d61a4ce8SGerd Hoffmann     }
1006d61a4ce8SGerd Hoffmann };
1007d61a4ce8SGerd Hoffmann 
1008d61a4ce8SGerd Hoffmann static Property hda_audio_properties[] = {
1009d61a4ce8SGerd Hoffmann     DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
1010d61a4ce8SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
1011d61a4ce8SGerd Hoffmann };
1012d61a4ce8SGerd Hoffmann 
1013d61a4ce8SGerd Hoffmann static int hda_audio_init_output(HDACodecDevice *hda)
1014d61a4ce8SGerd Hoffmann {
1015d61a4ce8SGerd Hoffmann     return hda_audio_init(hda, &output);
1016d61a4ce8SGerd Hoffmann }
1017d61a4ce8SGerd Hoffmann 
1018d61a4ce8SGerd Hoffmann static int hda_audio_init_duplex(HDACodecDevice *hda)
1019d61a4ce8SGerd Hoffmann {
1020d61a4ce8SGerd Hoffmann     return hda_audio_init(hda, &duplex);
1021d61a4ce8SGerd Hoffmann }
1022d61a4ce8SGerd Hoffmann 
102320110065SGerd Hoffmann static int hda_audio_init_micro(HDACodecDevice *hda)
102420110065SGerd Hoffmann {
102520110065SGerd Hoffmann     return hda_audio_init(hda, &micro);
102620110065SGerd Hoffmann }
102720110065SGerd Hoffmann 
1028dbaa7904SAnthony Liguori static void hda_audio_output_class_init(ObjectClass *klass, void *data)
1029dbaa7904SAnthony Liguori {
103039bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1031dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
1032dbaa7904SAnthony Liguori 
1033dbaa7904SAnthony Liguori     k->init = hda_audio_init_output;
1034dbaa7904SAnthony Liguori     k->exit = hda_audio_exit;
1035dbaa7904SAnthony Liguori     k->command = hda_audio_command;
1036dbaa7904SAnthony Liguori     k->stream = hda_audio_stream;
1037*125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
103820110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, output-only (line-out)";
103939bffca2SAnthony Liguori     dc->vmsd = &vmstate_hda_audio;
104039bffca2SAnthony Liguori     dc->props = hda_audio_properties;
1041dbaa7904SAnthony Liguori }
1042dbaa7904SAnthony Liguori 
10438c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = {
1044dbaa7904SAnthony Liguori     .name          = "hda-output",
104539bffca2SAnthony Liguori     .parent        = TYPE_HDA_CODEC_DEVICE,
104639bffca2SAnthony Liguori     .instance_size = sizeof(HDAAudioState),
1047dbaa7904SAnthony Liguori     .class_init    = hda_audio_output_class_init,
1048d61a4ce8SGerd Hoffmann };
1049d61a4ce8SGerd Hoffmann 
1050dbaa7904SAnthony Liguori static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
1051dbaa7904SAnthony Liguori {
105239bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1053dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
1054dbaa7904SAnthony Liguori 
1055dbaa7904SAnthony Liguori     k->init = hda_audio_init_duplex;
1056dbaa7904SAnthony Liguori     k->exit = hda_audio_exit;
1057dbaa7904SAnthony Liguori     k->command = hda_audio_command;
1058dbaa7904SAnthony Liguori     k->stream = hda_audio_stream;
1059*125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
106020110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
106139bffca2SAnthony Liguori     dc->vmsd = &vmstate_hda_audio;
106239bffca2SAnthony Liguori     dc->props = hda_audio_properties;
1063dbaa7904SAnthony Liguori }
1064dbaa7904SAnthony Liguori 
10658c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = {
1066dbaa7904SAnthony Liguori     .name          = "hda-duplex",
106739bffca2SAnthony Liguori     .parent        = TYPE_HDA_CODEC_DEVICE,
106839bffca2SAnthony Liguori     .instance_size = sizeof(HDAAudioState),
1069dbaa7904SAnthony Liguori     .class_init    = hda_audio_duplex_class_init,
1070d61a4ce8SGerd Hoffmann };
1071d61a4ce8SGerd Hoffmann 
107220110065SGerd Hoffmann static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
107320110065SGerd Hoffmann {
107420110065SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
107520110065SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
107620110065SGerd Hoffmann 
107720110065SGerd Hoffmann     k->init = hda_audio_init_micro;
107820110065SGerd Hoffmann     k->exit = hda_audio_exit;
107920110065SGerd Hoffmann     k->command = hda_audio_command;
108020110065SGerd Hoffmann     k->stream = hda_audio_stream;
1081*125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
108220110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
108320110065SGerd Hoffmann     dc->vmsd = &vmstate_hda_audio;
108420110065SGerd Hoffmann     dc->props = hda_audio_properties;
108520110065SGerd Hoffmann }
108620110065SGerd Hoffmann 
10878c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = {
108820110065SGerd Hoffmann     .name          = "hda-micro",
108920110065SGerd Hoffmann     .parent        = TYPE_HDA_CODEC_DEVICE,
109020110065SGerd Hoffmann     .instance_size = sizeof(HDAAudioState),
109120110065SGerd Hoffmann     .class_init    = hda_audio_micro_class_init,
109220110065SGerd Hoffmann };
109320110065SGerd Hoffmann 
109483f7d43aSAndreas Färber static void hda_audio_register_types(void)
1095d61a4ce8SGerd Hoffmann {
109639bffca2SAnthony Liguori     type_register_static(&hda_audio_output_info);
109739bffca2SAnthony Liguori     type_register_static(&hda_audio_duplex_info);
109820110065SGerd Hoffmann     type_register_static(&hda_audio_micro_info);
1099d61a4ce8SGerd Hoffmann }
110083f7d43aSAndreas Färber 
110183f7d43aSAndreas Färber type_init(hda_audio_register_types)
1102