From 34b8f63ea1aa0941f11c6c032f8e1716269a0449 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Feb 2013 12:55:19 +0100 Subject: hw: move audio devices to hw/audio/, configure via default-configs/ Signed-off-by: Paolo Bonzini --- default-configs/arm-softmmu.mak | 1 + default-configs/sparc-softmmu.mak | 1 + hw/arm/Makefile.objs | 2 +- hw/audio/Makefile.objs | 4 + hw/audio/cs4231.c | 181 ++++++++++++++++++++ hw/audio/marvell_88w8618.c | 303 +++++++++++++++++++++++++++++++++ hw/audio/milkymist-ac97.c | 344 ++++++++++++++++++++++++++++++++++++++ hw/cs4231.c | 181 -------------------- hw/lm32/Makefile.objs | 1 - hw/marvell_88w8618_audio.c | 303 --------------------------------- hw/milkymist-ac97.c | 344 -------------------------------------- hw/sparc/Makefile.objs | 2 +- 12 files changed, 836 insertions(+), 831 deletions(-) create mode 100644 hw/audio/cs4231.c create mode 100644 hw/audio/marvell_88w8618.c create mode 100644 hw/audio/milkymist-ac97.c delete mode 100644 hw/cs4231.c delete mode 100644 hw/marvell_88w8618_audio.c delete mode 100644 hw/milkymist-ac97.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 7717ea6672..c0e011084b 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -48,6 +48,7 @@ CONFIG_PL310=y CONFIG_PL330=y CONFIG_CADENCE=y CONFIG_XGMAC=y +CONFIG_MARVELL_88W8618=y CONFIG_ONENAND=y CONFIG_VERSATILE_PCI=y diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index 6d11ba0369..eda8797662 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -9,3 +9,4 @@ CONFIG_FDC=y CONFIG_EMPTY_SLOT=y CONFIG_PCNET_COMMON=y CONFIG_LANCE=y +CONFIG_CS4231=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index ede019da02..6582f5a0aa 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -19,7 +19,7 @@ obj-y += omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ obj-y += tsc210x.o obj-y += blizzard.o cbus.o tusb6010.o obj-y += mst_fpga.o -obj-y += bitbang_i2c.o marvell_88w8618_audio.o +obj-y += bitbang_i2c.o obj-y += framebuffer.o obj-y += strongarm.o obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o diff --git a/hw/audio/Makefile.objs b/hw/audio/Makefile.objs index c50c367da7..237510204b 100644 --- a/hw/audio/Makefile.objs +++ b/hw/audio/Makefile.objs @@ -13,4 +13,8 @@ common-obj-$(CONFIG_PCSPK) += pcspk.o common-obj-$(CONFIG_WM8750) += wm8750.o common-obj-$(CONFIG_PL041) += pl041.o lm4549.o +common-obj-$(CONFIG_CS4231) += cs4231.o +common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o +common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o + $(obj)/adlib.o $(obj)/fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 diff --git a/hw/audio/cs4231.c b/hw/audio/cs4231.c new file mode 100644 index 0000000000..2975336057 --- /dev/null +++ b/hw/audio/cs4231.c @@ -0,0 +1,181 @@ +/* + * QEMU Crystal CS4231 audio chip emulation + * + * Copyright (c) 2006 Fabrice Bellard + * + * 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 "hw/sysbus.h" +#include "trace.h" + +/* + * In addition to Crystal CS4231 there is a DMA controller on Sparc. + */ +#define CS_SIZE 0x40 +#define CS_REGS 16 +#define CS_DREGS 32 +#define CS_MAXDREG (CS_DREGS - 1) + +typedef struct CSState { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + uint32_t regs[CS_REGS]; + uint8_t dregs[CS_DREGS]; +} CSState; + +#define CS_RAP(s) ((s)->regs[0] & CS_MAXDREG) +#define CS_VER 0xa0 +#define CS_CDC_VER 0x8a + +static void cs_reset(DeviceState *d) +{ + CSState *s = container_of(d, CSState, busdev.qdev); + + memset(s->regs, 0, CS_REGS * 4); + memset(s->dregs, 0, CS_DREGS); + s->dregs[12] = CS_CDC_VER; + s->dregs[25] = CS_VER; +} + +static uint64_t cs_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + CSState *s = opaque; + uint32_t saddr, ret; + + saddr = addr >> 2; + switch (saddr) { + case 1: + switch (CS_RAP(s)) { + case 3: // Write only + ret = 0; + break; + default: + ret = s->dregs[CS_RAP(s)]; + break; + } + trace_cs4231_mem_readl_dreg(CS_RAP(s), ret); + break; + default: + ret = s->regs[saddr]; + trace_cs4231_mem_readl_reg(saddr, ret); + break; + } + return ret; +} + +static void cs_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + CSState *s = opaque; + uint32_t saddr; + + saddr = addr >> 2; + trace_cs4231_mem_writel_reg(saddr, s->regs[saddr], val); + switch (saddr) { + case 1: + trace_cs4231_mem_writel_dreg(CS_RAP(s), s->dregs[CS_RAP(s)], val); + switch(CS_RAP(s)) { + case 11: + case 25: // Read only + break; + case 12: + val &= 0x40; + val |= CS_CDC_VER; // Codec version + s->dregs[CS_RAP(s)] = val; + break; + default: + s->dregs[CS_RAP(s)] = val; + break; + } + break; + case 2: // Read only + break; + case 4: + if (val & 1) { + cs_reset(&s->busdev.qdev); + } + val &= 0x7f; + s->regs[saddr] = val; + break; + default: + s->regs[saddr] = val; + break; + } +} + +static const MemoryRegionOps cs_mem_ops = { + .read = cs_mem_read, + .write = cs_mem_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_cs4231 = { + .name ="cs4231", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32_ARRAY(regs, CSState, CS_REGS), + VMSTATE_UINT8_ARRAY(dregs, CSState, CS_DREGS), + VMSTATE_END_OF_LIST() + } +}; + +static int cs4231_init1(SysBusDevice *dev) +{ + CSState *s = FROM_SYSBUS(CSState, dev); + + memory_region_init_io(&s->iomem, &cs_mem_ops, s, "cs4321", CS_SIZE); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + + return 0; +} + +static Property cs4231_properties[] = { + {.name = NULL}, +}; + +static void cs4231_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = cs4231_init1; + dc->reset = cs_reset; + dc->vmsd = &vmstate_cs4231; + dc->props = cs4231_properties; +} + +static const TypeInfo cs4231_info = { + .name = "SUNW,CS4231", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CSState), + .class_init = cs4231_class_init, +}; + +static void cs4231_register_types(void) +{ + type_register_static(&cs4231_info); +} + +type_init(cs4231_register_types) diff --git a/hw/audio/marvell_88w8618.c b/hw/audio/marvell_88w8618.c new file mode 100644 index 0000000000..f9b68fd311 --- /dev/null +++ b/hw/audio/marvell_88w8618.c @@ -0,0 +1,303 @@ +/* + * Marvell 88w8618 audio emulation extracted from + * Marvell MV88w8618 / Freecom MusicPal emulation. + * + * Copyright (c) 2008 Jan Kiszka + * + * This code is licensed under the GNU GPL v2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "hw/sysbus.h" +#include "audio/audio.h" + +#define MP_AUDIO_SIZE 0x00001000 + +/* Audio register offsets */ +#define MP_AUDIO_PLAYBACK_MODE 0x00 +#define MP_AUDIO_CLOCK_DIV 0x18 +#define MP_AUDIO_IRQ_STATUS 0x20 +#define MP_AUDIO_IRQ_ENABLE 0x24 +#define MP_AUDIO_TX_START_LO 0x28 +#define MP_AUDIO_TX_THRESHOLD 0x2C +#define MP_AUDIO_TX_STATUS 0x38 +#define MP_AUDIO_TX_START_HI 0x40 + +/* Status register and IRQ enable bits */ +#define MP_AUDIO_TX_HALF (1 << 6) +#define MP_AUDIO_TX_FULL (1 << 7) + +/* Playback mode bits */ +#define MP_AUDIO_16BIT_SAMPLE (1 << 0) +#define MP_AUDIO_PLAYBACK_EN (1 << 7) +#define MP_AUDIO_CLOCK_24MHZ (1 << 9) +#define MP_AUDIO_MONO (1 << 14) + +typedef struct mv88w8618_audio_state { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + uint32_t playback_mode; + uint32_t status; + uint32_t irq_enable; + uint32_t phys_buf; + uint32_t target_buffer; + uint32_t threshold; + uint32_t play_pos; + uint32_t last_free; + uint32_t clock_div; + void *wm; +} mv88w8618_audio_state; + +static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in) +{ + mv88w8618_audio_state *s = opaque; + int16_t *codec_buffer; + int8_t buf[4096]; + int8_t *mem_buffer; + int pos, block_size; + + if (!(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) { + return; + } + if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) { + free_out <<= 1; + } + if (!(s->playback_mode & MP_AUDIO_MONO)) { + free_out <<= 1; + } + block_size = s->threshold / 2; + if (free_out - s->last_free < block_size) { + return; + } + if (block_size > 4096) { + return; + } + cpu_physical_memory_read(s->target_buffer + s->play_pos, (void *)buf, + block_size); + mem_buffer = buf; + if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) { + if (s->playback_mode & MP_AUDIO_MONO) { + codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1); + for (pos = 0; pos < block_size; pos += 2) { + *codec_buffer++ = *(int16_t *)mem_buffer; + *codec_buffer++ = *(int16_t *)mem_buffer; + mem_buffer += 2; + } + } else { + memcpy(wm8750_dac_buffer(s->wm, block_size >> 2), + (uint32_t *)mem_buffer, block_size); + } + } else { + if (s->playback_mode & MP_AUDIO_MONO) { + codec_buffer = wm8750_dac_buffer(s->wm, block_size); + for (pos = 0; pos < block_size; pos++) { + *codec_buffer++ = cpu_to_le16(256 * *mem_buffer); + *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); + } + } else { + codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1); + for (pos = 0; pos < block_size; pos += 2) { + *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); + *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); + } + } + } + wm8750_dac_commit(s->wm); + + s->last_free = free_out - block_size; + + if (s->play_pos == 0) { + s->status |= MP_AUDIO_TX_HALF; + s->play_pos = block_size; + } else { + s->status |= MP_AUDIO_TX_FULL; + s->play_pos = 0; + } + + if (s->status & s->irq_enable) { + qemu_irq_raise(s->irq); + } +} + +static void mv88w8618_audio_clock_update(mv88w8618_audio_state *s) +{ + int rate; + + if (s->playback_mode & MP_AUDIO_CLOCK_24MHZ) { + rate = 24576000 / 64; /* 24.576MHz */ + } else { + rate = 11289600 / 64; /* 11.2896MHz */ + } + rate /= ((s->clock_div >> 8) & 0xff) + 1; + + wm8750_set_bclk_in(s->wm, rate); +} + +static uint64_t mv88w8618_audio_read(void *opaque, hwaddr offset, + unsigned size) +{ + mv88w8618_audio_state *s = opaque; + + switch (offset) { + case MP_AUDIO_PLAYBACK_MODE: + return s->playback_mode; + + case MP_AUDIO_CLOCK_DIV: + return s->clock_div; + + case MP_AUDIO_IRQ_STATUS: + return s->status; + + case MP_AUDIO_IRQ_ENABLE: + return s->irq_enable; + + case MP_AUDIO_TX_STATUS: + return s->play_pos >> 2; + + default: + return 0; + } +} + +static void mv88w8618_audio_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + mv88w8618_audio_state *s = opaque; + + switch (offset) { + case MP_AUDIO_PLAYBACK_MODE: + if (value & MP_AUDIO_PLAYBACK_EN && + !(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) { + s->status = 0; + s->last_free = 0; + s->play_pos = 0; + } + s->playback_mode = value; + mv88w8618_audio_clock_update(s); + break; + + case MP_AUDIO_CLOCK_DIV: + s->clock_div = value; + s->last_free = 0; + s->play_pos = 0; + mv88w8618_audio_clock_update(s); + break; + + case MP_AUDIO_IRQ_STATUS: + s->status &= ~value; + break; + + case MP_AUDIO_IRQ_ENABLE: + s->irq_enable = value; + if (s->status & s->irq_enable) { + qemu_irq_raise(s->irq); + } + break; + + case MP_AUDIO_TX_START_LO: + s->phys_buf = (s->phys_buf & 0xFFFF0000) | (value & 0xFFFF); + s->target_buffer = s->phys_buf; + s->play_pos = 0; + s->last_free = 0; + break; + + case MP_AUDIO_TX_THRESHOLD: + s->threshold = (value + 1) * 4; + break; + + case MP_AUDIO_TX_START_HI: + s->phys_buf = (s->phys_buf & 0xFFFF) | (value << 16); + s->target_buffer = s->phys_buf; + s->play_pos = 0; + s->last_free = 0; + break; + } +} + +static void mv88w8618_audio_reset(DeviceState *d) +{ + mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state, + SYS_BUS_DEVICE(d)); + + s->playback_mode = 0; + s->status = 0; + s->irq_enable = 0; + s->clock_div = 0; + s->threshold = 0; + s->phys_buf = 0; +} + +static const MemoryRegionOps mv88w8618_audio_ops = { + .read = mv88w8618_audio_read, + .write = mv88w8618_audio_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int mv88w8618_audio_init(SysBusDevice *dev) +{ + mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state, dev); + + sysbus_init_irq(dev, &s->irq); + + wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s); + + memory_region_init_io(&s->iomem, &mv88w8618_audio_ops, s, + "audio", MP_AUDIO_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static const VMStateDescription mv88w8618_audio_vmsd = { + .name = "mv88w8618_audio", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(playback_mode, mv88w8618_audio_state), + VMSTATE_UINT32(status, mv88w8618_audio_state), + VMSTATE_UINT32(irq_enable, mv88w8618_audio_state), + VMSTATE_UINT32(phys_buf, mv88w8618_audio_state), + VMSTATE_UINT32(target_buffer, mv88w8618_audio_state), + VMSTATE_UINT32(threshold, mv88w8618_audio_state), + VMSTATE_UINT32(play_pos, mv88w8618_audio_state), + VMSTATE_UINT32(last_free, mv88w8618_audio_state), + VMSTATE_UINT32(clock_div, mv88w8618_audio_state), + VMSTATE_END_OF_LIST() + } +}; + +static Property mv88w8618_audio_properties[] = { + DEFINE_PROP_PTR("wm8750", mv88w8618_audio_state, wm), + {/* end of list */}, +}; + +static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = mv88w8618_audio_init; + dc->reset = mv88w8618_audio_reset; + dc->vmsd = &mv88w8618_audio_vmsd; + dc->props = mv88w8618_audio_properties; +} + +static const TypeInfo mv88w8618_audio_info = { + .name = "mv88w8618_audio", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(mv88w8618_audio_state), + .class_init = mv88w8618_audio_class_init, +}; + +static void mv88w8618_register_types(void) +{ + type_register_static(&mv88w8618_audio_info); +} + +type_init(mv88w8618_register_types) diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c new file mode 100644 index 0000000000..e08e9dca16 --- /dev/null +++ b/hw/audio/milkymist-ac97.c @@ -0,0 +1,344 @@ +/* + * 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/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "audio/audio.h" +#include "qemu/error-report.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; + MemoryRegion regs_region; + + 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 uint64_t ac97_read(void *opaque, hwaddr addr, + unsigned size) +{ + 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 unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_ac97_memory_read(addr << 2, r); + + return r; +} + +static void ac97_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + 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 unknown register 0x" + TARGET_FMT_plx, addr); + break; + } + +} + +static const MemoryRegionOps ac97_mmio_ops = { + .read = ac97_read, + .write = ac97_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +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); + + 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); + + memory_region_init_io(&s->regs_region, &ac97_mmio_ops, s, + "milkymist-ac97", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); + + 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 void milkymist_ac97_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = milkymist_ac97_init; + dc->reset = milkymist_ac97_reset; + dc->vmsd = &vmstate_milkymist_ac97; +} + +static const TypeInfo milkymist_ac97_info = { + .name = "milkymist-ac97", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MilkymistAC97State), + .class_init = milkymist_ac97_class_init, +}; + +static void milkymist_ac97_register_types(void) +{ + type_register_static(&milkymist_ac97_info); +} + +type_init(milkymist_ac97_register_types) diff --git a/hw/cs4231.c b/hw/cs4231.c deleted file mode 100644 index 2975336057..0000000000 --- a/hw/cs4231.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * QEMU Crystal CS4231 audio chip emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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 "hw/sysbus.h" -#include "trace.h" - -/* - * In addition to Crystal CS4231 there is a DMA controller on Sparc. - */ -#define CS_SIZE 0x40 -#define CS_REGS 16 -#define CS_DREGS 32 -#define CS_MAXDREG (CS_DREGS - 1) - -typedef struct CSState { - SysBusDevice busdev; - MemoryRegion iomem; - qemu_irq irq; - uint32_t regs[CS_REGS]; - uint8_t dregs[CS_DREGS]; -} CSState; - -#define CS_RAP(s) ((s)->regs[0] & CS_MAXDREG) -#define CS_VER 0xa0 -#define CS_CDC_VER 0x8a - -static void cs_reset(DeviceState *d) -{ - CSState *s = container_of(d, CSState, busdev.qdev); - - memset(s->regs, 0, CS_REGS * 4); - memset(s->dregs, 0, CS_DREGS); - s->dregs[12] = CS_CDC_VER; - s->dregs[25] = CS_VER; -} - -static uint64_t cs_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - CSState *s = opaque; - uint32_t saddr, ret; - - saddr = addr >> 2; - switch (saddr) { - case 1: - switch (CS_RAP(s)) { - case 3: // Write only - ret = 0; - break; - default: - ret = s->dregs[CS_RAP(s)]; - break; - } - trace_cs4231_mem_readl_dreg(CS_RAP(s), ret); - break; - default: - ret = s->regs[saddr]; - trace_cs4231_mem_readl_reg(saddr, ret); - break; - } - return ret; -} - -static void cs_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - CSState *s = opaque; - uint32_t saddr; - - saddr = addr >> 2; - trace_cs4231_mem_writel_reg(saddr, s->regs[saddr], val); - switch (saddr) { - case 1: - trace_cs4231_mem_writel_dreg(CS_RAP(s), s->dregs[CS_RAP(s)], val); - switch(CS_RAP(s)) { - case 11: - case 25: // Read only - break; - case 12: - val &= 0x40; - val |= CS_CDC_VER; // Codec version - s->dregs[CS_RAP(s)] = val; - break; - default: - s->dregs[CS_RAP(s)] = val; - break; - } - break; - case 2: // Read only - break; - case 4: - if (val & 1) { - cs_reset(&s->busdev.qdev); - } - val &= 0x7f; - s->regs[saddr] = val; - break; - default: - s->regs[saddr] = val; - break; - } -} - -static const MemoryRegionOps cs_mem_ops = { - .read = cs_mem_read, - .write = cs_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_cs4231 = { - .name ="cs4231", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField []) { - VMSTATE_UINT32_ARRAY(regs, CSState, CS_REGS), - VMSTATE_UINT8_ARRAY(dregs, CSState, CS_DREGS), - VMSTATE_END_OF_LIST() - } -}; - -static int cs4231_init1(SysBusDevice *dev) -{ - CSState *s = FROM_SYSBUS(CSState, dev); - - memory_region_init_io(&s->iomem, &cs_mem_ops, s, "cs4321", CS_SIZE); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - - return 0; -} - -static Property cs4231_properties[] = { - {.name = NULL}, -}; - -static void cs4231_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = cs4231_init1; - dc->reset = cs_reset; - dc->vmsd = &vmstate_cs4231; - dc->props = cs4231_properties; -} - -static const TypeInfo cs4231_info = { - .name = "SUNW,CS4231", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CSState), - .class_init = cs4231_class_init, -}; - -static void cs4231_register_types(void) -{ - type_register_static(&cs4231_info); -} - -type_init(cs4231_register_types) diff --git a/hw/lm32/Makefile.objs b/hw/lm32/Makefile.objs index d72756c877..e328ec8400 100644 --- a/hw/lm32/Makefile.objs +++ b/hw/lm32/Makefile.objs @@ -4,7 +4,6 @@ obj-y += lm32_juart.o obj-y += lm32_timer.o obj-y += lm32_uart.o obj-y += lm32_sys.o -obj-y += milkymist-ac97.o obj-y += milkymist-hpdmc.o obj-y += milkymist-memcard.o obj-y += milkymist-pfpu.o diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c deleted file mode 100644 index f9b68fd311..0000000000 --- a/hw/marvell_88w8618_audio.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Marvell 88w8618 audio emulation extracted from - * Marvell MV88w8618 / Freecom MusicPal emulation. - * - * Copyright (c) 2008 Jan Kiszka - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "hw/sysbus.h" -#include "audio/audio.h" - -#define MP_AUDIO_SIZE 0x00001000 - -/* Audio register offsets */ -#define MP_AUDIO_PLAYBACK_MODE 0x00 -#define MP_AUDIO_CLOCK_DIV 0x18 -#define MP_AUDIO_IRQ_STATUS 0x20 -#define MP_AUDIO_IRQ_ENABLE 0x24 -#define MP_AUDIO_TX_START_LO 0x28 -#define MP_AUDIO_TX_THRESHOLD 0x2C -#define MP_AUDIO_TX_STATUS 0x38 -#define MP_AUDIO_TX_START_HI 0x40 - -/* Status register and IRQ enable bits */ -#define MP_AUDIO_TX_HALF (1 << 6) -#define MP_AUDIO_TX_FULL (1 << 7) - -/* Playback mode bits */ -#define MP_AUDIO_16BIT_SAMPLE (1 << 0) -#define MP_AUDIO_PLAYBACK_EN (1 << 7) -#define MP_AUDIO_CLOCK_24MHZ (1 << 9) -#define MP_AUDIO_MONO (1 << 14) - -typedef struct mv88w8618_audio_state { - SysBusDevice busdev; - MemoryRegion iomem; - qemu_irq irq; - uint32_t playback_mode; - uint32_t status; - uint32_t irq_enable; - uint32_t phys_buf; - uint32_t target_buffer; - uint32_t threshold; - uint32_t play_pos; - uint32_t last_free; - uint32_t clock_div; - void *wm; -} mv88w8618_audio_state; - -static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in) -{ - mv88w8618_audio_state *s = opaque; - int16_t *codec_buffer; - int8_t buf[4096]; - int8_t *mem_buffer; - int pos, block_size; - - if (!(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) { - return; - } - if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) { - free_out <<= 1; - } - if (!(s->playback_mode & MP_AUDIO_MONO)) { - free_out <<= 1; - } - block_size = s->threshold / 2; - if (free_out - s->last_free < block_size) { - return; - } - if (block_size > 4096) { - return; - } - cpu_physical_memory_read(s->target_buffer + s->play_pos, (void *)buf, - block_size); - mem_buffer = buf; - if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) { - if (s->playback_mode & MP_AUDIO_MONO) { - codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1); - for (pos = 0; pos < block_size; pos += 2) { - *codec_buffer++ = *(int16_t *)mem_buffer; - *codec_buffer++ = *(int16_t *)mem_buffer; - mem_buffer += 2; - } - } else { - memcpy(wm8750_dac_buffer(s->wm, block_size >> 2), - (uint32_t *)mem_buffer, block_size); - } - } else { - if (s->playback_mode & MP_AUDIO_MONO) { - codec_buffer = wm8750_dac_buffer(s->wm, block_size); - for (pos = 0; pos < block_size; pos++) { - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer); - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); - } - } else { - codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1); - for (pos = 0; pos < block_size; pos += 2) { - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); - } - } - } - wm8750_dac_commit(s->wm); - - s->last_free = free_out - block_size; - - if (s->play_pos == 0) { - s->status |= MP_AUDIO_TX_HALF; - s->play_pos = block_size; - } else { - s->status |= MP_AUDIO_TX_FULL; - s->play_pos = 0; - } - - if (s->status & s->irq_enable) { - qemu_irq_raise(s->irq); - } -} - -static void mv88w8618_audio_clock_update(mv88w8618_audio_state *s) -{ - int rate; - - if (s->playback_mode & MP_AUDIO_CLOCK_24MHZ) { - rate = 24576000 / 64; /* 24.576MHz */ - } else { - rate = 11289600 / 64; /* 11.2896MHz */ - } - rate /= ((s->clock_div >> 8) & 0xff) + 1; - - wm8750_set_bclk_in(s->wm, rate); -} - -static uint64_t mv88w8618_audio_read(void *opaque, hwaddr offset, - unsigned size) -{ - mv88w8618_audio_state *s = opaque; - - switch (offset) { - case MP_AUDIO_PLAYBACK_MODE: - return s->playback_mode; - - case MP_AUDIO_CLOCK_DIV: - return s->clock_div; - - case MP_AUDIO_IRQ_STATUS: - return s->status; - - case MP_AUDIO_IRQ_ENABLE: - return s->irq_enable; - - case MP_AUDIO_TX_STATUS: - return s->play_pos >> 2; - - default: - return 0; - } -} - -static void mv88w8618_audio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - mv88w8618_audio_state *s = opaque; - - switch (offset) { - case MP_AUDIO_PLAYBACK_MODE: - if (value & MP_AUDIO_PLAYBACK_EN && - !(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) { - s->status = 0; - s->last_free = 0; - s->play_pos = 0; - } - s->playback_mode = value; - mv88w8618_audio_clock_update(s); - break; - - case MP_AUDIO_CLOCK_DIV: - s->clock_div = value; - s->last_free = 0; - s->play_pos = 0; - mv88w8618_audio_clock_update(s); - break; - - case MP_AUDIO_IRQ_STATUS: - s->status &= ~value; - break; - - case MP_AUDIO_IRQ_ENABLE: - s->irq_enable = value; - if (s->status & s->irq_enable) { - qemu_irq_raise(s->irq); - } - break; - - case MP_AUDIO_TX_START_LO: - s->phys_buf = (s->phys_buf & 0xFFFF0000) | (value & 0xFFFF); - s->target_buffer = s->phys_buf; - s->play_pos = 0; - s->last_free = 0; - break; - - case MP_AUDIO_TX_THRESHOLD: - s->threshold = (value + 1) * 4; - break; - - case MP_AUDIO_TX_START_HI: - s->phys_buf = (s->phys_buf & 0xFFFF) | (value << 16); - s->target_buffer = s->phys_buf; - s->play_pos = 0; - s->last_free = 0; - break; - } -} - -static void mv88w8618_audio_reset(DeviceState *d) -{ - mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state, - SYS_BUS_DEVICE(d)); - - s->playback_mode = 0; - s->status = 0; - s->irq_enable = 0; - s->clock_div = 0; - s->threshold = 0; - s->phys_buf = 0; -} - -static const MemoryRegionOps mv88w8618_audio_ops = { - .read = mv88w8618_audio_read, - .write = mv88w8618_audio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mv88w8618_audio_init(SysBusDevice *dev) -{ - mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state, dev); - - sysbus_init_irq(dev, &s->irq); - - wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s); - - memory_region_init_io(&s->iomem, &mv88w8618_audio_ops, s, - "audio", MP_AUDIO_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static const VMStateDescription mv88w8618_audio_vmsd = { - .name = "mv88w8618_audio", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(playback_mode, mv88w8618_audio_state), - VMSTATE_UINT32(status, mv88w8618_audio_state), - VMSTATE_UINT32(irq_enable, mv88w8618_audio_state), - VMSTATE_UINT32(phys_buf, mv88w8618_audio_state), - VMSTATE_UINT32(target_buffer, mv88w8618_audio_state), - VMSTATE_UINT32(threshold, mv88w8618_audio_state), - VMSTATE_UINT32(play_pos, mv88w8618_audio_state), - VMSTATE_UINT32(last_free, mv88w8618_audio_state), - VMSTATE_UINT32(clock_div, mv88w8618_audio_state), - VMSTATE_END_OF_LIST() - } -}; - -static Property mv88w8618_audio_properties[] = { - DEFINE_PROP_PTR("wm8750", mv88w8618_audio_state, wm), - {/* end of list */}, -}; - -static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mv88w8618_audio_init; - dc->reset = mv88w8618_audio_reset; - dc->vmsd = &mv88w8618_audio_vmsd; - dc->props = mv88w8618_audio_properties; -} - -static const TypeInfo mv88w8618_audio_info = { - .name = "mv88w8618_audio", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mv88w8618_audio_state), - .class_init = mv88w8618_audio_class_init, -}; - -static void mv88w8618_register_types(void) -{ - type_register_static(&mv88w8618_audio_info); -} - -type_init(mv88w8618_register_types) diff --git a/hw/milkymist-ac97.c b/hw/milkymist-ac97.c deleted file mode 100644 index e08e9dca16..0000000000 --- a/hw/milkymist-ac97.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * 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/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "audio/audio.h" -#include "qemu/error-report.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; - MemoryRegion regs_region; - - 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 uint64_t ac97_read(void *opaque, hwaddr addr, - unsigned size) -{ - 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 unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_ac97_memory_read(addr << 2, r); - - return r; -} - -static void ac97_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - 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 unknown register 0x" - TARGET_FMT_plx, addr); - break; - } - -} - -static const MemoryRegionOps ac97_mmio_ops = { - .read = ac97_read, - .write = ac97_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -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); - - 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); - - memory_region_init_io(&s->regs_region, &ac97_mmio_ops, s, - "milkymist-ac97", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - 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 void milkymist_ac97_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_ac97_init; - dc->reset = milkymist_ac97_reset; - dc->vmsd = &vmstate_milkymist_ac97; -} - -static const TypeInfo milkymist_ac97_info = { - .name = "milkymist-ac97", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistAC97State), - .class_init = milkymist_ac97_class_init, -}; - -static void milkymist_ac97_register_types(void) -{ - type_register_static(&milkymist_ac97_info); -} - -type_init(milkymist_ac97_register_types) diff --git a/hw/sparc/Makefile.objs b/hw/sparc/Makefile.objs index 56eeb9012b..b2a921e7e1 100644 --- a/hw/sparc/Makefile.objs +++ b/hw/sparc/Makefile.objs @@ -1,6 +1,6 @@ obj-y = tcx.o sun4m_iommu.o slavio_intctl.o obj-y += slavio_timer.o slavio_misc.o sparc32_dma.o -obj-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o +obj-y += eccmemctl.o sbi.o sun4c_intctl.o # GRLIB obj-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o -- cgit v1.2.1