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