xref: /qemu/hw/audio/hda-codec.c (revision 6086a565b0efe3eb66c5bf65a715a2c84425e47f)
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 
20*6086a565SPeter Maydell #include "qemu/osdep.h"
2183c9f4caSPaolo Bonzini #include "hw/hw.h"
2283c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
2347b43a1fSPaolo Bonzini #include "intel-hda.h"
2447b43a1fSPaolo Bonzini #include "intel-hda-defs.h"
25d61a4ce8SGerd Hoffmann #include "audio/audio.h"
26d61a4ce8SGerd Hoffmann 
27d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
28d61a4ce8SGerd Hoffmann 
29d61a4ce8SGerd Hoffmann typedef struct desc_param {
30d61a4ce8SGerd Hoffmann     uint32_t id;
31d61a4ce8SGerd Hoffmann     uint32_t val;
32d61a4ce8SGerd Hoffmann } desc_param;
33d61a4ce8SGerd Hoffmann 
34d61a4ce8SGerd Hoffmann typedef struct desc_node {
35d61a4ce8SGerd Hoffmann     uint32_t nid;
36d61a4ce8SGerd Hoffmann     const char *name;
37d61a4ce8SGerd Hoffmann     const desc_param *params;
38d61a4ce8SGerd Hoffmann     uint32_t nparams;
39d61a4ce8SGerd Hoffmann     uint32_t config;
40d61a4ce8SGerd Hoffmann     uint32_t pinctl;
41d61a4ce8SGerd Hoffmann     uint32_t *conn;
42d61a4ce8SGerd Hoffmann     uint32_t stindex;
43d61a4ce8SGerd Hoffmann } desc_node;
44d61a4ce8SGerd Hoffmann 
45d61a4ce8SGerd Hoffmann typedef struct desc_codec {
46d61a4ce8SGerd Hoffmann     const char *name;
47d61a4ce8SGerd Hoffmann     uint32_t iid;
48d61a4ce8SGerd Hoffmann     const desc_node *nodes;
49d61a4ce8SGerd Hoffmann     uint32_t nnodes;
50d61a4ce8SGerd Hoffmann } desc_codec;
51d61a4ce8SGerd Hoffmann 
52d61a4ce8SGerd Hoffmann static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
53d61a4ce8SGerd Hoffmann {
54d61a4ce8SGerd Hoffmann     int i;
55d61a4ce8SGerd Hoffmann 
56d61a4ce8SGerd Hoffmann     for (i = 0; i < node->nparams; i++) {
57d61a4ce8SGerd Hoffmann         if (node->params[i].id == id) {
58d61a4ce8SGerd Hoffmann             return &node->params[i];
59d61a4ce8SGerd Hoffmann         }
60d61a4ce8SGerd Hoffmann     }
61d61a4ce8SGerd Hoffmann     return NULL;
62d61a4ce8SGerd Hoffmann }
63d61a4ce8SGerd Hoffmann 
64d61a4ce8SGerd Hoffmann static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
65d61a4ce8SGerd Hoffmann {
66d61a4ce8SGerd Hoffmann     int i;
67d61a4ce8SGerd Hoffmann 
68d61a4ce8SGerd Hoffmann     for (i = 0; i < codec->nnodes; i++) {
69d61a4ce8SGerd Hoffmann         if (codec->nodes[i].nid == nid) {
70d61a4ce8SGerd Hoffmann             return &codec->nodes[i];
71d61a4ce8SGerd Hoffmann         }
72d61a4ce8SGerd Hoffmann     }
73d61a4ce8SGerd Hoffmann     return NULL;
74d61a4ce8SGerd Hoffmann }
75d61a4ce8SGerd Hoffmann 
76d61a4ce8SGerd Hoffmann static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
77d61a4ce8SGerd Hoffmann {
78d61a4ce8SGerd Hoffmann     if (format & AC_FMT_TYPE_NON_PCM) {
79d61a4ce8SGerd Hoffmann         return;
80d61a4ce8SGerd Hoffmann     }
81d61a4ce8SGerd Hoffmann 
82d61a4ce8SGerd Hoffmann     as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
83d61a4ce8SGerd Hoffmann 
84d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
85d61a4ce8SGerd Hoffmann     case 1: as->freq *= 2; break;
86d61a4ce8SGerd Hoffmann     case 2: as->freq *= 3; break;
87d61a4ce8SGerd Hoffmann     case 3: as->freq *= 4; break;
88d61a4ce8SGerd Hoffmann     }
89d61a4ce8SGerd Hoffmann 
90d61a4ce8SGerd Hoffmann     switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
91d61a4ce8SGerd Hoffmann     case 1: as->freq /= 2; break;
92d61a4ce8SGerd Hoffmann     case 2: as->freq /= 3; break;
93d61a4ce8SGerd Hoffmann     case 3: as->freq /= 4; break;
94d61a4ce8SGerd Hoffmann     case 4: as->freq /= 5; break;
95d61a4ce8SGerd Hoffmann     case 5: as->freq /= 6; break;
96d61a4ce8SGerd Hoffmann     case 6: as->freq /= 7; break;
97d61a4ce8SGerd Hoffmann     case 7: as->freq /= 8; break;
98d61a4ce8SGerd Hoffmann     }
99d61a4ce8SGerd Hoffmann 
100d61a4ce8SGerd Hoffmann     switch (format & AC_FMT_BITS_MASK) {
101d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_8:  as->fmt = AUD_FMT_S8;  break;
102d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
103d61a4ce8SGerd Hoffmann     case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
104d61a4ce8SGerd Hoffmann     }
105d61a4ce8SGerd Hoffmann 
106d61a4ce8SGerd Hoffmann     as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
107d61a4ce8SGerd Hoffmann }
108d61a4ce8SGerd Hoffmann 
109d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
110d61a4ce8SGerd Hoffmann /*
111d61a4ce8SGerd Hoffmann  * HDA codec descriptions
112d61a4ce8SGerd Hoffmann  */
113d61a4ce8SGerd Hoffmann 
114d61a4ce8SGerd Hoffmann /* some defines */
115d61a4ce8SGerd Hoffmann 
116d61a4ce8SGerd Hoffmann #define QEMU_HDA_ID_VENDOR  0x1af4
117d61a4ce8SGerd Hoffmann #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
118d61a4ce8SGerd Hoffmann                               0x1fc /* 16 -> 96 kHz */)
119d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_NONE    (0)
120d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_STEPS   0x4a
121d61a4ce8SGerd Hoffmann 
1222690e61eSBandan Das #define   PARAM mixemu
1232690e61eSBandan Das #define   HDA_MIXER
1247953793cSBandan Das #include "hda-codec-common.h"
1252690e61eSBandan Das 
1262690e61eSBandan Das #define   PARAM nomixemu
1272690e61eSBandan Das #include  "hda-codec-common.h"
1282690e61eSBandan Das 
129d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */
130d61a4ce8SGerd Hoffmann 
131d61a4ce8SGerd Hoffmann static const char *fmt2name[] = {
132d61a4ce8SGerd Hoffmann     [ AUD_FMT_U8  ] = "PCM-U8",
133d61a4ce8SGerd Hoffmann     [ AUD_FMT_S8  ] = "PCM-S8",
134d61a4ce8SGerd Hoffmann     [ AUD_FMT_U16 ] = "PCM-U16",
135d61a4ce8SGerd Hoffmann     [ AUD_FMT_S16 ] = "PCM-S16",
136d61a4ce8SGerd Hoffmann     [ AUD_FMT_U32 ] = "PCM-U32",
137d61a4ce8SGerd Hoffmann     [ AUD_FMT_S32 ] = "PCM-S32",
138d61a4ce8SGerd Hoffmann };
139d61a4ce8SGerd Hoffmann 
140d61a4ce8SGerd Hoffmann typedef struct HDAAudioState HDAAudioState;
141d61a4ce8SGerd Hoffmann typedef struct HDAAudioStream HDAAudioStream;
142d61a4ce8SGerd Hoffmann 
143d61a4ce8SGerd Hoffmann struct HDAAudioStream {
144d61a4ce8SGerd Hoffmann     HDAAudioState *state;
145d61a4ce8SGerd Hoffmann     const desc_node *node;
146d61a4ce8SGerd Hoffmann     bool output, running;
147d61a4ce8SGerd Hoffmann     uint32_t stream;
148d61a4ce8SGerd Hoffmann     uint32_t channel;
149d61a4ce8SGerd Hoffmann     uint32_t format;
150d61a4ce8SGerd Hoffmann     uint32_t gain_left, gain_right;
151d61a4ce8SGerd Hoffmann     bool mute_left, mute_right;
152d61a4ce8SGerd Hoffmann     struct audsettings as;
153d61a4ce8SGerd Hoffmann     union {
154d61a4ce8SGerd Hoffmann         SWVoiceIn *in;
155d61a4ce8SGerd Hoffmann         SWVoiceOut *out;
156d61a4ce8SGerd Hoffmann     } voice;
157d61a4ce8SGerd Hoffmann     uint8_t buf[HDA_BUFFER_SIZE];
158d61a4ce8SGerd Hoffmann     uint32_t bpos;
159d61a4ce8SGerd Hoffmann };
160d61a4ce8SGerd Hoffmann 
161cd6c8830SGerd Hoffmann #define TYPE_HDA_AUDIO "hda-audio"
162cd6c8830SGerd Hoffmann #define HDA_AUDIO(obj) OBJECT_CHECK(HDAAudioState, (obj), TYPE_HDA_AUDIO)
163cd6c8830SGerd Hoffmann 
164d61a4ce8SGerd Hoffmann struct HDAAudioState {
165d61a4ce8SGerd Hoffmann     HDACodecDevice hda;
166d61a4ce8SGerd Hoffmann     const char *name;
167d61a4ce8SGerd Hoffmann 
168d61a4ce8SGerd Hoffmann     QEMUSoundCard card;
169d61a4ce8SGerd Hoffmann     const desc_codec *desc;
170d61a4ce8SGerd Hoffmann     HDAAudioStream st[4];
171ba43d289SMarc-André Lureau     bool running_compat[16];
172ba43d289SMarc-André Lureau     bool running_real[2 * 16];
173d61a4ce8SGerd Hoffmann 
174d61a4ce8SGerd Hoffmann     /* properties */
175d61a4ce8SGerd Hoffmann     uint32_t debug;
1762690e61eSBandan Das     bool     mixer;
177d61a4ce8SGerd Hoffmann };
178d61a4ce8SGerd Hoffmann 
179d61a4ce8SGerd Hoffmann static void hda_audio_input_cb(void *opaque, int avail)
180d61a4ce8SGerd Hoffmann {
181d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
182d61a4ce8SGerd Hoffmann     int recv = 0;
183d61a4ce8SGerd Hoffmann     int len;
184d61a4ce8SGerd Hoffmann     bool rc;
185d61a4ce8SGerd Hoffmann 
186d61a4ce8SGerd Hoffmann     while (avail - recv >= sizeof(st->buf)) {
187d61a4ce8SGerd Hoffmann         if (st->bpos != sizeof(st->buf)) {
188d61a4ce8SGerd Hoffmann             len = AUD_read(st->voice.in, st->buf + st->bpos,
189d61a4ce8SGerd Hoffmann                            sizeof(st->buf) - st->bpos);
190d61a4ce8SGerd Hoffmann             st->bpos += len;
191d61a4ce8SGerd Hoffmann             recv += len;
192d61a4ce8SGerd Hoffmann             if (st->bpos != sizeof(st->buf)) {
193d61a4ce8SGerd Hoffmann                 break;
194d61a4ce8SGerd Hoffmann             }
195d61a4ce8SGerd Hoffmann         }
196d61a4ce8SGerd Hoffmann         rc = hda_codec_xfer(&st->state->hda, st->stream, false,
197d61a4ce8SGerd Hoffmann                             st->buf, sizeof(st->buf));
198d61a4ce8SGerd Hoffmann         if (!rc) {
199d61a4ce8SGerd Hoffmann             break;
200d61a4ce8SGerd Hoffmann         }
201d61a4ce8SGerd Hoffmann         st->bpos = 0;
202d61a4ce8SGerd Hoffmann     }
203d61a4ce8SGerd Hoffmann }
204d61a4ce8SGerd Hoffmann 
205d61a4ce8SGerd Hoffmann static void hda_audio_output_cb(void *opaque, int avail)
206d61a4ce8SGerd Hoffmann {
207d61a4ce8SGerd Hoffmann     HDAAudioStream *st = opaque;
208d61a4ce8SGerd Hoffmann     int sent = 0;
209d61a4ce8SGerd Hoffmann     int len;
210d61a4ce8SGerd Hoffmann     bool rc;
211d61a4ce8SGerd Hoffmann 
212d61a4ce8SGerd Hoffmann     while (avail - sent >= sizeof(st->buf)) {
213d61a4ce8SGerd Hoffmann         if (st->bpos == sizeof(st->buf)) {
214d61a4ce8SGerd Hoffmann             rc = hda_codec_xfer(&st->state->hda, st->stream, true,
215d61a4ce8SGerd Hoffmann                                 st->buf, sizeof(st->buf));
216d61a4ce8SGerd Hoffmann             if (!rc) {
217d61a4ce8SGerd Hoffmann                 break;
218d61a4ce8SGerd Hoffmann             }
219d61a4ce8SGerd Hoffmann             st->bpos = 0;
220d61a4ce8SGerd Hoffmann         }
221d61a4ce8SGerd Hoffmann         len = AUD_write(st->voice.out, st->buf + st->bpos,
222d61a4ce8SGerd Hoffmann                         sizeof(st->buf) - st->bpos);
223d61a4ce8SGerd Hoffmann         st->bpos += len;
224d61a4ce8SGerd Hoffmann         sent += len;
225d61a4ce8SGerd Hoffmann         if (st->bpos != sizeof(st->buf)) {
226d61a4ce8SGerd Hoffmann             break;
227d61a4ce8SGerd Hoffmann         }
228d61a4ce8SGerd Hoffmann     }
229d61a4ce8SGerd Hoffmann }
230d61a4ce8SGerd Hoffmann 
231d61a4ce8SGerd Hoffmann static void hda_audio_set_running(HDAAudioStream *st, bool running)
232d61a4ce8SGerd Hoffmann {
233d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
234d61a4ce8SGerd Hoffmann         return;
235d61a4ce8SGerd Hoffmann     }
236d61a4ce8SGerd Hoffmann     if (st->running == running) {
237d61a4ce8SGerd Hoffmann         return;
238d61a4ce8SGerd Hoffmann     }
239d61a4ce8SGerd Hoffmann     st->running = running;
240d61a4ce8SGerd Hoffmann     dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
241d61a4ce8SGerd Hoffmann            st->running ? "on" : "off", st->stream);
242d61a4ce8SGerd Hoffmann     if (st->output) {
243d61a4ce8SGerd Hoffmann         AUD_set_active_out(st->voice.out, st->running);
244d61a4ce8SGerd Hoffmann     } else {
245d61a4ce8SGerd Hoffmann         AUD_set_active_in(st->voice.in, st->running);
246d61a4ce8SGerd Hoffmann     }
247d61a4ce8SGerd Hoffmann }
248d61a4ce8SGerd Hoffmann 
249d61a4ce8SGerd Hoffmann static void hda_audio_set_amp(HDAAudioStream *st)
250d61a4ce8SGerd Hoffmann {
251d61a4ce8SGerd Hoffmann     bool muted;
252d61a4ce8SGerd Hoffmann     uint32_t left, right;
253d61a4ce8SGerd Hoffmann 
254d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
255d61a4ce8SGerd Hoffmann         return;
256d61a4ce8SGerd Hoffmann     }
257d61a4ce8SGerd Hoffmann 
258d61a4ce8SGerd Hoffmann     muted = st->mute_left && st->mute_right;
259d61a4ce8SGerd Hoffmann     left  = st->mute_left  ? 0 : st->gain_left;
260d61a4ce8SGerd Hoffmann     right = st->mute_right ? 0 : st->gain_right;
261d61a4ce8SGerd Hoffmann 
262d61a4ce8SGerd Hoffmann     left = left * 255 / QEMU_HDA_AMP_STEPS;
263d61a4ce8SGerd Hoffmann     right = right * 255 / QEMU_HDA_AMP_STEPS;
264d61a4ce8SGerd Hoffmann 
2654843877eSGerd Hoffmann     if (!st->state->mixer) {
2664843877eSGerd Hoffmann         return;
2674843877eSGerd Hoffmann     }
268d61a4ce8SGerd Hoffmann     if (st->output) {
269d61a4ce8SGerd Hoffmann         AUD_set_volume_out(st->voice.out, muted, left, right);
270d61a4ce8SGerd Hoffmann     } else {
271d61a4ce8SGerd Hoffmann         AUD_set_volume_in(st->voice.in, muted, left, right);
272d61a4ce8SGerd Hoffmann     }
273d61a4ce8SGerd Hoffmann }
274d61a4ce8SGerd Hoffmann 
275d61a4ce8SGerd Hoffmann static void hda_audio_setup(HDAAudioStream *st)
276d61a4ce8SGerd Hoffmann {
277d61a4ce8SGerd Hoffmann     if (st->node == NULL) {
278d61a4ce8SGerd Hoffmann         return;
279d61a4ce8SGerd Hoffmann     }
280d61a4ce8SGerd Hoffmann 
281d61a4ce8SGerd Hoffmann     dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
282d61a4ce8SGerd Hoffmann            st->node->name, st->as.nchannels,
283d61a4ce8SGerd Hoffmann            fmt2name[st->as.fmt], st->as.freq);
284d61a4ce8SGerd Hoffmann 
285d61a4ce8SGerd Hoffmann     if (st->output) {
286d61a4ce8SGerd Hoffmann         st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
287d61a4ce8SGerd Hoffmann                                      st->node->name, st,
288d61a4ce8SGerd Hoffmann                                      hda_audio_output_cb, &st->as);
289d61a4ce8SGerd Hoffmann     } else {
290d61a4ce8SGerd Hoffmann         st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
291d61a4ce8SGerd Hoffmann                                    st->node->name, st,
292d61a4ce8SGerd Hoffmann                                    hda_audio_input_cb, &st->as);
293d61a4ce8SGerd Hoffmann     }
294d61a4ce8SGerd Hoffmann }
295d61a4ce8SGerd Hoffmann 
296d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
297d61a4ce8SGerd Hoffmann {
298cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
299d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
300d61a4ce8SGerd Hoffmann     const desc_node *node = NULL;
301d61a4ce8SGerd Hoffmann     const desc_param *param;
302d61a4ce8SGerd Hoffmann     uint32_t verb, payload, response, count, shift;
303d61a4ce8SGerd Hoffmann 
304d61a4ce8SGerd Hoffmann     if ((data & 0x70000) == 0x70000) {
305d61a4ce8SGerd Hoffmann         /* 12/8 id/payload */
306d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xfff;
307d61a4ce8SGerd Hoffmann         payload = data & 0x00ff;
308d61a4ce8SGerd Hoffmann     } else {
309d61a4ce8SGerd Hoffmann         /* 4/16 id/payload */
310d61a4ce8SGerd Hoffmann         verb = (data >> 8) & 0xf00;
311d61a4ce8SGerd Hoffmann         payload = data & 0xffff;
312d61a4ce8SGerd Hoffmann     }
313d61a4ce8SGerd Hoffmann 
314d61a4ce8SGerd Hoffmann     node = hda_codec_find_node(a->desc, nid);
315d61a4ce8SGerd Hoffmann     if (node == NULL) {
316d61a4ce8SGerd Hoffmann         goto fail;
317d61a4ce8SGerd Hoffmann     }
318d61a4ce8SGerd Hoffmann     dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
319d61a4ce8SGerd Hoffmann            __FUNCTION__, nid, node->name, verb, payload);
320d61a4ce8SGerd Hoffmann 
321d61a4ce8SGerd Hoffmann     switch (verb) {
322d61a4ce8SGerd Hoffmann     /* all nodes */
323d61a4ce8SGerd Hoffmann     case AC_VERB_PARAMETERS:
324d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, payload);
325d61a4ce8SGerd Hoffmann         if (param == NULL) {
326d61a4ce8SGerd Hoffmann             goto fail;
327d61a4ce8SGerd Hoffmann         }
328d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, param->val);
329d61a4ce8SGerd Hoffmann         break;
330d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SUBSYSTEM_ID:
331d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, a->desc->iid);
332d61a4ce8SGerd Hoffmann         break;
333d61a4ce8SGerd Hoffmann 
334d61a4ce8SGerd Hoffmann     /* all functions */
335d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONNECT_LIST:
336d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
337d61a4ce8SGerd Hoffmann         count = param ? param->val : 0;
338d61a4ce8SGerd Hoffmann         response = 0;
339d61a4ce8SGerd Hoffmann         shift = 0;
340d61a4ce8SGerd Hoffmann         while (payload < count && shift < 32) {
341d61a4ce8SGerd Hoffmann             response |= node->conn[payload] << shift;
342d61a4ce8SGerd Hoffmann             payload++;
343d61a4ce8SGerd Hoffmann             shift += 8;
344d61a4ce8SGerd Hoffmann         }
345d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
346d61a4ce8SGerd Hoffmann         break;
347d61a4ce8SGerd Hoffmann 
348d61a4ce8SGerd Hoffmann     /* pin widget */
349d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONFIG_DEFAULT:
350d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->config);
351d61a4ce8SGerd Hoffmann         break;
352d61a4ce8SGerd Hoffmann     case AC_VERB_GET_PIN_WIDGET_CONTROL:
353d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, node->pinctl);
354d61a4ce8SGerd Hoffmann         break;
355d61a4ce8SGerd Hoffmann     case AC_VERB_SET_PIN_WIDGET_CONTROL:
356d61a4ce8SGerd Hoffmann         if (node->pinctl != payload) {
357d61a4ce8SGerd Hoffmann             dprint(a, 1, "unhandled pin control bit\n");
358d61a4ce8SGerd Hoffmann         }
359d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
360d61a4ce8SGerd Hoffmann         break;
361d61a4ce8SGerd Hoffmann 
362d61a4ce8SGerd Hoffmann     /* audio in/out widget */
363d61a4ce8SGerd Hoffmann     case AC_VERB_SET_CHANNEL_STREAMID:
364d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
365d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
366d61a4ce8SGerd Hoffmann             goto fail;
367d61a4ce8SGerd Hoffmann         }
368d61a4ce8SGerd Hoffmann         hda_audio_set_running(st, false);
369d61a4ce8SGerd Hoffmann         st->stream = (payload >> 4) & 0x0f;
370d61a4ce8SGerd Hoffmann         st->channel = payload & 0x0f;
371d61a4ce8SGerd Hoffmann         dprint(a, 2, "%s: stream %d, channel %d\n",
372d61a4ce8SGerd Hoffmann                st->node->name, st->stream, st->channel);
373ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
374d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
375d61a4ce8SGerd Hoffmann         break;
376d61a4ce8SGerd Hoffmann     case AC_VERB_GET_CONV:
377d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
378d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
379d61a4ce8SGerd Hoffmann             goto fail;
380d61a4ce8SGerd Hoffmann         }
381d61a4ce8SGerd Hoffmann         response = st->stream << 4 | st->channel;
382d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
383d61a4ce8SGerd Hoffmann         break;
384d61a4ce8SGerd Hoffmann     case AC_VERB_SET_STREAM_FORMAT:
385d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
386d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
387d61a4ce8SGerd Hoffmann             goto fail;
388d61a4ce8SGerd Hoffmann         }
389d61a4ce8SGerd Hoffmann         st->format = payload;
390d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
391d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
392d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
393d61a4ce8SGerd Hoffmann         break;
394d61a4ce8SGerd Hoffmann     case AC_VERB_GET_STREAM_FORMAT:
395d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
396d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
397d61a4ce8SGerd Hoffmann             goto fail;
398d61a4ce8SGerd Hoffmann         }
399d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, st->format);
400d61a4ce8SGerd Hoffmann         break;
401d61a4ce8SGerd Hoffmann     case AC_VERB_GET_AMP_GAIN_MUTE:
402d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
403d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
404d61a4ce8SGerd Hoffmann             goto fail;
405d61a4ce8SGerd Hoffmann         }
406d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_GET_LEFT) {
407d61a4ce8SGerd Hoffmann             response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
408d61a4ce8SGerd Hoffmann         } else {
409d61a4ce8SGerd Hoffmann             response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
410d61a4ce8SGerd Hoffmann         }
411d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, response);
412d61a4ce8SGerd Hoffmann         break;
413d61a4ce8SGerd Hoffmann     case AC_VERB_SET_AMP_GAIN_MUTE:
414d61a4ce8SGerd Hoffmann         st = a->st + node->stindex;
415d61a4ce8SGerd Hoffmann         if (st->node == NULL) {
416d61a4ce8SGerd Hoffmann             goto fail;
417d61a4ce8SGerd Hoffmann         }
418d61a4ce8SGerd Hoffmann         dprint(a, 1, "amp (%s): %s%s%s%s index %d  gain %3d %s\n",
419d61a4ce8SGerd Hoffmann                st->node->name,
420d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
421d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INPUT)  ? "i" : "-",
422d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_LEFT)   ? "l" : "-",
423d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_RIGHT)  ? "r" : "-",
424d61a4ce8SGerd Hoffmann                (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
425d61a4ce8SGerd Hoffmann                (payload & AC_AMP_GAIN),
426d61a4ce8SGerd Hoffmann                (payload & AC_AMP_MUTE) ? "muted" : "");
427d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_LEFT) {
428d61a4ce8SGerd Hoffmann             st->gain_left = payload & AC_AMP_GAIN;
429d61a4ce8SGerd Hoffmann             st->mute_left = payload & AC_AMP_MUTE;
430d61a4ce8SGerd Hoffmann         }
431d61a4ce8SGerd Hoffmann         if (payload & AC_AMP_SET_RIGHT) {
432d61a4ce8SGerd Hoffmann             st->gain_right = payload & AC_AMP_GAIN;
433d61a4ce8SGerd Hoffmann             st->mute_right = payload & AC_AMP_MUTE;
434d61a4ce8SGerd Hoffmann         }
435d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
436d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
437d61a4ce8SGerd Hoffmann         break;
438d61a4ce8SGerd Hoffmann 
439d61a4ce8SGerd Hoffmann     /* not supported */
440d61a4ce8SGerd Hoffmann     case AC_VERB_SET_POWER_STATE:
441d61a4ce8SGerd Hoffmann     case AC_VERB_GET_POWER_STATE:
442d61a4ce8SGerd Hoffmann     case AC_VERB_GET_SDI_SELECT:
443d61a4ce8SGerd Hoffmann         hda_codec_response(hda, true, 0);
444d61a4ce8SGerd Hoffmann         break;
445d61a4ce8SGerd Hoffmann     default:
446d61a4ce8SGerd Hoffmann         goto fail;
447d61a4ce8SGerd Hoffmann     }
448d61a4ce8SGerd Hoffmann     return;
449d61a4ce8SGerd Hoffmann 
450d61a4ce8SGerd Hoffmann fail:
451d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
452d61a4ce8SGerd Hoffmann            __FUNCTION__, nid, node ? node->name : "?", verb, payload);
453d61a4ce8SGerd Hoffmann     hda_codec_response(hda, true, 0);
454d61a4ce8SGerd Hoffmann }
455d61a4ce8SGerd Hoffmann 
456ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
457d61a4ce8SGerd Hoffmann {
458cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
459d61a4ce8SGerd Hoffmann     int s;
460d61a4ce8SGerd Hoffmann 
461ba43d289SMarc-André Lureau     a->running_compat[stnr] = running;
462ba43d289SMarc-André Lureau     a->running_real[output * 16 + stnr] = running;
463d61a4ce8SGerd Hoffmann     for (s = 0; s < ARRAY_SIZE(a->st); s++) {
464d61a4ce8SGerd Hoffmann         if (a->st[s].node == NULL) {
465d61a4ce8SGerd Hoffmann             continue;
466d61a4ce8SGerd Hoffmann         }
467ba43d289SMarc-André Lureau         if (a->st[s].output != output) {
468ba43d289SMarc-André Lureau             continue;
469ba43d289SMarc-André Lureau         }
470d61a4ce8SGerd Hoffmann         if (a->st[s].stream != stnr) {
471d61a4ce8SGerd Hoffmann             continue;
472d61a4ce8SGerd Hoffmann         }
473d61a4ce8SGerd Hoffmann         hda_audio_set_running(&a->st[s], running);
474d61a4ce8SGerd Hoffmann     }
475d61a4ce8SGerd Hoffmann }
476d61a4ce8SGerd Hoffmann 
477d61a4ce8SGerd Hoffmann static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
478d61a4ce8SGerd Hoffmann {
479cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
480d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
481d61a4ce8SGerd Hoffmann     const desc_node *node;
482d61a4ce8SGerd Hoffmann     const desc_param *param;
483d61a4ce8SGerd Hoffmann     uint32_t i, type;
484d61a4ce8SGerd Hoffmann 
485d61a4ce8SGerd Hoffmann     a->desc = desc;
486f79f2bfcSAnthony Liguori     a->name = object_get_typename(OBJECT(a));
487d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
488d61a4ce8SGerd Hoffmann 
489d61a4ce8SGerd Hoffmann     AUD_register_card("hda", &a->card);
490d61a4ce8SGerd Hoffmann     for (i = 0; i < a->desc->nnodes; i++) {
491d61a4ce8SGerd Hoffmann         node = a->desc->nodes + i;
492d61a4ce8SGerd Hoffmann         param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
4932ab5bf67SGonglei         if (param == NULL) {
494d61a4ce8SGerd Hoffmann             continue;
4952ab5bf67SGonglei         }
496d61a4ce8SGerd Hoffmann         type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
497d61a4ce8SGerd Hoffmann         switch (type) {
498d61a4ce8SGerd Hoffmann         case AC_WID_AUD_OUT:
499d61a4ce8SGerd Hoffmann         case AC_WID_AUD_IN:
500d61a4ce8SGerd Hoffmann             assert(node->stindex < ARRAY_SIZE(a->st));
501d61a4ce8SGerd Hoffmann             st = a->st + node->stindex;
502d61a4ce8SGerd Hoffmann             st->state = a;
503d61a4ce8SGerd Hoffmann             st->node = node;
504d61a4ce8SGerd Hoffmann             if (type == AC_WID_AUD_OUT) {
505d61a4ce8SGerd Hoffmann                 /* unmute output by default */
506d61a4ce8SGerd Hoffmann                 st->gain_left = QEMU_HDA_AMP_STEPS;
507d61a4ce8SGerd Hoffmann                 st->gain_right = QEMU_HDA_AMP_STEPS;
508d61a4ce8SGerd Hoffmann                 st->bpos = sizeof(st->buf);
509d61a4ce8SGerd Hoffmann                 st->output = true;
510d61a4ce8SGerd Hoffmann             } else {
511d61a4ce8SGerd Hoffmann                 st->output = false;
512d61a4ce8SGerd Hoffmann             }
513d61a4ce8SGerd Hoffmann             st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
514d61a4ce8SGerd Hoffmann                 (1 << AC_FMT_CHAN_SHIFT);
515d61a4ce8SGerd Hoffmann             hda_codec_parse_fmt(st->format, &st->as);
516d61a4ce8SGerd Hoffmann             hda_audio_setup(st);
517d61a4ce8SGerd Hoffmann             break;
518d61a4ce8SGerd Hoffmann         }
519d61a4ce8SGerd Hoffmann     }
520d61a4ce8SGerd Hoffmann     return 0;
521d61a4ce8SGerd Hoffmann }
522d61a4ce8SGerd Hoffmann 
523129dcd2cSGerd Hoffmann static int hda_audio_exit(HDACodecDevice *hda)
524129dcd2cSGerd Hoffmann {
525cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
526129dcd2cSGerd Hoffmann     HDAAudioStream *st;
527129dcd2cSGerd Hoffmann     int i;
528129dcd2cSGerd Hoffmann 
529129dcd2cSGerd Hoffmann     dprint(a, 1, "%s\n", __FUNCTION__);
530129dcd2cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
531129dcd2cSGerd Hoffmann         st = a->st + i;
532129dcd2cSGerd Hoffmann         if (st->node == NULL) {
533129dcd2cSGerd Hoffmann             continue;
534129dcd2cSGerd Hoffmann         }
535129dcd2cSGerd Hoffmann         if (st->output) {
536129dcd2cSGerd Hoffmann             AUD_close_out(&a->card, st->voice.out);
537129dcd2cSGerd Hoffmann         } else {
538129dcd2cSGerd Hoffmann             AUD_close_in(&a->card, st->voice.in);
539129dcd2cSGerd Hoffmann         }
540129dcd2cSGerd Hoffmann     }
541129dcd2cSGerd Hoffmann     AUD_remove_card(&a->card);
542129dcd2cSGerd Hoffmann     return 0;
543129dcd2cSGerd Hoffmann }
544129dcd2cSGerd Hoffmann 
545d61a4ce8SGerd Hoffmann static int hda_audio_post_load(void *opaque, int version)
546d61a4ce8SGerd Hoffmann {
547d61a4ce8SGerd Hoffmann     HDAAudioState *a = opaque;
548d61a4ce8SGerd Hoffmann     HDAAudioStream *st;
549d61a4ce8SGerd Hoffmann     int i;
550d61a4ce8SGerd Hoffmann 
551d61a4ce8SGerd Hoffmann     dprint(a, 1, "%s\n", __FUNCTION__);
552ba43d289SMarc-André Lureau     if (version == 1) {
553ba43d289SMarc-André Lureau         /* assume running_compat[] is for output streams */
554ba43d289SMarc-André Lureau         for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
555ba43d289SMarc-André Lureau             a->running_real[16 + i] = a->running_compat[i];
556ba43d289SMarc-André Lureau     }
557ba43d289SMarc-André Lureau 
558d61a4ce8SGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
559d61a4ce8SGerd Hoffmann         st = a->st + i;
560d61a4ce8SGerd Hoffmann         if (st->node == NULL)
561d61a4ce8SGerd Hoffmann             continue;
562d61a4ce8SGerd Hoffmann         hda_codec_parse_fmt(st->format, &st->as);
563d61a4ce8SGerd Hoffmann         hda_audio_setup(st);
564d61a4ce8SGerd Hoffmann         hda_audio_set_amp(st);
565ba43d289SMarc-André Lureau         hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
566d61a4ce8SGerd Hoffmann     }
567d61a4ce8SGerd Hoffmann     return 0;
568d61a4ce8SGerd Hoffmann }
569d61a4ce8SGerd Hoffmann 
57039e6a38cSGerd Hoffmann static void hda_audio_reset(DeviceState *dev)
57139e6a38cSGerd Hoffmann {
572cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(dev);
57339e6a38cSGerd Hoffmann     HDAAudioStream *st;
57439e6a38cSGerd Hoffmann     int i;
57539e6a38cSGerd Hoffmann 
57639e6a38cSGerd Hoffmann     dprint(a, 1, "%s\n", __func__);
57739e6a38cSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(a->st); i++) {
57839e6a38cSGerd Hoffmann         st = a->st + i;
57939e6a38cSGerd Hoffmann         if (st->node != NULL) {
58039e6a38cSGerd Hoffmann             hda_audio_set_running(st, false);
58139e6a38cSGerd Hoffmann         }
58239e6a38cSGerd Hoffmann     }
58339e6a38cSGerd Hoffmann }
58439e6a38cSGerd Hoffmann 
585d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = {
586d61a4ce8SGerd Hoffmann     .name = "hda-audio-stream",
587d61a4ce8SGerd Hoffmann     .version_id = 1,
588d61a4ce8SGerd Hoffmann     .fields = (VMStateField[]) {
589d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(stream, HDAAudioStream),
590d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(channel, HDAAudioStream),
591d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(format, HDAAudioStream),
592d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_left, HDAAudioStream),
593d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(gain_right, HDAAudioStream),
594d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_left, HDAAudioStream),
595d61a4ce8SGerd Hoffmann         VMSTATE_BOOL(mute_right, HDAAudioStream),
596d61a4ce8SGerd Hoffmann         VMSTATE_UINT32(bpos, HDAAudioStream),
597d61a4ce8SGerd Hoffmann         VMSTATE_BUFFER(buf, HDAAudioStream),
598d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
599d61a4ce8SGerd Hoffmann     }
600d61a4ce8SGerd Hoffmann };
601d61a4ce8SGerd Hoffmann 
602d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio = {
603d61a4ce8SGerd Hoffmann     .name = "hda-audio",
604ba43d289SMarc-André Lureau     .version_id = 2,
605d61a4ce8SGerd Hoffmann     .post_load = hda_audio_post_load,
606d61a4ce8SGerd Hoffmann     .fields = (VMStateField[]) {
607d61a4ce8SGerd Hoffmann         VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
608d61a4ce8SGerd Hoffmann                              vmstate_hda_audio_stream,
609d61a4ce8SGerd Hoffmann                              HDAAudioStream),
610ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
611ba43d289SMarc-André Lureau         VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
612d61a4ce8SGerd Hoffmann         VMSTATE_END_OF_LIST()
613d61a4ce8SGerd Hoffmann     }
614d61a4ce8SGerd Hoffmann };
615d61a4ce8SGerd Hoffmann 
616d61a4ce8SGerd Hoffmann static Property hda_audio_properties[] = {
617d61a4ce8SGerd Hoffmann     DEFINE_PROP_UINT32("debug", HDAAudioState, debug,   0),
6182690e61eSBandan Das     DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer,  true),
619d61a4ce8SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
620d61a4ce8SGerd Hoffmann };
621d61a4ce8SGerd Hoffmann 
622d61a4ce8SGerd Hoffmann static int hda_audio_init_output(HDACodecDevice *hda)
623d61a4ce8SGerd Hoffmann {
624cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
6252690e61eSBandan Das 
6262690e61eSBandan Das     if (!a->mixer) {
6272690e61eSBandan Das         return hda_audio_init(hda, &output_nomixemu);
6282690e61eSBandan Das     } else {
6292690e61eSBandan Das         return hda_audio_init(hda, &output_mixemu);
6302690e61eSBandan Das     }
631d61a4ce8SGerd Hoffmann }
632d61a4ce8SGerd Hoffmann 
633d61a4ce8SGerd Hoffmann static int hda_audio_init_duplex(HDACodecDevice *hda)
634d61a4ce8SGerd Hoffmann {
635cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
6362690e61eSBandan Das 
6372690e61eSBandan Das     if (!a->mixer) {
6382690e61eSBandan Das         return hda_audio_init(hda, &duplex_nomixemu);
6392690e61eSBandan Das     } else {
6402690e61eSBandan Das         return hda_audio_init(hda, &duplex_mixemu);
6412690e61eSBandan Das     }
642d61a4ce8SGerd Hoffmann }
643d61a4ce8SGerd Hoffmann 
64420110065SGerd Hoffmann static int hda_audio_init_micro(HDACodecDevice *hda)
64520110065SGerd Hoffmann {
646cd6c8830SGerd Hoffmann     HDAAudioState *a = HDA_AUDIO(hda);
6472690e61eSBandan Das 
6482690e61eSBandan Das     if (!a->mixer) {
6492690e61eSBandan Das         return hda_audio_init(hda, &micro_nomixemu);
6502690e61eSBandan Das     } else {
6512690e61eSBandan Das         return hda_audio_init(hda, &micro_mixemu);
6522690e61eSBandan Das     }
65320110065SGerd Hoffmann }
65420110065SGerd Hoffmann 
655cd6c8830SGerd Hoffmann static void hda_audio_base_class_init(ObjectClass *klass, void *data)
656cd6c8830SGerd Hoffmann {
657cd6c8830SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
658cd6c8830SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
659cd6c8830SGerd Hoffmann 
660cd6c8830SGerd Hoffmann     k->exit = hda_audio_exit;
661cd6c8830SGerd Hoffmann     k->command = hda_audio_command;
662cd6c8830SGerd Hoffmann     k->stream = hda_audio_stream;
663cd6c8830SGerd Hoffmann     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
664cd6c8830SGerd Hoffmann     dc->reset = hda_audio_reset;
665cd6c8830SGerd Hoffmann     dc->vmsd = &vmstate_hda_audio;
666cd6c8830SGerd Hoffmann     dc->props = hda_audio_properties;
667cd6c8830SGerd Hoffmann }
668cd6c8830SGerd Hoffmann 
669cd6c8830SGerd Hoffmann static const TypeInfo hda_audio_info = {
670cd6c8830SGerd Hoffmann     .name          = TYPE_HDA_AUDIO,
671cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_CODEC_DEVICE,
672cd6c8830SGerd Hoffmann     .class_init    = hda_audio_base_class_init,
673cd6c8830SGerd Hoffmann     .abstract      = true,
674cd6c8830SGerd Hoffmann };
675cd6c8830SGerd Hoffmann 
676dbaa7904SAnthony Liguori static void hda_audio_output_class_init(ObjectClass *klass, void *data)
677dbaa7904SAnthony Liguori {
67839bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
679dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
680dbaa7904SAnthony Liguori 
681dbaa7904SAnthony Liguori     k->init = hda_audio_init_output;
68220110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, output-only (line-out)";
683dbaa7904SAnthony Liguori }
684dbaa7904SAnthony Liguori 
6858c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = {
686dbaa7904SAnthony Liguori     .name          = "hda-output",
687cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
68839bffca2SAnthony Liguori     .instance_size = sizeof(HDAAudioState),
689dbaa7904SAnthony Liguori     .class_init    = hda_audio_output_class_init,
690d61a4ce8SGerd Hoffmann };
691d61a4ce8SGerd Hoffmann 
692dbaa7904SAnthony Liguori static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
693dbaa7904SAnthony Liguori {
69439bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
695dbaa7904SAnthony Liguori     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
696dbaa7904SAnthony Liguori 
697dbaa7904SAnthony Liguori     k->init = hda_audio_init_duplex;
69820110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
699dbaa7904SAnthony Liguori }
700dbaa7904SAnthony Liguori 
7018c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = {
702dbaa7904SAnthony Liguori     .name          = "hda-duplex",
703cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
70439bffca2SAnthony Liguori     .instance_size = sizeof(HDAAudioState),
705dbaa7904SAnthony Liguori     .class_init    = hda_audio_duplex_class_init,
706d61a4ce8SGerd Hoffmann };
707d61a4ce8SGerd Hoffmann 
70820110065SGerd Hoffmann static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
70920110065SGerd Hoffmann {
71020110065SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
71120110065SGerd Hoffmann     HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
71220110065SGerd Hoffmann 
71320110065SGerd Hoffmann     k->init = hda_audio_init_micro;
71420110065SGerd Hoffmann     dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
71520110065SGerd Hoffmann }
71620110065SGerd Hoffmann 
7178c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = {
71820110065SGerd Hoffmann     .name          = "hda-micro",
719cd6c8830SGerd Hoffmann     .parent        = TYPE_HDA_AUDIO,
72020110065SGerd Hoffmann     .instance_size = sizeof(HDAAudioState),
72120110065SGerd Hoffmann     .class_init    = hda_audio_micro_class_init,
72220110065SGerd Hoffmann };
72320110065SGerd Hoffmann 
72483f7d43aSAndreas Färber static void hda_audio_register_types(void)
725d61a4ce8SGerd Hoffmann {
726cd6c8830SGerd Hoffmann     type_register_static(&hda_audio_info);
72739bffca2SAnthony Liguori     type_register_static(&hda_audio_output_info);
72839bffca2SAnthony Liguori     type_register_static(&hda_audio_duplex_info);
72920110065SGerd Hoffmann     type_register_static(&hda_audio_micro_info);
730d61a4ce8SGerd Hoffmann }
73183f7d43aSAndreas Färber 
73283f7d43aSAndreas Färber type_init(hda_audio_register_types)
733