]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - sound/sparc/dbri.c
sparc64: Fix unsigned long long warnings in drivers.
[linux-2.6.git] / sound / sparc / dbri.c
index f3ae6e23610e5277bad2d996a83fddd66430948b..23ed6f04a718c30d30db22e577974c4ee54f8190 100644 (file)
@@ -2,22 +2,24 @@
  * Driver for DBRI sound chip found on Sparcs.
  * Copyright (C) 2004, 2005 Martin Habets (mhabets@users.sourceforge.net)
  *
+ * Converted to ring buffered version by Krzysztof Helt (krzysztof.h1@wp.pl)
+ *
  * Based entirely upon drivers/sbus/audio/dbri.c which is:
  * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
  * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
  *
- * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
- * on Sun SPARCstation 10, 20, LX and Voyager models.
+ * This is the low level driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
+ * on Sun SPARCStation 10, 20, LX and Voyager models.
  *
  * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel
  *   data time multiplexer with ISDN support (aka T7259)
  *   Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel.
  *   CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?).
  *   Documentation:
- *   - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Tranceiver" from
+ *   - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Transceiver" from
  *     Sparc Technology Business (courtesy of Sun Support)
  *   - Data sheet of the T7903, a newer but very similar ISA bus equivalent
- *     available from the Lucent (formarly AT&T microelectronics) home
+ *     available from the Lucent (formerly AT&T microelectronics) home
  *     page.
  *   - http://www.freesoft.org/Linux/DBRI/
  * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec
  *   Documentation: from the Crystal Semiconductor home page.
  *
  * The DBRI is a 32 pipe machine, each pipe can transfer some bits between
- * memory and a serial device (long pipes, nr 0-15) or between two serial
- * devices (short pipes, nr 16-31), or simply send a fixed data to a serial
+ * memory and a serial device (long pipes, no. 0-15) or between two serial
+ * devices (short pipes, no. 16-31), or simply send a fixed data to a serial
  * device (short pipes).
- * A timeslot defines the bit-offset and nr of bits read from a serial device.
+ * A timeslot defines the bit-offset and no. of bits read from a serial device.
  * The timeslots are linked to 6 circular lists, one for each direction for
  * each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes
  * (the second one is a monitor/tee pipe, valid only for serial input).
  *
  * The mmcodec is connected via the CHI bus and needs the data & some
- * parameters (volume, balance, output selection) timemultiplexed in 8 byte
+ * parameters (volume, output selection) time multiplexed in 8 byte
  * chunks. It also has a control mode, which serves for audio format setting.
  *
  * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
- * the same CHI bus, so I thought perhaps it is possible to use the onboard
- * & the speakerbox codec simultanously, giving 2 (not very independent :-)
+ * the same CHI bus, so I thought perhaps it is possible to use the on-board
+ * & the speakerbox codec simultaneously, giving 2 (not very independent :-)
  * audio devices. But the SUN HW group decided against it, at least on my
  * LX the speakerbox connector has at least 1 pin missing and 1 wrongly
  * connected.
  * other       DBRI low-level stuff
  */
 
-#include <sound/driver.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -62,9 +66,8 @@
 #include <sound/control.h>
 #include <sound/initval.h>
 
-#include <asm/irq.h>
-#include <asm/io.h>
-#include <asm/sbus.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <asm/atomic.h>
 
 MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
@@ -74,7 +77,8 @@ MODULE_SUPPORTED_DEVICE("{{Sun,DBRI}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;     /* Enable this card */
+/* Enable this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Sun DBRI soundcard.");
@@ -83,7 +87,7 @@ MODULE_PARM_DESC(id, "ID string for Sun DBRI soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard.");
 
-#define DBRI_DEBUG
+#undef DBRI_DEBUG
 
 #define D_INT  (1<<0)
 #define D_GEN  (1<<1)
@@ -102,19 +106,17 @@ static char *cmds[] = {
        "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
 };
 
-#define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x)
+#define dprintk(a, x...) if (dbri_debug & a) printk(KERN_DEBUG x)
 
-#define DBRI_CMD(cmd, intr, value) ((cmd << 28) |                      \
-                                   (1 << 27) | \
-                                   value)
 #else
-#define dprintk(a, x...)
+#define dprintk(a, x...) do { } while (0)
 
-#define DBRI_CMD(cmd, intr, value) ((cmd << 28) |                      \
-                                   (intr << 27) | \
-                                   value)
 #endif                         /* DBRI_DEBUG */
 
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) |      \
+                                   (intr << 27) |      \
+                                   value)
+
 /***************************************************************************
        CS4215 specific definitions and structures
 ****************************************************************************/
@@ -131,7 +133,7 @@ struct cs4215 {
 };
 
 /*
- * Control mode first 
+ * Control mode first
  */
 
 /* Time Slot 1, Status register */
@@ -160,7 +162,7 @@ static struct {
      /* {    NA, (1 << 4), (5 << 3) }, */
        { 48000, (1 << 4), (6 << 3) },
        {  9600, (1 << 4), (7 << 3) },
-       {  5513, (2 << 4), (0 << 3) },  /* Actually 5512.5 */
+       {  5512, (2 << 4), (0 << 3) },  /* Actually 5512.5 */
        { 11025, (2 << 4), (1 << 3) },
        { 18900, (2 << 4), (2 << 3) },
        { 22050, (2 << 4), (3 << 3) },
@@ -219,7 +221,7 @@ static struct {
 /* Time Slot 7, Input Setting */
 #define CS4215_LG(v)   v       /* Left Gain Setting 0xf: 22.5 dB */
 #define CS4215_IS      (1<<4)  /* Input Select: 1=Microphone, 0=Line */
-#define CS4215_OVR     (1<<5)  /* 1: Overrange condition occurred */
+#define CS4215_OVR     (1<<5)  /* 1: Over range condition occurred */
 #define CS4215_PIO0    (1<<6)  /* Parallel I/O 0 */
 #define CS4215_PIO1    (1<<7)
 
@@ -232,36 +234,29 @@ static struct {
 ****************************************************************************/
 
 /* DBRI main registers */
-#define REG0   0x00UL          /* Status and Control */
-#define REG1   0x04UL          /* Mode and Interrupt */
-#define REG2   0x08UL          /* Parallel IO */
-#define REG3   0x0cUL          /* Test */
-#define REG8   0x20UL          /* Command Queue Pointer */
-#define REG9   0x24UL          /* Interrupt Queue Pointer */
+#define REG0   0x00            /* Status and Control */
+#define REG1   0x04            /* Mode and Interrupt */
+#define REG2   0x08            /* Parallel IO */
+#define REG3   0x0c            /* Test */
+#define REG8   0x20            /* Command Queue Pointer */
+#define REG9   0x24            /* Interrupt Queue Pointer */
 
 #define DBRI_NO_CMDS   64
-#define DBRI_NO_INTS   1       /* Note: the value of this define was
-                                * originally 2.  The ringbuffer to store
-                                * interrupts in dma is currently broken.
-                                * This is a temporary fix until the ringbuffer
-                                * is fixed.
-                                */
 #define DBRI_INT_BLK   64
 #define DBRI_NO_DESCS  64
 #define DBRI_NO_PIPES  32
-
-#define DBRI_MM_ONB    1
-#define DBRI_MM_SB     2
+#define DBRI_MAX_PIPE  (DBRI_NO_PIPES - 1)
 
 #define DBRI_REC       0
 #define DBRI_PLAY      1
 #define DBRI_NO_STREAMS        2
 
 /* One transmit/receive descriptor */
+/* When ba != 0 descriptor is used */
 struct dbri_mem {
        volatile __u32 word1;
-       volatile __u32 ba;      /* Transmit/Receive Buffer Address */
-       volatile __u32 nda;     /* Next Descriptor Address */
+       __u32 ba;       /* Transmit/Receive Buffer Address */
+       __u32 nda;      /* Next Descriptor Address */
        volatile __u32 word4;
 };
 
@@ -269,8 +264,8 @@ struct dbri_mem {
  * the CPU and the DBRI
  */
 struct dbri_dma {
-       volatile s32 cmd[DBRI_NO_CMDS]; /* Place for commands       */
-       volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field  */
+       s32 cmd[DBRI_NO_CMDS];                  /* Place for commands */
+       volatile s32 intr[DBRI_INT_BLK];        /* Interrupt field  */
        struct dbri_mem desc[DBRI_NO_DESCS];    /* Xmit/receive descriptors */
 };
 
@@ -282,71 +277,50 @@ enum in_or_out { PIPEinput, PIPEoutput };
 
 struct dbri_pipe {
        u32 sdp;                /* SDP command word */
-       enum in_or_out direction;
        int nextpipe;           /* Next pipe in linked list */
-       int prevpipe;
-       int cycle;              /* Offset of timeslot (bits) */
        int length;             /* Length of timeslot (bits) */
        int first_desc;         /* Index of first descriptor */
        int desc;               /* Index of active descriptor */
        volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */
 };
 
-struct dbri_desc {
-       int inuse;              /* Boolean flag */
-       int next;               /* Index of next desc, or -1 */
-       unsigned int len;
-};
-
 /* Per stream (playback or record) information */
 struct dbri_streaminfo {
        struct snd_pcm_substream *substream;
-       u32 dvma_buffer;        /* Device view of Alsa DMA buffer */
-       int left;               /* # of bytes left in DMA buffer  */
+       u32 dvma_buffer;        /* Device view of ALSA DMA buffer */
        int size;               /* Size of DMA buffer             */
        size_t offset;          /* offset in user buffer          */
        int pipe;               /* Data pipe used                 */
        int left_gain;          /* mixer elements                 */
        int right_gain;
-       int balance;
 };
 
 /* This structure holds the information for both chips (DBRI & CS4215) */
 struct snd_dbri {
-       struct snd_card *card;  /* ALSA card */
-       struct snd_pcm *pcm;
-
        int regs_size, irq;     /* Needed for unload */
-       struct sbus_dev *sdev;  /* SBUS device info */
+       struct of_device *op;   /* OF device info */
        spinlock_t lock;
 
-       volatile struct dbri_dma *dma;  /* Pointer to our DMA block */
+       struct dbri_dma *dma;   /* Pointer to our DMA block */
        u32 dma_dvma;           /* DBRI visible DMA address */
 
        void __iomem *regs;     /* dbri HW regs */
-       int dbri_version;       /* 'e' and up is OK */
        int dbri_irqp;          /* intr queue pointer */
-       int wait_send;          /* sequence of command buffers send */
-       int wait_ackd;          /* sequence of command buffers acknowledged */
 
        struct dbri_pipe pipes[DBRI_NO_PIPES];  /* DBRI's 32 data pipes */
-       struct dbri_desc descs[DBRI_NO_DESCS];
+       int next_desc[DBRI_NO_DESCS];           /* Index of next desc, or -1 */
+       spinlock_t cmdlock;     /* Protects cmd queue accesses */
+       s32 *cmdptr;            /* Pointer to the last queued cmd */
 
-       int chi_in_pipe;
-       int chi_out_pipe;
        int chi_bpf;
 
        struct cs4215 mm;       /* mmcodec special info */
                                /* per stream (playback/record) info */
        struct dbri_streaminfo stream_info[DBRI_NO_STREAMS];
-
-       struct snd_dbri *next;
 };
 
 #define DBRI_MAX_VOLUME                63      /* Output volume */
 #define DBRI_MAX_GAIN          15      /* Input gain */
-#define DBRI_RIGHT_BALANCE     255
-#define DBRI_MID_BALANCE       (DBRI_RIGHT_BALANCE >> 1)
 
 /* DBRI Reg0 - Status Control Register - defines. (Page 17) */
 #define D_P            (1<<15) /* Program command & queue pointer valid */
@@ -365,11 +339,11 @@ struct snd_dbri {
 /* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */
 #define D_LITTLE_END   (1<<8)  /* Byte Order */
 #define D_BIG_END      (0<<8)  /* Byte Order */
-#define D_MRR          (1<<4)  /* Multiple Error Ack on SBus (readonly) */
-#define D_MLE          (1<<3)  /* Multiple Late Error on SBus (readonly) */
-#define D_LBG          (1<<2)  /* Lost Bus Grant on SBus (readonly) */
-#define D_MBE          (1<<1)  /* Burst Error on SBus (readonly) */
-#define D_IR           (1<<0)  /* Interrupt Indicator (readonly) */
+#define D_MRR          (1<<4)  /* Multiple Error Ack on SBus (read only) */
+#define D_MLE          (1<<3)  /* Multiple Late Error on SBus (read only) */
+#define D_LBG          (1<<2)  /* Lost Bus Grant on SBus (read only) */
+#define D_MBE          (1<<1)  /* Burst Error on SBus (read only) */
+#define D_IR           (1<<0)  /* Interrupt Indicator (read only) */
 
 /* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */
 #define D_ENPIO3       (1<<7)  /* Enable Pin 3 */
@@ -400,11 +374,11 @@ struct snd_dbri {
 #define D_CDM          0xe     /* CHI Data mode command */
 
 /* Special bits for some commands */
-#define D_PIPE(v)      ((v)<<0)        /* Pipe Nr: 0-15 long, 16-21 short */
+#define D_PIPE(v)      ((v)<<0)        /* Pipe No.: 0-15 long, 16-21 short */
 
 /* Setup Data Pipe */
 /* IRM */
-#define D_SDP_2SAME    (1<<18) /* Report 2nd time in a row value rcvd */
+#define D_SDP_2SAME    (1<<18) /* Report 2nd time in a row value received */
 #define D_SDP_CHANGE   (2<<18) /* Report any changes */
 #define D_SDP_EVERY    (3<<18) /* Report any changes */
 #define D_SDP_EOL      (1<<17) /* EOL interrupt enable */
@@ -443,7 +417,7 @@ struct snd_dbri {
 #define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */
 #define D_TS_ANCHOR    (7<<10) /* Starting short pipes */
 #define D_TS_MON(v)    ((v)<<5)        /* Monitor Pipe */
-#define D_TS_NEXT(v)   ((v)<<0)        /* Pipe Nr: 0-15 long, 16-21 short */
+#define D_TS_NEXT(v)   ((v)<<0)        /* Pipe no.: 0-15 long, 16-21 short */
 
 /* Concentration Highway Interface Modes */
 #define D_CHI_CHICM(v) ((v)<<16)       /* Clock mode */
@@ -459,7 +433,7 @@ struct snd_dbri {
 #define D_NT_NBF       (1<<16) /* Number of bad frames to loose framing */
 #define D_NT_IRM_IMM   (1<<15) /* Interrupt Report & Mask: Immediate */
 #define D_NT_IRM_EN    (1<<14) /* Interrupt Report & Mask: Enable */
-#define D_NT_ISNT      (1<<13) /* Configfure interface as NT */
+#define D_NT_ISNT      (1<<13) /* Configure interface as NT */
 #define D_NT_FT                (1<<12) /* Fixed Timing */
 #define D_NT_EZ                (1<<11) /* Echo Channel is Zeros */
 #define D_NT_IFA       (1<<10) /* Inhibit Final Activation */
@@ -479,7 +453,7 @@ struct snd_dbri {
 #define D_TEST_RAM(v)  ((v)<<16)       /* RAM Pointer */
 #define D_TEST_SIZE(v) ((v)<<11)       /* */
 #define D_TEST_ROMONOFF        0x5     /* Toggle ROM opcode monitor on/off */
-#define D_TEST_PROC    0x6     /* MicroProcessor test */
+#define D_TEST_PROC    0x6     /* Microprocessor test */
 #define D_TEST_SER     0x7     /* Serial-Controller test */
 #define D_TEST_RAMREAD 0x8     /* Copy from Ram to system memory */
 #define D_TEST_RAMWRITE        0x9     /* Copy into Ram from system memory */
@@ -488,12 +462,12 @@ struct snd_dbri {
 #define D_TEST_DUMP    0xe     /* ROM Dump */
 
 /* CHI Data Mode */
-#define D_CDM_THI      (1<<8)  /* Transmit Data on CHIDR Pin */
-#define D_CDM_RHI      (1<<7)  /* Receive Data on CHIDX Pin */
-#define D_CDM_RCE      (1<<6)  /* Receive on Rising Edge of CHICK */
-#define D_CDM_XCE      (1<<2)  /* Transmit Data on Rising Edge of CHICK */
-#define D_CDM_XEN      (1<<1)  /* Transmit Highway Enable */
-#define D_CDM_REN      (1<<0)  /* Receive Highway Enable */
+#define D_CDM_THI      (1 << 8)        /* Transmit Data on CHIDR Pin */
+#define D_CDM_RHI      (1 << 7)        /* Receive Data on CHIDX Pin */
+#define D_CDM_RCE      (1 << 6)        /* Receive on Rising Edge of CHICK */
+#define D_CDM_XCE      (1 << 2) /* Transmit Data on Rising Edge of CHICK */
+#define D_CDM_XEN      (1 << 1)        /* Transmit Highway Enable */
+#define D_CDM_REN      (1 << 0)        /* Receive Highway Enable */
 
 /* The Interrupts */
 #define D_INTR_BRDY    1       /* Buffer Ready for processing */
@@ -517,9 +491,9 @@ struct snd_dbri {
 #define D_INTR_CHI     36
 #define D_INTR_CMD     38
 
-#define D_INTR_GETCHAN(v)      (((v)>>24) & 0x3f)
-#define D_INTR_GETCODE(v)      (((v)>>20) & 0xf)
-#define D_INTR_GETCMD(v)       (((v)>>16) & 0xf)
+#define D_INTR_GETCHAN(v)      (((v) >> 24) & 0x3f)
+#define D_INTR_GETCODE(v)      (((v) >> 20) & 0xf)
+#define D_INTR_GETCMD(v)       (((v) >> 16) & 0xf)
 #define D_INTR_GETVAL(v)       ((v) & 0xffff)
 #define D_INTR_GETRVAL(v)      ((v) & 0xfffff)
 
@@ -557,43 +531,42 @@ struct snd_dbri {
 #define D_P_31         31      /* */
 
 /* Transmit descriptor defines */
-#define DBRI_TD_F      (1<<31) /* End of Frame */
-#define DBRI_TD_D      (1<<30) /* Do not append CRC */
-#define DBRI_TD_CNT(v) ((v)<<16)       /* Number of valid bytes in the buffer */
-#define DBRI_TD_B      (1<<15) /* Final interrupt */
-#define DBRI_TD_M      (1<<14) /* Marker interrupt */
-#define DBRI_TD_I      (1<<13) /* Transmit Idle Characters */
-#define DBRI_TD_FCNT(v)        (v)     /* Flag Count */
-#define DBRI_TD_UNR    (1<<3)  /* Underrun: transmitter is out of data */
-#define DBRI_TD_ABT    (1<<2)  /* Abort: frame aborted */
-#define DBRI_TD_TBC    (1<<0)  /* Transmit buffer Complete */
-#define DBRI_TD_STATUS(v)       ((v)&0xff)     /* Transmit status */
-                       /* Maximum buffer size per TD: almost 8Kb */
-#define DBRI_TD_MAXCNT ((1 << 13) - 1)
+#define DBRI_TD_F      (1 << 31)       /* End of Frame */
+#define DBRI_TD_D      (1 << 30)       /* Do not append CRC */
+#define DBRI_TD_CNT(v) ((v) << 16) /* Number of valid bytes in the buffer */
+#define DBRI_TD_B      (1 << 15)       /* Final interrupt */
+#define DBRI_TD_M      (1 << 14)       /* Marker interrupt */
+#define DBRI_TD_I      (1 << 13)       /* Transmit Idle Characters */
+#define DBRI_TD_FCNT(v)        (v)             /* Flag Count */
+#define DBRI_TD_UNR    (1 << 3) /* Underrun: transmitter is out of data */
+#define DBRI_TD_ABT    (1 << 2)        /* Abort: frame aborted */
+#define DBRI_TD_TBC    (1 << 0)        /* Transmit buffer Complete */
+#define DBRI_TD_STATUS(v)       ((v) & 0xff)   /* Transmit status */
+                       /* Maximum buffer size per TD: almost 8KB */
+#define DBRI_TD_MAXCNT ((1 << 13) - 4)
 
 /* Receive descriptor defines */
-#define DBRI_RD_F      (1<<31) /* End of Frame */
-#define DBRI_RD_C      (1<<30) /* Completed buffer */
-#define DBRI_RD_B      (1<<15) /* Final interrupt */
-#define DBRI_RD_M      (1<<14) /* Marker interrupt */
-#define DBRI_RD_BCNT(v)        (v)     /* Buffer size */
-#define DBRI_RD_CRC    (1<<7)  /* 0: CRC is correct */
-#define DBRI_RD_BBC    (1<<6)  /* 1: Bad Byte received */
-#define DBRI_RD_ABT    (1<<5)  /* Abort: frame aborted */
-#define DBRI_RD_OVRN   (1<<3)  /* Overrun: data lost */
-#define DBRI_RD_STATUS(v)      ((v)&0xff)      /* Receive status */
-#define DBRI_RD_CNT(v) (((v)>>16)&0x1fff)      /* Valid bytes in the buffer */
+#define DBRI_RD_F      (1 << 31)       /* End of Frame */
+#define DBRI_RD_C      (1 << 30)       /* Completed buffer */
+#define DBRI_RD_B      (1 << 15)       /* Final interrupt */
+#define DBRI_RD_M      (1 << 14)       /* Marker interrupt */
+#define DBRI_RD_BCNT(v)        (v)             /* Buffer size */
+#define DBRI_RD_CRC    (1 << 7)        /* 0: CRC is correct */
+#define DBRI_RD_BBC    (1 << 6)        /* 1: Bad Byte received */
+#define DBRI_RD_ABT    (1 << 5)        /* Abort: frame aborted */
+#define DBRI_RD_OVRN   (1 << 3)        /* Overrun: data lost */
+#define DBRI_RD_STATUS(v)      ((v) & 0xff)    /* Receive status */
+#define DBRI_RD_CNT(v) (((v) >> 16) & 0x1fff)  /* Valid bytes in the buffer */
 
 /* stream_info[] access */
 /* Translate the ALSA direction into the array index */
 #define DBRI_STREAMNO(substream)                               \
-               (substream->stream ==                           \
-                SNDRV_PCM_STREAM_PLAYBACK? DBRI_PLAY: DBRI_REC)
+               (substream->stream ==                           \
+                SNDRV_PCM_STREAM_PLAYBACK ? DBRI_PLAY: DBRI_REC)
 
 /* Return a pointer to dbri_streaminfo */
-#define DBRI_STREAM(dbri, substream)   &dbri->stream_info[DBRI_STREAMNO(substream)]
-
-static struct snd_dbri *dbri_list;     /* All DBRI devices */
+#define DBRI_STREAM(dbri, substream)   \
+               &dbri->stream_info[DBRI_STREAMNO(substream)]
 
 /*
  * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
@@ -633,93 +606,124 @@ The list is terminated with a WAIT command, which generates a
 CPU interrupt to signal completion.
 
 Since the DBRI can run in parallel with the CPU, several means of
-synchronization present themselves.  The method implemented here is close
-to the original scheme (Rudolf's), and uses 2 counters (wait_send and
-wait_ackd) to synchronize the command buffer between the CPU and the DBRI.
+synchronization present themselves. The method implemented here uses
+the dbri_cmdwait() to wait for execution of batch of sent commands.
 
-A more sophisticated scheme might involve a circular command buffer
-or an array of command buffers.  A routine could fill one with
-commands and link it onto a list.  When a interrupt signaled
-completion of the current command buffer, look on the list for
-the next one.
+A circular command buffer is used here. A new command is being added
+while another can be executed. The scheme works by adding two WAIT commands
+after each sent batch of commands. When the next batch is prepared it is
+added after the WAIT commands then the WAITs are replaced with single JUMP
+command to the new batch. The the DBRI is forced to reread the last WAIT
+command (replaced by the JUMP by then). If the DBRI is still executing
+previous commands the request to reread the WAIT command is ignored.
 
 Every time a routine wants to write commands to the DBRI, it must
-first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
-in return. dbri_cmdlock() will block if the previous commands have not
-been completed yet. After this the commands can be written to the buffer,
-and dbri_cmdsend() is called with the final pointer value to send them
-to the DBRI.
+first call dbri_cmdlock() and get pointer to a free space in
+dbri->dma->cmd buffer. After this, the commands can be written to
+the buffer, and dbri_cmdsend() is called with the final pointer value
+to send them to the DBRI.
 
 */
 
-static void dbri_process_interrupt_buffer(struct snd_dbri * dbri);
-
-enum dbri_lock { NoGetLock, GetLock };
-#define MAXLOOPS 10
-
-static volatile s32 *dbri_cmdlock(struct snd_dbri * dbri, enum dbri_lock get)
+#define MAXLOOPS 20
+/*
+ * Wait for the current command string to execute
+ */
+static void dbri_cmdwait(struct snd_dbri *dbri)
 {
        int maxloops = MAXLOOPS;
-
-#ifndef SMP
-       if ((get == GetLock) && spin_is_locked(&dbri->lock)) {
-               printk(KERN_ERR "DBRI: cmdlock called while in spinlock.");
-       }
-#endif
+       unsigned long flags;
 
        /* Delay if previous commands are still being processed */
-       while ((--maxloops) > 0 && (dbri->wait_send != dbri->wait_ackd)) {
+       spin_lock_irqsave(&dbri->lock, flags);
+       while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P)) {
+               spin_unlock_irqrestore(&dbri->lock, flags);
                msleep_interruptible(1);
-               /* If dbri_cmdlock() got called from inside the
-                * interrupt handler, this will do the processing.
-                */
-               dbri_process_interrupt_buffer(dbri);
+               spin_lock_irqsave(&dbri->lock, flags);
        }
-       if (maxloops == 0) {
-               printk(KERN_ERR "DBRI: Chip never completed command buffer %d\n",
-                       dbri->wait_send);
-       } else {
+       spin_unlock_irqrestore(&dbri->lock, flags);
+
+       if (maxloops == 0)
+               printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
+       else
                dprintk(D_CMD, "Chip completed command buffer (%d)\n",
                        MAXLOOPS - maxloops - 1);
-       }
+}
+/*
+ * Lock the command queue and return pointer to space for len cmd words
+ * It locks the cmdlock spinlock.
+ */
+static s32 *dbri_cmdlock(struct snd_dbri *dbri, int len)
+{
+       /* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */
+       len += 2;
+       spin_lock(&dbri->cmdlock);
+       if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2)
+               return dbri->cmdptr + 2;
+       else if (len < sbus_readl(dbri->regs + REG8) - dbri->dma_dvma)
+               return dbri->dma->cmd;
+       else
+               printk(KERN_ERR "DBRI: no space for commands.");
 
-       /*if (get == GetLock) spin_lock(&dbri->lock); */
-       return &dbri->dma->cmd[0];
+       return NULL;
 }
 
-static void dbri_cmdsend(struct snd_dbri * dbri, volatile s32 * cmd)
+/*
+ * Send prepared cmd string. It works by writing a JUMP cmd into
+ * the last WAIT cmd and force DBRI to reread the cmd.
+ * The JUMP cmd points to the new cmd string.
+ * It also releases the cmdlock spinlock.
+ *
+ * Lock must be held before calling this.
+ */
+static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len)
 {
-       volatile s32 *ptr;
-       u32     reg;
+       s32 tmp, addr;
+       static int wait_id = 0;
 
-       for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) {
-               dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
-       }
+       wait_id++;
+       wait_id &= 0xffff;      /* restrict it to a 16 bit counter. */
+       *(cmd) = DBRI_CMD(D_WAIT, 1, wait_id);
+       *(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id);
 
-       if ((cmd - &dbri->dma->cmd[0]) >= DBRI_NO_CMDS - 1) {
-               printk(KERN_ERR "DBRI: Command buffer overflow! (bug in driver)\n");
-               /* Ignore the last part. */
-               cmd = &dbri->dma->cmd[DBRI_NO_CMDS - 3];
-       }
+       /* Replace the last command with JUMP */
+       addr = dbri->dma_dvma + (cmd - len - dbri->dma->cmd) * sizeof(s32);
+       *(dbri->cmdptr+1) = addr;
+       *(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0);
 
-       dbri->wait_send++;
-       dbri->wait_send &= 0xffff;      /* restrict it to a 16 bit counter. */
-       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
-       *(cmd++) = DBRI_CMD(D_WAIT, 1, dbri->wait_send);
+#ifdef DBRI_DEBUG
+       if (cmd > dbri->cmdptr) {
+               s32 *ptr;
+
+               for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++)
+                       dprintk(D_CMD, "cmd: %lx:%08x\n",
+                               (unsigned long)ptr, *ptr);
+       } else {
+               s32 *ptr = dbri->cmdptr;
+
+               dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+               ptr++;
+               dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+               for (ptr = dbri->dma->cmd; ptr < cmd+2; ptr++)
+                       dprintk(D_CMD, "cmd: %lx:%08x\n",
+                               (unsigned long)ptr, *ptr);
+       }
+#endif
 
-       /* Set command pointer and signal it is valid. */
-       sbus_writel(dbri->dma_dvma, dbri->regs + REG8);
-       reg = sbus_readl(dbri->regs + REG0);
-       reg |= D_P;
-       sbus_writel(reg, dbri->regs + REG0);
+       /* Reread the last command */
+       tmp = sbus_readl(dbri->regs + REG0);
+       tmp |= D_P;
+       sbus_writel(tmp, dbri->regs + REG0);
 
-       /*spin_unlock(&dbri->lock); */
+       dbri->cmdptr = cmd;
+       spin_unlock(&dbri->cmdlock);
 }
 
 /* Lock must be held when calling this */
-static void dbri_reset(struct snd_dbri * dbri)
+static void dbri_reset(struct snd_dbri *dbri)
 {
        int i;
+       u32 tmp;
 
        dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n",
                sbus_readl(dbri->regs + REG0),
@@ -729,13 +733,20 @@ static void dbri_reset(struct snd_dbri * dbri)
        sbus_writel(D_R, dbri->regs + REG0);    /* Soft Reset */
        for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++)
                udelay(10);
+
+       /* A brute approach - DBRI falls back to working burst size by itself
+        * On SS20 D_S does not work, so do not try so high. */
+       tmp = sbus_readl(dbri->regs + REG0);
+       tmp |= D_G | D_E;
+       tmp &= ~D_S;
+       sbus_writel(tmp, dbri->regs + REG0);
 }
 
 /* Lock must not be held before calling this */
-static void dbri_initialize(struct snd_dbri * dbri)
+static void __devinit dbri_initialize(struct snd_dbri *dbri)
 {
-       volatile s32 *cmd;
-       u32 dma_addr, tmp;
+       s32 *cmd;
+       u32 dma_addr;
        unsigned long flags;
        int n;
 
@@ -743,42 +754,34 @@ static void dbri_initialize(struct snd_dbri * dbri)
 
        dbri_reset(dbri);
 
-       cmd = dbri_cmdlock(dbri, NoGetLock);
-       dprintk(D_GEN, "init: cmd: %p, int: %p\n",
-               &dbri->dma->cmd[0], &dbri->dma->intr[0]);
+       /* Initialize pipes */
+       for (n = 0; n < DBRI_NO_PIPES; n++)
+               dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
 
+       spin_lock_init(&dbri->cmdlock);
        /*
-        * Initialize the interrupt ringbuffer.
+        * Initialize the interrupt ring buffer.
         */
-       for (n = 0; n < DBRI_NO_INTS - 1; n++) {
-               dma_addr = dbri->dma_dvma;
-               dma_addr += dbri_dma_off(intr, ((n + 1) & DBRI_INT_BLK));
-               dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
-       }
        dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
-       dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
+       dbri->dma->intr[0] = dma_addr;
        dbri->dbri_irqp = 1;
-
-       /* Initialize pipes */
-       for (n = 0; n < DBRI_NO_PIPES; n++)
-               dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
-
-       /* A brute approach - DBRI falls back to working burst size by itself
-        * On SS20 D_S does not work, so do not try so high. */
-       tmp = sbus_readl(dbri->regs + REG0);
-       tmp |= D_G | D_E;
-       tmp &= ~D_S;
-       sbus_writel(tmp, dbri->regs + REG0);
-
        /*
         * Set up the interrupt queue
         */
-       dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+       spin_lock(&dbri->cmdlock);
+       cmd = dbri->cmdptr = dbri->dma->cmd;
        *(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
        *(cmd++) = dma_addr;
+       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+       dbri->cmdptr = cmd;
+       *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+       *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+       dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0);
+       sbus_writel(dma_addr, dbri->regs + REG8);
+       spin_unlock(&dbri->cmdlock);
 
-       dbri_cmdsend(dbri, cmd);
        spin_unlock_irqrestore(&dbri->lock, flags);
+       dbri_cmdwait(dbri);
 }
 
 /*
@@ -795,7 +798,7 @@ list ordering, among other things.  The transmit and receive functions
 here interface closely with the transmit and receive interrupt code.
 
 */
-static int pipe_active(struct snd_dbri * dbri, int pipe)
+static inline int pipe_active(struct snd_dbri *dbri, int pipe)
 {
        return ((pipe >= 0) && (dbri->pipes[pipe].desc != -1));
 }
@@ -805,48 +808,57 @@ static int pipe_active(struct snd_dbri * dbri, int pipe)
  * Called on an in-use pipe to clear anything being transmitted or received
  * Lock must be held before calling this.
  */
-static void reset_pipe(struct snd_dbri * dbri, int pipe)
+static void reset_pipe(struct snd_dbri *dbri, int pipe)
 {
        int sdp;
        int desc;
-       volatile int *cmd;
+       s32 *cmd;
 
-       if (pipe < 0 || pipe > 31) {
-               printk(KERN_ERR "DBRI: reset_pipe called with illegal pipe number\n");
+       if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
+               printk(KERN_ERR "DBRI: reset_pipe called with "
+                       "illegal pipe number\n");
                return;
        }
 
        sdp = dbri->pipes[pipe].sdp;
        if (sdp == 0) {
-               printk(KERN_ERR "DBRI: reset_pipe called on uninitialized pipe\n");
+               printk(KERN_ERR "DBRI: reset_pipe called "
+                       "on uninitialized pipe\n");
                return;
        }
 
-       cmd = dbri_cmdlock(dbri, NoGetLock);
+       cmd = dbri_cmdlock(dbri, 3);
        *(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
        *(cmd++) = 0;
-       dbri_cmdsend(dbri, cmd);
+       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+       dbri_cmdsend(dbri, cmd, 3);
 
        desc = dbri->pipes[pipe].first_desc;
-       while (desc != -1) {
-               dbri->descs[desc].inuse = 0;
-               desc = dbri->descs[desc].next;
-       }
+       if (desc >= 0)
+               do {
+                       dbri->dma->desc[desc].ba = 0;
+                       dbri->dma->desc[desc].nda = 0;
+                       desc = dbri->next_desc[desc];
+               } while (desc != -1 && desc != dbri->pipes[pipe].first_desc);
 
        dbri->pipes[pipe].desc = -1;
        dbri->pipes[pipe].first_desc = -1;
 }
 
-/* FIXME: direction as an argument? */
-static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
+/*
+ * Lock must be held before calling this.
+ */
+static void setup_pipe(struct snd_dbri *dbri, int pipe, int sdp)
 {
-       if (pipe < 0 || pipe > 31) {
-               printk(KERN_ERR "DBRI: setup_pipe called with illegal pipe number\n");
+       if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
+               printk(KERN_ERR "DBRI: setup_pipe called "
+                       "with illegal pipe number\n");
                return;
        }
 
        if ((sdp & 0xf800) != sdp) {
-               printk(KERN_ERR "DBRI: setup_pipe called with strange SDP value\n");
+               printk(KERN_ERR "DBRI: setup_pipe called "
+                       "with strange SDP value\n");
                /* sdp &= 0xf800; */
        }
 
@@ -860,119 +872,88 @@ static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
        dbri->pipes[pipe].sdp = sdp;
        dbri->pipes[pipe].desc = -1;
        dbri->pipes[pipe].first_desc = -1;
-       if (sdp & D_SDP_TO_SER)
-               dbri->pipes[pipe].direction = PIPEoutput;
-       else
-               dbri->pipes[pipe].direction = PIPEinput;
 
        reset_pipe(dbri, pipe);
 }
 
-/* FIXME: direction not needed */
-static void link_time_slot(struct snd_dbri * dbri, int pipe,
-                          enum in_or_out direction, int basepipe,
+/*
+ * Lock must be held before calling this.
+ */
+static void link_time_slot(struct snd_dbri *dbri, int pipe,
+                          int prevpipe, int nextpipe,
                           int length, int cycle)
 {
-       volatile s32 *cmd;
+       s32 *cmd;
        int val;
-       int prevpipe;
-       int nextpipe;
 
-       if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
-               printk(KERN_ERR 
+       if (pipe < 0 || pipe > DBRI_MAX_PIPE
+                       || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
+                       || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
+               printk(KERN_ERR
                    "DBRI: link_time_slot called with illegal pipe number\n");
                return;
        }
 
-       if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
-               printk(KERN_ERR "DBRI: link_time_slot called on uninitialized pipe\n");
+       if (dbri->pipes[pipe].sdp == 0
+                       || dbri->pipes[prevpipe].sdp == 0
+                       || dbri->pipes[nextpipe].sdp == 0) {
+               printk(KERN_ERR "DBRI: link_time_slot called "
+                       "on uninitialized pipe\n");
                return;
        }
 
-       /* Deal with CHI special case:
-        * "If transmission on edges 0 or 1 is desired, then cycle n
-        *  (where n = # of bit times per frame...) must be used."
-        *                  - DBRI data sheet, page 11
-        */
-       if (basepipe == 16 && direction == PIPEoutput && cycle == 0)
-               cycle = dbri->chi_bpf;
-
-       if (basepipe == pipe) {
-               prevpipe = pipe;
-               nextpipe = pipe;
-       } else {
-               /* We're not initializing a new linked list (basepipe != pipe),
-                * so run through the linked list and find where this pipe
-                * should be sloted in, based on its cycle.  CHI confuses
-                * things a bit, since it has a single anchor for both its
-                * transmit and receive lists.
-                */
-               if (basepipe == 16) {
-                       if (direction == PIPEinput) {
-                               prevpipe = dbri->chi_in_pipe;
-                       } else {
-                               prevpipe = dbri->chi_out_pipe;
-                       }
-               } else {
-                       prevpipe = basepipe;
-               }
-
-               nextpipe = dbri->pipes[prevpipe].nextpipe;
-
-               while (dbri->pipes[nextpipe].cycle < cycle
-                      && dbri->pipes[nextpipe].nextpipe != basepipe) {
-                       prevpipe = nextpipe;
-                       nextpipe = dbri->pipes[nextpipe].nextpipe;
-               }
-       }
-
-       if (prevpipe == 16) {
-               if (direction == PIPEinput) {
-                       dbri->chi_in_pipe = pipe;
-               } else {
-                       dbri->chi_out_pipe = pipe;
-               }
-       } else {
-               dbri->pipes[prevpipe].nextpipe = pipe;
-       }
-
+       dbri->pipes[prevpipe].nextpipe = pipe;
        dbri->pipes[pipe].nextpipe = nextpipe;
-       dbri->pipes[pipe].cycle = cycle;
        dbri->pipes[pipe].length = length;
 
-       cmd = dbri_cmdlock(dbri, NoGetLock);
+       cmd = dbri_cmdlock(dbri, 4);
 
-       if (direction == PIPEinput) {
-               val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+       if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+               /* Deal with CHI special case:
+                * "If transmission on edges 0 or 1 is desired, then cycle n
+                *  (where n = # of bit times per frame...) must be used."
+                *                  - DBRI data sheet, page 11
+                */
+               if (prevpipe == 16 && cycle == 0)
+                       cycle = dbri->chi_bpf;
+
+               val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
                *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+               *(cmd++) = 0;
                *(cmd++) =
                    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
-               *(cmd++) = 0;
        } else {
-               val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+               val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
                *(cmd++) = DBRI_CMD(D_DTS, 0, val);
-               *(cmd++) = 0;
                *(cmd++) =
                    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+               *(cmd++) = 0;
        }
+       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
 
-       dbri_cmdsend(dbri, cmd);
+       dbri_cmdsend(dbri, cmd, 4);
 }
 
-static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
+#if 0
+/*
+ * Lock must be held before calling this.
+ */
+static void unlink_time_slot(struct snd_dbri *dbri, int pipe,
                             enum in_or_out direction, int prevpipe,
                             int nextpipe)
 {
-       volatile s32 *cmd;
+       s32 *cmd;
        int val;
 
-       if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
-               printk(KERN_ERR 
+       if (pipe < 0 || pipe > DBRI_MAX_PIPE
+                       || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
+                       || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
+               printk(KERN_ERR
                    "DBRI: unlink_time_slot called with illegal pipe number\n");
                return;
        }
 
-       cmd = dbri_cmdlock(dbri, NoGetLock);
+       cmd = dbri_cmdlock(dbri, 4);
 
        if (direction == PIPEinput) {
                val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
@@ -985,9 +966,11 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
                *(cmd++) = 0;
                *(cmd++) = D_TS_NEXT(nextpipe);
        }
+       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
 
-       dbri_cmdsend(dbri, cmd);
+       dbri_cmdsend(dbri, cmd, 4);
 }
+#endif
 
 /* xmit_fixed() / recv_fixed()
  *
@@ -1001,19 +984,23 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
  * the actual time slot is.  The interrupt handler takes care of bit
  * ordering and alignment.  An 8-bit time slot will always end up
  * in the low-order 8 bits, filled either MSB-first or LSB-first,
- * depending on the settings passed to setup_pipe()
+ * depending on the settings passed to setup_pipe().
+ *
+ * Lock must not be held before calling it.
  */
-static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
+static void xmit_fixed(struct snd_dbri *dbri, int pipe, unsigned int data)
 {
-       volatile s32 *cmd;
+       s32 *cmd;
+       unsigned long flags;
 
-       if (pipe < 16 || pipe > 31) {
+       if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
                printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n");
                return;
        }
 
        if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
-               printk(KERN_ERR "DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe);
+               printk(KERN_ERR "DBRI: xmit_fixed: "
+                       "Uninitialized pipe %d\n", pipe);
                return;
        }
 
@@ -1023,7 +1010,8 @@ static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
        }
 
        if (!(dbri->pipes[pipe].sdp & D_SDP_TO_SER)) {
-               printk(KERN_ERR "DBRI: xmit_fixed: Called on receive pipe %d\n", pipe);
+               printk(KERN_ERR "DBRI: xmit_fixed: Called on receive pipe %d\n",
+                       pipe);
                return;
        }
 
@@ -1032,28 +1020,36 @@ static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
        if (dbri->pipes[pipe].sdp & D_SDP_MSB)
                data = reverse_bytes(data, dbri->pipes[pipe].length);
 
-       cmd = dbri_cmdlock(dbri, GetLock);
+       cmd = dbri_cmdlock(dbri, 3);
 
        *(cmd++) = DBRI_CMD(D_SSP, 0, pipe);
        *(cmd++) = data;
+       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+
+       spin_lock_irqsave(&dbri->lock, flags);
+       dbri_cmdsend(dbri, cmd, 3);
+       spin_unlock_irqrestore(&dbri->lock, flags);
+       dbri_cmdwait(dbri);
 
-       dbri_cmdsend(dbri, cmd);
 }
 
-static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
+static void recv_fixed(struct snd_dbri *dbri, int pipe, volatile __u32 *ptr)
 {
-       if (pipe < 16 || pipe > 31) {
-               printk(KERN_ERR "DBRI: recv_fixed called with illegal pipe number\n");
+       if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
+               printk(KERN_ERR "DBRI: recv_fixed called with "
+                       "illegal pipe number\n");
                return;
        }
 
        if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
-               printk(KERN_ERR "DBRI: recv_fixed called on non-fixed pipe %d\n", pipe);
+               printk(KERN_ERR "DBRI: recv_fixed called on "
+                       "non-fixed pipe %d\n", pipe);
                return;
        }
 
        if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
-               printk(KERN_ERR "DBRI: recv_fixed called on transmit pipe %d\n", pipe);
+               printk(KERN_ERR "DBRI: recv_fixed called on "
+                       "transmit pipe %d\n", pipe);
                return;
        }
 
@@ -1071,12 +1067,16 @@ static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
  * and work by building chains of descriptors which identify the
  * data buffers.  Buffers too large for a single descriptor will
  * be spread across multiple descriptors.
+ *
+ * All descriptors create a ring buffer.
+ *
+ * Lock must be held before calling this.
  */
-static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period)
+static int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period)
 {
        struct dbri_streaminfo *info = &dbri->stream_info[streamno];
        __u32 dvma_buffer;
-       int desc = 0;
+       int desc;
        int len;
        int first_desc = -1;
        int last_desc = -1;
@@ -1097,21 +1097,23 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
 
        if (streamno == DBRI_PLAY) {
                if (!(dbri->pipes[info->pipe].sdp & D_SDP_TO_SER)) {
-                       printk(KERN_ERR "DBRI: setup_descs: Called on receive pipe %d\n",
-                              info->pipe);
+                       printk(KERN_ERR "DBRI: setup_descs: "
+                               "Called on receive pipe %d\n", info->pipe);
                        return -2;
                }
        } else {
                if (dbri->pipes[info->pipe].sdp & D_SDP_TO_SER) {
-                       printk(KERN_ERR 
+                       printk(KERN_ERR
                            "DBRI: setup_descs: Called on transmit pipe %d\n",
                             info->pipe);
                        return -2;
                }
-               /* Should be able to queue multiple buffers to receive on a pipe */
+               /* Should be able to queue multiple buffers
+                * to receive on a pipe
+                */
                if (pipe_active(dbri, info->pipe)) {
-                       printk(KERN_ERR "DBRI: recv_on_pipe: Called on active pipe %d\n",
-                              info->pipe);
+                       printk(KERN_ERR "DBRI: recv_on_pipe: "
+                               "Called on active pipe %d\n", info->pipe);
                        return -2;
                }
 
@@ -1119,49 +1121,59 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
                len &= ~3;
        }
 
+       /* Free descriptors if pipe has any */
+       desc = dbri->pipes[info->pipe].first_desc;
+       if (desc >= 0)
+               do {
+                       dbri->dma->desc[desc].ba = 0;
+                       dbri->dma->desc[desc].nda = 0;
+                       desc = dbri->next_desc[desc];
+               } while (desc != -1 &&
+                        desc != dbri->pipes[info->pipe].first_desc);
+
+       dbri->pipes[info->pipe].desc = -1;
+       dbri->pipes[info->pipe].first_desc = -1;
+
+       desc = 0;
        while (len > 0) {
                int mylen;
 
                for (; desc < DBRI_NO_DESCS; desc++) {
-                       if (!dbri->descs[desc].inuse)
+                       if (!dbri->dma->desc[desc].ba)
                                break;
                }
+
                if (desc == DBRI_NO_DESCS) {
                        printk(KERN_ERR "DBRI: setup_descs: No descriptors\n");
                        return -1;
                }
 
-               if (len > DBRI_TD_MAXCNT) {
-                       mylen = DBRI_TD_MAXCNT; /* 8KB - 1 */
-               } else {
+               if (len > DBRI_TD_MAXCNT)
+                       mylen = DBRI_TD_MAXCNT; /* 8KB - 4 */
+               else
                        mylen = len;
-               }
-               if (mylen > period) {
+
+               if (mylen > period)
                        mylen = period;
-               }
 
-               dbri->descs[desc].inuse = 1;
-               dbri->descs[desc].next = -1;
+               dbri->next_desc[desc] = -1;
                dbri->dma->desc[desc].ba = dvma_buffer;
                dbri->dma->desc[desc].nda = 0;
 
                if (streamno == DBRI_PLAY) {
-                       dbri->descs[desc].len = mylen;
                        dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen);
                        dbri->dma->desc[desc].word4 = 0;
-                       if (first_desc != -1)
-                               dbri->dma->desc[desc].word1 |= DBRI_TD_M;
+                       dbri->dma->desc[desc].word1 |= DBRI_TD_F | DBRI_TD_B;
                } else {
-                       dbri->descs[desc].len = 0;
                        dbri->dma->desc[desc].word1 = 0;
                        dbri->dma->desc[desc].word4 =
                            DBRI_RD_B | DBRI_RD_BCNT(mylen);
                }
 
-               if (first_desc == -1) {
+               if (first_desc == -1)
                        first_desc = desc;
-               else {
-                       dbri->descs[last_desc].next = desc;
+               else {
+                       dbri->next_desc[last_desc] = desc;
                        dbri->dma->desc[last_desc].nda =
                            dbri->dma_dvma + dbri_dma_off(desc, desc);
                }
@@ -1172,25 +1184,29 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
        }
 
        if (first_desc == -1 || last_desc == -1) {
-               printk(KERN_ERR "DBRI: setup_descs: Not enough descriptors available\n");
+               printk(KERN_ERR "DBRI: setup_descs: "
+                       " Not enough descriptors available\n");
                return -1;
        }
 
-       dbri->dma->desc[last_desc].word1 &= ~DBRI_TD_M;
-       if (streamno == DBRI_PLAY) {
-               dbri->dma->desc[last_desc].word1 |=
-                   DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
-       }
+       dbri->dma->desc[last_desc].nda =
+           dbri->dma_dvma + dbri_dma_off(desc, first_desc);
+       dbri->next_desc[last_desc] = first_desc;
        dbri->pipes[info->pipe].first_desc = first_desc;
        dbri->pipes[info->pipe].desc = first_desc;
 
-       for (desc = first_desc; desc != -1; desc = dbri->descs[desc].next) {
+#ifdef DBRI_DEBUG
+       for (desc = first_desc; desc != -1;) {
                dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n",
                        desc,
                        dbri->dma->desc[desc].word1,
                        dbri->dma->desc[desc].ba,
                        dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4);
+                       desc = dbri->next_desc[desc];
+                       if (desc == first_desc)
+                               break;
        }
+#endif
        return 0;
 }
 
@@ -1207,56 +1223,31 @@ multiplexed serial interface which the DBRI can operate in either master
 
 enum master_or_slave { CHImaster, CHIslave };
 
-static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_slave,
+/*
+ * Lock must not be held before calling it.
+ */
+static void reset_chi(struct snd_dbri *dbri,
+                     enum master_or_slave master_or_slave,
                      int bits_per_frame)
 {
-       volatile s32 *cmd;
+       s32 *cmd;
        int val;
-       static int chi_initialized = 0; /* FIXME: mutex? */
-
-       if (!chi_initialized) {
 
-               cmd = dbri_cmdlock(dbri, GetLock);
-
-               /* Set CHI Anchor: Pipe 16 */
-
-               val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16);
-               *(cmd++) = DBRI_CMD(D_DTS, 0, val);
-               *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
-               *(cmd++) = 0;
+       /* Set CHI Anchor: Pipe 16 */
 
-               val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16);
-               *(cmd++) = DBRI_CMD(D_DTS, 0, val);
-               *(cmd++) = 0;
-               *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
-
-               dbri->pipes[16].sdp = 1;
-               dbri->pipes[16].nextpipe = 16;
-               dbri->chi_in_pipe = 16;
-               dbri->chi_out_pipe = 16;
-
-#if 0
-               chi_initialized++;
-#endif
-       } else {
-               int pipe;
-
-               for (pipe = dbri->chi_in_pipe;
-                    pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
-                       unlink_time_slot(dbri, pipe, PIPEinput,
-                                        16, dbri->pipes[pipe].nextpipe);
-               }
-               for (pipe = dbri->chi_out_pipe;
-                    pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
-                       unlink_time_slot(dbri, pipe, PIPEoutput,
-                                        16, dbri->pipes[pipe].nextpipe);
-               }
+       cmd = dbri_cmdlock(dbri, 4);
+       val = D_DTS_VO | D_DTS_VI | D_DTS_INS
+               | D_DTS_PRVIN(16) | D_PIPE(16) | D_DTS_PRVOUT(16);
+       *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+       *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+       *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+       dbri_cmdsend(dbri, cmd, 4);
 
-               dbri->chi_in_pipe = 16;
-               dbri->chi_out_pipe = 16;
+       dbri->pipes[16].sdp = 1;
+       dbri->pipes[16].nextpipe = 16;
 
-               cmd = dbri_cmdlock(dbri, GetLock);
-       }
+       cmd = dbri_cmdlock(dbri, 4);
 
        if (master_or_slave == CHIslave) {
                /* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
@@ -1269,15 +1260,16 @@ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_sla
        } else {
                /* Setup DBRI for CHI Master - generate clock, FS
                 *
-                * BPF                          =  bits per 8 kHz frame
-                * 12.288 MHz / CHICM_divisor   = clock rate
-                * FD  =  1 - drive CHIFS on rising edge of CHICK
+                * BPF                          =  bits per 8 kHz frame
+                * 12.288 MHz / CHICM_divisor   = clock rate
+                * FD = 1 - drive CHIFS on rising edge of CHICK
                 */
                int clockrate = bits_per_frame * 8;
                int divisor = 12288 / clockrate;
 
                if (divisor > 255 || divisor * clockrate != 12288)
-                       printk(KERN_ERR "DBRI: illegal bits_per_frame in setup_chi\n");
+                       printk(KERN_ERR "DBRI: illegal bits_per_frame "
+                               "in setup_chi\n");
 
                *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
                                    | D_CHI_BPF(bits_per_frame));
@@ -1295,8 +1287,9 @@ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_sla
 
        *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
        *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN);
+       *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
 
-       dbri_cmdsend(dbri, cmd);
+       dbri_cmdsend(dbri, cmd, 4);
 }
 
 /*
@@ -1307,9 +1300,14 @@ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_sla
 In the standard SPARC audio configuration, the CS4215 codec is attached
 to the DBRI via the CHI interface and few of the DBRI's PIO pins.
 
+ * Lock must not be held before calling it.
+
 */
-static void cs4215_setup_pipes(struct snd_dbri * dbri)
+static __devinit void cs4215_setup_pipes(struct snd_dbri *dbri)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&dbri->lock, flags);
        /*
         * Data mode:
         * Pipe  4: Send timeslots 1-4 (audio data)
@@ -1320,9 +1318,9 @@ static void cs4215_setup_pipes(struct snd_dbri * dbri)
         *          not relevant for us (only for doublechecking).
         *
         * Control mode:
-        * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+        * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only)
         * Pipe 18: Receive timeslot 1 (clb).
-        * Pipe 19: Receive timeslot 7 (version). 
+        * Pipe 19: Receive timeslot 7 (version).
         */
 
        setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
@@ -1333,9 +1331,12 @@ static void cs4215_setup_pipes(struct snd_dbri * dbri)
        setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
        setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
        setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+       spin_unlock_irqrestore(&dbri->lock, flags);
+
+       dbri_cmdwait(dbri);
 }
 
-static int cs4215_init_data(struct cs4215 *mm)
+static __devinit int cs4215_init_data(struct cs4215 *mm)
 {
        /*
         * No action, memory resetting only.
@@ -1364,12 +1365,12 @@ static int cs4215_init_data(struct cs4215 *mm)
        mm->status = 0;
        mm->version = 0xff;
        mm->precision = 8;      /* For ULAW */
-       mm->channels = 2;
+       mm->channels = 1;
 
        return 0;
 }
 
-static void cs4215_setdata(struct snd_dbri * dbri, int muted)
+static void cs4215_setdata(struct snd_dbri *dbri, int muted)
 {
        if (muted) {
                dbri->mm.data[0] |= 63;
@@ -1379,16 +1380,8 @@ static void cs4215_setdata(struct snd_dbri * dbri, int muted)
        } else {
                /* Start by setting the playback attenuation. */
                struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
-               int left_gain = info->left_gain % 64;
-               int right_gain = info->right_gain % 64;
-
-               if (info->balance < DBRI_MID_BALANCE) {
-                       right_gain *= info->balance;
-                       right_gain /= DBRI_MID_BALANCE;
-               } else {
-                       left_gain *= DBRI_RIGHT_BALANCE - info->balance;
-                       left_gain /= DBRI_MID_BALANCE;
-               }
+               int left_gain = info->left_gain & 0x3f;
+               int right_gain = info->right_gain & 0x3f;
 
                dbri->mm.data[0] &= ~0x3f;      /* Reset the volume bits */
                dbri->mm.data[1] &= ~0x3f;
@@ -1397,8 +1390,8 @@ static void cs4215_setdata(struct snd_dbri * dbri, int muted)
 
                /* Now set the recording gain. */
                info = &dbri->stream_info[DBRI_REC];
-               left_gain = info->left_gain % 16;
-               right_gain = info->right_gain % 16;
+               left_gain = info->left_gain & 0xf;
+               right_gain = info->right_gain & 0xf;
                dbri->mm.data[2] |= CS4215_LG(left_gain);
                dbri->mm.data[3] |= CS4215_RG(right_gain);
        }
@@ -1409,10 +1402,11 @@ static void cs4215_setdata(struct snd_dbri * dbri, int muted)
 /*
  * Set the CS4215 to data mode.
  */
-static void cs4215_open(struct snd_dbri * dbri)
+static void cs4215_open(struct snd_dbri *dbri)
 {
        int data_width;
        u32 tmp;
+       unsigned long flags;
 
        dprintk(D_MM, "cs4215_open: %d channels, %d bits\n",
                dbri->mm.channels, dbri->mm.precision);
@@ -1437,6 +1431,7 @@ static void cs4215_open(struct snd_dbri * dbri)
         * bits.  The CS4215, it seems, observes TSIN (the delayed signal)
         * even if it's the CHI master.  Don't ask me...
         */
+       spin_lock_irqsave(&dbri->lock, flags);
        tmp = sbus_readl(dbri->regs + REG0);
        tmp &= ~(D_C);          /* Disable CHI */
        sbus_writel(tmp, dbri->regs + REG0);
@@ -1455,15 +1450,16 @@ static void cs4215_open(struct snd_dbri * dbri)
         */
        data_width = dbri->mm.channels * dbri->mm.precision;
 
-       link_time_slot(dbri, 20, PIPEoutput, 16, 32, dbri->mm.offset + 32);
-       link_time_slot(dbri, 4, PIPEoutput, 16, data_width, dbri->mm.offset);
-       link_time_slot(dbri, 6, PIPEinput, 16, data_width, dbri->mm.offset);
-       link_time_slot(dbri, 21, PIPEinput, 16, 16, dbri->mm.offset + 40);
+       link_time_slot(dbri, 4, 16, 16, data_width, dbri->mm.offset);
+       link_time_slot(dbri, 20, 4, 16, 32, dbri->mm.offset + 32);
+       link_time_slot(dbri, 6, 16, 16, data_width, dbri->mm.offset);
+       link_time_slot(dbri, 21, 6, 16, 16, dbri->mm.offset + 40);
 
        /* FIXME: enable CHI after _setdata? */
        tmp = sbus_readl(dbri->regs + REG0);
        tmp |= D_C;             /* Enable CHI */
        sbus_writel(tmp, dbri->regs + REG0);
+       spin_unlock_irqrestore(&dbri->lock, flags);
 
        cs4215_setdata(dbri, 0);
 }
@@ -1471,10 +1467,11 @@ static void cs4215_open(struct snd_dbri * dbri)
 /*
  * Send the control information (i.e. audio format)
  */
-static int cs4215_setctrl(struct snd_dbri * dbri)
+static int cs4215_setctrl(struct snd_dbri *dbri)
 {
        int i, val;
        u32 tmp;
+       unsigned long flags;
 
        /* FIXME - let the CPU do something useful during these delays */
 
@@ -1511,6 +1508,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
         * done in hardware by a TI 248 that delays the DBRI->4215
         * frame sync signal by eight clock cycles.  Anybody know why?
         */
+       spin_lock_irqsave(&dbri->lock, flags);
        tmp = sbus_readl(dbri->regs + REG0);
        tmp &= ~D_C;            /* Disable CHI */
        sbus_writel(tmp, dbri->regs + REG0);
@@ -1519,26 +1517,29 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
 
        /*
         * Control mode:
-        * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+        * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only)
         * Pipe 18: Receive timeslot 1 (clb).
-        * Pipe 19: Receive timeslot 7 (version). 
+        * Pipe 19: Receive timeslot 7 (version).
         */
 
-       link_time_slot(dbri, 17, PIPEoutput, 16, 32, dbri->mm.offset);
-       link_time_slot(dbri, 18, PIPEinput, 16, 8, dbri->mm.offset);
-       link_time_slot(dbri, 19, PIPEinput, 16, 8, dbri->mm.offset + 48);
+       link_time_slot(dbri, 17, 16, 16, 32, dbri->mm.offset);
+       link_time_slot(dbri, 18, 16, 16, 8, dbri->mm.offset);
+       link_time_slot(dbri, 19, 18, 16, 8, dbri->mm.offset + 48);
+       spin_unlock_irqrestore(&dbri->lock, flags);
 
        /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
        dbri->mm.ctrl[0] &= ~CS4215_CLB;
        xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
 
+       spin_lock_irqsave(&dbri->lock, flags);
        tmp = sbus_readl(dbri->regs + REG0);
        tmp |= D_C;             /* Enable CHI */
        sbus_writel(tmp, dbri->regs + REG0);
+       spin_unlock_irqrestore(&dbri->lock, flags);
 
-       for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) {
+       for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i)
                msleep_interruptible(1);
-       }
+
        if (i == 0) {
                dprintk(D_MM, "CS4215 didn't respond to CLB (0x%02x)\n",
                        dbri->mm.status);
@@ -1570,7 +1571,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
  * As part of the process we resend the settings for the data
  * timeslots as well.
  */
-static int cs4215_prepare(struct snd_dbri * dbri, unsigned int rate,
+static int cs4215_prepare(struct snd_dbri *dbri, unsigned int rate,
                          snd_pcm_format_t format, unsigned int channels)
 {
        int freq_idx;
@@ -1614,8 +1615,7 @@ static int cs4215_prepare(struct snd_dbri * dbri, unsigned int rate,
            CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal;
 
        dbri->mm.channels = channels;
-       /* Stereo bit: 8 bit stereo not working yet. */
-       if ((channels > 1) && (dbri->mm.precision == 16))
+       if (channels == 2)
                dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
 
        ret = cs4215_setctrl(dbri);
@@ -1628,7 +1628,7 @@ static int cs4215_prepare(struct snd_dbri * dbri, unsigned int rate,
 /*
  *
  */
-static int cs4215_init(struct snd_dbri * dbri)
+static __devinit int cs4215_init(struct snd_dbri *dbri)
 {
        u32 reg2 = sbus_readl(dbri->regs + REG2);
        dprintk(D_MM, "cs4215_init: reg2=0x%x\n", reg2);
@@ -1655,7 +1655,6 @@ static int cs4215_init(struct snd_dbri * dbri)
        }
 
        cs4215_setup_pipes(dbri);
-
        cs4215_init_data(&dbri->mm);
 
        /* Enable capture of the status & version timeslots. */
@@ -1684,88 +1683,73 @@ buffer and calls dbri_process_one_interrupt() for each interrupt word.
 Complicated interrupts are handled by dedicated functions (which
 appear first in this file).  Any pending interrupts can be serviced by
 calling dbri_process_interrupt_buffer(), which works even if the CPU's
-interrupts are disabled.  This function is used by dbri_cmdlock()
-to make sure we're synced up with the chip before each command sequence,
-even if we're running cli'ed.
+interrupts are disabled.
 
 */
 
 /* xmit_descs()
  *
- * Transmit the current TD's for recording/playing, if needed.
+ * Starts transmitting the current TD's for recording/playing.
  * For playback, ALSA has filled the DMA memory with new data (we hope).
  */
-static void xmit_descs(unsigned long data)
+static void xmit_descs(struct snd_dbri *dbri)
 {
-       struct snd_dbri *dbri = (struct snd_dbri *) data;
        struct dbri_streaminfo *info;
-       volatile s32 *cmd;
+       s32 *cmd;
        unsigned long flags;
        int first_td;
 
        if (dbri == NULL)
                return;         /* Disabled */
 
-       /* First check the recording stream for buffer overflow */
        info = &dbri->stream_info[DBRI_REC];
        spin_lock_irqsave(&dbri->lock, flags);
 
-       if ((info->left >= info->size) && (info->pipe >= 0)) {
+       if (info->pipe >= 0) {
                first_td = dbri->pipes[info->pipe].first_desc;
 
                dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td);
 
                /* Stream could be closed by the time we run. */
-               if (first_td < 0) {
-                       goto play;
+               if (first_td >= 0) {
+                       cmd = dbri_cmdlock(dbri, 2);
+                       *(cmd++) = DBRI_CMD(D_SDP, 0,
+                                           dbri->pipes[info->pipe].sdp
+                                           | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+                       *(cmd++) = dbri->dma_dvma +
+                                  dbri_dma_off(desc, first_td);
+                       dbri_cmdsend(dbri, cmd, 2);
+
+                       /* Reset our admin of the pipe. */
+                       dbri->pipes[info->pipe].desc = first_td;
                }
-
-               cmd = dbri_cmdlock(dbri, NoGetLock);
-               *(cmd++) = DBRI_CMD(D_SDP, 0,
-                                   dbri->pipes[info->pipe].sdp
-                                   | D_SDP_P | D_SDP_EVERY | D_SDP_C);
-               *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
-               dbri_cmdsend(dbri, cmd);
-
-               /* Reset our admin of the pipe & bytes read. */
-               dbri->pipes[info->pipe].desc = first_td;
-               info->left = 0;
        }
 
-play:
-       spin_unlock_irqrestore(&dbri->lock, flags);
-
-       /* Now check the playback stream for buffer underflow */
        info = &dbri->stream_info[DBRI_PLAY];
-       spin_lock_irqsave(&dbri->lock, flags);
 
-       if ((info->left <= 0) && (info->pipe >= 0)) {
+       if (info->pipe >= 0) {
                first_td = dbri->pipes[info->pipe].first_desc;
 
                dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td);
 
                /* Stream could be closed by the time we run. */
-               if (first_td < 0) {
-                       spin_unlock_irqrestore(&dbri->lock, flags);
-                       return;
+               if (first_td >= 0) {
+                       cmd = dbri_cmdlock(dbri, 2);
+                       *(cmd++) = DBRI_CMD(D_SDP, 0,
+                                           dbri->pipes[info->pipe].sdp
+                                           | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+                       *(cmd++) = dbri->dma_dvma +
+                                  dbri_dma_off(desc, first_td);
+                       dbri_cmdsend(dbri, cmd, 2);
+
+                       /* Reset our admin of the pipe. */
+                       dbri->pipes[info->pipe].desc = first_td;
                }
-
-               cmd = dbri_cmdlock(dbri, NoGetLock);
-               *(cmd++) = DBRI_CMD(D_SDP, 0,
-                                   dbri->pipes[info->pipe].sdp
-                                   | D_SDP_P | D_SDP_EVERY | D_SDP_C);
-               *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
-               dbri_cmdsend(dbri, cmd);
-
-               /* Reset our admin of the pipe & bytes written. */
-               dbri->pipes[info->pipe].desc = first_td;
-               info->left = info->size;
        }
+
        spin_unlock_irqrestore(&dbri->lock, flags);
 }
 
-static DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0);
-
 /* transmission_complete_intr()
  *
  * Called by main interrupt handler when DBRI signals transmission complete
@@ -1775,20 +1759,17 @@ static DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0);
  * them as available. Stops when the first descriptor is found without
  * TBC (Transmit Buffer Complete) set, or we've run through them all.
  *
- * The DMA buffers are not released, but re-used. Since the transmit buffer
- * descriptors are not clobbered, they can be re-submitted as is. This is
- * done by the xmit_descs() tasklet above since that could take longer.
+ * The DMA buffers are not released. They form a ring buffer and
+ * they are filled by ALSA while others are transmitted by DMA.
+ *
  */
 
-static void transmission_complete_intr(struct snd_dbri * dbri, int pipe)
+static void transmission_complete_intr(struct snd_dbri *dbri, int pipe)
 {
-       struct dbri_streaminfo *info;
-       int td;
+       struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
+       int td = dbri->pipes[pipe].desc;
        int status;
 
-       info = &dbri->stream_info[DBRI_PLAY];
-
-       td = dbri->pipes[pipe].desc;
        while (td >= 0) {
                if (td >= DBRI_NO_DESCS) {
                        printk(KERN_ERR "DBRI: invalid td on pipe %d\n", pipe);
@@ -1796,41 +1777,25 @@ static void transmission_complete_intr(struct snd_dbri * dbri, int pipe)
                }
 
                status = DBRI_TD_STATUS(dbri->dma->desc[td].word4);
-               if (!(status & DBRI_TD_TBC)) {
+               if (!(status & DBRI_TD_TBC))
                        break;
-               }
 
                dprintk(D_INT, "TD %d, status 0x%02x\n", td, status);
 
                dbri->dma->desc[td].word4 = 0;  /* Reset it for next time. */
-               info->offset += dbri->descs[td].len;
-               info->left -= dbri->descs[td].len;
-
-               /* On the last TD, transmit them all again. */
-               if (dbri->descs[td].next == -1) {
-                       if (info->left > 0) {
-                               printk(KERN_WARNING
-                                      "%d bytes left after last transfer.\n",
-                                      info->left);
-                               info->left = 0;
-                       }
-                       tasklet_schedule(&xmit_descs_task);
-               }
+               info->offset += DBRI_RD_CNT(dbri->dma->desc[td].word1);
 
-               td = dbri->descs[td].next;
+               td = dbri->next_desc[td];
                dbri->pipes[pipe].desc = td;
        }
 
        /* Notify ALSA */
-       if (spin_is_locked(&dbri->lock)) {
-               spin_unlock(&dbri->lock);
-               snd_pcm_period_elapsed(info->substream);
-               spin_lock(&dbri->lock);
-       } else
-               snd_pcm_period_elapsed(info->substream);
+       spin_unlock(&dbri->lock);
+       snd_pcm_period_elapsed(info->substream);
+       spin_lock(&dbri->lock);
 }
 
-static void reception_complete_intr(struct snd_dbri * dbri, int pipe)
+static void reception_complete_intr(struct snd_dbri *dbri, int pipe)
 {
        struct dbri_streaminfo *info;
        int rd = dbri->pipes[pipe].desc;
@@ -1841,40 +1806,25 @@ static void reception_complete_intr(struct snd_dbri * dbri, int pipe)
                return;
        }
 
-       dbri->descs[rd].inuse = 0;
-       dbri->pipes[pipe].desc = dbri->descs[rd].next;
+       dbri->pipes[pipe].desc = dbri->next_desc[rd];
        status = dbri->dma->desc[rd].word1;
        dbri->dma->desc[rd].word1 = 0;  /* Reset it for next time. */
 
        info = &dbri->stream_info[DBRI_REC];
        info->offset += DBRI_RD_CNT(status);
-       info->left += DBRI_RD_CNT(status);
 
        /* FIXME: Check status */
 
        dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n",
                rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status));
 
-       /* On the last TD, transmit them all again. */
-       if (dbri->descs[rd].next == -1) {
-               if (info->left > info->size) {
-                       printk(KERN_WARNING
-                              "%d bytes recorded in %d size buffer.\n",
-                              info->left, info->size);
-               }
-               tasklet_schedule(&xmit_descs_task);
-       }
-
        /* Notify ALSA */
-       if (spin_is_locked(&dbri->lock)) {
-               spin_unlock(&dbri->lock);
-               snd_pcm_period_elapsed(info->substream);
-               spin_lock(&dbri->lock);
-       } else
-               snd_pcm_period_elapsed(info->substream);
+       spin_unlock(&dbri->lock);
+       snd_pcm_period_elapsed(info->substream);
+       spin_lock(&dbri->lock);
 }
 
-static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
+static void dbri_process_one_interrupt(struct snd_dbri *dbri, int x)
 {
        int val = D_INTR_GETVAL(x);
        int channel = D_INTR_GETCHAN(x);
@@ -1892,16 +1842,11 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
                        channel, code, rval);
        }
 
-       if (channel == D_INTR_CMD && command == D_WAIT) {
-               dbri->wait_ackd = val;
-               if (dbri->wait_send != val) {
-                       printk(KERN_ERR "Processing wait command %d when %d was send.\n",
-                              val, dbri->wait_send);
-               }
-               return;
-       }
-
        switch (code) {
+       case D_INTR_CMDI:
+               if (command != D_WAIT)
+                       printk(KERN_ERR "DBRI: Command read interrupt\n");
+               break;
        case D_INTR_BRDY:
                reception_complete_intr(dbri, channel);
                break;
@@ -1914,8 +1859,10 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
                 * resend SDP command with clear pipe bit (C) set
                 */
                {
-                       volatile s32 *cmd;
-
+       /* FIXME: do something useful in case of underrun */
+                       printk(KERN_ERR "DBRI: Underrun error\n");
+#if 0
+                       s32 *cmd;
                        int pipe = channel;
                        int td = dbri->pipes[pipe].desc;
 
@@ -1926,6 +1873,7 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
                                            | D_SDP_P | D_SDP_C | D_SDP_2SAME);
                        *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td);
                        dbri_cmdsend(dbri, cmd);
+#endif
                }
                break;
        case D_INTR_FXDT:
@@ -1946,28 +1894,23 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
 /* dbri_process_interrupt_buffer advances through the DBRI's interrupt
  * buffer until it finds a zero word (indicating nothing more to do
  * right now).  Non-zero words require processing and are handed off
- * to dbri_process_one_interrupt AFTER advancing the pointer.  This
- * order is important since we might recurse back into this function
- * and need to make sure the pointer has been advanced first.
+ * to dbri_process_one_interrupt AFTER advancing the pointer.
  */
-static void dbri_process_interrupt_buffer(struct snd_dbri * dbri)
+static void dbri_process_interrupt_buffer(struct snd_dbri *dbri)
 {
        s32 x;
 
        while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
                dbri->dma->intr[dbri->dbri_irqp] = 0;
                dbri->dbri_irqp++;
-               if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
+               if (dbri->dbri_irqp == DBRI_INT_BLK)
                        dbri->dbri_irqp = 1;
-               else if ((dbri->dbri_irqp & (DBRI_INT_BLK - 1)) == 0)
-                       dbri->dbri_irqp++;
 
                dbri_process_one_interrupt(dbri, x);
        }
 }
 
-static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id,
-                                     struct pt_regs *regs)
+static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id)
 {
        struct snd_dbri *dbri = dev_id;
        static int errcnt = 0;
@@ -2020,8 +1963,6 @@ static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id,
 
        dbri_process_interrupt_buffer(dbri);
 
-       /* FIXME: Write 0 into regs to ACK interrupt */
-
        spin_unlock(&dbri->lock);
 
        return IRQ_HANDLED;
@@ -2031,26 +1972,60 @@ static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id,
                PCM Interface
 ****************************************************************************/
 static struct snd_pcm_hardware snd_dbri_pcm_hw = {
-       .info                   = (SNDRV_PCM_INFO_MMAP |
-                                  SNDRV_PCM_INFO_INTERLEAVED |
-                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                                  SNDRV_PCM_INFO_MMAP_VALID),
-       .formats                = SNDRV_PCM_FMTBIT_MU_LAW |
-                                 SNDRV_PCM_FMTBIT_A_LAW |
-                                 SNDRV_PCM_FMTBIT_U8 |
-                                 SNDRV_PCM_FMTBIT_S16_BE,
-       .rates                  = SNDRV_PCM_RATE_8000_48000,
-       .rate_min               = 8000,
+       .info           SNDRV_PCM_INFO_MMAP |
+                         SNDRV_PCM_INFO_INTERLEAVED |
+                         SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                         SNDRV_PCM_INFO_MMAP_VALID,
+       .formats        = SNDRV_PCM_FMTBIT_MU_LAW |
+                         SNDRV_PCM_FMTBIT_A_LAW |
+                         SNDRV_PCM_FMTBIT_U8 |
+                         SNDRV_PCM_FMTBIT_S16_BE,
+       .rates          = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_5512,
+       .rate_min               = 5512,
        .rate_max               = 48000,
        .channels_min           = 1,
        .channels_max           = 2,
-       .buffer_bytes_max       = (64 * 1024),
+       .buffer_bytes_max       = 64 * 1024,
        .period_bytes_min       = 1,
        .period_bytes_max       = DBRI_TD_MAXCNT,
        .periods_min            = 1,
        .periods_max            = 1024,
 };
 
+static int snd_hw_rule_format(struct snd_pcm_hw_params *params,
+                             struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c = hw_param_interval(params,
+                               SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_mask fmt;
+
+       snd_mask_any(&fmt);
+       if (c->min > 1) {
+               fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_BE;
+               return snd_mask_refine(f, &fmt);
+       }
+       return 0;
+}
+
+static int snd_hw_rule_channels(struct snd_pcm_hw_params *params,
+                               struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c = hw_param_interval(params,
+                               SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_interval ch;
+
+       snd_interval_any(&ch);
+       if (!(f->bits[0] & SNDRV_PCM_FMTBIT_S16_BE)) {
+               ch.min = 1;
+               ch.max = 1;
+               ch.integer = 1;
+               return snd_interval_refine(c, &ch);
+       }
+       return 0;
+}
+
 static int snd_dbri_open(struct snd_pcm_substream *substream)
 {
        struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
@@ -2063,12 +2038,19 @@ static int snd_dbri_open(struct snd_pcm_substream *substream)
 
        spin_lock_irqsave(&dbri->lock, flags);
        info->substream = substream;
-       info->left = 0;
        info->offset = 0;
        info->dvma_buffer = 0;
        info->pipe = -1;
        spin_unlock_irqrestore(&dbri->lock, flags);
 
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                           snd_hw_rule_format, NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+                           -1);
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+                           snd_hw_rule_channels, NULL,
+                           SNDRV_PCM_HW_PARAM_CHANNELS,
+                           -1);
+
        cs4215_open(dbri);
 
        return 0;
@@ -2081,7 +2063,6 @@ static int snd_dbri_close(struct snd_pcm_substream *substream)
 
        dprintk(D_USR, "close audio output.\n");
        info->substream = NULL;
-       info->left = 0;
        info->offset = 0;
 
        return 0;
@@ -2113,14 +2094,15 @@ static int snd_dbri_hw_params(struct snd_pcm_substream *substream,
         */
        if (info->dvma_buffer == 0) {
                if (DBRI_STREAMNO(substream) == DBRI_PLAY)
-                       direction = SBUS_DMA_TODEVICE;
+                       direction = DMA_TO_DEVICE;
                else
-                       direction = SBUS_DMA_FROMDEVICE;
+                       direction = DMA_FROM_DEVICE;
 
-               info->dvma_buffer = sbus_map_single(dbri->sdev,
-                                       runtime->dma_area,
-                                       params_buffer_bytes(hw_params),
-                                       direction);
+               info->dvma_buffer =
+                       dma_map_single(&dbri->op->dev,
+                                      runtime->dma_area,
+                                      params_buffer_bytes(hw_params),
+                                      direction);
        }
 
        direction = params_buffer_bytes(hw_params);
@@ -2134,21 +2116,25 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *substream)
        struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
        struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
        int direction;
+
        dprintk(D_USR, "hw_free.\n");
 
        /* hw_free can get called multiple times. Only unmap the DMA once.
         */
        if (info->dvma_buffer) {
                if (DBRI_STREAMNO(substream) == DBRI_PLAY)
-                       direction = SBUS_DMA_TODEVICE;
+                       direction = DMA_TO_DEVICE;
                else
-                       direction = SBUS_DMA_FROMDEVICE;
+                       direction = DMA_FROM_DEVICE;
 
-               sbus_unmap_single(dbri->sdev, info->dvma_buffer,
-                                 substream->runtime->buffer_size, direction);
+               dma_unmap_single(&dbri->op->dev, info->dvma_buffer,
+                                substream->runtime->buffer_size, direction);
                info->dvma_buffer = 0;
        }
-       info->pipe = -1;
+       if (info->pipe != -1) {
+               reset_pipe(dbri, info->pipe);
+               info->pipe = -1;
+       }
 
        return snd_pcm_lib_free_pages(substream);
 }
@@ -2157,27 +2143,23 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
        struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int ret;
 
        info->size = snd_pcm_lib_buffer_bytes(substream);
        if (DBRI_STREAMNO(substream) == DBRI_PLAY)
                info->pipe = 4; /* Send pipe */
-       else {
+       else
                info->pipe = 6; /* Receive pipe */
-               info->left = info->size;        /* To trigger submittal */
-       }
 
        spin_lock_irq(&dbri->lock);
+       info->offset = 0;
 
-       /* Setup the all the transmit/receive desciptors to cover the
+       /* Setup the all the transmit/receive descriptors to cover the
         * whole DMA buffer.
         */
        ret = setup_descs(dbri, DBRI_STREAMNO(substream),
                          snd_pcm_lib_period_bytes(substream));
 
-       runtime->stop_threshold = DBRI_TD_MAXCNT / runtime->channels;
-
        spin_unlock_irq(&dbri->lock);
 
        dprintk(D_USR, "prepare audio output. %d bytes\n", info->size);
@@ -2194,14 +2176,11 @@ static int snd_dbri_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_START:
                dprintk(D_USR, "start audio, period is %d bytes\n",
                        (int)snd_pcm_lib_period_bytes(substream));
-               /* Enable & schedule the tasklet that re-submits the TDs. */
-               xmit_descs_task.data = (unsigned long)dbri;
-               tasklet_schedule(&xmit_descs_task);
+               /* Re-submit the TDs. */
+               xmit_descs(dbri);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                dprintk(D_USR, "stop audio.\n");
-               /* Make the tasklet bail out immediately. */
-               xmit_descs_task.data = 0;
                reset_pipe(dbri, info->pipe);
                break;
        default:
@@ -2219,8 +2198,8 @@ static snd_pcm_uframes_t snd_dbri_pointer(struct snd_pcm_substream *substream)
 
        ret = bytes_to_frames(substream->runtime, info->offset)
                % substream->runtime->buffer_size;
-       dprintk(D_USR, "I/O pointer: %ld frames, %d bytes left.\n",
-               ret, info->left);
+       dprintk(D_USR, "I/O pointer: %ld frames of %ld.\n",
+               ret, substream->runtime->buffer_size);
        return ret;
 }
 
@@ -2235,33 +2214,30 @@ static struct snd_pcm_ops snd_dbri_ops = {
        .pointer = snd_dbri_pointer,
 };
 
-static int __devinit snd_dbri_pcm(struct snd_dbri * dbri)
+static int __devinit snd_dbri_pcm(struct snd_card *card)
 {
        struct snd_pcm *pcm;
        int err;
 
-       if ((err = snd_pcm_new(dbri->card,
+       if ((err = snd_pcm_new(card,
                               /* ID */             "sun_dbri",
                               /* device */         0,
                               /* playback count */ 1,
                               /* capture count */  1, &pcm)) < 0)
                return err;
-       snd_assert(pcm != NULL, return -EINVAL);
 
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dbri_ops);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_dbri_ops);
 
-       pcm->private_data = dbri;
+       pcm->private_data = card->private_data;
        pcm->info_flags = 0;
-       strcpy(pcm->name, dbri->card->shortname);
-       dbri->pcm = pcm;
+       strcpy(pcm->name, card->shortname);
 
        if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm,
                        SNDRV_DMA_TYPE_CONTINUOUS,
                        snd_dma_continuous_data(GFP_KERNEL),
-                       64 * 1024, 64 * 1024)) < 0) {
+                       64 * 1024, 64 * 1024)) < 0)
                return err;
-       }
 
        return 0;
 }
@@ -2276,11 +2252,10 @@ static int snd_cs4215_info_volume(struct snd_kcontrol *kcontrol,
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 2;
        uinfo->value.integer.min = 0;
-       if (kcontrol->private_value == DBRI_PLAY) {
+       if (kcontrol->private_value == DBRI_PLAY)
                uinfo->value.integer.max = DBRI_MAX_VOLUME;
-       } else {
+       else
                uinfo->value.integer.max = DBRI_MAX_GAIN;
-       }
        return 0;
 }
 
@@ -2289,9 +2264,10 @@ static int snd_cs4215_get_volume(struct snd_kcontrol *kcontrol,
 {
        struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
        struct dbri_streaminfo *info;
-       snd_assert(dbri != NULL, return -EINVAL);
+
+       if (snd_BUG_ON(!dbri))
+               return -EINVAL;
        info = &dbri->stream_info[kcontrol->private_value];
-       snd_assert(info != NULL, return -EINVAL);
 
        ucontrol->value.integer.value[0] = info->left_gain;
        ucontrol->value.integer.value[1] = info->right_gain;
@@ -2302,29 +2278,36 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
-       struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value];
-       unsigned long flags;
+       struct dbri_streaminfo *info =
+                               &dbri->stream_info[kcontrol->private_value];
+       unsigned int vol[2];
        int changed = 0;
 
-       if (info->left_gain != ucontrol->value.integer.value[0]) {
-               info->left_gain = ucontrol->value.integer.value[0];
+       vol[0] = ucontrol->value.integer.value[0];
+       vol[1] = ucontrol->value.integer.value[1];
+       if (kcontrol->private_value == DBRI_PLAY) {
+               if (vol[0] > DBRI_MAX_VOLUME || vol[1] > DBRI_MAX_VOLUME)
+                       return -EINVAL;
+       } else {
+               if (vol[0] > DBRI_MAX_GAIN || vol[1] > DBRI_MAX_GAIN)
+                       return -EINVAL;
+       }
+
+       if (info->left_gain != vol[0]) {
+               info->left_gain = vol[0];
                changed = 1;
        }
-       if (info->right_gain != ucontrol->value.integer.value[1]) {
-               info->right_gain = ucontrol->value.integer.value[1];
+       if (info->right_gain != vol[1]) {
+               info->right_gain = vol[1];
                changed = 1;
        }
-       if (changed == 1) {
+       if (changed) {
                /* First mute outputs, and wait 1/8000 sec (125 us)
                 * to make sure this takes.  This avoids clicking noises.
                 */
-               spin_lock_irqsave(&dbri->lock, flags);
-
                cs4215_setdata(dbri, 1);
                udelay(125);
                cs4215_setdata(dbri, 0);
-
-               spin_unlock_irqrestore(&dbri->lock, flags);
        }
        return changed;
 }
@@ -2350,20 +2333,20 @@ static int snd_cs4215_get_single(struct snd_kcontrol *kcontrol,
        int shift = (kcontrol->private_value >> 8) & 0xff;
        int mask = (kcontrol->private_value >> 16) & 0xff;
        int invert = (kcontrol->private_value >> 24) & 1;
-       snd_assert(dbri != NULL, return -EINVAL);
 
-       if (elem < 4) {
+       if (snd_BUG_ON(!dbri))
+               return -EINVAL;
+
+       if (elem < 4)
                ucontrol->value.integer.value[0] =
                    (dbri->mm.data[elem] >> shift) & mask;
-       } else {
+       else
                ucontrol->value.integer.value[0] =
                    (dbri->mm.ctrl[elem - 4] >> shift) & mask;
-       }
 
-       if (invert == 1) {
+       if (invert == 1)
                ucontrol->value.integer.value[0] =
                    mask - ucontrol->value.integer.value[0];
-       }
        return 0;
 }
 
@@ -2371,14 +2354,15 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
-       unsigned long flags;
        int elem = kcontrol->private_value & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0xff;
        int mask = (kcontrol->private_value >> 16) & 0xff;
        int invert = (kcontrol->private_value >> 24) & 1;
        int changed = 0;
        unsigned short val;
-       snd_assert(dbri != NULL, return -EINVAL);
+
+       if (snd_BUG_ON(!dbri))
+               return -EINVAL;
 
        val = (ucontrol->value.integer.value[0] & mask);
        if (invert == 1)
@@ -2404,13 +2388,9 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
                /* First mute outputs, and wait 1/8000 sec (125 us)
                 * to make sure this takes.  This avoids clicking noises.
                 */
-               spin_lock_irqsave(&dbri->lock, flags);
-
                cs4215_setdata(dbri, 1);
                udelay(125);
                cs4215_setdata(dbri, 0);
-
-               spin_unlock_irqrestore(&dbri->lock, flags);
        }
        return changed;
 }
@@ -2419,11 +2399,12 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
    timeslots. Shift is the bit offset in the timeslot, mask defines the
    number of bits. invert is a boolean for use with attenuation.
  */
-#define CS4215_SINGLE(xname, entry, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-  .info = snd_cs4215_info_single, \
-  .get = snd_cs4215_get_single, .put = snd_cs4215_put_single, \
-  .private_value = entry | (shift << 8) | (mask << 16) | (invert << 24) },
+#define CS4215_SINGLE(xname, entry, shift, mask, invert)       \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),                \
+  .info = snd_cs4215_info_single,                              \
+  .get = snd_cs4215_get_single, .put = snd_cs4215_put_single,  \
+  .private_value = (entry) | ((shift) << 8) | ((mask) << 16) | \
+                       ((invert) << 24) },
 
 static struct snd_kcontrol_new dbri_controls[] __devinitdata = {
        {
@@ -2452,28 +2433,27 @@ static struct snd_kcontrol_new dbri_controls[] __devinitdata = {
        CS4215_SINGLE("Mic boost", 4, 4, 1, 1)
 };
 
-#define NUM_CS4215_CONTROLS (sizeof(dbri_controls)/sizeof(struct snd_kcontrol_new))
-
-static int __init snd_dbri_mixer(struct snd_dbri * dbri)
+static int __devinit snd_dbri_mixer(struct snd_card *card)
 {
-       struct snd_card *card;
        int idx, err;
+       struct snd_dbri *dbri;
 
-       snd_assert(dbri != NULL && dbri->card != NULL, return -EINVAL);
+       if (snd_BUG_ON(!card || !card->private_data))
+               return -EINVAL;
+       dbri = card->private_data;
 
-       card = dbri->card;
        strcpy(card->mixername, card->shortname);
 
-       for (idx = 0; idx < NUM_CS4215_CONTROLS; idx++) {
-               if ((err = snd_ctl_add(card,
-                               snd_ctl_new1(&dbri_controls[idx], dbri))) < 0)
+       for (idx = 0; idx < ARRAY_SIZE(dbri_controls); idx++) {
+               err = snd_ctl_add(card,
+                               snd_ctl_new1(&dbri_controls[idx], dbri));
+               if (err < 0)
                        return err;
        }
 
        for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) {
                dbri->stream_info[idx].left_gain = 0;
                dbri->stream_info[idx].right_gain = 0;
-               dbri->stream_info[idx].balance = DBRI_MID_BALANCE;
        }
 
        return 0;
@@ -2482,7 +2462,8 @@ static int __init snd_dbri_mixer(struct snd_dbri * dbri)
 /****************************************************************************
                        /proc interface
 ****************************************************************************/
-static void dbri_regs_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer)
+static void dbri_regs_read(struct snd_info_entry *entry,
+                          struct snd_info_buffer *buffer)
 {
        struct snd_dbri *dbri = entry->private_data;
 
@@ -2493,7 +2474,7 @@ static void dbri_regs_read(struct snd_info_entry * entry, struct snd_info_buffer
 }
 
 #ifdef DBRI_DEBUG
-static void dbri_debug_read(struct snd_info_entry * entry,
+static void dbri_debug_read(struct snd_info_entry *entry,
                            struct snd_info_buffer *buffer)
 {
        struct snd_dbri *dbri = entry->private_data;
@@ -2505,26 +2486,27 @@ static void dbri_debug_read(struct snd_info_entry * entry,
                        struct dbri_pipe *pptr = &dbri->pipes[pipe];
                        snd_iprintf(buffer,
                                    "Pipe %d: %s SDP=0x%x desc=%d, "
-                                   "len=%d @ %d prev: %d next %d\n",
+                                   "len=%d next %d\n",
                                    pipe,
-                                   (pptr->direction ==
-                                    PIPEinput ? "input" : "output"), pptr->sdp,
-                                   pptr->desc, pptr->length, pptr->cycle,
-                                   pptr->prevpipe, pptr->nextpipe);
+                                  (pptr->sdp & D_SDP_TO_SER) ? "output" :
+                                                                "input",
+                                   pptr->sdp, pptr->desc,
+                                   pptr->length, pptr->nextpipe);
                }
        }
 }
 #endif
 
-void snd_dbri_proc(struct snd_dbri * dbri)
+static void __devinit snd_dbri_proc(struct snd_card *card)
 {
+       struct snd_dbri *dbri = card->private_data;
        struct snd_info_entry *entry;
 
-       if (! snd_card_proc_new(dbri->card, "regs", &entry))
+       if (!snd_card_proc_new(card, "regs", &entry))
                snd_info_set_text_ops(entry, dbri, dbri_regs_read);
 
 #ifdef DBRI_DEBUG
-       if (! snd_card_proc_new(dbri->card, "debug", &entry)) {
+       if (!snd_card_proc_new(card, "debug", &entry)) {
                snd_info_set_text_ops(entry, dbri, dbri_debug_read);
                entry->mode = S_IFREG | S_IRUGO;        /* Readable only. */
        }
@@ -2536,36 +2518,37 @@ void snd_dbri_proc(struct snd_dbri * dbri)
 **************************** Initialization ********************************
 ****************************************************************************
 */
-static void snd_dbri_free(struct snd_dbri * dbri);
+static void snd_dbri_free(struct snd_dbri *dbri);
 
-static int __init snd_dbri_create(struct snd_card *card,
-                                 struct sbus_dev *sdev,
-                                 struct linux_prom_irqs *irq, int dev)
+static int __devinit snd_dbri_create(struct snd_card *card,
+                                    struct of_device *op,
+                                    int irq, int dev)
 {
        struct snd_dbri *dbri = card->private_data;
        int err;
 
        spin_lock_init(&dbri->lock);
-       dbri->card = card;
-       dbri->sdev = sdev;
-       dbri->irq = irq->pri;
-       dbri->dbri_version = sdev->prom_name[9];
+       dbri->op = op;
+       dbri->irq = irq;
 
-       dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma),
-                                         &dbri->dma_dvma);
+       dbri->dma = dma_alloc_coherent(&op->dev,
+                                      sizeof(struct dbri_dma),
+                                      &dbri->dma_dvma, GFP_ATOMIC);
+       if (!dbri->dma)
+               return -ENOMEM;
        memset((void *)dbri->dma, 0, sizeof(struct dbri_dma));
 
        dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n",
                dbri->dma, dbri->dma_dvma);
 
        /* Map the registers into memory. */
-       dbri->regs_size = sdev->reg_addrs[0].reg_size;
-       dbri->regs = sbus_ioremap(&sdev->resource[0], 0,
-                                 dbri->regs_size, "DBRI Registers");
+       dbri->regs_size = resource_size(&op->resource[0]);
+       dbri->regs = of_ioremap(&op->resource[0], 0,
+                               dbri->regs_size, "DBRI Registers");
        if (!dbri->regs) {
                printk(KERN_ERR "DBRI: could not allocate registers\n");
-               sbus_free_consistent(sdev, sizeof(struct dbri_dma),
-                                    (void *)dbri->dma, dbri->dma_dvma);
+               dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
+                                 (void *)dbri->dma, dbri->dma_dvma);
                return -EIO;
        }
 
@@ -2573,9 +2556,9 @@ static int __init snd_dbri_create(struct snd_card *card,
                          "DBRI audio", dbri);
        if (err) {
                printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
-               sbus_iounmap(dbri->regs, dbri->regs_size);
-               sbus_free_consistent(sdev, sizeof(struct dbri_dma),
-                                    (void *)dbri->dma, dbri->dma_dvma);
+               of_iounmap(&op->resource[0], dbri->regs, dbri->regs_size);
+               dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
+                                 (void *)dbri->dma, dbri->dma_dvma);
                return err;
        }
 
@@ -2587,13 +2570,10 @@ static int __init snd_dbri_create(struct snd_card *card,
                return err;
        }
 
-       dbri->next = dbri_list;
-       dbri_list = dbri;
-
        return 0;
 }
 
-static void snd_dbri_free(struct snd_dbri * dbri)
+static void snd_dbri_free(struct snd_dbri *dbri)
 {
        dprintk(D_GEN, "snd_dbri_free\n");
        dbri_reset(dbri);
@@ -2602,28 +2582,23 @@ static void snd_dbri_free(struct snd_dbri * dbri)
                free_irq(dbri->irq, dbri);
 
        if (dbri->regs)
-               sbus_iounmap(dbri->regs, dbri->regs_size);
+               of_iounmap(&dbri->op->resource[0], dbri->regs, dbri->regs_size);
 
        if (dbri->dma)
-               sbus_free_consistent(dbri->sdev, sizeof(struct dbri_dma),
-                                    (void *)dbri->dma, dbri->dma_dvma);
+               dma_free_coherent(&dbri->op->dev,
+                                 sizeof(struct dbri_dma),
+                                 (void *)dbri->dma, dbri->dma_dvma);
 }
 
-static int __init dbri_attach(int prom_node, struct sbus_dev *sdev)
+static int __devinit dbri_probe(struct of_device *op, const struct of_device_id *match)
 {
        struct snd_dbri *dbri;
-       struct linux_prom_irqs irq;
        struct resource *rp;
        struct snd_card *card;
        static int dev = 0;
+       int irq;
        int err;
 
-       if (sdev->prom_name[9] < 'e') {
-               printk(KERN_ERR "DBRI: unsupported chip version %c found.\n",
-                      sdev->prom_name[9]);
-               return -EIO;
-       }
-
        if (dev >= SNDRV_CARDS)
                return -ENODEV;
        if (!enable[dev]) {
@@ -2631,9 +2606,9 @@ static int __init dbri_attach(int prom_node, struct sbus_dev *sdev)
                return -ENOENT;
        }
 
-       err = prom_getproperty(prom_node, "intr", (char *)&irq, sizeof(irq));
-       if (err < 0) {
-               printk(KERN_ERR "DBRI-%d: Firmware node lacks IRQ property.\n", dev);
+       irq = op->irqs[0];
+       if (irq <= 0) {
+               printk(KERN_ERR "DBRI-%d: No IRQ.\n", dev);
                return -ENODEV;
        }
 
@@ -2644,79 +2619,87 @@ static int __init dbri_attach(int prom_node, struct sbus_dev *sdev)
 
        strcpy(card->driver, "DBRI");
        strcpy(card->shortname, "Sun DBRI");
-       rp = &sdev->resource[0];
+       rp = &op->resource[0];
        sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
                card->shortname,
-               rp->flags & 0xffL, (unsigned long long)rp->start, irq.pri);
+               rp->flags & 0xffL, (unsigned long long)rp->start, irq);
 
