patch-2.1.124 linux/drivers/sbus/audio/dbri.c

Next file: linux/drivers/sbus/audio/dbri.h
Previous file: linux/drivers/sbus/audio/cs4215.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.123/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c
@@ -59,7 +59,7 @@
 #include <asm/delay.h>
 #include <asm/sbus.h>
 
-#include "audio.h"
+#include <asm/audioio.h>
 #include "dbri.h"
 
 
@@ -84,10 +84,13 @@
 /* Bit hunting */
 #define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->cmd[i]); }
 
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value)
+
 #else
 
 #define dprintk(a, x)
 #define dumpcmd
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
 
 #endif	/* DBRI_DEBUG */
 
@@ -103,6 +106,7 @@
 static int num_drivers;
 static int dbri_cmdlocked = 0;
 
+static void * output_callback_arg;
 
 /*
  * Make sure, that we can send a command to the dbri
@@ -130,21 +134,6 @@
 	return 0;
 }
 
-static void dummy()
-{
-}
-
-static struct sparcaudio_operations dbri_ops = {
-	dummy, /* dbri_open, */
-	dummy, /* dbri_release, */
-	dummy, /* dbri_ioctl, */
-	dummy, /* dbri_start_output, */
-	dummy, /* dbri_stop_output, */
-	dummy, /* dbri_start_input, */
-        dummy, /* dbri_stop_input, */
-	dummy, /* dbri_audio_getdev, */
-};
-
 static void dbri_reset(struct sparcaudio_driver *drv)
 {
 	struct dbri *dbri = (struct dbri *)drv->private;
@@ -171,7 +160,7 @@
 }
 
 
-static void dbri_init(struct sparcaudio_driver *drv)
+static void dbri_initialize(struct sparcaudio_driver *drv)
 {
 	struct dbri *dbri = (struct dbri *)drv->private;
 	int n;
@@ -191,7 +180,13 @@
 	dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr);
 	dbri->dbri_irqp = 1;
 
+#ifdef USE_SBUS_BURSTS
+        /* Enable 4-word, 8-word, and 16-word SBus Bursts */
 	dbri->regs->reg0 |= (D_G|D_S|D_E);
+#else
+        /* Disable 4-word, 8-word, and 16-word SBus Bursts */
+        dbri->regs->reg0 &= ~(D_G|D_S|D_E);
+#endif
 
 	/*
 	 * Set up the interrupt queue
@@ -236,7 +231,7 @@
 	 */
 	mm->data[0] = CS4215_LO(0x20) | CS4215_HE|CS4215_LE;
 	mm->data[1] = CS4215_RO(0x20) | CS4215_SE;
-	mm->data[2] = CS4215_LG( 0x8) | CS4215_IS;
+	mm->data[2] = CS4215_LG( 0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1;
 	mm->data[3] = CS4215_RG( 0x8) | CS4215_MA(0xf);
 
 	/*
@@ -248,7 +243,7 @@
 	 */
 	mm->ctrl[0] = CS4215_RSRVD_1;
 	mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
-	mm->ctrl[2] = CS4215_XEN | CS4215_XCLK |
+	mm->ctrl[2] = CS4215_XCLK |
 			CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
 	mm->ctrl[3] = 0;
 }
@@ -264,69 +259,86 @@
 	 * Pipe  4: Send timeslots 1-4 (audio data)
 	 * Pipe 17: Send timeslots 5-8 (part of ctrl data)
 	 * Pipe  6: Receive timeslots 1-4 (audio data)
-	 * Pipe 19: Receive timeslots 6-7. We can only receive 20 bits via
+	 * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via
 	 *          interrupt, and the rest of the data (slot 5 and 8) is
 	 *	    not relevant for us (only for doublechecking).
+         *
+         * Just like in control mode, the time slots are all offset by eight
+         * bits.  The CS4215, it seems, observes TSIN (the delayed signal)
+         * even if it's the CHI master.  Don't ask me...
 	 */
 	
-	/* Transmit & Receive Memory setup */
-	dbri->mm.td.flags = DBRI_TD_F|DBRI_TD_D|DBRI_TD_CNT(0);
-	dbri->mm.td.ba = 0;
-	dbri->mm.td.nda = (__u32)&dbri->mm.td;
-	dbri->mm.td.status = 0;
-
-	dbri->mm.td.flags = DBRI_RD_BCNT(0);
-	dbri->mm.td.ba = 0;
-	dbri->mm.td.nda = (__u32)&dbri->mm.rd;
-	dbri->mm.td.status = 0;
 
-	/* Pipe 4: SDP + DTS */
-	val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_4);
+	/* Pipe 4: SDP */
+	val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4);
 	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = (__u32)&dbri->mm.td;
