149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * Copyright (C) 2010 Red Hat, Inc.
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * written by Gerd Hoffmann <kraxel@redhat.com>
549ab747fSPaolo Bonzini *
649ab747fSPaolo Bonzini * This program is free software; you can redistribute it and/or
749ab747fSPaolo Bonzini * modify it under the terms of the GNU General Public License as
849ab747fSPaolo Bonzini * published by the Free Software Foundation; either version 2 or
949ab747fSPaolo Bonzini * (at your option) version 3 of the License.
1049ab747fSPaolo Bonzini *
1149ab747fSPaolo Bonzini * This program is distributed in the hope that it will be useful,
1249ab747fSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of
1349ab747fSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1449ab747fSPaolo Bonzini * GNU General Public License for more details.
1549ab747fSPaolo Bonzini *
1649ab747fSPaolo Bonzini * You should have received a copy of the GNU General Public License
1749ab747fSPaolo Bonzini * along with this program; if not, see <http://www.gnu.org/licenses/>.
1849ab747fSPaolo Bonzini */
1949ab747fSPaolo Bonzini
206086a565SPeter Maydell #include "qemu/osdep.h"
2149ab747fSPaolo 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"
2574e8593eSVolker Rümelin #include "qemu/host-utils.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
2747b43a1fSPaolo Bonzini #include "intel-hda-defs.h"
2849ab747fSPaolo Bonzini #include "audio/audio.h"
290a373bb3SGerd Hoffmann #include "trace.h"
30db1015e9SEduardo Habkost #include "qom/object.h"
3149ab747fSPaolo Bonzini
3249ab747fSPaolo Bonzini /* -------------------------------------------------------------------------- */
3349ab747fSPaolo Bonzini
3449ab747fSPaolo Bonzini typedef struct desc_param {
3549ab747fSPaolo Bonzini uint32_t id;
3649ab747fSPaolo Bonzini uint32_t val;
3749ab747fSPaolo Bonzini } desc_param;
3849ab747fSPaolo Bonzini
3949ab747fSPaolo Bonzini typedef struct desc_node {
4049ab747fSPaolo Bonzini uint32_t nid;
4149ab747fSPaolo Bonzini const char *name;
4249ab747fSPaolo Bonzini const desc_param *params;
4349ab747fSPaolo Bonzini uint32_t nparams;
4449ab747fSPaolo Bonzini uint32_t config;
4549ab747fSPaolo Bonzini uint32_t pinctl;
4649ab747fSPaolo Bonzini uint32_t *conn;
4749ab747fSPaolo Bonzini uint32_t stindex;
4849ab747fSPaolo Bonzini } desc_node;
4949ab747fSPaolo Bonzini
5049ab747fSPaolo Bonzini typedef struct desc_codec {
5149ab747fSPaolo Bonzini const char *name;
5249ab747fSPaolo Bonzini uint32_t iid;
5349ab747fSPaolo Bonzini const desc_node *nodes;
5449ab747fSPaolo Bonzini uint32_t nnodes;
5549ab747fSPaolo Bonzini } desc_codec;
5649ab747fSPaolo Bonzini
hda_codec_find_param(const desc_node * node,uint32_t id)5749ab747fSPaolo Bonzini static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
5849ab747fSPaolo Bonzini {
5949ab747fSPaolo Bonzini int i;
6049ab747fSPaolo Bonzini
6149ab747fSPaolo Bonzini for (i = 0; i < node->nparams; i++) {
6249ab747fSPaolo Bonzini if (node->params[i].id == id) {
6349ab747fSPaolo Bonzini return &node->params[i];
6449ab747fSPaolo Bonzini }
6549ab747fSPaolo Bonzini }
6649ab747fSPaolo Bonzini return NULL;
6749ab747fSPaolo Bonzini }
6849ab747fSPaolo Bonzini
hda_codec_find_node(const desc_codec * codec,uint32_t nid)6949ab747fSPaolo Bonzini static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
7049ab747fSPaolo Bonzini {
7149ab747fSPaolo Bonzini int i;
7249ab747fSPaolo Bonzini
7349ab747fSPaolo Bonzini for (i = 0; i < codec->nnodes; i++) {
7449ab747fSPaolo Bonzini if (codec->nodes[i].nid == nid) {
7549ab747fSPaolo Bonzini return &codec->nodes[i];
7649ab747fSPaolo Bonzini }
7749ab747fSPaolo Bonzini }
7849ab747fSPaolo Bonzini return NULL;
7949ab747fSPaolo Bonzini }
8049ab747fSPaolo Bonzini
hda_codec_parse_fmt(uint32_t format,struct audsettings * as)8149ab747fSPaolo Bonzini static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
8249ab747fSPaolo Bonzini {
8349ab747fSPaolo Bonzini if (format & AC_FMT_TYPE_NON_PCM) {
8449ab747fSPaolo Bonzini return;
8549ab747fSPaolo Bonzini }
8649ab747fSPaolo Bonzini
8749ab747fSPaolo Bonzini as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
8849ab747fSPaolo Bonzini
8949ab747fSPaolo Bonzini switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
9049ab747fSPaolo Bonzini case 1: as->freq *= 2; break;
9149ab747fSPaolo Bonzini case 2: as->freq *= 3; break;
9249ab747fSPaolo Bonzini case 3: as->freq *= 4; break;
9349ab747fSPaolo Bonzini }
9449ab747fSPaolo Bonzini
9549ab747fSPaolo Bonzini switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
9649ab747fSPaolo Bonzini case 1: as->freq /= 2; break;
9749ab747fSPaolo Bonzini case 2: as->freq /= 3; break;
9849ab747fSPaolo Bonzini case 3: as->freq /= 4; break;
9949ab747fSPaolo Bonzini case 4: as->freq /= 5; break;
10049ab747fSPaolo Bonzini case 5: as->freq /= 6; break;
10149ab747fSPaolo Bonzini case 6: as->freq /= 7; break;
10249ab747fSPaolo Bonzini case 7: as->freq /= 8; break;
10349ab747fSPaolo Bonzini }
10449ab747fSPaolo Bonzini
10549ab747fSPaolo Bonzini 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;
10949ab747fSPaolo Bonzini }
11049ab747fSPaolo Bonzini
11149ab747fSPaolo Bonzini as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
11249ab747fSPaolo Bonzini }
11349ab747fSPaolo Bonzini
11449ab747fSPaolo Bonzini /* -------------------------------------------------------------------------- */
11549ab747fSPaolo Bonzini /*
11649ab747fSPaolo Bonzini * HDA codec descriptions
11749ab747fSPaolo Bonzini */
11849ab747fSPaolo Bonzini
11949ab747fSPaolo Bonzini /* some defines */
12049ab747fSPaolo Bonzini
12149ab747fSPaolo Bonzini #define QEMU_HDA_ID_VENDOR 0x1af4
12249ab747fSPaolo Bonzini #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \
12349ab747fSPaolo Bonzini 0x1fc /* 16 -> 96 kHz */)
12449ab747fSPaolo Bonzini #define QEMU_HDA_AMP_NONE (0)
12549ab747fSPaolo Bonzini #define QEMU_HDA_AMP_STEPS 0x4a
12649ab747fSPaolo Bonzini
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
13849ab747fSPaolo Bonzini /* -------------------------------------------------------------------------- */
13949ab747fSPaolo Bonzini
14049ab747fSPaolo Bonzini 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",
14749ab747fSPaolo Bonzini };
14849ab747fSPaolo Bonzini
149acab7d60SPhilippe Mathieu-Daudé #define TYPE_HDA_AUDIO "hda-audio"
150acab7d60SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO)
151acab7d60SPhilippe Mathieu-Daudé
15249ab747fSPaolo Bonzini typedef struct HDAAudioStream HDAAudioStream;
15349ab747fSPaolo Bonzini
15449ab747fSPaolo Bonzini struct HDAAudioStream {
15549ab747fSPaolo Bonzini HDAAudioState *state;
15649ab747fSPaolo Bonzini const desc_node *node;
15749ab747fSPaolo Bonzini bool output, running;
15849ab747fSPaolo Bonzini uint32_t stream;
15949ab747fSPaolo Bonzini uint32_t channel;
16049ab747fSPaolo Bonzini uint32_t format;
16149ab747fSPaolo Bonzini uint32_t gain_left, gain_right;
16249ab747fSPaolo Bonzini bool mute_left, mute_right;
16349ab747fSPaolo Bonzini struct audsettings as;
16449ab747fSPaolo Bonzini union {
16549ab747fSPaolo Bonzini SWVoiceIn *in;
16649ab747fSPaolo Bonzini SWVoiceOut *out;
16749ab747fSPaolo Bonzini } 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;
17549ab747fSPaolo Bonzini };
17649ab747fSPaolo Bonzini
17749ab747fSPaolo Bonzini struct HDAAudioState {
17849ab747fSPaolo Bonzini HDACodecDevice hda;
17949ab747fSPaolo Bonzini const char *name;
18049ab747fSPaolo Bonzini
18149ab747fSPaolo Bonzini QEMUSoundCard card;
18249ab747fSPaolo Bonzini const desc_codec *desc;
18349ab747fSPaolo Bonzini HDAAudioStream st[4];
18449ab747fSPaolo Bonzini bool running_compat[16];
18549ab747fSPaolo Bonzini bool running_real[2 * 16];
18649ab747fSPaolo Bonzini
18749ab747fSPaolo Bonzini /* properties */
18849ab747fSPaolo Bonzini uint32_t debug;
1892690e61eSBandan Das bool mixer;
190280c1e1cSGerd Hoffmann bool use_timer;
19149ab747fSPaolo Bonzini };
19249ab747fSPaolo Bonzini
hda_bytes_per_second(HDAAudioStream * st)19374e8593eSVolker Rümelin static inline uint32_t hda_bytes_per_second(HDAAudioStream *st)
194280c1e1cSGerd Hoffmann {
19574e8593eSVolker Rümelin return 2 * (uint32_t)st->as.nchannels * (uint32_t)st->as.freq;
196280c1e1cSGerd Hoffmann }
197280c1e1cSGerd Hoffmann
hda_timer_sync_adjust(HDAAudioStream * st,int64_t target_pos)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
hda_audio_input_timer(void * opaque)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
22674e8593eSVolker Rümelin int64_t uptime = now - st->buft_start;
2273b84967cSGerd Hoffmann int64_t wpos = st->wpos;
2283b84967cSGerd Hoffmann int64_t rpos = st->rpos;
22974e8593eSVolker Rümelin int64_t wanted_rpos;
230280c1e1cSGerd Hoffmann
23174e8593eSVolker Rümelin if (uptime <= 0) {
23274e8593eSVolker Rümelin /* wanted_rpos <= 0 */
23374e8593eSVolker Rümelin goto out_timer;
23474e8593eSVolker Rümelin }
23574e8593eSVolker Rümelin
23674e8593eSVolker Rümelin wanted_rpos = muldiv64(uptime, hda_bytes_per_second(st),
23774e8593eSVolker 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
hda_audio_input_cb(void * opaque,int avail)26649ab747fSPaolo Bonzini static void hda_audio_input_cb(void *opaque, int avail)
26749ab747fSPaolo Bonzini {
26849ab747fSPaolo Bonzini HDAAudioStream *st = opaque;
26949ab747fSPaolo Bonzini
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) {
28349ab747fSPaolo Bonzini break;
28449ab747fSPaolo Bonzini }
28549ab747fSPaolo Bonzini }
286c435fea7SVolker Rümelin
287c435fea7SVolker Rümelin hda_timer_sync_adjust(st, -((wpos - rpos) - (B_SIZE >> 1)));
288280c1e1cSGerd Hoffmann }
289280c1e1cSGerd Hoffmann
hda_audio_output_timer(void * opaque)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
29674e8593eSVolker Rümelin int64_t uptime = now - st->buft_start;
2973b84967cSGerd Hoffmann int64_t wpos = st->wpos;
2983b84967cSGerd Hoffmann int64_t rpos = st->rpos;
29974e8593eSVolker Rümelin int64_t wanted_wpos;
300280c1e1cSGerd Hoffmann
30174e8593eSVolker Rümelin if (uptime <= 0) {
30274e8593eSVolker Rümelin /* wanted_wpos <= 0 */
30374e8593eSVolker Rümelin goto out_timer;
30474e8593eSVolker Rümelin }
30574e8593eSVolker Rümelin
30674e8593eSVolker Rümelin wanted_wpos = muldiv64(uptime, hda_bytes_per_second(st),
30774e8593eSVolker 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);
32149ab747fSPaolo Bonzini if (!rc) {
32249ab747fSPaolo Bonzini break;
32349ab747fSPaolo Bonzini }
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);
33349ab747fSPaolo Bonzini }
33449ab747fSPaolo Bonzini }
33549ab747fSPaolo Bonzini
hda_audio_output_cb(void * opaque,int avail)33649ab747fSPaolo Bonzini static void hda_audio_output_cb(void *opaque, int avail)
33749ab747fSPaolo Bonzini {
33849ab747fSPaolo Bonzini 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
hda_audio_compat_input_cb(void * opaque,int avail)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
hda_audio_compat_output_cb(void * opaque,int avail)395280c1e1cSGerd Hoffmann static void hda_audio_compat_output_cb(void *opaque, int avail)
396280c1e1cSGerd Hoffmann {
397280c1e1cSGerd Hoffmann HDAAudioStream *st = opaque;
39849ab747fSPaolo Bonzini int sent = 0;
39949ab747fSPaolo Bonzini int len;
40049ab747fSPaolo Bonzini bool rc;
40149ab747fSPaolo Bonzini
402280c1e1cSGerd Hoffmann while (avail - sent >= sizeof(st->compat_buf)) {
403280c1e1cSGerd Hoffmann if (st->compat_bpos == sizeof(st->compat_buf)) {
40449ab747fSPaolo Bonzini rc = hda_codec_xfer(&st->state->hda, st->stream, true,
405280c1e1cSGerd Hoffmann st->compat_buf, sizeof(st->compat_buf));
40649ab747fSPaolo Bonzini if (!rc) {
40749ab747fSPaolo Bonzini break;
40849ab747fSPaolo Bonzini }
409280c1e1cSGerd Hoffmann st->compat_bpos = 0;
41049ab747fSPaolo Bonzini }
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;
41449ab747fSPaolo Bonzini sent += len;
415280c1e1cSGerd Hoffmann if (st->compat_bpos != sizeof(st->compat_buf)) {
41649ab747fSPaolo Bonzini break;
41749ab747fSPaolo Bonzini }
41849ab747fSPaolo Bonzini }
41949ab747fSPaolo Bonzini }
42049ab747fSPaolo Bonzini
hda_audio_set_running(HDAAudioStream * st,bool running)42149ab747fSPaolo Bonzini static void hda_audio_set_running(HDAAudioStream *st, bool running)
42249ab747fSPaolo Bonzini {
42349ab747fSPaolo Bonzini if (st->node == NULL) {
42449ab747fSPaolo Bonzini return;
42549ab747fSPaolo Bonzini }
42649ab747fSPaolo Bonzini if (st->running == running) {
42749ab747fSPaolo Bonzini return;
42849ab747fSPaolo Bonzini }
42949ab747fSPaolo Bonzini 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 }
44249ab747fSPaolo Bonzini if (st->output) {
44349ab747fSPaolo Bonzini AUD_set_active_out(st->voice.out, st->running);
44449ab747fSPaolo Bonzini } else {
44549ab747fSPaolo Bonzini AUD_set_active_in(st->voice.in, st->running);
44649ab747fSPaolo Bonzini }
44749ab747fSPaolo Bonzini }
44849ab747fSPaolo Bonzini
hda_audio_set_amp(HDAAudioStream * st)44949ab747fSPaolo Bonzini static void hda_audio_set_amp(HDAAudioStream *st)
45049ab747fSPaolo Bonzini {
45149ab747fSPaolo Bonzini bool muted;
45249ab747fSPaolo Bonzini uint32_t left, right;
45349ab747fSPaolo Bonzini
45449ab747fSPaolo Bonzini if (st->node == NULL) {
45549ab747fSPaolo Bonzini return;
45649ab747fSPaolo Bonzini }
45749ab747fSPaolo Bonzini
45849ab747fSPaolo Bonzini muted = st->mute_left && st->mute_right;
45949ab747fSPaolo Bonzini left = st->mute_left ? 0 : st->gain_left;
46049ab747fSPaolo Bonzini right = st->mute_right ? 0 : st->gain_right;
46149ab747fSPaolo Bonzini
46249ab747fSPaolo Bonzini left = left * 255 / QEMU_HDA_AMP_STEPS;
46349ab747fSPaolo Bonzini right = right * 255 / QEMU_HDA_AMP_STEPS;
46449ab747fSPaolo Bonzini
4654843877eSGerd Hoffmann if (!st->state->mixer) {
4664843877eSGerd Hoffmann return;
4674843877eSGerd Hoffmann }
46849ab747fSPaolo Bonzini if (st->output) {
46949ab747fSPaolo Bonzini AUD_set_volume_out(st->voice.out, muted, left, right);
47049ab747fSPaolo Bonzini } else {
47149ab747fSPaolo Bonzini AUD_set_volume_in(st->voice.in, muted, left, right);
47249ab747fSPaolo Bonzini }
47349ab747fSPaolo Bonzini }
47449ab747fSPaolo Bonzini
hda_audio_setup(HDAAudioStream * st)47549ab747fSPaolo Bonzini static void hda_audio_setup(HDAAudioStream *st)
47649ab747fSPaolo Bonzini {
477280c1e1cSGerd Hoffmann bool use_timer = st->state->use_timer;
478280c1e1cSGerd Hoffmann audio_callback_fn cb;
479280c1e1cSGerd Hoffmann
48049ab747fSPaolo Bonzini if (st->node == NULL) {
48149ab747fSPaolo Bonzini return;
48249ab747fSPaolo Bonzini }
48349ab747fSPaolo Bonzini
4840a373bb3SGerd Hoffmann trace_hda_audio_format(st->node->name, st->as.nchannels,
48549ab747fSPaolo Bonzini fmt2name[st->as.fmt], st->as.freq);
48649ab747fSPaolo Bonzini
48749ab747fSPaolo Bonzini if (st->output) {
488280c1e1cSGerd Hoffmann if (use_timer) {
489280c1e1cSGerd Hoffmann cb = hda_audio_output_cb;
490*a7b7953dSPaolo Bonzini timer_del(st->buft);
49149ab747fSPaolo Bonzini } else {
492280c1e1cSGerd Hoffmann cb = hda_audio_compat_output_cb;
493280c1e1cSGerd Hoffmann }
494280c1e1cSGerd Hoffmann st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
495280c1e1cSGerd Hoffmann st->node->name, st, cb, &st->as);
496280c1e1cSGerd Hoffmann } else {
497280c1e1cSGerd Hoffmann if (use_timer) {
498280c1e1cSGerd Hoffmann cb = hda_audio_input_cb;
499*a7b7953dSPaolo Bonzini timer_del(st->buft);
500280c1e1cSGerd Hoffmann } else {
501280c1e1cSGerd Hoffmann cb = hda_audio_compat_input_cb;
502280c1e1cSGerd Hoffmann }
50349ab747fSPaolo Bonzini st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
504280c1e1cSGerd Hoffmann st->node->name, st, cb, &st->as);
50549ab747fSPaolo Bonzini }
50649ab747fSPaolo Bonzini }
50749ab747fSPaolo Bonzini
hda_audio_command(HDACodecDevice * hda,uint32_t nid,uint32_t data)50849ab747fSPaolo Bonzini static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
50949ab747fSPaolo Bonzini {
510cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
51149ab747fSPaolo Bonzini HDAAudioStream *st;
51249ab747fSPaolo Bonzini const desc_node *node = NULL;
51349ab747fSPaolo Bonzini const desc_param *param;
51449ab747fSPaolo Bonzini uint32_t verb, payload, response, count, shift;
51549ab747fSPaolo Bonzini
51649ab747fSPaolo Bonzini if ((data & 0x70000) == 0x70000) {
51749ab747fSPaolo Bonzini /* 12/8 id/payload */
51849ab747fSPaolo Bonzini verb = (data >> 8) & 0xfff;
51949ab747fSPaolo Bonzini payload = data & 0x00ff;
52049ab747fSPaolo Bonzini } else {
52149ab747fSPaolo Bonzini /* 4/16 id/payload */
52249ab747fSPaolo Bonzini verb = (data >> 8) & 0xf00;
52349ab747fSPaolo Bonzini payload = data & 0xffff;
52449ab747fSPaolo Bonzini }
52549ab747fSPaolo Bonzini
52649ab747fSPaolo Bonzini node = hda_codec_find_node(a->desc, nid);
52749ab747fSPaolo Bonzini if (node == NULL) {
52849ab747fSPaolo Bonzini goto fail;
52949ab747fSPaolo Bonzini }
53049ab747fSPaolo Bonzini dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
531a89f364aSAlistair Francis __func__, nid, node->name, verb, payload);
53249ab747fSPaolo Bonzini
53349ab747fSPaolo Bonzini switch (verb) {
53449ab747fSPaolo Bonzini /* all nodes */
53549ab747fSPaolo Bonzini case AC_VERB_PARAMETERS:
53649ab747fSPaolo Bonzini param = hda_codec_find_param(node, payload);
53749ab747fSPaolo Bonzini if (param == NULL) {
53849ab747fSPaolo Bonzini goto fail;
53949ab747fSPaolo Bonzini }
54049ab747fSPaolo Bonzini hda_codec_response(hda, true, param->val);
54149ab747fSPaolo Bonzini break;
54249ab747fSPaolo Bonzini case AC_VERB_GET_SUBSYSTEM_ID:
54349ab747fSPaolo Bonzini hda_codec_response(hda, true, a->desc->iid);
54449ab747fSPaolo Bonzini break;
54549ab747fSPaolo Bonzini
54649ab747fSPaolo Bonzini /* all functions */
54749ab747fSPaolo Bonzini case AC_VERB_GET_CONNECT_LIST:
54849ab747fSPaolo Bonzini param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
54949ab747fSPaolo Bonzini count = param ? param->val : 0;
55049ab747fSPaolo Bonzini response = 0;
55149ab747fSPaolo Bonzini shift = 0;
55249ab747fSPaolo Bonzini while (payload < count && shift < 32) {
55349ab747fSPaolo Bonzini response |= node->conn[payload] << shift;
55449ab747fSPaolo Bonzini payload++;
55549ab747fSPaolo Bonzini shift += 8;
55649ab747fSPaolo Bonzini }
55749ab747fSPaolo Bonzini hda_codec_response(hda, true, response);
55849ab747fSPaolo Bonzini break;
55949ab747fSPaolo Bonzini
56049ab747fSPaolo Bonzini /* pin widget */
56149ab747fSPaolo Bonzini case AC_VERB_GET_CONFIG_DEFAULT:
56249ab747fSPaolo Bonzini hda_codec_response(hda, true, node->config);
56349ab747fSPaolo Bonzini break;
56449ab747fSPaolo Bonzini case AC_VERB_GET_PIN_WIDGET_CONTROL:
56549ab747fSPaolo Bonzini hda_codec_response(hda, true, node->pinctl);
56649ab747fSPaolo Bonzini break;
56749ab747fSPaolo Bonzini case AC_VERB_SET_PIN_WIDGET_CONTROL:
56849ab747fSPaolo Bonzini if (node->pinctl != payload) {
56949ab747fSPaolo Bonzini dprint(a, 1, "unhandled pin control bit\n");
57049ab747fSPaolo Bonzini }
57149ab747fSPaolo Bonzini hda_codec_response(hda, true, 0);
57249ab747fSPaolo Bonzini break;
57349ab747fSPaolo Bonzini
57449ab747fSPaolo Bonzini /* audio in/out widget */
57549ab747fSPaolo Bonzini case AC_VERB_SET_CHANNEL_STREAMID:
57649ab747fSPaolo Bonzini st = a->st + node->stindex;
57749ab747fSPaolo Bonzini if (st->node == NULL) {
57849ab747fSPaolo Bonzini goto fail;
57949ab747fSPaolo Bonzini }
58049ab747fSPaolo Bonzini hda_audio_set_running(st, false);
58149ab747fSPaolo Bonzini st->stream = (payload >> 4) & 0x0f;
58249ab747fSPaolo Bonzini st->channel = payload & 0x0f;
58349ab747fSPaolo Bonzini dprint(a, 2, "%s: stream %d, channel %d\n",
58449ab747fSPaolo Bonzini st->node->name, st->stream, st->channel);
58549ab747fSPaolo Bonzini hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
58649ab747fSPaolo Bonzini hda_codec_response(hda, true, 0);
58749ab747fSPaolo Bonzini break;
58849ab747fSPaolo Bonzini case AC_VERB_GET_CONV:
58949ab747fSPaolo Bonzini st = a->st + node->stindex;
59049ab747fSPaolo Bonzini if (st->node == NULL) {
59149ab747fSPaolo Bonzini goto fail;
59249ab747fSPaolo Bonzini }
59349ab747fSPaolo Bonzini response = st->stream << 4 | st->channel;
59449ab747fSPaolo Bonzini hda_codec_response(hda, true, response);
59549ab747fSPaolo Bonzini break;
59649ab747fSPaolo Bonzini case AC_VERB_SET_STREAM_FORMAT:
59749ab747fSPaolo Bonzini st = a->st + node->stindex;
59849ab747fSPaolo Bonzini if (st->node == NULL) {
59949ab747fSPaolo Bonzini goto fail;
60049ab747fSPaolo Bonzini }
60149ab747fSPaolo Bonzini st->format = payload;
60249ab747fSPaolo Bonzini hda_codec_parse_fmt(st->format, &st->as);
60349ab747fSPaolo Bonzini hda_audio_setup(st);
60449ab747fSPaolo Bonzini hda_codec_response(hda, true, 0);
60549ab747fSPaolo Bonzini break;
60649ab747fSPaolo Bonzini case AC_VERB_GET_STREAM_FORMAT:
60749ab747fSPaolo Bonzini st = a->st + node->stindex;
60849ab747fSPaolo Bonzini if (st->node == NULL) {
60949ab747fSPaolo Bonzini goto fail;
61049ab747fSPaolo Bonzini }
61149ab747fSPaolo Bonzini hda_codec_response(hda, true, st->format);
61249ab747fSPaolo Bonzini break;
61349ab747fSPaolo Bonzini case AC_VERB_GET_AMP_GAIN_MUTE:
61449ab747fSPaolo Bonzini st = a->st + node->stindex;
61549ab747fSPaolo Bonzini if (st->node == NULL) {
61649ab747fSPaolo Bonzini goto fail;
61749ab747fSPaolo Bonzini }
61849ab747fSPaolo Bonzini if (payload & AC_AMP_GET_LEFT) {
61949ab747fSPaolo Bonzini response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
62049ab747fSPaolo Bonzini } else {
62149ab747fSPaolo Bonzini response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
62249ab747fSPaolo Bonzini }
62349ab747fSPaolo Bonzini hda_codec_response(hda, true, response);
62449ab747fSPaolo Bonzini break;
62549ab747fSPaolo Bonzini case AC_VERB_SET_AMP_GAIN_MUTE:
62649ab747fSPaolo Bonzini st = a->st + node->stindex;
62749ab747fSPaolo Bonzini if (st->node == NULL) {
62849ab747fSPaolo Bonzini goto fail;
62949ab747fSPaolo Bonzini }
63049ab747fSPaolo Bonzini dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n",
63149ab747fSPaolo Bonzini st->node->name,
63249ab747fSPaolo Bonzini (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
63349ab747fSPaolo Bonzini (payload & AC_AMP_SET_INPUT) ? "i" : "-",
63449ab747fSPaolo Bonzini (payload & AC_AMP_SET_LEFT) ? "l" : "-",
63549ab747fSPaolo Bonzini (payload & AC_AMP_SET_RIGHT) ? "r" : "-",
63649ab747fSPaolo Bonzini (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
63749ab747fSPaolo Bonzini (payload & AC_AMP_GAIN),
63849ab747fSPaolo Bonzini (payload & AC_AMP_MUTE) ? "muted" : "");
63949ab747fSPaolo Bonzini if (payload & AC_AMP_SET_LEFT) {
64049ab747fSPaolo Bonzini st->gain_left = payload & AC_AMP_GAIN;
64149ab747fSPaolo Bonzini st->mute_left = payload & AC_AMP_MUTE;
64249ab747fSPaolo Bonzini }
64349ab747fSPaolo Bonzini if (payload & AC_AMP_SET_RIGHT) {
64449ab747fSPaolo Bonzini st->gain_right = payload & AC_AMP_GAIN;
64549ab747fSPaolo Bonzini st->mute_right = payload & AC_AMP_MUTE;
64649ab747fSPaolo Bonzini }
64749ab747fSPaolo Bonzini hda_audio_set_amp(st);
64849ab747fSPaolo Bonzini hda_codec_response(hda, true, 0);
64949ab747fSPaolo Bonzini break;
65049ab747fSPaolo Bonzini
65149ab747fSPaolo Bonzini /* not supported */
65249ab747fSPaolo Bonzini case AC_VERB_SET_POWER_STATE:
65349ab747fSPaolo Bonzini case AC_VERB_GET_POWER_STATE:
65449ab747fSPaolo Bonzini case AC_VERB_GET_SDI_SELECT:
65549ab747fSPaolo Bonzini hda_codec_response(hda, true, 0);
65649ab747fSPaolo Bonzini break;
65749ab747fSPaolo Bonzini default:
65849ab747fSPaolo Bonzini goto fail;
65949ab747fSPaolo Bonzini }
66049ab747fSPaolo Bonzini return;
66149ab747fSPaolo Bonzini
66249ab747fSPaolo Bonzini fail:
66349ab747fSPaolo Bonzini dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
664a89f364aSAlistair Francis __func__, nid, node ? node->name : "?", verb, payload);
66549ab747fSPaolo Bonzini hda_codec_response(hda, true, 0);
66649ab747fSPaolo Bonzini }
66749ab747fSPaolo Bonzini
hda_audio_stream(HDACodecDevice * hda,uint32_t stnr,bool running,bool output)66849ab747fSPaolo Bonzini static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
66949ab747fSPaolo Bonzini {
670cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
67149ab747fSPaolo Bonzini int s;
67249ab747fSPaolo Bonzini
67349ab747fSPaolo Bonzini a->running_compat[stnr] = running;
67449ab747fSPaolo Bonzini a->running_real[output * 16 + stnr] = running;
67549ab747fSPaolo Bonzini for (s = 0; s < ARRAY_SIZE(a->st); s++) {
67649ab747fSPaolo Bonzini if (a->st[s].node == NULL) {
67749ab747fSPaolo Bonzini continue;
67849ab747fSPaolo Bonzini }
67949ab747fSPaolo Bonzini if (a->st[s].output != output) {
68049ab747fSPaolo Bonzini continue;
68149ab747fSPaolo Bonzini }
68249ab747fSPaolo Bonzini if (a->st[s].stream != stnr) {
68349ab747fSPaolo Bonzini continue;
68449ab747fSPaolo Bonzini }
68549ab747fSPaolo Bonzini hda_audio_set_running(&a->st[s], running);
68649ab747fSPaolo Bonzini }
68749ab747fSPaolo Bonzini }
68849ab747fSPaolo Bonzini
hda_audio_init(HDACodecDevice * hda,const struct desc_codec * desc,Error ** errp)689b7639b7dSMartin Kletzander static void hda_audio_init(HDACodecDevice *hda,
690b7639b7dSMartin Kletzander const struct desc_codec *desc,
691b7639b7dSMartin Kletzander Error **errp)
69249ab747fSPaolo Bonzini {
693cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
69449ab747fSPaolo Bonzini HDAAudioStream *st;
69549ab747fSPaolo Bonzini const desc_node *node;
69649ab747fSPaolo Bonzini const desc_param *param;
69749ab747fSPaolo Bonzini uint32_t i, type;
69849ab747fSPaolo Bonzini
699cb94ff5fSMartin Kletzander if (!AUD_register_card("hda", &a->card, errp)) {
700cb94ff5fSMartin Kletzander return;
701cb94ff5fSMartin Kletzander }
702cb94ff5fSMartin Kletzander
70349ab747fSPaolo Bonzini a->desc = desc;
70449ab747fSPaolo Bonzini a->name = object_get_typename(OBJECT(a));
705a89f364aSAlistair Francis dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad);
70649ab747fSPaolo Bonzini
70749ab747fSPaolo Bonzini for (i = 0; i < a->desc->nnodes; i++) {
70849ab747fSPaolo Bonzini node = a->desc->nodes + i;
70949ab747fSPaolo Bonzini param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
7102ab5bf67SGonglei if (param == NULL) {
71149ab747fSPaolo Bonzini continue;
7122ab5bf67SGonglei }
71349ab747fSPaolo Bonzini type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
71449ab747fSPaolo Bonzini switch (type) {
71549ab747fSPaolo Bonzini case AC_WID_AUD_OUT:
71649ab747fSPaolo Bonzini case AC_WID_AUD_IN:
71749ab747fSPaolo Bonzini assert(node->stindex < ARRAY_SIZE(a->st));
71849ab747fSPaolo Bonzini st = a->st + node->stindex;
71949ab747fSPaolo Bonzini st->state = a;
72049ab747fSPaolo Bonzini st->node = node;
72149ab747fSPaolo Bonzini if (type == AC_WID_AUD_OUT) {
72249ab747fSPaolo Bonzini /* unmute output by default */
72349ab747fSPaolo Bonzini st->gain_left = QEMU_HDA_AMP_STEPS;
72449ab747fSPaolo Bonzini st->gain_right = QEMU_HDA_AMP_STEPS;
725280c1e1cSGerd Hoffmann st->compat_bpos = sizeof(st->compat_buf);
72649ab747fSPaolo Bonzini st->output = true;
727*a7b7953dSPaolo Bonzini st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
728*a7b7953dSPaolo Bonzini hda_audio_output_timer, st);
72949ab747fSPaolo Bonzini } else {
73049ab747fSPaolo Bonzini st->output = false;
731*a7b7953dSPaolo Bonzini st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
732*a7b7953dSPaolo Bonzini hda_audio_input_timer, st);
73349ab747fSPaolo Bonzini }
73449ab747fSPaolo Bonzini st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
73549ab747fSPaolo Bonzini (1 << AC_FMT_CHAN_SHIFT);
73649ab747fSPaolo Bonzini hda_codec_parse_fmt(st->format, &st->as);
73749ab747fSPaolo Bonzini hda_audio_setup(st);
73849ab747fSPaolo Bonzini break;
73949ab747fSPaolo Bonzini }
74049ab747fSPaolo Bonzini }
74149ab747fSPaolo Bonzini }
74249ab747fSPaolo Bonzini
hda_audio_exit(HDACodecDevice * hda)7435eaa8e1eSZihan Yang static void hda_audio_exit(HDACodecDevice *hda)
74449ab747fSPaolo Bonzini {
745cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
7468d9c6f6fSPaolo Bonzini HDAAudioStream *st;
74749ab747fSPaolo Bonzini int i;
74849ab747fSPaolo Bonzini
749a89f364aSAlistair Francis dprint(a, 1, "%s\n", __func__);
75049ab747fSPaolo Bonzini for (i = 0; i < ARRAY_SIZE(a->st); i++) {
7518d9c6f6fSPaolo Bonzini st = a->st + i;
7528d9c6f6fSPaolo Bonzini if (st->node == NULL) {
7538d9c6f6fSPaolo Bonzini continue;
7548d9c6f6fSPaolo Bonzini }
7558d9c6f6fSPaolo Bonzini timer_free(st->buft);
7568d9c6f6fSPaolo Bonzini if (st->output) {
7578d9c6f6fSPaolo Bonzini AUD_close_out(&a->card, st->voice.out);
7588d9c6f6fSPaolo Bonzini } else {
7598d9c6f6fSPaolo Bonzini AUD_close_in(&a->card, st->voice.in);
7608d9c6f6fSPaolo Bonzini }
76149ab747fSPaolo Bonzini }
76249ab747fSPaolo Bonzini AUD_remove_card(&a->card);
76349ab747fSPaolo Bonzini }
76449ab747fSPaolo Bonzini
hda_audio_post_load(void * opaque,int version)76549ab747fSPaolo Bonzini static int hda_audio_post_load(void *opaque, int version)
76649ab747fSPaolo Bonzini {
76749ab747fSPaolo Bonzini HDAAudioState *a = opaque;
76849ab747fSPaolo Bonzini HDAAudioStream *st;
76949ab747fSPaolo Bonzini int i;
77049ab747fSPaolo Bonzini
771a89f364aSAlistair Francis dprint(a, 1, "%s\n", __func__);
77249ab747fSPaolo Bonzini if (version == 1) {
77349ab747fSPaolo Bonzini /* assume running_compat[] is for output streams */
77449ab747fSPaolo Bonzini for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
77549ab747fSPaolo Bonzini a->running_real[16 + i] = a->running_compat[i];
77649ab747fSPaolo Bonzini }
77749ab747fSPaolo Bonzini
77849ab747fSPaolo Bonzini for (i = 0; i < ARRAY_SIZE(a->st); i++) {
77949ab747fSPaolo Bonzini st = a->st + i;
78049ab747fSPaolo Bonzini if (st->node == NULL)
78149ab747fSPaolo Bonzini continue;
78249ab747fSPaolo Bonzini hda_codec_parse_fmt(st->format, &st->as);
78349ab747fSPaolo Bonzini hda_audio_setup(st);
78449ab747fSPaolo Bonzini hda_audio_set_amp(st);
78549ab747fSPaolo Bonzini hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
78649ab747fSPaolo Bonzini }
78749ab747fSPaolo Bonzini return 0;
78849ab747fSPaolo Bonzini }
78949ab747fSPaolo Bonzini
hda_audio_reset(DeviceState * dev)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
vmstate_hda_audio_stream_buf_needed(void * opaque)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,
815856a6fe4SRichard Henderson .fields = (const 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
82549ab747fSPaolo Bonzini static const VMStateDescription vmstate_hda_audio_stream = {
82649ab747fSPaolo Bonzini .name = "hda-audio-stream",
82749ab747fSPaolo Bonzini .version_id = 1,
828856a6fe4SRichard Henderson .fields = (const VMStateField[]) {
82949ab747fSPaolo Bonzini VMSTATE_UINT32(stream, HDAAudioStream),
83049ab747fSPaolo Bonzini VMSTATE_UINT32(channel, HDAAudioStream),
83149ab747fSPaolo Bonzini VMSTATE_UINT32(format, HDAAudioStream),
83249ab747fSPaolo Bonzini VMSTATE_UINT32(gain_left, HDAAudioStream),
83349ab747fSPaolo Bonzini VMSTATE_UINT32(gain_right, HDAAudioStream),
83449ab747fSPaolo Bonzini VMSTATE_BOOL(mute_left, HDAAudioStream),
83549ab747fSPaolo Bonzini VMSTATE_BOOL(mute_right, HDAAudioStream),
836280c1e1cSGerd Hoffmann VMSTATE_UINT32(compat_bpos, HDAAudioStream),
837280c1e1cSGerd Hoffmann VMSTATE_BUFFER(compat_buf, HDAAudioStream),
83849ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
839280c1e1cSGerd Hoffmann },
840856a6fe4SRichard Henderson .subsections = (const VMStateDescription * const []) {
841280c1e1cSGerd Hoffmann &vmstate_hda_audio_stream_buf,
842280c1e1cSGerd Hoffmann NULL
84349ab747fSPaolo Bonzini }
84449ab747fSPaolo Bonzini };
84549ab747fSPaolo Bonzini
84649ab747fSPaolo Bonzini static const VMStateDescription vmstate_hda_audio = {
84749ab747fSPaolo Bonzini .name = "hda-audio",
84849ab747fSPaolo Bonzini .version_id = 2,
84949ab747fSPaolo Bonzini .post_load = hda_audio_post_load,
850856a6fe4SRichard Henderson .fields = (const VMStateField[]) {
85149ab747fSPaolo Bonzini VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
85249ab747fSPaolo Bonzini vmstate_hda_audio_stream,
85349ab747fSPaolo Bonzini HDAAudioStream),
85449ab747fSPaolo Bonzini VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
85549ab747fSPaolo Bonzini VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
85649ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
85749ab747fSPaolo Bonzini }
85849ab747fSPaolo Bonzini };
85949ab747fSPaolo Bonzini
86049ab747fSPaolo Bonzini static Property hda_audio_properties[] = {
86188e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(HDAAudioState, card),
86249ab747fSPaolo Bonzini 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),
86549ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST(),
86649ab747fSPaolo Bonzini };
86749ab747fSPaolo Bonzini
hda_audio_init_output(HDACodecDevice * hda,Error ** errp)868b7639b7dSMartin Kletzander static void hda_audio_init_output(HDACodecDevice *hda, Error **errp)
86949ab747fSPaolo Bonzini {
870cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
871e60bdfb7SVolker Rümelin const struct desc_codec *desc = &output_mixemu;
8722690e61eSBandan Das
8732690e61eSBandan Das if (!a->mixer) {
874e60bdfb7SVolker Rümelin desc = &output_nomixemu;
87549ab747fSPaolo Bonzini }
87649ab747fSPaolo Bonzini
877b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp);
878b7639b7dSMartin Kletzander }
879b7639b7dSMartin Kletzander
hda_audio_init_duplex(HDACodecDevice * hda,Error ** errp)880b7639b7dSMartin Kletzander static void hda_audio_init_duplex(HDACodecDevice *hda, Error **errp)
88149ab747fSPaolo Bonzini {
882cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
883e60bdfb7SVolker Rümelin const struct desc_codec *desc = &duplex_mixemu;
8842690e61eSBandan Das
8852690e61eSBandan Das if (!a->mixer) {
886e60bdfb7SVolker Rümelin desc = &duplex_nomixemu;
88749ab747fSPaolo Bonzini }
88849ab747fSPaolo Bonzini
889b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp);
890b7639b7dSMartin Kletzander }
891b7639b7dSMartin Kletzander
hda_audio_init_micro(HDACodecDevice * hda,Error ** errp)892b7639b7dSMartin Kletzander static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp)
89349ab747fSPaolo Bonzini {
894cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
895e60bdfb7SVolker Rümelin const struct desc_codec *desc = µ_mixemu;
8962690e61eSBandan Das
8972690e61eSBandan Das if (!a->mixer) {
898e60bdfb7SVolker Rümelin desc = µ_nomixemu;
8992690e61eSBandan Das }
900b7639b7dSMartin Kletzander
901b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp);
90249ab747fSPaolo Bonzini }
90349ab747fSPaolo Bonzini
hda_audio_base_class_init(ObjectClass * klass,void * data)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 device_class_set_legacy_reset(dc, 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
hda_audio_output_class_init(ObjectClass * klass,void * data)92649ab747fSPaolo Bonzini static void hda_audio_output_class_init(ObjectClass *klass, void *data)
92749ab747fSPaolo Bonzini {
92849ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
92949ab747fSPaolo Bonzini HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
93049ab747fSPaolo Bonzini
93149ab747fSPaolo Bonzini k->init = hda_audio_init_output;
93249ab747fSPaolo Bonzini dc->desc = "HDA Audio Codec, output-only (line-out)";
93349ab747fSPaolo Bonzini }
93449ab747fSPaolo Bonzini
93549ab747fSPaolo Bonzini static const TypeInfo hda_audio_output_info = {
93649ab747fSPaolo Bonzini .name = "hda-output",
937cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO,
93849ab747fSPaolo Bonzini .class_init = hda_audio_output_class_init,
93949ab747fSPaolo Bonzini };
94049ab747fSPaolo Bonzini
hda_audio_duplex_class_init(ObjectClass * klass,void * data)94149ab747fSPaolo Bonzini static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
94249ab747fSPaolo Bonzini {
94349ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
94449ab747fSPaolo Bonzini HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
94549ab747fSPaolo Bonzini
94649ab747fSPaolo Bonzini k->init = hda_audio_init_duplex;
94749ab747fSPaolo Bonzini dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
94849ab747fSPaolo Bonzini }
94949ab747fSPaolo Bonzini
95049ab747fSPaolo Bonzini static const TypeInfo hda_audio_duplex_info = {
95149ab747fSPaolo Bonzini .name = "hda-duplex",
952cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO,
95349ab747fSPaolo Bonzini .class_init = hda_audio_duplex_class_init,
95449ab747fSPaolo Bonzini };
95549ab747fSPaolo Bonzini
hda_audio_micro_class_init(ObjectClass * klass,void * data)95649ab747fSPaolo Bonzini static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
95749ab747fSPaolo Bonzini {
95849ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
95949ab747fSPaolo Bonzini HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
96049ab747fSPaolo Bonzini
96149ab747fSPaolo Bonzini k->init = hda_audio_init_micro;
96249ab747fSPaolo Bonzini dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
96349ab747fSPaolo Bonzini }
96449ab747fSPaolo Bonzini
96549ab747fSPaolo Bonzini static const TypeInfo hda_audio_micro_info = {
96649ab747fSPaolo Bonzini .name = "hda-micro",
967cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO,
96849ab747fSPaolo Bonzini .class_init = hda_audio_micro_class_init,
96949ab747fSPaolo Bonzini };
97049ab747fSPaolo Bonzini
hda_audio_register_types(void)97149ab747fSPaolo Bonzini static void hda_audio_register_types(void)
97249ab747fSPaolo Bonzini {
973cd6c8830SGerd Hoffmann type_register_static(&hda_audio_info);
97449ab747fSPaolo Bonzini type_register_static(&hda_audio_output_info);
97549ab747fSPaolo Bonzini type_register_static(&hda_audio_duplex_info);
97649ab747fSPaolo Bonzini type_register_static(&hda_audio_micro_info);
97749ab747fSPaolo Bonzini }
97849ab747fSPaolo Bonzini
97949ab747fSPaolo Bonzini type_init(hda_audio_register_types)
980