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" 2183c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 2347b43a1fSPaolo Bonzini #include "intel-hda.h" 24d6454270SMarkus Armbruster #include "migration/vmstate.h" 25*74e8593eSVolker Rümelin #include "qemu/host-utils.h" 260b8fa32fSMarkus Armbruster #include "qemu/module.h" 2747b43a1fSPaolo Bonzini #include "intel-hda-defs.h" 28d61a4ce8SGerd Hoffmann #include "audio/audio.h" 290a373bb3SGerd Hoffmann #include "trace.h" 30db1015e9SEduardo Habkost #include "qom/object.h" 31d61a4ce8SGerd Hoffmann 32d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */ 33d61a4ce8SGerd Hoffmann 34d61a4ce8SGerd Hoffmann typedef struct desc_param { 35d61a4ce8SGerd Hoffmann uint32_t id; 36d61a4ce8SGerd Hoffmann uint32_t val; 37d61a4ce8SGerd Hoffmann } desc_param; 38d61a4ce8SGerd Hoffmann 39d61a4ce8SGerd Hoffmann typedef struct desc_node { 40d61a4ce8SGerd Hoffmann uint32_t nid; 41d61a4ce8SGerd Hoffmann const char *name; 42d61a4ce8SGerd Hoffmann const desc_param *params; 43d61a4ce8SGerd Hoffmann uint32_t nparams; 44d61a4ce8SGerd Hoffmann uint32_t config; 45d61a4ce8SGerd Hoffmann uint32_t pinctl; 46d61a4ce8SGerd Hoffmann uint32_t *conn; 47d61a4ce8SGerd Hoffmann uint32_t stindex; 48d61a4ce8SGerd Hoffmann } desc_node; 49d61a4ce8SGerd Hoffmann 50d61a4ce8SGerd Hoffmann typedef struct desc_codec { 51d61a4ce8SGerd Hoffmann const char *name; 52d61a4ce8SGerd Hoffmann uint32_t iid; 53d61a4ce8SGerd Hoffmann const desc_node *nodes; 54d61a4ce8SGerd Hoffmann uint32_t nnodes; 55d61a4ce8SGerd Hoffmann } desc_codec; 56d61a4ce8SGerd Hoffmann 57d61a4ce8SGerd Hoffmann static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id) 58d61a4ce8SGerd Hoffmann { 59d61a4ce8SGerd Hoffmann int i; 60d61a4ce8SGerd Hoffmann 61d61a4ce8SGerd Hoffmann for (i = 0; i < node->nparams; i++) { 62d61a4ce8SGerd Hoffmann if (node->params[i].id == id) { 63d61a4ce8SGerd Hoffmann return &node->params[i]; 64d61a4ce8SGerd Hoffmann } 65d61a4ce8SGerd Hoffmann } 66d61a4ce8SGerd Hoffmann return NULL; 67d61a4ce8SGerd Hoffmann } 68d61a4ce8SGerd Hoffmann 69d61a4ce8SGerd Hoffmann static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid) 70d61a4ce8SGerd Hoffmann { 71d61a4ce8SGerd Hoffmann int i; 72d61a4ce8SGerd Hoffmann 73d61a4ce8SGerd Hoffmann for (i = 0; i < codec->nnodes; i++) { 74d61a4ce8SGerd Hoffmann if (codec->nodes[i].nid == nid) { 75d61a4ce8SGerd Hoffmann return &codec->nodes[i]; 76d61a4ce8SGerd Hoffmann } 77d61a4ce8SGerd Hoffmann } 78d61a4ce8SGerd Hoffmann return NULL; 79d61a4ce8SGerd Hoffmann } 80d61a4ce8SGerd Hoffmann 81d61a4ce8SGerd Hoffmann static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) 82d61a4ce8SGerd Hoffmann { 83d61a4ce8SGerd Hoffmann if (format & AC_FMT_TYPE_NON_PCM) { 84d61a4ce8SGerd Hoffmann return; 85d61a4ce8SGerd Hoffmann } 86d61a4ce8SGerd Hoffmann 87d61a4ce8SGerd Hoffmann as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000; 88d61a4ce8SGerd Hoffmann 89d61a4ce8SGerd Hoffmann switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_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 } 94d61a4ce8SGerd Hoffmann 95d61a4ce8SGerd Hoffmann switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) { 96d61a4ce8SGerd Hoffmann case 1: as->freq /= 2; break; 97d61a4ce8SGerd Hoffmann case 2: as->freq /= 3; break; 98d61a4ce8SGerd Hoffmann case 3: as->freq /= 4; break; 99d61a4ce8SGerd Hoffmann case 4: as->freq /= 5; break; 100d61a4ce8SGerd Hoffmann case 5: as->freq /= 6; break; 101d61a4ce8SGerd Hoffmann case 6: as->freq /= 7; break; 102d61a4ce8SGerd Hoffmann case 7: as->freq /= 8; break; 103d61a4ce8SGerd Hoffmann } 104d61a4ce8SGerd Hoffmann 105d61a4ce8SGerd Hoffmann switch (format & AC_FMT_BITS_MASK) { 10685bc5852SKővágó, Zoltán case AC_FMT_BITS_8: as->fmt = AUDIO_FORMAT_S8; break; 10785bc5852SKővágó, Zoltán case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break; 10885bc5852SKővágó, Zoltán case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break; 109d61a4ce8SGerd Hoffmann } 110d61a4ce8SGerd Hoffmann 111d61a4ce8SGerd Hoffmann as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1; 112d61a4ce8SGerd Hoffmann } 113d61a4ce8SGerd Hoffmann 114d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */ 115d61a4ce8SGerd Hoffmann /* 116d61a4ce8SGerd Hoffmann * HDA codec descriptions 117d61a4ce8SGerd Hoffmann */ 118d61a4ce8SGerd Hoffmann 119d61a4ce8SGerd Hoffmann /* some defines */ 120d61a4ce8SGerd Hoffmann 121d61a4ce8SGerd Hoffmann #define QEMU_HDA_ID_VENDOR 0x1af4 122d61a4ce8SGerd Hoffmann #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \ 123d61a4ce8SGerd Hoffmann 0x1fc /* 16 -> 96 kHz */) 124d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_NONE (0) 125d61a4ce8SGerd Hoffmann #define QEMU_HDA_AMP_STEPS 0x4a 126d61a4ce8SGerd Hoffmann 1272690e61eSBandan Das #define PARAM mixemu 1282690e61eSBandan Das #define HDA_MIXER 1297953793cSBandan Das #include "hda-codec-common.h" 1302690e61eSBandan Das 1312690e61eSBandan Das #define PARAM nomixemu 1322690e61eSBandan Das #include "hda-codec-common.h" 1332690e61eSBandan Das 134280c1e1cSGerd Hoffmann #define HDA_TIMER_TICKS (SCALE_MS) 135280c1e1cSGerd Hoffmann #define B_SIZE sizeof(st->buf) 136280c1e1cSGerd Hoffmann #define B_MASK (sizeof(st->buf) - 1) 137280c1e1cSGerd Hoffmann 138d61a4ce8SGerd Hoffmann /* -------------------------------------------------------------------------- */ 139d61a4ce8SGerd Hoffmann 140d61a4ce8SGerd Hoffmann static const char *fmt2name[] = { 14185bc5852SKővágó, Zoltán [ AUDIO_FORMAT_U8 ] = "PCM-U8", 14285bc5852SKővágó, Zoltán [ AUDIO_FORMAT_S8 ] = "PCM-S8", 14385bc5852SKővágó, Zoltán [ AUDIO_FORMAT_U16 ] = "PCM-U16", 14485bc5852SKővágó, Zoltán [ AUDIO_FORMAT_S16 ] = "PCM-S16", 14585bc5852SKővágó, Zoltán [ AUDIO_FORMAT_U32 ] = "PCM-U32", 14685bc5852SKővágó, Zoltán [ AUDIO_FORMAT_S32 ] = "PCM-S32", 147d61a4ce8SGerd Hoffmann }; 148d61a4ce8SGerd Hoffmann 149acab7d60SPhilippe Mathieu-Daudé #define TYPE_HDA_AUDIO "hda-audio" 150acab7d60SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO) 151acab7d60SPhilippe Mathieu-Daudé 152d61a4ce8SGerd Hoffmann typedef struct HDAAudioStream HDAAudioStream; 153d61a4ce8SGerd Hoffmann 154d61a4ce8SGerd Hoffmann struct HDAAudioStream { 155d61a4ce8SGerd Hoffmann HDAAudioState *state; 156d61a4ce8SGerd Hoffmann const desc_node *node; 157d61a4ce8SGerd Hoffmann bool output, running; 158d61a4ce8SGerd Hoffmann uint32_t stream; 159d61a4ce8SGerd Hoffmann uint32_t channel; 160d61a4ce8SGerd Hoffmann uint32_t format; 161d61a4ce8SGerd Hoffmann uint32_t gain_left, gain_right; 162d61a4ce8SGerd Hoffmann bool mute_left, mute_right; 163d61a4ce8SGerd Hoffmann struct audsettings as; 164d61a4ce8SGerd Hoffmann union { 165d61a4ce8SGerd Hoffmann SWVoiceIn *in; 166d61a4ce8SGerd Hoffmann SWVoiceOut *out; 167d61a4ce8SGerd Hoffmann } voice; 168280c1e1cSGerd Hoffmann uint8_t compat_buf[HDA_BUFFER_SIZE]; 169280c1e1cSGerd Hoffmann uint32_t compat_bpos; 170280c1e1cSGerd Hoffmann uint8_t buf[8192]; /* size must be power of two */ 171280c1e1cSGerd Hoffmann int64_t rpos; 172280c1e1cSGerd Hoffmann int64_t wpos; 173280c1e1cSGerd Hoffmann QEMUTimer *buft; 174280c1e1cSGerd Hoffmann int64_t buft_start; 175d61a4ce8SGerd Hoffmann }; 176d61a4ce8SGerd Hoffmann 177d61a4ce8SGerd Hoffmann struct HDAAudioState { 178d61a4ce8SGerd Hoffmann HDACodecDevice hda; 179d61a4ce8SGerd Hoffmann const char *name; 180d61a4ce8SGerd Hoffmann 181d61a4ce8SGerd Hoffmann QEMUSoundCard card; 182d61a4ce8SGerd Hoffmann const desc_codec *desc; 183d61a4ce8SGerd Hoffmann HDAAudioStream st[4]; 184ba43d289SMarc-André Lureau bool running_compat[16]; 185ba43d289SMarc-André Lureau bool running_real[2 * 16]; 186d61a4ce8SGerd Hoffmann 187d61a4ce8SGerd Hoffmann /* properties */ 188d61a4ce8SGerd Hoffmann uint32_t debug; 1892690e61eSBandan Das bool mixer; 190280c1e1cSGerd Hoffmann bool use_timer; 191d61a4ce8SGerd Hoffmann }; 192d61a4ce8SGerd Hoffmann 193*74e8593eSVolker Rümelin static inline uint32_t hda_bytes_per_second(HDAAudioStream *st) 194280c1e1cSGerd Hoffmann { 195*74e8593eSVolker Rümelin return 2 * (uint32_t)st->as.nchannels * (uint32_t)st->as.freq; 196280c1e1cSGerd Hoffmann } 197280c1e1cSGerd Hoffmann 198280c1e1cSGerd Hoffmann static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) 199280c1e1cSGerd Hoffmann { 2008ced0669SGerd Hoffmann int64_t limit = B_SIZE / 8; 2018ced0669SGerd Hoffmann int64_t corr = 0; 2028ced0669SGerd Hoffmann 2038ced0669SGerd Hoffmann if (target_pos > limit) { 2048ced0669SGerd Hoffmann corr = HDA_TIMER_TICKS; 205280c1e1cSGerd Hoffmann } 2068ced0669SGerd Hoffmann if (target_pos < -limit) { 2078ced0669SGerd Hoffmann corr = -HDA_TIMER_TICKS; 2088ced0669SGerd Hoffmann } 2099d340f67SGerd Hoffmann if (target_pos < -(2 * limit)) { 2109d340f67SGerd Hoffmann corr = -(4 * HDA_TIMER_TICKS); 2119d340f67SGerd Hoffmann } 2128ced0669SGerd Hoffmann if (corr == 0) { 2138ced0669SGerd Hoffmann return; 2148ced0669SGerd Hoffmann } 2158ced0669SGerd Hoffmann 2168ced0669SGerd Hoffmann trace_hda_audio_adjust(st->node->name, target_pos); 2173b84967cSGerd Hoffmann st->buft_start += corr; 218280c1e1cSGerd Hoffmann } 219280c1e1cSGerd Hoffmann 220280c1e1cSGerd Hoffmann static void hda_audio_input_timer(void *opaque) 221280c1e1cSGerd Hoffmann { 222280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 223280c1e1cSGerd Hoffmann 224280c1e1cSGerd Hoffmann int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 225280c1e1cSGerd Hoffmann 226*74e8593eSVolker Rümelin int64_t uptime = now - st->buft_start; 2273b84967cSGerd Hoffmann int64_t wpos = st->wpos; 2283b84967cSGerd Hoffmann int64_t rpos = st->rpos; 229*74e8593eSVolker Rümelin int64_t wanted_rpos; 230280c1e1cSGerd Hoffmann 231*74e8593eSVolker Rümelin if (uptime <= 0) { 232*74e8593eSVolker Rümelin /* wanted_rpos <= 0 */ 233*74e8593eSVolker Rümelin goto out_timer; 234*74e8593eSVolker Rümelin } 235*74e8593eSVolker Rümelin 236*74e8593eSVolker Rümelin wanted_rpos = muldiv64(uptime, hda_bytes_per_second(st), 237*74e8593eSVolker Rümelin NANOSECONDS_PER_SECOND); 238280c1e1cSGerd Hoffmann wanted_rpos &= -4; /* IMPORTANT! clip to frames */ 239280c1e1cSGerd Hoffmann 240280c1e1cSGerd Hoffmann if (wanted_rpos <= rpos) { 241280c1e1cSGerd Hoffmann /* we already transmitted the data */ 242280c1e1cSGerd Hoffmann goto out_timer; 243280c1e1cSGerd Hoffmann } 244280c1e1cSGerd Hoffmann 24558935915SKővágó, Zoltán int64_t to_transfer = MIN(wpos - rpos, wanted_rpos - rpos); 246280c1e1cSGerd Hoffmann while (to_transfer) { 247280c1e1cSGerd Hoffmann uint32_t start = (rpos & B_MASK); 24858935915SKővágó, Zoltán uint32_t chunk = MIN(B_SIZE - start, to_transfer); 249280c1e1cSGerd Hoffmann int rc = hda_codec_xfer( 250280c1e1cSGerd Hoffmann &st->state->hda, st->stream, false, st->buf + start, chunk); 251280c1e1cSGerd Hoffmann if (!rc) { 252280c1e1cSGerd Hoffmann break; 253280c1e1cSGerd Hoffmann } 254280c1e1cSGerd Hoffmann rpos += chunk; 255280c1e1cSGerd Hoffmann to_transfer -= chunk; 2563b84967cSGerd Hoffmann st->rpos += chunk; 257280c1e1cSGerd Hoffmann } 258280c1e1cSGerd Hoffmann 259280c1e1cSGerd Hoffmann out_timer: 260280c1e1cSGerd Hoffmann 261280c1e1cSGerd Hoffmann if (st->running) { 262280c1e1cSGerd Hoffmann timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); 263280c1e1cSGerd Hoffmann } 264280c1e1cSGerd Hoffmann } 265280c1e1cSGerd Hoffmann 266d61a4ce8SGerd Hoffmann static void hda_audio_input_cb(void *opaque, int avail) 267d61a4ce8SGerd Hoffmann { 268d61a4ce8SGerd Hoffmann HDAAudioStream *st = opaque; 269d61a4ce8SGerd Hoffmann 2703b84967cSGerd Hoffmann int64_t wpos = st->wpos; 2713b84967cSGerd Hoffmann int64_t rpos = st->rpos; 272280c1e1cSGerd Hoffmann 27358935915SKővágó, Zoltán int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), avail); 274280c1e1cSGerd Hoffmann 275280c1e1cSGerd Hoffmann while (to_transfer) { 276280c1e1cSGerd Hoffmann uint32_t start = (uint32_t) (wpos & B_MASK); 27758935915SKővágó, Zoltán uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer); 278280c1e1cSGerd Hoffmann uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk); 279280c1e1cSGerd Hoffmann wpos += read; 280280c1e1cSGerd Hoffmann to_transfer -= read; 2813b84967cSGerd Hoffmann st->wpos += read; 282280c1e1cSGerd Hoffmann if (chunk != read) { 283d61a4ce8SGerd Hoffmann break; 284d61a4ce8SGerd Hoffmann } 285d61a4ce8SGerd Hoffmann } 286c435fea7SVolker Rümelin 287c435fea7SVolker Rümelin hda_timer_sync_adjust(st, -((wpos - rpos) - (B_SIZE >> 1))); 288280c1e1cSGerd Hoffmann } 289280c1e1cSGerd Hoffmann 290280c1e1cSGerd Hoffmann static void hda_audio_output_timer(void *opaque) 291280c1e1cSGerd Hoffmann { 292280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 293280c1e1cSGerd Hoffmann 294280c1e1cSGerd Hoffmann int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 295280c1e1cSGerd Hoffmann 296*74e8593eSVolker Rümelin int64_t uptime = now - st->buft_start; 2973b84967cSGerd Hoffmann int64_t wpos = st->wpos; 2983b84967cSGerd Hoffmann int64_t rpos = st->rpos; 299*74e8593eSVolker Rümelin int64_t wanted_wpos; 300280c1e1cSGerd Hoffmann 301*74e8593eSVolker Rümelin if (uptime <= 0) { 302*74e8593eSVolker Rümelin /* wanted_wpos <= 0 */ 303*74e8593eSVolker Rümelin goto out_timer; 304*74e8593eSVolker Rümelin } 305*74e8593eSVolker Rümelin 306*74e8593eSVolker Rümelin wanted_wpos = muldiv64(uptime, hda_bytes_per_second(st), 307*74e8593eSVolker Rümelin NANOSECONDS_PER_SECOND); 308280c1e1cSGerd Hoffmann wanted_wpos &= -4; /* IMPORTANT! clip to frames */ 309280c1e1cSGerd Hoffmann 310280c1e1cSGerd Hoffmann if (wanted_wpos <= wpos) { 311280c1e1cSGerd Hoffmann /* we already received the data */ 312280c1e1cSGerd Hoffmann goto out_timer; 313280c1e1cSGerd Hoffmann } 314280c1e1cSGerd Hoffmann 31558935915SKővágó, Zoltán int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos); 316280c1e1cSGerd Hoffmann while (to_transfer) { 317280c1e1cSGerd Hoffmann uint32_t start = (wpos & B_MASK); 31858935915SKővágó, Zoltán uint32_t chunk = MIN(B_SIZE - start, to_transfer); 319280c1e1cSGerd Hoffmann int rc = hda_codec_xfer( 320280c1e1cSGerd Hoffmann &st->state->hda, st->stream, true, st->buf + start, chunk); 321d61a4ce8SGerd Hoffmann if (!rc) { 322d61a4ce8SGerd Hoffmann break; 323d61a4ce8SGerd Hoffmann } 324280c1e1cSGerd Hoffmann wpos += chunk; 325280c1e1cSGerd Hoffmann to_transfer -= chunk; 3263b84967cSGerd Hoffmann st->wpos += chunk; 327280c1e1cSGerd Hoffmann } 328280c1e1cSGerd Hoffmann 329280c1e1cSGerd Hoffmann out_timer: 330280c1e1cSGerd Hoffmann 331280c1e1cSGerd Hoffmann if (st->running) { 332280c1e1cSGerd Hoffmann timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); 333d61a4ce8SGerd Hoffmann } 334d61a4ce8SGerd Hoffmann } 335d61a4ce8SGerd Hoffmann 336d61a4ce8SGerd Hoffmann static void hda_audio_output_cb(void *opaque, int avail) 337d61a4ce8SGerd Hoffmann { 338d61a4ce8SGerd Hoffmann HDAAudioStream *st = opaque; 339280c1e1cSGerd Hoffmann 3403b84967cSGerd Hoffmann int64_t wpos = st->wpos; 3413b84967cSGerd Hoffmann int64_t rpos = st->rpos; 342280c1e1cSGerd Hoffmann 34358935915SKővágó, Zoltán int64_t to_transfer = MIN(wpos - rpos, avail); 344280c1e1cSGerd Hoffmann 3454501ee16SGerd Hoffmann if (wpos - rpos == B_SIZE) { 3464501ee16SGerd Hoffmann /* drop buffer, reset timer adjust */ 3474501ee16SGerd Hoffmann st->rpos = 0; 3484501ee16SGerd Hoffmann st->wpos = 0; 3494501ee16SGerd Hoffmann st->buft_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 3504501ee16SGerd Hoffmann trace_hda_audio_overrun(st->node->name); 3514501ee16SGerd Hoffmann return; 3524501ee16SGerd Hoffmann } 3534501ee16SGerd Hoffmann 354280c1e1cSGerd Hoffmann while (to_transfer) { 355280c1e1cSGerd Hoffmann uint32_t start = (uint32_t) (rpos & B_MASK); 35658935915SKővágó, Zoltán uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer); 357280c1e1cSGerd Hoffmann uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk); 358280c1e1cSGerd Hoffmann rpos += written; 359280c1e1cSGerd Hoffmann to_transfer -= written; 3603b84967cSGerd Hoffmann st->rpos += written; 361280c1e1cSGerd Hoffmann if (chunk != written) { 362280c1e1cSGerd Hoffmann break; 363280c1e1cSGerd Hoffmann } 364280c1e1cSGerd Hoffmann } 365df016a19SVolker Rümelin 366df016a19SVolker Rümelin hda_timer_sync_adjust(st, (wpos - rpos) - (B_SIZE >> 1)); 367280c1e1cSGerd Hoffmann } 368280c1e1cSGerd Hoffmann 369280c1e1cSGerd Hoffmann static void hda_audio_compat_input_cb(void *opaque, int avail) 370280c1e1cSGerd Hoffmann { 371280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 372280c1e1cSGerd Hoffmann int recv = 0; 373280c1e1cSGerd Hoffmann int len; 374280c1e1cSGerd Hoffmann bool rc; 375280c1e1cSGerd Hoffmann 376280c1e1cSGerd Hoffmann while (avail - recv >= sizeof(st->compat_buf)) { 377280c1e1cSGerd Hoffmann if (st->compat_bpos != sizeof(st->compat_buf)) { 378280c1e1cSGerd Hoffmann len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos, 379280c1e1cSGerd Hoffmann sizeof(st->compat_buf) - st->compat_bpos); 380280c1e1cSGerd Hoffmann st->compat_bpos += len; 381280c1e1cSGerd Hoffmann recv += len; 382280c1e1cSGerd Hoffmann if (st->compat_bpos != sizeof(st->compat_buf)) { 383280c1e1cSGerd Hoffmann break; 384280c1e1cSGerd Hoffmann } 385280c1e1cSGerd Hoffmann } 386280c1e1cSGerd Hoffmann rc = hda_codec_xfer(&st->state->hda, st->stream, false, 387280c1e1cSGerd Hoffmann st->compat_buf, sizeof(st->compat_buf)); 388280c1e1cSGerd Hoffmann if (!rc) { 389280c1e1cSGerd Hoffmann break; 390280c1e1cSGerd Hoffmann } 391280c1e1cSGerd Hoffmann st->compat_bpos = 0; 392280c1e1cSGerd Hoffmann } 393280c1e1cSGerd Hoffmann } 394280c1e1cSGerd Hoffmann 395280c1e1cSGerd Hoffmann static void hda_audio_compat_output_cb(void *opaque, int avail) 396280c1e1cSGerd Hoffmann { 397280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 398d61a4ce8SGerd Hoffmann int sent = 0; 399d61a4ce8SGerd Hoffmann int len; 400d61a4ce8SGerd Hoffmann bool rc; 401d61a4ce8SGerd Hoffmann 402280c1e1cSGerd Hoffmann while (avail - sent >= sizeof(st->compat_buf)) { 403280c1e1cSGerd Hoffmann if (st->compat_bpos == sizeof(st->compat_buf)) { 404d61a4ce8SGerd Hoffmann rc = hda_codec_xfer(&st->state->hda, st->stream, true, 405280c1e1cSGerd Hoffmann st->compat_buf, sizeof(st->compat_buf)); 406d61a4ce8SGerd Hoffmann if (!rc) { 407d61a4ce8SGerd Hoffmann break; 408d61a4ce8SGerd Hoffmann } 409280c1e1cSGerd Hoffmann st->compat_bpos = 0; 410d61a4ce8SGerd Hoffmann } 411280c1e1cSGerd Hoffmann len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos, 412280c1e1cSGerd Hoffmann sizeof(st->compat_buf) - st->compat_bpos); 413280c1e1cSGerd Hoffmann st->compat_bpos += len; 414d61a4ce8SGerd Hoffmann sent += len; 415280c1e1cSGerd Hoffmann if (st->compat_bpos != sizeof(st->compat_buf)) { 416d61a4ce8SGerd Hoffmann break; 417d61a4ce8SGerd Hoffmann } 418d61a4ce8SGerd Hoffmann } 419d61a4ce8SGerd Hoffmann } 420d61a4ce8SGerd Hoffmann 421d61a4ce8SGerd Hoffmann static void hda_audio_set_running(HDAAudioStream *st, bool running) 422d61a4ce8SGerd Hoffmann { 423d61a4ce8SGerd Hoffmann if (st->node == NULL) { 424d61a4ce8SGerd Hoffmann return; 425d61a4ce8SGerd Hoffmann } 426d61a4ce8SGerd Hoffmann if (st->running == running) { 427d61a4ce8SGerd Hoffmann return; 428d61a4ce8SGerd Hoffmann } 429d61a4ce8SGerd Hoffmann st->running = running; 4300a373bb3SGerd Hoffmann trace_hda_audio_running(st->node->name, st->stream, st->running); 431280c1e1cSGerd Hoffmann if (st->state->use_timer) { 432280c1e1cSGerd Hoffmann if (running) { 433280c1e1cSGerd Hoffmann int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 434280c1e1cSGerd Hoffmann st->rpos = 0; 435280c1e1cSGerd Hoffmann st->wpos = 0; 436280c1e1cSGerd Hoffmann st->buft_start = now; 437280c1e1cSGerd Hoffmann timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); 438280c1e1cSGerd Hoffmann } else { 439280c1e1cSGerd Hoffmann timer_del(st->buft); 440280c1e1cSGerd Hoffmann } 441280c1e1cSGerd Hoffmann } 442d61a4ce8SGerd Hoffmann if (st->output) { 443d61a4ce8SGerd Hoffmann AUD_set_active_out(st->voice.out, st->running); 444d61a4ce8SGerd Hoffmann } else { 445d61a4ce8SGerd Hoffmann AUD_set_active_in(st->voice.in, st->running); 446d61a4ce8SGerd Hoffmann } 447d61a4ce8SGerd Hoffmann } 448d61a4ce8SGerd Hoffmann 449d61a4ce8SGerd Hoffmann static void hda_audio_set_amp(HDAAudioStream *st) 450d61a4ce8SGerd Hoffmann { 451d61a4ce8SGerd Hoffmann bool muted; 452d61a4ce8SGerd Hoffmann uint32_t left, right; 453d61a4ce8SGerd Hoffmann 454d61a4ce8SGerd Hoffmann if (st->node == NULL) { 455d61a4ce8SGerd Hoffmann return; 456d61a4ce8SGerd Hoffmann } 457d61a4ce8SGerd Hoffmann 458d61a4ce8SGerd Hoffmann muted = st->mute_left && st->mute_right; 459d61a4ce8SGerd Hoffmann left = st->mute_left ? 0 : st->gain_left; 460d61a4ce8SGerd Hoffmann right = st->mute_right ? 0 : st->gain_right; 461d61a4ce8SGerd Hoffmann 462d61a4ce8SGerd Hoffmann left = left * 255 / QEMU_HDA_AMP_STEPS; 463d61a4ce8SGerd Hoffmann right = right * 255 / QEMU_HDA_AMP_STEPS; 464d61a4ce8SGerd Hoffmann 4654843877eSGerd Hoffmann if (!st->state->mixer) { 4664843877eSGerd Hoffmann return; 4674843877eSGerd Hoffmann } 468d61a4ce8SGerd Hoffmann if (st->output) { 469d61a4ce8SGerd Hoffmann AUD_set_volume_out(st->voice.out, muted, left, right); 470d61a4ce8SGerd Hoffmann } else { 471d61a4ce8SGerd Hoffmann AUD_set_volume_in(st->voice.in, muted, left, right); 472d61a4ce8SGerd Hoffmann } 473d61a4ce8SGerd Hoffmann } 474d61a4ce8SGerd Hoffmann 475d61a4ce8SGerd Hoffmann static void hda_audio_setup(HDAAudioStream *st) 476d61a4ce8SGerd Hoffmann { 477280c1e1cSGerd Hoffmann bool use_timer = st->state->use_timer; 478280c1e1cSGerd Hoffmann audio_callback_fn cb; 479280c1e1cSGerd Hoffmann 480d61a4ce8SGerd Hoffmann if (st->node == NULL) { 481d61a4ce8SGerd Hoffmann return; 482d61a4ce8SGerd Hoffmann } 483d61a4ce8SGerd Hoffmann 4840a373bb3SGerd Hoffmann trace_hda_audio_format(st->node->name, st->as.nchannels, 485d61a4ce8SGerd Hoffmann fmt2name[st->as.fmt], st->as.freq); 486d61a4ce8SGerd Hoffmann 487d61a4ce8SGerd Hoffmann if (st->output) { 488280c1e1cSGerd Hoffmann if (use_timer) { 489280c1e1cSGerd Hoffmann cb = hda_audio_output_cb; 490280c1e1cSGerd Hoffmann st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, 491280c1e1cSGerd Hoffmann hda_audio_output_timer, st); 492d61a4ce8SGerd Hoffmann } else { 493280c1e1cSGerd Hoffmann cb = hda_audio_compat_output_cb; 494280c1e1cSGerd Hoffmann } 495280c1e1cSGerd Hoffmann st->voice.out = AUD_open_out(&st->state->card, st->voice.out, 496280c1e1cSGerd Hoffmann st->node->name, st, cb, &st->as); 497280c1e1cSGerd Hoffmann } else { 498280c1e1cSGerd Hoffmann if (use_timer) { 499280c1e1cSGerd Hoffmann cb = hda_audio_input_cb; 500280c1e1cSGerd Hoffmann st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, 501280c1e1cSGerd Hoffmann hda_audio_input_timer, st); 502280c1e1cSGerd Hoffmann } else { 503280c1e1cSGerd Hoffmann cb = hda_audio_compat_input_cb; 504280c1e1cSGerd Hoffmann } 505d61a4ce8SGerd Hoffmann st->voice.in = AUD_open_in(&st->state->card, st->voice.in, 506280c1e1cSGerd Hoffmann st->node->name, st, cb, &st->as); 507d61a4ce8SGerd Hoffmann } 508d61a4ce8SGerd Hoffmann } 509d61a4ce8SGerd Hoffmann 510d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) 511d61a4ce8SGerd Hoffmann { 512cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 513d61a4ce8SGerd Hoffmann HDAAudioStream *st; 514d61a4ce8SGerd Hoffmann const desc_node *node = NULL; 515d61a4ce8SGerd Hoffmann const desc_param *param; 516d61a4ce8SGerd Hoffmann uint32_t verb, payload, response, count, shift; 517d61a4ce8SGerd Hoffmann 518d61a4ce8SGerd Hoffmann if ((data & 0x70000) == 0x70000) { 519d61a4ce8SGerd Hoffmann /* 12/8 id/payload */ 520d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xfff; 521d61a4ce8SGerd Hoffmann payload = data & 0x00ff; 522d61a4ce8SGerd Hoffmann } else { 523d61a4ce8SGerd Hoffmann /* 4/16 id/payload */ 524d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xf00; 525d61a4ce8SGerd Hoffmann payload = data & 0xffff; 526d61a4ce8SGerd Hoffmann } 527d61a4ce8SGerd Hoffmann 528d61a4ce8SGerd Hoffmann node = hda_codec_find_node(a->desc, nid); 529d61a4ce8SGerd Hoffmann if (node == NULL) { 530d61a4ce8SGerd Hoffmann goto fail; 531d61a4ce8SGerd Hoffmann } 532d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n", 533a89f364aSAlistair Francis __func__, nid, node->name, verb, payload); 534d61a4ce8SGerd Hoffmann 535d61a4ce8SGerd Hoffmann switch (verb) { 536d61a4ce8SGerd Hoffmann /* all nodes */ 537d61a4ce8SGerd Hoffmann case AC_VERB_PARAMETERS: 538d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, payload); 539d61a4ce8SGerd Hoffmann if (param == NULL) { 540d61a4ce8SGerd Hoffmann goto fail; 541d61a4ce8SGerd Hoffmann } 542d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, param->val); 543d61a4ce8SGerd Hoffmann break; 544d61a4ce8SGerd Hoffmann case AC_VERB_GET_SUBSYSTEM_ID: 545d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, a->desc->iid); 546d61a4ce8SGerd Hoffmann break; 547d61a4ce8SGerd Hoffmann 548d61a4ce8SGerd Hoffmann /* all functions */ 549d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONNECT_LIST: 550d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN); 551d61a4ce8SGerd Hoffmann count = param ? param->val : 0; 552d61a4ce8SGerd Hoffmann response = 0; 553d61a4ce8SGerd Hoffmann shift = 0; 554d61a4ce8SGerd Hoffmann while (payload < count && shift < 32) { 555d61a4ce8SGerd Hoffmann response |= node->conn[payload] << shift; 556d61a4ce8SGerd Hoffmann payload++; 557d61a4ce8SGerd Hoffmann shift += 8; 558d61a4ce8SGerd Hoffmann } 559d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 560d61a4ce8SGerd Hoffmann break; 561d61a4ce8SGerd Hoffmann 562d61a4ce8SGerd Hoffmann /* pin widget */ 563d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONFIG_DEFAULT: 564d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->config); 565d61a4ce8SGerd Hoffmann break; 566d61a4ce8SGerd Hoffmann case AC_VERB_GET_PIN_WIDGET_CONTROL: 567d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->pinctl); 568d61a4ce8SGerd Hoffmann break; 569d61a4ce8SGerd Hoffmann case AC_VERB_SET_PIN_WIDGET_CONTROL: 570d61a4ce8SGerd Hoffmann if (node->pinctl != payload) { 571d61a4ce8SGerd Hoffmann dprint(a, 1, "unhandled pin control bit\n"); 572d61a4ce8SGerd Hoffmann } 573d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 574d61a4ce8SGerd Hoffmann break; 575d61a4ce8SGerd Hoffmann 576d61a4ce8SGerd Hoffmann /* audio in/out widget */ 577d61a4ce8SGerd Hoffmann case AC_VERB_SET_CHANNEL_STREAMID: 578d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 579d61a4ce8SGerd Hoffmann if (st->node == NULL) { 580d61a4ce8SGerd Hoffmann goto fail; 581d61a4ce8SGerd Hoffmann } 582d61a4ce8SGerd Hoffmann hda_audio_set_running(st, false); 583d61a4ce8SGerd Hoffmann st->stream = (payload >> 4) & 0x0f; 584d61a4ce8SGerd Hoffmann st->channel = payload & 0x0f; 585d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: stream %d, channel %d\n", 586d61a4ce8SGerd Hoffmann st->node->name, st->stream, st->channel); 587ba43d289SMarc-André Lureau hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); 588d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 589d61a4ce8SGerd Hoffmann break; 590d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONV: 591d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 592d61a4ce8SGerd Hoffmann if (st->node == NULL) { 593d61a4ce8SGerd Hoffmann goto fail; 594d61a4ce8SGerd Hoffmann } 595d61a4ce8SGerd Hoffmann response = st->stream << 4 | st->channel; 596d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 597d61a4ce8SGerd Hoffmann break; 598d61a4ce8SGerd Hoffmann case AC_VERB_SET_STREAM_FORMAT: 599d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 600d61a4ce8SGerd Hoffmann if (st->node == NULL) { 601d61a4ce8SGerd Hoffmann goto fail; 602d61a4ce8SGerd Hoffmann } 603d61a4ce8SGerd Hoffmann st->format = payload; 604d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 605d61a4ce8SGerd Hoffmann hda_audio_setup(st); 606d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 607d61a4ce8SGerd Hoffmann break; 608d61a4ce8SGerd Hoffmann case AC_VERB_GET_STREAM_FORMAT: 609d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 610d61a4ce8SGerd Hoffmann if (st->node == NULL) { 611d61a4ce8SGerd Hoffmann goto fail; 612d61a4ce8SGerd Hoffmann } 613d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, st->format); 614d61a4ce8SGerd Hoffmann break; 615d61a4ce8SGerd Hoffmann case AC_VERB_GET_AMP_GAIN_MUTE: 616d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 617d61a4ce8SGerd Hoffmann if (st->node == NULL) { 618d61a4ce8SGerd Hoffmann goto fail; 619d61a4ce8SGerd Hoffmann } 620d61a4ce8SGerd Hoffmann if (payload & AC_AMP_GET_LEFT) { 621d61a4ce8SGerd Hoffmann response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0); 622d61a4ce8SGerd Hoffmann } else { 623d61a4ce8SGerd Hoffmann response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0); 624d61a4ce8SGerd Hoffmann } 625d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response); 626d61a4ce8SGerd Hoffmann break; 627d61a4ce8SGerd Hoffmann case AC_VERB_SET_AMP_GAIN_MUTE: 628d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 629d61a4ce8SGerd Hoffmann if (st->node == NULL) { 630d61a4ce8SGerd Hoffmann goto fail; 631d61a4ce8SGerd Hoffmann } 632d61a4ce8SGerd Hoffmann dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n", 633d61a4ce8SGerd Hoffmann st->node->name, 634d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_OUTPUT) ? "o" : "-", 635d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INPUT) ? "i" : "-", 636d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_LEFT) ? "l" : "-", 637d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_RIGHT) ? "r" : "-", 638d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT, 639d61a4ce8SGerd Hoffmann (payload & AC_AMP_GAIN), 640d61a4ce8SGerd Hoffmann (payload & AC_AMP_MUTE) ? "muted" : ""); 641d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_LEFT) { 642d61a4ce8SGerd Hoffmann st->gain_left = payload & AC_AMP_GAIN; 643d61a4ce8SGerd Hoffmann st->mute_left = payload & AC_AMP_MUTE; 644d61a4ce8SGerd Hoffmann } 645d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_RIGHT) { 646d61a4ce8SGerd Hoffmann st->gain_right = payload & AC_AMP_GAIN; 647d61a4ce8SGerd Hoffmann st->mute_right = payload & AC_AMP_MUTE; 648d61a4ce8SGerd Hoffmann } 649d61a4ce8SGerd Hoffmann hda_audio_set_amp(st); 650d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 651d61a4ce8SGerd Hoffmann break; 652d61a4ce8SGerd Hoffmann 653d61a4ce8SGerd Hoffmann /* not supported */ 654d61a4ce8SGerd Hoffmann case AC_VERB_SET_POWER_STATE: 655d61a4ce8SGerd Hoffmann case AC_VERB_GET_POWER_STATE: 656d61a4ce8SGerd Hoffmann case AC_VERB_GET_SDI_SELECT: 657d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 658d61a4ce8SGerd Hoffmann break; 659d61a4ce8SGerd Hoffmann default: 660d61a4ce8SGerd Hoffmann goto fail; 661d61a4ce8SGerd Hoffmann } 662d61a4ce8SGerd Hoffmann return; 663d61a4ce8SGerd Hoffmann 664d61a4ce8SGerd Hoffmann fail: 665d61a4ce8SGerd Hoffmann dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n", 666a89f364aSAlistair Francis __func__, nid, node ? node->name : "?", verb, payload); 667d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0); 668d61a4ce8SGerd Hoffmann } 669d61a4ce8SGerd Hoffmann 670ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output) 671d61a4ce8SGerd Hoffmann { 672cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 673d61a4ce8SGerd Hoffmann int s; 674d61a4ce8SGerd Hoffmann 675ba43d289SMarc-André Lureau a->running_compat[stnr] = running; 676ba43d289SMarc-André Lureau a->running_real[output * 16 + stnr] = running; 677d61a4ce8SGerd Hoffmann for (s = 0; s < ARRAY_SIZE(a->st); s++) { 678d61a4ce8SGerd Hoffmann if (a->st[s].node == NULL) { 679d61a4ce8SGerd Hoffmann continue; 680d61a4ce8SGerd Hoffmann } 681ba43d289SMarc-André Lureau if (a->st[s].output != output) { 682ba43d289SMarc-André Lureau continue; 683ba43d289SMarc-André Lureau } 684d61a4ce8SGerd Hoffmann if (a->st[s].stream != stnr) { 685d61a4ce8SGerd Hoffmann continue; 686d61a4ce8SGerd Hoffmann } 687d61a4ce8SGerd Hoffmann hda_audio_set_running(&a->st[s], running); 688d61a4ce8SGerd Hoffmann } 689d61a4ce8SGerd Hoffmann } 690d61a4ce8SGerd Hoffmann 691b7639b7dSMartin Kletzander static void hda_audio_init(HDACodecDevice *hda, 692b7639b7dSMartin Kletzander const struct desc_codec *desc, 693b7639b7dSMartin Kletzander Error **errp) 694d61a4ce8SGerd Hoffmann { 695cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 696d61a4ce8SGerd Hoffmann HDAAudioStream *st; 697d61a4ce8SGerd Hoffmann const desc_node *node; 698d61a4ce8SGerd Hoffmann const desc_param *param; 699d61a4ce8SGerd Hoffmann uint32_t i, type; 700d61a4ce8SGerd Hoffmann 701cb94ff5fSMartin Kletzander if (!AUD_register_card("hda", &a->card, errp)) { 702cb94ff5fSMartin Kletzander return; 703cb94ff5fSMartin Kletzander } 704cb94ff5fSMartin Kletzander 705d61a4ce8SGerd Hoffmann a->desc = desc; 706f79f2bfcSAnthony Liguori a->name = object_get_typename(OBJECT(a)); 707a89f364aSAlistair Francis dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad); 708d61a4ce8SGerd Hoffmann 709d61a4ce8SGerd Hoffmann for (i = 0; i < a->desc->nnodes; i++) { 710d61a4ce8SGerd Hoffmann node = a->desc->nodes + i; 711d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP); 7122ab5bf67SGonglei if (param == NULL) { 713d61a4ce8SGerd Hoffmann continue; 7142ab5bf67SGonglei } 715d61a4ce8SGerd Hoffmann type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; 716d61a4ce8SGerd Hoffmann switch (type) { 717d61a4ce8SGerd Hoffmann case AC_WID_AUD_OUT: 718d61a4ce8SGerd Hoffmann case AC_WID_AUD_IN: 719d61a4ce8SGerd Hoffmann assert(node->stindex < ARRAY_SIZE(a->st)); 720d61a4ce8SGerd Hoffmann st = a->st + node->stindex; 721d61a4ce8SGerd Hoffmann st->state = a; 722d61a4ce8SGerd Hoffmann st->node = node; 723d61a4ce8SGerd Hoffmann if (type == AC_WID_AUD_OUT) { 724d61a4ce8SGerd Hoffmann /* unmute output by default */ 725d61a4ce8SGerd Hoffmann st->gain_left = QEMU_HDA_AMP_STEPS; 726d61a4ce8SGerd Hoffmann st->gain_right = QEMU_HDA_AMP_STEPS; 727280c1e1cSGerd Hoffmann st->compat_bpos = sizeof(st->compat_buf); 728d61a4ce8SGerd Hoffmann st->output = true; 729d61a4ce8SGerd Hoffmann } else { 730d61a4ce8SGerd Hoffmann st->output = false; 731d61a4ce8SGerd Hoffmann } 732d61a4ce8SGerd Hoffmann st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 | 733d61a4ce8SGerd Hoffmann (1 << AC_FMT_CHAN_SHIFT); 734d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 735d61a4ce8SGerd Hoffmann hda_audio_setup(st); 736d61a4ce8SGerd Hoffmann break; 737d61a4ce8SGerd Hoffmann } 738d61a4ce8SGerd Hoffmann } 739d61a4ce8SGerd Hoffmann } 740d61a4ce8SGerd Hoffmann 7415eaa8e1eSZihan Yang static void hda_audio_exit(HDACodecDevice *hda) 742129dcd2cSGerd Hoffmann { 743cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 744129dcd2cSGerd Hoffmann HDAAudioStream *st; 745129dcd2cSGerd Hoffmann int i; 746129dcd2cSGerd Hoffmann 747a89f364aSAlistair Francis dprint(a, 1, "%s\n", __func__); 748129dcd2cSGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 749129dcd2cSGerd Hoffmann st = a->st + i; 750129dcd2cSGerd Hoffmann if (st->node == NULL) { 751129dcd2cSGerd Hoffmann continue; 752129dcd2cSGerd Hoffmann } 753280c1e1cSGerd Hoffmann if (a->use_timer) { 754280c1e1cSGerd Hoffmann timer_del(st->buft); 755280c1e1cSGerd Hoffmann } 756129dcd2cSGerd Hoffmann if (st->output) { 757129dcd2cSGerd Hoffmann AUD_close_out(&a->card, st->voice.out); 758129dcd2cSGerd Hoffmann } else { 759129dcd2cSGerd Hoffmann AUD_close_in(&a->card, st->voice.in); 760129dcd2cSGerd Hoffmann } 761129dcd2cSGerd Hoffmann } 762129dcd2cSGerd Hoffmann AUD_remove_card(&a->card); 763129dcd2cSGerd Hoffmann } 764129dcd2cSGerd Hoffmann 765d61a4ce8SGerd Hoffmann static int hda_audio_post_load(void *opaque, int version) 766d61a4ce8SGerd Hoffmann { 767d61a4ce8SGerd Hoffmann HDAAudioState *a = opaque; 768d61a4ce8SGerd Hoffmann HDAAudioStream *st; 769d61a4ce8SGerd Hoffmann int i; 770d61a4ce8SGerd Hoffmann 771a89f364aSAlistair Francis dprint(a, 1, "%s\n", __func__); 772ba43d289SMarc-André Lureau if (version == 1) { 773ba43d289SMarc-André Lureau /* assume running_compat[] is for output streams */ 774ba43d289SMarc-André Lureau for (i = 0; i < ARRAY_SIZE(a->running_compat); i++) 775ba43d289SMarc-André Lureau a->running_real[16 + i] = a->running_compat[i]; 776ba43d289SMarc-André Lureau } 777ba43d289SMarc-André Lureau 778d61a4ce8SGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 779d61a4ce8SGerd Hoffmann st = a->st + i; 780d61a4ce8SGerd Hoffmann if (st->node == NULL) 781d61a4ce8SGerd Hoffmann continue; 782d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as); 783d61a4ce8SGerd Hoffmann hda_audio_setup(st); 784d61a4ce8SGerd Hoffmann hda_audio_set_amp(st); 785ba43d289SMarc-André Lureau hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); 786d61a4ce8SGerd Hoffmann } 787d61a4ce8SGerd Hoffmann return 0; 788d61a4ce8SGerd Hoffmann } 789d61a4ce8SGerd Hoffmann 79039e6a38cSGerd Hoffmann static void hda_audio_reset(DeviceState *dev) 79139e6a38cSGerd Hoffmann { 792cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(dev); 79339e6a38cSGerd Hoffmann HDAAudioStream *st; 79439e6a38cSGerd Hoffmann int i; 79539e6a38cSGerd Hoffmann 79639e6a38cSGerd Hoffmann dprint(a, 1, "%s\n", __func__); 79739e6a38cSGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) { 79839e6a38cSGerd Hoffmann st = a->st + i; 79939e6a38cSGerd Hoffmann if (st->node != NULL) { 80039e6a38cSGerd Hoffmann hda_audio_set_running(st, false); 80139e6a38cSGerd Hoffmann } 80239e6a38cSGerd Hoffmann } 80339e6a38cSGerd Hoffmann } 80439e6a38cSGerd Hoffmann 805280c1e1cSGerd Hoffmann static bool vmstate_hda_audio_stream_buf_needed(void *opaque) 806280c1e1cSGerd Hoffmann { 807280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque; 80867fa1f57SDr. David Alan Gilbert return st->state && st->state->use_timer; 809280c1e1cSGerd Hoffmann } 810280c1e1cSGerd Hoffmann 811280c1e1cSGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream_buf = { 812280c1e1cSGerd Hoffmann .name = "hda-audio-stream/buffer", 813280c1e1cSGerd Hoffmann .version_id = 1, 814280c1e1cSGerd Hoffmann .needed = vmstate_hda_audio_stream_buf_needed, 815280c1e1cSGerd Hoffmann .fields = (VMStateField[]) { 816280c1e1cSGerd Hoffmann VMSTATE_BUFFER(buf, HDAAudioStream), 817280c1e1cSGerd Hoffmann VMSTATE_INT64(rpos, HDAAudioStream), 818280c1e1cSGerd Hoffmann VMSTATE_INT64(wpos, HDAAudioStream), 819280c1e1cSGerd Hoffmann VMSTATE_TIMER_PTR(buft, HDAAudioStream), 820280c1e1cSGerd Hoffmann VMSTATE_INT64(buft_start, HDAAudioStream), 821280c1e1cSGerd Hoffmann VMSTATE_END_OF_LIST() 822280c1e1cSGerd Hoffmann } 823280c1e1cSGerd Hoffmann }; 824280c1e1cSGerd Hoffmann 825d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = { 826d61a4ce8SGerd Hoffmann .name = "hda-audio-stream", 827d61a4ce8SGerd Hoffmann .version_id = 1, 828d61a4ce8SGerd Hoffmann .fields = (VMStateField[]) { 829d61a4ce8SGerd Hoffmann VMSTATE_UINT32(stream, HDAAudioStream), 830d61a4ce8SGerd Hoffmann VMSTATE_UINT32(channel, HDAAudioStream), 831d61a4ce8SGerd Hoffmann VMSTATE_UINT32(format, HDAAudioStream), 832d61a4ce8SGerd Hoffmann VMSTATE_UINT32(gain_left, HDAAudioStream), 833d61a4ce8SGerd Hoffmann VMSTATE_UINT32(gain_right, HDAAudioStream), 834d61a4ce8SGerd Hoffmann VMSTATE_BOOL(mute_left, HDAAudioStream), 835d61a4ce8SGerd Hoffmann VMSTATE_BOOL(mute_right, HDAAudioStream), 836280c1e1cSGerd Hoffmann VMSTATE_UINT32(compat_bpos, HDAAudioStream), 837280c1e1cSGerd Hoffmann VMSTATE_BUFFER(compat_buf, HDAAudioStream), 838d61a4ce8SGerd Hoffmann VMSTATE_END_OF_LIST() 839280c1e1cSGerd Hoffmann }, 840280c1e1cSGerd Hoffmann .subsections = (const VMStateDescription * []) { 841280c1e1cSGerd Hoffmann &vmstate_hda_audio_stream_buf, 842280c1e1cSGerd Hoffmann NULL 843d61a4ce8SGerd Hoffmann } 844d61a4ce8SGerd Hoffmann }; 845d61a4ce8SGerd Hoffmann 846d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio = { 847d61a4ce8SGerd Hoffmann .name = "hda-audio", 848ba43d289SMarc-André Lureau .version_id = 2, 849d61a4ce8SGerd Hoffmann .post_load = hda_audio_post_load, 850d61a4ce8SGerd Hoffmann .fields = (VMStateField[]) { 851d61a4ce8SGerd Hoffmann VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, 852d61a4ce8SGerd Hoffmann vmstate_hda_audio_stream, 853d61a4ce8SGerd Hoffmann HDAAudioStream), 854ba43d289SMarc-André Lureau VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16), 855ba43d289SMarc-André Lureau VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2), 856d61a4ce8SGerd Hoffmann VMSTATE_END_OF_LIST() 857d61a4ce8SGerd Hoffmann } 858d61a4ce8SGerd Hoffmann }; 859d61a4ce8SGerd Hoffmann 860d61a4ce8SGerd Hoffmann static Property hda_audio_properties[] = { 86188e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(HDAAudioState, card), 862d61a4ce8SGerd Hoffmann DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), 8632690e61eSBandan Das DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), 864bc753dc0SGerd Hoffmann DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true), 865d61a4ce8SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 866d61a4ce8SGerd Hoffmann }; 867d61a4ce8SGerd Hoffmann 868b7639b7dSMartin Kletzander static void hda_audio_init_output(HDACodecDevice *hda, Error **errp) 869d61a4ce8SGerd Hoffmann { 870cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 871b7639b7dSMartin Kletzander const struct desc_codec *desc = &output_nomixemu; 8722690e61eSBandan Das 8732690e61eSBandan Das if (!a->mixer) { 874b7639b7dSMartin Kletzander desc = &output_mixemu; 875d61a4ce8SGerd Hoffmann } 876d61a4ce8SGerd Hoffmann 877b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp); 878b7639b7dSMartin Kletzander } 879b7639b7dSMartin Kletzander 880b7639b7dSMartin Kletzander static void hda_audio_init_duplex(HDACodecDevice *hda, Error **errp) 881d61a4ce8SGerd Hoffmann { 882cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 883b7639b7dSMartin Kletzander const struct desc_codec *desc = &duplex_nomixemu; 8842690e61eSBandan Das 8852690e61eSBandan Das if (!a->mixer) { 886b7639b7dSMartin Kletzander desc = &duplex_mixemu; 887d61a4ce8SGerd Hoffmann } 888d61a4ce8SGerd Hoffmann 889b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp); 890b7639b7dSMartin Kletzander } 891b7639b7dSMartin Kletzander 892b7639b7dSMartin Kletzander static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp) 89320110065SGerd Hoffmann { 894cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda); 895b7639b7dSMartin Kletzander const struct desc_codec *desc = µ_nomixemu; 8962690e61eSBandan Das 8972690e61eSBandan Das if (!a->mixer) { 898b7639b7dSMartin Kletzander desc = µ_mixemu; 8992690e61eSBandan Das } 900b7639b7dSMartin Kletzander 901b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp); 90220110065SGerd Hoffmann } 90320110065SGerd Hoffmann 904cd6c8830SGerd Hoffmann static void hda_audio_base_class_init(ObjectClass *klass, void *data) 905cd6c8830SGerd Hoffmann { 906cd6c8830SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 907cd6c8830SGerd Hoffmann HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 908cd6c8830SGerd Hoffmann 909cd6c8830SGerd Hoffmann k->exit = hda_audio_exit; 910cd6c8830SGerd Hoffmann k->command = hda_audio_command; 911cd6c8830SGerd Hoffmann k->stream = hda_audio_stream; 912cd6c8830SGerd Hoffmann set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 913cd6c8830SGerd Hoffmann dc->reset = hda_audio_reset; 914cd6c8830SGerd Hoffmann dc->vmsd = &vmstate_hda_audio; 9154f67d30bSMarc-André Lureau device_class_set_props(dc, hda_audio_properties); 916cd6c8830SGerd Hoffmann } 917cd6c8830SGerd Hoffmann 918cd6c8830SGerd Hoffmann static const TypeInfo hda_audio_info = { 919cd6c8830SGerd Hoffmann .name = TYPE_HDA_AUDIO, 920cd6c8830SGerd Hoffmann .parent = TYPE_HDA_CODEC_DEVICE, 921edf632eeSEduardo Habkost .instance_size = sizeof(HDAAudioState), 922cd6c8830SGerd Hoffmann .class_init = hda_audio_base_class_init, 923cd6c8830SGerd Hoffmann .abstract = true, 924cd6c8830SGerd Hoffmann }; 925cd6c8830SGerd Hoffmann 926dbaa7904SAnthony Liguori static void hda_audio_output_class_init(ObjectClass *klass, void *data) 927dbaa7904SAnthony Liguori { 92839bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 929dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 930dbaa7904SAnthony Liguori 931dbaa7904SAnthony Liguori k->init = hda_audio_init_output; 93220110065SGerd Hoffmann dc->desc = "HDA Audio Codec, output-only (line-out)"; 933dbaa7904SAnthony Liguori } 934dbaa7904SAnthony Liguori 9358c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = { 936dbaa7904SAnthony Liguori .name = "hda-output", 937cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO, 938dbaa7904SAnthony Liguori .class_init = hda_audio_output_class_init, 939d61a4ce8SGerd Hoffmann }; 940d61a4ce8SGerd Hoffmann 941dbaa7904SAnthony Liguori static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) 942dbaa7904SAnthony Liguori { 94339bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 944dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 945dbaa7904SAnthony Liguori 946dbaa7904SAnthony Liguori k->init = hda_audio_init_duplex; 94720110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (line-out, line-in)"; 948dbaa7904SAnthony Liguori } 949dbaa7904SAnthony Liguori 9508c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = { 951dbaa7904SAnthony Liguori .name = "hda-duplex", 952cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO, 953dbaa7904SAnthony Liguori .class_init = hda_audio_duplex_class_init, 954d61a4ce8SGerd Hoffmann }; 955d61a4ce8SGerd Hoffmann 95620110065SGerd Hoffmann static void hda_audio_micro_class_init(ObjectClass *klass, void *data) 95720110065SGerd Hoffmann { 95820110065SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 95920110065SGerd Hoffmann HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); 96020110065SGerd Hoffmann 96120110065SGerd Hoffmann k->init = hda_audio_init_micro; 96220110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (speaker, microphone)"; 96320110065SGerd Hoffmann } 96420110065SGerd Hoffmann 9658c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = { 96620110065SGerd Hoffmann .name = "hda-micro", 967cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO, 96820110065SGerd Hoffmann .class_init = hda_audio_micro_class_init, 96920110065SGerd Hoffmann }; 97020110065SGerd Hoffmann 97183f7d43aSAndreas Färber static void hda_audio_register_types(void) 972d61a4ce8SGerd Hoffmann { 973cd6c8830SGerd Hoffmann type_register_static(&hda_audio_info); 97439bffca2SAnthony Liguori type_register_static(&hda_audio_output_info); 97539bffca2SAnthony Liguori type_register_static(&hda_audio_duplex_info); 97620110065SGerd Hoffmann type_register_static(&hda_audio_micro_info); 977d61a4ce8SGerd Hoffmann } 97883f7d43aSAndreas Färber 97983f7d43aSAndreas Färber type_init(hda_audio_register_types) 980