-       if ((err = snd_dbri_create(card, sdev, &irq, dev)) < 0) {
+       err = snd_dbri_create(card, op, irq, dev);
+       if (err < 0) {
                snd_card_free(card);
                return err;
        }
 
        dbri = card->private_data;
-       if ((err = snd_dbri_pcm(dbri)) < 0)
+       err = snd_dbri_pcm(card);
+       if (err < 0)
                goto _err;
 
-       if ((err = snd_dbri_mixer(dbri)) < 0)
+       err = snd_dbri_mixer(card);
+       if (err < 0)
                goto _err;
 
        /* /proc file handling */
-       snd_dbri_proc(dbri);
+       snd_dbri_proc(card);
+       dev_set_drvdata(&op->dev, card);
 
-       if ((err = snd_card_register(card)) < 0)
+       err = snd_card_register(card);
+       if (err < 0)
                goto _err;
 
        printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
               dev, dbri->regs,
-              dbri->irq, dbri->dbri_version, dbri->mm.version);
+              dbri->irq, op->node->name[9], dbri->mm.version);
        dev++;
 
        return 0;
 
- _err:
+_err:
        snd_dbri_free(dbri);
        snd_card_free(card);
        return err;
 }
 
-/* Probe for the dbri chip and then attach the driver. */
-static int __init dbri_init(void)
+static int __devexit dbri_remove(struct of_device *op)
 {
-       struct sbus_bus *sbus;
-       struct sbus_dev *sdev;
-       int found = 0;
-
-       /* Probe each SBUS for the DBRI chip(s). */
-       for_all_sbusdev(sdev, sbus) {
-               /*
-                * The version is coded in the last character
-                */
-               if (!strncmp(sdev->prom_name, "SUNW,DBRI", 9)) {
-                       dprintk(D_GEN, "DBRI: Found %s in SBUS slot %d\n",
-                               sdev->prom_name, sdev->slot);
+       struct snd_card *card = dev_get_drvdata(&op->dev);
 
-                       if (dbri_attach(sdev->prom_node, sdev) == 0)
-                               found++;
-               }
-       }
+       snd_dbri_free(card->private_data);
+       snd_card_free(card);
+
+       dev_set_drvdata(&op->dev, NULL);
 
-       return (found > 0) ? 0 : -EIO;
+       return 0;
 }
 
-static void __exit dbri_exit(void)
-{
-       struct snd_dbri *this = dbri_list;
+static const struct of_device_id dbri_match[] = {
+       {
+               .name = "SUNW,DBRIe",
+       },
+       {
+               .name = "SUNW,DBRIf",
+       },
+       {},
+};
 
-       while (this != NULL) {
-               struct snd_dbri *next = this->next;
-               struct snd_card *card = this->card;
+MODULE_DEVICE_TABLE(of, dbri_match);
 
-               snd_dbri_free(this);
-               snd_card_free(card);
-               this = next;
-       }
-       dbri_list = NULL;
+static struct of_platform_driver dbri_sbus_driver = {
+       .name           = "dbri",
+       .match_table    = dbri_match,
+       .probe          = dbri_probe,
+       .remove         = __devexit_p(dbri_remove),
+};
+
+/* Probe for the dbri chip and then attach the driver. */
+static int __init dbri_init(void)
+{
+       return of_register_driver(&dbri_sbus_driver, &of_bus_type);
+}
+
+static void __exit dbri_exit(void)
+{
+       of_unregister_driver(&dbri_sbus_driver);
 }
 
 module_init(dbri_init);