goldfish: enable 64 bit goldfish audio device
Enable the 64 bit audio device emulation in the goldfish platform.
Change-Id: Ia3d289b52d9acdc8d3a7b18e66a0476496e9a489
Signed-off-by: Jun Tian <jun.j.tian@intel.com>
diff --git a/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT b/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT
index d375d4c..88220e8 100644
--- a/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT
+++ b/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT
@@ -502,6 +502,11 @@
0x20 START_READ
0x24 READ_BUFFER_AVAILABLE
+ # For 64-bit guest CPUs
+ 0x28 SET_WRITE_BUFFER_1_HIGH W: Set high 32 bits of 1st kernel output buffer address.
+ 0x30 SET_WRITE_BUFFER_2_HIGH W: Set high 32 bits of 2nd kernel output buffer address.
+ 0x34 SET_READ_BUFFER_HIGH W: Set high 32 bits of kernel input buffer address.
+
This device implements a virtual sound card with the following properties:
- Stereo output at fixed 44.1 kHz frequency, using signed 16-bit samples.
@@ -511,9 +516,14 @@
Optional.
For output, the kernel driver allocates two internal buffers to hold output
-samples, and passes their physical address with
-IO_WRITE(SET_WRITE_BUFFER_1, <buffer1>) and
-IO_WRITE(SET_WRITE_BUFFER_2, <buffer2>).
+samples, and passes their physical address to the emulator as follows:
+
+ #if 64BIT_GUEST_CPU
+ IO_WRITE(SET_WRITE_BUFFER_1_HIGH, (uint32_t)(buffer1 >> 32));
+ IO_WRITE(SET_WRITE_BUFFER_2_HIGH, (uint32_t)(buffer2 >> 32));
+ #endif
+ IO_WRITE(SET_WRITE_BUFFER_1, (uint32_t)buffer1);
+ IO_WRITE(SET_WRITE_BUFFER_2, (uint32_t)buffer2);
After this, samples will be sent from the driver to the virtual device by
using one of IO_WRITE(WRITE_BUFFER_1, <length1>) or
@@ -540,9 +550,10 @@
For input, the driver should first IO_READ(READ_SUPPORTED), which will return 1
if the virtual device supports input, or 0 otherwise. If it does support it,
the driver must allocate an internal buffer and send its physical address with
-IO_WRITE(SET_READ_BUFFER, <read-buffer>), then perform
-IO_WRITE(START_READ, <read-buffer-length>) to start recording and specify
-the kernel's buffer length.
+IO_WRITE(SET_READ_BUFFER, <read-buffer>) (with a previous write to
+SET_READ_BUFFER_HIGH on 64-bit guest CPUS), then perform
+IO_WRITE(START_READ, <read-buffer-length>) to start recording and
+specify the kernel's buffer length.
Later, the device will raise its IRQ and set bit2 of 'int_status' to indicate
there are incoming samples to the driver. In its interrupt handler, the latter
diff --git a/hw/android/goldfish/audio.c b/hw/android/goldfish/audio.c
index ce0b642..228a7ca 100644
--- a/hw/android/goldfish/audio.c
+++ b/hw/android/goldfish/audio.c
@@ -34,38 +34,43 @@
#define USE_QEMU_AUDIO_IN 1
enum {
- /* audio status register */
- AUDIO_INT_STATUS = 0x00,
- /* set this to enable IRQ */
- AUDIO_INT_ENABLE = 0x04,
- /* set these to specify buffer addresses */
- AUDIO_SET_WRITE_BUFFER_1 = 0x08,
- AUDIO_SET_WRITE_BUFFER_2 = 0x0C,
- /* set number of bytes in buffer to write */
- AUDIO_WRITE_BUFFER_1 = 0x10,
- AUDIO_WRITE_BUFFER_2 = 0x14,
+ /* audio status register */
+ AUDIO_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ AUDIO_INT_ENABLE = 0x04,
+ /* set these to specify buffer addresses */
+ AUDIO_SET_WRITE_BUFFER_1 = 0x08,
+ AUDIO_SET_WRITE_BUFFER_2 = 0x0C,
+ /* set number of bytes in buffer to write */
+ AUDIO_WRITE_BUFFER_1 = 0x10,
+ AUDIO_WRITE_BUFFER_2 = 0x14,
- /* true if audio input is supported */
- AUDIO_READ_SUPPORTED = 0x18,
- /* buffer to use for audio input */
- AUDIO_SET_READ_BUFFER = 0x1C,
+ /* true if audio input is supported */
+ AUDIO_READ_SUPPORTED = 0x18,
+ /* buffer to use for audio input */
+ AUDIO_SET_READ_BUFFER = 0x1C,
- /* driver writes number of bytes to read */
- AUDIO_START_READ = 0x20,
+ /* driver writes number of bytes to read */
+ AUDIO_START_READ = 0x20,
- /* number of bytes available in read buffer */
- AUDIO_READ_BUFFER_AVAILABLE = 0x24,
+ /* number of bytes available in read buffer */
+ AUDIO_READ_BUFFER_AVAILABLE = 0x24,
- /* AUDIO_INT_STATUS bits */
+ /* for 64-bit guest CPUs only */
+ AUDIO_SET_WRITE_BUFFER_1_HIGH = 0x28,
+ AUDIO_SET_WRITE_BUFFER_2_HIGH = 0x30,
+ AUDIO_SET_READ_BUFFER_HIGH = 0x34,
- /* this bit set when it is safe to write more bytes to the buffer */
- AUDIO_INT_WRITE_BUFFER_1_EMPTY = 1U << 0,
- AUDIO_INT_WRITE_BUFFER_2_EMPTY = 1U << 1,
- AUDIO_INT_READ_BUFFER_FULL = 1U << 2,
+ /* AUDIO_INT_STATUS bits */
+
+ /* this bit set when it is safe to write more bytes to the buffer */
+ AUDIO_INT_WRITE_BUFFER_1_EMPTY = 1U << 0,
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY = 1U << 1,
+ AUDIO_INT_READ_BUFFER_FULL = 1U << 2,
};
struct goldfish_audio_buff {
- uint32_t address;
+ uint64_t address;
uint32_t length;
uint8* data;
uint32_t capacity;
@@ -147,7 +152,13 @@
static void
goldfish_audio_buff_set_address( struct goldfish_audio_buff* b, uint32_t addr )
{
- b->address = addr;
+ uint64_set_low(&b->address, addr);
+}
+
+static void
+goldfish_audio_buff_set_address_high( struct goldfish_audio_buff* b, uint32_t addr )
+{
+ uint64_set_high(&b->address, addr);
}
static void
@@ -210,27 +221,8 @@
return read;
}
-static void
-goldfish_audio_buff_put( struct goldfish_audio_buff* b, QEMUFile* f )
-{
- qemu_put_be32(f, b->address );
- qemu_put_be32(f, b->length );
- qemu_put_be32(f, b->offset );
- qemu_put_buffer(f, b->data, b->length );
-}
-
-static void
-goldfish_audio_buff_get( struct goldfish_audio_buff* b, QEMUFile* f )
-{
- b->address = qemu_get_be32(f);
- b->length = qemu_get_be32(f);
- b->offset = qemu_get_be32(f);
- goldfish_audio_buff_ensure(b, b->length);
- qemu_get_buffer(f, b->data, b->length);
-}
-
/* update this whenever you change the goldfish_audio_state structure */
-#define AUDIO_STATE_SAVE_VERSION 2
+#define AUDIO_STATE_SAVE_VERSION 3
#define QFIELD_STRUCT struct goldfish_audio_state
QFIELD_BEGIN(audio_state_fields)
@@ -240,6 +232,28 @@
QFIELD_INT32(current_buffer),
QFIELD_END
+static void
+goldfish_audio_buff_put( struct goldfish_audio_buff* b, QEMUFile* f )
+{
+ qemu_put_be64(f, b->address );
+ qemu_put_be32(f, b->length );
+ qemu_put_be32(f, b->offset );
+ qemu_put_buffer(f, b->data, b->length );
+}
+
+static void
+goldfish_audio_buff_get( struct goldfish_audio_buff* b, QEMUFile* f, int version_id )
+{
+ if (version_id == (AUDIO_STATE_SAVE_VERSION - 1))
+ b->address = (uint64_t)qemu_get_be32(f);
+ else
+ b->address = qemu_get_be64(f);
+ b->length = qemu_get_be32(f);
+ b->offset = qemu_get_be32(f);
+ goldfish_audio_buff_ensure(b, b->length);
+ qemu_get_buffer(f, b->data, b->length);
+}
+
static void audio_state_save( QEMUFile* f, void* opaque )
{
struct goldfish_audio_state* s = opaque;
@@ -256,14 +270,15 @@
struct goldfish_audio_state* s = opaque;
int ret;
- if (version_id != AUDIO_STATE_SAVE_VERSION)
+ if ((version_id != AUDIO_STATE_SAVE_VERSION) &&
+ (version_id != (AUDIO_STATE_SAVE_VERSION - 1))) {
return -1;
-
+ }
ret = qemu_get_struct(f, audio_state_fields, s);
if (!ret) {
- goldfish_audio_buff_get( s->out_buff1, f );
- goldfish_audio_buff_get( s->out_buff2, f );
- goldfish_audio_buff_get (s->in_buff, f);
+ goldfish_audio_buff_get( s->out_buff1, f, version_id);
+ goldfish_audio_buff_get( s->out_buff2, f, version_id);
+ goldfish_audio_buff_get (s->in_buff, f, version_id);
}
// Similar to enable_audio - without the buffer reset.
@@ -427,11 +442,21 @@
D( "%s: AUDIO_SET_WRITE_BUFFER_1 %08x", __FUNCTION__, val);
goldfish_audio_buff_set_address( s->out_buff1, val );
break;
+ case AUDIO_SET_WRITE_BUFFER_1_HIGH:
+ /* save pointer to buffer 1 */
+ D( "%s: AUDIO_SET_WRITE_BUFFER_1_HIGH %08x", __FUNCTION__, val);
+ goldfish_audio_buff_set_address_high( s->out_buff1, val );
+ break;
case AUDIO_SET_WRITE_BUFFER_2:
/* save pointer to buffer 2 */
D( "%s: AUDIO_SET_WRITE_BUFFER_2 %08x", __FUNCTION__, val);
goldfish_audio_buff_set_address( s->out_buff2, val );
break;
+ case AUDIO_SET_WRITE_BUFFER_2_HIGH:
+ /* save pointer to buffer 2 */
+ D( "%s: AUDIO_SET_WRITE_BUFFER_2_HIGH %08x", __FUNCTION__, val);
+ goldfish_audio_buff_set_address_high( s->out_buff2, val );
+ break;
case AUDIO_WRITE_BUFFER_1:
/* record that data in buffer 1 is ready to write */
//D( "%s: AUDIO_WRITE_BUFFER_1 %08x", __FUNCTION__, val);
@@ -462,6 +487,12 @@
goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
break;
+ case AUDIO_SET_READ_BUFFER_HIGH:
+ /* save pointer to the read buffer */
+ goldfish_audio_buff_set_address_high( s->in_buff, val );
+ D( "%s: AUDIO_SET_READ_BUFFER_HIGH %08x", __FUNCTION__, val );
+ break;
+
default:
cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
}