From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=36356 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pzu3X-0001Tl-9I for qemu-devel@nongnu.org; Wed, 16 Mar 2011 12:58:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Pztve-0006ea-OG for qemu-devel@nongnu.org; Wed, 16 Mar 2011 12:50:38 -0400 Received: from cantor2.suse.de ([195.135.220.15]:54982 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Pztve-0006eI-8C for qemu-devel@nongnu.org; Wed, 16 Mar 2011 12:50:34 -0400 Message-ID: <4D80EA56.6050800@suse.de> Date: Wed, 16 Mar 2011 17:50:30 +0100 From: Alexander Graf MIME-Version: 1.0 References: <1299537165-16711-1-git-send-email-michael@walle.cc> <1299537165-16711-2-git-send-email-michael@walle.cc> In-Reply-To: <1299537165-16711-2-git-send-email-michael@walle.cc> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: [Qemu-devel] Re: [PATCH 01/14] lm32: add Milkymist AC97 support List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Michael Walle Cc: "Edgar E. Iglesias" , qemu-devel@nongnu.org On 03/07/2011 11:32 PM, Michael Walle wrote: > This patch adds support for the Milkymist AC97 compatible sound output and > input core. Malc, could you please take a look at this? :) > Signed-off-by: Michael Walle > --- > Makefile.target | 1 + > configure | 3 + > hw/milkymist-ac97.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++ > trace-events | 12 ++ > 4 files changed, 351 insertions(+), 0 deletions(-) > create mode 100644 hw/milkymist-ac97.c > > diff --git a/Makefile.target b/Makefile.target > index f0df98e..3be7868 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -256,6 +256,7 @@ obj-lm32-y += lm32_juart.o > obj-lm32-y += lm32_timer.o > obj-lm32-y += lm32_uart.o > obj-lm32-y += lm32_sys.o > +obj-lm32-y += milkymist-ac97.o > > obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o > obj-mips-y += mips_addr.o mips_timer.o mips_int.o > diff --git a/configure b/configure > index 5513d3e..e75e1a2 100755 > --- a/configure > +++ b/configure > @@ -3281,6 +3281,9 @@ if test "$target_softmmu" = "yes" ; then > arm) > cflags="-DHAS_AUDIO $cflags" > ;; > + lm32) > + cflags="-DHAS_AUDIO $cflags" > + ;; > i386|mips|ppc) > cflags="-DHAS_AUDIO -DHAS_AUDIO_CHOICE $cflags" > ;; > diff --git a/hw/milkymist-ac97.c b/hw/milkymist-ac97.c > new file mode 100644 > index 0000000..6c9e318 > --- /dev/null > +++ b/hw/milkymist-ac97.c > @@ -0,0 +1,335 @@ > +/* > + * QEMU model of the Milkymist System Controller. > + * > + * Copyright (c) 2010 Michael Walle > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library 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 > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see. > + * > + * > + * Specification available at: > + * http://www.milkymist.org/socdoc/ac97.pdf > + */ > + > +#include "hw.h" > +#include "sysbus.h" > +#include "trace.h" > +#include "audio/audio.h" > +#include "qemu-error.h" > + > +enum { > + R_AC97_CTRL = 0, > + R_AC97_ADDR, > + R_AC97_DATAOUT, > + R_AC97_DATAIN, > + R_D_CTRL, > + R_D_ADDR, > + R_D_REMAINING, > + R_RESERVED, > + R_U_CTRL, > + R_U_ADDR, > + R_U_REMAINING, > + R_MAX > +}; > + > +enum { > + AC97_CTRL_RQEN = (1<<0), > + AC97_CTRL_WRITE = (1<<1), > +}; > + > +enum { > + CTRL_EN = (1<<0), > +}; > + > +struct MilkymistAC97State { > + SysBusDevice busdev; > + > + QEMUSoundCard card; > + SWVoiceIn *voice_in; > + SWVoiceOut *voice_out; > + > + uint32_t regs[R_MAX]; > + > + qemu_irq crrequest_irq; > + qemu_irq crreply_irq; > + qemu_irq dmar_irq; > + qemu_irq dmaw_irq; > +}; > +typedef struct MilkymistAC97State MilkymistAC97State; > + > +static void update_voices(MilkymistAC97State *s) > +{ > + if (s->regs[R_D_CTRL]& CTRL_EN) { > + AUD_set_active_out(s->voice_out, 1); > + } else { > + AUD_set_active_out(s->voice_out, 0); > + } > + > + if (s->regs[R_U_CTRL]& CTRL_EN) { > + AUD_set_active_in(s->voice_in, 1); > + } else { > + AUD_set_active_in(s->voice_in, 0); > + } > +} > + > +static uint32_t ac97_read(void *opaque, target_phys_addr_t addr) > +{ > + MilkymistAC97State *s = opaque; > + uint32_t r = 0; > + > + addr>>= 2; > + switch (addr) { > + case R_AC97_CTRL: > + case R_AC97_ADDR: > + case R_AC97_DATAOUT: > + case R_AC97_DATAIN: > + case R_D_CTRL: > + case R_D_ADDR: > + case R_D_REMAINING: > + case R_U_CTRL: > + case R_U_ADDR: > + case R_U_REMAINING: > + r = s->regs[addr]; > + break; > + > + default: > + error_report("milkymist_ac97: read access to unkown register 0x" > + TARGET_FMT_plx, addr<< 2); > + break; > + } > + > + trace_milkymist_ac97_memory_read(addr<< 2, r); > + > + return r; > +} > + > +static void ac97_write(void *opaque, target_phys_addr_t addr, uint32_t value) > +{ > + MilkymistAC97State *s = opaque; > + > + trace_milkymist_ac97_memory_write(addr, value); > + > + addr>>= 2; > + switch (addr) { > + case R_AC97_CTRL: > + /* always raise an IRQ according to the direction */ > + if (value& AC97_CTRL_RQEN) { > + if (value& AC97_CTRL_WRITE) { > + trace_milkymist_ac97_pulse_irq_crrequest(); > + qemu_irq_pulse(s->crrequest_irq); > + } else { > + trace_milkymist_ac97_pulse_irq_crreply(); > + qemu_irq_pulse(s->crreply_irq); > + } > + } > + > + /* RQEN is self clearing */ > + s->regs[addr] = value& ~AC97_CTRL_RQEN; > + break; > + case R_D_CTRL: > + case R_U_CTRL: > + s->regs[addr] = value; > + update_voices(s); > + break; > + case R_AC97_ADDR: > + case R_AC97_DATAOUT: > + case R_AC97_DATAIN: > + case R_D_ADDR: > + case R_D_REMAINING: > + case R_U_ADDR: > + case R_U_REMAINING: > + s->regs[addr] = value; > + break; > + > + default: > + error_report("milkymist_ac97: write access to unkown register 0x" > + TARGET_FMT_plx, addr); > + break; > + } > + > +} > + > +static CPUReadMemoryFunc * const ac97_read_fn[] = { > + NULL, > + NULL, > +&ac97_read, > +}; > + > +static CPUWriteMemoryFunc * const ac97_write_fn[] = { > + NULL, > + NULL, > +&ac97_write, > +}; > + > +static void ac97_in_cb(void *opaque, int avail_b) > +{ > + MilkymistAC97State *s = opaque; > + uint8_t buf[4096]; > + uint32_t remaining = s->regs[R_U_REMAINING]; > + int temp = audio_MIN(remaining, avail_b); > + uint32_t addr = s->regs[R_U_ADDR]; > + int transferred = 0; > + > + trace_milkymist_ac97_in_cb(avail_b, remaining); > + > + /* prevent from raising an IRQ */ > + if (temp == 0) { > + return; > + } > + > + while (temp) { > + int acquired, to_copy; > + > + to_copy = audio_MIN(temp, sizeof(buf)); > + acquired = AUD_read(s->voice_in, buf, to_copy); > + if (!acquired) { > + break; > + } > + > + cpu_physical_memory_write(addr, buf, acquired); > + > + temp -= acquired; > + addr += acquired; > + transferred += acquired; > + } > + > + trace_milkymist_ac97_in_cb_transferred(transferred); > + > + s->regs[R_U_ADDR] = addr; > + s->regs[R_U_REMAINING] -= transferred; > + > + if ((s->regs[R_U_CTRL]& CTRL_EN)&& (s->regs[R_U_REMAINING] == 0)) { > + trace_milkymist_ac97_pulse_irq_dmaw(); > + qemu_irq_pulse(s->dmaw_irq); > + } > +} > + > +static void ac97_out_cb(void *opaque, int free_b) > +{ > + MilkymistAC97State *s = opaque; > + uint8_t buf[4096]; > + uint32_t remaining = s->regs[R_D_REMAINING]; > + int temp = audio_MIN(remaining, free_b); > + uint32_t addr = s->regs[R_D_ADDR]; > + int transferred = 0; > + > + trace_milkymist_ac97_out_cb(free_b, remaining); > + > + /* prevent from raising an IRQ */ > + if (temp == 0) { > + return; > + } > + > + while (temp) { > + int copied, to_copy; > + > + to_copy = audio_MIN(temp, sizeof(buf)); > + cpu_physical_memory_read(addr, buf, to_copy); > + copied = AUD_write(s->voice_out, buf, to_copy); > + if (!copied) { > + break; > + } > + temp -= copied; > + addr += copied; > + transferred += copied; > + } > + > + trace_milkymist_ac97_out_cb_transferred(transferred); > + > + s->regs[R_D_ADDR] = addr; > + s->regs[R_D_REMAINING] -= transferred; > + > + if ((s->regs[R_D_CTRL]& CTRL_EN)&& (s->regs[R_D_REMAINING] == 0)) { > + trace_milkymist_ac97_pulse_irq_dmar(); > + qemu_irq_pulse(s->dmar_irq); > + } > +} > + > +static void milkymist_ac97_reset(DeviceState *d) > +{ > + MilkymistAC97State *s = container_of(d, MilkymistAC97State, busdev.qdev); > + int i; > + > + for (i = 0; i< R_MAX; i++) { > + s->regs[i] = 0; > + } > + > + AUD_set_active_in(s->voice_in, 0); > + AUD_set_active_out(s->voice_out, 0); > +} > + > +static int ac97_post_load(void *opaque, int version_id) > +{ > + MilkymistAC97State *s = opaque; > + > + update_voices(s); > + > + return 0; > +} > + > +static int milkymist_ac97_init(SysBusDevice *dev) > +{ > + MilkymistAC97State *s = FROM_SYSBUS(typeof(*s), dev); > + int ac97_regs; > + > + struct audsettings as; > + sysbus_init_irq(dev,&s->crrequest_irq); > + sysbus_init_irq(dev,&s->crreply_irq); > + sysbus_init_irq(dev,&s->dmar_irq); > + sysbus_init_irq(dev,&s->dmaw_irq); > + > + AUD_register_card("Milkymist AC'97",&s->card); > + > + as.freq = 48000; > + as.nchannels = 2; > + as.fmt = AUD_FMT_S16; > + as.endianness = 1; > + > + s->voice_in = AUD_open_in(&s->card, s->voice_in, > + "mm_ac97.in", s, ac97_in_cb,&as); > + s->voice_out = AUD_open_out(&s->card, s->voice_out, > + "mm_ac97.out", s, ac97_out_cb,&as); > + > + ac97_regs = cpu_register_io_memory(ac97_read_fn, ac97_write_fn, s, > + DEVICE_NATIVE_ENDIAN); > + sysbus_init_mmio(dev, R_MAX * 4, ac97_regs); > + > + return 0; > +} > + > +static const VMStateDescription vmstate_milkymist_ac97 = { > + .name = "milkymist-ac97", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .post_load = ac97_post_load, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32_ARRAY(regs, MilkymistAC97State, R_MAX), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static SysBusDeviceInfo milkymist_ac97_info = { > + .init = milkymist_ac97_init, > + .qdev.name = "milkymist-ac97", > + .qdev.size = sizeof(MilkymistAC97State), > + .qdev.vmsd =&vmstate_milkymist_ac97, > + .qdev.reset = milkymist_ac97_reset, > +}; > + > +static void milkymist_ac97_register(void) > +{ > + sysbus_register_withprop(&milkymist_ac97_info); > +} > + > +device_init(milkymist_ac97_register) > diff --git a/trace-events b/trace-events > index c791719..9241ac9 100644 > --- a/trace-events > +++ b/trace-events > @@ -283,3 +283,15 @@ disable lm32_uart_irq_state(int level) "irq state %d" > > # hw/lm32_sys.c > disable lm32_sys_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" > + > +# hw/milkymist-ac97.c > +disable milkymist_ac97_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" > +disable milkymist_ac97_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" > +disable milkymist_ac97_pulse_irq_crrequest(void) "Pulse IRQ CR request" > +disable milkymist_ac97_pulse_irq_crreply(void) "Pulse IRQ CR reply" > +disable milkymist_ac97_pulse_irq_dmaw(void) "Pulse IRQ DMA write" > +disable milkymist_ac97_pulse_irq_dmar(void) "Pulse IRQ DMA read" > +disable milkymist_ac97_in_cb(int avail, uint32_t remaining) "avail %d remaining %u" > +disable milkymist_ac97_in_cb_transferred(int transferred) "transferred %d" > +disable milkymist_ac97_out_cb(int free, uint32_t remaining) "free %d remaining %u" > +disable milkymist_ac97_out_cb_transferred(int transferred) "transferred %d"