-
-	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
 	dbri->cmd[n++] = 0;
-	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16);
 
 
-	/* Pipe 17: SDP + DTS + SSP */
+	/* Pipe 17: SDP */
 	val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_C|D_PIPE(D_P_17);
 	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
 	dbri->cmd[n++] = 0;				/* Fixed data */
 
-	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = 0;
-	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(32) | D_TS_NONCONTIG |
-			 D_TS_MON(D_P_4) | D_TS_NEXT(D_P_16);
-
+        /* Pipe 17: SSP */
 	dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
 	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.data, 4);
 
 
-	/* Pipe 6: SDP + DTS */
-	val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_6);
+	/* Pipe 6: SDP */
+	val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_6);
 	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = (__u32)&dbri->mm.rd;
-
-	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16);
 	dbri->cmd[n++] = 0;
 
 
-	/* Pipe 19: SDP + DTS */
-	val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_19);
+	/* Pipe 20: SDP */
+	val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_20);
 	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
 	dbri->cmd[n++] = 0;				/* Fixed data */
 
-	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_19);
+
+
+	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+
+
+        /* Pipe 4: DTS */
+	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4);
 	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(40) | D_TS_NONCONTIG |
-			 D_TS_MON(D_P_6) | D_TS_NEXT(D_P_16); 
 	dbri->cmd[n++] = 0;
+#if 0
+        /* Full blown, four time slots, 16 bit stereo */
+	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#else
+        /* Single time slot, 8 bit mono */
+	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#endif
+
+        /* Pipe 17: DTS */
+	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = 0;
+	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(40) | D_TS_NEXT(D_P_16);
+
+        /* Pipe 6: DTS */
+	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+#if 0
+        /* Full blown, four time slots, 16 bit stereo */
+	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#else
+        /* Single time slot, 8 bit mono */
+	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#endif
+	dbri->cmd[n++] = 0;
+
+        /* Pipe 20: DTS */
+	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_20);
+	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16); 
+	dbri->cmd[n++] = 0;
+
+        /* CHI: Slave mode; enable interrupts */
+	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN);
 
 	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
 
@@ -342,12 +354,31 @@
 	int n = 0, val;
 
 	/*
-	 * Enable Command mode: Set PIO3 to 0, then wait
+	 * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
 	 * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
 	 */
 	val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2);
 	dbri->regs->reg2 = val;
-	udelay(34);	
+	udelay(34);
+
+        /* In Control mode, the CS4215 is a slave device, so the DBRI must
+         * operate as CHI master, supplying clocking and frame synchronization.
+         *
+         * In Data mode, however, the CS4215 must be CHI master to insure
+         * that its data stream is synchronous with its codec.
+         *
+         * The upshot of all this?  We start by putting the DBRI into master
+         * mode, program the CS4215 in Control mode, then switch the CS4215
+         * into Data mode and put the DBRI into slave mode.  Various timing
+         * requirements must be observed along the way.
+         *
+         * Oh, and one more thing - when the DBRI is master (and only when
+         * the DBRI is master), the addressing of the CS4215's time slots
+         * is offset by eight bits, so we add eight to all the "cycle"
+         * values in the Define Time Slot (DTS) commands.  This is done in
+         * hardware by a TI 248 that delays the DBRI->4215 frame sync signal
+         * by eight clock cycles.  Anybody know why?
+         */
 
 	dbri_cmdlock(dbri);
 
@@ -371,11 +402,11 @@
 	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
 	dbri->cmd[n++] = 0;
 
-	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_18);
+	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_18);
 	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
 	dbri->cmd[n++] = 0;
 
-	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_19);
+	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_19);
 	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
 	dbri->cmd[n++] = 0;
 
@@ -389,76 +420,103 @@
 
 
 	/* Link the timeslots */
+
+        /* Pipe 17 - CS4215 Status, Data Format, Serial Control, Test - output
+         *           time slots 1, 2, 3 and 4 - 32 bits
+         */
+
 	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_17);
 	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
 	dbri->cmd[n++] = 0;
-	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(256) | D_TS_NEXT(D_P_16);
+	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16);
+
+        /* Pipe 18 - CS4215 Status and Data Format - input
+         *           time slots 1 & 2 - 16 bits
+         */
 
 	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_18);
 	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(0) | D_TS_NEXT(D_P_16);
+	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16);
 	dbri->cmd[n++] = 0;
 
+        /* Pipe 19 - CS4215 Revision - time slot 7, eight bits - input
+         */
+
 	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_18) | D_PIPE(D_P_19);
 	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16);
