[Qemu-devel] [PATCH V5] Add AACI audio playback support to the ARM Versatile/PB platform
This driver emulates the ARM AACI interface (PL041) connected to a LM4549 codec. It enables audio playback for the Versatile/PB platform. Limitations: - Supports only a playback on one channel (Versatile/Vexpress) - Supports only one TX FIFO in compact-mode or non-compact mode. - Supports playback of 12, 16, 18 and 20 bits samples. - Record is not supported. - The PL041 is hardwired to a LM4549 codec. Versatile/PB test build: linux-2.6.38.5 buildroot-2010.11 alsa-lib-1.0.22 alsa-utils-1.0.22 mpg123-0.66 Qemu host: Ubuntu 10.04 in Vmware/OS X Playback tested successfully with speaker-test/aplay/mpg123. Signed-off-by: Mathieu Sonet --- v4->v5 * Move the lm4549 post_load hook in lm4549.c * Fix naked debug printf in lm4549.c * Clarify the size of the lm4549 audio buffer Makefile.target |1 + hw/lm4549.c | 336 hw/lm4549.h | 43 hw/pl041.c | 636 ++ hw/pl041.h | 135 hw/pl041.hx | 81 +++ hw/versatilepb.c |8 + 7 files changed, 1240 insertions(+), 0 deletions(-) create mode 100644 hw/lm4549.c create mode 100644 hw/lm4549.h create mode 100644 hw/pl041.c create mode 100644 hw/pl041.h create mode 100644 hw/pl041.hx diff --git a/Makefile.target b/Makefile.target index 417f23e..25b9fc1 100644 --- a/Makefile.target +++ b/Makefile.target @@ -355,6 +355,7 @@ obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o +obj-arm-y += pl041.o lm4549.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o diff --git a/hw/lm4549.c b/hw/lm4549.c new file mode 100644 index 000..4d5b831 --- /dev/null +++ b/hw/lm4549.c @@ -0,0 +1,336 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * * + * + * This driver emulates the LM4549 codec. + * + * It supports only one playback voice and no record voice. + */ + +#include "hw.h" +#include "audio/audio.h" +#include "lm4549.h" + +#if 0 +#define LM4549_DEBUG 1 +#endif + +#if 0 +#define LM4549_DUMP_DAC_INPUT 1 +#endif + +#ifdef LM4549_DEBUG +#define DPRINTF(fmt, ...) \ +do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#if defined(LM4549_DUMP_DAC_INPUT) +#include +static FILE *fp_dac_input; +#endif + +/* LM4549 register list */ +enum { +LM4549_Reset= 0x00, +LM4549_Master_Volume= 0x02, +LM4549_Line_Out_Volume = 0x04, +LM4549_Master_Volume_Mono = 0x06, +LM4549_PC_Beep_Volume = 0x0A, +LM4549_Phone_Volume = 0x0C, +LM4549_Mic_Volume = 0x0E, +LM4549_Line_In_Volume = 0x10, +LM4549_CD_Volume= 0x12, +LM4549_Video_Volume = 0x14, +LM4549_Aux_Volume = 0x16, +LM4549_PCM_Out_Volume = 0x18, +LM4549_Record_Select= 0x1A, +LM4549_Record_Gain = 0x1C, +LM4549_General_Purpose = 0x20, +LM4549_3D_Control = 0x22, +LM4549_Powerdown_Ctrl_Stat = 0x26, +LM4549_Ext_Audio_ID = 0x28, +LM4549_Ext_Audio_Stat_Ctrl = 0x2A, +LM4549_PCM_Front_DAC_Rate = 0x2C, +LM4549_PCM_ADC_Rate = 0x32, +LM4549_Vendor_ID1 = 0x7C, +LM4549_Vendor_ID2 = 0x7E +}; + +static void lm4549_reset(lm4549_state *s) +{ +uint16_t *regfile = s->regfile; + +regfile[LM4549_Reset] = 0x0d50; +regfile[LM4549_Master_Volume] = 0x8008; +regfile[LM4549_Line_Out_Volume] = 0x8000; +regfile[LM4549_Master_Volume_Mono] = 0x8000; +regfile[LM4549_PC_Beep_Volume] = 0x; +regfile[LM4549_Phone_Volume]= 0x8008; +regfile[LM4549_Mic_Volume] = 0x8008; +regfile[LM4549_Line_In_Volume] = 0x8808; +regfile[LM4549_CD_Volume] = 0x8808; +regfile[LM4549_Video_Volume]= 0x8808; +regfile[LM4549_Aux_Volume] = 0x8808; +regfile[LM4549_PCM_Out_Volume] = 0x8808; +regfile[LM4549_Record_Select] = 0x; +regfile[LM4549_Record_Gain] = 0x8000; +regfile[LM4549_General_Purpose] = 0x; +regfile[LM4549_3D_Control] = 0x0101; +regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f; +regfile[LM4549_Ext_Audio_ID]= 0x0001; +regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x; +regfile[LM4549_PCM_Front_DAC_Rate] = 0xbb80; +regfile[LM4549_PCM_ADC_Rate]= 0xbb80; +regfile[LM4549_Vendor_ID1] = 0x4e53; +regfile[LM4549_Vendor_ID2] = 0x4331; +} + +static void lm4549_au
[Qemu-devel] [PATCH V4] Add AACI audio playback support to the ARM Versatile/PB platform
This driver emulates the ARM AACI interface (PL041) connected to a LM4549 codec. It enables audio playback for the Versatile/PB platform. Limitations: - Supports only a playback on one channel (Versatile/Vexpress) - Supports only one TX FIFO in compact-mode or non-compact mode. - Supports playback of 12, 16, 18 and 20 bits samples. - Record is not supported. - The PL041 is hardwired to a LM4549 codec. Versatile/PB test build: linux-2.6.38.5 buildroot-2010.11 alsa-lib-1.0.22 alsa-utils-1.0.22 mpg123-0.66 Qemu host: Ubuntu 10.04 in Vmware/OS X Playback tested successfully with speaker-test/aplay/mpg123. Signed-off-by: Mathieu Sonet --- v3->v4 * Fix debug message compile error * Remove Versatile/PB reference in pl041.c * Use 20-bit samples in the LM4549 API * Add support for non-compact mode * Now support 12, 16, 18 and 20 bit sample size * A FIFO reset is triggered when the AACIFE bit is reset Makefile.target |1 + hw/lm4549.c | 332 hw/lm4549.h | 44 hw/pl041.c | 644 ++ hw/pl041.h | 135 hw/pl041.hx | 81 +++ hw/versatilepb.c |8 + 7 files changed, 1245 insertions(+), 0 deletions(-) create mode 100644 hw/lm4549.c create mode 100644 hw/lm4549.h create mode 100644 hw/pl041.c create mode 100644 hw/pl041.h create mode 100644 hw/pl041.hx diff --git a/Makefile.target b/Makefile.target index 42adfec..00173cd 100644 --- a/Makefile.target +++ b/Makefile.target @@ -355,6 +355,7 @@ obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o +obj-arm-y += pl041.o lm4549.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o diff --git a/hw/lm4549.c b/hw/lm4549.c new file mode 100644 index 000..883ef60 --- /dev/null +++ b/hw/lm4549.c @@ -0,0 +1,332 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * * + * + * This driver emulates the LM4549 codec. + * + * It supports only one playback voice and no record voice. + */ + +#include "hw.h" +#include "audio/audio.h" +#include "lm4549.h" + +#if 0 +#define LM4549_DEBUG 1 +#endif + +#if 0 +#define LM4549_DUMP_DAC_INPUT 1 +#endif + +#ifdef LM4549_DEBUG +#define DPRINTF(fmt, ...) \ +do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#if defined(LM4549_DUMP_DAC_INPUT) +#include +static FILE *fp_dac_input; +#endif + +/* LM4549 register list */ +enum { +LM4549_Reset= 0x00, +LM4549_Master_Volume= 0x02, +LM4549_Line_Out_Volume = 0x04, +LM4549_Master_Volume_Mono = 0x06, +LM4549_PC_Beep_Volume = 0x0A, +LM4549_Phone_Volume = 0x0C, +LM4549_Mic_Volume = 0x0E, +LM4549_Line_In_Volume = 0x10, +LM4549_CD_Volume= 0x12, +LM4549_Video_Volume = 0x14, +LM4549_Aux_Volume = 0x16, +LM4549_PCM_Out_Volume = 0x18, +LM4549_Record_Select= 0x1A, +LM4549_Record_Gain = 0x1C, +LM4549_General_Purpose = 0x20, +LM4549_3D_Control = 0x22, +LM4549_Powerdown_Ctrl_Stat = 0x26, +LM4549_Ext_Audio_ID = 0x28, +LM4549_Ext_Audio_Stat_Ctrl = 0x2A, +LM4549_PCM_Front_DAC_Rate = 0x2C, +LM4549_PCM_ADC_Rate = 0x32, +LM4549_Vendor_ID1 = 0x7C, +LM4549_Vendor_ID2 = 0x7E +}; + +static void lm4549_reset(lm4549_state *s) +{ +uint16_t *regfile = s->regfile; + +regfile[LM4549_Reset] = 0x0d50; +regfile[LM4549_Master_Volume] = 0x8008; +regfile[LM4549_Line_Out_Volume] = 0x8000; +regfile[LM4549_Master_Volume_Mono] = 0x8000; +regfile[LM4549_PC_Beep_Volume] = 0x; +regfile[LM4549_Phone_Volume]= 0x8008; +regfile[LM4549_Mic_Volume] = 0x8008; +regfile[LM4549_Line_In_Volume] = 0x8808; +regfile[LM4549_CD_Volume] = 0x8808; +regfile[LM4549_Video_Volume]= 0x8808; +regfile[LM4549_Aux_Volume] = 0x8808; +regfile[LM4549_PCM_Out_Volume] = 0x8808; +regfile[LM4549_Record_Select] = 0x; +regfile[LM4549_Record_Gain] = 0x8000; +regfile[LM4549_General_Purpose] = 0x; +regfile[LM4549_3D_Control] = 0x0101; +regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f; +regfile[LM4549_Ext_Audio_ID]= 0x0001; +regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x; +regfile[LM4549_PCM_Front_DAC_Rate] = 0xbb80; +regfile[LM4549_PCM_ADC_Rate]= 0xbb80; +
Re: [Qemu-devel] [PATCH v3] Add AACI audio playback support to the ARM Versatile/PB platform
Peter Maydell wrote: On 29 September 2011 18:31, Mathieu Sonet wrote: This driver emulates the ARM AACI interface (PL041) connected to a LM4549 codec. It enables audio playback for the Versatile/PB platform. Limitations: - Supports only a playback on one channel (Versatile/Vexpress) - Supports only a TX FIFO in compact-mode. Actually you seem to have implemented a weird hybrid of compact and non-compact modes. Non-compact mode: FIFOs are 20 bits wide; a word write from the CPU writes to bits [19:0] of the FIFO slot; each 'slot' of data to the LM4549 reads 20 bits from the FIFO. Compact mode: FIFOs are 40 bits wide (and half as deep); a word write from the CPU writes to bits [31:0] of the FIFO slot; each 'slot' of data to the LM4549 reads 16 bits from the FIFO (bits [15:0] then [31:16], and you have to read two slots (ie the 2 channels of stereo). You've implemented a single 32 bit depth FIFO which one CPU write writes to and which always passes one 32 bit word to the LM4549. There are two issues here: (1) the LM4549 can take up to 18 bits of data per slot, which your current lm4549_write_sample() API doesn't allow. Passing two 32 bit words here would fix that. [My point here is that we shouldn't hardwire the missing non-compact mode support into the API between the two components.] (2) when the Linux driver dynamically identifies the size of the FIFO it does it in non-compact mode, so it will return a value half as big as is actually right for the compact-mode behaviour you've implemented. (Also your FIFO is twice as deep as it should be if we're only implementing compact mode.) Playback tested successfully with speaker-test/aplay/mpg123. I find that on vexpress mpg123 playback works but can be quite stuttery. madplay (integer only) is somewhat better, so I suspect that qemu is spending ages emulating Neon/VFP in mpg123... Further (minor) comments below. +regfile[LM4549_PCM_Front_DAC_Rate] = 0xBB80; +regfile[LM4549_PCM_ADC_Rate]= 0xBB80; +regfile[LM4549_Vendor_ID1] = 0x4e53; +regfile[LM4549_Vendor_ID2] = 0x4331; Can we be consistent about upper or lower case for the hex? +#define MAX_FIFO_DEPTH (1024) +#define VERSATILEPB_DEFAULT_FIFO_DEPTH (256) /* AN115B - Table 1.1 */ pl041.c shouldn't know anything about VersatilePB. The default fifo depth should be 8 (same as the hardware PL041). +static uint8_t pl041_compute_periphid3(pl041_state *s) +{ +uint8_t id3 = (1 | ((s->fifo_depth >> 4) << 3)); +return id3; +} This isn't right. [5:3] FIFO depth (non-compact mode) b000 8 b001 16 b010 32 b011 64 b100 128 b101 256 b110 512 b111 1024 ...which isn't what your function calculates. (Linux determines FIFO depth programmatically by stuffing words into the FIFO until the status register says it's full, which is why it doesn't complain.) NB that some of the board TRMs have what seem to be incorrect labelling on the tables of depth vs ID register bits. +/* Update the irq state */ +qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0); +DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n", + s->regs.sr1, s->regs.isr1, s->regs.isr1 & mask); +} This debug printf won't compile if enabled -- you forgot to update it when you changed the main code to remove the 'mask' variable. +static int pl041_post_load(void *opaque, int version_id) +{ +pl041_state *s = opaque; +lm4549_post_load(&s->codec); +return 0; +} Is it not possible to just register lm4549_post_load() as the post_load function for lm4549_state, rather than having a pl041 post_load hook which only passes it through? The lm4549 model has no knowledge of its pl041 client and uses a separate context structure. The opaque pointer passed to the post_load hook can not be used directly to setup the lm4549. I would prefer to keep the two models separated. (Something in your mailsending path is wrapping long lines, by the way.) -- PMM Mathieu
[Qemu-devel] [PATCH v3] Add AACI audio playback support to the ARM Versatile/PB platform
This driver emulates the ARM AACI interface (PL041) connected to a LM4549 codec. It enables audio playback for the Versatile/PB platform. Limitations: - Supports only a playback on one channel (Versatile/Vexpress) - Supports only a TX FIFO in compact-mode. - Record is not supported. - The PL041 is hardwired to a LM4549 codec. Versatile/PB test build: linux-2.6.38.5 buildroot-2010.11 alsa-lib-1.0.22 alsa-utils-1.0.22 mpg123-0.66 Qemu host: Ubuntu 10.04 in Vmware/OS X Playback tested successfully with speaker-test/aplay/mpg123. Signed-off-by: Mathieu Sonet --- v2->v3 This version takes into account the remarks from Peter Maydell's review: * Both pl041 and lm4549 source code has been simplified by removing extraneous abstractions. * hw_error occurences has been replaced by warnings. * The FIFO depth is now a qdev property. * The model now has load/save support. Makefile.target |1 + hw/lm4549.c | 326 +++ hw/lm4549.h | 44 + hw/pl041.c | 501 ++ hw/pl041.h | 135 +++ hw/pl041.hx | 81 + hw/versatilepb.c |8 + 7 files changed, 1096 insertions(+), 0 deletions(-) create mode 100644 hw/lm4549.c create mode 100644 hw/lm4549.h create mode 100644 hw/pl041.c create mode 100644 hw/pl041.h create mode 100644 hw/pl041.hx diff --git a/Makefile.target b/Makefile.target index 88d2f1f..cb1e86a 100644 --- a/Makefile.target +++ b/Makefile.target @@ -355,6 +355,7 @@ obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o +obj-arm-y += pl041.o lm4549.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o diff --git a/hw/lm4549.c b/hw/lm4549.c new file mode 100644 index 000..55b7105 --- /dev/null +++ b/hw/lm4549.c @@ -0,0 +1,326 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * * + * + * This driver emulates the LM4549 codec. + * + * It supports only one playback voice and no record voice. + */ + +#include "hw.h" +#include "audio/audio.h" +#include "lm4549.h" + +#if 0 +#define LM4549_DEBUG 1 +#endif + +#if 0 +#define LM4549_DUMP_DAC_INPUT 1 +#endif + +#ifdef LM4549_DEBUG +#define DPRINTF(fmt, ...) \ +do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#if defined(LM4549_DUMP_DAC_INPUT) +#include +static FILE *fp_dac_input; +#endif + +/* LM4549 register list */ +enum { +LM4549_Reset= 0x00, +LM4549_Master_Volume= 0x02, +LM4549_Line_Out_Volume = 0x04, +LM4549_Master_Volume_Mono = 0x06, +LM4549_PC_Beep_Volume = 0x0A, +LM4549_Phone_Volume = 0x0C, +LM4549_Mic_Volume = 0x0E, +LM4549_Line_In_Volume = 0x10, +LM4549_CD_Volume= 0x12, +LM4549_Video_Volume = 0x14, +LM4549_Aux_Volume = 0x16, +LM4549_PCM_Out_Volume = 0x18, +LM4549_Record_Select= 0x1A, +LM4549_Record_Gain = 0x1C, +LM4549_General_Purpose = 0x20, +LM4549_3D_Control = 0x22, +LM4549_Powerdown_Ctrl_Stat = 0x26, +LM4549_Ext_Audio_ID = 0x28, +LM4549_Ext_Audio_Stat_Ctrl = 0x2A, +LM4549_PCM_Front_DAC_Rate = 0x2C, +LM4549_PCM_ADC_Rate = 0x32, +LM4549_Vendor_ID1 = 0x7C, +LM4549_Vendor_ID2 = 0x7E +}; + +static void lm4549_reset(lm4549_state *s) +{ +uint16_t *regfile = s->regfile; + +regfile[LM4549_Reset] = 0x0d50; +regfile[LM4549_Master_Volume] = 0x8008; +regfile[LM4549_Line_Out_Volume] = 0x8000; +regfile[LM4549_Master_Volume_Mono] = 0x8000; +regfile[LM4549_PC_Beep_Volume] = 0x; +regfile[LM4549_Phone_Volume]= 0x8008; +regfile[LM4549_Mic_Volume] = 0x8008; +regfile[LM4549_Line_In_Volume] = 0x8808; +regfile[LM4549_CD_Volume] = 0x8808; +regfile[LM4549_Video_Volume]= 0x8808; +regfile[LM4549_Aux_Volume] = 0x8808; +regfile[LM4549_PCM_Out_Volume] = 0x8808; +regfile[LM4549_Record_Select] = 0x; +regfile[LM4549_Record_Gain] = 0x8000; +regfile[LM4549_General_Purpose] = 0x; +regfile[LM4549_3D_Control] = 0x0101; +regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f; +regfile[LM4549_Ext_Audio_ID]= 0x0001; +regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x; +regfile[LM4549_PCM_Front_DAC_Rate] = 0xBB80; +regfile[LM4549_PCM_ADC_Rate]= 0xBB80; +regfile
[Qemu-devel] [PATCH v2] Add AACI audio playback support to the ARM Versatile/PB platform
The ACLink bus has been removed. The PL041/AACI driver now directly control the LM4549 codec driver. The AC97 emulation in ac97.c has not been reused. Making the current implementation sharable is not trivial and could bring regression to other platforms. Versatile/PB test build: linux-2.6.38.5 buildroot-2010.11 alsa-lib-1.0.22 alsa-utils-1.0.22 mpg123-0.66 Qemu host: Ubuntu 10.04 in Vmware/OS X Playback tested successfully with aplay and mpg123. Signed-off-by: Mathieu Sonet --- Makefile.target |1 + hw/lm4549.c | 307 + hw/lm4549.h | 45 ++ hw/pl041.c | 406 ++ hw/pl041.h | 125 + hw/pl041.hx | 61 hw/versatilepb.c |3 + 7 files changed, 948 insertions(+), 0 deletions(-) create mode 100644 hw/lm4549.c create mode 100644 hw/lm4549.h create mode 100644 hw/pl041.c create mode 100644 hw/pl041.h create mode 100644 hw/pl041.hx diff --git a/Makefile.target b/Makefile.target index 21f864a..1a6565f 100644 --- a/Makefile.target +++ b/Makefile.target @@ -354,6 +354,7 @@ obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o +obj-arm-y += pl041.o lm4549.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o diff --git a/hw/lm4549.c b/hw/lm4549.c new file mode 100644 index 000..e58d4d4 --- /dev/null +++ b/hw/lm4549.c @@ -0,0 +1,307 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * * + * + * This driver emulates the LM4549 codec. + * + */ + +#include "hw.h" +#include "audio/audio.h" +#include "lm4549.h" + +/* #define LM4549_DEBUG 1 */ +/* #define LM4549_DUMP_DAC_INPUT 1 */ + +#ifdef LM4549_DEBUG +#define DPRINTF(fmt, ...) \ +do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#if defined(LM4549_DUMP_DAC_INPUT) +#include +static FILE *fp_dac_input; +#endif + +/*** LM4549 register list ***/ + +enum { +LM4549_Reset= 0x00, +LM4549_Master_Volume= 0x02, +LM4549_Line_Out_Volume = 0x04, +LM4549_Master_Volume_Mono = 0x06, +LM4549_PC_Beep_Volume = 0x0A, +LM4549_Phone_Volume = 0x0C, +LM4549_Mic_Volume = 0x0E, +LM4549_Line_In_Volume = 0x10, +LM4549_CD_Volume= 0x12, +LM4549_Video_Volume = 0x14, +LM4549_Aux_Volume = 0x16, +LM4549_PCM_Out_Volume = 0x18, +LM4549_Record_Select= 0x1A, +LM4549_Record_Gain = 0x1C, +LM4549_General_Purpose = 0x20, +LM4549_3D_Control = 0x22, +LM4549_Powerdown_Ctrl_Stat = 0x26, +LM4549_Extended_Audio_ID= 0x28, +LM4549_Extended_Audio_Stat_Ctrl = 0x2A, +LM4549_PCM_Front_DAC_Rate = 0x2C, +LM4549_PCM_ADC_Rate = 0x32, +LM4549_Vendor_ID1 = 0x7C, +LM4549_Vendor_ID2 = 0x7E +}; + +/*** Functions ***/ + +static void lm4549_store_init(lm4549_state *s, uint32_t offset, uint32_t value, + uint32_t is_read_only) +{ +lm4549_registers *r = &s->codec_regs; + +if (offset > 128) { +DPRINTF("store_init: Out of bound offset 0x%x\n", (int)offset); +} + +r->data[offset].value = value & 0x; +r->data[offset].default_value = value & 0x; +r->data[offset].read_only = (is_read_only > 0) ? 1 : 0; +} + +static void lm4549_store_reset(lm4549_state *s) +{ +lm4549_registers *r = &s->codec_regs; +int i; + +for (i = 0; i < 128; i++) { +r->data[i].value = r->data[i].default_value; +} +} + +static void lm4549_store(lm4549_state *s, uint32_t offset, uint16_t value) +{ +lm4549_registers *r = &s->codec_regs; + +if (offset > 128) { +DPRINTF("store: Out of bound offset 0x%x\n", (int)offset); +} + +if (r->data[offset].read_only) { +DPRINTF("store: Read-only offset 0x%x\n", (int)offset); +return; +} + +r->data[offset].value = value & 0x; +} + +static uint16_t lm4549_load(lm4549_state *s, uint32_t offset) +{ +lm4549_registers *r = &s->codec_regs; + +if (offset > 128) { +hw_error("load: Out of bound offset 0x%x\n", (int)offset); +} + +return r->data[offset].value; +} + +static void lm4549_audio_transfer(lm4549_state *s) +{ +uint32_t written_bytes, written_samples; +uint32_t i; + +/* Activate the voice */ +AUD_set_active_out(s->v
Re: [Qemu-devel] [PATCH] Add AACI audio playback support to the ARM Versatile/PB platform
Paul Brook wrote: On the other hand the current ac97.c implementation is a closely coupled combination of a PCI/ACLink bridge (Intel 82801AA) with a generic AC97 codec. This has prevent me to easily reuse this code. The milkymist-ac97 implementation is another case. It looks like a basic implementation with the AC97 registers directly mapped on the system bus. Using the ACLink bus I defined, it could be interesting to implement separately the PCI/ACLink bridge from ac97.c. Is it what you mean by saying this should be shared with the other AC97 devices ? Yes. The whole point of AClink is that it separates the host bridge from the codec. We now have at least three devices implementing this. Your aclink implementation is only used by one of these, which gives me little confidence it actually does what it claims. Paul I understand your concern. In fact after digging the Intel PCI bridge documentation, I see that it offers a mapping of the AC97 registers in the PCI I/O space. Reusing my current ACLink bus with this bridge would mean to encode register accesses into ACLink frames and then to decode them again on the codec side. Not very simple just for the sake of device models correctness and no added value. Also QEMU may not need N different re-implemention of an AC97 codec. So I will ditch ACLink/LM4549 and will instead interface the PL041 driver with the codec defined in ac97.c. PCI---AC97 PL041--/ Thanks for your input Mathieu
Re: [Qemu-devel] [PATCH] Add AACI audio playback support to the ARM Versatile/PB platform
Paul Brook wrote: The PL041 driver provides an interface to an ACLink bus. The LM4549 driver emulates a DAC connected on the ACLink bus. Only audio playback is implemented. Shouldn't this be shared with the other AC97 devices? Paul I organized the code in 3 different drivers (PL041 <=> ACLink <=> LM4549) to decorrelate the codec interface from its implementation. This could allow the use of alternative AC97 models with the same PL041 implementation. On the other hand the current ac97.c implementation is a closely coupled combination of a PCI/ACLink bridge (Intel 82801AA) with a generic AC97 codec. This has prevent me to easily reuse this code. The milkymist-ac97 implementation is another case. It looks like a basic implementation with the AC97 registers directly mapped on the system bus. Using the ACLink bus I defined, it could be interesting to implement separately the PCI/ACLink bridge from ac97.c. Is it what you mean by saying this should be shared with the other AC97 devices ? Mathieu
[Qemu-devel] [PATCH] Add AACI audio playback support to the ARM Versatile/PB platform
The PL041 driver provides an interface to an ACLink bus. The LM4549 driver emulates a DAC connected on the ACLink bus. Only audio playback is implemented. Versatile/PB test build: linux-2.6.38.5 buildroot-2010.11 alsa-lib-1.0.22 alsa-utils-1.0.22 mpg123-0.66 Qemu host: Ubuntu 10.04 in Vmware/OS X Playback tested successfully with aplay and mpg123. Signed-off-by: Mathieu Sonet --- Makefile.target |1 + hw/aclink.c | 121 +++ hw/aclink.h | 63 hw/lm4549.c | 368 + hw/pl041.c | 436 ++ hw/pl041.h | 126 hw/pl041.hx | 62 hw/versatilepb.c |6 + 8 files changed, 1183 insertions(+), 0 deletions(-) create mode 100644 hw/aclink.c create mode 100644 hw/aclink.h create mode 100644 hw/lm4549.c create mode 100644 hw/pl041.c create mode 100644 hw/pl041.h create mode 100644 hw/pl041.hx diff --git a/Makefile.target b/Makefile.target index 21f864a..cdd7b40 100644 --- a/Makefile.target +++ b/Makefile.target @@ -354,6 +354,7 @@ obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o +obj-arm-y += pl041.o aclink.o lm4549.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o diff --git a/hw/aclink.c b/hw/aclink.c new file mode 100644 index 000..c335f60 --- /dev/null +++ b/hw/aclink.c @@ -0,0 +1,121 @@ +/* + * ACLink Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * * + * + * This file defines the ACLink bus interface to exchange data + * between an host and a codec. + * + */ + +#include "aclink.h" + +/*** Types ***/ + +struct ACLinkBus { +BusState qbus; +ACLinkControllerInfo *controller_info; +uint32_t bitclk; +}; + +struct BusInfo aclink_bus_info = { +.name = "aclink", +.size = sizeof(ACLinkBus), +}; + +/*** Functions ***/ + +ACLinkBus *aclink_create_bus(DeviceState *parent, const char *name) +{ +BusState *bus; +bus = qbus_create(&aclink_bus_info, parent, name); +return FROM_QBUS(ACLinkBus, bus); +} + +void aclink_set_controller_info(ACLinkBus *bus, ACLinkControllerInfo *info) +{ +bus->controller_info = info; +} + +static int aclink_device_init(DeviceState *dev, DeviceInfo *base_info) +{ +ACLinkDeviceInfo *info = container_of(base_info, ACLinkDeviceInfo, qdev); +ACLinkDevice *s = ACLINK_DEVICE_FROM_QDEV(dev); +ACLinkBus *bus; + +bus = FROM_QBUS(ACLinkBus, qdev_get_parent_bus(dev)); +if (QLIST_FIRST(&bus->qbus.children) != dev +|| QLIST_NEXT(dev, sibling) != NULL) { +hw_error("Too many devices on the ACLINK bus"); +} + +s->info = info; +return info->init(s); +} + +void aclink_register_device(ACLinkDeviceInfo *info) +{ +assert(info->qdev.size >= sizeof(ACLinkDevice)); +info->qdev.init = aclink_device_init; +info->qdev.bus_info = &aclink_bus_info; +qdev_register(&info->qdev); +} + +DeviceState *aclink_create_device(ACLinkBus *bus, const char *name) +{ +DeviceState *dev; +dev = qdev_create(&bus->qbus, name); +qdev_init_nofail(dev); +return dev; +} + +static ACLinkDevice *aclink_get_device(ACLinkBus *bus) +{ +DeviceState *dev = QLIST_FIRST(&bus->qbus.children); +if (!dev) { +return NULL; +} +return ACLINK_DEVICE_FROM_QDEV(dev); +} + +void aclink_bitclk_enable(ACLinkDevice *dev, uint32_t on) +{ +ACLinkBus *bus = FROM_QBUS(ACLinkBus, qdev_get_parent_bus(&dev->qdev)); +uint32_t has_changed; + +on = (on > 0) ? 1 : 0; +has_changed = (bus->bitclk != on) ? 1 : 0; + +bus->bitclk = on; +if (has_changed) { +bus->controller_info->bitclk_state_changed(bus->qbus.parent); +} +} + +uint32_t aclink_bitclk_is_enabled(ACLinkBus *bus) +{ +return bus->bitclk; +} + +void aclink_sdataout_slot12(ACLinkBus *bus, uint32_t slot1, uint32_t slot2) +{ +ACLinkDevice *device = aclink_get_device(bus); +device->info->sdataout_slot12(device, slot1, slot2); +} + +void aclink_sdataout_slot34(ACLinkBus *bus, uint32_t slot3, uint32_t slot4) +{ +ACLinkDevice *device = aclink_get_device(bus); +device->info->sdataout_slot34(device, slot3, slot4); +} + +void aclink_sdatain_slot12(ACLinkDevice *dev, uint32_t slot1, uint32_t slot2) +{ +ACLinkBus *bus = FROM_QBUS(ACLinkBus, qdev_get_parent_bus(&dev->qdev)); +bus->controller_info->sdatain_slot12(bus->qbus.parent, slot1, slot2); +} diff --git a/hw/aclink.h b/hw/aclink.h new file mode 100644 index 000..d360d4b --- /dev/null +++ b/hw/aclink.h @@ -0,0 +1,63 @@ +/*