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 206086a565SPeter Maydell #include "qemu/osdep.h" 21*280c1e1cSGerd Hoffmann #include "qemu/atomic.h" 2283c9f4caSPaolo Bonzini #include "hw/hw.h" 2383c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 2447b43a1fSPaolo Bonzini #include "intel-hda.h" 2547b43a1fSPaolo Bonzini #include "intel-hda-defs.h" 26d61a4ce8SGerd Hoffmann #include "audio/audio.h" 27d61a4ce8SGerd Hoffmann 28d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */ 29d61a4ce8SGerd Hoffmann 30d61a4ce8SGerd Hoffmann typedef struct desc_param { 31d61a4ce8SGerd Hoffmann uint32_t id; 32d61a4ce8SGerd Hoffmann uint32_t val; 33d61a4ce8SGerd Hoffmann } desc_param; 34d61a4ce8SGerd Hoffmann 35d61a4ce8SGerd Hoffmann typedef struct desc_node { 36d61a4ce8SGerd Hoffmann uint32_t nid; 37d61a4ce8SGerd Hoffmann const char *name; 38d61a4ce8SGerd Hoffmann const desc_param *params; 39d61a4ce8SGerd Hoffmann uint32_t nparams; 40d61a4ce8SGerd Hoffmann uint32_t config; 41d61a4ce8SGerd Hoffmann uint32_t pinctl; 42d61a4ce8SGerd Hoffmann uint32_t *conn; 43d61a4ce8SGerd Hoffmann uint32_t stindex; 44d61a4ce8SGerd Hoffmann } desc_node; 45d61a4ce8SGerd Hoffmann 46d61a4ce8SGerd Hoffmann typedef struct desc_codec { 47d61a4ce8SGerd Hoffmann const char *name; 48d61a4ce8SGerd Hoffmann uint32_t iid; 49d61a4ce8SGerd Hoffmann const desc_node *nodes; 50d61a4ce8SGerd Hoffmann uint32_t nnodes; 51d61a4ce8SGerd Hoffmann } desc_codec; 52d61a4ce8SGerd Hoffmann 53d61a4ce8SGerd Hoffmann static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id) 54d61a4ce8SGerd Hoffmann { 55d61a4ce8SGerd Hoffmann int i; 56d61a4ce8SGerd Hoffmann 57d61a4ce8SGerd Hoffmann for (i = 0; i < node->nparams; i++) { 58d61a4ce8SGerd Hoffmann if (node->params[i].id == id) { 59d61a4ce8SGerd Hoffmann return &node->params[i]; 60d61a4ce8SGerd Hoffmann } 61d61a4ce8SGerd Hoffmann } 62d61a4ce8SGerd Hoffmann return NULL; 63d61a4ce8SGerd Hoffmann } 64d61a4ce8SGerd Hoffmann 65d61a4ce8SGerd Hoffmann static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid) 66d61a4ce8SGerd Hoffmann { 67d61a4ce8SGerd Hoffmann int i; 68d61a4ce8SGerd Hoffmann 69d61a4ce8SGerd Hoffmann for (i = 0; i < codec->nnodes; i++) { 70d61a4ce8SGerd Hoffmann if (codec->nodes[i].nid == nid) { 71d61a4ce8SGerd Hoffmann return &codec->nodes[i]; 72d61a4ce8SGerd Hoffmann } 73d61a4ce8SGerd Hoffmann } 74d61a4ce8SGerd Hoffmann return NULL; 75d61a4ce8SGerd Hoffmann } 76d61a4ce8SGerd Hoffmann 77d61a4ce8SGerd Hoffmann static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) 78d61a4ce8SGerd Hoffmann { 79d61a4ce8SGerd Hoffmann if (format & AC_FMT_TYPE_NON_PCM) { 80d61a4ce8SGerd Hoffmann return; 81d61a4ce8SGerd Hoffmann } 82d61a4ce8SGerd Hoffmann 83d61a4ce8SGerd Hoffmann as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000; 84d61a4ce8SGerd Hoffmann 85d61a4ce8SGerd Hoffmann switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) { 86d61a4ce8SGerd Hoffmann case 1: as->freq *= 2; break; 87d61a4ce8SGerd Hoffmann case 2: as->freq *= 3; break; 88d61a4ce8SGerd Hoffmann case 3: as->freq *= 4; break; 89d61a4ce8SGerd Hoffmann } 90d61a4ce8SGerd Hoffmann 91d61a4ce8SGerd Hoffmann switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) { 92d61a4ce8SGerd Hoffmann case 1: as->freq /= 2; break; 93d61a4ce8SGerd Hoffmann case 2: as->freq /= 3; break; 94d61a4ce8SGerd Hoffmann case 3: as->freq /= 4; break; 95d61a4ce8SGerd Hoffmann case 4: as->freq /= 5; break; 96d61a4ce8SGerd Hoffmann case 5: as->freq /= 6; break; 97d61a4ce8SGerd Hoffmann case 6: as->freq /= 7; break; 98d61a4ce8SGerd Hoffmann case 7: as->freq /= 8; break; 99d61a4ce8SGerd Hoffmann } 100d61a4ce8SGerd Hoffmann 101d61a4ce8SGerd Hoffmann switch (format & AC_FMT_BITS_MASK) { 102d61a4ce8SGerd Hoffmann case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break; 103d61a4ce8SGerd Hoffmann case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break; 104d61a4ce8SGerd Hoffmann case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break; 105d61a4ce8SGerd Hoffmann } 106d61a4ce8SGerd Hoffmann 107d61a4ce8SGerd Hoffmann as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1; 108d61a4ce8SGerd Hoffmann } 109d61a4ce8SGerd Hoffmann 110d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */ 111d61a4ce8SGerd Hoffmann /* 112d61a4ce8SGerd Hoffmann * HDA codec descriptions 113d61a4ce8SGerd Hoffmann */ 114d61a4ce8SGerd Hoffmann 115d61a4ce8SGerd Hoffmann /* some defines */ 116d61a4ce8SGerd Hoffmann 117d61a4ce8SGerd Hoffmann #define QEMU_HDA_ID_VENDOR 0x1af4 118d61a4ce8SGerd Hoffmann #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \ 119d61a4ce8SGerd Hoffmann 0x1fc /* 16 -> 96 kHz */) 120d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_NONE (0) 121d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_STEPS 0x4a 122d61a4ce8SGerd Hoffmann 1232690e61eSBandan Das #define PARAM mixemu 1242690e61eSBandan Das #define HDA_MIXER 1257953793cSBandan Das #include "hda-codec-common.h" 1262690e61eSBandan Das 1272690e61eSBandan Das #define PARAM nomixemu 1282690e61eSBandan Das #include "hda-codec-common.h" 1292690e61eSBandan Das 130*280c1e1cSGerd Hoffmann #define HDA_TIMER_TICKS (SCALE_MS) 131*280c1e1cSGerd Hoffmann #define MAX_CORR (SCALE_US * 100) 132*280c1e1cSGerd Hoffmann #define B_SIZE sizeof(st->buf) 133*280c1e1cSGerd Hoffmann #define B_MASK (sizeof(st->buf) - 1) 134*280c1e1cSGerd Hoffmann 135d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */ 136d61a4ce8SGerd Hoffmann 137d61a4ce8SGerd Hoffmann static const char *fmt2name[] = { 138d61a4ce8SGerd Hoffmann [ AUD_FMT_U8 ] = "PCM-U8", 139d61a4ce8SGerd Hoffmann [ AUD_FMT_S8 ] = "PCM-S8", 140d61a4ce8SGerd Hoffmann [ AUD_FMT_U16 ] = "PCM-U16", 141d61a4ce8SGerd Hoffmann [ AUD_FMT_S16 ] = "PCM-S16", 142d61a4ce8SGerd Hoffmann [ AUD_FMT_U32 ] = "PCM-U32", 143d61a4ce8SGerd Hoffmann [ AUD_FMT_S32 ] = "PCM-S32", 144d61a4ce8SGerd Hoffmann }; 145d61a4ce8SGerd Hoffmann 146d61a4ce8SGerd Hoffmann typedef struct HDAAudioState HDAAudioState; 147d61a4ce8SGerd Hoffmann typedef struct HDAAudioStream HDAAudioStream; 148d61a4ce8SGerd Hoffmann 149d61a4ce8SGerd Hoffmann struct HDAAudioStream { 150d61a4ce8SGerd Hoffmann HDAAudioState *state; 151d61a4ce8SGerd Hoffmann const desc_node *node; 152d61a4ce8SGerd Hoffmann bool output, running; 153d61a4ce8SGerd Hoffmann uint32_t stream; 154d61a4ce8SGerd Hoffmann uint32_t channel; 155d61a4ce8SGerd Hoffmann uint32_t format; 156d61a4ce8SGerd Hoffmann uint32_t gain_left, gain_right; 157d61a4ce8SGerd Hoffmann bool mute_left, mute_right; 158d61a4ce8SGerd Hoffmann struct audsettings as; 159d61a4ce8SGerd Hoffmann union { 160d61a4ce8SGerd Hoffmann SWVoiceIn *in; 161d61a4ce8SGerd Hoffmann SWVoiceOut *out; 162d61a4ce8SGerd Hoffmann } voice; 163*280c1e1cSGerd Hoffmann uint8_t compat_buf[HDA_BUFFER_SIZE]; 164*280c1e1cSGerd Hoffmann uint32_t compat_bpos; 165*280c1e1cSGerd Hoffmann uint8_t buf[8192]; /* size must be power of two */ 166*280c1e1cSGerd Hoffmann int64_t rpos; 167*280c1e1cSGerd Hoffmann int64_t wpos; 168*280c1e1cSGerd Hoffmann QEMUTimer *buft; 169*280c1e1cSGerd Hoffmann int64_t buft_start; 170d61a4ce8SGerd Hoffmann }; 171d61a4ce8SGerd Hoffmann 172cd6c8830SGerd Hoffmann #define TYPE_HDA_AUDIO "hda-audio" 173cd6c8830SGerd Hoffmann #define HDA_AUDIO(obj) OBJECT_CHECK(HDAAudioState, (obj), TYPE_HDA_AUDIO) 174cd6c8830SGerd Hoffmann 175d61a4ce8SGerd Hoffmann struct HDAAudioState { 176d61a4ce8SGerd Hoffmann HDACodecDevice hda; 177d61a4ce8SGerd Hoffmann const char *name; 178d61a4ce8SGerd Hoffmann 179d61a4ce8SGerd Hoffmann QEMUSoundCard card; 180d61a4ce8SGerd Hoffmann const desc_codec *desc; 181d61a4ce8SGerd Hoffmann HDAAudioStream st[4]; 182ba43d289SMarc-André Lureau bool running_compat[16]; 183ba43d289SMarc-André Lureau bool running_real[2 * 16]; 184d61a4ce8SGerd Hoffmann 185d61a4ce8SGerd Hoffmann /* properties */ 186d61a4ce8SGerd Hoffmann uint32_t debug; 1872690e61eSBandan Das bool mixer; 188*280c1e1cSGerd Hoffmann bool use_timer; 189d61a4ce8SGerd Hoffmann }; 190d61a4ce8SGerd Hoffmann 191*280c1e1cSGerd Hoffmann static inline int64_t hda_bytes_per_second(HDAAudioStream *st) 192*280c1e1cSGerd Hoffmann { 193*280c1e1cSGerd Hoffmann return 2 * st->as.nchannels * st->as.freq; 194*280c1e1cSGerd Hoffmann } 195*280c1e1cSGerd Hoffmann 196*280c1e1cSGerd Hoffmann static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) 197*280c1e1cSGerd Hoffmann { 198*280c1e1cSGerd Hoffmann int64_t corr = 199*280c1e1cSGerd Hoffmann NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st); 200*280c1e1cSGerd Hoffmann if (corr > MAX_CORR) { 201*280c1e1cSGerd Hoffmann corr = MAX_CORR; 202*280c1e1cSGerd Hoffmann } else if (corr < -MAX_CORR) { 203*280c1e1cSGerd Hoffmann corr = -MAX_CORR; 204*280c1e1cSGerd Hoffmann } 205*280c1e1cSGerd Hoffmann atomic_fetch_add(&st->buft_start, corr); 206*280c1e1cSGerd Hoffmann } 207*280c1e1cSGerd Hoffmann 208*280c1e1cSGerd Hoffmann static void hda_audio_input_timer(void *opaque) 209*280c1e1cSGerd Hoffmann { 210*280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 211*280c1e1cSGerd Hoffmann 212*280c1e1cSGerd Hoffmann int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 213*280c1e1cSGerd Hoffmann 214*280c1e1cSGerd Hoffmann int64_t buft_start = atomic_fetch_add(&st->buft_start, 0); 215*280c1e1cSGerd Hoffmann int64_t wpos = atomic_fetch_add(&st->wpos, 0); 216*280c1e1cSGerd Hoffmann int64_t rpos = atomic_fetch_add(&st->rpos, 0); 217*280c1e1cSGerd Hoffmann 218*280c1e1cSGerd Hoffmann int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start) 219*280c1e1cSGerd Hoffmann / NANOSECONDS_PER_SECOND; 220*280c1e1cSGerd Hoffmann wanted_rpos &= -4; /* IMPORTANT! clip to frames */ 221*280c1e1cSGerd Hoffmann 222*280c1e1cSGerd Hoffmann if (wanted_rpos <= rpos) { 223*280c1e1cSGerd Hoffmann /* we already transmitted the data */ 224*280c1e1cSGerd Hoffmann goto out_timer; 225*280c1e1cSGerd Hoffmann } 226*280c1e1cSGerd Hoffmann 227*280c1e1cSGerd Hoffmann int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos); 228*280c1e1cSGerd Hoffmann while (to_transfer) { 229*280c1e1cSGerd Hoffmann uint32_t start = (rpos & B_MASK); 230*280c1e1cSGerd Hoffmann uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); 231*280c1e1cSGerd Hoffmann int rc = hda_codec_xfer( 232*280c1e1cSGerd Hoffmann &st->state->hda, st->stream, false, st->buf + start, chunk); 233*280c1e1cSGerd Hoffmann if (!rc) { 234*280c1e1cSGerd Hoffmann break; 235*280c1e1cSGerd Hoffmann } 236*280c1e1cSGerd Hoffmann rpos += chunk; 237*280c1e1cSGerd Hoffmann to_transfer -= chunk; 238*280c1e1cSGerd Hoffmann atomic_fetch_add(&st->rpos, chunk); 239*280c1e1cSGerd Hoffmann } 240*280c1e1cSGerd Hoffmann 241*280c1e1cSGerd Hoffmann out_timer: 242*280c1e1cSGerd Hoffmann 243*280c1e1cSGerd Hoffmann if (st->running) { 244*280c1e1cSGerd Hoffmann timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); 245*280c1e1cSGerd Hoffmann } 246*280c1e1cSGerd Hoffmann } 247*280c1e1cSGerd Hoffmann 248d61a4ce8SGerd Hoffmann static void hda_audio_input_cb(void *opaque, int avail) 249d61a4ce8SGerd Hoffmann { 250d61a4ce8SGerd Hoffmann HDAAudioStream *st = opaque; 251d61a4ce8SGerd Hoffmann 252*280c1e1cSGerd Hoffmann int64_t wpos = atomic_fetch_add(&st->wpos, 0); 253*280c1e1cSGerd Hoffmann int64_t rpos = atomic_fetch_add(&st->rpos, 0); 254*280c1e1cSGerd Hoffmann 255*280c1e1cSGerd Hoffmann int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail); 256*280c1e1cSGerd Hoffmann 257*280c1e1cSGerd Hoffmann hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1))); 258*280c1e1cSGerd Hoffmann 259*280c1e1cSGerd Hoffmann while (to_transfer) { 260*280c1e1cSGerd Hoffmann uint32_t start = (uint32_t) (wpos & B_MASK); 261*280c1e1cSGerd Hoffmann uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); 262*280c1e1cSGerd Hoffmann uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk); 263*280c1e1cSGerd Hoffmann wpos += read; 264*280c1e1cSGerd Hoffmann to_transfer -= read; 265*280c1e1cSGerd Hoffmann atomic_fetch_add(&st->wpos, read); 266*280c1e1cSGerd Hoffmann if (chunk != read) { 267d61a4ce8SGerd Hoffmann break; 268d61a4ce8SGerd Hoffmann } 269d61a4ce8SGerd Hoffmann } 270*280c1e1cSGerd Hoffmann } 271*280c1e1cSGerd Hoffmann 272*280c1e1cSGerd Hoffmann static void hda_audio_output_timer(void *opaque) 273*280c1e1cSGerd Hoffmann { 274*280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 275*280c1e1cSGerd Hoffmann 276*280c1e1cSGerd Hoffmann int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 277*280c1e1cSGerd Hoffmann 278*280c1e1cSGerd Hoffmann int64_t buft_start = atomic_fetch_add(&st->buft_start, 0); 279*280c1e1cSGerd Hoffmann int64_t wpos = atomic_fetch_add(&st->wpos, 0); 280*280c1e1cSGerd Hoffmann int64_t rpos = atomic_fetch_add(&st->rpos, 0); 281*280c1e1cSGerd Hoffmann 282*280c1e1cSGerd Hoffmann int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) 283*280c1e1cSGerd Hoffmann / NANOSECONDS_PER_SECOND; 284*280c1e1cSGerd Hoffmann wanted_wpos &= -4; /* IMPORTANT! clip to frames */ 285*280c1e1cSGerd Hoffmann 286*280c1e1cSGerd Hoffmann if (wanted_wpos <= wpos) { 287*280c1e1cSGerd Hoffmann /* we already received the data */ 288*280c1e1cSGerd Hoffmann goto out_timer; 289*280c1e1cSGerd Hoffmann } 290*280c1e1cSGerd Hoffmann 291*280c1e1cSGerd Hoffmann int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos); 292*280c1e1cSGerd Hoffmann while (to_transfer) { 293*280c1e1cSGerd Hoffmann uint32_t start = (wpos & B_MASK); 294*280c1e1cSGerd Hoffmann uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); 295*280c1e1cSGerd Hoffmann int rc = hda_codec_xfer( 296*280c1e1cSGerd Hoffmann &st->state->hda, st->stream, true, st->buf + start, chunk); 297d61a4ce8SGerd Hoffmann if (!rc) { 298d61a4ce8SGerd Hoffmann break; 299d61a4ce8SGerd Hoffmann } 300*280c1e1cSGerd Hoffmann wpos += chunk; 301*280c1e1cSGerd Hoffmann to_transfer -= chunk; 302*280c1e1cSGerd Hoffmann atomic_fetch_add(&st->wpos, chunk); 303*280c1e1cSGerd Hoffmann } 304*280c1e1cSGerd Hoffmann 305*280c1e1cSGerd Hoffmann out_timer: 306*280c1e1cSGerd Hoffmann 307*280c1e1cSGerd Hoffmann if (st->running) { 308*280c1e1cSGerd Hoffmann timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); 309d61a4ce8SGerd Hoffmann } 310d61a4ce8SGerd Hoffmann } 311d61a4ce8SGerd Hoffmann 312d61a4ce8SGerd Hoffmann static void hda_audio_output_cb(void *opaque, int avail) 313d61a4ce8SGerd Hoffmann { 314d61a4ce8SGerd Hoffmann HDAAudioStream *st = opaque; 315*280c1e1cSGerd Hoffmann 316*280c1e1cSGerd Hoffmann int64_t wpos = atomic_fetch_add(&st->wpos, 0); 317*280c1e1cSGerd Hoffmann int64_t rpos = atomic_fetch_add(&st->rpos, 0); 318*280c1e1cSGerd Hoffmann 319*280c1e1cSGerd Hoffmann int64_t to_transfer = audio_MIN(wpos - rpos, avail); 320*280c1e1cSGerd Hoffmann 321*280c1e1cSGerd Hoffmann hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1)); 322*280c1e1cSGerd Hoffmann 323*280c1e1cSGerd Hoffmann while (to_transfer) { 324*280c1e1cSGerd Hoffmann uint32_t start = (uint32_t) (rpos & B_MASK); 325*280c1e1cSGerd Hoffmann uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); 326*280c1e1cSGerd Hoffmann uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk); 327*280c1e1cSGerd Hoffmann rpos += written; 328*280c1e1cSGerd Hoffmann to_transfer -= written; 329*280c1e1cSGerd Hoffmann atomic_fetch_add(&st->rpos, written); 330*280c1e1cSGerd Hoffmann if (chunk != written) { 331*280c1e1cSGerd Hoffmann break; 332*280c1e1cSGerd Hoffmann } 333*280c1e1cSGerd Hoffmann } 334*280c1e1cSGerd Hoffmann } 335*280c1e1cSGerd Hoffmann 336*280c1e1cSGerd Hoffmann static void hda_audio_compat_input_cb(void *opaque, int avail) 337*280c1e1cSGerd Hoffmann { 338*280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 339*280c1e1cSGerd Hoffmann int recv = 0; 340*280c1e1cSGerd Hoffmann int len; 341*280c1e1cSGerd Hoffmann bool rc; 342*280c1e1cSGerd Hoffmann 343*280c1e1cSGerd Hoffmann while (avail - recv >= sizeof(st->compat_buf)) { 344*280c1e1cSGerd Hoffmann if (st->compat_bpos != sizeof(st->compat_buf)) { 345*280c1e1cSGerd Hoffmann len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos, 346*280c1e1cSGerd Hoffmann sizeof(st->compat_buf) - st->compat_bpos); 347*280c1e1cSGerd Hoffmann st->compat_bpos += len; 348*280c1e1cSGerd Hoffmann recv += len; 349*280c1e1cSGerd Hoffmann if (st->compat_bpos != sizeof(st->compat_buf)) { 350*280c1e1cSGerd Hoffmann break; 351*280c1e1cSGerd Hoffmann } 352*280c1e1cSGerd Hoffmann } 353*280c1e1cSGerd Hoffmann rc = hda_codec_xfer(&st->state->hda, st->stream, false, 354*280c1e1cSGerd Hoffmann st->compat_buf, sizeof(st->compat_buf)); 355*280c1e1cSGerd Hoffmann if (!rc) { 356*280c1e1cSGerd Hoffmann break; 357*280c1e1cSGerd Hoffmann } 358*280c1e1cSGerd Hoffmann st->compat_bpos = 0; 359*280c1e1cSGerd Hoffmann } 360*280c1e1cSGerd Hoffmann } 361*280c1e1cSGerd Hoffmann 362*280c1e1cSGerd Hoffmann static void hda_audio_compat_output_cb(void *opaque, int avail) 363*280c1e1cSGerd Hoffmann { 364*280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 365d61a4ce8SGerd Hoffmann int sent = 0; 366d61a4ce8SGerd Hoffmann int len; 367d61a4ce8SGerd Hoffmann bool rc; 368d61a4ce8SGerd Hoffmann 369*280c1e1cSGerd Hoffmann while (avail - sent >= sizeof(st->compat_buf)) { 370*280c1e1cSGerd Hoffmann if (st->compat_bpos == sizeof(st->compat_buf)) { 371d61a4ce8SGerd Hoffmann rc = hda_codec_xfer(&st->state->hda, st->stream, true, 372*280c1e1cSGerd Hoffmann st->compat_buf, sizeof(st->compat_buf)); 373d61a4ce8SGerd Hoffmann if (!rc) { 374d61a4ce8SGerd Hoffmann break; 375d61a4ce8SGerd Hoffmann } 376*280c1e1cSGerd Hoffmann st->compat_bpos = 0; 377d61a4ce8SGerd Hoffmann } 378*280c1e1cSGerd Hoffmann len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos, 379*280c1e1cSGerd Hoffmann sizeof(st->compat_buf) - st->compat_bpos); 380*280c1e1cSGerd Hoffmann st->compat_bpos += len; 381d61a4ce8SGerd Hoffmann sent += len; 382*280c1e1cSGerd Hoffmann if (st->compat_bpos != sizeof(st->compat_buf)) { 383d61a4ce8SGerd Hoffmann break; 384d61a4ce8SGerd Hoffmann } 385d61a4ce8SGerd Hoffmann } 386d61a4ce8SGerd Hoffmann } 387d61a4ce8SGerd Hoffmann 388d61a4ce8SGerd Hoffmann static void hda_audio_set_running(HDAAudioStream *st, bool running) 389d61a4ce8SGerd Hoffmann { 390d61a4ce8SGerd Hoffmann if (st->node == NULL) { 391d61a4ce8SGerd Hoffmann return; 392d61a4ce8SGerd Hoffmann } 393d61a4ce8SGerd Hoffmann if (st->running == running) { 394d61a4ce8SGerd Hoffmann return; 395d61a4ce8SGerd Hoffmann } 396d61a4ce8SGerd Hoffmann st->running = running; 397d61a4ce8SGerd Hoffmann dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, 398d61a4ce8SGerd Hoffmann st->running ? "on" : "off", st->stream); 399*280c1e1cSGerd Hoffmann if (st->state->use_timer) { 400*280c1e1cSGerd Hoffmann if (running) { 401*280c1e1cSGerd Hoffmann int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 402*280c1e1cSGerd Hoffmann st->rpos = 0; 403*280c1e1cSGerd Hoffmann st->wpos = 0; 404*280c1e1cSGerd Hoffmann st->buft_start = now; 405*280c1e1cSGerd Hoffmann timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); 406*280c1e1cSGerd Hoffmann } else { 407*280c1e1cSGerd Hoffmann timer_del(st->buft); 408*280c1e1cSGerd Hoffmann } 409*280c1e1cSGerd Hoffmann } 410d61a4ce8SGerd Hoffmann if (st->output) { 411d61a4ce8SGerd Hoffmann AUD_set_active_out(st->voice.out, st->running); 412d61a4ce8SGerd Hoffmann } else { 413d61a4ce8SGerd Hoffmann AUD_set_active_in(st->voice.in, st->running); 414d61a4ce8SGerd Hoffmann } 415d61a4ce8SGerd Hoffmann } 416d61a4ce8SGerd Hoffmann 417d61a4ce8SGerd Hoffmann static void hda_audio_set_amp(HDAAudioStream *st) 418d61a4ce8SGerd Hoffmann { 419d61a4ce8SGerd Hoffmann bool muted; 420d61a4ce8SGerd Hoffmann uint32_t left, right; 421d61a4ce8SGerd Hoffmann 422d61a4ce8SGerd Hoffmann if (st->node == NULL) { 423d61a4ce8SGerd Hoffmann return; 424d61a4ce8SGerd Hoffmann } 425d61a4ce8SGerd Hoffmann 426d61a4ce8SGerd Hoffmann muted = st->mute_left && st->mute_right; 427d61a4ce8SGerd Hoffmann left = st->mute_left ? 0 : st->gain_left; 428d61a4ce8SGerd Hoffmann right = st->mute_right ? 0 : st->gain_right; 429d61a4ce8SGerd Hoffmann 430d61a4ce8SGerd Hoffmann left = left * 255 / QEMU_HDA_AMP_STEPS; 431d61a4ce8SGerd Hoffmann right = right * 255 / QEMU_HDA_AMP_STEPS; 432d61a4ce8SGerd Hoffmann 4334843877eSGerd Hoffmann if (!st->state->mixer) { 4344843877eSGerd Hoffmann return; 4354843877eSGerd Hoffmann } 436d61a4ce8SGerd Hoffmann if (st->output) { 437d61a4ce8SGerd Hoffmann AUD_set_volume_out(st->voice.out, muted, left, right); 438d61a4ce8SGerd Hoffmann } else { 439d61a4ce8SGerd Hoffmann AUD_set_volume_in(st->voice.in, muted, left, right); 440d61a4ce8SGerd Hoffmann } 441d61a4ce8SGerd Hoffmann } 442d61a4ce8SGerd Hoffmann 443d61a4ce8SGerd Hoffmann static void hda_audio_setup(HDAAudioStream *st) 444d61a4ce8SGerd Hoffmann { 445*280c1e1cSGerd Hoffmann bool use_timer = st->state->use_timer; 446*280c1e1cSGerd Hoffmann audio_callback_fn cb; 447*280c1e1cSGerd Hoffmann 448d61a4ce8SGerd Hoffmann if (st->node == NULL) { 449d61a4ce8SGerd Hoffmann return; 450d61a4ce8SGerd Hoffmann } 451d61a4ce8SGerd Hoffmann 452d61a4ce8SGerd Hoffmann dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", 453d61a4ce8SGerd Hoffmann st->node->name, st->as.nchannels, 454d61a4ce8SGerd Hoffmann fmt2name[st->as.fmt], st->as.freq); 455d61a4ce8SGerd Hoffmann 456d61a4ce8SGerd Hoffmann if (st->output) { 457*280c1e1cSGerd Hoffmann if (use_timer) { 458*280c1e1cSGerd Hoffmann cb = hda_audio_output_cb; 459*280c1e1cSGerd Hoffmann st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, 460*280c1e1cSGerd Hoffmann hda_audio_output_timer, st); 461d61a4ce8SGerd Hoffmann } else { 462*280c1e1cSGerd Hoffmann cb = hda_audio_compat_output_cb; 463*280c1e1cSGerd Hoffmann } 464*280c1e1cSGerd Hoffmann st->voice.out = AUD_open_out(&st->state->card, st->voice.out, 465*280c1e1cSGerd Hoffmann st->node->name, st, cb, &st->as); 466*280c1e1cSGerd Hoffmann } else { 467*280c1e1cSGerd Hoffmann if (use_timer) { 468*280c1e1cSGerd Hoffmann cb = hda_audio_input_cb; 469*280c1e1cSGerd Hoffmann st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, 470*280c1e1cSGerd Hoffmann hda_audio_input_timer, st); 471*280c1e1cSGerd Hoffmann } else { 472*280c1e1cSGerd Hoffmann cb = hda_audio_compat_input_cb; 473*280c1e1cSGerd Hoffmann } 474d61a4ce8SGerd Hoffmann st->voice.in = AUD_open_in(&st->state->card, st->voice.in, 475*280c1e1cSGerd Hoffmann st->node->name, st, cb, &st->as); 476d61a4ce8SGerd Hoffmann } 477d61a4ce8SGerd Hoffmann } 478d61a4ce8SGerd Hoffmann 479d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) 480d61a4ce8SGerd Hoffmann { 481cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 482d61a4ce8SGerd Hoffmann HDAAudioStream *st; 483d61a4ce8SGerd Hoffmann const desc_node *node = NULL; 484d61a4ce8SGerd Hoffmann const desc_param *param; 485d61a4ce8SGerd Hoffmann uint32_t verb, payload, response, count, shift; 486d61a4ce8SGerd Hoffmann 487d61a4ce8SGerd Hoffmann if ((data & 0x70000) == 0x70000) { 488d61a4ce8SGerd Hoffmann /* 12/8 id/payload */ 489d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xfff; 490d61a4ce8SGerd Hoffmann payload = data & 0x00ff; 491d61a4ce8SGerd Hoffmann } else { 492d61a4ce8SGerd Hoffmann /* 4/16 id/payload */ 493d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xf00; 494d61a4ce8SGerd Hoffmann payload = data & 0xffff; 495d61a4ce8SGerd Hoffmann } 496d61a4ce8SGerd Hoffmann 497d61a4ce8SGerd Hoffmann node = hda_codec_find_node(a->desc, nid); 498d61a4ce8SGerd Hoffmann if (node == NULL) { 499d61a4ce8SGerd Hoffmann goto fail; 500d61a4ce8SGerd Hoffmann } 501d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n", 502a89f364aSAlistair Francis __func__, nid, node->name, verb, payload); 503d61a4ce8SGerd Hoffmann 504d61a4ce8SGerd Hoffmann switch (verb) { 505d61a4ce8SGerd Hoffmann /* all nodes */ 506d61a4ce8SGerd Hoffmann case AC_VERB_PARAMETERS: 507d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, payload); 508d61a4ce8SGerd Hoffmann if (param == NULL) { 509d61a4ce8SGerd Hoffmann goto fail; 510d61a4ce8SGerd Hoffmann } 511d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, param->val); 512d61a4ce8SGerd Hoffmann break; 513d61a4ce8SGerd Hoffmann case AC_VERB_GET_SUBSYSTEM_ID: 514d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, a->desc->iid); 515d61a4ce8SGerd Hoffmann break; 516d61a4ce8SGerd Hoffmann 517d61a4ce8SGerd Hoffmann /* all functions */ 518d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONNECT_LIST: 519d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN); 520d61a4ce8SGerd Hoffmann count = param ? param->val : 0; 521d61a4ce8SGerd Hoffmann response = 0; 522d61a4ce8SGerd Hoffmann shift = 0; 523d61a4ce8SGerd Hoffmann while (payload < count && shift < 32) { 524d61a4ce8SGerd Hoffmann response |= node->conn[payload] << shift; 525d61a4ce8SGerd Hoffmann payload++; 526d61a4ce8SGerd Hoffmann shift += 8; 527d61a4ce8SGerd Hoffmann } 528d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 529d61a4ce8SGerd Hoffmann break; 530d61a4ce8SGerd Hoffmann 531d61a4ce8SGerd Hoffmann /* pin widget */ 532d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONFIG_DEFAULT: 533d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->config); 534d61a4ce8SGerd Hoffmann break; 535d61a4ce8SGerd Hoffmann case AC_VERB_GET_PIN_WIDGET_CONTROL: 536d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->pinctl); 537d61a4ce8SGerd Hoffmann break; 538d61a4ce8SGerd Hoffmann case AC_VERB_SET_PIN_WIDGET_CONTROL: 539d61a4ce8SGerd Hoffmann if (node->pinctl != payload) { 540d61a4ce8SGerd Hoffmann dprint(a, 1, "unhandled pin control bit\n"); 541d61a4ce8SGerd Hoffmann } 542d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 543d61a4ce8SGerd Hoffmann break; 544d61a4ce8SGerd Hoffmann 545d61a4ce8SGerd Hoffmann /* audio in/out widget */ 546d61a4ce8SGerd Hoffmann case AC_VERB_SET_CHANNEL_STREAMID: 547d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 548d61a4ce8SGerd Hoffmann if (st->node == NULL) { 549d61a4ce8SGerd Hoffmann goto fail; 550d61a4ce8SGerd Hoffmann } 551d61a4ce8SGerd Hoffmann hda_audio_set_running(st, false); 552d61a4ce8SGerd Hoffmann st->stream = (payload >> 4) & 0x0f; 553d61a4ce8SGerd Hoffmann st->channel = payload & 0x0f; 554d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: stream %d, channel %d\n", 555d61a4ce8SGerd Hoffmann st->node->name, st->stream, st->channel); 556ba43d289SMarc-André Lureau hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); 557d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 558d61a4ce8SGerd Hoffmann break; 559d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONV: 560d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 561d61a4ce8SGerd Hoffmann if (st->node == NULL) { 562d61a4ce8SGerd Hoffmann goto fail; 563d61a4ce8SGerd Hoffmann } 564d61a4ce8SGerd Hoffmann response = st->stream << 4 | st->channel; 565d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 566d61a4ce8SGerd Hoffmann break; 567d61a4ce8SGerd Hoffmann case AC_VERB_SET_STREAM_FORMAT: 568d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 569d61a4ce8SGerd Hoffmann if (st->node == NULL) { 570d61a4ce8SGerd Hoffmann goto fail; 571d61a4ce8SGerd Hoffmann } 572d61a4ce8SGerd Hoffmann st->format = payload; 573d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 574d61a4ce8SGerd Hoffmann hda_audio_setup(st); 575d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 576d61a4ce8SGerd Hoffmann break; 577d61a4ce8SGerd Hoffmann case AC_VERB_GET_STREAM_FORMAT: 578d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 579d61a4ce8SGerd Hoffmann if (st->node == NULL) { 580d61a4ce8SGerd Hoffmann goto fail; 581d61a4ce8SGerd Hoffmann } 582d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, st->format); 583d61a4ce8SGerd Hoffmann break; 584d61a4ce8SGerd Hoffmann case AC_VERB_GET_AMP_GAIN_MUTE: 585d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 586d61a4ce8SGerd Hoffmann if (st->node == NULL) { 587d61a4ce8SGerd Hoffmann goto fail; 588d61a4ce8SGerd Hoffmann } 589d61a4ce8SGerd Hoffmann if (payload & AC_AMP_GET_LEFT) { 590d61a4ce8SGerd Hoffmann response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0); 591d61a4ce8SGerd Hoffmann } else { 592d61a4ce8SGerd Hoffmann response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0); 593d61a4ce8SGerd Hoffmann } 594d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 595d61a4ce8SGerd Hoffmann break; 596d61a4ce8SGerd Hoffmann case AC_VERB_SET_AMP_GAIN_MUTE: 597d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 598d61a4ce8SGerd Hoffmann if (st->node == NULL) { 599d61a4ce8SGerd Hoffmann goto fail; 600d61a4ce8SGerd Hoffmann } 601d61a4ce8SGerd Hoffmann dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n", 602d61a4ce8SGerd Hoffmann st->node->name, 603d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_OUTPUT) ? "o" : "-", 604d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INPUT) ? "i" : "-", 605d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_LEFT) ? "l" : "-", 606d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_RIGHT) ? "r" : "-", 607d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT, 608d61a4ce8SGerd Hoffmann (payload & AC_AMP_GAIN), 609d61a4ce8SGerd Hoffmann (payload & AC_AMP_MUTE) ? "muted" : ""); 610d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_LEFT) { 611d61a4ce8SGerd Hoffmann st->gain_left = payload & AC_AMP_GAIN; 612d61a4ce8SGerd Hoffmann st->mute_left = payload & AC_AMP_MUTE; 613d61a4ce8SGerd Hoffmann } 614d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_RIGHT) { 615d61a4ce8SGerd Hoffmann st->gain_right = payload & AC_AMP_GAIN; 616d61a4ce8SGerd Hoffmann st->mute_right = payload & AC_AMP_MUTE; 617d61a4ce8SGerd Hoffmann } 618d61a4ce8SGerd Hoffmann hda_audio_set_amp(st); 619d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 620d61a4ce8SGerd Hoffmann break; 621d61a4ce8SGerd Hoffmann 622d61a4ce8SGerd Hoffmann /* not supported */ 623d61a4ce8SGerd Hoffmann case AC_VERB_SET_POWER_STATE: 624d61a4ce8SGerd Hoffmann case AC_VERB_GET_POWER_STATE: 625d61a4ce8SGerd Hoffmann case AC_VERB_GET_SDI_SELECT: 626d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 627d61a4ce8SGerd Hoffmann break; 628d61a4ce8SGerd Hoffmann default: 629d61a4ce8SGerd Hoffmann goto fail; 630d61a4ce8SGerd Hoffmann } 631d61a4ce8SGerd Hoffmann return; 632d61a4ce8SGerd Hoffmann 633d61a4ce8SGerd Hoffmann fail: 634d61a4ce8SGerd Hoffmann dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n", 635a89f364aSAlistair Francis __func__, nid, node ? node->name : "?", verb, payload); 636d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 637d61a4ce8SGerd Hoffmann } 638d61a4ce8SGerd Hoffmann 639ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output) 640d61a4ce8SGerd Hoffmann { 641cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 642d61a4ce8SGerd Hoffmann int s; 643d61a4ce8SGerd Hoffmann 644ba43d289SMarc-André Lureau a->running_compat[stnr] = running; 645ba43d289SMarc-André Lureau a->running_real[output * 16 + stnr] = running; 646d61a4ce8SGerd Hoffmann for (s = 0; s < ARRAY_SIZE(a->st); s++) { 647d61a4ce8SGerd Hoffmann if (a->st[s].node == NULL) { 648d61a4ce8SGerd Hoffmann continue; 649d61a4ce8SGerd Hoffmann } 650ba43d289SMarc-André Lureau if (a->st[s].output != output) { 651ba43d289SMarc-André Lureau continue; 652ba43d289SMarc-André Lureau } 653d61a4ce8SGerd Hoffmann if (a->st[s].stream != stnr) { 654d61a4ce8SGerd Hoffmann continue; 655d61a4ce8SGerd Hoffmann } 656d61a4ce8SGerd Hoffmann hda_audio_set_running(&a->st[s], running); 657d61a4ce8SGerd Hoffmann } 658d61a4ce8SGerd Hoffmann } 659d61a4ce8SGerd Hoffmann 660d61a4ce8SGerd Hoffmann static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) 661d61a4ce8SGerd Hoffmann { 662cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 663d61a4ce8SGerd Hoffmann HDAAudioStream *st; 664d61a4ce8SGerd Hoffmann const desc_node *node; 665d61a4ce8SGerd Hoffmann const desc_param *param; 666d61a4ce8SGerd Hoffmann uint32_t i, type; 667d61a4ce8SGerd Hoffmann 668d61a4ce8SGerd Hoffmann a->desc = desc; 669f79f2bfcSAnthony Liguori a->name = object_get_typename(OBJECT(a)); 670a89f364aSAlistair Francis dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad); 671d61a4ce8SGerd Hoffmann 672d61a4ce8SGerd Hoffmann AUD_register_card("hda", &a->card); 673d61a4ce8SGerd Hoffmann for (i = 0; i < a->desc->nnodes; i++) { 674d61a4ce8SGerd Hoffmann node = a->desc->nodes + i; 675d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP); 6762ab5bf67SGonglei if (param == NULL) { 677d61a4ce8SGerd Hoffmann continue; 6782ab5bf67SGonglei } 679d61a4ce8SGerd Hoffmann type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; 680d61a4ce8SGerd Hoffmann switch (type) { 681d61a4ce8SGerd Hoffmann case AC_WID_AUD_OUT: 682d61a4ce8SGerd Hoffmann case AC_WID_AUD_IN: 683d61a4ce8SGerd Hoffmann assert(node->stindex < ARRAY_SIZE(a->st)); 684d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 685d61a4ce8SGerd Hoffmann st->state = a; 686d61a4ce8SGerd Hoffmann st->node = node; 687d61a4ce8SGerd Hoffmann if (type == AC_WID_AUD_OUT) { 688d61a4ce8SGerd Hoffmann /* unmute output by default */ 689d61a4ce8SGerd Hoffmann st->gain_left = QEMU_HDA_AMP_STEPS; 690d61a4ce8SGerd Hoffmann st->gain_right = QEMU_HDA_AMP_STEPS; 691*280c1e1cSGerd Hoffmann st->compat_bpos = sizeof(st->compat_buf); 692d61a4ce8SGerd Hoffmann st->output = true; 693d61a4ce8SGerd Hoffmann } else { 694d61a4ce8SGerd Hoffmann st->output = false; 695d61a4ce8SGerd Hoffmann } 696d61a4ce8SGerd Hoffmann st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 | 697d61a4ce8SGerd Hoffmann (1 << AC_FMT_CHAN_SHIFT); 698d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 699d61a4ce8SGerd Hoffmann hda_audio_setup(st); 700d61a4ce8SGerd Hoffmann break; 701d61a4ce8SGerd Hoffmann } 702d61a4ce8SGerd Hoffmann } 703d61a4ce8SGerd Hoffmann return 0; 704d61a4ce8SGerd Hoffmann } 705d61a4ce8SGerd Hoffmann 7065eaa8e1eSZihan Yang static void hda_audio_exit(HDACodecDevice *hda) 707129dcd2cSGerd Hoffmann { 708cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 709129dcd2cSGerd Hoffmann HDAAudioStream *st; 710129dcd2cSGerd Hoffmann int i; 711129dcd2cSGerd Hoffmann 712a89f364aSAlistair Francis dprint(a, 1, "%s\n", __func__); 713129dcd2cSGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 714129dcd2cSGerd Hoffmann st = a->st + i; 715129dcd2cSGerd Hoffmann if (st->node == NULL) { 716129dcd2cSGerd Hoffmann continue; 717129dcd2cSGerd Hoffmann } 718*280c1e1cSGerd Hoffmann if (a->use_timer) { 719*280c1e1cSGerd Hoffmann timer_del(st->buft); 720*280c1e1cSGerd Hoffmann } 721129dcd2cSGerd Hoffmann if (st->output) { 722129dcd2cSGerd Hoffmann AUD_close_out(&a->card, st->voice.out); 723129dcd2cSGerd Hoffmann } else { 724129dcd2cSGerd Hoffmann AUD_close_in(&a->card, st->voice.in); 725129dcd2cSGerd Hoffmann } 726129dcd2cSGerd Hoffmann } 727129dcd2cSGerd Hoffmann AUD_remove_card(&a->card); 728129dcd2cSGerd Hoffmann } 729129dcd2cSGerd Hoffmann 730d61a4ce8SGerd Hoffmann static int hda_audio_post_load(void *opaque, int version) 731d61a4ce8SGerd Hoffmann { 732d61a4ce8SGerd Hoffmann HDAAudioState *a = opaque; 733d61a4ce8SGerd Hoffmann HDAAudioStream *st; 734d61a4ce8SGerd Hoffmann int i; 735d61a4ce8SGerd Hoffmann 736a89f364aSAlistair Francis dprint(a, 1, "%s\n", __func__); 737ba43d289SMarc-André Lureau if (version == 1) { 738ba43d289SMarc-André Lureau /* assume running_compat[] is for output streams */ 739ba43d289SMarc-André Lureau for (i = 0; i < ARRAY_SIZE(a->running_compat); i++) 740ba43d289SMarc-André Lureau a->running_real[16 + i] = a->running_compat[i]; 741ba43d289SMarc-André Lureau } 742ba43d289SMarc-André Lureau 743d61a4ce8SGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 744d61a4ce8SGerd Hoffmann st = a->st + i; 745d61a4ce8SGerd Hoffmann if (st->node == NULL) 746d61a4ce8SGerd Hoffmann continue; 747d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 748d61a4ce8SGerd Hoffmann hda_audio_setup(st); 749d61a4ce8SGerd Hoffmann hda_audio_set_amp(st); 750ba43d289SMarc-André Lureau hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); 751d61a4ce8SGerd Hoffmann } 752d61a4ce8SGerd Hoffmann return 0; 753d61a4ce8SGerd Hoffmann } 754d61a4ce8SGerd Hoffmann 75539e6a38cSGerd Hoffmann static void hda_audio_reset(DeviceState *dev) 75639e6a38cSGerd Hoffmann { 757cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(dev); 75839e6a38cSGerd Hoffmann HDAAudioStream *st; 75939e6a38cSGerd Hoffmann int i; 76039e6a38cSGerd Hoffmann 76139e6a38cSGerd Hoffmann dprint(a, 1, "%s\n", __func__); 76239e6a38cSGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 76339e6a38cSGerd Hoffmann st = a->st + i; 76439e6a38cSGerd Hoffmann if (st->node != NULL) { 76539e6a38cSGerd Hoffmann hda_audio_set_running(st, false); 76639e6a38cSGerd Hoffmann } 76739e6a38cSGerd Hoffmann } 76839e6a38cSGerd Hoffmann } 76939e6a38cSGerd Hoffmann 770*280c1e1cSGerd Hoffmann static bool vmstate_hda_audio_stream_buf_needed(void *opaque) 771*280c1e1cSGerd Hoffmann { 772*280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 773*280c1e1cSGerd Hoffmann return st->state->use_timer; 774*280c1e1cSGerd Hoffmann } 775*280c1e1cSGerd Hoffmann 776*280c1e1cSGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream_buf = { 777*280c1e1cSGerd Hoffmann .name = "hda-audio-stream/buffer", 778*280c1e1cSGerd Hoffmann .version_id = 1, 779*280c1e1cSGerd Hoffmann .needed = vmstate_hda_audio_stream_buf_needed, 780*280c1e1cSGerd Hoffmann .fields = (VMStateField[]) { 781*280c1e1cSGerd Hoffmann VMSTATE_BUFFER(buf, HDAAudioStream), 782*280c1e1cSGerd Hoffmann VMSTATE_INT64(rpos, HDAAudioStream), 783*280c1e1cSGerd Hoffmann VMSTATE_INT64(wpos, HDAAudioStream), 784*280c1e1cSGerd Hoffmann VMSTATE_TIMER_PTR(buft, HDAAudioStream), 785*280c1e1cSGerd Hoffmann VMSTATE_INT64(buft_start, HDAAudioStream), 786*280c1e1cSGerd Hoffmann VMSTATE_END_OF_LIST() 787*280c1e1cSGerd Hoffmann } 788*280c1e1cSGerd Hoffmann }; 789*280c1e1cSGerd Hoffmann 790d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = { 791d61a4ce8SGerd Hoffmann .name = "hda-audio-stream", 792d61a4ce8SGerd Hoffmann .version_id = 1, 793d61a4ce8SGerd Hoffmann .fields = (VMStateField[]) { 794d61a4ce8SGerd Hoffmann VMSTATE_UINT32(stream, HDAAudioStream), 795d61a4ce8SGerd Hoffmann VMSTATE_UINT32(channel, HDAAudioStream), 796d61a4ce8SGerd Hoffmann VMSTATE_UINT32(format, HDAAudioStream), 797d61a4ce8SGerd Hoffmann VMSTATE_UINT32(gain_left, HDAAudioStream), 798d61a4ce8SGerd Hoffmann VMSTATE_UINT32(gain_right, HDAAudioStream), 799d61a4ce8SGerd Hoffmann VMSTATE_BOOL(mute_left, HDAAudioStream), 800d61a4ce8SGerd Hoffmann VMSTATE_BOOL(mute_right, HDAAudioStream), 801*280c1e1cSGerd Hoffmann VMSTATE_UINT32(compat_bpos, HDAAudioStream), 802*280c1e1cSGerd Hoffmann VMSTATE_BUFFER(compat_buf, HDAAudioStream), 803d61a4ce8SGerd Hoffmann VMSTATE_END_OF_LIST() 804*280c1e1cSGerd Hoffmann }, 805*280c1e1cSGerd Hoffmann .subsections = (const VMStateDescription * []) { 806*280c1e1cSGerd Hoffmann &vmstate_hda_audio_stream_buf, 807*280c1e1cSGerd Hoffmann NULL 808d61a4ce8SGerd Hoffmann } 809d61a4ce8SGerd Hoffmann }; 810d61a4ce8SGerd Hoffmann 811d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio = { 812d61a4ce8SGerd Hoffmann .name = "hda-audio", 813ba43d289SMarc-André Lureau .version_id = 2, 814d61a4ce8SGerd Hoffmann .post_load = hda_audio_post_load, 815d61a4ce8SGerd Hoffmann .fields = (VMStateField[]) { 816d61a4ce8SGerd Hoffmann VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, 817d61a4ce8SGerd Hoffmann vmstate_hda_audio_stream, 818d61a4ce8SGerd Hoffmann HDAAudioStream), 819ba43d289SMarc-André Lureau VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16), 820ba43d289SMarc-André Lureau VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2), 821d61a4ce8SGerd Hoffmann VMSTATE_END_OF_LIST() 822d61a4ce8SGerd Hoffmann } 823d61a4ce8SGerd Hoffmann }; 824d61a4ce8SGerd Hoffmann 825d61a4ce8SGerd Hoffmann static Property hda_audio_properties[] = { 826d61a4ce8SGerd Hoffmann DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), 8272690e61eSBandan Das DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), 828*280c1e1cSGerd Hoffmann DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, false), 829d61a4ce8SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 830d61a4ce8SGerd Hoffmann }; 831d61a4ce8SGerd Hoffmann 832d61a4ce8SGerd Hoffmann static int hda_audio_init_output(HDACodecDevice *hda) 833d61a4ce8SGerd Hoffmann { 834cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 8352690e61eSBandan Das 8362690e61eSBandan Das if (!a->mixer) { 8372690e61eSBandan Das return hda_audio_init(hda, &output_nomixemu); 8382690e61eSBandan Das } else { 8392690e61eSBandan Das return hda_audio_init(hda, &output_mixemu); 8402690e61eSBandan Das } 841d61a4ce8SGerd Hoffmann } 842d61a4ce8SGerd Hoffmann 843d61a4ce8SGerd Hoffmann static int hda_audio_init_duplex(HDACodecDevice *hda) 844d61a4ce8SGerd Hoffmann { 845cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 8462690e61eSBandan Das 8472690e61eSBandan Das if (!a->mixer) { 8482690e61eSBandan Das return hda_audio_init(hda, &duplex_nomixemu); 8492690e61eSBandan Das } else { 8502690e61eSBandan Das return hda_audio_init(hda, &duplex_mixemu); 8512690e61eSBandan Das } 852d61a4ce8SGerd Hoffmann } 853d61a4ce8SGerd Hoffmann 85420110065SGerd Hoffmann static int hda_audio_init_micro(HDACodecDevice *hda) 85520110065SGerd Hoffmann { 856cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 8572690e61eSBandan Das 8582690e61eSBandan Das if (!a->mixer) { 8592690e61eSBandan Das return hda_audio_init(hda, µ_nomixemu); 8602690e61eSBandan Das } else { 8612690e61eSBandan Das return hda_audio_init(hda, µ_mixemu); 8622690e61eSBandan Das } 86320110065SGerd Hoffmann } 86420110065SGerd Hoffmann 865cd6c8830SGerd Hoffmann static void hda_audio_base_class_init(ObjectClass *klass, void *data) 866cd6c8830SGerd Hoffmann { 867cd6c8830SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 868cd6c8830SGerd Hoffmann HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 869cd6c8830SGerd Hoffmann 870cd6c8830SGerd Hoffmann k->exit = hda_audio_exit; 871cd6c8830SGerd Hoffmann k->command = hda_audio_command; 872cd6c8830SGerd Hoffmann k->stream = hda_audio_stream; 873cd6c8830SGerd Hoffmann set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 874cd6c8830SGerd Hoffmann dc->reset = hda_audio_reset; 875cd6c8830SGerd Hoffmann dc->vmsd = &vmstate_hda_audio; 876cd6c8830SGerd Hoffmann dc->props = hda_audio_properties; 877cd6c8830SGerd Hoffmann } 878cd6c8830SGerd Hoffmann 879cd6c8830SGerd Hoffmann static const TypeInfo hda_audio_info = { 880cd6c8830SGerd Hoffmann .name = TYPE_HDA_AUDIO, 881cd6c8830SGerd Hoffmann .parent = TYPE_HDA_CODEC_DEVICE, 882cd6c8830SGerd Hoffmann .class_init = hda_audio_base_class_init, 883cd6c8830SGerd Hoffmann .abstract = true, 884cd6c8830SGerd Hoffmann }; 885cd6c8830SGerd Hoffmann 886dbaa7904SAnthony Liguori static void hda_audio_output_class_init(ObjectClass *klass, void *data) 887dbaa7904SAnthony Liguori { 88839bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 889dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 890dbaa7904SAnthony Liguori 891dbaa7904SAnthony Liguori k->init = hda_audio_init_output; 89220110065SGerd Hoffmann dc->desc = "HDA Audio Codec, output-only (line-out)"; 893dbaa7904SAnthony Liguori } 894dbaa7904SAnthony Liguori 8958c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = { 896dbaa7904SAnthony Liguori .name = "hda-output", 897cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO, 89839bffca2SAnthony Liguori .instance_size = sizeof(HDAAudioState), 899dbaa7904SAnthony Liguori .class_init = hda_audio_output_class_init, 900d61a4ce8SGerd Hoffmann }; 901d61a4ce8SGerd Hoffmann 902dbaa7904SAnthony Liguori static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) 903dbaa7904SAnthony Liguori { 90439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 905dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 906dbaa7904SAnthony Liguori 907dbaa7904SAnthony Liguori k->init = hda_audio_init_duplex; 90820110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (line-out, line-in)"; 909dbaa7904SAnthony Liguori } 910dbaa7904SAnthony Liguori 9118c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = { 912dbaa7904SAnthony Liguori .name = "hda-duplex", 913cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO, 91439bffca2SAnthony Liguori .instance_size = sizeof(HDAAudioState), 915dbaa7904SAnthony Liguori .class_init = hda_audio_duplex_class_init, 916d61a4ce8SGerd Hoffmann }; 917d61a4ce8SGerd Hoffmann 91820110065SGerd Hoffmann static void hda_audio_micro_class_init(ObjectClass *klass, void *data) 91920110065SGerd Hoffmann { 92020110065SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 92120110065SGerd Hoffmann HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 92220110065SGerd Hoffmann 92320110065SGerd Hoffmann k->init = hda_audio_init_micro; 92420110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (speaker, microphone)"; 92520110065SGerd Hoffmann } 92620110065SGerd Hoffmann 9278c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = { 92820110065SGerd Hoffmann .name = "hda-micro", 929cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO, 93020110065SGerd Hoffmann .instance_size = sizeof(HDAAudioState), 93120110065SGerd Hoffmann .class_init = hda_audio_micro_class_init, 93220110065SGerd Hoffmann }; 93320110065SGerd Hoffmann 93483f7d43aSAndreas Färber static void hda_audio_register_types(void) 935d61a4ce8SGerd Hoffmann { 936cd6c8830SGerd Hoffmann type_register_static(&hda_audio_info); 93739bffca2SAnthony Liguori type_register_static(&hda_audio_output_info); 93839bffca2SAnthony Liguori type_register_static(&hda_audio_duplex_info); 93920110065SGerd Hoffmann type_register_static(&hda_audio_micro_info); 940d61a4ce8SGerd Hoffmann } 94183f7d43aSAndreas Färber 94283f7d43aSAndreas Färber type_init(hda_audio_register_types) 943