* [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
@ 2010-10-27 16:04 Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 1/6] pa: allow processing buffer pieces Gerd Hoffmann
` (6 more replies)
0 siblings, 7 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-27 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Hi,
This patch series brings a bunch of pulseaudio tweaks and two new sound
devices. Number one is the slightly improved usb-audio patch which was
originally created by hpa. Number two is a virtual HD Audio controller
with HDA codecs.
The patches are also available in the git repository at:
git://anongit.freedesktop.org/spice/qemu audio.2
enjoy,
Gerd
Gerd Hoffmann (5):
pa: allow processing buffer pieces
pa: process 1/4 buffer max at once
pa: setup buffer attrs
pa: tweak config
Add Intel HD Audio support to qemu.
H. Peter Anvin (1):
Add support for a USB audio device model
Makefile.objs | 3 +-
audio/paaudio.c | 44 +--
hw/hda-audio.c | 820 +++++++++++++++++++++++++++++++++++++++
hw/intel-hda-defs.h | 717 ++++++++++++++++++++++++++++++++++
hw/intel-hda.c | 1071 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/intel-hda.h | 61 +++
hw/usb-audio.c | 758 ++++++++++++++++++++++++++++++++++++
hw/usb-net.c | 3 -
hw/usb.h | 7 +
9 files changed, 3457 insertions(+), 27 deletions(-)
create mode 100644 hw/hda-audio.c
create mode 100644 hw/intel-hda-defs.h
create mode 100644 hw/intel-hda.c
create mode 100644 hw/intel-hda.h
create mode 100644 hw/usb-audio.c
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Qemu-devel] [PATCH 1/6] pa: allow processing buffer pieces
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
@ 2010-10-27 16:04 ` Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 2/6] pa: process 1/4 buffer max at once Gerd Hoffmann
` (5 subsequent siblings)
6 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-27 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Remove some state update dependencies between the pulseaudio worker
threads and main thread. The worker threads can process the buffer
piecewise now.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
audio/paaudio.c | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index ff71dac..f768ca9 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -83,7 +83,7 @@ static void *qpa_thread_out (void *arg)
}
decr = to_mix = pa->live;
- rpos = hw->rpos;
+ rpos = pa->rpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -110,8 +110,8 @@ static void *qpa_thread_out (void *arg)
return NULL;
}
- pa->live = 0;
pa->rpos = rpos;
+ pa->live -= decr;
pa->decr += decr;
}
@@ -178,7 +178,7 @@ static void *qpa_thread_in (void *arg)
}
incr = to_grab = pa->dead;
- wpos = hw->wpos;
+ wpos = pa->wpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -323,6 +323,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ pa->rpos = hw->rpos;
if (!pa->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
@@ -377,6 +378,7 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ pa->wpos = hw->wpos;
if (!pa->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
--
1.7.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [Qemu-devel] [PATCH 2/6] pa: process 1/4 buffer max at once
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 1/6] pa: allow processing buffer pieces Gerd Hoffmann
@ 2010-10-27 16:04 ` Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 3/6] pa: setup buffer attrs Gerd Hoffmann
` (4 subsequent siblings)
6 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-27 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Limit the size of data pieces processed by the pulseaudio worker
threads. Never ever process more than 1/4 of the buffer at once.
Data processing is less bulky then.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
audio/paaudio.c | 14 ++++----------
1 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index f768ca9..f99ca73 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -57,9 +57,6 @@ static void *qpa_thread_out (void *arg)
{
PAVoiceOut *pa = arg;
HWVoiceOut *hw = &pa->hw;
- int threshold;
-
- threshold = conf.divisor ? hw->samples / conf.divisor : 0;
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -73,7 +70,7 @@ static void *qpa_thread_out (void *arg)
goto exit;
}
- if (pa->live > threshold) {
+ if (pa->live > 0) {
break;
}
@@ -82,7 +79,7 @@ static void *qpa_thread_out (void *arg)
}
}
- decr = to_mix = pa->live;
+ decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
rpos = pa->rpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -152,9 +149,6 @@ static void *qpa_thread_in (void *arg)
{
PAVoiceIn *pa = arg;
HWVoiceIn *hw = &pa->hw;
- int threshold;
-
- threshold = conf.divisor ? hw->samples / conf.divisor : 0;
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -168,7 +162,7 @@ static void *qpa_thread_in (void *arg)
goto exit;
}
- if (pa->dead > threshold) {
+ if (pa->dead > 0) {
break;
}
@@ -177,7 +171,7 @@ static void *qpa_thread_in (void *arg)
}
}
- incr = to_grab = pa->dead;
+ incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
wpos = pa->wpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
--
1.7.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [Qemu-devel] [PATCH 3/6] pa: setup buffer attrs
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 1/6] pa: allow processing buffer pieces Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 2/6] pa: process 1/4 buffer max at once Gerd Hoffmann
@ 2010-10-27 16:04 ` Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 4/6] pa: tweak config Gerd Hoffmann
` (3 subsequent siblings)
6 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-27 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Request reasonable buffer sizes from pulseaudio. Without this
pa_simple_write() can block quite long and lead to dropouts,
especially with guests which use small audio ring buffers.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
audio/paaudio.c | 12 +++++++++++-
1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index f99ca73..d019978 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -289,6 +289,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
{
int error;
static pa_sample_spec ss;
+ static pa_buffer_attr ba;
struct audsettings obt_as = *as;
PAVoiceOut *pa = (PAVoiceOut *) hw;
@@ -296,6 +297,15 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
ss.channels = as->nchannels;
ss.rate = as->freq;
+ /*
+ * qemu audio tick runs at 250 Hz (by default), so processing
+ * data chunks worth 4 ms of sound should be a good fit.
+ */
+ ba.tlength = pa_usec_to_bytes(4 * 1000, &ss);
+ ba.minreq = pa_usec_to_bytes(2 * 1000, &ss);
+ ba.maxlength = -1;
+ ba.prebuf = -1;
+
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->s = pa_simple_new (
@@ -306,7 +316,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
"pcm.playback",
&ss,
NULL, /* channel map */
- NULL, /* buffering attributes */
+ &ba, /* buffering attributes */
&error
);
if (!pa->s) {
--
1.7.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [Qemu-devel] [PATCH 4/6] pa: tweak config
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
` (2 preceding siblings ...)
2010-10-27 16:04 ` [Qemu-devel] [PATCH 3/6] pa: setup buffer attrs Gerd Hoffmann
@ 2010-10-27 16:04 ` Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 5/6] Add support for a USB audio device model Gerd Hoffmann
` (2 subsequent siblings)
6 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-27 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
Zap unused divisor field.
Raise the buffer size default.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
audio/paaudio.c | 10 +---------
1 files changed, 1 insertions(+), 9 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index d019978..d13d1ac 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -33,13 +33,11 @@ typedef struct {
static struct {
int samples;
- int divisor;
char *server;
char *sink;
char *source;
} conf = {
- .samples = 1024,
- .divisor = 2,
+ .samples = 4096,
};
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
@@ -478,12 +476,6 @@ struct audio_option qpa_options[] = {
.descr = "buffer size in samples"
},
{
- .name = "DIVISOR",
- .tag = AUD_OPT_INT,
- .valp = &conf.divisor,
- .descr = "threshold divisor"
- },
- {
.name = "SERVER",
.tag = AUD_OPT_STR,
.valp = &conf.server,
--
1.7.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [Qemu-devel] [PATCH 5/6] Add support for a USB audio device model
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
` (3 preceding siblings ...)
2010-10-27 16:04 ` [Qemu-devel] [PATCH 4/6] pa: tweak config Gerd Hoffmann
@ 2010-10-27 16:04 ` Gerd Hoffmann
2010-10-27 17:13 ` [Qemu-devel] " H. Peter Anvin
2010-10-27 16:04 ` [Qemu-devel] [PATCH 6/6] Add Intel HD Audio support to qemu Gerd Hoffmann
2010-10-27 17:46 ` [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series malc
6 siblings, 1 reply; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-27 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: H. Peter Anvin, Gerd Hoffmann
From: H. Peter Anvin <hpa@linux.intel.com>
This brings a usb audio device to qemu. Output only, fixed at
16bit stereo @ 480000 Hz. Based on a patch from
H. Peter Anvin <hpa@linux.intel.com>
Usage: add '-device usb-audio' to your qemu command line.
Works sorta ok on a idle machine. Known issues:
* Is *very* sensitive to latencies: when the uhci emulation misses one
of the usb frame rate wakeups (1k/sec!) you'll loose/delay data from
the audio stream, resulting in dropouts.
* Also seems to not play very well with the usb tablet (and/or usb hub,
to be investigated). Best try this as the only device on a usb bus.
* Burns quite some CPU due to usb polling.
In short: It brings the qemu usb emulation to its limits. Enjoy!
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
Makefile.objs | 2 +-
hw/usb-audio.c | 758 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/usb-net.c | 3 -
hw/usb.h | 7 +
4 files changed, 766 insertions(+), 4 deletions(-)
create mode 100644 hw/usb-audio.c
diff --git a/Makefile.objs b/Makefile.objs
index f07fb01..06dfcd0 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -73,7 +73,7 @@ common-obj-y += eeprom93xx.o
common-obj-y += scsi-disk.o cdrom.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
-common-obj-y += usb-serial.o usb-net.o usb-bus.o
+common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-audio.o
common-obj-$(CONFIG_SSI) += ssi.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
common-obj-$(CONFIG_SD) += sd.o
diff --git a/hw/usb-audio.c b/hw/usb-audio.c
new file mode 100644
index 0000000..8fb00b4
--- /dev/null
+++ b/hw/usb-audio.c
@@ -0,0 +1,758 @@
+/*
+ * QEMU USB Net devices
+ *
+ * Copyright (c) 2006 Thomas Sailer
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+
+#define USBAUDIO_VENDOR_NUM 0xabcd
+#define USBAUDIO_PRODUCT_NUM 0x1234
+
+#define DEV_CONFIG_VALUE 1 /* The one and only */
+
+/* Descriptor subtypes for AC interfaces */
+#define DST_AC_HEADER 1
+#define DST_AC_INPUT_TERMINAL 2
+#define DST_AC_OUTPUT_TERMINAL 3
+#define DST_AC_FEATURE_UNIT 6
+/* Descriptor subtypes for AS interfaces */
+#define DST_AS_GENERAL 1
+#define DST_AS_FORMAT_TYPE 2
+/* Descriptor subtypes for endpoints */
+#define DST_EP_GENERAL 1
+
+enum usb_audio_strings {
+ STRING_NULL,
+ STRING_MANUFACTURER,
+ STRING_PRODUCT,
+ STRING_SERIALNUMBER,
+ STRING_CONFIG,
+ STRING_USBAUDIO_CONTROL,
+ STRING_INPUT_TERMINAL,
+ STRING_FEATURE_UNIT,
+ STRING_OUTPUT_TERMINAL,
+ STRING_NULL_STREAM,
+ STRING_REAL_STREAM,
+};
+
+static const char * const usb_audio_stringtable[256] = {
+ [STRING_MANUFACTURER] = "QEMU",
+ [STRING_PRODUCT] = "QEMU USB Audio",
+ [STRING_SERIALNUMBER] = "1",
+ [STRING_CONFIG] = "QEMU USB Audio Configuration",
+ [STRING_USBAUDIO_CONTROL] = "QEMU USB Audio Device",
+ [STRING_INPUT_TERMINAL] = "QEMU USB Audio Output Pipe",
+ [STRING_FEATURE_UNIT] = "QEMU USB Audio Output Volume Control",
+ [STRING_OUTPUT_TERMINAL] = "QEMU USB Audio Output Terminal",
+ [STRING_NULL_STREAM] = "QEMU USB Audio Output - Disabled",
+ [STRING_REAL_STREAM] = "QEMU USB Audio Output - 48 kHz Stereo",
+};
+
+#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff)
+#define U24(x) U16(x), (((x) >> 16) & 0xff)
+#define U32(x) U24(x), (((x) >> 24) & 0xff)
+
+static const uint8_t qemu_usb_audio_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ USB_DT_DEVICE, /* u8 bDescriptorType; Device */
+ 0x00, 0x02, /* u16 bcdUSB; v2.0 */
+ 0x00, /* u8 bDeviceClass; [ interface level ] */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full only ] */
+ 0x40, /* u8 bMaxPacketSize0 */
+ U16(USBAUDIO_VENDOR_NUM), /* u16 idVendor; */
+ U16(USBAUDIO_PRODUCT_NUM), /* u16 idProduct; */
+ 0x00, 0x00, /* u16 bcdDevice */
+ STRING_MANUFACTURER, /* u8 iManufacturer; */
+ STRING_PRODUCT, /* u8 iProduct; */
+ STRING_SERIALNUMBER, /* u8 iSerialNumber; */
+ 0x01, /* u8 bNumConfigurations; */
+};
+
+/*
+ * A Basic Audio Device uses these specific values
+ */
+#define USBAUDIO_PACKET_SIZE 192
+#define USBAUDIO_SAMPLE_RATE 48000
+#define USBAUDIO_PACKET_INTERVAL 1
+
+/*
+ * This basically follows a "Basic Audio Device Headphone Type 1",
+ * except the Terminal Type is set to SPEAKER (0x0301) instead
+ * of HEADPHONE (0x0302)
+ */
+static const uint8_t qemu_usb_audio_config_descriptor[] = {
+ /* Configuration Descriptor */
+ 0x09, /* u8 bLength */
+ USB_DT_CONFIG, /* u8 bDescriptorType */
+ U16(0), /* le16 wTotalLength, filled at runtime */
+ 0x02, /* u8 bNumInterfaces */
+ DEV_CONFIG_VALUE, /* u8 bConfigurationValue */
+ STRING_CONFIG, /* u8 iConfiguration */
+ 0xc0, /* u8 bmAttributes */
+ 0x32, /* u8 bMaxPower */
+ /* USB Basic Headphone AC Interface */
+ 0x09, /* u8 bLength */
+ USB_DT_INTERFACE, /* u8 bDescriptorType */
+ 0x00, /* u8 bInterfaceNumber */
+ 0x00, /* u8 bAlternateSetting */
+ 0x00, /* u8 bNumEndpoints */
+ USB_CLASS_AUDIO, /* u8 bInterfaceClass */
+ USB_SUBCLASS_AUDIO_CONTROL, /* u8 bInterfaceSubClass */
+ 0x04, /* u8 bInterfaceProtocol */
+ STRING_USBAUDIO_CONTROL, /* u8 iInterface */
+ /* Headphone Class-Specific AC Interface Header Descriptor */
+ 0x09, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ DST_AC_HEADER, /* u8 bDescriptorSubtype */
+ U16(0x0100), /* u16 bcdADC */
+ U16(0x2b), /* u16 wTotalLength */
+ 0x01, /* u8 bInCollection */
+ 0x01, /* u8 baInterfaceNr */
+ /* Generic Stereo Input Terminal ID1 Descriptor */
+ 0x0c, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */
+ 0x01, /* u8 bTerminalID */
+ U16(0x0101), /* u16 wTerminalType */
+ 0x00, /* u8 bAssocTerminal */
+ 0x02, /* u16 bNrChannels */
+ U16(0x0003), /* u16 wChannelConfig */
+ 0x00, /* u8 iChannelNames */
+ STRING_INPUT_TERMINAL, /* u8 iTerminal */
+ /* Generic Stereo Feature Unit ID2 Descriptor */
+ 0x0d, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */
+ 0x02, /* u8 bUnitID */
+ 0x01, /* u8 bSourceID */
+ 0x02, /* u8 bControlSize */
+ U16(0x0001), /* u16 bmaControls(0) */
+ U16(0x0002), /* u16 bmaControls(1) */
+ U16(0x0002), /* u16 bmaControls(2) */
+ STRING_FEATURE_UNIT, /* u8 iFeature */
+ /* Headphone Ouptut Terminal ID3 Descriptor */
+ 0x09, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */
+ 0x03, /* u8 bUnitID */
+ U16(0x0301), /* u16 wTerminalType (SPEAKER) */
+ 0x00, /* u8 bAssocTerminal */
+ 0x02, /* u8 bSourceID */
+ STRING_OUTPUT_TERMINAL, /* u8 iTerminal */
+ /* Headphone Standard AS Interface Descriptor (Alt Set 0) */
+ 0x09, /* u8 bLength */
+ USB_DT_INTERFACE, /* u8 bDescriptorType */
+ 0x01, /* u8 bInterfaceNumber */
+ 0x00, /* u8 bAlternateSetting */
+ 0x00, /* u8 bNumEndpoints */
+ USB_CLASS_AUDIO, /* u8 bInterfaceClass */
+ USB_SUBCLASS_AUDIO_STREAMING, /* u8 bInterfaceSubclass */
+ 0x00, /* u8 bInterfaceProtocol */
+ STRING_NULL_STREAM, /* u8 iInterface */
+ /* Headphone Standard AS Interface Descriptor (Alt Set 1) */
+ 0x09, /* u8 bLength */
+ USB_DT_INTERFACE, /* u8 bDescriptorType */
+ 0x01, /* u8 bInterfaceNumber */
+ 0x01, /* u8 bAlternateSetting */
+ 0x01, /* u8 bNumEndpoins */
+ USB_CLASS_AUDIO, /* u8 bInterfaceClass */
+ USB_SUBCLASS_AUDIO_STREAMING, /* u8 bInterfaceSubclass */
+ 0x00, /* u8 bInterfaceProtocol */
+ STRING_REAL_STREAM, /* u8 iInterface */
+ /* Headphone Class-specific AS General Interface Descriptor */
+ 0x07, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ DST_AS_GENERAL, /* u8 bDescriptorSubtype */
+ 0x01, /* u8 bTerminalLink */
+ 0x00, /* u8 bDelay */
+ 0x01, 0x00, /* u16 wFormatTag */
+ /* Headphone Type I Format Type Descriptor */
+ 0x0b, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */
+ 0x01, /* u8 bFormatType */
+ 0x02, /* u8 bNrChannels */
+ 0x02, /* u8 bSubFrameSize */
+ 0x10, /* u8 bBitResolution */
+ 0x01, /* u8 bSamFreqType */
+ U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */
+ /* Stereo Headphone Standard AS Audio Data Endpoint Descriptor */
+ 0x09, /* u8 bLength */
+ USB_DT_ENDPOINT, /* u8 bDescriptorType */
+ 0x01, /* u8 bEndpointAddress */
+ 0x0d, /* u8 bmAttributes */
+ U16(USBAUDIO_PACKET_SIZE), /* u16 wMaxPacketSize */
+ USBAUDIO_PACKET_INTERVAL, /* u8 bInterval */
+ 0x00, /* u8 bRefresh */
+ 0x00, /* u8 bSynchAddress */
+ /* Stereo Headphone Class-specific AS Audio Data Endpoint Descriptor */
+ 0x07, /* u8 bLength */
+ USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */
+ DST_EP_GENERAL, /* u8 bDescriptorSubtype */
+ 0x00, /* u8 bmAttributes */
+ 0x00, /* u8 bLockDelayUnits */
+ U16(0x0000), /* u16 wLockDelay */
+};
+
+/*
+ * A USB audio device supports an arbitrary number of alternate
+ * interface settings for each interface. Each corresponds to a block
+ * diagram of parameterized blocks. This can thus refer to things like
+ * number of channels, data rates, or in fact completely different
+ * block diagrams. Alternative setting 0 is always the null block diagram,
+ * which is used by a disabled device.
+ */
+enum usb_audio_altset {
+ ALTSET_OFF = 0x00, /* No endpoint */
+ ALTSET_ON = 0x01, /* Single endpoint */
+};
+
+/*
+ * Class-specific control requests
+ */
+#define CR_SET_CUR 0x01
+#define CR_GET_CUR 0x81
+#define CR_SET_MIN 0x02
+#define CR_GET_MIN 0x82
+#define CR_SET_MAX 0x03
+#define CR_GET_MAX 0x83
+#define CR_SET_RES 0x04
+#define CR_GET_RES 0x84
+#define CR_SET_MEM 0x05
+#define CR_GET_MEM 0x85
+#define CR_GET_STAT 0xff
+
+/*
+ * Feature Unit Control Selectors
+ */
+#define MUTE_CONTROL 0x01
+#define VOLUME_CONTROL 0x02
+#define BASS_CONTROL 0x03
+#define MID_CONTROL 0x04
+#define TREBLE_CONTROL 0x05
+#define GRAPHIC_EQUALIZER_CONTROL 0x06
+#define AUTOMATIC_GAIN_CONTROL 0x07
+#define DELAY_CONTROL 0x08
+#define BASS_BOOST_CONTROL 0x09
+#define LOUDNESS_CONTROL 0x0a
+
+/*
+ * buffering
+ */
+
+struct streambuf {
+ uint8_t *data;
+ uint32_t size;
+ uint32_t prod;
+ uint32_t cons;
+};
+
+static void streambuf_init(struct streambuf *buf, uint32_t size)
+{
+ qemu_free(buf->data);
+ buf->size = size - (size % USBAUDIO_PACKET_SIZE);
+ buf->data = qemu_malloc(buf->size);
+ buf->prod = 0;
+ buf->cons = 0;
+}
+
+static void streambuf_fini(struct streambuf *buf)
+{
+ qemu_free(buf->data);
+ buf->data = NULL;
+}
+
+static int streambuf_put(struct streambuf *buf, uint8_t *data)
+{
+ uint32_t free = buf->size - (buf->prod - buf->cons);
+
+ if (!free)
+ return 0;
+ assert(free >= USBAUDIO_PACKET_SIZE);
+ memcpy(buf->data + (buf->prod % buf->size), data, USBAUDIO_PACKET_SIZE);
+ buf->prod += USBAUDIO_PACKET_SIZE;
+ return USBAUDIO_PACKET_SIZE;
+}
+
+static uint8_t *streambuf_get(struct streambuf *buf)
+{
+ uint32_t used = buf->prod - buf->cons;
+ uint8_t *data;
+
+ if (!used)
+ return NULL;
+ assert(used >= USBAUDIO_PACKET_SIZE);
+ data = buf->data + (buf->cons % buf->size);
+ buf->cons += USBAUDIO_PACKET_SIZE;
+ return data;
+}
+
+typedef struct USBAudioState {
+ /* qemu interfaces */
+ USBDevice dev;
+ QEMUSoundCard card;
+
+ /* state */
+ struct {
+ enum usb_audio_altset altset;
+ struct audsettings as;
+ SWVoiceOut *voice;
+ bool mute;
+ uint8_t vol[2];
+ struct streambuf buf;
+ } out;
+
+ /* properties */
+ uint32_t debug;
+ uint32_t buffer;
+} USBAudioState;
+
+static void output_callback(void *opaque, int avail)
+{
+ USBAudioState *s = opaque;
+ uint8_t *data;
+
+ for (;;) {
+ if (avail < USBAUDIO_PACKET_SIZE)
+ return;
+ data = streambuf_get(&s->out.buf);
+ if (NULL == data) {
+ return;
+ }
+ AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE);
+ avail -= USBAUDIO_PACKET_SIZE;
+ }
+}
+
+static int usb_audio_set_output_altset(USBAudioState *s, int altset)
+{
+ switch (altset) {
+ case ALTSET_OFF:
+ streambuf_init(&s->out.buf, s->buffer);
+ AUD_set_active_out(s->out.voice, false);
+ break;
+ case ALTSET_ON:
+ AUD_set_active_out(s->out.voice, true);
+ break;
+ default:
+ return -1;
+ }
+
+ if (s->debug)
+ fprintf(stderr, "usb-audio: set interface %d\n", altset);
+ s->out.altset = altset;
+ return 0;
+}
+
+/*
+ * Note: we arbitrarily map the volume control range onto -inf..+8 dB
+ */
+#define ATTRIB_ID(cs, attrib, idif) \
+ (((cs) << 24) | ((attrib) << 16) | (idif))
+
+static int usb_audio_get_control(USBAudioState *s, uint8_t attrib,
+ uint16_t cscn, uint16_t idif,
+ int length, uint8_t *data)
+{
+ uint8_t cs = cscn >> 8;
+ uint8_t cn = cscn - 1; /* -1 for the non-present master control */
+ uint32_t aid = ATTRIB_ID(cs, attrib, idif);
+ int ret = USB_RET_STALL;
+
+ switch (aid) {
+ case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200):
+ data[0] = s->out.mute;
+ ret = 1;
+ break;
+ case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
+ if (cn < 2) {
+ uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
+ data[0] = vol;
+ data[1] = vol >> 8;
+ ret = 2;
+ }
+ break;
+ case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200):
+ if (cn < 2) {
+ data[0] = 0x01;
+ data[1] = 0x80;
+ ret = 2;
+ }
+ break;
+ case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200):
+ if (cn < 2) {
+ data[0] = 0x00;
+ data[1] = 0x08;
+ ret = 2;
+ }
+ break;
+ case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200):
+ if (cn < 2) {
+ data[0] = 0x88;
+ data[1] = 0x00;
+ ret = 2;
+ }
+ break;
+ }
+
+ return ret;
+}
+static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
+ uint16_t cscn, uint16_t idif,
+ int length, uint8_t *data)
+{
+ uint8_t cs = cscn >> 8;
+ uint8_t cn = cscn - 1; /* -1 for the non-present master control */
+ uint32_t aid = ATTRIB_ID(cs, attrib, idif);
+ int ret = USB_RET_STALL;
+ bool set_vol = false;
+
+ switch (aid) {
+ case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200):
+ s->out.mute = data[0] & 1;
+ set_vol = true;
+ ret = 0;
+ break;
+ case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200):
+ if (cn < 2) {
+ uint16_t vol = data[0] + (data[1] << 8);
+
+ if (s->debug)
+ fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol);
+
+ vol -= 0x8000;
+ vol = (vol * 255 + 0x4400) / 0x8800;
+ if (vol > 255)
+ vol = 255;
+
+ s->out.vol[cn] = vol;
+ set_vol = true;
+ ret = 0;
+ }
+ break;
+ }
+
+ if (set_vol) {
+ if (s->debug)
+ fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
+ s->out.mute, s->out.vol[0], s->out.vol[1]);
+ AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
+ }
+
+ return ret;
+}
+
+static int usb_audio_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+ int ret = 0;
+
+ if (s->debug)
+ fprintf(stderr, "usb-audio: control transaction: "
+ "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
+ request, value, index, length);
+
+ switch(request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ ret = sizeof(qemu_usb_audio_dev_descriptor);
+ memcpy(data, qemu_usb_audio_dev_descriptor, ret);
+ break;
+
+ case USB_DT_CONFIG:
+ switch (value & 0xff) {
+ case 0:
+ ret = sizeof(qemu_usb_audio_config_descriptor);
+ memcpy(data, qemu_usb_audio_config_descriptor, ret);
+ data[2] = ret & 0xff;
+ data[3] = ret >> 8;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case USB_DT_STRING:
+ switch (value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+
+ default:
+ if (usb_audio_stringtable[value & 0xff]) {
+ ret = set_usb_string(data,
+ usb_audio_stringtable[value & 0xff]);
+ break;
+ }
+ if (s->debug)
+ fprintf(stderr, "usb-audio: fail: string 0x%x doesn't exist\n",
+ value & 0xff);
+ goto fail;
+ }
+ break;
+ default:
+ if (s->debug)
+ fprintf(stderr, "usb-audio: fail: unknown descriptor 0x%x\n",
+ value >> 8);
+ goto fail;
+ }
+ break;
+
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = DEV_CONFIG_VALUE;
+ ret = 1;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ switch (value & 0xff) {
+ case DEV_CONFIG_VALUE:
+ break;
+ default:
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+
+ case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ if (index != 0x01) {
+ goto fail;
+ }
+ data[0] = s->out.altset;
+ ret = 1;
+ break;
+
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ if (index != 0x01) {
+ goto fail;
+ }
+ if (usb_audio_set_output_altset(s, value)) {
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ case ClassInterfaceRequest | CR_GET_CUR:
+ case ClassInterfaceRequest | CR_GET_MIN:
+ case ClassInterfaceRequest | CR_GET_MAX:
+ case ClassInterfaceRequest | CR_GET_RES:
+ ret = usb_audio_get_control(s, request & 0xff, value, index,
+ length, data);
+ if (ret < 0) {
+ if (s->debug)
+ fprintf(stderr, "usb-audio: fail: get control\n");
+ goto fail;
+ }
+ break;
+
+ case ClassInterfaceOutRequest | CR_SET_CUR:
+ case ClassInterfaceOutRequest | CR_SET_MIN:
+ case ClassInterfaceOutRequest | CR_SET_MAX:
+ case ClassInterfaceOutRequest | CR_SET_RES:
+ ret = usb_audio_set_control(s, request & 0xff, value, index,
+ length, data);
+ if (ret < 0) {
+ if (s->debug)
+ fprintf(stderr, "usb-audio: fail: set control\n");
+ goto fail;
+ }
+ break;
+
+ default:
+ fail:
+ if (s->debug)
+ fprintf(stderr, "usb-audio: failed control transaction: "
+ "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
+ request, value, index, length);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+
+static void usb_audio_handle_reset(USBDevice *dev)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+ if (s->debug)
+ fprintf(stderr, "usb-audio: reset\n");
+ usb_audio_set_output_altset(s, ALTSET_OFF);
+}
+
+static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
+{
+ int rc;
+
+ if (s->out.altset == ALTSET_OFF)
+ return USB_RET_STALL;
+
+ rc = streambuf_put(&s->out.buf, p->data);
+ if (rc < p->len && s->debug > 1) {
+ fprintf(stderr, "usb-audio: output overrun (%d bytes)\n", p->len - rc);
+ }
+
+ return 0;
+}
+
+static int usb_audio_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBAudioState *s = (USBAudioState *) dev;
+ int ret = 0;
+
+ switch(p->pid) {
+ case USB_TOKEN_OUT:
+ switch (p->devep) {
+ case 1:
+ ret = usb_audio_handle_dataout(s, p);
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ if (ret == USB_RET_STALL && s->debug)
+ fprintf(stderr, "usb-audio: failed data transaction: "
+ "pid 0x%x ep 0x%x len 0x%x\n",
+ p->pid, p->devep, p->len);
+ return ret;
+}
+
+static void usb_audio_handle_destroy(USBDevice *dev)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+ if (s->debug)
+ fprintf(stderr, "usb-audio: destroy\n");
+
+ usb_audio_set_output_altset(s, ALTSET_OFF);
+ AUD_close_out(&s->card, s->out.voice);
+ AUD_remove_card(&s->card);
+
+ streambuf_fini(&s->out.buf);
+}
+
+static int usb_audio_initfn(USBDevice *dev)
+{
+ USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.opaque = s;
+ AUD_register_card("usb-audio", &s->card);
+
+ s->out.altset = ALTSET_OFF;
+ s->out.mute = false;
+ s->out.vol[0] = 240; /* 0 dB */
+ s->out.vol[1] = 240; /* 0 dB */
+ s->out.as.freq = USBAUDIO_SAMPLE_RATE;
+ s->out.as.nchannels = 2;
+ s->out.as.fmt = AUD_FMT_S16;
+ s->out.as.endianness = 0;
+ streambuf_init(&s->out.buf, s->buffer);
+
+ s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio",
+ s, output_callback, &s->out.as);
+ AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
+ AUD_set_active_out(s->out.voice, 0);
+ return 0;
+}
+
+static struct USBDeviceInfo usb_audio_info = {
+ .product_desc = "QEMU USB Audio Interface",
+ .usbdevice_name = "audio",
+ .qdev.name = "usb-audio",
+ .qdev.size = sizeof(USBAudioState),
+ .init = usb_audio_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_audio_handle_reset,
+ .handle_control = usb_audio_handle_control,
+ .handle_data = usb_audio_handle_data,
+ .handle_destroy = usb_audio_handle_destroy,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
+ DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, 8 * USBAUDIO_PACKET_SIZE),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void usb_audio_register_devices(void)
+{
+ usb_qdev_register(&usb_audio_info);
+}
+
+device_init(usb_audio_register_devices)
diff --git a/hw/usb-net.c b/hw/usb-net.c
index 70f9263..61469f3 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -68,9 +68,6 @@ enum usbstring_idx {
#define USB_CDC_UNION_TYPE 0x06 /* union_desc */
#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */
-#define USB_DT_CS_INTERFACE 0x24
-#define USB_DT_CS_ENDPOINT 0x25
-
#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00
#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01
#define USB_CDC_REQ_SET_LINE_CODING 0x20
diff --git a/hw/usb.h b/hw/usb.h
index 00d2802..111aa39 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -67,6 +67,11 @@
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
+#define USB_SUBCLASS_UNDEFINED 0
+#define USB_SUBCLASS_AUDIO_CONTROL 1
+#define USB_SUBCLASS_AUDIO_STREAMING 2
+#define USB_SUBCLASS_AUDIO_MIDISTREAMING 3
+
#define USB_DIR_OUT 0
#define USB_DIR_IN 0x80
@@ -116,6 +121,8 @@
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
+#define USB_DT_CS_INTERFACE 0x24
+#define USB_DT_CS_ENDPOINT 0x25
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
--
1.7.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [Qemu-devel] [PATCH 6/6] Add Intel HD Audio support to qemu.
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
` (4 preceding siblings ...)
2010-10-27 16:04 ` [Qemu-devel] [PATCH 5/6] Add support for a USB audio device model Gerd Hoffmann
@ 2010-10-27 16:04 ` Gerd Hoffmann
2010-10-27 17:46 ` [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series malc
6 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-27 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann
This patch adds three devices to qemu:
intel-hda
Intel HD Audio Controller, the PCI device. Provides a HDA bus.
Emulates ICH6 at the moment. Adding a ICH9 PCIE
variant shouldn't be hard.
hda-duplex
HDA Codec. Attaches to the HDA bus. Supports 16bit stereo,
rates 16k -> 96k, playback, recording and volume control
(with CONFIG_MIXEMU=y).
hda-output
HDA Codec without recording support. Subset of the hda-duplex
codec. Use this if you don't want your guests access your mic.
Usage: add '-device intel-hda -device hda-duplex' to your command line.
Tested guests:
* Linux works.
* Win7 works.
* WinXP doesn't work.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
Makefile.objs | 1 +
hw/hda-audio.c | 820 +++++++++++++++++++++++++++++++++++++++
hw/intel-hda-defs.h | 717 ++++++++++++++++++++++++++++++++++
hw/intel-hda.c | 1071 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/intel-hda.h | 61 +++
5 files changed, 2670 insertions(+), 0 deletions(-)
create mode 100644 hw/hda-audio.c
create mode 100644 hw/intel-hda-defs.h
create mode 100644 hw/intel-hda.c
create mode 100644 hw/intel-hda.h
diff --git a/Makefile.objs b/Makefile.objs
index 06dfcd0..79c12f8 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -205,6 +205,7 @@ hw-obj-y += msix.o
hw-obj-y += ne2000.o
hw-obj-y += eepro100.o
hw-obj-y += pcnet.o
+hw-obj-y += intel-hda.o hda-audio.o
hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
hw-obj-$(CONFIG_LAN9118) += lan9118.o
diff --git a/hw/hda-audio.c b/hw/hda-audio.c
new file mode 100644
index 0000000..f2bc787
--- /dev/null
+++ b/hw/hda-audio.c
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "intel-hda.h"
+#include "intel-hda-defs.h"
+#include "audio/audio.h"
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct desc_param {
+ uint32_t id;
+ uint32_t val;
+} desc_param;
+
+typedef struct desc_node {
+ uint32_t nid;
+ const char *name;
+ const desc_param *params;
+ uint32_t nparams;
+ uint32_t config;
+ uint32_t pinctl;
+ uint32_t *conn;
+ uint32_t stindex;
+} desc_node;
+
+typedef struct desc_codec {
+ const char *name;
+ uint32_t iid;
+ const desc_node *nodes;
+ uint32_t nnodes;
+} desc_codec;
+
+static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
+{
+ int i;
+
+ for (i = 0; i < node->nparams; i++) {
+ if (node->params[i].id == id) {
+ return &node->params[i];
+ }
+ }
+ return NULL;
+}
+
+static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
+{
+ int i;
+
+ for (i = 0; i < codec->nnodes; i++) {
+ if (codec->nodes[i].nid == nid) {
+ return &codec->nodes[i];
+ }
+ }
+ return NULL;
+}
+
+static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
+{
+ assert(!(format & AC_FMT_TYPE_NON_PCM));
+
+ as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
+
+ switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
+ case 1: as->freq *= 2; break;
+ case 2: as->freq *= 3; break;
+ case 3: as->freq *= 4; break;
+ }
+
+ switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
+ case 1: as->freq /= 2; break;
+ case 2: as->freq /= 3; break;
+ case 3: as->freq /= 4; break;
+ case 4: as->freq /= 5; break;
+ case 5: as->freq /= 6; break;
+ case 6: as->freq /= 7; break;
+ case 7: as->freq /= 8; break;
+ }
+
+ switch (format & AC_FMT_BITS_MASK) {
+ case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break;
+ case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
+ case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
+ }
+
+ as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * HDA codec descriptions
+ */
+
+/* some defines */
+
+#define QEMU_HDA_ID_VENDOR 0x1af4
+#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x10)
+#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x20)
+
+#define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \
+ 0x1fc /* 16 -> 96 kHz */)
+#define QEMU_HDA_AMP_NONE (0)
+#define QEMU_HDA_AMP_STEPS 0x4a
+
+#ifdef CONFIG_MIXEMU
+#define QEMU_HDA_AMP_CAPS \
+ (AC_AMPCAP_MUTE | \
+ (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \
+ (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \
+ (3 << AC_AMPCAP_STEP_SIZE_SHIFT))
+#else
+#define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE
+#endif
+
+/* common: audio output widget */
+static const desc_param common_params_audio_dac[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_FORMAT_OVRD |
+ AC_WCAP_AMP_OVRD |
+ AC_WCAP_OUT_AMP |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_CAPS,
+ },
+};
+
+/* common: pin widget (line-out) */
+static const desc_param common_params_audio_lineout[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_CONN_LIST |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_PIN_CAP,
+ .val = AC_PINCAP_OUT,
+ },{
+ .id = AC_PAR_CONNLIST_LEN,
+ .val = 1,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },
+};
+
+/* output: root node */
+static const desc_param output_params_root[] = {
+ {
+ .id = AC_PAR_VENDOR_ID,
+ .val = QEMU_HDA_ID_OUTPUT,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_OUTPUT,
+ },{
+ .id = AC_PAR_REV_ID,
+ .val = 0x00100101,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00010001,
+ },
+};
+
+/* output: audio function */
+static const desc_param output_params_audio_func[] = {
+ {
+ .id = AC_PAR_FUNCTION_TYPE,
+ .val = AC_GRP_AUDIO_FUNCTION,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_OUTPUT,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00020002,
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_GPIO_CAP,
+ .val = 0,
+ },{
+ .id = AC_PAR_AUDIO_FG_CAP,
+ .val = 0x00000808,
+ },{
+ .id = AC_PAR_POWER_STATE,
+ .val = 0,
+ },
+};
+
+/* output: nodes */
+static const desc_node output_nodes[] = {
+ {
+ .nid = AC_NODE_ROOT,
+ .name = "root",
+ .params = output_params_root,
+ .nparams = ARRAY_SIZE(output_params_root),
+ },{
+ .nid = 1,
+ .name = "func",
+ .params = output_params_audio_func,
+ .nparams = ARRAY_SIZE(output_params_audio_func),
+ },{
+ .nid = 2,
+ .name = "dac",
+ .params = common_params_audio_dac,
+ .nparams = ARRAY_SIZE(common_params_audio_dac),
+ .stindex = 0,
+ },{
+ .nid = 3,
+ .name = "out",
+ .params = common_params_audio_lineout,
+ .nparams = ARRAY_SIZE(common_params_audio_lineout),
+ .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+ (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) |
+ (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+ (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) |
+ 0x10),
+ .pinctl = AC_PINCTL_OUT_EN,
+ .conn = (uint32_t[]) { 2 },
+ }
+};
+
+/* output: codec */
+static const desc_codec output = {
+ .name = "output",
+ .iid = QEMU_HDA_ID_OUTPUT,
+ .nodes = output_nodes,
+ .nnodes = ARRAY_SIZE(output_nodes),
+};
+
+/* duplex: root node */
+static const desc_param duplex_params_root[] = {
+ {
+ .id = AC_PAR_VENDOR_ID,
+ .val = QEMU_HDA_ID_DUPLEX,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_DUPLEX,
+ },{
+ .id = AC_PAR_REV_ID,
+ .val = 0x00100101,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00010001,
+ },
+};
+
+/* duplex: audio input widget */
+static const desc_param duplex_params_audio_adc[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_CONN_LIST |
+ AC_WCAP_FORMAT_OVRD |
+ AC_WCAP_AMP_OVRD |
+ AC_WCAP_IN_AMP |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_CONNLIST_LEN,
+ .val = 1,
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_CAPS,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },
+};
+
+/* duplex: pin widget (line-in) */
+static const desc_param duplex_params_audio_linein[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_PIN_CAP,
+ .val = AC_PINCAP_IN,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },
+};
+
+/* duplex: audio function */
+static const desc_param duplex_params_audio_func[] = {
+ {
+ .id = AC_PAR_FUNCTION_TYPE,
+ .val = AC_GRP_AUDIO_FUNCTION,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_DUPLEX,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00020004,
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_GPIO_CAP,
+ .val = 0,
+ },{
+ .id = AC_PAR_AUDIO_FG_CAP,
+ .val = 0x00000808,
+ },{
+ .id = AC_PAR_POWER_STATE,
+ .val = 0,
+ },
+};
+
+/* duplex: nodes */
+static const desc_node duplex_nodes[] = {
+ {
+ .nid = AC_NODE_ROOT,
+ .name = "root",
+ .params = duplex_params_root,
+ .nparams = ARRAY_SIZE(duplex_params_root),
+ },{
+ .nid = 1,
+ .name = "func",
+ .params = duplex_params_audio_func,
+ .nparams = ARRAY_SIZE(duplex_params_audio_func),
+ },{
+ .nid = 2,
+ .name = "dac",
+ .params = common_params_audio_dac,
+ .nparams = ARRAY_SIZE(common_params_audio_dac),
+ .stindex = 0,
+ },{
+ .nid = 3,
+ .name = "out",
+ .params = common_params_audio_lineout,
+ .nparams = ARRAY_SIZE(common_params_audio_lineout),
+ .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+ (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) |
+ (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+ (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) |
+ 0x10),
+ .pinctl = AC_PINCTL_OUT_EN,
+ .conn = (uint32_t[]) { 2 },
+ },{
+ .nid = 4,
+ .name = "adc",
+ .params = duplex_params_audio_adc,
+ .nparams = ARRAY_SIZE(duplex_params_audio_adc),
+ .stindex = 1,
+ .conn = (uint32_t[]) { 5 },
+ },{
+ .nid = 5,
+ .name = "in",
+ .params = duplex_params_audio_linein,
+ .nparams = ARRAY_SIZE(duplex_params_audio_linein),
+ .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+ (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) |
+ (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+ (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) |
+ 0x20),
+ .pinctl = AC_PINCTL_IN_EN,
+ }
+};
+
+/* duplex: codec */
+static const desc_codec duplex = {
+ .name = "duplex",
+ .iid = QEMU_HDA_ID_DUPLEX,
+ .nodes = duplex_nodes,
+ .nnodes = ARRAY_SIZE(duplex_nodes),
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const char *fmt2name[] = {
+ [ AUD_FMT_U8 ] = "PCM-U8",
+ [ AUD_FMT_S8 ] = "PCM-S8",
+ [ AUD_FMT_U16 ] = "PCM-U16",
+ [ AUD_FMT_S16 ] = "PCM-S16",
+ [ AUD_FMT_U32 ] = "PCM-U32",
+ [ AUD_FMT_S32 ] = "PCM-S32",
+};
+
+typedef struct HDAAudioState HDAAudioState;
+typedef struct HDAAudioStream HDAAudioStream;
+
+struct HDAAudioStream {
+ HDAAudioState *state;
+ bool output, running, wbuf;
+ const desc_node *node;
+ uint32_t stream;
+ uint32_t channel;
+ uint32_t format;
+ uint32_t gain_left, gain_right;
+ bool mute_left, mute_right;
+ struct audsettings as;
+ union {
+ SWVoiceIn *in;
+ SWVoiceOut *out;
+ } voice;
+ uint8_t buf[HDA_BUFFER_SIZE];
+};
+
+struct HDAAudioState {
+ HDACodecDevice hda;
+ char name[32];
+
+ QEMUSoundCard card;
+ const desc_codec *desc;
+ HDAAudioStream st[4];
+ bool running[16];
+
+ /* properties */
+ uint32_t debug;
+};
+
+static void hda_audio_input_cb(void *opaque, int avail)
+{
+ HDAAudioStream *st = opaque;
+ int recv = 0;
+ bool rc;
+
+ while (avail - recv >= sizeof(st->buf)) {
+ if (!st->wbuf) {
+ AUD_read(st->voice.in, st->buf, sizeof(st->buf));
+ recv += sizeof(st->buf);
+ st->wbuf = true;
+ }
+ rc = hda_codec_xfer(&st->state->hda, st->stream, false,
+ st->buf, sizeof(st->buf));
+ if (!rc)
+ break;
+ st->wbuf = false;
+ }
+}
+
+static void hda_audio_output_cb(void *opaque, int avail)
+{
+ HDAAudioStream *st = opaque;
+ int sent = 0;
+ bool rc;
+
+ while (avail - sent >= sizeof(st->buf)) {
+ rc = hda_codec_xfer(&st->state->hda, st->stream, true,
+ st->buf, sizeof(st->buf));
+ if (!rc)
+ break;
+ AUD_write(st->voice.out, st->buf, sizeof(st->buf));
+ sent += sizeof(st->buf);
+ }
+}
+
+static void hda_audio_set_running(HDAAudioStream *st, bool running)
+{
+ if (st->node == NULL)
+ return;
+ if (st->running == running)
+ return;
+ st->running = running;
+ dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
+ st->running ? "on" : "off", st->stream);
+ if (st->output) {
+ AUD_set_active_out(st->voice.out, st->running);
+ } else {
+ AUD_set_active_in(st->voice.in, st->running);
+ }
+}
+
+static void hda_audio_set_amp(HDAAudioStream *st)
+{
+ bool muted;
+ uint32_t left, right;
+
+ if (st->node == NULL)
+ return;
+
+ muted = st->mute_left && st->mute_right;
+ left = st->mute_left ? 0 : st->gain_left;
+ right = st->mute_right ? 0 : st->gain_right;
+
+ left = left * 255 / QEMU_HDA_AMP_STEPS;
+ right = right * 255 / QEMU_HDA_AMP_STEPS;
+
+ if (st->output) {
+ AUD_set_volume_out(st->voice.out, muted, left, right);
+ } else {
+ AUD_set_volume_in(st->voice.in, muted, left, right);
+ }
+}
+
+static void hda_audio_setup(HDAAudioStream *st)
+{
+ if (st->node == NULL)
+ return;
+
+ dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
+ st->node->name, st->as.nchannels,
+ fmt2name[st->as.fmt], st->as.freq);
+
+ if (st->output) {
+ st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
+ st->node->name, st,
+ hda_audio_output_cb, &st->as);
+ } else {
+ st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
+ st->node->name, st,
+ hda_audio_input_cb, &st->as);
+ }
+}
+
+static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioStream *st;
+ const desc_node *node = NULL;
+ const desc_param *param;
+ uint32_t verb, payload, response, count, shift;
+
+ if ((data & 0x70000) == 0x70000) {
+ /* 12/8 id/payload */
+ verb = (data >> 8) & 0xfff;
+ payload = data & 0x00ff;
+ } else {
+ /* 4/16 id/payload */
+ verb = (data >> 8) & 0xf00;
+ payload = data & 0xffff;
+ }
+
+ node = hda_codec_find_node(a->desc, nid);
+ if (node == NULL)
+ goto fail;
+ dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
+ __FUNCTION__, nid, node->name, verb, payload);
+
+ switch (verb) {
+ /* all nodes */
+ case AC_VERB_PARAMETERS:
+ param = hda_codec_find_param(node, payload);
+ if (param == NULL)
+ goto fail;
+ hda_codec_response(hda, true, param->val);
+ break;
+ case AC_VERB_GET_SUBSYSTEM_ID:
+ hda_codec_response(hda, true, a->desc->iid);
+ break;
+
+ /* all functions */
+ case AC_VERB_GET_CONNECT_LIST:
+ param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
+ count = param ? param->val : 0;
+ response = 0;
+ shift = 0;
+ while (payload < count && shift < 32) {
+ response |= node->conn[payload] << shift;
+ payload++;
+ shift += 8;
+ }
+ hda_codec_response(hda, true, response);
+ break;
+
+ /* pin widget */
+ case AC_VERB_GET_CONFIG_DEFAULT:
+ hda_codec_response(hda, true, node->config);
+ break;
+ case AC_VERB_GET_PIN_WIDGET_CONTROL:
+ hda_codec_response(hda, true, node->pinctl);
+ break;
+ case AC_VERB_SET_PIN_WIDGET_CONTROL:
+ if (node->pinctl != payload) {
+ dprint(a, 1, "unhandled pin control bit\n");
+ }
+ hda_codec_response(hda, true, 0);
+ break;
+
+ /* audio in/out widget */
+ case AC_VERB_SET_CHANNEL_STREAMID:
+ st = a->st + node->stindex;
+ if (st->node == NULL)
+ goto fail;
+ hda_audio_set_running(st, false);
+ st->stream = (payload >> 4) & 0x0f;
+ st->channel = payload & 0x0f;
+ dprint(a, 2, "%s: stream %d, channel %d\n",
+ st->node->name, st->stream, st->channel);
+ hda_audio_set_running(st, a->running[st->stream]);
+ hda_codec_response(hda, true, 0);
+ break;
+ case AC_VERB_GET_CONV:
+ st = a->st + node->stindex;
+ if (st->node == NULL)
+ goto fail;
+ response = st->stream << 4 | st->channel;
+ hda_codec_response(hda, true, response);
+ break;
+ case AC_VERB_SET_STREAM_FORMAT:
+ st = a->st + node->stindex;
+ if (st->node == NULL)
+ goto fail;
+ st->format = payload;
+ hda_codec_parse_fmt(st->format, &st->as);
+ dprint(a, 1, "%s: format: %d x %s @ %d Hz\n",
+ st->node->name, st->as.nchannels,
+ fmt2name[st->as.fmt], st->as.freq);
+ hda_audio_setup(st);
+ hda_codec_response(hda, true, 0);
+ break;
+ case AC_VERB_GET_STREAM_FORMAT:
+ st = a->st + node->stindex;
+ if (st->node == NULL)
+ goto fail;
+ hda_codec_response(hda, true, st->format);
+ break;
+ case AC_VERB_GET_AMP_GAIN_MUTE:
+ st = a->st + node->stindex;
+ if (st->node == NULL)
+ goto fail;
+ if (payload & AC_AMP_GET_LEFT) {
+ response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
+ } else {
+ response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
+ }
+ hda_codec_response(hda, true, response);
+ break;
+ case AC_VERB_SET_AMP_GAIN_MUTE:
+ st = a->st + node->stindex;
+ if (st->node == NULL)
+ goto fail;
+ dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n",
+ st->node->name,
+ (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
+ (payload & AC_AMP_SET_INPUT) ? "i" : "-",
+ (payload & AC_AMP_SET_LEFT) ? "l" : "-",
+ (payload & AC_AMP_SET_RIGHT) ? "r" : "-",
+ (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
+ (payload & AC_AMP_GAIN),
+ (payload & AC_AMP_MUTE) ? "muted" : "");
+ if (payload & AC_AMP_SET_LEFT) {
+ st->gain_left = payload & AC_AMP_GAIN;
+ st->mute_left = payload & AC_AMP_MUTE;
+ }
+ if (payload & AC_AMP_SET_RIGHT) {
+ st->gain_right = payload & AC_AMP_GAIN;
+ st->mute_right = payload & AC_AMP_MUTE;
+ }
+ hda_audio_set_amp(st);
+ hda_codec_response(hda, true, 0);
+ break;
+
+ /* not supported */
+ case AC_VERB_SET_POWER_STATE:
+ case AC_VERB_GET_POWER_STATE:
+ case AC_VERB_GET_SDI_SELECT:
+ hda_codec_response(hda, true, 0);
+ break;
+ default:
+ goto fail;
+ }
+ return;
+
+fail:
+ dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
+ __FUNCTION__, nid, node ? node->name : "?", verb, payload);
+ hda_codec_response(hda, true, 0);
+}
+
+static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ int s;
+
+ a->running[stnr] = running;
+ for (s = 0; s < ARRAY_SIZE(a->st); s++) {
+ if (a->st[s].node == NULL)
+ continue;
+ if (a->st[s].stream != stnr)
+ continue;
+ hda_audio_set_running(&a->st[s], running);
+ }
+}
+
+static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioStream *st;
+ const desc_node *node;
+ const desc_param *param;
+ uint32_t i, type;
+
+ a->desc = desc;
+ snprintf(a->name, sizeof(a->name), "%s/%s",
+ a->hda.qdev.info->name, a->desc->name);
+ dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
+
+ AUD_register_card("hda", &a->card);
+ for (i = 0; i < a->desc->nnodes; i++) {
+ node = a->desc->nodes + i;
+ param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
+ if (NULL == param)
+ continue;
+ type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ switch (type) {
+ case AC_WID_AUD_OUT:
+ case AC_WID_AUD_IN:
+ assert(node->stindex < ARRAY_SIZE(a->st));
+ st = a->st + node->stindex;
+ st->state = a;
+ st->node = node;
+ if (type == AC_WID_AUD_OUT) {
+ /* unmute output by default */
+ st->gain_left = QEMU_HDA_AMP_STEPS;
+ st->gain_right = QEMU_HDA_AMP_STEPS;
+ st->output = true;
+ } else {
+ st->output = false;
+ }
+ st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
+ (1 << AC_FMT_CHAN_SHIFT);
+ hda_codec_parse_fmt(st->format, &st->as);
+ hda_audio_setup(st);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int hda_audio_init_output(HDACodecDevice *hda)
+{
+ return hda_audio_init(hda, &output);
+}
+
+static int hda_audio_init_duplex(HDACodecDevice *hda)
+{
+ return hda_audio_init(hda, &duplex);
+}
+
+static HDACodecDeviceInfo hda_audio_info_output = {
+ .qdev.name = "hda-output",
+ .qdev.desc = "HDA Audio Codec, output-only",
+ .qdev.size = sizeof(HDAAudioState),
+ .init = hda_audio_init_output,
+ .command = hda_audio_command,
+ .stream = hda_audio_stream,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static HDACodecDeviceInfo hda_audio_info_duplex = {
+ .qdev.name = "hda-duplex",
+ .qdev.desc = "HDA Audio Codec, duplex",
+ .qdev.size = sizeof(HDAAudioState),
+ .init = hda_audio_init_duplex,
+ .command = hda_audio_command,
+ .stream = hda_audio_stream,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void hda_audio_register(void)
+{
+ hda_codec_register(&hda_audio_info_output);
+ hda_codec_register(&hda_audio_info_duplex);
+}
+device_init(hda_audio_register);
diff --git a/hw/intel-hda-defs.h b/hw/intel-hda-defs.h
new file mode 100644
index 0000000..2e37e5b
--- /dev/null
+++ b/hw/intel-hda-defs.h
@@ -0,0 +1,717 @@
+#ifndef HW_INTEL_HDA_DEFS_H
+#define HW_INTEL_HDA_DEFS_H
+
+/* qemu */
+#define HDA_BUFFER_SIZE 256
+
+/* --------------------------------------------------------------------- */
+/* from linux/sound/pci/hda/hda_intel.c */
+
+/*
+ * registers
+ */
+#define ICH6_REG_GCAP 0x00
+#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
+#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
+#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
+#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
+#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
+#define ICH6_REG_VMIN 0x02
+#define ICH6_REG_VMAJ 0x03
+#define ICH6_REG_OUTPAY 0x04
+#define ICH6_REG_INPAY 0x06
+#define ICH6_REG_GCTL 0x08
+#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
+#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
+#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
+#define ICH6_REG_WAKEEN 0x0c
+#define ICH6_REG_STATESTS 0x0e
+#define ICH6_REG_GSTS 0x10
+#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
+#define ICH6_REG_INTCTL 0x20
+#define ICH6_REG_INTSTS 0x24
+#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
+#define ICH6_REG_SYNC 0x34
+#define ICH6_REG_CORBLBASE 0x40
+#define ICH6_REG_CORBUBASE 0x44
+#define ICH6_REG_CORBWP 0x48
+#define ICH6_REG_CORBRP 0x4a
+#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
+#define ICH6_REG_CORBCTL 0x4c
+#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
+#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
+#define ICH6_REG_CORBSTS 0x4d
+#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
+#define ICH6_REG_CORBSIZE 0x4e
+
+#define ICH6_REG_RIRBLBASE 0x50
+#define ICH6_REG_RIRBUBASE 0x54
+#define ICH6_REG_RIRBWP 0x58
+#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
+#define ICH6_REG_RINTCNT 0x5a
+#define ICH6_REG_RIRBCTL 0x5c
+#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
+#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
+#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
+#define ICH6_REG_RIRBSTS 0x5d
+#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
+#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
+#define ICH6_REG_RIRBSIZE 0x5e
+
+#define ICH6_REG_IC 0x60
+#define ICH6_REG_IR 0x64
+#define ICH6_REG_IRS 0x68
+#define ICH6_IRS_VALID (1<<1)
+#define ICH6_IRS_BUSY (1<<0)
+
+#define ICH6_REG_DPLBASE 0x70
+#define ICH6_REG_DPUBASE 0x74
+#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */
+
+/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
+
+/* stream register offsets from stream base */
+#define ICH6_REG_SD_CTL 0x00
+#define ICH6_REG_SD_STS 0x03
+#define ICH6_REG_SD_LPIB 0x04
+#define ICH6_REG_SD_CBL 0x08
+#define ICH6_REG_SD_LVI 0x0c
+#define ICH6_REG_SD_FIFOW 0x0e
+#define ICH6_REG_SD_FIFOSIZE 0x10
+#define ICH6_REG_SD_FORMAT 0x12
+#define ICH6_REG_SD_BDLPL 0x18
+#define ICH6_REG_SD_BDLPU 0x1c
+
+/* PCI space */
+#define ICH6_PCIREG_TCSEL 0x44
+
+/*
+ * other constants
+ */
+
+/* max number of SDs */
+/* ICH, ATI and VIA have 4 playback and 4 capture */
+#define ICH6_NUM_CAPTURE 4
+#define ICH6_NUM_PLAYBACK 4
+
+/* ULI has 6 playback and 5 capture */
+#define ULI_NUM_CAPTURE 5
+#define ULI_NUM_PLAYBACK 6
+
+/* ATI HDMI has 1 playback and 0 capture */
+#define ATIHDMI_NUM_CAPTURE 0
+#define ATIHDMI_NUM_PLAYBACK 1
+
+/* TERA has 4 playback and 3 capture */
+#define TERA_NUM_CAPTURE 3
+#define TERA_NUM_PLAYBACK 4
+
+/* this number is statically defined for simplicity */
+#define MAX_AZX_DEV 16
+
+/* max number of fragments - we may use more if allocating more pages for BDL */
+#define BDL_SIZE 4096
+#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
+#define AZX_MAX_FRAG 32
+/* max buffer size - no h/w limit, you can increase as you like */
+#define AZX_MAX_BUF_SIZE (1024*1024*1024)
+
+/* RIRB int mask: overrun[2], response[0] */
+#define RIRB_INT_RESPONSE 0x01
+#define RIRB_INT_OVERRUN 0x04
+#define RIRB_INT_MASK 0x05
+
+/* STATESTS int mask: S3,SD2,SD1,SD0 */
+#define AZX_MAX_CODECS 8
+#define AZX_DEFAULT_CODECS 4
+#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
+
+/* SD_CTL bits */
+#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
+#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
+#define SD_CTL_STRIPE (3 << 16) /* stripe control */
+#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
+#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
+#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
+#define SD_CTL_STREAM_TAG_SHIFT 20
+
+/* SD_CTL and SD_STS */
+#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
+#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
+#define SD_INT_COMPLETE 0x04 /* completion interrupt */
+#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
+ SD_INT_COMPLETE)
+
+/* SD_STS */
+#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
+
+/* INTCTL and INTSTS */
+#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
+#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
+#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
+
+/* below are so far hardcoded - should read registers in future */
+#define ICH6_MAX_CORB_ENTRIES 256
+#define ICH6_MAX_RIRB_ENTRIES 256
+
+/* position fix mode */
+enum {
+ POS_FIX_AUTO,
+ POS_FIX_LPIB,
+ POS_FIX_POSBUF,
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
+
+/* Defines for Nvidia HDA support */
+#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
+#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
+#define NVIDIA_HDA_ISTRM_COH 0x4d
+#define NVIDIA_HDA_OSTRM_COH 0x4c
+#define NVIDIA_HDA_ENABLE_COHBIT 0x01
+
+/* Defines for Intel SCH HDA snoop control */
+#define INTEL_SCH_HDA_DEVC 0x78
+#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
+
+/* Define IN stream 0 FIFO size offset in VIA controller */
+#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
+/* Define VIA HD Audio Device ID*/
+#define VIA_HDAC_DEVICE_ID 0x3288
+
+/* HD Audio class code */
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+
+/* --------------------------------------------------------------------- */
+/* from linux/sound/pci/hda/hda_codec.h */
+
+/*
+ * nodes
+ */
+#define AC_NODE_ROOT 0x00
+
+/*
+ * function group types
+ */
+enum {
+ AC_GRP_AUDIO_FUNCTION = 0x01,
+ AC_GRP_MODEM_FUNCTION = 0x02,
+};
+
+/*
+ * widget types
+ */
+enum {
+ AC_WID_AUD_OUT, /* Audio Out */
+ AC_WID_AUD_IN, /* Audio In */
+ AC_WID_AUD_MIX, /* Audio Mixer */
+ AC_WID_AUD_SEL, /* Audio Selector */
+ AC_WID_PIN, /* Pin Complex */
+ AC_WID_POWER, /* Power */
+ AC_WID_VOL_KNB, /* Volume Knob */
+ AC_WID_BEEP, /* Beep Generator */
+ AC_WID_VENDOR = 0x0f /* Vendor specific */
+};
+
+/*
+ * GET verbs
+ */
+#define AC_VERB_GET_STREAM_FORMAT 0x0a00
+#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
+#define AC_VERB_GET_PROC_COEF 0x0c00
+#define AC_VERB_GET_COEF_INDEX 0x0d00
+#define AC_VERB_PARAMETERS 0x0f00
+#define AC_VERB_GET_CONNECT_SEL 0x0f01
+#define AC_VERB_GET_CONNECT_LIST 0x0f02
+#define AC_VERB_GET_PROC_STATE 0x0f03
+#define AC_VERB_GET_SDI_SELECT 0x0f04
+#define AC_VERB_GET_POWER_STATE 0x0f05
+#define AC_VERB_GET_CONV 0x0f06
+#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
+#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
+#define AC_VERB_GET_PIN_SENSE 0x0f09
+#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
+#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
+#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */
+#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
+/* f10-f1a: GPIO */
+#define AC_VERB_GET_GPIO_DATA 0x0f15
+#define AC_VERB_GET_GPIO_MASK 0x0f16
+#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
+#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
+#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
+#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
+#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
+/* f20: AFG/MFG */
+#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
+#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
+#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
+#define AC_VERB_GET_HDMI_ELDD 0x0f2f
+#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30
+#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31
+#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
+#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
+#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
+
+/*
+ * SET verbs
+ */
+#define AC_VERB_SET_STREAM_FORMAT 0x200
+#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
+#define AC_VERB_SET_PROC_COEF 0x400
+#define AC_VERB_SET_COEF_INDEX 0x500
+#define AC_VERB_SET_CONNECT_SEL 0x701
+#define AC_VERB_SET_PROC_STATE 0x703
+#define AC_VERB_SET_SDI_SELECT 0x704
+#define AC_VERB_SET_POWER_STATE 0x705
+#define AC_VERB_SET_CHANNEL_STREAMID 0x706
+#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
+#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define AC_VERB_SET_PIN_SENSE 0x709
+#define AC_VERB_SET_BEEP_CONTROL 0x70a
+#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
+#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
+#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
+#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
+#define AC_VERB_SET_GPIO_DATA 0x715
+#define AC_VERB_SET_GPIO_MASK 0x716
+#define AC_VERB_SET_GPIO_DIRECTION 0x717
+#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
+#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
+#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
+#define AC_VERB_SET_EAPD 0x788
+#define AC_VERB_SET_CODEC_RESET 0x7ff
+#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
+#define AC_VERB_SET_HDMI_DIP_INDEX 0x730
+#define AC_VERB_SET_HDMI_DIP_DATA 0x731
+#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
+#define AC_VERB_SET_HDMI_CP_CTRL 0x733
+#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
+
+/*
+ * Parameter IDs
+ */
+#define AC_PAR_VENDOR_ID 0x00
+#define AC_PAR_SUBSYSTEM_ID 0x01
+#define AC_PAR_REV_ID 0x02
+#define AC_PAR_NODE_COUNT 0x04
+#define AC_PAR_FUNCTION_TYPE 0x05
+#define AC_PAR_AUDIO_FG_CAP 0x08
+#define AC_PAR_AUDIO_WIDGET_CAP 0x09
+#define AC_PAR_PCM 0x0a
+#define AC_PAR_STREAM 0x0b
+#define AC_PAR_PIN_CAP 0x0c
+#define AC_PAR_AMP_IN_CAP 0x0d
+#define AC_PAR_CONNLIST_LEN 0x0e
+#define AC_PAR_POWER_STATE 0x0f
+#define AC_PAR_PROC_CAP 0x10
+#define AC_PAR_GPIO_CAP 0x11
+#define AC_PAR_AMP_OUT_CAP 0x12
+#define AC_PAR_VOL_KNB_CAP 0x13
+#define AC_PAR_HDMI_LPCM_CAP 0x20
+
+/*
+ * AC_VERB_PARAMETERS results (32bit)
+ */
+
+/* Function Group Type */
+#define AC_FGT_TYPE (0xff<<0)
+#define AC_FGT_TYPE_SHIFT 0
+#define AC_FGT_UNSOL_CAP (1<<8)
+
+/* Audio Function Group Capabilities */
+#define AC_AFG_OUT_DELAY (0xf<<0)
+#define AC_AFG_IN_DELAY (0xf<<8)
+#define AC_AFG_BEEP_GEN (1<<16)
+
+/* Audio Widget Capabilities */
+#define AC_WCAP_STEREO (1<<0) /* stereo I/O */
+#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */
+#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */
+#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */
+#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */
+#define AC_WCAP_STRIPE (1<<5) /* stripe */
+#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */
+#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */
+#define AC_WCAP_CONN_LIST (1<<8) /* connection list */
+#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
+#define AC_WCAP_POWER (1<<10) /* power control */
+#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
+#define AC_WCAP_CP_CAPS (1<<12) /* content protection */
+#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */
+#define AC_WCAP_DELAY (0xf<<16)
+#define AC_WCAP_DELAY_SHIFT 16
+#define AC_WCAP_TYPE (0xf<<20)
+#define AC_WCAP_TYPE_SHIFT 20
+
+/* supported PCM rates and bits */
+#define AC_SUPPCM_RATES (0xfff << 0)
+#define AC_SUPPCM_BITS_8 (1<<16)
+#define AC_SUPPCM_BITS_16 (1<<17)
+#define AC_SUPPCM_BITS_20 (1<<18)
+#define AC_SUPPCM_BITS_24 (1<<19)
+#define AC_SUPPCM_BITS_32 (1<<20)
+
+/* supported PCM stream format */
+#define AC_SUPFMT_PCM (1<<0)
+#define AC_SUPFMT_FLOAT32 (1<<1)
+#define AC_SUPFMT_AC3 (1<<2)
+
+/* GP I/O count */
+#define AC_GPIO_IO_COUNT (0xff<<0)
+#define AC_GPIO_O_COUNT (0xff<<8)
+#define AC_GPIO_O_COUNT_SHIFT 8
+#define AC_GPIO_I_COUNT (0xff<<16)
+#define AC_GPIO_I_COUNT_SHIFT 16
+#define AC_GPIO_UNSOLICITED (1<<30)
+#define AC_GPIO_WAKE (1<<31)
+
+/* Converter stream, channel */
+#define AC_CONV_CHANNEL (0xf<<0)
+#define AC_CONV_STREAM (0xf<<4)
+#define AC_CONV_STREAM_SHIFT 4
+
+/* Input converter SDI select */
+#define AC_SDI_SELECT (0xf<<0)
+
+/* stream format id */
+#define AC_FMT_CHAN_SHIFT 0
+#define AC_FMT_CHAN_MASK (0x0f << 0)
+#define AC_FMT_BITS_SHIFT 4
+#define AC_FMT_BITS_MASK (7 << 4)
+#define AC_FMT_BITS_8 (0 << 4)
+#define AC_FMT_BITS_16 (1 << 4)
+#define AC_FMT_BITS_20 (2 << 4)
+#define AC_FMT_BITS_24 (3 << 4)
+#define AC_FMT_BITS_32 (4 << 4)
+#define AC_FMT_DIV_SHIFT 8
+#define AC_FMT_DIV_MASK (7 << 8)
+#define AC_FMT_MULT_SHIFT 11
+#define AC_FMT_MULT_MASK (7 << 11)
+#define AC_FMT_BASE_SHIFT 14
+#define AC_FMT_BASE_48K (0 << 14)
+#define AC_FMT_BASE_44K (1 << 14)
+#define AC_FMT_TYPE_SHIFT 15
+#define AC_FMT_TYPE_PCM (0 << 15)
+#define AC_FMT_TYPE_NON_PCM (1 << 15)
+
+/* Unsolicited response control */
+#define AC_UNSOL_TAG (0x3f<<0)
+#define AC_UNSOL_ENABLED (1<<7)
+#define AC_USRSP_EN AC_UNSOL_ENABLED
+
+/* Unsolicited responses */
+#define AC_UNSOL_RES_TAG (0x3f<<26)
+#define AC_UNSOL_RES_TAG_SHIFT 26
+#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
+#define AC_UNSOL_RES_SUBTAG_SHIFT 21
+#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
+#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
+#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
+#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */
+
+/* Pin widget capabilies */
+#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
+#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
+#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */
+#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */
+#define AC_PINCAP_OUT (1<<4) /* output capable */
+#define AC_PINCAP_IN (1<<5) /* input capable */
+#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
+/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
+ * but is marked reserved in the Intel HDA specification.
+ */
+#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
+/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
+ * in HD-audio specification
+ */
+#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
+#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can
+ * coexist with AC_PINCAP_HDMI
+ */
+#define AC_PINCAP_VREF (0x37<<8)
+#define AC_PINCAP_VREF_SHIFT 8
+#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
+#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */
+/* Vref status (used in pin cap) */
+#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */
+#define AC_PINCAP_VREF_50 (1<<1) /* 50% */
+#define AC_PINCAP_VREF_GRD (1<<2) /* ground */
+#define AC_PINCAP_VREF_80 (1<<4) /* 80% */
+#define AC_PINCAP_VREF_100 (1<<5) /* 100% */
+
+/* Amplifier capabilities */
+#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */
+#define AC_AMPCAP_OFFSET_SHIFT 0
+#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */
+#define AC_AMPCAP_NUM_STEPS_SHIFT 8
+#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB
+ * in 0.25dB
+ */
+#define AC_AMPCAP_STEP_SIZE_SHIFT 16
+#define AC_AMPCAP_MUTE (1<<31) /* mute capable */
+#define AC_AMPCAP_MUTE_SHIFT 31
+
+/* Connection list */
+#define AC_CLIST_LENGTH (0x7f<<0)
+#define AC_CLIST_LONG (1<<7)
+
+/* Supported power status */
+#define AC_PWRST_D0SUP (1<<0)
+#define AC_PWRST_D1SUP (1<<1)
+#define AC_PWRST_D2SUP (1<<2)
+#define AC_PWRST_D3SUP (1<<3)
+#define AC_PWRST_D3COLDSUP (1<<4)
+#define AC_PWRST_S3D3COLDSUP (1<<29)
+#define AC_PWRST_CLKSTOP (1<<30)
+#define AC_PWRST_EPSS (1U<<31)
+
+/* Power state values */
+#define AC_PWRST_SETTING (0xf<<0)
+#define AC_PWRST_ACTUAL (0xf<<4)
+#define AC_PWRST_ACTUAL_SHIFT 4
+#define AC_PWRST_D0 0x00
+#define AC_PWRST_D1 0x01
+#define AC_PWRST_D2 0x02
+#define AC_PWRST_D3 0x03
+
+/* Processing capabilies */
+#define AC_PCAP_BENIGN (1<<0)
+#define AC_PCAP_NUM_COEF (0xff<<8)
+#define AC_PCAP_NUM_COEF_SHIFT 8
+
+/* Volume knobs capabilities */
+#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
+#define AC_KNBCAP_DELTA (1<<7)
+
+/* HDMI LPCM capabilities */
+#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */
+#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */
+#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */
+#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */
+#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */
+#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */
+#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */
+#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */
+#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */
+#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */
+#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */
+#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */
+#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
+#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
+
+/*
+ * Control Parameters
+ */
+
+/* Amp gain/mute */
+#define AC_AMP_MUTE (1<<7)
+#define AC_AMP_GAIN (0x7f)
+#define AC_AMP_GET_INDEX (0xf<<0)
+
+#define AC_AMP_GET_LEFT (1<<13)
+#define AC_AMP_GET_RIGHT (0<<13)
+#define AC_AMP_GET_OUTPUT (1<<15)
+#define AC_AMP_GET_INPUT (0<<15)
+
+#define AC_AMP_SET_INDEX (0xf<<8)
+#define AC_AMP_SET_INDEX_SHIFT 8
+#define AC_AMP_SET_RIGHT (1<<12)
+#define AC_AMP_SET_LEFT (1<<13)
+#define AC_AMP_SET_INPUT (1<<14)
+#define AC_AMP_SET_OUTPUT (1<<15)
+
+/* DIGITAL1 bits */
+#define AC_DIG1_ENABLE (1<<0)
+#define AC_DIG1_V (1<<1)
+#define AC_DIG1_VCFG (1<<2)
+#define AC_DIG1_EMPHASIS (1<<3)
+#define AC_DIG1_COPYRIGHT (1<<4)
+#define AC_DIG1_NONAUDIO (1<<5)
+#define AC_DIG1_PROFESSIONAL (1<<6)
+#define AC_DIG1_LEVEL (1<<7)
+
+/* DIGITAL2 bits */
+#define AC_DIG2_CC (0x7f<<0)
+
+/* Pin widget control - 8bit */
+#define AC_PINCTL_EPT (0x3<<0)
+#define AC_PINCTL_EPT_NATIVE 0
+#define AC_PINCTL_EPT_HBR 3
+#define AC_PINCTL_VREFEN (0x7<<0)
+#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
+#define AC_PINCTL_VREF_50 1 /* 50% */
+#define AC_PINCTL_VREF_GRD 2 /* ground */
+#define AC_PINCTL_VREF_80 4 /* 80% */
+#define AC_PINCTL_VREF_100 5 /* 100% */
+#define AC_PINCTL_IN_EN (1<<5)
+#define AC_PINCTL_OUT_EN (1<<6)
+#define AC_PINCTL_HP_EN (1<<7)
+
+/* Pin sense - 32bit */
+#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
+#define AC_PINSENSE_PRESENCE (1<<31)
+#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */
+
+/* EAPD/BTL enable - 32bit */
+#define AC_EAPDBTL_BALANCED (1<<0)
+#define AC_EAPDBTL_EAPD (1<<1)
+#define AC_EAPDBTL_LR_SWAP (1<<2)
+
+/* HDMI ELD data */
+#define AC_ELDD_ELD_VALID (1<<31)
+#define AC_ELDD_ELD_DATA 0xff
+
+/* HDMI DIP size */
+#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */
+#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */
+
+/* HDMI DIP index */
+#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */
+#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */
+
+/* HDMI DIP xmit (transmit) control */
+#define AC_DIPXMIT_MASK (0x3<<6)
+#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */
+#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */
+#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */
+
+/* HDMI content protection (CP) control */
+#define AC_CPCTRL_CES (1<<9) /* current encryption state */
+#define AC_CPCTRL_READY (1<<8) /* ready bit */
+#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */
+#define AC_CPCTRL_STATE (3<<0) /* current CP request state */
+
+/* Converter channel <-> HDMI slot mapping */
+#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */
+#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */
+
+/* configuration default - 32bit */
+#define AC_DEFCFG_SEQUENCE (0xf<<0)
+#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
+#define AC_DEFCFG_ASSOC_SHIFT 4
+#define AC_DEFCFG_MISC (0xf<<8)
+#define AC_DEFCFG_MISC_SHIFT 8
+#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
+#define AC_DEFCFG_COLOR (0xf<<12)
+#define AC_DEFCFG_COLOR_SHIFT 12
+#define AC_DEFCFG_CONN_TYPE (0xf<<16)
+#define AC_DEFCFG_CONN_TYPE_SHIFT 16
+#define AC_DEFCFG_DEVICE (0xf<<20)
+#define AC_DEFCFG_DEVICE_SHIFT 20
+#define AC_DEFCFG_LOCATION (0x3f<<24)
+#define AC_DEFCFG_LOCATION_SHIFT 24
+#define AC_DEFCFG_PORT_CONN (0x3<<30)
+#define AC_DEFCFG_PORT_CONN_SHIFT 30
+
+/* device device types (0x0-0xf) */
+enum {
+ AC_JACK_LINE_OUT,
+ AC_JACK_SPEAKER,
+ AC_JACK_HP_OUT,
+ AC_JACK_CD,
+ AC_JACK_SPDIF_OUT,
+ AC_JACK_DIG_OTHER_OUT,
+ AC_JACK_MODEM_LINE_SIDE,
+ AC_JACK_MODEM_HAND_SIDE,
+ AC_JACK_LINE_IN,
+ AC_JACK_AUX,
+ AC_JACK_MIC_IN,
+ AC_JACK_TELEPHONY,
+ AC_JACK_SPDIF_IN,
+ AC_JACK_DIG_OTHER_IN,
+ AC_JACK_OTHER = 0xf,
+};
+
+/* jack connection types (0x0-0xf) */
+enum {
+ AC_JACK_CONN_UNKNOWN,
+ AC_JACK_CONN_1_8,
+ AC_JACK_CONN_1_4,
+ AC_JACK_CONN_ATAPI,
+ AC_JACK_CONN_RCA,
+ AC_JACK_CONN_OPTICAL,
+ AC_JACK_CONN_OTHER_DIGITAL,
+ AC_JACK_CONN_OTHER_ANALOG,
+ AC_JACK_CONN_DIN,
+ AC_JACK_CONN_XLR,
+ AC_JACK_CONN_RJ11,
+ AC_JACK_CONN_COMB,
+ AC_JACK_CONN_OTHER = 0xf,
+};
+
+/* jack colors (0x0-0xf) */
+enum {
+ AC_JACK_COLOR_UNKNOWN,
+ AC_JACK_COLOR_BLACK,
+ AC_JACK_COLOR_GREY,
+ AC_JACK_COLOR_BLUE,
+ AC_JACK_COLOR_GREEN,
+ AC_JACK_COLOR_RED,
+ AC_JACK_COLOR_ORANGE,
+ AC_JACK_COLOR_YELLOW,
+ AC_JACK_COLOR_PURPLE,
+ AC_JACK_COLOR_PINK,
+ AC_JACK_COLOR_WHITE = 0xe,
+ AC_JACK_COLOR_OTHER,
+};
+
+/* Jack location (0x0-0x3f) */
+/* common case */
+enum {
+ AC_JACK_LOC_NONE,
+ AC_JACK_LOC_REAR,
+ AC_JACK_LOC_FRONT,
+ AC_JACK_LOC_LEFT,
+ AC_JACK_LOC_RIGHT,
+ AC_JACK_LOC_TOP,
+ AC_JACK_LOC_BOTTOM,
+};
+/* bits 4-5 */
+enum {
+ AC_JACK_LOC_EXTERNAL = 0x00,
+ AC_JACK_LOC_INTERNAL = 0x10,
+ AC_JACK_LOC_SEPARATE = 0x20,
+ AC_JACK_LOC_OTHER = 0x30,
+};
+enum {
+ /* external on primary chasis */
+ AC_JACK_LOC_REAR_PANEL = 0x07,
+ AC_JACK_LOC_DRIVE_BAY,
+ /* internal */
+ AC_JACK_LOC_RISER = 0x17,
+ AC_JACK_LOC_HDMI,
+ AC_JACK_LOC_ATAPI,
+ /* others */
+ AC_JACK_LOC_MOBILE_IN = 0x37,
+ AC_JACK_LOC_MOBILE_OUT,
+};
+
+/* Port connectivity (0-3) */
+enum {
+ AC_JACK_PORT_COMPLEX,
+ AC_JACK_PORT_NONE,
+ AC_JACK_PORT_FIXED,
+ AC_JACK_PORT_BOTH,
+};
+
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS 32
+
+/* max. codec address */
+#define HDA_MAX_CODEC_ADDRESS 0x0f
+
+/* max number of PCM devics per card */
+#define HDA_MAX_PCMS 10
+
+/* --------------------------------------------------------------------- */
+
+#endif
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
new file mode 100644
index 0000000..af8909e
--- /dev/null
+++ b/hw/intel-hda.c
@@ -0,0 +1,1071 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "intel-hda.h"
+#include "intel-hda-defs.h"
+
+/* --------------------------------------------------------------------- */
+/* hda bus */
+
+static struct BusInfo hda_codec_bus_info = {
+ .name = "HDA",
+ .size = sizeof(HDACodecBus),
+ .props = (Property[]) {
+ DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
+
+void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus,
+ hda_codec_response_func response,
+ hda_codec_xfer_func xfer)
+{
+ qbus_create_inplace(&bus->qbus, &hda_codec_bus_info, dev, NULL);
+ bus->response = response;
+ bus->xfer = xfer;
+}
+
+static int hda_codec_dev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, qdev->parent_bus);
+ HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ HDACodecDeviceInfo *info = DO_UPCAST(HDACodecDeviceInfo, qdev, base);
+
+ dev->info = info;
+ if (dev->cad == -1) {
+ dev->cad = bus->next_cad;
+ }
+ if (dev->cad > 15)
+ return -1;
+ bus->next_cad = dev->cad + 1;
+ return info->init(dev);
+}
+
+void hda_codec_register(HDACodecDeviceInfo *info)
+{
+ info->qdev.init = hda_codec_dev_init;
+ info->qdev.bus_info = &hda_codec_bus_info;
+ qdev_register(&info->qdev);
+}
+
+HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad)
+{
+ DeviceState *qdev;
+ HDACodecDevice *cdev;
+
+ QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
+ cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ if (cdev->cad == cad) {
+ return cdev;
+ }
+ }
+ return NULL;
+}
+
+void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ bus->response(dev, solicited, response);
+}
+
+bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ return bus->xfer(dev, stnr, output, buf, len);
+}
+
+/* --------------------------------------------------------------------- */
+/* intel hda emulation */
+
+typedef struct IntelHDAStream IntelHDAStream;
+typedef struct IntelHDAState IntelHDAState;
+typedef struct IntelHDAReg IntelHDAReg;
+
+typedef struct bpl {
+ uint64_t addr;
+ uint32_t len;
+ uint32_t flags;
+} bpl;
+
+struct IntelHDAStream {
+ /* registers */
+ uint32_t ctl;
+ uint32_t lpib;
+ uint32_t cbl;
+ uint32_t lvi;
+ uint32_t fmt;
+ uint32_t bdlp_lbase;
+ uint32_t bdlp_ubase;
+
+ /* state */
+ bpl *bpl;
+ uint32_t bentries;
+ uint32_t bsize, be, bp;
+};
+
+struct IntelHDAState {
+ PCIDevice pci;
+ const char *name;
+ HDACodecBus codecs;
+
+ /* registers */
+ uint32_t g_ctl;
+ uint32_t wake_en;
+ uint32_t state_sts;
+ uint32_t int_ctl;
+ uint32_t int_sts;
+ uint32_t wall_clk;
+
+ uint32_t corb_lbase;
+ uint32_t corb_ubase;
+ uint32_t corb_rp;
+ uint32_t corb_wp;
+ uint32_t corb_ctl;
+ uint32_t corb_sts;
+ uint32_t corb_size;
+
+ uint32_t rirb_lbase;
+ uint32_t rirb_ubase;
+ uint32_t rirb_wp;
+ uint32_t rirb_cnt;
+ uint32_t rirb_ctl;
+ uint32_t rirb_sts;
+ uint32_t rirb_size;
+
+ uint32_t dp_lbase;
+ uint32_t dp_ubase;
+
+ /* streams */
+ IntelHDAStream st[8];
+
+ /* state */
+ int mmio_addr;
+ uint32_t rirb_count;
+ qemu_timeval wall_base;
+
+ /* debug logging */
+ const IntelHDAReg *last_reg;
+ uint32_t last_val;
+ uint32_t last_write;
+ uint32_t last_sec;
+ uint32_t repeat_count;
+
+ /* properties */
+ uint32_t debug;
+};
+
+struct IntelHDAReg {
+ const char *name; /* register name */
+ uint32_t size; /* size in bytes */
+ uint32_t reset; /* reset value */
+ uint32_t wmask; /* write mask */
+ uint32_t wclear; /* write 1 to clear bits */
+ uint32_t offset; /* location in IntelHDAState */
+ uint32_t shift; /* byte access entries for dwords */
+ uint32_t stream;
+ void (*whandler)(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old);
+ void (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg);
+};
+
+static void intel_hda_reset(DeviceState *dev);
+
+/* --------------------------------------------------------------------- */
+
+static target_phys_addr_t intel_hda_addr(uint32_t lbase, uint32_t ubase)
+{
+ target_phys_addr_t addr;
+
+#if TARGET_PHYS_ADDR_BITS == 32
+ addr = lbase;
+#else
+ addr = ubase;
+ addr <<= 32;
+ addr |= lbase;
+#endif
+ return addr;
+}
+
+static void intel_hda_update_int_sts(IntelHDAState *d)
+{
+ uint32_t sts = 0;
+ uint32_t i;
+
+ /* update controller status */
+ if (d->rirb_sts & ICH6_RBSTS_IRQ)
+ sts |= (1 << 30);
+ if (d->rirb_sts & ICH6_RBSTS_OVERRUN)
+ sts |= (1 << 30);
+ if (d->state_sts)
+ sts |= (1 << 30);
+
+ /* update stream status */
+ for (i = 0; i < 8; i++) {
+ /* buffer completion interrupt */
+ if (d->st[i].ctl & (1 << 26)) {
+ sts |= (1 << i);
+ }
+ }
+
+ /* update global status */
+ if (sts & d->int_ctl)
+ sts |= (1 << 31);
+
+ d->int_sts = sts;
+}
+
+static void intel_hda_update_irq(IntelHDAState *d)
+{
+ int level;
+
+ intel_hda_update_int_sts(d);
+ if (d->int_sts & (1 << 31) && d->int_ctl & (1 << 31)) {
+ level = 1;
+ } else {
+ level = 0;
+ }
+ dprint(d, 2, "%s: level %d\n", __FUNCTION__, level);
+ qemu_set_irq(d->pci.irq[0], level);
+}
+
+static void intel_hda_corb_run(IntelHDAState *d)
+{
+ target_phys_addr_t addr;
+ uint32_t rp, verb, cad, nid, data;
+ HDACodecDevice *codec;
+
+ for (;;) {
+ if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) {
+ dprint(d, 2, "%s: !run\n", __FUNCTION__);
+ return;
+ }
+ if ((d->corb_rp & 0xff) == d->corb_wp) {
+ dprint(d, 2, "%s: corb ring empty\n", __FUNCTION__);
+ return;
+ }
+ if (d->rirb_count == d->rirb_cnt) {
+ dprint(d, 2, "%s: rirb count reached\n", __FUNCTION__);
+ return;
+ }
+
+ rp = (d->corb_rp + 1) & 0xff;
+ addr = intel_hda_addr(d->corb_lbase, d->corb_ubase);
+ verb = ldl_phys(addr + 4*rp);
+ d->corb_rp = rp;
+
+ cad = (verb >> 28) & 0x0f;
+ assert(!(verb & (1 << 27))); /* indirect addressing, unspecified */
+ nid = (verb >> 20) & 0x7f;
+ data = verb & 0xfffff;
+
+ dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x: cad %d, nid %d, data 0x%x\n",
+ __FUNCTION__, rp, verb, cad, nid, data);
+ codec = hda_codec_find(&d->codecs, cad);
+ assert(codec != NULL);
+ codec->info->command(codec, nid, data);
+ }
+}
+
+static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
+ target_phys_addr_t addr;
+ uint32_t wp, ex;
+
+ assert(d->rirb_ctl & ICH6_RBCTL_DMA_EN);
+
+ ex = (solicited ? 0 : (1 << 4)) | dev->cad;
+ wp = (d->rirb_wp + 1) & 0xff;
+ addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase);
+ stl_phys(addr + 8*wp, response);
+ stl_phys(addr + 8*wp + 4, ex);
+ d->rirb_wp = wp;
+
+ dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n",
+ __FUNCTION__, wp, response, ex);
+
+ d->rirb_count++;
+ if (d->rirb_count == d->rirb_cnt) {
+ dprint(d, 2, "%s: rirb count reached (%d)\n", __FUNCTION__, d->rirb_count);
+ if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
+ d->rirb_sts |= ICH6_RBSTS_IRQ;
+ intel_hda_update_irq(d);
+ }
+ } else if ((d->corb_rp & 0xff) == d->corb_wp) {
+ dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __FUNCTION__,
+ d->rirb_count, d->rirb_cnt);
+ if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
+ d->rirb_sts |= ICH6_RBSTS_IRQ;
+ intel_hda_update_irq(d);
+ }
+ }
+}
+
+static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
+ IntelHDAStream *st = NULL;
+ target_phys_addr_t addr;
+ uint32_t s, copy, left;
+ bool irq = false;
+
+ for (s = 0; s < ARRAY_SIZE(d->st); s++) {
+ if (stnr == ((d->st[s].ctl >> 20) & 0x0f)) {
+ st = d->st + s;
+ break;
+ }
+ }
+ if (st == NULL)
+ return false;
+ if (st->bpl == NULL)
+ return false;
+ if (st->ctl & (1 << 26))
+ /*
+ * Wait with the next DMA xfer until the guest
+ * has acked the buffer completion interrupt
+ */
+ return false;
+
+ left = len;
+ while (left > 0) {
+ copy = left;
+ if (copy > st->bsize - st->lpib)
+ copy = st->bsize - st->lpib;
+ if (copy > st->bpl[st->be].len - st->bp)
+ copy = st->bpl[st->be].len - st->bp;
+
+ dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n",
+ st->be, st->bp, st->bpl[st->be].len, copy);
+
+ cpu_physical_memory_rw(st->bpl[st->be].addr + st->bp,
+ buf, copy, !output);
+ st->lpib += copy;
+ st->bp += copy;
+ buf += copy;
+ left -= copy;
+
+ if (st->bpl[st->be].len == st->bp) {
+ /* bpl entry filled */
+ if (st->bpl[st->be].flags & 0x01) {
+ irq = true;
+ }
+ st->bp = 0;
+ st->be++;
+ if (st->be == st->bentries) {
+ /* bpl wrap around */
+ st->be = 0;
+ st->lpib = 0;
+ }
+ }
+ }
+ if (d->dp_lbase & 0x01) {
+ addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase);
+ stl_phys(addr + 8*s, st->lpib);
+ }
+ /* TODO: update DP */
+ dprint(d, 3, "dma: --\n");
+
+ if (irq) {
+ st->ctl |= (1 << 26); /* buffer completion interrupt */
+ intel_hda_update_irq(d);
+ }
+ return true;
+}
+
+static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st)
+{
+ target_phys_addr_t addr;
+ uint32_t i;
+
+ addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase);
+ st->bentries = st->lvi +1;
+ qemu_free(st->bpl);
+ st->bpl = qemu_malloc(sizeof(bpl) * st->bentries);
+ for (i = 0; i < st->bentries; i++, addr += 16) {
+ st->bpl[i].addr = ldq_phys(addr);
+ st->bpl[i].len = ldl_phys(addr + 8);
+ st->bpl[i].flags = ldl_phys(addr + 12);
+ dprint(d, 1, "bdl/%d: 0x%" PRIx64 " +0x%x, 0x%x\n",
+ i, st->bpl[i].addr, st->bpl[i].len, st->bpl[i].flags);
+ }
+
+ st->bsize = st->cbl;
+ st->lpib = 0;
+ st->be = 0;
+ st->bp = 0;
+}
+
+static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running)
+{
+ DeviceState *qdev;
+ HDACodecDevice *cdev;
+
+ QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
+ cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ if (cdev->info->stream)
+ cdev->info->stream(cdev, stream, running);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ if ((d->g_ctl & ICH6_GCTL_RESET) == 0) {
+ intel_hda_reset(&d->pci.qdev);
+ }
+}
+
+static void intel_hda_set_state_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_update_irq(d);
+}
+
+static void intel_hda_set_int_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_update_irq(d);
+}
+
+static void intel_hda_get_wall_clk(IntelHDAState *d, const IntelHDAReg *reg)
+{
+ qemu_timeval tv;
+ uint64_t usec;
+
+ qemu_gettimeofday(&tv);
+ usec = (tv.tv_sec - d->wall_base.tv_sec) * 1000000;
+ usec += tv.tv_usec;
+ usec -= d->wall_base.tv_sec;
+ d->wall_clk = usec * 24; /* 24 MHz */
+}
+
+static void intel_hda_set_corb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_corb_run(d);
+}
+
+static void intel_hda_set_corb_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_corb_run(d);
+}
+
+static void intel_hda_set_rirb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ if (d->rirb_wp & ICH6_RIRBWP_RST)
+ d->rirb_wp = 0;
+}
+
+static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_update_irq(d);
+
+ if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) {
+ /* cleared ICH6_RBSTS_IRQ */
+ d->rirb_count = 0;
+ intel_hda_corb_run(d);
+ }
+}
+
+static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ IntelHDAStream *st = d->st + reg->stream;
+
+ if (st->ctl & 0x01) {
+ /* reset */
+ dprint(d, 1, "st #%d: reset\n", reg->stream);
+ st->ctl = 0;
+ }
+ if ((st->ctl & 0x02) != (old & 0x02)) {
+ uint32_t stnr = (st->ctl >> 20) & 0x0f;
+ /* run bit flipped */
+ if (st->ctl & 0x02) {
+ /* start */
+ dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n",
+ reg->stream, stnr, st->cbl);
+ intel_hda_parse_bdl(d, st);
+ intel_hda_notify_codecs(d, stnr, true);
+ } else {
+ /* stop */
+ dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr);
+ intel_hda_notify_codecs(d, stnr, false);
+ }
+ }
+ intel_hda_update_irq(d);
+}
+
+/* --------------------------------------------------------------------- */
+
+#define ST_REG(_n, _o) (0x80 + (_n) * 0x20 + (_o))
+
+static const struct IntelHDAReg regtab[] = {
+ /* global */
+ [ ICH6_REG_GCAP ] = {
+ .name = "GCAP",
+ .size = 2,
+ .reset = 0x4401,
+ },
+ [ ICH6_REG_VMIN ] = {
+ .name = "VMIN",
+ .size = 1,
+ },
+ [ ICH6_REG_VMAJ ] = {
+ .name = "VMAJ",
+ .size = 1,
+ .reset = 1,
+ },
+ [ ICH6_REG_OUTPAY ] = {
+ .name = "OUTPAY",
+ .size = 2,
+ .reset = 0x3c,
+ },
+ [ ICH6_REG_INPAY ] = {
+ .name = "INPAY",
+ .size = 2,
+ .reset = 0x1d,
+ },
+ [ ICH6_REG_GCTL ] = {
+ .name = "GCTL",
+ .size = 4,
+ .wmask = 0x0103,
+ .offset = offsetof(IntelHDAState, g_ctl),
+ .whandler = intel_hda_set_g_ctl,
+ },
+ [ ICH6_REG_WAKEEN ] = {
+ .name = "WAKEEN",
+ .size = 2,
+ .offset = offsetof(IntelHDAState, wake_en),
+ },
+ [ ICH6_REG_STATESTS ] = {
+ .name = "STATESTS",
+ .size = 2,
+ .wmask = 0x3fff,
+ .wclear = 0x3fff,
+ .offset = offsetof(IntelHDAState, state_sts),
+ .whandler = intel_hda_set_state_sts,
+ },
+
+ /* interrupts */
+ [ ICH6_REG_INTCTL ] = {
+ .name = "INTCTL",
+ .size = 4,
+ .wmask = 0xc00000ff,
+ .offset = offsetof(IntelHDAState, int_ctl),
+ .whandler = intel_hda_set_int_ctl,
+ },
+ [ ICH6_REG_INTSTS ] = {
+ .name = "INTSTS",
+ .size = 4,
+ .wmask = 0xc00000ff,
+ .wclear = 0xc00000ff,
+ .offset = offsetof(IntelHDAState, int_sts),
+ },
+
+ /* misc */
+ [ ICH6_REG_WALLCLK ] = {
+ .name = "WALLCLK",
+ .size = 4,
+ .offset = offsetof(IntelHDAState, wall_clk),
+ .rhandler = intel_hda_get_wall_clk,
+ },
+ [ ICH6_REG_WALLCLK + 0x2000 ] = {
+ .name = "WALLCLK(alias)",
+ .size = 4,
+ .offset = offsetof(IntelHDAState, wall_clk),
+ .rhandler = intel_hda_get_wall_clk,
+ },
+
+ /* dma engine */
+ [ ICH6_REG_CORBLBASE ] = {
+ .name = "CORBLBASE",
+ .size = 4,
+ .wmask = 0xffffff80,
+ .offset = offsetof(IntelHDAState, corb_lbase),
+ },
+ [ ICH6_REG_CORBUBASE ] = {
+ .name = "CORBUBASE",
+ .size = 4,
+ .wmask = 0xffffffff,
+ .offset = offsetof(IntelHDAState, corb_ubase),
+ },
+ [ ICH6_REG_CORBWP ] = {
+ .name = "CORBWP",
+ .size = 2,
+ .wmask = 0xff,
+ .offset = offsetof(IntelHDAState, corb_wp),
+ .whandler = intel_hda_set_corb_wp,
+ },
+ [ ICH6_REG_CORBRP ] = {
+ .name = "CORBRP",
+ .size = 2,
+ .wmask = 0x80ff,
+ .offset = offsetof(IntelHDAState, corb_rp),
+ },
+ [ ICH6_REG_CORBCTL ] = {
+ .name = "CORBCTL",
+ .size = 1,
+ .wmask = 0x03,
+ .offset = offsetof(IntelHDAState, corb_ctl),
+ .whandler = intel_hda_set_corb_ctl,
+ },
+ [ ICH6_REG_CORBSTS ] = {
+ .name = "CORBSTS",
+ .size = 1,
+ .wmask = 0x01,
+ .wclear = 0x01,
+ .offset = offsetof(IntelHDAState, corb_sts),
+ },
+ [ ICH6_REG_CORBSIZE ] = {
+ .name = "CORBSIZE",
+ .size = 1,
+ .reset = 0x42,
+ .offset = offsetof(IntelHDAState, corb_size),
+ },
+ [ ICH6_REG_RIRBLBASE ] = {
+ .name = "RIRBLBASE",
+ .size = 4,
+ .wmask = 0xffffff80,
+ .offset = offsetof(IntelHDAState, rirb_lbase),
+ },
+ [ ICH6_REG_RIRBUBASE ] = {
+ .name = "RIRBUBASE",
+ .size = 4,
+ .wmask = 0xffffffff,
+ .offset = offsetof(IntelHDAState, rirb_ubase),
+ },
+ [ ICH6_REG_RIRBWP ] = {
+ .name = "RIRBWP",
+ .size = 2,
+ .wmask = 0x8000,
+ .offset = offsetof(IntelHDAState, rirb_wp),
+ .whandler = intel_hda_set_rirb_wp,
+ },
+ [ ICH6_REG_RINTCNT ] = {
+ .name = "RINTCNT",
+ .size = 2,
+ .wmask = 0xff,
+ .offset = offsetof(IntelHDAState, rirb_cnt),
+ },
+ [ ICH6_REG_RIRBCTL ] = {
+ .name = "RIRBCTL",
+ .size = 1,
+ .wmask = 0x07,
+ .offset = offsetof(IntelHDAState, rirb_ctl),
+ },
+ [ ICH6_REG_RIRBSTS ] = {
+ .name = "RIRBSTS",
+ .size = 1,
+ .wmask = 0x05,
+ .wclear = 0x05,
+ .offset = offsetof(IntelHDAState, rirb_sts),
+ .whandler = intel_hda_set_rirb_sts,
+ },
+ [ ICH6_REG_RIRBSIZE ] = {
+ .name = "RIRBSIZE",
+ .size = 1,
+ .reset = 0x42,
+ .offset = offsetof(IntelHDAState, rirb_size),
+ },
+ [ ICH6_REG_DPLBASE ] = {
+ .name = "DPLBASE",
+ .size = 4,
+ .wmask = 0xffffff81,
+ .offset = offsetof(IntelHDAState, dp_lbase),
+ },
+ [ ICH6_REG_DPUBASE ] = {
+ .name = "DPUBASE",
+ .size = 4,
+ .wmask = 0xffffffff,
+ .offset = offsetof(IntelHDAState, dp_ubase),
+ },
+
+#define HDA_STREAM(_t, _i) \
+ [ ST_REG(_i, ICH6_REG_SD_CTL) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CTL", \
+ .size = 4, \
+ .wmask = 0x1cff001f, \
+ .offset = offsetof(IntelHDAState, st[_i].ctl), \
+ .whandler = intel_hda_set_st_ctl, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_CTL) + 2] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CTL(stnr)", \
+ .size = 1, \
+ .shift = 16, \
+ .wmask = 0x00ff0000, \
+ .offset = offsetof(IntelHDAState, st[_i].ctl), \
+ .whandler = intel_hda_set_st_ctl, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_STS)] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CTL(sts)", \
+ .size = 1, \
+ .shift = 24, \
+ .wmask = 0x1c000000, \
+ .wclear = 0x1c000000, \
+ .offset = offsetof(IntelHDAState, st[_i].ctl), \
+ .whandler = intel_hda_set_st_ctl, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " LPIB", \
+ .size = 4, \
+ .offset = offsetof(IntelHDAState, st[_i].lpib), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " LPIB(alias)", \
+ .size = 4, \
+ .offset = offsetof(IntelHDAState, st[_i].lpib), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_CBL) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CBL", \
+ .size = 4, \
+ .wmask = 0xffffffff, \
+ .offset = offsetof(IntelHDAState, st[_i].cbl), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_LVI) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " LVI", \
+ .size = 2, \
+ .wmask = 0x00ff, \
+ .offset = offsetof(IntelHDAState, st[_i].lvi), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_FIFOSIZE) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " FIFOS", \
+ .size = 2, \
+ .reset = HDA_BUFFER_SIZE, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_FORMAT) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " FMT", \
+ .size = 2, \
+ .wmask = 0x7f7f, \
+ .offset = offsetof(IntelHDAState, st[_i].fmt), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_BDLPL) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " BDLPL", \
+ .size = 4, \
+ .wmask = 0xffffff80, \
+ .offset = offsetof(IntelHDAState, st[_i].bdlp_lbase), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_BDLPU) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " BDLPU", \
+ .size = 4, \
+ .wmask = 0xffffffff, \
+ .offset = offsetof(IntelHDAState, st[_i].bdlp_ubase), \
+ }, \
+
+ HDA_STREAM("IN", 0)
+ HDA_STREAM("IN", 1)
+ HDA_STREAM("IN", 2)
+ HDA_STREAM("IN", 3)
+
+ HDA_STREAM("OUT", 4)
+ HDA_STREAM("OUT", 5)
+ HDA_STREAM("OUT", 6)
+ HDA_STREAM("OUT", 7)
+
+};
+
+static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, target_phys_addr_t addr)
+{
+ const IntelHDAReg *reg;
+
+ if (addr >= sizeof(regtab)/sizeof(regtab[0]))
+ goto noreg;
+ reg = regtab+addr;
+ if (reg->name == NULL)
+ goto noreg;
+ return reg;
+
+noreg:
+ dprint(d, 1, "unknown register, addr 0x%x\n", (int) addr);
+ return NULL;
+}
+
+static uint32_t *intel_hda_reg_addr(IntelHDAState *d, const IntelHDAReg *reg)
+{
+ uint8_t *addr = (void*)d;
+
+ addr += reg->offset;
+ return (uint32_t*)addr;
+}
+
+static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32_t val,
+ uint32_t wmask)
+{
+ uint32_t *addr;
+ uint32_t old;
+
+ if (!reg)
+ return;
+
+ if (d->debug) {
+ time_t now = time(NULL);
+ if (d->last_write && d->last_reg == reg && d->last_val == val) {
+ d->repeat_count++;
+ if (d->last_sec != now) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ } else {
+ if (d->repeat_count) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ }
+ dprint(d, 2, "write %-16s: 0x%x (%x)\n", reg->name, val, wmask);
+ d->last_write = 1;
+ d->last_reg = reg;
+ d->last_val = val;
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ }
+ assert(reg->offset != 0);
+
+ addr = intel_hda_reg_addr(d, reg);
+ old = *addr;
+
+ if (reg->shift) {
+ val <<= reg->shift;
+ wmask <<= reg->shift;
+ }
+ wmask &= reg->wmask;
+ val ^= reg->wclear;
+ *addr &= ~wmask;
+ *addr |= wmask & val;
+
+ if (reg->whandler) {
+ reg->whandler(d, reg, old);
+ }
+}
+
+static uint32_t intel_hda_reg_read(IntelHDAState *d, const IntelHDAReg *reg,
+ uint32_t rmask)
+{
+ uint32_t *addr, ret;
+
+ if (!reg)
+ return 0;
+
+ if (reg->rhandler) {
+ reg->rhandler(d, reg);
+ }
+
+ if (reg->offset == 0) {
+ /* constant read-only register */
+ ret = reg->reset;
+ } else {
+ addr = intel_hda_reg_addr(d, reg);
+ ret = *addr;
+ if (reg->shift) {
+ ret >>= reg->shift;
+ }
+ ret &= rmask;
+ }
+ if (d->debug) {
+ time_t now = time(NULL);
+ if (!d->last_write && d->last_reg == reg && d->last_val == ret) {
+ d->repeat_count++;
+ if (d->last_sec != now) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ } else {
+ if (d->repeat_count) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ }
+ dprint(d, 2, "read %-16s: 0x%x (%x)\n", reg->name, ret, rmask);
+ d->last_write = 0;
+ d->last_reg = reg;
+ d->last_val = ret;
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ }
+ return ret;
+}
+
+static void intel_hda_regs_reset(IntelHDAState *d)
+{
+ uint32_t *addr;
+ int i;
+
+ for (i = 0; i < sizeof(regtab)/sizeof(regtab[0]); i++) {
+ if (regtab[i].name == NULL)
+ continue;
+ if (regtab[i].offset == 0)
+ continue;
+ addr = intel_hda_reg_addr(d, regtab + i);
+ *addr = regtab[i].reset;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ intel_hda_reg_write(d, reg, val, 0xff);
+}
+
+static void intel_hda_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ intel_hda_reg_write(d, reg, val, 0xffff);
+}
+
+static void intel_hda_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ intel_hda_reg_write(d, reg, val, 0xffffffff);
+}
+
+static uint32_t intel_hda_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ return intel_hda_reg_read(d, reg, 0xff);
+}
+
+static uint32_t intel_hda_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ return intel_hda_reg_read(d, reg, 0xffff);
+}
+
+static uint32_t intel_hda_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ return intel_hda_reg_read(d, reg, 0xffffffff);
+}
+
+static CPUReadMemoryFunc * const intel_hda_mmio_read[3] = {
+ intel_hda_mmio_readb,
+ intel_hda_mmio_readw,
+ intel_hda_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const intel_hda_mmio_write[3] = {
+ intel_hda_mmio_writeb,
+ intel_hda_mmio_writew,
+ intel_hda_mmio_writel,
+};
+
+static void intel_hda_map(PCIDevice *pci, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+
+ cpu_register_physical_memory(addr, 0x4000, d->mmio_addr);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_reset(DeviceState *dev)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci.qdev, dev);
+ DeviceState *qdev;
+ HDACodecDevice *cdev;
+
+ intel_hda_regs_reset(d);
+ qemu_gettimeofday(&d->wall_base);
+
+ /* reset codecs */
+ QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
+ cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ if (qdev->info->reset)
+ qdev->info->reset(qdev);
+ d->state_sts |= (1 << cdev->cad);
+ }
+}
+
+static int intel_hda_init(PCIDevice *pci)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+ uint8_t *conf = d->pci.config;
+
+ d->name = d->pci.qdev.info->name;
+
+ pci_config_set_vendor_id(conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(conf, 0x2668);
+ pci_config_set_revision(conf, 1);
+ pci_config_set_class(conf, PCI_CLASS_MULTIMEDIA_HD_AUDIO);
+ pci_config_set_interrupt_pin(conf, 1);
+
+ /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
+ conf[0x40] = 0x01;
+
+ d->mmio_addr = cpu_register_io_memory(intel_hda_mmio_read,
+ intel_hda_mmio_write, d);
+ pci_register_bar(&d->pci, 0, 0x4000, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ intel_hda_map);
+
+ hda_codec_bus_init(&d->pci.qdev, &d->codecs,
+ intel_hda_response, intel_hda_xfer);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_intel_hda = {
+ .name = "intel-hda",
+ .version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(pci, IntelHDAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo intel_hda_info = {
+ .qdev.name = "intel-hda",
+ .qdev.desc = "Intel HD Audio Controller",
+ .qdev.size = sizeof(IntelHDAState),
+ .qdev.vmsd = &vmstate_intel_hda,
+ .qdev.reset = intel_hda_reset,
+ .init = intel_hda_init,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void intel_hda_register(void)
+{
+ pci_qdev_register(&intel_hda_info);
+}
+device_init(intel_hda_register);
diff --git a/hw/intel-hda.h b/hw/intel-hda.h
new file mode 100644
index 0000000..ba290ec
--- /dev/null
+++ b/hw/intel-hda.h
@@ -0,0 +1,61 @@
+#ifndef HW_INTEL_HDA_H
+#define HW_INTEL_HDA_H
+
+#include "qdev.h"
+
+/* --------------------------------------------------------------------- */
+/* hda bus */
+
+typedef struct HDACodecBus HDACodecBus;
+typedef struct HDACodecDevice HDACodecDevice;
+typedef struct HDACodecDeviceInfo HDACodecDeviceInfo;
+
+typedef void (*hda_codec_response_func)(HDACodecDevice *dev,
+ bool solicited, uint32_t response);
+typedef bool (*hda_codec_xfer_func)(HDACodecDevice *dev,
+ uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len);
+
+struct HDACodecBus {
+ BusState qbus;
+ uint32_t next_cad;
+ hda_codec_response_func response;
+ hda_codec_xfer_func xfer;
+};
+
+struct HDACodecDevice {
+ DeviceState qdev;
+ HDACodecDeviceInfo *info;
+ uint32_t cad; /* codec address */
+};
+
+struct HDACodecDeviceInfo {
+ DeviceInfo qdev;
+ int (*init)(HDACodecDevice *dev);
+ void (*command)(HDACodecDevice *dev, uint32_t nid, uint32_t data);
+ void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running);
+};
+
+void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus,
+ hda_codec_response_func response,
+ hda_codec_xfer_func xfer);
+void hda_codec_register(HDACodecDeviceInfo *info);
+HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad);
+
+void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response);
+bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len);
+
+/* --------------------------------------------------------------------- */
+
+#define dprint(_dev, _level, _fmt, ...) \
+ do { \
+ if (_dev->debug >= _level) { \
+ fprintf(stderr, "%s: ", _dev->name); \
+ fprintf(stderr, _fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+/* --------------------------------------------------------------------- */
+
+#endif
--
1.7.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [Qemu-devel] Re: [PATCH 5/6] Add support for a USB audio device model
2010-10-27 16:04 ` [Qemu-devel] [PATCH 5/6] Add support for a USB audio device model Gerd Hoffmann
@ 2010-10-27 17:13 ` H. Peter Anvin
2010-10-28 8:01 ` Gerd Hoffmann
0 siblings, 1 reply; 20+ messages in thread
From: H. Peter Anvin @ 2010-10-27 17:13 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On 10/27/2010 9:04 AM, Gerd Hoffmann wrote:
> From: H. Peter Anvin<hpa@linux.intel.com>
>
> This brings a usb audio device to qemu. Output only, fixed at
> 16bit stereo @ 480000 Hz. Based on a patch from
> H. Peter Anvin<hpa@linux.intel.com>
>
> Usage: add '-device usb-audio' to your qemu command line.
>
> Works sorta ok on a idle machine. Known issues:
>
> * Is *very* sensitive to latencies: when the uhci emulation misses one
> of the usb frame rate wakeups (1k/sec!) you'll loose/delay data from
> the audio stream, resulting in dropouts.
> * Also seems to not play very well with the usb tablet (and/or usb hub,
> to be investigated). Best try this as the only device on a usb bus.
> * Burns quite some CPU due to usb polling.
>
> In short: It brings the qemu usb emulation to its limits. Enjoy!
>
> Signed-off-by: Gerd Hoffmann<kraxel@redhat.com>
Please don't apply this. This patch is outdated and has several glaring
bugs. I can send a better patch later (I'm in an all-day meeting with
insanely bad networking.)
-hpa
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
` (5 preceding siblings ...)
2010-10-27 16:04 ` [Qemu-devel] [PATCH 6/6] Add Intel HD Audio support to qemu Gerd Hoffmann
@ 2010-10-27 17:46 ` malc
2010-10-27 17:51 ` malc
2010-10-28 8:47 ` Gerd Hoffmann
6 siblings, 2 replies; 20+ messages in thread
From: malc @ 2010-10-27 17:46 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On Wed, 27 Oct 2010, Gerd Hoffmann wrote:
> Hi,
>
> This patch series brings a bunch of pulseaudio tweaks and two new sound
> devices. Number one is the slightly improved usb-audio patch which was
> originally created by hpa. Number two is a virtual HD Audio controller
> with HDA codecs.
>
Nice timing there. I wonder how long it took bright folks at redhat to
code this HDA stuff? Since yesterday i was contacted by folks who wanted
to pay money to get VirtualBox's HDA ported to QEMU, and it took me from
16:00 today till basically 15 minutes ago to get the sound pumping from
DOS and Linux..
Regardless of the answer to my question the NIH is such a nice thing, eh?
[..snip..]
--
mailto:av1474@comtv.ru
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
2010-10-27 17:46 ` [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series malc
@ 2010-10-27 17:51 ` malc
2010-10-28 8:47 ` Gerd Hoffmann
1 sibling, 0 replies; 20+ messages in thread
From: malc @ 2010-10-27 17:51 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On Wed, 27 Oct 2010, malc wrote:
> On Wed, 27 Oct 2010, Gerd Hoffmann wrote:
>
> > Hi,
> >
> > This patch series brings a bunch of pulseaudio tweaks and two new sound
> > devices. Number one is the slightly improved usb-audio patch which was
> > originally created by hpa. Number two is a virtual HD Audio controller
> > with HDA codecs.
> >
>
> Nice timing there. I wonder how long it took bright folks at redhat to
> code this HDA stuff? Since yesterday i was contacted by folks who wanted
> to pay money to get VirtualBox's HDA ported to QEMU, and it took me from
> 16:00 today till basically 15 minutes ago to get the sound pumping from
> DOS and Linux..
>
> Regardless of the answer to my question the NIH is such a nice thing, eh?
>
> [..snip..]
>
Uh, Wu Fengguang did an initial stab at porting the code, sorry for
fogetting to mention this.
P.S. It would be nice to see audio related stuff being CC-ed to audio
maintainer btw.
--
mailto:av1474@comtv.ru
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Qemu-devel] Re: [PATCH 5/6] Add support for a USB audio device model
2010-10-27 17:13 ` [Qemu-devel] " H. Peter Anvin
@ 2010-10-28 8:01 ` Gerd Hoffmann
2010-10-28 15:23 ` H. Peter Anvin
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-28 8:01 UTC (permalink / raw)
To: H. Peter Anvin; +Cc: qemu-devel
On 10/27/10 19:13, H. Peter Anvin wrote:
> On 10/27/2010 9:04 AM, Gerd Hoffmann wrote:
>> This brings a usb audio device to qemu. Output only, fixed at
>> 16bit stereo @ 480000 Hz. Based on a patch from
>> H. Peter Anvin<hpa@linux.intel.com>
> Please don't apply this. This patch is outdated and has several glaring
> bugs. I can send a better patch later (I'm in an all-day meeting with
> insanely bad networking.)
Oh, I thought you've abandoned the project ...
Note that I've update the patch too and fixed at least some of those
bugs. Nevertheless I'll be happy to see your latest bits and try to
merge the best of both versions.
cheers,
Gerd
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
2010-10-27 17:46 ` [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series malc
2010-10-27 17:51 ` malc
@ 2010-10-28 8:47 ` Gerd Hoffmann
2010-10-28 9:46 ` malc
1 sibling, 1 reply; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-28 8:47 UTC (permalink / raw)
To: malc; +Cc: qemu-devel
Hi,
> Nice timing there. I wonder how long it took bright folks at redhat to
> code this HDA stuff?
A bunch of days. Played with the usb-audio patch first. But I suspect
getting timing-sensitive isochronous usb devices emulated reasonable
well is pretty hard due to the latency requirements. And it isn't just
the usb subsytem, all qemu must improve here. Example: enabling the
threaded vnc server improves usb-audio sound quality, you don't get
dropouts on every bulky screen update then.
But for now I tried HDA route instead.
> Since yesterday i was contacted by folks who wanted
> to pay money to get VirtualBox's HDA ported to QEMU, and it took me from
> 16:00 today till basically 15 minutes ago to get the sound pumping from
> DOS and Linux..
Oh, there is a HDA driver for DOS? /me looks surprised ...
> Regardless of the answer to my question the NIH is such a nice thing, eh?
Sure ;) I don't do that just for fun though. I *have* looked at the
vbox driver first.
The fundamental problem is the qemu world didn't stop at the point where
vbox forked off, and of course in vbox things are changing too. We have
alot of infrastructure for drivers which isn't in vbox, and likewise the
other way around. Most notable difference is qemu's qdev is quite
useful to model the HDA bus.
So there are basically two options:
(1) Port the vbox driver to qemu, then to tons of changes and
cleanups to properly integrate into modern qemu.
(2) Start over from scratch.
I believe in the end it wouldn't have saved work to use the vbox code as
starting point.
cheers,
Gerd
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
2010-10-28 8:47 ` Gerd Hoffmann
@ 2010-10-28 9:46 ` malc
2010-10-28 10:06 ` malc
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: malc @ 2010-10-28 9:46 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On Thu, 28 Oct 2010, Gerd Hoffmann wrote:
> Hi,
>
> > Nice timing there. I wonder how long it took bright folks at redhat to
> > code this HDA stuff?
>
> A bunch of days. Played with the usb-audio patch first. But I suspect
> getting timing-sensitive isochronous usb devices emulated reasonable well is
> pretty hard due to the latency requirements. And it isn't just the usb
> subsytem, all qemu must improve here. Example: enabling the threaded vnc
> server improves usb-audio sound quality, you don't get dropouts on every bulky
> screen update then.
We (me, Wu Fengguang and Hans Peter Anvin) tried to do something about
usb-audio, to no avail.
>
> But for now I tried HDA route instead.
>
> > Since yesterday i was contacted by folks who wanted
> > to pay money to get VirtualBox's HDA ported to QEMU, and it took me from
> > 16:00 today till basically 15 minutes ago to get the sound pumping from
> > DOS and Linux..
>
> Oh, there is a HDA driver for DOS? /me looks surprised ...
DOS doesn't need drivers you know, anyhow:
http://mpxplay.sourceforge.net/
>
> > Regardless of the answer to my question the NIH is such a nice thing, eh?
>
> Sure ;) I don't do that just for fun though. I *have* looked at the vbox
> driver first.
>
> The fundamental problem is the qemu world didn't stop at the point where vbox
> forked off, and of course in vbox things are changing too. We have alot of
> infrastructure for drivers which isn't in vbox, and likewise the other way
> around. Most notable difference is qemu's qdev is quite useful to model the
> HDA bus.
>
> So there are basically two options:
>
> (1) Port the vbox driver to qemu, then to tons of changes and
> cleanups to properly integrate into modern qemu.
> (2) Start over from scratch.
>
> I believe in the end it wouldn't have saved work to use the vbox code as
> starting point.
At the very least their code does work here (on a PPC with DOS and
mpxplay) and yours doesn't. I'd have to check things on my x86_64 box
to ensure it's not a endianness issue of some sort, otherwise things
are hard to explain since mpxplay's hda handling is based on ALSA.
--
mailto:av1474@comtv.ru
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
2010-10-28 9:46 ` malc
@ 2010-10-28 10:06 ` malc
2010-10-28 10:06 ` Gerd Hoffmann
2010-10-28 22:21 ` Gerd Hoffmann
2 siblings, 0 replies; 20+ messages in thread
From: malc @ 2010-10-28 10:06 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On Thu, 28 Oct 2010, malc wrote:
> On Thu, 28 Oct 2010, Gerd Hoffmann wrote:
>
[..snip..]
> > I believe in the end it wouldn't have saved work to use the vbox code as
> > starting point.
>
> At the very least their code does work here (on a PPC with DOS and
> mpxplay) and yours doesn't. I'd have to check things on my x86_64 box
> to ensure it's not a endianness issue of some sort, otherwise things
> are hard to explain since mpxplay's hda handling is based on ALSA.
Nope, doesn't work on x86_64 either.
--
mailto:av1474@comtv.ru
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
2010-10-28 9:46 ` malc
2010-10-28 10:06 ` malc
@ 2010-10-28 10:06 ` Gerd Hoffmann
2010-10-28 22:21 ` Gerd Hoffmann
2 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-28 10:06 UTC (permalink / raw)
To: malc; +Cc: qemu-devel
Hi,
> At the very least their code does work here (on a PPC with DOS and
> mpxplay) and yours doesn't. I'd have to check things on my x86_64 box
> to ensure it's not a endianness issue of some sort,
It most likely *is* endianness, the mmio_read/write handlers don't swap
bytes on bigendian hosts. That is the only endian issue I'm aware of
though. Guest memory access goes through {stl,ldl}_phys() + friends and
thus should be fine as-is.
/me goes try dos+mpxplay ...
cheers,
Gerd
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Qemu-devel] Re: [PATCH 5/6] Add support for a USB audio device model
2010-10-28 8:01 ` Gerd Hoffmann
@ 2010-10-28 15:23 ` H. Peter Anvin
2010-10-28 16:13 ` Gerd Hoffmann
2010-10-28 15:28 ` H. Peter Anvin
2010-10-28 15:29 ` H. Peter Anvin
2 siblings, 1 reply; 20+ messages in thread
From: H. Peter Anvin @ 2010-10-28 15:23 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On 10/28/2010 1:01 AM, Gerd Hoffmann wrote:
> On 10/27/10 19:13, H. Peter Anvin wrote:
>> On 10/27/2010 9:04 AM, Gerd Hoffmann wrote:
>
>>> This brings a usb audio device to qemu. Output only, fixed at
>>> 16bit stereo @ 480000 Hz. Based on a patch from
>>> H. Peter Anvin<hpa@linux.intel.com>
>
>> Please don't apply this. This patch is outdated and has several glaring
>> bugs. I can send a better patch later (I'm in an all-day meeting with
>> insanely bad networking.)
>
> Oh, I thought you've abandoned the project ...
>
> Note that I've update the patch too and fixed at least some of those
> bugs. Nevertheless I'll be happy to see your latest bits and try to
> merge the best of both versions.
>
I have a git tree on kernel.org with the latest version. However, the
rate-matching code needs to be ripped out since it just plain doesn't
work, and tends to produce worse results than no rate matching:
http://git.kernel.org/?p=virt/qemu/hpa/qemu-usb-audio-wip.git;a=shortlog;h=refs/heads/usb-audio
-hpa
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Qemu-devel] Re: [PATCH 5/6] Add support for a USB audio device model
2010-10-28 8:01 ` Gerd Hoffmann
2010-10-28 15:23 ` H. Peter Anvin
@ 2010-10-28 15:28 ` H. Peter Anvin
2010-10-28 15:29 ` H. Peter Anvin
2 siblings, 0 replies; 20+ messages in thread
From: H. Peter Anvin @ 2010-10-28 15:28 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On 10/28/2010 1:01 AM, Gerd Hoffmann wrote:
> On 10/27/10 19:13, H. Peter Anvin wrote:
>> On 10/27/2010 9:04 AM, Gerd Hoffmann wrote:
>
>>> This brings a usb audio device to qemu. Output only, fixed at
>>> 16bit stereo @ 480000 Hz. Based on a patch from
>>> H. Peter Anvin<hpa@linux.intel.com>
>
>> Please don't apply this. This patch is outdated and has several glaring
>> bugs. I can send a better patch later (I'm in an all-day meeting with
>> insanely bad networking.)
>
> Oh, I thought you've abandoned the project ...
>
> Note that I've update the patch too and fixed at least some of those
> bugs. Nevertheless I'll be happy to see your latest bits and try to
> merge the best of both versions.
>
> cheers,
> Gerd
Grmf ... looks like I have multiple pieces of code in multiple places...
I need to reconcile the best version I have by hand first.
-hpa
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Qemu-devel] Re: [PATCH 5/6] Add support for a USB audio device model
2010-10-28 8:01 ` Gerd Hoffmann
2010-10-28 15:23 ` H. Peter Anvin
2010-10-28 15:28 ` H. Peter Anvin
@ 2010-10-28 15:29 ` H. Peter Anvin
2 siblings, 0 replies; 20+ messages in thread
From: H. Peter Anvin @ 2010-10-28 15:29 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
On 10/28/2010 1:01 AM, Gerd Hoffmann wrote:
> On 10/27/10 19:13, H. Peter Anvin wrote:
>> On 10/27/2010 9:04 AM, Gerd Hoffmann wrote:
>
>>> This brings a usb audio device to qemu. Output only, fixed at
>>> 16bit stereo @ 480000 Hz. Based on a patch from
>>> H. Peter Anvin<hpa@linux.intel.com>
>
>> Please don't apply this. This patch is outdated and has several glaring
>> bugs. I can send a better patch later (I'm in an all-day meeting with
>> insanely bad networking.)
>
> Oh, I thought you've abandoned the project ...
>
> Note that I've update the patch too and fixed at least some of those
> bugs. Nevertheless I'll be happy to see your latest bits and try to
> merge the best of both versions.
I'm going to have to look at this later... I'm in a F2F meeting most of
the day today.
-hpa
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Qemu-devel] Re: [PATCH 5/6] Add support for a USB audio device model
2010-10-28 15:23 ` H. Peter Anvin
@ 2010-10-28 16:13 ` Gerd Hoffmann
0 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-28 16:13 UTC (permalink / raw)
To: H. Peter Anvin; +Cc: qemu-devel
Hi,
> I have a git tree on kernel.org with the latest version.
Thanks, I'll have a look when I find some time.
> However, the
> rate-matching code needs to be ripped out since it just plain doesn't
> work, and tends to produce worse results than no rate matching:
Doesn't surprise me. There are a bunch of more serve issues which have
to be fixed before rate-matching has a chance to produce useful results.
cheers,
Gerd
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series.
2010-10-28 9:46 ` malc
2010-10-28 10:06 ` malc
2010-10-28 10:06 ` Gerd Hoffmann
@ 2010-10-28 22:21 ` Gerd Hoffmann
2 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2010-10-28 22:21 UTC (permalink / raw)
To: malc; +Cc: qemu-devel
Hi,
> At the very least their code does work here (on a PPC with DOS and
> mpxplay) and yours doesn't.
Pull latest fixes from audio.2 branch @ freedesktop.org, mpxplay works
for me (on x86_64). Test reports from your ppc box are welcome too.
cheers,
Gerd
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2010-10-28 22:21 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-27 16:04 [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 1/6] pa: allow processing buffer pieces Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 2/6] pa: process 1/4 buffer max at once Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 3/6] pa: setup buffer attrs Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 4/6] pa: tweak config Gerd Hoffmann
2010-10-27 16:04 ` [Qemu-devel] [PATCH 5/6] Add support for a USB audio device model Gerd Hoffmann
2010-10-27 17:13 ` [Qemu-devel] " H. Peter Anvin
2010-10-28 8:01 ` Gerd Hoffmann
2010-10-28 15:23 ` H. Peter Anvin
2010-10-28 16:13 ` Gerd Hoffmann
2010-10-28 15:28 ` H. Peter Anvin
2010-10-28 15:29 ` H. Peter Anvin
2010-10-27 16:04 ` [Qemu-devel] [PATCH 6/6] Add Intel HD Audio support to qemu Gerd Hoffmann
2010-10-27 17:46 ` [Qemu-devel] [PATCH 0/6] The windows 7 audio support patch series malc
2010-10-27 17:51 ` malc
2010-10-28 8:47 ` Gerd Hoffmann
2010-10-28 9:46 ` malc
2010-10-28 10:06 ` malc
2010-10-28 10:06 ` Gerd Hoffmann
2010-10-28 22:21 ` Gerd Hoffmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).