[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH RFC] sparc32: add dbri audio device
From: |
Blue Swirl |
Subject: |
Re: [Qemu-devel] [PATCH RFC] sparc32: add dbri audio device |
Date: |
Fri, 12 Aug 2011 21:17:45 +0000 |
On Fri, Aug 12, 2011 at 3:57 PM, Bob Breuer <address@hidden> wrote:
> Here's a first look at adding the dbri audio device for sparc32.
> For now, this is only usable with the SS-20 OBP rom, but I'm
> looking at adding the slot probing to OpenBIOS to make it work
> there. It also needs to be adapted to the new memory api. If
> a bus for sbus was created, it should become possible to plug
> this into and have it work with any of the sparc32 machines.
>
> Only audio output is supported. Tested with Debian 4.0 guest.
Nice work, this is already much better than cs4231. I have a few minor
comments below.
> diff --git a/Makefile.target b/Makefile.target
> index 096214a..680106f 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -328,6 +328,7 @@ else
> obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
> obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
> obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
> +obj-sparc-y += dbri.o
>
> # GRLIB
> obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
> diff --git a/hw/dbri.c b/hw/dbri.c
> new file mode 100644
> index 0000000..46f21e4
> --- /dev/null
> +++ b/hw/dbri.c
> @@ -0,0 +1,1342 @@
> +/*
> + * QEMU DBRI audio interface
> + *
> + * Copyright (c) 2011 Bob Breuer
> + *
> + * 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.h"
> +#include "audio/audio.h"
> +#include "sysbus.h"
> +#include "sun4m.h"
> +
> +/*
> + * DBRI (Dual Basic Rate ISDN)
> + * interface with the audio codec (CS4215) in several SPARCstation models
> + * SS20 (internal codec), SS10 (external codec), LX, ...
> + *
> + * Documentation at: http://www.freesoft.org/Linux/DBRI/
> + * Linux 2.6 driver: sound/sparc/dbri.[ch]
> + * NetBSD driver: src/sys/dev/sbus/dbri*
> + *
> + * Unimplemented:
> + * volume control
> + * audio record - alaw, ulaw for record?
> + * isdn
> + * iommu errors (sbus faults)
> + * monitor pipes (for 8-bit stereo?)
> + */
> +
> +//#define DEBUG_DBRI
> +//#define DEBUG_PIPE
> +//#define DEBUG_DMA
> +//#define DEBUG_CODEC
> +
> +#ifdef DEBUG_DBRI
> +#define DBRI_DPRINTF(fmt, ...) \
> + printf("DBRI: " fmt , ## __VA_ARGS__)
> +#else
> +#define DBRI_DPRINTF(fmt, ...)
> +#endif
> +
> +#ifdef DEBUG_PIPE
> +#define PIPE_DPRINTF(fmt, ...) \
> + printf("DBRI Pipe: " fmt , ## __VA_ARGS__)
> +#else
> +#define PIPE_DPRINTF(fmt, ...)
> +#endif
> +
> +#ifdef DEBUG_DMA
> +#define DMA_DPRINTF(fmt, ...) \
> + printf("DBRI Dma: " fmt , ## __VA_ARGS__)
> +#else
> +#define DMA_DPRINTF(fmt, ...)
> +#endif
> +
> +#ifdef DEBUG_CODEC
> +#define CODEC_DPRINTF(fmt, ...) \
> + printf("CS4215: " fmt , ## __VA_ARGS__)
> +#else
> +#define CODEC_DPRINTF(fmt, ...)
> +#endif
Tracepoints are preferred these days over debugging printfs. That can
be done later.
> +
> +
> +typedef struct CS4215State {
> + int data_mode;
> + int freq;
> + const int16_t *tbl;
> +
> + uint8_t status;
> + uint8_t data_format;
> + uint8_t port_control;
> + uint8_t settings[4];
> +} CS4215State;
> +
> +typedef struct DBRIPipeState {
> + uint32_t setup, ptr, data;
> + uint32_t in_desc, out_desc;
> + uint32_t in_next, out_next;
> +} DBRIPipeState;
> +
> +typedef struct DBRIState {
> + SysBusDevice busdev;
> + QEMUSoundCard card;
> + SWVoiceOut *voice_out;
> +
> + qemu_irq irq;
> + void *iommu;
> +
> + uint32_t pio_default;
> + int32_t codec_offset;
> +
> + uint32_t reg[2];
> + uint32_t pio;
> + uint32_t cmdq_ptr; /* REG8 */
> + uint32_t intq_ptr; /* REG9 */
> + uint32_t intq_idx;
> + uint32_t chi_global_mode;
> + uint32_t chi_data_mode;
> +
> + bool pipe_update;
> + bool chi_active;
> +
> + struct {
> + uint32_t pipe, ctrl;
> + int offset;
> + bool stopped;
> + } play, rec;
> +
> + DBRIPipeState pipe[32];
> +
> + CS4215State codec;
> +} DBRIState;
> +
> +
> +#define DBRI_ROM_SIZE 0x30
> +#define DBRI_REG_SIZE 0x100
> +#define DBRI_REG_OFFSET 0x10000
> +
> +/* bits in reg0 (status/control */
> +#define DBRI_COMMAND_VALID (1 << 15)
> +#define DBRI_CHI_ACTIVATE (1 << 4)
> +#define DBRI_SOFT_RESET (1 << 0)
> +
> +/* reg1 = interrupt status */
> +#define DBRI_INT_STATUS (1 << 0)
> +/* reg2 = PIO (Parallel I/O)
> + * 4 bits of I/O: high nibble=enable, low nibble = value
> + * PIO0: 1=internal codec
> + * PIO1: 0=codec reset default = low
> + * PIO2: 1=external speakerbox
> + * PIO3: codec D/C, 1=data, 0=control default = high
> + *
> + * 0x09 = SS-20 internal, codec offset 8
> + * 0x0c = speakerbox?, codec offset 0
> + */
> +#define DBRI_PIO_EN 0xf0
> +#define DBRI_PIO3 0x08
> +#define DBRI_PIO2 0x04
> +#define DBRI_PIO1 0x02
> +#define DBRI_PIO0 0x01
> +#define DBRI_PIO_DEFAULT_INTERNAL (DBRI_PIO3 | DBRI_PIO0)
> +#define DBRI_PIO_DEFAULT_EXTERNAL (DBRI_PIO3 | DBRI_PIO2)
> +#define DBRI_CODEC_RESET(s) (((s)->pio & 0x22) != 0x22)
> +#define DBRI_CODEC_DATA_MODE(s) (((s)->pio & 0x88) != 0x80)
> +
> +/* interrupt codes */
> +#define INTR_BRDY 1 /* Receive buffer ready */
> +#define INTR_MINT 2 /* Marker interrupt in TD/RD */
> +#define INTR_EOL 5 /* End of list */
> +#define INTR_CMDI 6 /* Command has been read */
> +#define INTR_XCMP 8 /* Transmit complete */
> +#define INTR_FXDT 10 /* Fixed data change */
> +#define INTR_CHIL 11 /* CHI lost frame */
> +
> +/* ctrl is from the TX/RX descriptors */
> +#define DBRI_TXBUF_LEN(s) (((s)->play.ctrl >> 16) & 0x1fff)
> +#define DBRI_RXBUF_LEN(s) ((s)->rec.ctrl & 0x1fff)
> +
> +/* pipe setup from SDP command */
> +#define SDP_CHANGE (2 << 18) /* report any changes */
> +#define SDP_EOL (1 << 17)
> +#define SDP_MODE_MASK (7 << 13)
> +#define SDP_MODE_FIXED (6 << 13)
> +#define SDP_MODE_MEM (0 << 13)
> +#define SDP_DIR_OUT (1 << 12) /* direction */
> +#define SDP_MSB (1 << 11) /* bit order within byte */
> +#define SDP_PTR_VALID (1 << 10)
> +#define SDP_ABORT (1 << 8)
> +#define SDP_CLEAR (1 << 7)
> +
> +#define SDP_MODE(setup) ((setup) & SDP_MODE_MASK)
> +#define SDP_REPORT_CHANGE(setup) ((setup) & SDP_CHANGE)
> +
> +/* time slot defined by DTS command */
> +#define DTS_VIN (1 << 17) /* valid in */
> +#define DTS_VOUT (1 << 16) /* valid out */
> +#define DTS_INSERT (1 << 15) /* 1=insert, 0=delete */
> +#define DTS_PIPE_IN_PREV(dts) (((dts) >> 10) & 0x1f)
> +#define DTS_PIPE_OUT_PREV(dts) (((dts) >> 5) & 0x1f)
> +/* timeslot fields for in and out slots */
> +#define SLOT_LEN(ts) (((ts) >> 24) & 0xff)
> +#define SLOT_START(ts) (((ts) >> 14) & 0x3ff)
> +#define SLOT_MODE(ts) (((ts) >> 10) & 0x7)
> +#define SLOT_MON(ts) (((ts) >> 5) & 0x1f)
> +#define SLOT_NEXT(ts) ((ts) & 0x1f)
> +
> +/* CHI global mode from CHI command */
> +#define DBRI_CHI_CLOCK(s) (((s)->chi_global_mode >> 16) & 0xff)
> +#define DBRI_CHI_IS_MASTER(s) (DBRI_CHI_CLOCK(s) >= 3)
> +
> +
> +/* audio codec */
> +/* control slot 1 = status */
> +#define CS4215_CLB (1 << 2) /* control latch bit */
> +#define CS4215_STATUS_RWMASK 0x1f
> +#define CS4215_STATUS_FIXED 0x20 /* upper 3 bits fixed at 001 */
> +#define CS4215_CLB_CLEAR(st) (((st) & 0x1b) | 0x20)
> +
> +/* control slot 2 = data format */
> +#define CS4215_DF_MASK (3 << 0) /* data format */
> +#define CS4215_DF_S16 (0 << 0)
> +#define CS4215_DF_ULAW (1 << 0)
> +#define CS4215_DF_ALAW (2 << 0)
> +#define CS4215_DF_U8 (3 << 0)
> +#define CS4215_ST (1 << 2) /* stereo */
> +#define CS4215_DFR_MASK (7 << 3) /* data frequency rate */
> +#define CS4215_DFR_EXTRACT(d) (((d) & CS4215_DFR_MASK) >> 3)
> +
> +/* control slot 3 = port control */
> +#define CS4215_XEN (1 << 0)
> +#define CS4215_XCLK (1 << 1) /* transmit clock master mode */
> +#define CS4215_BSEL_MASK (3 << 2)
> +#define CS4215_BSEL_256 (2 << 2)
> +#define CS4215_MCK_MASK (7 << 4) /* clock source select */
> +#define CS4215_MCK_XTAL1 (1 << 4)
> +#define CS4215_MCK_XTAL2 (2 << 4)
Enums are more friedly to GDB, but it's OK to leave them as #defines.
> +
> +#define CODEC_IS_MASTER(c) \
> + ((c)->data_mode && ((c)->port_control & CS4215_XCLK))
> +
> +/* 2 clocks, 8 clock dividers */
> +#define AUD_CLK1 24576000
> +#define AUD_CLK2 16934400
> +
> +static const int cs4215_clk_div[8] = {
> + 3072, 1536, 896, 768, 448, 384, 512, 2560
> +};
> +
> +/* MuLaw/ALaw tables are also in cs4231a.c, move to common code? */
> +/* Tables courtesy
> http://hazelware.luggle.com/tutorials/mulawcompression.html */
> +static const int16_t MuLawDecompressTable[256] =
> +{
> + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
> + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
> + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
> + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
> + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
> + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
> + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
> + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
> + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
> + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
> + -876, -844, -812, -780, -748, -716, -684, -652,
> + -620, -588, -556, -524, -492, -460, -428, -396,
> + -372, -356, -340, -324, -308, -292, -276, -260,
> + -244, -228, -212, -196, -180, -164, -148, -132,
> + -120, -112, -104, -96, -88, -80, -72, -64,
> + -56, -48, -40, -32, -24, -16, -8, 0,
> + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
> + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
> + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
> + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
> + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
> + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
> + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
> + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
> + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
> + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
> + 876, 844, 812, 780, 748, 716, 684, 652,
> + 620, 588, 556, 524, 492, 460, 428, 396,
> + 372, 356, 340, 324, 308, 292, 276, 260,
> + 244, 228, 212, 196, 180, 164, 148, 132,
> + 120, 112, 104, 96, 88, 80, 72, 64,
> + 56, 48, 40, 32, 24, 16, 8, 0
> +};
> +
> +static const int16_t ALawDecompressTable[256] =
> +{
> + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
> + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
> + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
> + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
> + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
> + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
> + -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
> + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
> + -344, -328, -376, -360, -280, -264, -312, -296,
> + -472, -456, -504, -488, -408, -392, -440, -424,
> + -88, -72, -120, -104, -24, -8, -56, -40,
> + -216, -200, -248, -232, -152, -136, -184, -168,
> + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
> + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
> + -688, -656, -752, -720, -560, -528, -624, -592,
> + -944, -912, -1008, -976, -816, -784, -880, -848,
> + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
> + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
> + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
> + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
> + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
> + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
> + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
> + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
> + 344, 328, 376, 360, 280, 264, 312, 296,
> + 472, 456, 504, 488, 408, 392, 440, 424,
> + 88, 72, 120, 104, 24, 8, 56, 40,
> + 216, 200, 248, 232, 152, 136, 184, 168,
> + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
> + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
> + 688, 656, 752, 720, 560, 528, 624, 592,
> + 944, 912, 1008, 976, 816, 784, 880, 848
> +};
> +
> +/* reverse bits in a byte */
> +static uint8_t byte_rev(uint8_t b)
> +{
> + b = ((b & 0xf0) >> 4) | ((b & 0x0f) << 4);
> + b = ((b & 0xcc) >> 2) | ((b & 0x33) << 2);
> + b = ((b & 0xaa) >> 1) | ((b & 0x55) << 1);
> +
> + return b;
> +}
> +
> +static void cs4215_reset(CS4215State *s)
> +{
> + s->status = CS4215_STATUS_FIXED | CS4215_CLB;
> + s->data_format = CS4215_DF_ULAW;
> + s->port_control = CS4215_BSEL_256 | CS4215_XEN;
> + s->settings[0] = 0x3f;
> + s->settings[1] = 0xbf;
> + s->settings[2] = 0xc0;
> + s->settings[3] = 0xf0;
> +}
> +
> +static void cs4215_setmode(CS4215State *s, int mode)
> +{
> + int div;
> +
> + s->data_mode = mode;
> +
> + if (mode) {
> + /* calculate frequency */
> + div = cs4215_clk_div[CS4215_DFR_EXTRACT(s->data_format)];
> +
> + switch (s->port_control & CS4215_MCK_MASK) {
> + case CS4215_MCK_XTAL1:
> + s->freq = AUD_CLK1 / div;
> + break;
> + case CS4215_MCK_XTAL2:
> + s->freq = AUD_CLK2 / div;
> + break;
> + default:
> + s->freq = 0;
> + break;
> + }
> + } else {
> + s->freq = 0;
> + s->status = CS4215_CLB_CLEAR(s->status);
> + }
> +}
> +
> +static void cs4215_getformat(CS4215State *s, struct audsettings *as)
> +{
> + as->freq = s->freq;
> + as->nchannels = (s->data_format & CS4215_ST) ? 2 : 1;
> + as->endianness = AUDIO_HOST_ENDIANNESS;
> +
> + CODEC_DPRINTF("audio format: %d Hz, %d chan, fmt %d\n",
> + as->freq, as->nchannels, s->data_format & CS4215_DF_MASK);
> +
> + s->tbl = NULL; /* conversion table */
> +
> + switch (s->data_format & CS4215_DF_MASK) {
> + case CS4215_DF_S16: /* 16-bit 2's complement */
> + as->fmt = AUD_FMT_S16;
> + as->endianness = 1; /* big-endian */
> + break;
> + case CS4215_DF_ULAW: /* 8-bit Mu-law */
> + s->tbl = MuLawDecompressTable;
> + as->fmt = AUD_FMT_S16;
> + break;
> + case CS4215_DF_ALAW: /* 8-bit A-law */
> + s->tbl = ALawDecompressTable;
> + as->fmt = AUD_FMT_S16;
> + break;
> + case CS4215_DF_U8: /* 8-bit unsigned */
> + as->fmt = AUD_FMT_U8;
> + break;
> + }
> +}
> +
> +static uint8_t cs4215_read(CS4215State *s, int slot)
> +{
> + uint8_t val;
> +
> + /* 1-based slot number */
> + if (s->data_mode) {
> + switch (slot) {
> + case 5:
> + val = s->settings[0];
> + break;
> + case 6:
> + val = s->settings[1] & 0x7f;
> + break;
> + case 7:
> + val = s->settings[2] & 0xdf;
> + break;
> + case 8:
> + val = s->settings[3];
> + break;
> + default:
> + val = 0;
> + break;
> + }
> + } else {
> + switch (slot) {
> + case 1:
> + val = s->status;
> + break;
> + case 2:
> + val = s->data_format;
> + break;
> + case 3:
> + val = s->port_control;
> + break;
> + case 4:
> + val = 0; /* test */
> + break;
> + case 5:
> + val = 0xc0; /* codec pio */
> + break;
> + case 7:
> + val = 0x02; /* Rev E */
> + break;
> + default:
> + val = 0;
> + break;
> + }
> + }
> + CODEC_DPRINTF("read 0x%02x from %s slot %d\n",
> + val, s->data_mode ? "data" : "control", slot);
> + return val;
> +}
> +
> +static void cs4215_write(CS4215State *s, int slot, uint8_t val)
> +{
> + CODEC_DPRINTF("write 0x%02x to %s slot %d\n",
> + val, s->data_mode ? "data" : "control", slot);
> +
> + if (s->data_mode) {
> + if (slot >= 5 && slot <= 8) {
> + s->settings[slot-5] = val;
Please add spaces around '-'. I'd expect scripts/checkpatch.pl to complain.
> + }
> + } else {
> + switch (slot) {
> + case 1:
> + s->status = (val & CS4215_STATUS_RWMASK) | CS4215_STATUS_FIXED;
> + break;
> + case 2:
> + s->data_format = val;
> + break;
> + case 3:
> + s->port_control = val;
> + break;
> + }
> + }
> +}
> +
> +static uint32_t dbri_dma_readl(DBRIState *s, uint32_t addr)
> +{
> + uint32_t val;
> +
> + sparc_iommu_memory_read(s->iommu, addr, (uint8_t *)&val, 4);
> + val = be32_to_cpu(val);
> + DMA_DPRINTF("readl 0x%08x\n", val);
> + return val;
> +}
> +
> +static void dbri_dma_writel(DBRIState *s, uint32_t addr, uint32_t val)
> +{
> + DMA_DPRINTF("writel 0x%08x\n", val);
> + val = cpu_to_be32(val);
> + sparc_iommu_memory_write(s->iommu, addr, (uint8_t *)&val, 4);
> +}
> +
> +#ifdef DEBUG_DBRI
> +static const char *intr_code[16] = {
> + "code 0", "BRDY", "MINT", "IBEG", "IEND", "EOL", "CMDI", "code 7",
> + "XCMP", "SBRI", "FXDT", "CHIL/COLL", "DBYT", "RBYT", "LINT", "UNDR"
> +};
> +#endif
> +
> +static void dbri_post_interrupt(DBRIState *s, int chan, int code, int field)
> +{
> + uint32_t addr, val;
> +
> + addr = s->intq_ptr;
> + if (!addr) {
> + /* disabled */
> + return;
> + }
> +
> + if (s->intq_idx == 64) {
> + /* read next interrupt block from 1st word */
> + addr = dbri_dma_readl(s, addr);
> + if (!addr) {
> + /* out of space? */
> + return;
> + }
> + s->intq_ptr = addr;
> + s->intq_idx = 1;
> + }
> +
> + field &= 0xfffff;
> + val = 0x80000000 | (chan << 24) | (code << 20) | field;
> +
> + DBRI_DPRINTF("interrupt (0x%08x) %s, chan %d, field 0x%05x\n",
> + val, intr_code[code], chan, field);
> +
> + addr += 4 * s->intq_idx;
> + s->intq_idx++;
> +
> + dbri_dma_writel(s, addr, val);
> + s->reg[1] |= DBRI_INT_STATUS;
> + qemu_irq_raise(s->irq);
> +}
> +
> +static void dbri_out_cb(void *opaque, int free)
> +{
> + DBRIState *s = opaque;
> + int pipe = s->play.pipe;
> + int left, used;
> + int len, buf_len;
> + uint32_t buf_ptr;
> + uint8_t tmpbuf[1024];
> + int16_t linbuf[1024];
> +
> + if (!pipe || s->play.stopped) {
> + AUD_set_active_out(s->voice_out, 0);
> + return;
> + }
> +
> + used = 0;
> + while (free) {
> + buf_len = DBRI_TXBUF_LEN(s);
> + left = buf_len - s->play.offset;
> +
> + /* end of buffer? */
> + if (left <= 0) {
> + uint32_t td_ptr = s->pipe[pipe].ptr;
> +
> + if (!td_ptr) {
> + /* bad descriptor pointer - flag a fault? */
> + s->play.stopped = 1;
> + break;
> + }
> +
> + if (s->pipe[pipe].data) {
> + /* clear our data pointer to flag buffer done */
> + s->pipe[pipe].data = 0;
> +
> + /* update status */
> + dbri_dma_writel(s, td_ptr+12, 0x01);
> + if ((s->play.ctrl & 0x80008000) == 0x80008000) {
> + /* transmit complete */
> + dbri_post_interrupt(s, pipe, INTR_XCMP, 0);
> + }
> + }
> + /* next descriptor */
> + td_ptr = dbri_dma_readl(s, td_ptr+8);
> + if (!td_ptr) {
> + DBRI_DPRINTF("out of tx data after %d bytes\n", used);
> + if (used) {
> + /*
> + * maybe the host buffers are too big,
> + * give the guest a chance to catch up
> + */
> + break;
> + }
> + if (s->pipe[pipe].setup & SDP_EOL) {
> + /* end of list */
> + dbri_post_interrupt(s, pipe, INTR_EOL, td_ptr);
> + }
> + s->play.stopped = 1;
> + break;
> + }
> + s->pipe[pipe].ptr = td_ptr;
> + s->play.ctrl = dbri_dma_readl(s, td_ptr);
> + if (s->play.ctrl & 0x4000) {
> + /* marker int */
> + dbri_post_interrupt(s, pipe, INTR_MINT, td_ptr);
> + }
> + /* setup next buffer */
> + s->pipe[pipe].data = dbri_dma_readl(s, td_ptr+4);
> + s->play.offset = 0;
> + if (!s->pipe[pipe].data) {
> + /* NULL data pointer, what now? */
> + }
> + left = buf_len = DBRI_TXBUF_LEN(s);
> +
> + DBRI_DPRINTF("play buffer @ 0x%08x, len %d\n",
> + s->pipe[pipe].data, buf_len);
> + }
> +
> + if (!s->pipe[pipe].data) {
> + /* bad data pointer - flag a fault? */
> + s->play.stopped = 1;
> + break;
> + }
> + buf_ptr = s->pipe[pipe].data + s->play.offset;
> + len = audio_MIN(left, sizeof(tmpbuf));
> +
> + if (s->codec.tbl) {
> + /* convert from A/Mu-Law to 16-bit linear */
> + const int16_t *tbl;
> + int i;
> +
> + len = audio_MIN(len, free >> 1);
> +
> + sparc_iommu_memory_read(s->iommu, buf_ptr, tmpbuf, len);
> +
> + tbl = s->codec.tbl;
> + for (i = 0; i < len; i++) {
> + linbuf[i] = tbl[tmpbuf[i]];
> + }
> + len = AUD_write(s->voice_out, linbuf, len << 1);
> + len >>= 1;
> + } else {
> + len = audio_MIN(len, free);
> +
> + sparc_iommu_memory_read(s->iommu, buf_ptr, tmpbuf, len);
> +
> + len = AUD_write(s->voice_out, tmpbuf, len);
> + }
> +
> + if (!len) {
> + break;
> + }
> +
> + s->play.offset += len;
> + free -= len;
> + used += len;
> + }
> +}
> +
> +static void dbri_update_audio(DBRIState *s)
> +{
> + struct audsettings as;
> +
> + cs4215_getformat(&s->codec, &as);
> + s->voice_out = AUD_open_out(&s->card, s->voice_out,
> + "dbri_out", s, dbri_out_cb, &as);
> +
> + /* zero is not a valid pipe */
> + if (s->play.pipe && !s->play.stopped) {
> + AUD_set_active_out(s->voice_out, 1);
> + } else {
> + AUD_set_active_out(s->voice_out, 0);
> + }
> +}
> +
> +static void dbri_start_audio_out(DBRIState *s, int pipe)
> +{
> + uint32_t td_ptr; /* transmit descriptor */
> +
> + if (pipe == s->play.pipe && !s->play.stopped) {
> + return;
> + }
> + s->play.pipe = pipe;
> +
> + /* ptr was validated by caller */
> + td_ptr = s->pipe[pipe].ptr;
> +
> + s->play.ctrl = dbri_dma_readl(s, td_ptr); /* control/length */
> + if (s->play.ctrl & 0x4000) {
> + /* marker int */
> + dbri_post_interrupt(s, pipe, INTR_MINT, td_ptr);
> + }
> + /* get data pointer, re-use pipe data field */
> + s->pipe[pipe].data = dbri_dma_readl(s, td_ptr+4);
> +
> + /* prepare buffer */
> + s->play.offset = 0;
> + s->play.stopped = 0;
> +
> + DBRI_DPRINTF("play buffer @ 0x%08x, len %d\n",
> + s->pipe[pipe].data, DBRI_TXBUF_LEN(s));
> +
> + dbri_update_audio(s);
> +}
> +
> +static void dbri_start_audio_in(DBRIState *s, int pipe)
> +{
> + uint32_t rd_ptr; /* receive descriptor */
> +
> + if (pipe == s->rec.pipe && !s->rec.stopped) {
> + return;
> + }
> + s->rec.pipe = pipe;
> +
> + /* setup format */
> +
> + rd_ptr = s->pipe[pipe].ptr;
> +
> + s->rec.ctrl = dbri_dma_readl(s, rd_ptr+12); /* control/length */
> + if (s->rec.ctrl & 0x4000) {
> + /* marker int */
> + dbri_post_interrupt(s, pipe, INTR_MINT, rd_ptr);
> + }
> + /* get data pointer, re-use pipe data field */
> + s->pipe[pipe].data = dbri_dma_readl(s, rd_ptr+4);
> +
> + /* prepare buffer */
> + s->rec.offset = 0;
> + s->rec.stopped = 0;
> +
> + /* TODO: audio record, this is just a placeholder */
> + if (0) {
> + /* write buffer */
> + uint32_t len = DBRI_RXBUF_LEN(s);
> +
> + /* update status - completed and EOF */
> + dbri_dma_writel(s, rd_ptr, 0xc0000000 | (len << 16));
> + if (s->rec.ctrl & 0x8000) {
> + /* buffer ready */
> + dbri_post_interrupt(s, pipe, INTR_BRDY, rd_ptr);
> + }
> + /* next */
> + rd_ptr = dbri_dma_readl(s, rd_ptr+8);
> + s->pipe[pipe].ptr = rd_ptr;
> + }
> + if (s->pipe[pipe].setup & SDP_EOL) {
> + /* end of list */
> + dbri_post_interrupt(s, pipe, INTR_EOL, rd_ptr);
> + }
> + s->rec.stopped = 1;
> + dbri_update_audio(s);
> +}
> +
> +static void dbri_stop_audio(DBRIState *s, int pipe, int abort)
> +{
> + if (pipe == s->play.pipe) {
> + if (abort && s->pipe[pipe].ptr) {
> + /* abort */
> + dbri_dma_writel(s, s->pipe[pipe].ptr+12, 0x04);
> + AUD_set_active_out(s->voice_out, 0);
> + }
> + s->play.stopped = 1;
> + s->play.pipe = 0;
> + }
> + if (pipe == s->rec.pipe) {
> + if (abort && s->pipe[pipe].ptr) {
> + /* abort */
> + dbri_dma_writel(s, s->pipe[pipe].ptr, 0x20);
> + }
> + s->rec.stopped = 1;
> + s->rec.pipe = 0;
> + }
> +}
> +
> +static void dbri_update_chi_status(DBRIState *s)
> +{
> + if ((s->reg[0] & DBRI_CHI_ACTIVATE)
> + && (DBRI_CHI_IS_MASTER(s) || CODEC_IS_MASTER(&s->codec))) {
> + s->chi_active = 1;
> + } else {
> + s->chi_active = 0;
> + }
> +}
> +
> +static void dbri_run_pipes(DBRIState *s)
> +{
> + int i, start, len, codec_slot;
> + uint32_t val;
> + uint32_t been_here;
> +
> + if (!s->chi_active) {
> + return; /* CHI not active */
> + }
> +
> + /* run through the pipes */
> + s->pipe_update = 0;
> +
> + /* pipe 16 is the anchor where the CHI starts and ends */
> + if (s->chi_data_mode & 0x02) {
> + i = s->pipe[16].out_next;
> + been_here = 1 << 16;
> + PIPE_DPRINTF("OUT pipes:\n");
> + while (i != 16) {
> + if (been_here & (1 << i)) {
> + PIPE_DPRINTF("linked list loop before OUT anchor\n");
> + break;
> + }
> + been_here |= 1 << i;
> + start = SLOT_START(s->pipe[i].out_desc);
> + len = SLOT_LEN(s->pipe[i].out_desc);
> +
> + /* use bits per frame instead of 0 for OUT pipes */
> + if (start >= 64) {
> + start = 0;
> + }
> + PIPE_DPRINTF(" %d, 0x%08x = start %d, len %d\n",
> + i, s->pipe[i].out_desc, start, len);
> +
> + switch (SDP_MODE(s->pipe[i].setup)) {
> + case SDP_MODE_MEM:
> + PIPE_DPRINTF(" TD @ 0x%08x\n", s->pipe[i].ptr);
> +
> + if (s->pipe[i].ptr && start == s->codec_offset) {
> + dbri_start_audio_out(s, i);
> + }
> + break;
> +
> + case SDP_MODE_FIXED:
> + PIPE_DPRINTF(" Fixed 0x%08x\n", s->pipe[i].data);
> +
> + /* assume 8-bit alignment */
> + codec_slot = 1 + (start - s->codec_offset) / 8;
> + /* fixed pipe is LSB first, codec is MSB first */
> + val = s->pipe[i].data;
> + do {
> + cs4215_write(&s->codec, codec_slot, byte_rev(val &
> 0xff));
> + codec_slot++;
> + val >>= 8;
> + len -= 8;
> + } while (len > 0);
> + break;
> + }
> + i = s->pipe[i].out_next;
> + }
> + }
> +
> + i = s->pipe[16].in_next;
> + been_here = 1 << 16;
> + PIPE_DPRINTF("IN pipes:\n");
> + while (i != 16) {
> + if (been_here & (1 << i)) {
> + PIPE_DPRINTF("linked list loop before IN anchor\n");
> + break;
> + }
> + been_here |= 1 << i;
> + start = SLOT_START(s->pipe[i].in_desc);
> + len = SLOT_LEN(s->pipe[i].in_desc);
> +
> + PIPE_DPRINTF(" %d, 0x%08x = start %d, len %d\n",
> + i, s->pipe[i].in_desc, start, len);
> +
> + switch (SDP_MODE(s->pipe[i].setup)) {
> + case SDP_MODE_MEM:
> + PIPE_DPRINTF(" RD @ 0x%08x\n", s->pipe[i].ptr);
> +
> + if (s->pipe[i].ptr && start == s->codec_offset) {
> + dbri_start_audio_in(s, i);
> + }
> + break;
> +
> + case SDP_MODE_FIXED:
> + /* assume 8-bit alignment */
> + codec_slot = 1 + (start - s->codec_offset) / 8;
> + /* fixed pipe is LSB first, codec is MSB first */
> + val = byte_rev(cs4215_read(&s->codec, codec_slot));
> + if (len > 8) {
> + val |= byte_rev(cs4215_read(&s->codec, codec_slot+1)) << 8;
> + }
> + if (s->pipe[i].data != val) {
> + s->pipe[i].data = val;
> + if (SDP_REPORT_CHANGE(s->pipe[i].setup)) {
> + dbri_post_interrupt(s, i, INTR_FXDT, val);
> + }
> + }
> + PIPE_DPRINTF(" Fixed 0x%08x\n", s->pipe[i].data);
> + break;
> + }
> + i = s->pipe[i].in_next;
> + }
> +}
> +
> +static void dbri_cmd_pause(DBRIState *s, uint32_t *cmd)
> +{
> + dbri_run_pipes(s);
> +}
> +
> +static void dbri_cmd_jump(DBRIState *s, uint32_t *cmd)
> +{
> + s->cmdq_ptr = cmd[1];
> +}
> +
> +/* initialize interrupt queue */
> +static void dbri_cmd_iiq(DBRIState *s, uint32_t *cmd)
> +{
> + s->intq_ptr = cmd[1];
> + s->intq_idx = 1;
> +}
> +
> +/* setup data pipe, set data pointer */
> +static void dbri_cmd_sdp(DBRIState *s, uint32_t *cmd)
> +{
> + int i = cmd[0] & 0x1f;
> +
> + PIPE_DPRINTF("Setup pipe %d for %s: mode %d, IRM 0x%x, clear %d\n",
> + i, (cmd[0] & SDP_DIR_OUT) ? "output" : "input",
> + (cmd[0] >> 13) & 7, (cmd[0] >> 16) & 0xf,
> + (cmd[0] >> 7) & 1);
> +
> + s->pipe[i].setup = cmd[0];
> + if (cmd[0] & (SDP_PTR_VALID | SDP_CLEAR | SDP_ABORT)) {
> + /* stop any audio on this pipe */
> + if (i) {
> + dbri_stop_audio(s, i, cmd[0] & SDP_ABORT);
> + }
> + }
> + if (cmd[0] & SDP_PTR_VALID) {
> + PIPE_DPRINTF(" pipe pointer = 0x%08x\n", cmd[1]);
> + s->pipe[i].ptr = cmd[1];
> + }
> +}
> +
> +/* continue data pipe */
> +static void dbri_cmd_cdp(DBRIState *s, uint32_t *cmd)
> +{
> + int i = cmd[0] & 0x1f;
> +
> + if (i == s->play.pipe) {
> + s->play.stopped = 0;
> + }
> + if (i == s->rec.pipe) {
> + s->rec.stopped = 0;
> + }
> + dbri_update_audio(s);
> +}
> +
> +/* define time slot */
> +static void dbri_cmd_dts(DBRIState *s, uint32_t *cmd)
> +{
> + int prev, next;
> + int i = cmd[0] & 0x1f;
> +
> + PIPE_DPRINTF("%s time slots for pipe %d\n",
> + (cmd[0] & DTS_INSERT) ? "add/modify" : "delete", i);
> +
> + if (cmd[0] & DTS_VIN) {
> + prev = DTS_PIPE_IN_PREV(cmd[0]);
> + if (cmd[0] & DTS_INSERT) {
> + next = SLOT_NEXT(cmd[1]);
> +
> + s->pipe[i].in_next = next;
> + s->pipe[prev].in_next = i;
> + s->pipe[i].in_desc = cmd[1];
> + } else {
> + /* delete */
> + next = s->pipe[i].in_next;
> + s->pipe[prev].in_next = next;
> + }
> +
> + PIPE_DPRINTF("In: prev=%d, next=%d, mode=%d, len=%d, cycle=%d\n",
> + prev, next, SLOT_MODE(cmd[1]),
> + SLOT_LEN(cmd[1]), SLOT_START(cmd[1]));
> + }
> + if (cmd[0] & DTS_VOUT) {
> + prev = DTS_PIPE_OUT_PREV(cmd[0]);
> + if (cmd[0] & DTS_INSERT) {
> + next = SLOT_NEXT(cmd[2]);
> +
> + s->pipe[i].out_next = next;
> + s->pipe[prev].out_next = i;
> + s->pipe[i].out_desc = cmd[2];
> + } else {
> + /* delete */
> + next = s->pipe[i].out_next;
> + s->pipe[prev].out_next = next;
> + }
> +
> + PIPE_DPRINTF("Out: prev=%d, next=%d, mode=%d, len=%d, cycle=%d\n",
> + prev, next, SLOT_MODE(cmd[2]),
> + SLOT_LEN(cmd[2]), SLOT_START(cmd[2]));
> + }
> +}
> +
> +/* set short pipe data */
> +static void dbri_cmd_ssp(DBRIState *s, uint32_t *cmd)
> +{
> + unsigned int i = cmd[0] & 0x1f;
> +
> + /* short pipe only */
> + if (i > 16) {
> + s->pipe[i].data = cmd[1];
> + }
> +}
> +
> +/* set CHI global mode */
> +static void dbri_cmd_chi(DBRIState *s, uint32_t *cmd)
> +{
> + int active;
> + uint32_t status;
> +
> + active = DBRI_CHI_IS_MASTER(s) || CODEC_IS_MASTER(&s->codec);
> + s->chi_global_mode = cmd[0];
> +
> + if (s->chi_global_mode & 0x8000) {
> + /* report status */
> + status = s->chi_data_mode & 0x03;
> + if (!active) {
> + status |= 0x04;
> + }
> + dbri_post_interrupt(s, 36, INTR_CHIL, status);
> + }
> + dbri_update_chi_status(s);
> +}
> +
> +/* set CHI data mode */
> +static void dbri_cmd_cdm(DBRIState *s, uint32_t *cmd)
> +{
> + s->chi_data_mode = cmd[0];
> +}
> +
> +static const struct {
> + const char *name;
> + int len;
> + void(*action)(DBRIState *, uint32_t *);
> +} command_list[16] = {
> + { "WAIT", 0, NULL },
> + { "PAUSE", 4, dbri_cmd_pause },
> + { "JUMP", 4, dbri_cmd_jump },
> + { "IIQ", 8, dbri_cmd_iiq }, /* Initialize Interrupt Queue */
> + { "REX", 4, NULL }, /* Report command EXecution */
> + { "SDP", 8, dbri_cmd_sdp }, /* Setup Data Pipe */
> + { "CDP", 4, dbri_cmd_cdp }, /* Continue Data Pipe */
> + { "DTS", 12, dbri_cmd_dts }, /* Define Time Slot */
> + { "SSP", 8, dbri_cmd_ssp }, /* Set Short Pipe */
> + { "CHI", 4, dbri_cmd_chi }, /* Set CHI Global Mode */
> + { "NT", 4, NULL },
> + { "TE", 4, NULL },
> + { "CDEC", 4, NULL }, /* Codec Setup */
> + { "TEST", 12, NULL },
> + { "CDM", 4, dbri_cmd_cdm }, /* Set CHI Data Mode */
> + { "Reserved", -1, NULL }
> +};
> +
> +static void dbri_run_commands(DBRIState *s)
> +{
> + uint32_t cmd[3];
> + uint32_t val;
> + int stopped, i;
> +
> + for (stopped = 0; !stopped; ) {
> + cmd[0] = dbri_dma_readl(s, s->cmdq_ptr);
> + i = cmd[0] >> 28;
> +
> + /* interrupt on command? */
> + if (cmd[0] & (1<<27)) {
> + val = (i << 16) | (cmd[0] & 0xffff);
> + dbri_post_interrupt(s, 38, INTR_CMDI, val);
> + }
> +
> + DBRI_DPRINTF("cmd %s = 0x%08x\n", command_list[i].name, cmd[0]);
> +
> + switch (command_list[i].len) {
> + case 12:
> + cmd[2] = dbri_dma_readl(s, s->cmdq_ptr+8);
Here...
> + case 8:
> + cmd[1] = dbri_dma_readl(s, s->cmdq_ptr+4);
... and here, either a 'break' or a comment about fall through is missing.
> + case 4:
> + s->cmdq_ptr += command_list[i].len;
> + s->pipe_update = 1;
> + if (command_list[i].action) {
> + command_list[i].action(s, cmd);
> + }
> + break;
> + case 0:
> + default:
> + stopped = 1;
> + s->reg[0] &= ~DBRI_COMMAND_VALID;
> + if (s->pipe_update) {
> + dbri_run_pipes(s);
> + }
> + break;
> + }
> + }
> +}
> +
> +static void dbri_reset(DeviceState *dev)
> +{
> + DBRIState *s = container_of(dev, DBRIState, busdev.qdev);
> + int i;
> +
> + AUD_set_active_out(s->voice_out, 0);
> + qemu_irq_lower(s->irq);
> +
> + /* set defaults */
> + s->reg[0] = 0x4008;
> + s->reg[1] = 0;
> + if (s->codec_offset) {
> + s->pio_default = DBRI_PIO_DEFAULT_INTERNAL;
> + } else {
> + s->pio_default = DBRI_PIO_DEFAULT_EXTERNAL;
> + }
> + s->pio = s->pio_default;
> + /* reset pointers */
> + s->cmdq_ptr = 0;
> + s->intq_ptr = 0;
> +
> + s->chi_global_mode = 0;
> + s->chi_data_mode = 0;
> + s->chi_active = 0;
> +
> + /* reset linked list */
> + s->pipe[16].in_next = 16;
> + s->pipe[16].out_next = 16;
> +
> + /* clear all pipes */
> + s->pipe_update = 0;
> + for (i = 0; i < 32; i++) {
> + s->pipe[i].setup = 0;
> + s->pipe[i].ptr = 0;
> + s->pipe[i].data = 0;
> + }
> + s->play.pipe = 0;
> + s->rec.pipe = 0;
> + s->play.stopped = 1;
> + s->rec.stopped = 1;
> +
> + cs4215_reset(&s->codec);
> + cs4215_setmode(&s->codec, DBRI_CODEC_DATA_MODE(s));
> +}
> +
> +/* registers */
> +static uint32_t dbri_reg_readl(void *opaque, target_phys_addr_t addr)
> +{
> + DBRIState *s = opaque;
> + int val;
> +
> + switch (addr) {
> + case 0x00: /* Status and Control */
> + val = s->reg[0];
> + break;
> + case 0x04: /* Mode and Interrupt */
> + val = s->reg[1];
> + if (val) {
> + /* clear interrupt status */
> + s->reg[1] = 0;
> + qemu_irq_lower(s->irq);
> + }
> + break;
> + case 0x08: /* I/O */
> + val = s->pio;
> + break;
> + case 0x20: /* Command Queue Pointer */
> + val = s->cmdq_ptr;
> + break;
> + case 0x24: /* Interrupt Queue Pointer */
> + val = s->intq_ptr;
> + break;
> + default:
> + val = 0;
> + break;
> + }
> +
> + DBRI_DPRINTF("readl 0x%08x from reg " TARGET_FMT_plx "\n", val, addr);
> +
> + return val;
> +}
> +
> +static void dbri_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t
> val)
> +{
> + DBRIState *s = opaque;
> +
> + DBRI_DPRINTF("writel 0x%08x to reg " TARGET_FMT_plx "\n", val, addr);
> +
> + switch (addr) {
> + case 0x00: /* Status and Control */
> + s->reg[0] = val;
> + if (val & DBRI_SOFT_RESET) {
> + dbri_reset(&s->busdev.qdev);
> + } else {
> + dbri_update_chi_status(s);
> + if (val & (DBRI_COMMAND_VALID | DBRI_CHI_ACTIVATE)) {
> + dbri_run_commands(s);
> + }
> + }
> + break;
> + case 0x08: /* I/O */
> + s->pio = (val & DBRI_PIO_EN) ? val : s->pio_default;
> + if (DBRI_CODEC_RESET(s)) {
> + cs4215_reset(&s->codec);
> + }
> + cs4215_setmode(&s->codec, DBRI_CODEC_DATA_MODE(s));
> + dbri_update_chi_status(s);
> + break;
> + case 0x20: /* Command Queue Pointer */
> + s->cmdq_ptr = val;
> + s->reg[0] |= DBRI_COMMAND_VALID;
> + dbri_run_commands(s);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static CPUReadMemoryFunc * const dbri_reg_read[3] = {
> + NULL,
> + NULL,
> + dbri_reg_readl,
> +};
> +
> +static CPUWriteMemoryFunc * const dbri_reg_write[3] = {
> + NULL,
> + NULL,
> + dbri_reg_writel,
> +};
> +
> +static int vmstate_dbri_post_load(void *opaque, int version_id)
> +{
> + DBRIState *s = opaque;
> +
> + cs4215_setmode(&s->codec, DBRI_CODEC_DATA_MODE(s));
> + dbri_update_chi_status(s);
> +
> + if (s->intq_ptr && s->reg[1]) {
> + qemu_irq_raise(s->irq);
> + }
> +
> + /* resume playback */
> + dbri_update_audio(s);
> +
> + return 0;
> +}
> +
> +
> +/* the FCode rom */
I think the FCode sources are not very big, so they should be included
here as comment.
> +static const uint8_t dbri_rom[DBRI_ROM_SIZE] = {
> + 0xfd, 0x00, 0x09, 0xea, 0x00, 0x00, 0x00, 0x30, 0x12, 0x0a,
> + 'S', 'U', 'N', 'W', ',', 'D', 'B', 'R', 'I', 'e',
> + 0x01, 0x14, 0x12, 0x04, 'n', 'a', 'm', 'e', 0x01, 0x10,
> + 0x01, 0x02, 0xa5, 0xa6, 0x7d, 0x1e, 0x01, 0x03, 0xa6, 0x80,
> + 0x01, 0x16, 0xa8, 0x63, 0xa5, 0x01, 0x17, 0x00
> +};
> +
> +/* everything is an offset within our sbus slot */
> +static void dbri_sbus_map(SysBusDevice *dev, target_phys_addr_t base)
> +{
> + DBRIState *s = FROM_SYSBUS(DBRIState, dev);
> + int rom, regs;
> +
> + rom = qemu_ram_alloc(NULL, "dbri.rom", DBRI_ROM_SIZE);
> + /* the rom is at offset 0 */
> + cpu_register_physical_memory(base, DBRI_ROM_SIZE, rom|IO_MEM_ROM);
> + cpu_physical_memory_write_rom(base, dbri_rom, DBRI_ROM_SIZE);
> +
> + /* mirror at 0x1000, where the SS-20 bootrom looks for it */
> + cpu_register_physical_memory(base+0x1000, DBRI_ROM_SIZE, rom|IO_MEM_ROM);
> +
> + regs = cpu_register_io_memory(dbri_reg_read, dbri_reg_write, s,
> + DEVICE_NATIVE_ENDIAN);
> + cpu_register_physical_memory(base+DBRI_REG_OFFSET, DBRI_REG_SIZE, regs);
> +}
> +
> +static int dbri_init1(SysBusDevice *dev)
> +{
> + DBRIState *s = FROM_SYSBUS(DBRIState, dev);
> +
> + sysbus_init_mmio_cb(dev, DBRI_REG_OFFSET+DBRI_REG_SIZE, dbri_sbus_map);
> + sysbus_init_irq(dev, &s->irq);
> +
> + AUD_register_card("sun_dbri", &s->card);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_dbri_pipe = {
> + .name = "dbri_pipe",
> + .version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(setup, DBRIPipeState),
> + VMSTATE_UINT32(ptr, DBRIPipeState),
> + VMSTATE_UINT32(data, DBRIPipeState),
> + VMSTATE_UINT32(in_desc, DBRIPipeState),
> + VMSTATE_UINT32(out_desc, DBRIPipeState),
> + VMSTATE_UINT32(in_next, DBRIPipeState),
> + VMSTATE_UINT32(out_next, DBRIPipeState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static const VMStateDescription vmstate_dbri = {
> + .name = "dbri",
> + .version_id = 1,
> + .post_load = vmstate_dbri_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(reg[0], DBRIState),
> + VMSTATE_UINT32(reg[1], DBRIState),
> + VMSTATE_UINT32(pio, DBRIState),
> + VMSTATE_UINT32(cmdq_ptr, DBRIState),
> + VMSTATE_UINT32(intq_ptr, DBRIState),
> + VMSTATE_UINT32(intq_idx, DBRIState),
> + VMSTATE_UINT32(chi_global_mode, DBRIState),
> + VMSTATE_UINT32(chi_data_mode, DBRIState),
> +
> + VMSTATE_UINT32(play.pipe, DBRIState),
> + VMSTATE_UINT32(rec.pipe, DBRIState),
> + VMSTATE_UINT32(play.ctrl, DBRIState),
> + VMSTATE_UINT32(rec.ctrl, DBRIState),
> + VMSTATE_BOOL(play.stopped, DBRIState),
> + VMSTATE_BOOL(rec.stopped, DBRIState),
> +
> + VMSTATE_BOOL(pipe_update, DBRIState),
> + VMSTATE_STRUCT_ARRAY(pipe, DBRIState, 32, 1,
> + vmstate_dbri_pipe, DBRIPipeState),
> +
> + VMSTATE_UINT8(codec.status, DBRIState),
> + VMSTATE_UINT8(codec.data_format, DBRIState),
> + VMSTATE_UINT8(codec.port_control, DBRIState),
> + VMSTATE_UINT8_ARRAY(codec.settings, DBRIState, 4),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static SysBusDeviceInfo dbri_info = {
> + .init = dbri_init1,
> + .qdev.name = "SUNW,DBRIe",
> + .qdev.desc = "Sun DBRI audio interface",
> + .qdev.size = sizeof(DBRIState),
> + .qdev.reset = dbri_reset,
> + .qdev.vmsd = &vmstate_dbri,
> + .qdev.props = (Property[]) {
> + DEFINE_PROP_PTR("iommu_opaque", DBRIState, iommu),
> + DEFINE_PROP_INT32("codec_offset", DBRIState, codec_offset, 8),
> + DEFINE_PROP_END_OF_LIST(),
> + }
> +};
> +
> +static void dbri_register_devices(void)
> +{
> + sysbus_register_withprop(&dbri_info);
> +}
> +
> +device_init(dbri_register_devices);
> diff --git a/hw/sun4m.c b/hw/sun4m.c
> index df3aa32..3112d19 100644
> --- a/hw/sun4m.c
> +++ b/hw/sun4m.c
> @@ -421,6 +421,20 @@ static void lance_init(NICInfo *nd, target_phys_addr_t
> leaddr,
> qdev_connect_gpio_out(dma_opaque, 0, reset);
> }
>
> +static void dbri_init(target_phys_addr_t daddr, qemu_irq parent_irq,
> + void *iommu)
> +{
> + DeviceState *dev;
> + SysBusDevice *s;
> +
> + dev = qdev_create(NULL, "SUNW,DBRIe");
> + qdev_prop_set_ptr(dev, "iommu_opaque", iommu);
> + qdev_init_nofail(dev);
> + s = sysbus_from_qdev(dev);
> + sysbus_connect_irq(s, 0, parent_irq);
> + sysbus_mmio_map(s, 0, daddr);
> +}
> +
> static DeviceState *slavio_intctl_init(target_phys_addr_t addr,
> target_phys_addr_t addrg,
> qemu_irq **parent_irq)
> @@ -943,10 +957,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef
> *hwdef, ram_addr_t RAM_size,
>
> if (hwdef->dbri_base) {
> /* ISDN chip with attached CS4215 audio codec */
> - /* prom space */
> - empty_slot_init(hwdef->dbri_base+0x1000, 0x30);
> - /* reg space */
> - empty_slot_init(hwdef->dbri_base+0x10000, 0x100);
> + dbri_init(hwdef->dbri_base, slavio_irq[11], iommu);
> }
>
> if (hwdef->bpp_base) {
>