qemu-devel
[Top][All Lists]
Advanced

[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) {
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]