From nobody Fri Oct 18 08:31:12 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=ilande.co.uk Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1684962789068842.1345546388367; Wed, 24 May 2023 14:13:09 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1vmg-0002sE-34; Wed, 24 May 2023 17:12:43 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q1vmK-0002ir-Ga for qemu-devel@nongnu.org; Wed, 24 May 2023 17:12:16 -0400 Received: from mail.ilande.co.uk ([2001:41c9:1:41f::167]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q1vmG-000283-Ax for qemu-devel@nongnu.org; Wed, 24 May 2023 17:12:16 -0400 Received: from [2a00:23c4:8bac:6900:b726:cf58:4c12:f013] (helo=kentang.home) by mail.ilande.co.uk with esmtpsa (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q1vm8-0005XR-M5; Wed, 24 May 2023 22:12:08 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=ilande.co.uk; s=20220518; h=Subject:Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-Id:Date:To:From:Sender:Reply-To: Cc:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=R3TuP+W/Vc7mJivGtHeuJs5OMlf/rZb26T9EpLNNhjQ=; b=x9wwEjAczPiM9OOJg6ABxCF+0Q 5THrT+LnQqVM1iG4AAWla0uCeFTOjIZJV1gNnfM3pQlzAF/wrxxoGa2L+8HbNrNiTvSQXHju/d13N TRXbrdraICmH6H+i23fLFwMLF9H8cGMm3h0rKxg1lQL+9STatrDUJIlrX+0c/tHtpaYcGP5F2yZwe 3+T1H8ZrtWi3uryjNeDphvJ6Iwl/KbwcdoEgE+m0ihQuk0k0KuV3AgaqdUzo/u4FEQC2VgO4n9akB +l9WDSjj0/b9QcBxjvs23F7hcTgfWgtJDsJiF2TJGS7AV3dW8/GGdgpf6oTvqaLLLjWk96Vg6oXgH GmsePantPHV+hxaB1Ai6CMVMIUo17+BV+KeOxRcUMeUMyndw1+D1osEYtkSpEcUcmfeYQkMY5DG6x 6dlOS4kxWz7GdiVMMu7LF2I1CthSLWVwR513iRzfqEvTmb62uDJpcVdqn5MOJfL4pkMbHfmjABLfq m29Ij+3gtKvQgDwTGQ8HnKKrx/IG4Si1qVW18oi1kRMf500jVcgsJSv9xXyHys/bRPXXAi4fGzFTm GitxYsm+LLBnefm3dR044UpCjYeBkERTS7avCmjJJmDBgcy5BXvgWYuYkswhCp6niDe4ZKQmHT14f H3apsjFygy95y247yxxQMrSCIURx5LUc3qbYB06CQ=; From: Mark Cave-Ayland To: laurent@vivier.eu, qemu-devel@nongnu.org Date: Wed, 24 May 2023 22:10:48 +0100 Message-Id: <20230524211104.686087-15-mark.cave-ayland@ilande.co.uk> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230524211104.686087-1-mark.cave-ayland@ilande.co.uk> References: <20230524211104.686087-1-mark.cave-ayland@ilande.co.uk> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-SA-Exim-Connect-IP: 2a00:23c4:8bac:6900:b726:cf58:4c12:f013 X-SA-Exim-Mail-From: mark.cave-ayland@ilande.co.uk Subject: [PATCH 14/30] audio: add Apple Sound Chip (ASC) emulation X-SA-Exim-Version: 4.2.1 (built Wed, 08 May 2019 21:11:16 +0000) X-SA-Exim-Scanned: Yes (on mail.ilande.co.uk) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:41c9:1:41f::167; envelope-from=mark.cave-ayland@ilande.co.uk; helo=mail.ilande.co.uk X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1684962789316100001 The Apple Sound Chip was primarily used by the Macintosh II to generate sou= nd in hardware which was previously handled by the toolbox ROM with software interrupts. Implement both the standard ASC and also the enhanced ASC (EASC) functional= ity which is used in the Quadra 800. Note that whilst real ASC hardware uses AUDIO_FORMAT_S8, this implementatio= n uses AUDIO_FORMAT_U8 instead because AUDIO_FORMAT_S8 is rarely used and not supp= orted by some audio backends like PulseAudio and DirectSound when played directly= with -audiodev out.mixing-engine=3Doff. Co-developed-by: Laurent Vivier Co-developed-by: Volker R=C3=BCmelin Signed-off-by: Mark Cave-Ayland Reviewed-by: Volker R=C3=BCmelin --- MAINTAINERS | 2 + hw/audio/Kconfig | 3 + hw/audio/asc.c | 688 +++++++++++++++++++++++++++++++++++++++++ hw/audio/meson.build | 1 + hw/audio/trace-events | 10 + hw/m68k/Kconfig | 1 + include/hw/audio/asc.h | 75 +++++ 7 files changed, 780 insertions(+) create mode 100644 hw/audio/asc.c create mode 100644 include/hw/audio/asc.h diff --git a/MAINTAINERS b/MAINTAINERS index f151aaf99f..1b79ab7965 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1229,6 +1229,7 @@ F: hw/display/macfb.c F: hw/block/swim.c F: hw/misc/djmemc.c F: hw/misc/iosb.c +F: hw/audio/asc.c F: hw/m68k/bootinfo.h F: include/standard-headers/asm-m68k/bootinfo.h F: include/standard-headers/asm-m68k/bootinfo-mac.h @@ -1239,6 +1240,7 @@ F: include/hw/block/swim.h F: include/hw/m68k/q800.h F: include/hw/misc/djmemc.c F: include/hw/misc/iosb.c +F: include/hw/audio/asc.h =20 virt M: Laurent Vivier diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig index e76c69ca7e..d0993514a1 100644 --- a/hw/audio/Kconfig +++ b/hw/audio/Kconfig @@ -47,3 +47,6 @@ config PL041 =20 config CS4231 bool + +config ASC + bool diff --git a/hw/audio/asc.c b/hw/audio/asc.c new file mode 100644 index 0000000000..04194b1e43 --- /dev/null +++ b/hw/audio/asc.c @@ -0,0 +1,688 @@ +/* + * QEMU Apple Sound Chip emulation + * + * Apple Sound Chip (ASC) 344S0063 + * Enhanced Apple Sound Chip (EASC) 343S1063 + * + * Copyright (c) 2012-2018 Laurent Vivier + * Copyright (c) 2022 Mark Cave-Ayland + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "audio/audio.h" +#include "hw/audio/asc.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "trace.h" + +/* + * Linux doesn't provide information about ASC, see arch/m68k/mac/macboing= .c + * and arch/m68k/include/asm/mac_asc.h + * + * best information is coming from MAME: + * https://github.com/mamedev/mame/blob/master/src/devices/sound/asc.h + * https://github.com/mamedev/mame/blob/master/src/devices/sound/asc.cpp + * Emulation by R. Belmont + * or MESS: + * http://mess.redump.net/mess/driver_info/easc + * + * 0x800: VERSION + * 0x801: MODE + * 1=3DFIFO mode, + * 2=3Dwavetable mode + * 0x802: CONTROL + * bit 0=3Danalog or PWM output, + * 1=3Dstereo/mono, + * 7=3Dprocessing time exceeded + * 0x803: FIFO MODE + * bit 7=3Dclear FIFO, + * bit 1=3D"non-ROM companding", + * bit 0=3D"ROM companding") + * 0x804: FIFO IRQ STATUS + * bit 0=3Dch A 1/2 full, + * 1=3Dch A full, + * 2=3Dch B 1/2 full, + * 3=3Dch B full) + * 0x805: WAVETABLE CONTROL + * bits 0-3 wavetables 0-3 start + * 0x806: VOLUME + * bits 2-4 =3D 3 bit internal ASC volume, + * bits 5-7 =3D volume control sent to Sony sound chip + * 0x807: CLOCK RATE + * 0 =3D Mac 22257 Hz, + * 1 =3D undefined, + * 2 =3D 22050 Hz, + * 3 =3D 44100 Hz + * 0x80a: PLAY REC A + * 0x80f: TEST + * bits 6-7 =3D digital test, + * bits 4-5 =3D analog test + * 0x810: WAVETABLE 0 PHASE + * big-endian 9.15 fixed-point, only 24 bits valid + * 0x814: WAVETABLE 0 INCREMENT + * big-endian 9.15 fixed-point, only 24 bits valid + * 0x818: WAVETABLE 1 PHASE + * 0x81C: WAVETABLE 1 INCREMENT + * 0x820: WAVETABLE 2 PHASE + * 0x824: WAVETABLE 2 INCREMENT + * 0x828: WAVETABLE 3 PHASE + * 0x82C: WAVETABLE 3 INCREMENT + * 0x830: UNKNOWN START + * NetBSD writes Wavetable data here (are there more + * wavetables/channels than we know about?) + * 0x857: UNKNOWN END + */ + +#define ASC_SIZE 0x2000 + +enum { + ASC_VERSION =3D 0x00, + ASC_MODE =3D 0x01, + ASC_CONTROL =3D 0x02, + ASC_FIFOMODE =3D 0x03, + ASC_FIFOIRQ =3D 0x04, + ASC_WAVECTRL =3D 0x05, + ASC_VOLUME =3D 0x06, + ASC_CLOCK =3D 0x07, + ASC_PLAYRECA =3D 0x0a, + ASC_TEST =3D 0x0f, + ASC_WAVETABLE =3D 0x10 +}; + +#define ASC_FIFO_STATUS_HALF_FULL 1 +#define ASC_FIFO_STATUS_FULL_EMPTY 2 + +#define ASC_EXTREGS_FIFOCTRL 0x8 +#define ASC_EXTREGS_INTCTRL 0x9 +#define ASC_EXTREGS_CDXA_DECOMP_FILT 0x10 + + +static void asc_raise_irq(ASCState *s) +{ + qemu_set_irq(s->irq, 1); +} + +static void asc_lower_irq(ASCState *s) +{ + qemu_set_irq(s->irq, 0); +} + +static uint8_t asc_fifo_get(ASCFIFOState *fs) +{ + ASCState *s =3D container_of(fs, ASCState, fifos[fs->index]); + bool fifo_half_irq_enabled =3D fs->extregs[ASC_EXTREGS_INTCTRL] & 1; + uint8_t val; + + assert(fs->cnt); + + val =3D fs->fifo[fs->rptr]; + trace_asc_fifo_get('A' + fs->index, fs->rptr, fs->cnt, val); + + fs->rptr++; + fs->rptr &=3D 0x3ff; + fs->cnt--; + + if (fs->cnt <=3D 0x1ff) { + /* FIFO less than half full */ + fs->int_status |=3D ASC_FIFO_STATUS_HALF_FULL; + } else { + /* FIFO more than half full */ + fs->int_status &=3D ~ASC_FIFO_STATUS_HALF_FULL; + } + + if (fs->cnt =3D=3D 0x1ff && fifo_half_irq_enabled) { + /* Raise FIFO half full IRQ */ + asc_raise_irq(s); + } + + if (fs->cnt =3D=3D 0) { + /* Raise FIFO empty IRQ */ + fs->int_status |=3D ASC_FIFO_STATUS_FULL_EMPTY; + asc_raise_irq(s); + } + + return val; +} + +static int generate_fifo(ASCState *s, int maxsamples) +{ + uint8_t *buf =3D s->mixbuf; + int i, limit, count =3D 0; + + limit =3D MIN(MAX(s->fifos[0].cnt, s->fifos[1].cnt), maxsamples); + + /* + * If starting a new run with no FIFO data present, update the IRQ and + * continue + */ + if (limit =3D=3D 0 && s->fifos[0].int_status =3D=3D 0 && + s->fifos[1].int_status =3D=3D 0) { + s->fifos[0].int_status |=3D ASC_FIFO_STATUS_HALF_FULL | + ASC_FIFO_STATUS_FULL_EMPTY; + s->fifos[1].int_status |=3D ASC_FIFO_STATUS_HALF_FULL | + ASC_FIFO_STATUS_FULL_EMPTY; + + asc_raise_irq(s); + return 0; + } + + while (count < limit) { + uint8_t val; + int16_t d, f0, f1; + int32_t t; + int shift, filter; + bool hasdata =3D true; + + for (i =3D 0; i < 2; i++) { + ASCFIFOState *fs =3D &s->fifos[i]; + + switch (fs->extregs[ASC_EXTREGS_FIFOCTRL] & 0x83) { + case 0x82: + /* + * CD-XA BRR mode: exit if there isn't enough data in the = FIFO + * for a complete 15 byte packet + */ + if (fs->xa_cnt =3D=3D -1 && fs->cnt < 15) { + hasdata =3D false; + continue; + } + + if (fs->xa_cnt =3D=3D -1) { + /* Start of packet, get flags */ + fs->xa_flags =3D asc_fifo_get(fs); + fs->xa_cnt =3D 0; + } + + shift =3D fs->xa_flags & 0xf; + filter =3D fs->xa_flags >> 4; + f0 =3D (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT + + (filter << 1) + 1]; + f1 =3D (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT + + (filter << 1)]; + if ((fs->xa_cnt & 1) =3D=3D 0) { + fs->xa_val =3D asc_fifo_get(fs); + d =3D (fs->xa_val & 0xf) << 12; + } else { + d =3D (fs->xa_val & 0xf0) << 8; + } + t =3D (d >> shift) + (((fs->xa_last[0] * f0) + + (fs->xa_last[1] * f1) + 32) >> 6); + if (t < -32768) { + t =3D -32768; + } else if (t > 32768) { + t =3D 32768; + } + + /* + * CD-XA BRR generates 16-bit signed output, so convert to + * 8-bit before writing to buffer. Does real hardware do t= he + * same? + */ + buf[count * 2 + i] =3D (uint8_t)(t / 256) ^ 0x80; + fs->xa_cnt++; + + fs->xa_last[1] =3D fs->xa_last[0]; + fs->xa_last[0] =3D (int16_t)t; + + if (fs->xa_cnt =3D=3D 28) { + /* End of packet */ + fs->xa_cnt =3D -1; + } + break; + + default: + /* fallthrough */ + case 0x80: + /* Raw mode */ + if (fs->cnt) { + val =3D asc_fifo_get(fs); + } else { + val =3D 0x80; + } + + buf[count * 2 + i] =3D val; + break; + } + } + + if (!hasdata) { + break; + } + + count++; + } + + return count; +} + +static int generate_wavetable(ASCState *s, int maxsamples) +{ + uint8_t *buf =3D s->mixbuf; + int channel, count =3D 0; + + while (count < maxsamples) { + uint32_t left =3D 0, right =3D 0; + uint8_t sample; + + for (channel =3D 0; channel < 4; channel++) { + ASCFIFOState *fs =3D &s->fifos[channel >> 1]; + int chanreg =3D ASC_WAVETABLE + (channel << 3); + uint32_t phase, incr, offset; + + phase =3D ldl_be_p(&s->regs[chanreg]); + incr =3D ldl_be_p(&s->regs[chanreg + sizeof(uint32_t)]); + + phase +=3D incr; + offset =3D (phase >> 15) & 0x1ff; + sample =3D fs->fifo[0x200 * (channel >> 1) + offset]; + + stl_be_p(&s->regs[chanreg], phase); + + left +=3D sample; + right +=3D sample; + } + + buf[count * 2] =3D left >> 2; + buf[count * 2 + 1] =3D right >> 2; + + count++; + } + + return count; +} + +static void asc_out_cb(void *opaque, int free_b) +{ + ASCState *s =3D opaque; + int samples; + + samples =3D MIN(s->samples, free_b >> s->shift); + if (!samples) { + return; + } + + switch (s->regs[ASC_MODE] & 3) { + default: + /* Off */ + samples =3D 0; + break; + case 1: + /* FIFO mode */ + samples =3D generate_fifo(s, samples); + break; + case 2: + /* Wave table mode */ + samples =3D generate_wavetable(s, samples); + break; + } + + if (!samples) { + return; + } + + AUD_write(s->voice, s->mixbuf, samples << s->shift); +} + +static uint64_t asc_fifo_read(void *opaque, hwaddr addr, + unsigned size) +{ + ASCFIFOState *fs =3D opaque; + + trace_asc_read_fifo('A' + fs->index, addr, size, fs->fifo[addr]); + return fs->fifo[addr]; +} + +static void asc_fifo_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ASCFIFOState *fs =3D opaque; + ASCState *s =3D container_of(fs, ASCState, fifos[fs->index]); + bool fifo_half_irq_enabled =3D fs->extregs[ASC_EXTREGS_INTCTRL] & 1; + + trace_asc_write_fifo('A' + fs->index, addr, size, fs->wptr, fs->cnt, v= alue); + + if (s->regs[ASC_MODE] =3D=3D 1) { + fs->fifo[fs->wptr++] =3D value; + fs->wptr &=3D 0x3ff; + fs->cnt++; + + if (fs->cnt <=3D 0x1ff) { + /* FIFO less than half full */ + fs->int_status |=3D ASC_FIFO_STATUS_HALF_FULL; + } else { + /* FIFO at least half full */ + fs->int_status &=3D ~ASC_FIFO_STATUS_HALF_FULL; + } + + if (fs->cnt =3D=3D 0x200 && fifo_half_irq_enabled) { + /* Raise FIFO half full interrupt */ + asc_raise_irq(s); + } + + if (fs->cnt =3D=3D 0x3ff) { + /* Raise FIFO full interrupt */ + fs->int_status |=3D ASC_FIFO_STATUS_FULL_EMPTY; + asc_raise_irq(s); + } + } else { + fs->fifo[addr] =3D value; + } + return; +} + +static const MemoryRegionOps asc_fifo_ops =3D { + .read =3D asc_fifo_read, + .write =3D asc_fifo_write, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, + .endianness =3D DEVICE_BIG_ENDIAN, +}; + +static void asc_fifo_reset(ASCFIFOState *fs); + +static uint64_t asc_read(void *opaque, hwaddr addr, + unsigned size) +{ + ASCState *s =3D opaque; + uint64_t prev, value; + + switch (addr) { + case ASC_VERSION: + switch (s->type) { + default: + case ASC_TYPE_ASC: + value =3D 0; + break; + case ASC_TYPE_EASC: + value =3D 0xb0; + break; + } + break; + case ASC_FIFOIRQ: + prev =3D (s->fifos[0].int_status & 0x3) | + (s->fifos[1].int_status & 0x3) << 2; + + s->fifos[0].int_status =3D 0; + s->fifos[1].int_status =3D 0; + asc_lower_irq(s); + value =3D prev; + break; + default: + value =3D s->regs[addr]; + break; + } + + trace_asc_read_reg(addr, size, value); + return value; +} + +static void asc_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ASCState *s =3D opaque; + + switch (addr) { + case ASC_MODE: + value &=3D 3; + if (value !=3D s->regs[ASC_MODE]) { + asc_fifo_reset(&s->fifos[0]); + asc_fifo_reset(&s->fifos[1]); + asc_lower_irq(s); + if (value !=3D 0) { + AUD_set_active_out(s->voice, 1); + } else { + AUD_set_active_out(s->voice, 0); + } + } + break; + case ASC_FIFOMODE: + if (value & 0x80) { + asc_fifo_reset(&s->fifos[0]); + asc_fifo_reset(&s->fifos[1]); + asc_lower_irq(s); + } + break; + case ASC_WAVECTRL: + break; + case ASC_VOLUME: + { + int vol =3D (value & 0xe0); + + AUD_set_volume_out(s->voice, 0, vol, vol); + break; + } + } + + trace_asc_write_reg(addr, size, value); + s->regs[addr] =3D value; +} + +static const MemoryRegionOps asc_regs_ops =3D { + .read =3D asc_read, + .write =3D asc_write, + .endianness =3D DEVICE_BIG_ENDIAN, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + } +}; + +static uint64_t asc_ext_read(void *opaque, hwaddr addr, + unsigned size) +{ + ASCFIFOState *fs =3D opaque; + uint64_t value; + + value =3D fs->extregs[addr]; + + trace_asc_read_extreg('A' + fs->index, addr, size, value); + return value; +} + +static void asc_ext_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ASCFIFOState *fs =3D opaque; + + trace_asc_write_extreg('A' + fs->index, addr, size, value); + + fs->extregs[addr] =3D value; +} + +static const MemoryRegionOps asc_extregs_ops =3D { + .read =3D asc_ext_read, + .write =3D asc_ext_write, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, + .endianness =3D DEVICE_BIG_ENDIAN, +}; + +static int asc_post_load(void *opaque, int version) +{ + ASCState *s =3D ASC(opaque); + + if (s->regs[ASC_MODE] !=3D 0) { + AUD_set_active_out(s->voice, 1); + } + + return 0; +} + +static const VMStateDescription vmstate_asc_fifo =3D { + .name =3D "apple-sound-chip.fifo", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_UINT8_ARRAY(fifo, ASCFIFOState, ASC_FIFO_SIZE), + VMSTATE_UINT8(int_status, ASCFIFOState), + VMSTATE_INT32(cnt, ASCFIFOState), + VMSTATE_INT32(wptr, ASCFIFOState), + VMSTATE_INT32(rptr, ASCFIFOState), + VMSTATE_UINT8_ARRAY(extregs, ASCFIFOState, ASC_EXTREG_SIZE), + VMSTATE_INT32(xa_cnt, ASCFIFOState), + VMSTATE_UINT8(xa_val, ASCFIFOState), + VMSTATE_UINT8(xa_flags, ASCFIFOState), + VMSTATE_INT16_ARRAY(xa_last, ASCFIFOState, 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_asc =3D { + .name =3D "apple-sound-chip", + .version_id =3D 0, + .minimum_version_id =3D 0, + .post_load =3D asc_post_load, + .fields =3D (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(fifos, ASCState, 2, 0, vmstate_asc_fifo, + ASCFIFOState), + VMSTATE_UINT8_ARRAY(regs, ASCState, ASC_REG_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static void asc_fifo_reset(ASCFIFOState *fs) +{ + fs->wptr =3D 0; + fs->rptr =3D 0; + fs->cnt =3D 0; + fs->xa_cnt =3D -1; + fs->int_status =3D 0; +} + +static void asc_fifo_init(ASCFIFOState *fs, int index) +{ + ASCState *s =3D container_of(fs, ASCState, fifos[index]); + char *name; + + fs->index =3D index; + name =3D g_strdup_printf("asc.fifo%c", 'A' + index); + memory_region_init_io(&fs->mem_fifo, OBJECT(s), &asc_fifo_ops, fs, + name, ASC_FIFO_SIZE); + g_free(name); + + name =3D g_strdup_printf("asc.extregs%c", 'A' + index); + memory_region_init_io(&fs->mem_extregs, OBJECT(s), &asc_extregs_ops, + fs, name, ASC_EXTREG_SIZE); + g_free(name); +} + +static void asc_reset(DeviceState *d) +{ + ASCState *s =3D ASC(d); + + AUD_set_active_out(s->voice, 0); + + memset(s->regs, 0, sizeof(s->regs)); + asc_fifo_reset(&s->fifos[0]); + asc_fifo_reset(&s->fifos[1]); + + if (s->type =3D=3D ASC_TYPE_ASC) { + /* FIFO half full IRQs enabled by default */ + s->fifos[0].extregs[ASC_EXTREGS_INTCTRL] =3D 1; + s->fifos[1].extregs[ASC_EXTREGS_INTCTRL] =3D 1; + } +} + +static void asc_unrealize(DeviceState *dev) +{ + ASCState *s =3D ASC(dev); + + g_free(s->mixbuf); + + AUD_remove_card(&s->card); +} + +static void asc_realize(DeviceState *dev, Error **errp) +{ + ASCState *s =3D ASC(dev); + struct audsettings as; + + AUD_register_card("Apple Sound Chip", &s->card); + + as.freq =3D 22257; + as.nchannels =3D 2; + as.fmt =3D AUDIO_FORMAT_U8; + as.endianness =3D AUDIO_HOST_ENDIANNESS; + + s->voice =3D AUD_open_out(&s->card, s->voice, "asc.out", s, asc_out_cb, + &as); + s->shift =3D 1; + s->samples =3D AUD_get_buffer_size_out(s->voice) >> s->shift; + s->mixbuf =3D g_malloc0(s->samples << s->shift); + + /* Add easc registers if required */ + if (s->type =3D=3D ASC_TYPE_EASC) { + memory_region_add_subregion(&s->asc, ASC_EXTREG_OFFSET, + &s->fifos[0].mem_extregs); + memory_region_add_subregion(&s->asc, + ASC_EXTREG_OFFSET + ASC_EXTREG_SIZE, + &s->fifos[1].mem_extregs); + } +} + +static void asc_init(Object *obj) +{ + ASCState *s =3D ASC(obj); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + + memory_region_init(&s->asc, OBJECT(obj), "asc", ASC_SIZE); + + asc_fifo_init(&s->fifos[0], 0); + asc_fifo_init(&s->fifos[1], 1); + + memory_region_add_subregion(&s->asc, ASC_FIFO_OFFSET, + &s->fifos[0].mem_fifo); + memory_region_add_subregion(&s->asc, + ASC_FIFO_OFFSET + ASC_FIFO_SIZE, + &s->fifos[1].mem_fifo); + + memory_region_init_io(&s->mem_regs, OBJECT(obj), &asc_regs_ops, s, + "asc.regs", ASC_REG_SIZE); + memory_region_add_subregion(&s->asc, ASC_REG_OFFSET, &s->mem_regs); + + sysbus_init_irq(sbd, &s->irq); + sysbus_init_mmio(sbd, &s->asc); +} + +static Property asc_properties[] =3D { + DEFINE_AUDIO_PROPERTIES(ASCState, card), + DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC), + DEFINE_PROP_END_OF_LIST(), +}; + +static void asc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(oc); + + dc->realize =3D asc_realize; + dc->unrealize =3D asc_unrealize; + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + dc->reset =3D asc_reset; + dc->vmsd =3D &vmstate_asc; + device_class_set_props(dc, asc_properties); +} + +static const TypeInfo asc_info =3D { + .name =3D TYPE_ASC, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(ASCState), + .instance_init =3D asc_init, + .class_init =3D asc_class_init, +}; + +static void asc_register_types(void) +{ + type_register_static(&asc_info); +} + +type_init(asc_register_types) diff --git a/hw/audio/meson.build b/hw/audio/meson.build index e48a9fc73d..2de764912f 100644 --- a/hw/audio/meson.build +++ b/hw/audio/meson.build @@ -1,6 +1,7 @@ softmmu_ss.add(files('soundhw.c')) softmmu_ss.add(when: 'CONFIG_AC97', if_true: files('ac97.c')) softmmu_ss.add(when: 'CONFIG_ADLIB', if_true: files('fmopl.c', 'adlib.c')) +softmmu_ss.add(when: 'CONFIG_ASC', if_true: files('asc.c')) softmmu_ss.add(when: 'CONFIG_CS4231', if_true: files('cs4231.c')) softmmu_ss.add(when: 'CONFIG_CS4231A', if_true: files('cs4231a.c')) softmmu_ss.add(when: 'CONFIG_ES1370', if_true: files('es1370.c')) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 4dec48a4fd..89ef2996e5 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -17,3 +17,13 @@ via_ac97_codec_write(uint8_t addr, uint16_t val) "0x%x <= - 0x%x" via_ac97_sgd_fetch(uint32_t curr, uint32_t addr, char stop, char eol, char= flag, uint32_t len) "curr=3D0x%x addr=3D0x%x %c%c%c len=3D%d" via_ac97_sgd_read(uint64_t addr, unsigned size, uint64_t val) "0x%"PRIx64"= %d -> 0x%"PRIx64 via_ac97_sgd_write(uint64_t addr, unsigned size, uint64_t val) "0x%"PRIx64= " %d <- 0x%"PRIx64 + +# asc.c +asc_read_fifo(const char fifo, int reg, unsigned size, uint64_t value) "fi= fo %c reg=3D0x%03x size=3D%u value=3D0x%"PRIx64 +asc_read_reg(int reg, unsigned size, uint64_t value) "reg=3D0x%03x size=3D= %u value=3D0x%"PRIx64 +asc_read_extreg(const char fifo, int reg, unsigned size, uint64_t value) "= fifo %c reg=3D0x%03x size=3D%u value=3D0x%"PRIx64 +asc_fifo_get(const char fifo, int rptr, int cnt, uint64_t value) "fifo %c = rptr=3D0x%x cnt=3D0x%x value=3D0x%"PRIx64 +asc_write_fifo(const char fifo, int reg, unsigned size, int wrptr, int cnt= , uint64_t value) "fifo %c reg=3D0x%03x size=3D%u wptr=3D0x%x cnt=3D0x%x va= lue=3D0x%"PRIx64 +asc_write_reg(int reg, unsigned size, uint64_t value) "reg=3D0x%03x size= =3D%u value=3D0x%"PRIx64 +asc_write_extreg(const char fifo, int reg, unsigned size, uint64_t value) = "fifo %c reg=3D0x%03x size=3D%u value=3D0x%"PRIx64 +asc_update_irq(int irq, int a, int b) "set IRQ to %d (A: 0x%x B: 0x%x)" diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig index 64fa70a0db..d88741ec9d 100644 --- a/hw/m68k/Kconfig +++ b/hw/m68k/Kconfig @@ -25,6 +25,7 @@ config Q800 select OR_IRQ select DJMEMC select IOSB + select ASC =20 config M68K_VIRT bool diff --git a/include/hw/audio/asc.h b/include/hw/audio/asc.h new file mode 100644 index 0000000000..d69aa4ade1 --- /dev/null +++ b/include/hw/audio/asc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2012-2018 Laurent Vivier + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HW_AUDIO_ASC_H +#define HW_AUDIO_ASC_H + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "audio/audio.h" + +enum { + ASC_TYPE_ASC =3D 0, /* original discrete Apple Sound Chip */ + ASC_TYPE_EASC =3D 1 /* discrete Enhanced Apple Sound Chip */ +}; + +#define ASC_FIFO_OFFSET 0x0 +#define ASC_FIFO_SIZE 0x400 + +#define ASC_REG_OFFSET 0x800 +#define ASC_REG_SIZE 0x60 + +#define ASC_EXTREG_OFFSET 0xf00 +#define ASC_EXTREG_SIZE 0x20 + +typedef struct ASCFIFOState { + int index; + + MemoryRegion mem_fifo; + uint8_t fifo[ASC_FIFO_SIZE]; + uint8_t int_status; + + int cnt; + int wptr; + int rptr; + + MemoryRegion mem_extregs; + uint8_t extregs[ASC_EXTREG_SIZE]; + + int xa_cnt; + uint8_t xa_val; + uint8_t xa_flags; + int16_t xa_last[2]; +} ASCFIFOState; + +struct ASCState { + SysBusDevice parent_obj; + + uint8_t type; + MemoryRegion asc; + MemoryRegion mem_fifo; + MemoryRegion mem_regs; + MemoryRegion mem_extregs; + + QEMUSoundCard card; + SWVoiceOut *voice; + uint8_t *mixbuf; + int samples; + int shift; + + qemu_irq irq; + + ASCFIFOState fifos[2]; + + uint8_t regs[ASC_REG_SIZE]; +}; + +#define TYPE_ASC "apple-sound-chip" +OBJECT_DECLARE_SIMPLE_TYPE(ASCState, ASC) + +#endif --=20 2.30.2