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, µ_nomixemu); 6502690e61eSBandan Das } else { 6512690e61eSBandan Das return hda_audio_init(hda, µ_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