-	/*
-	 * According to the manual we should also specify 
-	 * D_TS_NONCONTIG | D_TS_MON(D_P_18), but the machine freezes
-	 * if we do that. Can somebody explain me why?
-	 */
+	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(56) | D_TS_NEXT(D_P_16);
 	dbri->cmd[n++] = 0;
 
 
-	/* Setup DBRI for CHI Master */
-	dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_REN);
-	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(6) | D_CHI_FD |
-					D_CHI_IR | D_CHI_EN);
-	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+	/* Setup DBRI for CHI Master
+         *
+         * BPF   =  128 (128 bits per 8 kHz frame = 1.024 MHz clock rate)
+         * CHICM =  12 (12.288 MHz / 24 = 1.024 MHz clock rate)
+         * FD    =  1 - drive CHIFS on rising edge of CHICK
+         *
+         * RCE   =  0 - receive on falling edge of CHICK
+         * XCE   =  1 - transmit on rising edge of CHICK
+         */
+	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(12) | D_CHI_FD |
+					D_CHI_IR | D_CHI_EN | D_CHI_BPF(128));
 	dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
 
-	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0);
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1);
 	dbri->regs->reg8 = (int)dbri->cmd;
 
+
 	/* Wait for the data from the CS4215 */
-	interruptible_sleep_on(&dbri->int_wait);
-printk("Woke up (1) reg2: %x\n", dbri->regs->reg2);
+        interruptible_sleep_on(&dbri->int_wait);
+
+        /* Switch CS4215 to data mode - data sheet says
+         * "Set CLB=1 and send two more frames of valid control info"
+         */
+	dbri_cmdlock(dbri);
+
+        n = 0;
+	dbri->mm.ctrl[0] |= CS4215_CLB;
+	dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
+	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
 
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1);
+	dbri->regs->reg8 = (int)dbri->cmd;
+
+        dbri_cmdlock(dbri);
+
+        /* Two frames of control info @ 8kHz frame rate = 250 us delay */
+        udelay(250);
 
-	/* Now switch back to data mode */
 	n = 0;
-	/* CHI Anchor: Stop Send/Receive */
+
+	/* Now switch back to data mode */
+	/* Reset CHI Anchor: Stop Send/Receive */
 	val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
 	      D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
 	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
 	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
 	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
 
-	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0x17);
-	dbri->regs->reg8 = (int)dbri->cmd;
-
-#if 0      
-	dbri->mm.ctrl[0] |= CS4215_CLB;
-	dbri->cmd[n++] = DBRI_CMD(D_SSP, 1, D_PIPE(D_P_17));
-	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
 
 	/* Setup DBRI for CHI Slave */
-	dbri->cmd[n++] = DBRI_CMD(D_CDM, 1, D_CDM_XCE|D_CDM_REN);
-	dbri->cmd[n++] = DBRI_CMD(D_CHI, 1, D_CHI_CHICM(1) | D_CHI_FD |
-					D_CHI_IR | D_CHI_EN);
-	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 1, 0x16);
-	dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
+	/* dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN); */
+	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0x16);
 
-	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, 0x17);
+
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1);
 	dbri->regs->reg8 = (int)dbri->cmd;
 
-	dbri->regs->reg2 = D_ENPIO | D_PIO3 |
-				(dbri->mm.onboard ? D_PIO0 : D_PIO2);
-#endif
+        /* Wait for command to complete */
+        dbri_cmdlock(dbri);
+        n = 0;
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1);
+	dbri->regs->reg8 = (int)dbri->cmd;
 
-	/* We are ready */
-	dbri_cmdlocked = 0;
-	wake_up(&dbri->wait);
+
+        /* Switch CS4215 to data mode - set PIO3 to 1 */
+	dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 |
+				(dbri->mm.onboard ? D_PIO0 : D_PIO2);
 }
 
 static int mmcodec_init(struct sparcaudio_driver *drv)
@@ -500,9 +558,7 @@
 	if(dbri->mm.version == 0xff) 
 		return -EIO;
 
-	/*
-	mmcodec_init_data(dbri, &n);
-	*/
+	mmcodec_init_data(dbri);
 
 	return 0;
 }
@@ -518,11 +574,13 @@
 	 * Read it, so the interrupt goes away.
 	 */
 	x = dbri->regs->reg1;
+#if 0
 	if(numint++ > 20) {
 	    dbri->regs->reg0 = D_R; /* Soft Reset */
 	    numint = 0;
 	    printk("Soft reset\n");
 	}
