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"
2574e8593eSVolker 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
hda_codec_find_param(const desc_node * node,uint32_t id)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
hda_codec_find_node(const desc_codec * codec,uint32_t nid)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
hda_codec_parse_fmt(uint32_t format,struct audsettings * as)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
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)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
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);
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
hda_audio_output_cb(void * opaque,int avail)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
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;
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
hda_audio_set_running(HDAAudioStream * st,bool running)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
hda_audio_set_amp(HDAAudioStream * st)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
hda_audio_setup(HDAAudioStream * st)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;
490626b3900SPaolo Bonzini timer_del(st->buft);
491d61a4ce8SGerd Hoffmann } 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;
499626b3900SPaolo Bonzini timer_del(st->buft);
500280c1e1cSGerd Hoffmann } else {
501280c1e1cSGerd Hoffmann cb = hda_audio_compat_input_cb;
502280c1e1cSGerd Hoffmann }
503d61a4ce8SGerd Hoffmann st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
504280c1e1cSGerd Hoffmann st->node->name, st, cb, &st->as);
505d61a4ce8SGerd Hoffmann }
506d61a4ce8SGerd Hoffmann }
507d61a4ce8SGerd Hoffmann
hda_audio_command(HDACodecDevice * hda,uint32_t nid,uint32_t data)508d61a4ce8SGerd Hoffmann static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
509d61a4ce8SGerd Hoffmann {
510cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
511d61a4ce8SGerd Hoffmann HDAAudioStream *st;
512d61a4ce8SGerd Hoffmann const desc_node *node = NULL;
513d61a4ce8SGerd Hoffmann const desc_param *param;
514d61a4ce8SGerd Hoffmann uint32_t verb, payload, response, count, shift;
515d61a4ce8SGerd Hoffmann
516d61a4ce8SGerd Hoffmann if ((data & 0x70000) == 0x70000) {
517d61a4ce8SGerd Hoffmann /* 12/8 id/payload */
518d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xfff;
519d61a4ce8SGerd Hoffmann payload = data & 0x00ff;
520d61a4ce8SGerd Hoffmann } else {
521d61a4ce8SGerd Hoffmann /* 4/16 id/payload */
522d61a4ce8SGerd Hoffmann verb = (data >> 8) & 0xf00;
523d61a4ce8SGerd Hoffmann payload = data & 0xffff;
524d61a4ce8SGerd Hoffmann }
525d61a4ce8SGerd Hoffmann
526d61a4ce8SGerd Hoffmann node = hda_codec_find_node(a->desc, nid);
527d61a4ce8SGerd Hoffmann if (node == NULL) {
528d61a4ce8SGerd Hoffmann goto fail;
529d61a4ce8SGerd Hoffmann }
530d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
531a89f364aSAlistair Francis __func__, nid, node->name, verb, payload);
532d61a4ce8SGerd Hoffmann
533d61a4ce8SGerd Hoffmann switch (verb) {
534d61a4ce8SGerd Hoffmann /* all nodes */
535d61a4ce8SGerd Hoffmann case AC_VERB_PARAMETERS:
536d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, payload);
537d61a4ce8SGerd Hoffmann if (param == NULL) {
538d61a4ce8SGerd Hoffmann goto fail;
539d61a4ce8SGerd Hoffmann }
540d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, param->val);
541d61a4ce8SGerd Hoffmann break;
542d61a4ce8SGerd Hoffmann case AC_VERB_GET_SUBSYSTEM_ID:
543d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, a->desc->iid);
544d61a4ce8SGerd Hoffmann break;
545d61a4ce8SGerd Hoffmann
546d61a4ce8SGerd Hoffmann /* all functions */
547d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONNECT_LIST:
548d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
549d61a4ce8SGerd Hoffmann count = param ? param->val : 0;
550d61a4ce8SGerd Hoffmann response = 0;
551d61a4ce8SGerd Hoffmann shift = 0;
552d61a4ce8SGerd Hoffmann while (payload < count && shift < 32) {
553d61a4ce8SGerd Hoffmann response |= node->conn[payload] << shift;
554d61a4ce8SGerd Hoffmann payload++;
555d61a4ce8SGerd Hoffmann shift += 8;
556d61a4ce8SGerd Hoffmann }
557d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response);
558d61a4ce8SGerd Hoffmann break;
559d61a4ce8SGerd Hoffmann
560d61a4ce8SGerd Hoffmann /* pin widget */
561d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONFIG_DEFAULT:
562d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->config);
563d61a4ce8SGerd Hoffmann break;
564d61a4ce8SGerd Hoffmann case AC_VERB_GET_PIN_WIDGET_CONTROL:
565d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, node->pinctl);
566d61a4ce8SGerd Hoffmann break;
567d61a4ce8SGerd Hoffmann case AC_VERB_SET_PIN_WIDGET_CONTROL:
568d61a4ce8SGerd Hoffmann if (node->pinctl != payload) {
569d61a4ce8SGerd Hoffmann dprint(a, 1, "unhandled pin control bit\n");
570d61a4ce8SGerd Hoffmann }
571d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0);
572d61a4ce8SGerd Hoffmann break;
573d61a4ce8SGerd Hoffmann
574d61a4ce8SGerd Hoffmann /* audio in/out widget */
575d61a4ce8SGerd Hoffmann case AC_VERB_SET_CHANNEL_STREAMID:
576d61a4ce8SGerd Hoffmann st = a->st + node->stindex;
577d61a4ce8SGerd Hoffmann if (st->node == NULL) {
578d61a4ce8SGerd Hoffmann goto fail;
579d61a4ce8SGerd Hoffmann }
580d61a4ce8SGerd Hoffmann hda_audio_set_running(st, false);
581d61a4ce8SGerd Hoffmann st->stream = (payload >> 4) & 0x0f;
582d61a4ce8SGerd Hoffmann st->channel = payload & 0x0f;
583d61a4ce8SGerd Hoffmann dprint(a, 2, "%s: stream %d, channel %d\n",
584d61a4ce8SGerd Hoffmann st->node->name, st->stream, st->channel);
585ba43d289SMarc-André Lureau hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
586d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0);
587d61a4ce8SGerd Hoffmann break;
588d61a4ce8SGerd Hoffmann case AC_VERB_GET_CONV:
589d61a4ce8SGerd Hoffmann st = a->st + node->stindex;
590d61a4ce8SGerd Hoffmann if (st->node == NULL) {
591d61a4ce8SGerd Hoffmann goto fail;
592d61a4ce8SGerd Hoffmann }
593d61a4ce8SGerd Hoffmann response = st->stream << 4 | st->channel;
594d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response);
595d61a4ce8SGerd Hoffmann break;
596d61a4ce8SGerd Hoffmann case AC_VERB_SET_STREAM_FORMAT:
597d61a4ce8SGerd Hoffmann st = a->st + node->stindex;
598d61a4ce8SGerd Hoffmann if (st->node == NULL) {
599d61a4ce8SGerd Hoffmann goto fail;
600d61a4ce8SGerd Hoffmann }
601d61a4ce8SGerd Hoffmann st->format = payload;
602d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as);
603d61a4ce8SGerd Hoffmann hda_audio_setup(st);
604d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0);
605d61a4ce8SGerd Hoffmann break;
606d61a4ce8SGerd Hoffmann case AC_VERB_GET_STREAM_FORMAT:
607d61a4ce8SGerd Hoffmann st = a->st + node->stindex;
608d61a4ce8SGerd Hoffmann if (st->node == NULL) {
609d61a4ce8SGerd Hoffmann goto fail;
610d61a4ce8SGerd Hoffmann }
611d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, st->format);
612d61a4ce8SGerd Hoffmann break;
613d61a4ce8SGerd Hoffmann case AC_VERB_GET_AMP_GAIN_MUTE:
614d61a4ce8SGerd Hoffmann st = a->st + node->stindex;
615d61a4ce8SGerd Hoffmann if (st->node == NULL) {
616d61a4ce8SGerd Hoffmann goto fail;
617d61a4ce8SGerd Hoffmann }
618d61a4ce8SGerd Hoffmann if (payload & AC_AMP_GET_LEFT) {
619d61a4ce8SGerd Hoffmann response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
620d61a4ce8SGerd Hoffmann } else {
621d61a4ce8SGerd Hoffmann response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
622d61a4ce8SGerd Hoffmann }
623d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, response);
624d61a4ce8SGerd Hoffmann break;
625d61a4ce8SGerd Hoffmann case AC_VERB_SET_AMP_GAIN_MUTE:
626d61a4ce8SGerd Hoffmann st = a->st + node->stindex;
627d61a4ce8SGerd Hoffmann if (st->node == NULL) {
628d61a4ce8SGerd Hoffmann goto fail;
629d61a4ce8SGerd Hoffmann }
630d61a4ce8SGerd Hoffmann dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n",
631d61a4ce8SGerd Hoffmann st->node->name,
632d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
633d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INPUT) ? "i" : "-",
634d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_LEFT) ? "l" : "-",
635d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_RIGHT) ? "r" : "-",
636d61a4ce8SGerd Hoffmann (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
637d61a4ce8SGerd Hoffmann (payload & AC_AMP_GAIN),
638d61a4ce8SGerd Hoffmann (payload & AC_AMP_MUTE) ? "muted" : "");
639d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_LEFT) {
640d61a4ce8SGerd Hoffmann st->gain_left = payload & AC_AMP_GAIN;
641d61a4ce8SGerd Hoffmann st->mute_left = payload & AC_AMP_MUTE;
642d61a4ce8SGerd Hoffmann }
643d61a4ce8SGerd Hoffmann if (payload & AC_AMP_SET_RIGHT) {
644d61a4ce8SGerd Hoffmann st->gain_right = payload & AC_AMP_GAIN;
645d61a4ce8SGerd Hoffmann st->mute_right = payload & AC_AMP_MUTE;
646d61a4ce8SGerd Hoffmann }
647d61a4ce8SGerd Hoffmann hda_audio_set_amp(st);
648d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0);
649d61a4ce8SGerd Hoffmann break;
650d61a4ce8SGerd Hoffmann
651d61a4ce8SGerd Hoffmann /* not supported */
652d61a4ce8SGerd Hoffmann case AC_VERB_SET_POWER_STATE:
653d61a4ce8SGerd Hoffmann case AC_VERB_GET_POWER_STATE:
654d61a4ce8SGerd Hoffmann case AC_VERB_GET_SDI_SELECT:
655d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0);
656d61a4ce8SGerd Hoffmann break;
657d61a4ce8SGerd Hoffmann default:
658d61a4ce8SGerd Hoffmann goto fail;
659d61a4ce8SGerd Hoffmann }
660d61a4ce8SGerd Hoffmann return;
661d61a4ce8SGerd Hoffmann
662d61a4ce8SGerd Hoffmann fail:
663d61a4ce8SGerd Hoffmann 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);
665d61a4ce8SGerd Hoffmann hda_codec_response(hda, true, 0);
666d61a4ce8SGerd Hoffmann }
667d61a4ce8SGerd Hoffmann
hda_audio_stream(HDACodecDevice * hda,uint32_t stnr,bool running,bool output)668ba43d289SMarc-André Lureau static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
669d61a4ce8SGerd Hoffmann {
670cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
671d61a4ce8SGerd Hoffmann int s;
672d61a4ce8SGerd Hoffmann
673ba43d289SMarc-André Lureau a->running_compat[stnr] = running;
674ba43d289SMarc-André Lureau a->running_real[output * 16 + stnr] = running;
675d61a4ce8SGerd Hoffmann for (s = 0; s < ARRAY_SIZE(a->st); s++) {
676d61a4ce8SGerd Hoffmann if (a->st[s].node == NULL) {
677d61a4ce8SGerd Hoffmann continue;
678d61a4ce8SGerd Hoffmann }
679ba43d289SMarc-André Lureau if (a->st[s].output != output) {
680ba43d289SMarc-André Lureau continue;
681ba43d289SMarc-André Lureau }
682d61a4ce8SGerd Hoffmann if (a->st[s].stream != stnr) {
683d61a4ce8SGerd Hoffmann continue;
684d61a4ce8SGerd Hoffmann }
685d61a4ce8SGerd Hoffmann hda_audio_set_running(&a->st[s], running);
686d61a4ce8SGerd Hoffmann }
687d61a4ce8SGerd Hoffmann }
688d61a4ce8SGerd Hoffmann
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)
692d61a4ce8SGerd Hoffmann {
693cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
694d61a4ce8SGerd Hoffmann HDAAudioStream *st;
695d61a4ce8SGerd Hoffmann const desc_node *node;
696d61a4ce8SGerd Hoffmann const desc_param *param;
697d61a4ce8SGerd Hoffmann uint32_t i, type;
698d61a4ce8SGerd Hoffmann
699cb94ff5fSMartin Kletzander if (!AUD_register_card("hda", &a->card, errp)) {
700cb94ff5fSMartin Kletzander return;
701cb94ff5fSMartin Kletzander }
702cb94ff5fSMartin Kletzander
703d61a4ce8SGerd Hoffmann a->desc = desc;
704f79f2bfcSAnthony Liguori a->name = object_get_typename(OBJECT(a));
705a89f364aSAlistair Francis dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad);
706d61a4ce8SGerd Hoffmann
707d61a4ce8SGerd Hoffmann for (i = 0; i < a->desc->nnodes; i++) {
708d61a4ce8SGerd Hoffmann node = a->desc->nodes + i;
709d61a4ce8SGerd Hoffmann param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
7102ab5bf67SGonglei if (param == NULL) {
711d61a4ce8SGerd Hoffmann continue;
7122ab5bf67SGonglei }
713d61a4ce8SGerd Hoffmann type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
714d61a4ce8SGerd Hoffmann switch (type) {
715d61a4ce8SGerd Hoffmann case AC_WID_AUD_OUT:
716d61a4ce8SGerd Hoffmann case AC_WID_AUD_IN:
717d61a4ce8SGerd Hoffmann assert(node->stindex < ARRAY_SIZE(a->st));
718d61a4ce8SGerd Hoffmann st = a->st + node->stindex;
719d61a4ce8SGerd Hoffmann st->state = a;
720d61a4ce8SGerd Hoffmann st->node = node;
721d61a4ce8SGerd Hoffmann if (type == AC_WID_AUD_OUT) {
722d61a4ce8SGerd Hoffmann /* unmute output by default */
723d61a4ce8SGerd Hoffmann st->gain_left = QEMU_HDA_AMP_STEPS;
724d61a4ce8SGerd Hoffmann st->gain_right = QEMU_HDA_AMP_STEPS;
725280c1e1cSGerd Hoffmann st->compat_bpos = sizeof(st->compat_buf);
726d61a4ce8SGerd Hoffmann st->output = true;
727626b3900SPaolo Bonzini st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
728626b3900SPaolo Bonzini hda_audio_output_timer, st);
729d61a4ce8SGerd Hoffmann } else {
730d61a4ce8SGerd Hoffmann st->output = false;
731626b3900SPaolo Bonzini st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL,
732626b3900SPaolo Bonzini hda_audio_input_timer, st);
733d61a4ce8SGerd Hoffmann }
734d61a4ce8SGerd Hoffmann st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
735d61a4ce8SGerd Hoffmann (1 << AC_FMT_CHAN_SHIFT);
736d61a4ce8SGerd Hoffmann hda_codec_parse_fmt(st->format, &st->as);
737d61a4ce8SGerd Hoffmann hda_audio_setup(st);
738d61a4ce8SGerd Hoffmann break;
739d61a4ce8SGerd Hoffmann }
740d61a4ce8SGerd Hoffmann }
741d61a4ce8SGerd Hoffmann }
742d61a4ce8SGerd Hoffmann
hda_audio_exit(HDACodecDevice * hda)7435eaa8e1eSZihan Yang static void hda_audio_exit(HDACodecDevice *hda)
744129dcd2cSGerd Hoffmann {
745cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
746e125d983SPaolo Bonzini HDAAudioStream *st;
747129dcd2cSGerd Hoffmann int i;
748129dcd2cSGerd Hoffmann
749a89f364aSAlistair Francis dprint(a, 1, "%s\n", __func__);
750129dcd2cSGerd Hoffmann for (i = 0; i < ARRAY_SIZE(a->st); i++) {
751e125d983SPaolo Bonzini st = a->st + i;
752e125d983SPaolo Bonzini if (st->node == NULL) {
753e125d983SPaolo Bonzini continue;
754e125d983SPaolo Bonzini }
755e125d983SPaolo Bonzini timer_free(st->buft);
756e125d983SPaolo Bonzini if (st->output) {
757e125d983SPaolo Bonzini AUD_close_out(&a->card, st->voice.out);
758e125d983SPaolo Bonzini } else {
759e125d983SPaolo Bonzini AUD_close_in(&a->card, st->voice.in);
760e125d983SPaolo Bonzini }
761129dcd2cSGerd Hoffmann }
762129dcd2cSGerd Hoffmann AUD_remove_card(&a->card);
763129dcd2cSGerd Hoffmann }
764129dcd2cSGerd Hoffmann
hda_audio_post_load(void * opaque,int version)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
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
825d61a4ce8SGerd Hoffmann static const VMStateDescription vmstate_hda_audio_stream = {
826d61a4ce8SGerd Hoffmann .name = "hda-audio-stream",
827d61a4ce8SGerd Hoffmann .version_id = 1,
828856a6fe4SRichard Henderson .fields = (const 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 },
840856a6fe4SRichard Henderson .subsections = (const VMStateDescription * const []) {
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,
850856a6fe4SRichard Henderson .fields = (const 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
860ed1e71daSRichard Henderson static const 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 };
866d61a4ce8SGerd Hoffmann
hda_audio_init_output(HDACodecDevice * hda,Error ** errp)867b7639b7dSMartin Kletzander static void hda_audio_init_output(HDACodecDevice *hda, Error **errp)
868d61a4ce8SGerd Hoffmann {
869cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
870e60bdfb7SVolker Rümelin const struct desc_codec *desc = &output_mixemu;
8712690e61eSBandan Das
8722690e61eSBandan Das if (!a->mixer) {
873e60bdfb7SVolker Rümelin desc = &output_nomixemu;
874d61a4ce8SGerd Hoffmann }
875d61a4ce8SGerd Hoffmann
876b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp);
877b7639b7dSMartin Kletzander }
878b7639b7dSMartin Kletzander
hda_audio_init_duplex(HDACodecDevice * hda,Error ** errp)879b7639b7dSMartin Kletzander static void hda_audio_init_duplex(HDACodecDevice *hda, Error **errp)
880d61a4ce8SGerd Hoffmann {
881cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
882e60bdfb7SVolker Rümelin const struct desc_codec *desc = &duplex_mixemu;
8832690e61eSBandan Das
8842690e61eSBandan Das if (!a->mixer) {
885e60bdfb7SVolker Rümelin desc = &duplex_nomixemu;
886d61a4ce8SGerd Hoffmann }
887d61a4ce8SGerd Hoffmann
888b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp);
889b7639b7dSMartin Kletzander }
890b7639b7dSMartin Kletzander
hda_audio_init_micro(HDACodecDevice * hda,Error ** errp)891b7639b7dSMartin Kletzander static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp)
89220110065SGerd Hoffmann {
893cd6c8830SGerd Hoffmann HDAAudioState *a = HDA_AUDIO(hda);
894e60bdfb7SVolker Rümelin const struct desc_codec *desc = µ_mixemu;
8952690e61eSBandan Das
8962690e61eSBandan Das if (!a->mixer) {
897e60bdfb7SVolker Rümelin desc = µ_nomixemu;
8982690e61eSBandan Das }
899b7639b7dSMartin Kletzander
900b7639b7dSMartin Kletzander hda_audio_init(hda, desc, errp);
90120110065SGerd Hoffmann }
90220110065SGerd Hoffmann
hda_audio_base_class_init(ObjectClass * klass,const void * data)903*12d1a768SPhilippe Mathieu-Daudé static void hda_audio_base_class_init(ObjectClass *klass, const void *data)
904cd6c8830SGerd Hoffmann {
905cd6c8830SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass);
906cd6c8830SGerd Hoffmann HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
907cd6c8830SGerd Hoffmann
908cd6c8830SGerd Hoffmann k->exit = hda_audio_exit;
909cd6c8830SGerd Hoffmann k->command = hda_audio_command;
910cd6c8830SGerd Hoffmann k->stream = hda_audio_stream;
911cd6c8830SGerd Hoffmann set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
912e3d08143SPeter Maydell device_class_set_legacy_reset(dc, hda_audio_reset);
913cd6c8830SGerd Hoffmann dc->vmsd = &vmstate_hda_audio;
9144f67d30bSMarc-André Lureau device_class_set_props(dc, hda_audio_properties);
915cd6c8830SGerd Hoffmann }
916cd6c8830SGerd Hoffmann
917cd6c8830SGerd Hoffmann static const TypeInfo hda_audio_info = {
918cd6c8830SGerd Hoffmann .name = TYPE_HDA_AUDIO,
919cd6c8830SGerd Hoffmann .parent = TYPE_HDA_CODEC_DEVICE,
920edf632eeSEduardo Habkost .instance_size = sizeof(HDAAudioState),
921cd6c8830SGerd Hoffmann .class_init = hda_audio_base_class_init,
922cd6c8830SGerd Hoffmann .abstract = true,
923cd6c8830SGerd Hoffmann };
924cd6c8830SGerd Hoffmann
hda_audio_output_class_init(ObjectClass * klass,const void * data)925*12d1a768SPhilippe Mathieu-Daudé static void hda_audio_output_class_init(ObjectClass *klass, const void *data)
926dbaa7904SAnthony Liguori {
92739bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass);
928dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
929dbaa7904SAnthony Liguori
930dbaa7904SAnthony Liguori k->init = hda_audio_init_output;
93120110065SGerd Hoffmann dc->desc = "HDA Audio Codec, output-only (line-out)";
932dbaa7904SAnthony Liguori }
933dbaa7904SAnthony Liguori
9348c43a6f0SAndreas Färber static const TypeInfo hda_audio_output_info = {
935dbaa7904SAnthony Liguori .name = "hda-output",
936cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO,
937dbaa7904SAnthony Liguori .class_init = hda_audio_output_class_init,
938d61a4ce8SGerd Hoffmann };
939d61a4ce8SGerd Hoffmann
hda_audio_duplex_class_init(ObjectClass * klass,const void * data)940*12d1a768SPhilippe Mathieu-Daudé static void hda_audio_duplex_class_init(ObjectClass *klass, const void *data)
941dbaa7904SAnthony Liguori {
94239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass);
943dbaa7904SAnthony Liguori HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
944dbaa7904SAnthony Liguori
945dbaa7904SAnthony Liguori k->init = hda_audio_init_duplex;
94620110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
947dbaa7904SAnthony Liguori }
948dbaa7904SAnthony Liguori
9498c43a6f0SAndreas Färber static const TypeInfo hda_audio_duplex_info = {
950dbaa7904SAnthony Liguori .name = "hda-duplex",
951cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO,
952dbaa7904SAnthony Liguori .class_init = hda_audio_duplex_class_init,
953d61a4ce8SGerd Hoffmann };
954d61a4ce8SGerd Hoffmann
hda_audio_micro_class_init(ObjectClass * klass,const void * data)955*12d1a768SPhilippe Mathieu-Daudé static void hda_audio_micro_class_init(ObjectClass *klass, const void *data)
95620110065SGerd Hoffmann {
95720110065SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass);
95820110065SGerd Hoffmann HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
95920110065SGerd Hoffmann
96020110065SGerd Hoffmann k->init = hda_audio_init_micro;
96120110065SGerd Hoffmann dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
96220110065SGerd Hoffmann }
96320110065SGerd Hoffmann
9648c43a6f0SAndreas Färber static const TypeInfo hda_audio_micro_info = {
96520110065SGerd Hoffmann .name = "hda-micro",
966cd6c8830SGerd Hoffmann .parent = TYPE_HDA_AUDIO,
96720110065SGerd Hoffmann .class_init = hda_audio_micro_class_init,
96820110065SGerd Hoffmann };
96920110065SGerd Hoffmann
hda_audio_register_types(void)97083f7d43aSAndreas Färber static void hda_audio_register_types(void)
971d61a4ce8SGerd Hoffmann {
972cd6c8830SGerd Hoffmann type_register_static(&hda_audio_info);
97339bffca2SAnthony Liguori type_register_static(&hda_audio_output_info);
97439bffca2SAnthony Liguori type_register_static(&hda_audio_duplex_info);
97520110065SGerd Hoffmann type_register_static(&hda_audio_micro_info);
976d61a4ce8SGerd Hoffmann }
97783f7d43aSAndreas Färber
97883f7d43aSAndreas Färber type_init(hda_audio_register_types)
979