From patchwork Tue Aug 11 17:52:19 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benoit Canet X-Patchwork-Id: 31153 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by bilbo.ozlabs.org (Postfix) with ESMTPS id 7D9FFB6F31 for ; Wed, 12 Aug 2009 03:56:04 +1000 (EST) Received: from localhost ([127.0.0.1]:46151 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MavZo-00040A-7t for incoming@patchwork.ozlabs.org; Tue, 11 Aug 2009 13:56:00 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MavWT-0003CK-WC for qemu-devel@nongnu.org; Tue, 11 Aug 2009 13:52:34 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MavWP-0003AA-U8 for qemu-devel@nongnu.org; Tue, 11 Aug 2009 13:52:33 -0400 Received: from [199.232.76.173] (port=53125 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MavWP-0003A3-Q1 for qemu-devel@nongnu.org; Tue, 11 Aug 2009 13:52:29 -0400 Received: from mail-ew0-f210.google.com ([209.85.219.210]:51115) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MavWO-0001il-PL for qemu-devel@nongnu.org; Tue, 11 Aug 2009 13:52:29 -0400 Received: by mail-ew0-f210.google.com with SMTP id 6so2121788ewy.34 for ; Tue, 11 Aug 2009 10:52:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references; bh=GlCRGtzA0lhgAw7bIf4S5MiBbaRSu1z7jyYoZ+/8uBs=; b=Io/eALOf01ckrsYg/qmXoJaTpsMFAPduxwdNWsTZcuBYS8RyWYMSssH8GIgQNR72G4 gde4o9oZ7x4Mgzt5wKjBwYvNYNXNChYmU1qTiALUimrM4XuB0Fx31871hniNtx4sIj0f cXAwReQK9NZpDiZt91uixPPZWypu0LZ6eaAzg= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=xsFuqfsdxKzvnbCDpVgUu4RpI9+JT5QejJX0gHn2/TqB2GxhqMVqJNGz0MRFr3mAAr jINihheMIHenuB+8wkvLYn/bBhtFTHLAzpqp2AKvdGC+y4OIhxybOSKokNnJ4pl3Vchf WXBAeNobyb8UX0Nh/5esDp1AW6WHg39xvF96g= Received: by 10.210.105.12 with SMTP id d12mr6779823ebc.53.1250013148419; Tue, 11 Aug 2009 10:52:28 -0700 (PDT) Received: from localhost.localdomain (AMontsouris-157-1-65-87.w90-46.abo.wanadoo.fr [90.46.56.87]) by mx.google.com with ESMTPS id 28sm331960eye.24.2009.08.11.10.52.27 (version=SSLv3 cipher=RC4-MD5); Tue, 11 Aug 2009 10:52:27 -0700 (PDT) From: Benoit Canet To: qemu-devel@nongnu.org Date: Tue, 11 Aug 2009 19:52:19 +0200 Message-Id: <1250013140-4061-4-git-send-email-benoit.canet@gmail.com> X-Mailer: git-send-email 1.6.0.4 In-Reply-To: <1250013140-4061-3-git-send-email-benoit.canet@gmail.com> References: <1250013140-4061-1-git-send-email-benoit.canet@gmail.com> <1250013140-4061-2-git-send-email-benoit.canet@gmail.com> <1250013140-4061-3-git-send-email-benoit.canet@gmail.com> X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6 (newer, 2) Cc: Benoit Canet Subject: [Qemu-devel] [PATCH 3/4] Extract the Marvell 88w8xx8 audio device from musicpal.c X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Benoit Canet --- Makefile.target | 2 +- hw/mv88w8xx8_audio.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 1 deletions(-) create mode 100644 hw/mv88w8xx8_audio.c diff --git a/Makefile.target b/Makefile.target index 12fcf21..d505873 100644 --- a/Makefile.target +++ b/Makefile.target @@ -592,7 +592,7 @@ obj-arm-y += omap2.o omap_dss.o soc_dma.o obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o -obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o +obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o mv88w8xx8_audio.o obj-arm-y += framebuffer.o obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o diff --git a/hw/mv88w8xx8_audio.c b/hw/mv88w8xx8_audio.c new file mode 100644 index 0000000..fd94c5f --- /dev/null +++ b/hw/mv88w8xx8_audio.c @@ -0,0 +1,274 @@ +/* + * Marvell 88w8xx8 audio emulation extracted from + * Marvell MV88W8618 / Freecom 88w8xx8 emulation. + * + * Copyright (c) 2008 Jan Kiszka + * + * This code is licenced under the GNU GPL v2. + */ +#include "sysbus.h" +#include "hw.h" +#include "i2c.h" +#include "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) + +#ifdef HAS_AUDIO +typedef struct mv88w8xx8_audio_state { + SysBusDevice busdev; + qemu_irq irq; + uint32_t playback_mode; + uint32_t status; + uint32_t irq_enable; + unsigned long phys_buf; + uint32_t target_buffer; + unsigned int threshold; + unsigned int play_pos; + unsigned int last_free; + uint32_t clock_div; + DeviceState *wm; +} mv88w8xx8_audio_state; + +static void mv88w8xx8_audio_callback(void *opaque, int free_out, int free_in) +{ + mv88w8xx8_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 mv88w8xx8_audio_clock_update(mv88w8xx8_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 uint32_t mv88w8xx8_audio_read(void *opaque, target_phys_addr_t offset) +{ + mv88w8xx8_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 mv88w8xx8_audio_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + mv88w8xx8_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; + mv88w8xx8_audio_clock_update(s); + break; + + case MP_AUDIO_CLOCK_DIV: + s->clock_div = value; + s->last_free = 0; + s->play_pos = 0; + mv88w8xx8_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 mv88w8xx8_audio_reset(void *opaque) +{ + mv88w8xx8_audio_state *s = opaque; + + s->playback_mode = 0; + s->status = 0; + s->irq_enable = 0; +} + +static CPUReadMemoryFunc *mv88w8xx8_audio_readfn[] = { + mv88w8xx8_audio_read, + mv88w8xx8_audio_read, + mv88w8xx8_audio_read +}; + +static CPUWriteMemoryFunc *mv88w8xx8_audio_writefn[] = { + mv88w8xx8_audio_write, + mv88w8xx8_audio_write, + mv88w8xx8_audio_write +}; + +static void mv88w8xx8_audio_init(SysBusDevice *dev) +{ + mv88w8xx8_audio_state *s = FROM_SYSBUS(mv88w8xx8_audio_state, dev); + int iomemtype; + + sysbus_init_irq(dev, &s->irq); + + wm8750_data_req_set(s->wm, mv88w8xx8_audio_callback, s); + + iomemtype = cpu_register_io_memory(mv88w8xx8_audio_readfn, + mv88w8xx8_audio_writefn, s); + sysbus_init_mmio(dev, MP_AUDIO_SIZE, iomemtype); + + qemu_register_reset(mv88w8xx8_audio_reset, s); +} + +static SysBusDeviceInfo mv88w8xx8_audio_info = { + .init = mv88w8xx8_audio_init, + .qdev.name = "mv88w8xx8_audio", + .qdev.size = sizeof(mv88w8xx8_audio_state), + .qdev.props = (Property[]) { + { + .name = "wm8750", + .info = &qdev_prop_ptr, + .offset = offsetof(mv88w8xx8_audio_state, wm), + }, + {/* end of list */} + } +}; +#endif + +static void mv88w8xx8_register_devices(void) +{ +#ifdef HAS_AUDIO + sysbus_register_withprop(&mv88w8xx8_audio_info); +#endif +} + +device_init(mv88w8xx8_register_devices)