+#endif
 
 	if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
 		/*
@@ -562,6 +620,12 @@
 					if(val == WAIT_INTR2)
 						wake_up(&dbri->int_wait);
 				break;
+			case D_P_4:
+				if (D_INTR_GETCODE(x) == D_INTR_XCMP) {
+					sparcaudio_output_done(output_callback_arg, 1);
+				}
+				break;
+
 			case D_P_18:
 				if(val != 0) {
 					x = reverse_bytes(val,2)&CS4215_12_MASK;
@@ -591,7 +655,123 @@
 }
 
 
+/*
+****************************************************************************
+******************** Interface with sparcaudio midlevel ********************
+****************************************************************************
+*/
+
+
+static void dummy()
+{
+}
+
+static int dbri_open(struct inode * inode, struct file * file,
+                     struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+
+#if 0
+	/* Set the default audio parameters. */
+	info->rgain = 128;
+	info->pgain = 200;
+	info->mgain = 0;
+#endif
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static void dbri_release(struct inode * inode, struct file * file,
+                         struct sparcaudio_driver *drv)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static void dbri_start_output(struct sparcaudio_driver *drv,
+                              __u8 * buffer, unsigned long count)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+        int val, n = 0;
+
+        /* XXX - This routine can be called via interrupt.  If DBRI
+         * was cmdlocked, that would cause a sleep, which would be
+         * scheduling in an interrupt, and that's not allowed
+         *
+         * Fortunately, there's nothing else talking to our DBRI (yet),
+         * so this isn't a problem (yet)
+         */
+
+        dbri_cmdlock(dbri);
+
+        dbri->mm.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_D | DBRI_TD_CNT(count);
+        dbri->mm.td.ba = (__u32) buffer;
+        dbri->mm.td.nda = 0;
+        dbri->mm.td.status = 0;
+
+        /* Pipe 4 is audio transmit */
+	val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4);
+	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+	dbri->cmd[n++] = (__u32)&dbri->mm.td;
 
+	dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
+
+	dbri->regs->reg8 = (int)dbri->cmd;
+
+        output_callback_arg = drv;
+}
+
+static void dbri_stop_output(struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+}
+
+static struct sparcaudio_operations dbri_ops = {
+	dbri_open,
+	dbri_release,
+	dummy, /* dbri_ioctl, */
+	dbri_start_output,
+	dbri_stop_output,
+	dummy, /* dbri_start_input, */
+        dummy, /* dbri_stop_input, */
+	dummy, /* dbri_audio_getdev, */
+	dummy, /* dbri_set_output_volume, */
+	dummy, /* dbri_get_output_volume, */
+	dummy, /* dbri_set_input_volume, */
+	dummy, /* dbri_get_input_volume, */
+	dummy, /* dbri_set_monitor_volume, */
+	dummy, /* dbri_get_monitor_volume, */
+	dummy, /* dbri_set_output_balance */
+	dummy, /* dbri_get_output_balance, */
+	dummy, /* dbri_set_input_balance */
+	dummy, /* dbri_get_input_balance, */
+	dummy, /* dbri_set_output_channels */
+	dummy, /* dbri_get_output_channels, */
+	dummy, /* dbri_set_input_channels */
+	dummy, /* dbri_get_input_channels, */
+	dummy, /* dbri_set_output_precision */
+	dummy, /* dbri_get_output_precision, */
+	dummy, /* dbri_set_input_precision */
+	dummy, /* dbri_get_input_precision, */
+	dummy, /* dbri_set_output_port */
+	dummy, /* dbri_get_output_port, */
+	dummy, /* dbri_set_input_port */
+	dummy, /* dbri_get_input_port, */
+	dummy, /* dbri_set_output_encoding */
+	dummy, /* dbri_get_output_encoding, */
+	dummy, /* dbri_set_input_encoding */
+	dummy, /* dbri_get_input_encoding, */
+	dummy, /* dbri_set_output_rate */
+	dummy, /* dbri_get_output_rate, */
+	dummy, /* dbri_set_input_rate */
+	dummy, /* dbri_get_input_rate, */
+	dummy, /* dbri_sunaudio_getdev_sunos, */
+	dummy, /* dbri_get_output_ports, */
+	dummy, /* dbri_get_input_ports, */
+	dummy, /* dbri_set_output_muted */
+	dummy, /* dbri_get_output_muted, */
+};
 
 
 static int dbri_attach(struct sparcaudio_driver *drv, 
@@ -651,7 +831,7 @@
 		return err;
 	}
 
-	dbri_init(drv);
+	dbri_initialize(drv);
 	err = mmcodec_init(drv);
 	if(err) {
 		dbri_detach(drv);
@@ -713,3 +893,22 @@
         }
 }
 #endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov