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 1212690e61eSBandan Das #define PARAM mixemu 1222690e61eSBandan Das #define HDA_MIXER 1237953793cSBandan Das #include "hda-codec-common.h" 1242690e61eSBandan Das 1252690e61eSBandan Das #define PARAM nomixemu 1262690e61eSBandan Das #include "hda-codec-common.h" 1272690e61eSBandan Das 128d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */ 129d61a4ce8SGerd Hoffmann 130d61a4ce8SGerd Hoffmann static const char *fmt2name[] = { 131d61a4ce8SGerd Hoffmann [ AUD_FMT_U8 ] = "PCM-U8", 132d61a4ce8SGerd Hoffmann [ AUD_FMT_S8 ] = "PCM-S8", 133d61a4ce8SGerd Hoffmann [ AUD_FMT_U16 ] = "PCM-U16", 134d61a4ce8SGerd Hoffmann [ AUD_FMT_S16 ] = "PCM-S16", 135d61a4ce8SGerd Hoffmann [ AUD_FMT_U32 ] = "PCM-U32", 136d61a4ce8SGerd Hoffmann [ AUD_FMT_S32 ] = "PCM-S32", 137d61a4ce8SGerd Hoffmann }; 138d61a4ce8SGerd Hoffmann 139d61a4ce8SGerd Hoffmann typedef struct HDAAudioState HDAAudioState; 140d61a4ce8SGerd Hoffmann typedef struct HDAAudioStream HDAAudioStream; 141d61a4ce8SGerd Hoffmann 142d61a4ce8SGerd Hoffmann struct HDAAudioStream { 143d61a4ce8SGerd Hoffmann HDAAudioState *state; 144d61a4ce8SGerd Hoffmann const desc_node *node; 145d61a4ce8SGerd Hoffmann bool output, running; 146d61a4ce8SGerd Hoffmann uint32_t stream; 147d61a4ce8SGerd Hoffmann uint32_t channel; 148d61a4ce8SGerd Hoffmann uint32_t format; 149d61a4ce8SGerd Hoffmann uint32_t gain_left, gain_right; 150d61a4ce8SGerd Hoffmann bool mute_left, mute_right; 151d61a4ce8SGerd Hoffmann struct audsettings as; 152d61a4ce8SGerd Hoffmann union { 153d61a4ce8SGerd Hoffmann SWVoiceIn *in; 154d61a4ce8SGerd Hoffmann SWVoiceOut *out; 155d61a4ce8SGerd Hoffmann } voice; 156d61a4ce8SGerd Hoffmann uint8_t buf[HDA_BUFFER_SIZE]; 157d61a4ce8SGerd Hoffmann uint32_t bpos; 158d61a4ce8SGerd Hoffmann }; 159d61a4ce8SGerd Hoffmann 160d61a4ce8SGerd Hoffmann struct HDAAudioState { 161d61a4ce8SGerd Hoffmann HDACodecDevice hda; 162d61a4ce8SGerd Hoffmann const char *name; 163d61a4ce8SGerd Hoffmann 164d61a4ce8SGerd Hoffmann QEMUSoundCard card; 165d61a4ce8SGerd Hoffmann const desc_codec *desc; 166d61a4ce8SGerd Hoffmann HDAAudioStream st[4]; 167ba43d289SMarc-André Lureau bool running_compat[16]; 168ba43d289SMarc-André Lureau bool running_real[2 * 16]; 169d61a4ce8SGerd Hoffmann 170d61a4ce8SGerd Hoffmann /* properties */ 171d61a4ce8SGerd Hoffmann uint32_t debug; 1722690e61eSBandan Das bool mixer; 173d61a4ce8SGerd Hoffmann }; 174d61a4ce8SGerd Hoffmann 175d61a4ce8SGerd Hoffmann static void hda_audio_input_cb(void *opaque, int avail) 176d61a4ce8SGerd Hoffmann { 177d61a4ce8SGerd Hoffmann HDAAudioStream *st = opaque; 178d61a4ce8SGerd Hoffmann int recv = 0; 179d61a4ce8SGerd Hoffmann int len; 180d61a4ce8SGerd Hoffmann bool rc; 181d61a4ce8SGerd Hoffmann 182d61a4ce8SGerd Hoffmann while (avail - recv >= sizeof(st->buf)) { 183d61a4ce8SGerd Hoffmann if (st->bpos != sizeof(st->buf)) { 184d61a4ce8SGerd Hoffmann len = AUD_read(st->voice.in, st->buf + st->bpos, 185d61a4ce8SGerd Hoffmann sizeof(st->buf) - st->bpos); 186d61a4ce8SGerd Hoffmann st->bpos += len; 187d61a4ce8SGerd Hoffmann recv += len; 188d61a4ce8SGerd Hoffmann if (st->bpos != sizeof(st->buf)) { 189d61a4ce8SGerd Hoffmann break; 190d61a4ce8SGerd Hoffmann } 191d61a4ce8SGerd Hoffmann } 192d61a4ce8SGerd Hoffmann rc = hda_codec_xfer(&st->state->hda, st->stream, false, 193d61a4ce8SGerd Hoffmann st->buf, sizeof(st->buf)); 194d61a4ce8SGerd Hoffmann if (!rc) { 195d61a4ce8SGerd Hoffmann break; 196d61a4ce8SGerd Hoffmann } 197d61a4ce8SGerd Hoffmann st->bpos = 0; 198d61a4ce8SGerd Hoffmann } 199d61a4ce8SGerd Hoffmann } 200d61a4ce8SGerd Hoffmann 201d61a4ce8SGerd Hoffmann static void hda_audio_output_cb(void *opaque, int avail) 202d61a4ce8SGerd Hoffmann { 203d61a4ce8SGerd Hoffmann HDAAudioStream *st = opaque; 204d61a4ce8SGerd Hoffmann int sent = 0; 205d61a4ce8SGerd Hoffmann int len; 206d61a4ce8SGerd Hoffmann bool rc; 207d61a4ce8SGerd Hoffmann 208d61a4ce8SGerd Hoffmann while (avail - sent >= sizeof(st->buf)) { 209d61a4ce8SGerd Hoffmann if (st->bpos == sizeof(st->buf)) { 210d61a4ce8SGerd Hoffmann rc = hda_codec_xfer(&st->state->hda, st->stream, true, 211d61a4ce8SGerd Hoffmann st->buf, sizeof(st->buf)); 212d61a4ce8SGerd Hoffmann if (!rc) { 213d61a4ce8SGerd Hoffmann break; 214d61a4ce8SGerd Hoffmann } 215d61a4ce8SGerd Hoffmann st->bpos = 0; 216d61a4ce8SGerd Hoffmann } 217d61a4ce8SGerd Hoffmann len = AUD_write(st->voice.out, st->buf + st->bpos, 218d61a4ce8SGerd Hoffmann sizeof(st->buf) - st->bpos); 219d61a4ce8SGerd Hoffmann st->bpos += len; 220d61a4ce8SGerd Hoffmann sent += len; 221d61a4ce8SGerd Hoffmann if (st->bpos != sizeof(st->buf)) { 222d61a4ce8SGerd Hoffmann break; 223d61a4ce8SGerd Hoffmann } 224d61a4ce8SGerd Hoffmann } 225d61a4ce8SGerd Hoffmann } 226d61a4ce8SGerd Hoffmann 227d61a4ce8SGerd Hoffmann static void hda_audio_set_running(HDAAudioStream *st, bool running) 228d61a4ce8SGerd Hoffmann { 229d61a4ce8SGerd Hoffmann if (st->node == NULL) { 230d61a4ce8SGerd Hoffmann return; 231d61a4ce8SGerd Hoffmann } 232d61a4ce8SGerd Hoffmann if (st->running == running) { 233d61a4ce8SGerd Hoffmann return; 234d61a4ce8SGerd Hoffmann } 235d61a4ce8SGerd Hoffmann st->running = running; 236d61a4ce8SGerd Hoffmann dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, 237d61a4ce8SGerd Hoffmann st->running ? "on" : "off", st->stream); 238d61a4ce8SGerd Hoffmann if (st->output) { 239d61a4ce8SGerd Hoffmann AUD_set_active_out(st->voice.out, st->running); 240d61a4ce8SGerd Hoffmann } else { 241d61a4ce8SGerd Hoffmann AUD_set_active_in(st->voice.in, st->running); 242d61a4ce8SGerd Hoffmann } 243d61a4ce8SGerd Hoffmann } 244d61a4ce8SGerd Hoffmann 245d61a4ce8SGerd Hoffmann static void hda_audio_set_amp(HDAAudioStream *st) 246d61a4ce8SGerd Hoffmann { 247d61a4ce8SGerd Hoffmann bool muted; 248d61a4ce8SGerd Hoffmann uint32_t left, right; 249d61a4ce8SGerd Hoffmann 250d61a4ce8SGerd Hoffmann if (st->node == NULL) { 251d61a4ce8SGerd Hoffmann return; 252d61a4ce8SGerd Hoffmann } 253d61a4ce8SGerd Hoffmann 254d61a4ce8SGerd Hoffmann muted = st->mute_left && st->mute_right; 255d61a4ce8SGerd Hoffmann left = st->mute_left ? 0 : st->gain_left; 256d61a4ce8SGerd Hoffmann right = st->mute_right ? 0 : st->gain_right; 257d61a4ce8SGerd Hoffmann 258d61a4ce8SGerd Hoffmann left = left * 255 / QEMU_HDA_AMP_STEPS; 259d61a4ce8SGerd Hoffmann right = right * 255 / QEMU_HDA_AMP_STEPS; 260d61a4ce8SGerd Hoffmann 261d61a4ce8SGerd Hoffmann if (st->output) { 262d61a4ce8SGerd Hoffmann AUD_set_volume_out(st->voice.out, muted, left, right); 263d61a4ce8SGerd Hoffmann } else { 264d61a4ce8SGerd Hoffmann AUD_set_volume_in(st->voice.in, muted, left, right); 265d61a4ce8SGerd Hoffmann } 266d61a4ce8SGerd Hoffmann } 267d61a4ce8SGerd Hoffmann 268d61a4ce8SGerd Hoffmann static void hda_audio_setup(HDAAudioStream *st) 269d61a4ce8SGerd Hoffmann { 270d61a4ce8SGerd Hoffmann if (st->node == NULL) { 271d61a4ce8SGerd Hoffmann return; 272d61a4ce8SGerd Hoffmann } 273d61a4ce8SGerd Hoffmann 274d61a4ce8SGerd Hoffmann dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", 275d61a4ce8SGerd Hoffmann st->node->name, st->as.nchannels, 276d61a4ce8SGerd Hoffmann fmt2name[st->as.fmt], st->as.freq); 277d61a4ce8SGerd Hoffmann 278d61a4ce8SGerd Hoffmann if (st->output) { 279d61a4ce8SGerd Hoffmann st->voice.out = AUD_open_out(&st->state->card, st->voice.out, 280d61a4ce8SGerd Hoffmann st->node->name, st, 281d61a4ce8SGerd Hoffmann hda_audio_output_cb, &st->as); 282d61a4ce8SGerd Hoffmann } else { 283d61a4ce8SGerd Hoffmann st->voice.in = AUD_open_in(&st->state->card, st->voice.in, 284d61a4ce8SGerd Hoffmann st->node->name, st, 285d61a4ce8SGerd Hoffmann hda_audio_input_cb, &st->as); 286d61a4ce8SGerd Hoffmann } 287d61a4ce8SGerd Hoffmann } 288d61a4ce8SGerd Hoffmann 289d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) 290d61a4ce8SGerd Hoffmann { 291d61a4ce8SGerd Hoffmann HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); 292d61a4ce8SGerd Hoffmann HDAAudioStream *st; 293d61a4ce8SGerd Hoffmann const desc_node *node = NULL; 294d61a4ce8SGerd Hoffmann const desc_param *param; 295d61a4ce8SGerd Hoffmann uint32_t verb, payload, response, count, shift; 296d61a4ce8SGerd Hoffmann 297d61a4ce8SGerd Hoffmann if ((data & 0x70000) == 0x70000) { 298d61a4ce8SGerd Hoffmann /* 12/8 id/payload */ 299d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xfff; 300d61a4ce8SGerd Hoffmann payload = data & 0x00ff; 301d61a4ce8SGerd Hoffmann } else { 302d61a4ce8SGerd Hoffmann /* 4/16 id/payload */ 303d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xf00; 304d61a4ce8SGerd Hoffmann payload = data & 0xffff; 305d61a4ce8SGerd Hoffmann } 306d61a4ce8SGerd Hoffmann 307d61a4ce8SGerd Hoffmann node = hda_codec_find_node(a->desc, nid); 308d61a4ce8SGerd Hoffmann if (node == NULL) { 309d61a4ce8SGerd Hoffmann goto fail; 310d61a4ce8SGerd Hoffmann } 311d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n", 312d61a4ce8SGerd Hoffmann __FUNCTION__, nid, node->name, verb, payload); 313d61a4ce8SGerd Hoffmann 314d61a4ce8SGerd Hoffmann switch (verb) { 315d61a4ce8SGerd Hoffmann /* all nodes */ 316d61a4ce8SGerd Hoffmann case AC_VERB_PARAMETERS: 317d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, payload); 318d61a4ce8SGerd Hoffmann if (param == NULL) { 319d61a4ce8SGerd Hoffmann goto fail; 320d61a4ce8SGerd Hoffmann } 321d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, param->val); 322d61a4ce8SGerd Hoffmann break; 323d61a4ce8SGerd Hoffmann case AC_VERB_GET_SUBSYSTEM_ID: 324d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, a->desc->iid); 325d61a4ce8SGerd Hoffmann break; 326d61a4ce8SGerd Hoffmann 327d61a4ce8SGerd Hoffmann /* all functions */ 328d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONNECT_LIST: 329d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN); 330d61a4ce8SGerd Hoffmann count = param ? param->val : 0; 331d61a4ce8SGerd Hoffmann response = 0; 332d61a4ce8SGerd Hoffmann shift = 0; 333d61a4ce8SGerd Hoffmann while (payload < count && shift < 32) { 334d61a4ce8SGerd Hoffmann response |= node->conn[payload] << shift; 335d61a4ce8SGerd Hoffmann payload++; 336d61a4ce8SGerd Hoffmann shift += 8; 337d61a4ce8SGerd Hoffmann } 338d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 339d61a4ce8SGerd Hoffmann break; 340d61a4ce8SGerd Hoffmann 341d61a4ce8SGerd Hoffmann /* pin widget */ 342d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONFIG_DEFAULT: 343d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->config); 344d61a4ce8SGerd Hoffmann break; 345d61a4ce8SGerd Hoffmann case AC_VERB_GET_PIN_WIDGET_CONTROL: 346d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->pinctl); 347d61a4ce8SGerd Hoffmann break; 348d61a4ce8SGerd Hoffmann case AC_VERB_SET_PIN_WIDGET_CONTROL: 349d61a4ce8SGerd Hoffmann if (node->pinctl != payload) { 350d61a4ce8SGerd Hoffmann dprint(a, 1, "unhandled pin control bit\n"); 351d61a4ce8SGerd Hoffmann } 352d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 353d61a4ce8SGerd Hoffmann break; 354d61a4ce8SGerd Hoffmann 355d61a4ce8SGerd Hoffmann /* audio in/out widget */ 356d61a4ce8SGerd Hoffmann case AC_VERB_SET_CHANNEL_STREAMID: 357d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 358d61a4ce8SGerd Hoffmann if (st->node == NULL) { 359d61a4ce8SGerd Hoffmann goto fail; 360d61a4ce8SGerd Hoffmann } 361d61a4ce8SGerd Hoffmann hda_audio_set_running(st, false); 362d61a4ce8SGerd Hoffmann st->stream = (payload >> 4) & 0x0f; 363d61a4ce8SGerd Hoffmann st->channel = payload & 0x0f; 364d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: stream %d, channel %d\n", 365d61a4ce8SGerd Hoffmann st->node->name, st->stream, st->channel); 366ba43d289SMarc-André Lureau hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); 367d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 368d61a4ce8SGerd Hoffmann break; 369d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONV: 370d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 371d61a4ce8SGerd Hoffmann if (st->node == NULL) { 372d61a4ce8SGerd Hoffmann goto fail; 373d61a4ce8SGerd Hoffmann } 374d61a4ce8SGerd Hoffmann response = st->stream << 4 | st->channel; 375d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 376d61a4ce8SGerd Hoffmann break; 377d61a4ce8SGerd Hoffmann case AC_VERB_SET_STREAM_FORMAT: 378d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 379d61a4ce8SGerd Hoffmann if (st->node == NULL) { 380d61a4ce8SGerd Hoffmann goto fail; 381d61a4ce8SGerd Hoffmann } 382d61a4ce8SGerd Hoffmann st->format = payload; 383d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 384d61a4ce8SGerd Hoffmann hda_audio_setup(st); 385d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 386d61a4ce8SGerd Hoffmann break; 387d61a4ce8SGerd Hoffmann case AC_VERB_GET_STREAM_FORMAT: 388d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 389d61a4ce8SGerd Hoffmann if (st->node == NULL) { 390d61a4ce8SGerd Hoffmann goto fail; 391d61a4ce8SGerd Hoffmann } 392d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, st->format); 393d61a4ce8SGerd Hoffmann break; 394d61a4ce8SGerd Hoffmann case AC_VERB_GET_AMP_GAIN_MUTE: 395d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 396d61a4ce8SGerd Hoffmann if (st->node == NULL) { 397d61a4ce8SGerd Hoffmann goto fail; 398d61a4ce8SGerd Hoffmann } 399d61a4ce8SGerd Hoffmann if (payload & AC_AMP_GET_LEFT) { 400d61a4ce8SGerd Hoffmann response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0); 401d61a4ce8SGerd Hoffmann } else { 402d61a4ce8SGerd Hoffmann response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0); 403d61a4ce8SGerd Hoffmann } 404d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 405d61a4ce8SGerd Hoffmann break; 406d61a4ce8SGerd Hoffmann case AC_VERB_SET_AMP_GAIN_MUTE: 407d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 408d61a4ce8SGerd Hoffmann if (st->node == NULL) { 409d61a4ce8SGerd Hoffmann goto fail; 410d61a4ce8SGerd Hoffmann } 411d61a4ce8SGerd Hoffmann dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n", 412d61a4ce8SGerd Hoffmann st->node->name, 413d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_OUTPUT) ? "o" : "-", 414d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INPUT) ? "i" : "-", 415d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_LEFT) ? "l" : "-", 416d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_RIGHT) ? "r" : "-", 417d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT, 418d61a4ce8SGerd Hoffmann (payload & AC_AMP_GAIN), 419d61a4ce8SGerd Hoffmann (payload & AC_AMP_MUTE) ? "muted" : ""); 420d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_LEFT) { 421d61a4ce8SGerd Hoffmann st->gain_left = payload & AC_AMP_GAIN; 422d61a4ce8SGerd Hoffmann st->mute_left = payload & AC_AMP_MUTE; 423d61a4ce8SGerd Hoffmann } 424d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_RIGHT) { 425d61a4ce8SGerd Hoffmann st->gain_right = payload & AC_AMP_GAIN; 426d61a4ce8SGerd Hoffmann st->mute_right = payload & AC_AMP_MUTE; 427d61a4ce8SGerd Hoffmann } 428d61a4ce8SGerd Hoffmann hda_audio_set_amp(st); 429d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 430d61a4ce8SGerd Hoffmann break; 431d61a4ce8SGerd Hoffmann 432d61a4ce8SGerd Hoffmann /* not supported */ 433d61a4ce8SGerd Hoffmann case AC_VERB_SET_POWER_STATE: 434d61a4ce8SGerd Hoffmann case AC_VERB_GET_POWER_STATE: 435d61a4ce8SGerd Hoffmann case AC_VERB_GET_SDI_SELECT: 436d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 437d61a4ce8SGerd Hoffmann break; 438d61a4ce8SGerd Hoffmann default: 439d61a4ce8SGerd Hoffmann goto fail; 440d61a4ce8SGerd Hoffmann } 441d61a4ce8SGerd Hoffmann return; 442d61a4ce8SGerd Hoffmann 443d61a4ce8SGerd Hoffmann fail: 444d61a4ce8SGerd Hoffmann dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n", 445d61a4ce8SGerd Hoffmann __FUNCTION__, nid, node ? node->name : "?", verb, payload); 446d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 447d61a4ce8SGerd Hoffmann } 448d61a4ce8SGerd Hoffmann 449ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output) 450d61a4ce8SGerd Hoffmann { 451d61a4ce8SGerd Hoffmann HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); 452d61a4ce8SGerd Hoffmann int s; 453d61a4ce8SGerd Hoffmann 454ba43d289SMarc-André Lureau a->running_compat[stnr] = running; 455ba43d289SMarc-André Lureau a->running_real[output * 16 + stnr] = running; 456d61a4ce8SGerd Hoffmann for (s = 0; s < ARRAY_SIZE(a->st); s++) { 457d61a4ce8SGerd Hoffmann if (a->st[s].node == NULL) { 458d61a4ce8SGerd Hoffmann continue; 459d61a4ce8SGerd Hoffmann } 460ba43d289SMarc-André Lureau if (a->st[s].output != output) { 461ba43d289SMarc-André Lureau continue; 462ba43d289SMarc-André Lureau } 463d61a4ce8SGerd Hoffmann if (a->st[s].stream != stnr) { 464d61a4ce8SGerd Hoffmann continue; 465d61a4ce8SGerd Hoffmann } 466d61a4ce8SGerd Hoffmann hda_audio_set_running(&a->st[s], running); 467d61a4ce8SGerd Hoffmann } 468d61a4ce8SGerd Hoffmann } 469d61a4ce8SGerd Hoffmann 470d61a4ce8SGerd Hoffmann static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) 471d61a4ce8SGerd Hoffmann { 472d61a4ce8SGerd Hoffmann HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); 473d61a4ce8SGerd Hoffmann HDAAudioStream *st; 474d61a4ce8SGerd Hoffmann const desc_node *node; 475d61a4ce8SGerd Hoffmann const desc_param *param; 476d61a4ce8SGerd Hoffmann uint32_t i, type; 477d61a4ce8SGerd Hoffmann 478d61a4ce8SGerd Hoffmann a->desc = desc; 479f79f2bfcSAnthony Liguori a->name = object_get_typename(OBJECT(a)); 480d61a4ce8SGerd Hoffmann dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad); 481d61a4ce8SGerd Hoffmann 482d61a4ce8SGerd Hoffmann AUD_register_card("hda", &a->card); 483d61a4ce8SGerd Hoffmann for (i = 0; i < a->desc->nnodes; i++) { 484d61a4ce8SGerd Hoffmann node = a->desc->nodes + i; 485d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP); 486d61a4ce8SGerd Hoffmann if (NULL == param) 487d61a4ce8SGerd Hoffmann continue; 488d61a4ce8SGerd Hoffmann type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; 489d61a4ce8SGerd Hoffmann switch (type) { 490d61a4ce8SGerd Hoffmann case AC_WID_AUD_OUT: 491d61a4ce8SGerd Hoffmann case AC_WID_AUD_IN: 492d61a4ce8SGerd Hoffmann assert(node->stindex < ARRAY_SIZE(a->st)); 493d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 494d61a4ce8SGerd Hoffmann st->state = a; 495d61a4ce8SGerd Hoffmann st->node = node; 496d61a4ce8SGerd Hoffmann if (type == AC_WID_AUD_OUT) { 497d61a4ce8SGerd Hoffmann /* unmute output by default */ 498d61a4ce8SGerd Hoffmann st->gain_left = QEMU_HDA_AMP_STEPS; 499d61a4ce8SGerd Hoffmann st->gain_right = QEMU_HDA_AMP_STEPS; 500d61a4ce8SGerd Hoffmann st->bpos = sizeof(st->buf); 501d61a4ce8SGerd Hoffmann st->output = true; 502d61a4ce8SGerd Hoffmann } else { 503d61a4ce8SGerd Hoffmann st->output = false; 504d61a4ce8SGerd Hoffmann } 505d61a4ce8SGerd Hoffmann st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 | 506d61a4ce8SGerd Hoffmann (1 << AC_FMT_CHAN_SHIFT); 507d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 508d61a4ce8SGerd Hoffmann hda_audio_setup(st); 509d61a4ce8SGerd Hoffmann break; 510d61a4ce8SGerd Hoffmann } 511d61a4ce8SGerd Hoffmann } 512d61a4ce8SGerd Hoffmann return 0; 513d61a4ce8SGerd Hoffmann } 514d61a4ce8SGerd Hoffmann 515129dcd2cSGerd Hoffmann static int hda_audio_exit(HDACodecDevice *hda) 516129dcd2cSGerd Hoffmann { 517129dcd2cSGerd Hoffmann HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); 518129dcd2cSGerd Hoffmann HDAAudioStream *st; 519129dcd2cSGerd Hoffmann int i; 520129dcd2cSGerd Hoffmann 521129dcd2cSGerd Hoffmann dprint(a, 1, "%s\n", __FUNCTION__); 522129dcd2cSGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 523129dcd2cSGerd Hoffmann st = a->st + i; 524129dcd2cSGerd Hoffmann if (st->node == NULL) { 525129dcd2cSGerd Hoffmann continue; 526129dcd2cSGerd Hoffmann } 527129dcd2cSGerd Hoffmann if (st->output) { 528129dcd2cSGerd Hoffmann AUD_close_out(&a->card, st->voice.out); 529129dcd2cSGerd Hoffmann } else { 530129dcd2cSGerd Hoffmann AUD_close_in(&a->card, st->voice.in); 531129dcd2cSGerd Hoffmann } 532129dcd2cSGerd Hoffmann } 533129dcd2cSGerd Hoffmann AUD_remove_card(&a->card); 534129dcd2cSGerd Hoffmann return 0; 535129dcd2cSGerd Hoffmann } 536129dcd2cSGerd Hoffmann 537d61a4ce8SGerd Hoffmann static int hda_audio_post_load(void *opaque, int version) 538d61a4ce8SGerd Hoffmann { 539d61a4ce8SGerd Hoffmann HDAAudioState *a = opaque; 540d61a4ce8SGerd Hoffmann HDAAudioStream *st; 541d61a4ce8SGerd Hoffmann int i; 542d61a4ce8SGerd Hoffmann 543d61a4ce8SGerd Hoffmann dprint(a, 1, "%s\n", __FUNCTION__); 544ba43d289SMarc-André Lureau if (version == 1) { 545ba43d289SMarc-André Lureau /* assume running_compat[] is for output streams */ 546ba43d289SMarc-André Lureau for (i = 0; i < ARRAY_SIZE(a->running_compat); i++) 547ba43d289SMarc-André Lureau a->running_real[16 + i] = a->running_compat[i]; 548ba43d289SMarc-André Lureau } 549ba43d289SMarc-André Lureau 550d61a4ce8SGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 551d61a4ce8SGerd Hoffmann st = a->st + i; 552d61a4ce8SGerd Hoffmann if (st->node == NULL) 553d61a4ce8SGerd Hoffmann continue; 554d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 555d61a4ce8SGerd Hoffmann hda_audio_setup(st); 556d61a4ce8SGerd Hoffmann hda_audio_set_amp(st); 557ba43d289SMarc-André Lureau hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); 558d61a4ce8SGerd Hoffmann } 559d61a4ce8SGerd Hoffmann return 0; 560d61a4ce8SGerd Hoffmann } 561d61a4ce8SGerd Hoffmann 562*39e6a38cSGerd Hoffmann static void hda_audio_reset(DeviceState *dev) 563*39e6a38cSGerd Hoffmann { 564*39e6a38cSGerd Hoffmann HDAAudioState *a = DO_UPCAST(HDAAudioState, hda.qdev, dev); 565*39e6a38cSGerd Hoffmann HDAAudioStream *st; 566*39e6a38cSGerd Hoffmann int i; 567*39e6a38cSGerd Hoffmann 568*39e6a38cSGerd Hoffmann dprint(a, 1, "%s\n", __func__); 569*39e6a38cSGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 570*39e6a38cSGerd Hoffmann st = a->st + i; 571*39e6a38cSGerd Hoffmann if (st->node != NULL) { 572*39e6a38cSGerd Hoffmann hda_audio_set_running(st, false); 573*39e6a38cSGerd Hoffmann } 574*39e6a38cSGerd Hoffmann } 575*39e6a38cSGerd Hoffmann } 576*39e6a38cSGerd Hoffmann 577d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = { 578d61a4ce8SGerd Hoffmann .name = "hda-audio-stream", 579d61a4ce8SGerd Hoffmann .version_id = 1, 580d61a4ce8SGerd Hoffmann .fields = (VMStateField []) { 581d61a4ce8SGerd Hoffmann VMSTATE_UINT32(stream, HDAAudioStream), 582d61a4ce8SGerd Hoffmann VMSTATE_UINT32(channel, HDAAudioStream), 583d61a4ce8SGerd Hoffmann VMSTATE_UINT32(format, HDAAudioStream), 584d61a4ce8SGerd Hoffmann VMSTATE_UINT32(gain_left, HDAAudioStream), 585d61a4ce8SGerd Hoffmann VMSTATE_UINT32(gain_right, HDAAudioStream), 586d61a4ce8SGerd Hoffmann VMSTATE_BOOL(mute_left, HDAAudioStream), 587d61a4ce8SGerd Hoffmann VMSTATE_BOOL(mute_right, HDAAudioStream), 588d61a4ce8SGerd Hoffmann VMSTATE_UINT32(bpos, HDAAudioStream), 589d61a4ce8SGerd Hoffmann VMSTATE_BUFFER(buf, HDAAudioStream), 590d61a4ce8SGerd Hoffmann VMSTATE_END_OF_LIST() 591d61a4ce8SGerd Hoffmann } 592d61a4ce8SGerd Hoffmann }; 593d61a4ce8SGerd Hoffmann 594d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio = { 595d61a4ce8SGerd Hoffmann .name = "hda-audio", 596ba43d289SMarc-André Lureau .version_id = 2, 597d61a4ce8SGerd Hoffmann .post_load = hda_audio_post_load, 598d61a4ce8SGerd Hoffmann .fields = (VMStateField []) { 599d61a4ce8SGerd Hoffmann VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, 600d61a4ce8SGerd Hoffmann vmstate_hda_audio_stream, 601d61a4ce8SGerd Hoffmann HDAAudioStream), 602ba43d289SMarc-André Lureau VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16), 603ba43d289SMarc-André Lureau VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2), 604d61a4ce8SGerd Hoffmann VMSTATE_END_OF_LIST() 605d61a4ce8SGerd Hoffmann } 606d61a4ce8SGerd Hoffmann }; 607d61a4ce8SGerd Hoffmann 608d61a4ce8SGerd Hoffmann static Property hda_audio_properties[] = { 609d61a4ce8SGerd Hoffmann DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), 6102690e61eSBandan Das DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), 611d61a4ce8SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 612d61a4ce8SGerd Hoffmann }; 613d61a4ce8SGerd Hoffmann 614d61a4ce8SGerd Hoffmann static int hda_audio_init_output(HDACodecDevice *hda) 615d61a4ce8SGerd Hoffmann { 6162690e61eSBandan Das HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); 6172690e61eSBandan Das 6182690e61eSBandan Das if (!a->mixer) { 6192690e61eSBandan Das return hda_audio_init(hda, &output_nomixemu); 6202690e61eSBandan Das } else { 6212690e61eSBandan Das return hda_audio_init(hda, &output_mixemu); 6222690e61eSBandan Das } 623d61a4ce8SGerd Hoffmann } 624d61a4ce8SGerd Hoffmann 625d61a4ce8SGerd Hoffmann static int hda_audio_init_duplex(HDACodecDevice *hda) 626d61a4ce8SGerd Hoffmann { 6272690e61eSBandan Das HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); 6282690e61eSBandan Das 6292690e61eSBandan Das if (!a->mixer) { 6302690e61eSBandan Das return hda_audio_init(hda, &duplex_nomixemu); 6312690e61eSBandan Das } else { 6322690e61eSBandan Das return hda_audio_init(hda, &duplex_mixemu); 6332690e61eSBandan Das } 634d61a4ce8SGerd Hoffmann } 635d61a4ce8SGerd Hoffmann 63620110065SGerd Hoffmann static int hda_audio_init_micro(HDACodecDevice *hda) 63720110065SGerd Hoffmann { 6382690e61eSBandan Das HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); 6392690e61eSBandan Das 6402690e61eSBandan Das if (!a->mixer) { 6412690e61eSBandan Das return hda_audio_init(hda, µ_nomixemu); 6422690e61eSBandan Das } else { 6432690e61eSBandan Das return hda_audio_init(hda, µ_mixemu); 6442690e61eSBandan Das } 64520110065SGerd Hoffmann } 64620110065SGerd Hoffmann 647dbaa7904SAnthony Liguori static void hda_audio_output_class_init(ObjectClass *klass, void *data) 648dbaa7904SAnthony Liguori { 64939bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 650dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 651dbaa7904SAnthony Liguori 652dbaa7904SAnthony Liguori k->init = hda_audio_init_output; 653dbaa7904SAnthony Liguori k->exit = hda_audio_exit; 654dbaa7904SAnthony Liguori k->command = hda_audio_command; 655dbaa7904SAnthony Liguori k->stream = hda_audio_stream; 656125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 65720110065SGerd Hoffmann dc->desc = "HDA Audio Codec, output-only (line-out)"; 658*39e6a38cSGerd Hoffmann dc->reset = hda_audio_reset; 65939bffca2SAnthony Liguori dc->vmsd = &vmstate_hda_audio; 66039bffca2SAnthony Liguori dc->props = hda_audio_properties; 661dbaa7904SAnthony Liguori } 662dbaa7904SAnthony Liguori 6638c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = { 664dbaa7904SAnthony Liguori .name = "hda-output", 66539bffca2SAnthony Liguori .parent = TYPE_HDA_CODEC_DEVICE, 66639bffca2SAnthony Liguori .instance_size = sizeof(HDAAudioState), 667dbaa7904SAnthony Liguori .class_init = hda_audio_output_class_init, 668d61a4ce8SGerd Hoffmann }; 669d61a4ce8SGerd Hoffmann 670dbaa7904SAnthony Liguori static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) 671dbaa7904SAnthony Liguori { 67239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 673dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 674dbaa7904SAnthony Liguori 675dbaa7904SAnthony Liguori k->init = hda_audio_init_duplex; 676dbaa7904SAnthony Liguori k->exit = hda_audio_exit; 677dbaa7904SAnthony Liguori k->command = hda_audio_command; 678dbaa7904SAnthony Liguori k->stream = hda_audio_stream; 679125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 68020110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (line-out, line-in)"; 681*39e6a38cSGerd Hoffmann dc->reset = hda_audio_reset; 68239bffca2SAnthony Liguori dc->vmsd = &vmstate_hda_audio; 68339bffca2SAnthony Liguori dc->props = hda_audio_properties; 684dbaa7904SAnthony Liguori } 685dbaa7904SAnthony Liguori 6868c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = { 687dbaa7904SAnthony Liguori .name = "hda-duplex", 68839bffca2SAnthony Liguori .parent = TYPE_HDA_CODEC_DEVICE, 68939bffca2SAnthony Liguori .instance_size = sizeof(HDAAudioState), 690dbaa7904SAnthony Liguori .class_init = hda_audio_duplex_class_init, 691d61a4ce8SGerd Hoffmann }; 692d61a4ce8SGerd Hoffmann 69320110065SGerd Hoffmann static void hda_audio_micro_class_init(ObjectClass *klass, void *data) 69420110065SGerd Hoffmann { 69520110065SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 69620110065SGerd Hoffmann HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 69720110065SGerd Hoffmann 69820110065SGerd Hoffmann k->init = hda_audio_init_micro; 69920110065SGerd Hoffmann k->exit = hda_audio_exit; 70020110065SGerd Hoffmann k->command = hda_audio_command; 70120110065SGerd Hoffmann k->stream = hda_audio_stream; 702125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 70320110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (speaker, microphone)"; 704*39e6a38cSGerd Hoffmann dc->reset = hda_audio_reset; 70520110065SGerd Hoffmann dc->vmsd = &vmstate_hda_audio; 70620110065SGerd Hoffmann dc->props = hda_audio_properties; 70720110065SGerd Hoffmann } 70820110065SGerd Hoffmann 7098c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = { 71020110065SGerd Hoffmann .name = "hda-micro", 71120110065SGerd Hoffmann .parent = TYPE_HDA_CODEC_DEVICE, 71220110065SGerd Hoffmann .instance_size = sizeof(HDAAudioState), 71320110065SGerd Hoffmann .class_init = hda_audio_micro_class_init, 71420110065SGerd Hoffmann }; 71520110065SGerd Hoffmann 71683f7d43aSAndreas Färber static void hda_audio_register_types(void) 717d61a4ce8SGerd Hoffmann { 71839bffca2SAnthony Liguori type_register_static(&hda_audio_output_info); 71939bffca2SAnthony Liguori type_register_static(&hda_audio_duplex_info); 72020110065SGerd Hoffmann type_register_static(&hda_audio_micro_info); 721d61a4ce8SGerd Hoffmann } 72283f7d43aSAndreas Färber 72383f7d43aSAndreas Färber type_init(hda_audio_register_types) 724