From patchwork Fri Jun 8 20:05:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Vivier X-Patchwork-Id: 927025 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=vivier.eu Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 412YRx2gQtz9rxs for ; Sat, 9 Jun 2018 06:10:21 +1000 (AEST) Received: from localhost ([::1]:37979 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fRNiE-0001W9-U4 for incoming@patchwork.ozlabs.org; Fri, 08 Jun 2018 16:10:18 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:47771) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fRNen-0007Wm-Qb for qemu-devel@nongnu.org; Fri, 08 Jun 2018 16:06:51 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fRNel-0006gf-J7 for qemu-devel@nongnu.org; Fri, 08 Jun 2018 16:06:45 -0400 Received: from mout.kundenserver.de ([212.227.126.134]:55083) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fRNel-0006g0-5S; Fri, 08 Jun 2018 16:06:43 -0400 Received: from localhost.localdomain ([78.238.229.36]) by mrelayeu.kundenserver.de (mreue002 [212.227.15.167]) with ESMTPSA (Nemesis) id 0MVEDt-1fmTp616tR-00YQn5; Fri, 08 Jun 2018 22:06:17 +0200 From: Laurent Vivier To: qemu-devel@nongnu.org Date: Fri, 8 Jun 2018 22:05:50 +0200 Message-Id: <20180608200558.386-6-laurent@vivier.eu> X-Mailer: git-send-email 2.14.4 In-Reply-To: <20180608200558.386-1-laurent@vivier.eu> References: <20180608200558.386-1-laurent@vivier.eu> X-Provags-ID: V03:K1:297Gry1o3/AJvqCI90hpmEZMra2hr6hgUqRNagikeAEJJwCOPQk WaXBdFC3izidEDQteUxGDQ77tOHyQBXsNunT71E7ZnpOKBQofUkM1u7BXcsKbH+XDcgywOV 0+QIPi/UB8XJLxD6fVbOh6l6RUU9vNa9cG1qAqbipfw7obQ5AJiecFcgyUExGmbWMvOAc9J 97Jv+RWeai6ViNv38UeYw== X-UI-Out-Filterresults: notjunk:1; V01:K0:Jl1EZa530fM=:RGYz1Rg3ZsMLJpSfSwv6vL QE8pdwOu2WqNw2sSNagsORRHeoGyqGLOUyINkqiBJh1sZwHBTgWdgwLlNv/6HYBIt4zzET5IL 1YVmx0fVl10IUZ9bFUcwViZ1qQ54FXnSh/CZaCxM/mg/JpeBGtZysZ8zSHl55k/F6pQjjsqtk q14QOqrarNDENwL6xG/U1V4YN1vDmnuHhQO5Nl5JqDlmIwWgL681g5370eCaTHNzUglCpET+5 rJlJhhkF6G1IkmTZCWSsmuHAEor06OtovosKoUtNU1sXnLr8O6M4nLiwRSkQs9ZMl4a2OMOoQ nlm15SipVFTZ3pxJ+pvK1m2OwPxzdqpkDY+SpOJUOH8zy+31AZJ1NYQT+BemKV9TOHEurtn2v jxUqC5DeWgYywHncA8su9bQv9rZNZiaguGgvvURCk+UaV5LMNRfwx1gFwM/s+I1SndcJaU5PC sGvxne2Y0T4PDbJjlsLyqrCI5qv8D9Tll/wuElceHwddMY3HHkdV9oTbtFkjmhG1n+jlFCGxr UJljgMTfvJ5WWgcOpO00EvdlGO3AmAbRkhUuOflBI8Iam1Uk366ngH6uCNF2syzta1vLVwOJd hTTEtDva0P8VIBJVeQkvNrHTW/CE++9XoEFYwdRu5sjnj/AcfziUXsM9W2PQPOKivr5883VGz /jcbkxvDi41I4GPqnqZz/SJIywi92/7GdaUSxe5uFVhmFXTffPgCxdJ++x1n7Qgehnr0= X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 212.227.126.134 Subject: [Qemu-devel] [RFC 05/13] hw/m68k: Apple Sound Chip (ASC) emulation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Fam Zheng , qemu-block@nongnu.org, Jason Wang , "Dr. David Alan Gilbert" , Max Reitz , =?utf-8?q?Herv=C3=A9_Poussineau?= , Gerd Hoffmann , Paolo Bonzini , Yongbok Kim , =?utf-8?q?Andreas_F=C3=A4rber?= , Aurelien Jarno , Laurent Vivier Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" From: Laurent Vivier This is broken as the linux driver seems broken too... Signed-off-by: Laurent Vivier --- hw/audio/Makefile.objs | 1 + hw/audio/asc.c | 492 +++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/audio/asc.h | 21 +++ 3 files changed, 514 insertions(+) create mode 100644 hw/audio/asc.c create mode 100644 include/hw/audio/asc.h diff --git a/hw/audio/Makefile.objs b/hw/audio/Makefile.objs index 63db383709..44d1ada7b0 100644 --- a/hw/audio/Makefile.objs +++ b/hw/audio/Makefile.objs @@ -16,3 +16,4 @@ common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o common-obj-y += soundhw.o +common-obj-$(CONFIG_ASC) += asc.o diff --git a/hw/audio/asc.c b/hw/audio/asc.c new file mode 100644 index 0000000000..3c07d4fa91 --- /dev/null +++ b/hw/audio/asc.c @@ -0,0 +1,492 @@ +/* + * QEMU Apple Sound Chip emulation + * + * Apple Sound Chip (ASC) 344S0063 + * Enhanced Apple Sound Chip (EASC) 343S1063 + * + * Copyright (c) 2012-2018 Laurent Vivier + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "audio/audio.h" +#include "hw/audio/asc.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: + * http://mamedev.org/source/src/emu/sound/asc.h.html + * http://mamedev.org/source/src/emu/sound/asc.c.html + * Emulation by R. Belmont + * + * 0x800: VERSION + * 0x801: MODE + * 1=FIFO mode, + * 2=wavetable mode + * 0x802: CONTROL + * bit 0=analog or PWM output, + * 1=stereo/mono, + * 7=processing time exceeded + * 0x803: FIFO MODE + * bit 7=clear FIFO, + * bit 1="non-ROM companding", + * bit 0="ROM companding") + * 0x804: FIFO IRQ STATUS + * bit 0=ch A 1/2 full, + * 1=ch A full, + * 2=ch B 1/2 full, + * 3=ch B full) + * 0x805: WAVETABLE CONTROL + * bits 0-3 wavetables 0-3 start + * 0x806: VOLUME + * bits 2-4 = 3 bit internal ASC volume, + * bits 5-7 = volume control sent to Sony sound chip + * 0x807: CLOCK RATE + * 0 = Mac 22257 Hz, + * 1 = undefined, + * 2 = 22050 Hz, + * 3 = 44100 Hz + * 0x80a: PLAY REC A + * 0x80f: TEST + * bits 6-7 = digital test, + * bits 4-5 = 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 + */ + +#define ASC_LENGTH 0x2000 +#define ASC_BUF_SIZE 0x0800 + +#define ASC_REG_BASE 0x0800 +enum { + ASC_VERSION = 0x00, + ASC_MODE = 0x01, + ASC_CONTROL = 0x02, + ASC_FIFOMODE = 0x03, + ASC_FIFOIRQ = 0x04, + ASC_WAVECTRL = 0x05, + ASC_VOLUME = 0x06, + ASC_CLOCK = 0x07, + ASC_PLAYRECA = 0x0a, + ASC_TEST = 0x0f, + ASC_WAVETABLE = 0x10 +}; + +struct ASCState { + SysBusDevice busdev; + MemoryRegion mem_regs; + + QEMUSoundCard card; + SWVoiceOut *channel; + + qemu_irq irq; + + uint8_t type; + int a_wptr, a_rptr, a_cnt; + int b_wptr, b_rptr, b_cnt; + + uint8_t *fifo; + + uint8_t regs[48]; +}; +typedef struct ASCState ASCState; + +#define TYPE_ASC "apple-sound-chip" +#define ASC(obj) OBJECT_CHECK(ASCSysBusState, (obj), TYPE_ASC) + +static inline uint32_t get_phase(ASCState *s, int channel) +{ + return be32_to_cpu(*(uint32_t *)(s->regs + ASC_WAVETABLE + channel * 8)); +} + +static inline void set_phase(ASCState *s, int channel, uint32_t phase) +{ + *(uint32_t *)(s->regs + ASC_WAVETABLE + channel * 8) = cpu_to_be32(phase); +} + +static inline uint32_t get_incr(ASCState *s, int channel) +{ + return be32_to_cpu(*(uint32_t *)(s->regs + ASC_WAVETABLE + 4 + + channel * 8)); +} + +static inline uint32_t incr_phase(ASCState *s, int channel) +{ + uint32_t incr = get_incr(s, channel); + uint32_t phase = get_phase(s, channel); + + set_phase(s, channel, phase + incr); + + return get_phase(s, channel); +} + +static void generate_fifo(ASCState *s, int free_b) +{ + int8_t buf[2048]; + int i; + int to_copy; + + do { + to_copy = audio_MIN(sizeof(buf), free_b); + for (i = 0; i < (to_copy >> 1); to_copy++) { + int8_t left, right; + + left = s->fifo[s->a_rptr] ^ 0x80; + right = s->fifo[s->b_rptr + 0x400] ^ 0x80; + + if (s->a_cnt) { + s->a_rptr++; + s->a_rptr &= 0x3ff; + s->a_cnt--; + } + + if (s->b_cnt) { + s->b_rptr++; + s->b_rptr &= 0x3ff; + s->b_cnt--; + } + + if (s->type == ASC_TYPE_SONORA) { + if (s->a_cnt < 0x200) { + s->regs[ASC_FIFOIRQ] |= 4; /* FIFO A less than half full */ + qemu_irq_raise(s->irq); + } + if (s->b_cnt < 0x200) { + s->regs[ASC_FIFOIRQ] |= 8; /* FIFO B less than half full */ + qemu_irq_raise(s->irq); + } + } else { + if (s->a_cnt == 0x1ff) { + s->regs[ASC_FIFOIRQ] |= 1; /* FIFO A half empty */ + qemu_irq_raise(s->irq); + } else if (s->a_cnt == 0x001) { + s->regs[ASC_FIFOIRQ] |= 2; /* FIFO A half empty */ + qemu_irq_raise(s->irq); + } + if (s->b_cnt == 0x1ff) { + s->regs[ASC_FIFOIRQ] |= 4; /* FIFO A half empty */ + qemu_irq_raise(s->irq); + } else if (s->b_cnt == 0x001) { + s->regs[ASC_FIFOIRQ] |= 8; /* FIFO A half empty */ + qemu_irq_raise(s->irq); + } + } + buf[i * 2] = left; + buf[i * 2 + 1] = right; + } + AUD_write(s->channel, buf, to_copy); + free_b -= to_copy; + } while (free_b); +} + +static void generate_wavetable(ASCState *s, int free_b) +{ + int8_t buf[2048]; + int i; + int channel; + int to_copy; + int control = s->regs[ASC_WAVECTRL]; + + do { + to_copy = audio_MIN(sizeof(buf), free_b); + for (i = 0; i < (to_copy >> 1); i++) { + int32_t left, right; + int8_t sample; + + left = 0; + right = 0; + + if (control) { /* FIXME: how to use it ? */ + for (channel = 0; channel < 4; channel++) { + uint32_t phase = incr_phase(s, channel); + + phase = (phase >> 15) & 0x1ff; + sample = s->fifo[0x200 * channel + phase] ^ 0x80; + + left += sample; + right += sample; + } + buf[i * 2] = left >> 2; + buf[i * 2 + 1] = right >> 2; + } else { + /* FIXME: only works with linux macboing.c */ + uint32_t phase = incr_phase(s, 0); + phase = (phase >> 15) & 0x7ff; + sample = s->fifo[phase]; + buf[i * 2] = sample; + buf[i * 2 + 1] = sample; + } + } + AUD_write(s->channel, buf, to_copy); + free_b -= to_copy; + } while (free_b); +} + +static void asc_out_cb(void *opaque, int free_b) +{ + ASCState *s = opaque; + + switch (s->regs[ASC_MODE] & 3) { + case 0: /* Off */ + break; + case 1: /* FIFO mode */ + generate_fifo(s, free_b); + break; + case 2: /* Wave table mode */ + generate_wavetable(s, free_b); + break; + } +} + +static uint64_t asc_read(void *opaque, hwaddr addr, + unsigned size) +{ + ASCState *s = opaque; + uint64_t prev; + + if (addr < 0x800) { + return s->fifo[addr]; + } + + addr -= 0x800; + + if (addr >= 0x030) { + return 0; + } + + switch (addr) { + case ASC_VERSION: + switch (s->type) { + case ASC_TYPE_ASC: + return 0; + case ASC_TYPE_V8: + case ASC_TYPE_EAGLE: + case ASC_TYPE_SPICE: + case ASC_TYPE_VASP: + return 0xe8; + case ASC_TYPE_SONORA: + return 0xbc; + default: + break; + } + break; + case ASC_MODE: + switch (s->type) { + case ASC_TYPE_V8: + case ASC_TYPE_EAGLE: + case ASC_TYPE_SPICE: + case ASC_TYPE_VASP: + return 1; + default: + break; + } + break; + case ASC_CONTROL: + switch (s->type) { + case ASC_TYPE_V8: + case ASC_TYPE_EAGLE: + case ASC_TYPE_SPICE: + case ASC_TYPE_VASP: + return 1; + default: + break; + } + break; + case ASC_FIFOIRQ: + if (s->type == ASC_TYPE_V8) { + prev = 3; + } else { + prev = s->regs[ASC_FIFOIRQ]; + } + s->regs[ASC_FIFOIRQ] = 0; + qemu_irq_lower(s->irq); + return prev; + default: + break; + } + + return s->regs[addr]; +} + +static void asc_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ASCState *s = opaque; + + if (addr < 0x800) { + if (s->regs[ASC_MODE] == 1) { + if (addr < 0x400) { + /* FIFO A */ + s->fifo[s->a_wptr++] = value; + s->a_cnt++; + if (s->a_cnt == 0x3ff) { + s->regs[ASC_FIFOIRQ] |= 2; /* FIFO A Full */ + } + s->a_wptr &= 0x3ff; + } else { + /* FIFO B */ + s->fifo[s->b_wptr++ + 0x400] = value; + s->b_cnt++; + if (s->b_cnt == 0x3ff) { + s->regs[ASC_FIFOIRQ] |= 8; /* FIFO B Full */ + } + s->b_wptr &= 0x3ff; + } + } else { + s->fifo[addr] = value; + } + return; + } + + addr -= 0x800; + if (addr >= 0x30) { + return; + } + switch (addr) { + case ASC_MODE: + value &= 3; + if (value != s->regs[ASC_MODE]) { + s->a_rptr = 0; + s->a_wptr = 0; + s->a_cnt = 0; + s->b_rptr = 0; + s->b_wptr = 0; + s->b_cnt = 0; + if (value != 0) { + AUD_set_active_out(s->channel, 1); + } else { + AUD_set_active_out(s->channel, 0); + } + } + break; + case ASC_FIFOMODE: + if (value & 0x80) { + s->a_rptr = 0; + s->a_wptr = 0; + s->a_cnt = 0; + s->b_rptr = 0; + s->b_wptr = 0; + s->b_cnt = 0; + } + break; + case ASC_WAVECTRL: + break; + } + s->regs[addr] = value; +} + +static const MemoryRegionOps asc_mmio_ops = { + .read = asc_read, + .write = asc_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int asc_post_load(void *opaque, int version_id) +{ + return 0; +} + +static const VMStateDescription vmstate_asc = { + .name = "apple-sound-chip", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = asc_post_load, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +typedef struct { + SysBusDevice busdev; + ASCState asc; +} ASCSysBusState; + +static int asc_sysbus_init(SysBusDevice *dev) +{ + ASCState *s = &ASC(dev)->asc; + struct audsettings as; + + s->fifo = g_malloc0(ASC_BUF_SIZE); + + sysbus_init_irq(dev, &s->irq); + + AUD_register_card("Apple Sound Chip", &s->card); + + as.freq = 22257; + as.nchannels = 2; + as.fmt = AUD_FMT_S8; + as.endianness = 0; + + s->channel = AUD_open_out(&s->card, s->channel, "asc.out", + s, asc_out_cb, &as); + + memory_region_init_io(&s->mem_regs, NULL, &asc_mmio_ops, s, "asc", + ASC_LENGTH); + sysbus_init_mmio(dev, &s->mem_regs); + + return 0; +} + +static void asc_sysbus_reset(DeviceState *d) +{ + ASCSysBusState *s = ASC(d); + + AUD_set_active_out(s->asc.channel, 0); + + memset(s->asc.regs, 0, sizeof(s->asc.regs)); + s->asc.a_wptr = 0; + s->asc.a_rptr = 0; + s->asc.a_cnt = 0; + s->asc.b_wptr = 0; + s->asc.b_rptr = 0; + s->asc.b_cnt = 0; +} + +static Property asc_sysbus_properties[] = { + DEFINE_PROP_UINT8("asctype", ASCSysBusState, asc.type, ASC_TYPE_ASC), + DEFINE_PROP_END_OF_LIST(), +}; + +static void asc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = asc_sysbus_init; + dc->reset = asc_sysbus_reset; + dc->vmsd = &vmstate_asc; + dc->props = asc_sysbus_properties; +} + +static TypeInfo asc_sysbus_info = { + .name = TYPE_ASC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ASCSysBusState), + .class_init = asc_class_init, +}; + +static void asc_register_types(void) +{ + type_register_static(&asc_sysbus_info); +} + +type_init(asc_register_types) diff --git a/include/hw/audio/asc.h b/include/hw/audio/asc.h new file mode 100644 index 0000000000..f2a292bbb0 --- /dev/null +++ b/include/hw/audio/asc.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2012-2018 Laurent Vivier + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HW_AUDIO_ASC_H +#define HW_AUDIO_ASC_H +enum { + ASC_TYPE_ASC = 0, /* original discrete Apple Sound Chip */ + ASC_TYPE_EASC = 1, /* discrete Enhanced Apple Sound Chip */ + ASC_TYPE_V8 = 2, /* ASC included in the V8 ASIC (LC/LCII) */ + ASC_TYPE_EAGLE = 3, /* ASC included in the Eagle ASIC (Classic II) */ + ASC_TYPE_SPICE = 4, /* ASC included in the Spice ASIC (Color Classic) */ + ASC_TYPE_SONORA = 5, /* ASC included in the Sonora ASIC (LCIII) */ + ASC_TYPE_VASP = 6, /* ASC included in the VASP ASIC (IIvx/IIvi) */ + ASC_TYPE_ARDBEG = 7 /* ASC included in the Ardbeg ASIC (LC520) */ +}; +#endif