patch-2.3.7 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
- Lines: 2089
- Date:
Thu Jun 17 01:08:50 1999
- Orig file:
v2.3.6/linux/drivers/sbus/audio/dbri.c
- Orig date:
Mon Mar 15 16:11:30 1999
diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c
@@ -79,25 +79,25 @@
#define D_CMD (1<<2)
#define D_MM (1<<3)
#define D_USR (1<<4)
+#define D_DESC (1<<5)
-/* static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; */
static int dbri_debug = 0;
MODULE_PARM(dbri_debug, "i");
+static int dbri_trace = 0;
+MODULE_PARM(dbri_trace, "i");
+#define tprintk(x) if(dbri_trace) printk x
+
static char *cmds[] = {
"WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
"SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
};
-/* Bit hunting */
-#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->dma->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 */
@@ -114,41 +114,42 @@
****************************************************************************
************** DBRI initialization and command synchronization *************
****************************************************************************
-*/
+Commands are sent to the DBRI by building a list of them in memory,
+then writing the address of the first list item to DBRI register 8.
+The list is terminated with a WAIT command, which can generate a
+CPU interrupt if required.
+
+Since the DBRI can run in parallel with the CPU, several means of
+synchronization present themselves. The original scheme (Rudolf's)
+was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
+an interrupt signaled completion, and wait on a wait_queue if a routine
+attempted to cmdlock while the flag was set. The problems arose when
+we tried to cmdlock from inside an interrupt handler, which might
+cause scheduling in an interrupt (if we waited), etc, etc
+
+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.
+
+I've decided to implement something much simpler - after each command,
+the CPU waits for the DBRI to finish the command by polling the P bit
+in DBRI register 0. I've tried to implement this in such a way
+that might make implementing a more sophisticated scheme easier.
+
+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. After the commands have been writen, dbri_cmdsend() is
+called with the final pointer value.
-/*
- * Commands are sent to the DBRI by building a list of them in memory,
- * then writing the address of the first list item to DBRI register 8.
- * The list is terminated with a WAIT command, which can generate a
- * CPU interrupt if required.
- *
- * Since the DBRI can run asynchronously to the CPU, several means of
- * synchronization present themselves. The original scheme (Rudolf's)
- * was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
- * an interrupt signaled completion, and wait on a wait_queue if a routine
- * attempted to cmdlock while the flag was set. The problems arose when
- * we tried to cmdlock from inside an interrupt handler, which might
- * cause scheduling in an interrupt (if we waited), etc, etc
- *
- * 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.
- *
- * I've decided to implement something much simpler - after each command,
- * the CPU waits for the DBRI to finish the command by polling the P bit
- * in DBRI register 0. I've tried to implement this in such a way
- * that might make implementing a more sophisticated scheme easier.
- *
- * 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. After the commands have been writen, dbri_cmdsend() is
- * called with the final pointer value.
- */
+Something a little more clever is required if this code is ever run
+on an SMP machine.
+
+*/
-static int dbri_locked = 0; /* XXX not SMP safe! XXX */
+static int dbri_locked = 0;
static volatile int * dbri_cmdlock(struct dbri *dbri)
{
@@ -159,9 +160,21 @@
return dbri->dma->cmd;
}
+static void dbri_process_interrupt_buffer(struct dbri *);
+
static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd)
{
- int maxloops = 1000000;
+ int MAXLOOPS = 1000000;
+ int maxloops = MAXLOOPS;
+ unsigned int flags;
+ volatile int * ptr;
+
+ for (ptr = dbri->dma->cmd; ptr < cmd; ptr ++) {
+ dprintk(D_CMD, ("DBRI cmd: %08x:%08x\n",
+ (unsigned int) ptr, *ptr));
+ }
+
+ save_and_cli(flags);
dbri_locked --;
if (dbri_locked != 0) {
@@ -170,14 +183,25 @@
printk("DBRI: Command buffer overflow! (bug in driver)\n");
} else {
*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- *(cmd++) = DBRI_CMD(D_WAIT, 0, 0);
+ *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+ dbri->wait_seen = 0;
dbri->regs->reg8 = (int)dbri->dma_dvma->cmd;
- while ((maxloops--) > 0 && (dbri->regs->reg0 & D_P));
+ while ((--maxloops) > 0 && (dbri->regs->reg0 & D_P));
+ if (maxloops == 0) {
+ printk("DBRI: Chip never completed command buffer\n");
+ } else {
+ while ((--maxloops) > 0 && (! dbri->wait_seen))
+ dbri_process_interrupt_buffer(dbri);
+ if (maxloops == 0) {
+ printk("DBRI: Chip never acked WAIT\n");
+ } else {
+ dprintk(D_INT, ("DBRI: Chip completed command buffer (%d)\n",
+ MAXLOOPS - maxloops));
+ }
+ }
}
- if (maxloops == 0) {
- printk("DBRI: Chip never completed command buffer\n");
- }
+ restore_flags(flags);
}
static void dbri_reset(struct dbri *dbri)
@@ -198,7 +222,7 @@
dbri_reset(dbri);
free_irq(dbri->irq, dbri);
sparc_free_io(dbri->regs, dbri->regs_size);
- /* Should we release the DMA structure dbri->dma here? */
+ release_region((unsigned long) dbri->dma, sizeof(struct dbri_dma));
kfree(dbri);
}
@@ -242,6 +266,17 @@
****************************************************************************
*************************** DBRI interrupt handler *************************
****************************************************************************
+
+The DBRI communicates with the CPU mainly via a circular interrupt
+buffer. When an interrupt is signaled, the CPU walks through the
+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_cmdsend()
+to make sure we're synced up with the chip after each command sequence,
+even if we're running cli'ed.
+
*/
@@ -263,6 +298,7 @@
case 2:
b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
case 1:
+ case 0:
break;
default:
printk("DBRI reverse_bytes: unsupported length\n");
@@ -273,37 +309,39 @@
/* transmission_complete_intr()
*
* Called by main interrupt handler when DBRI signals transmission complete
- * on a pipe.
+ * on a pipe (interrupt triggered by the B bit in a transmit descriptor).
*
* Walks through the pipe's list of transmit buffer descriptors, releasing
- * each one's DMA buffer (if present) and signaling its callback routine
- * (if present), before flaging the descriptor available and proceeding
- * to the next one.
- *
- * Assumes that only the last in a chain of descriptors will have FINT
- * sent to signal an interrupt, so that the chain will be completely
- * transmitted by the time we get here, and there's no need to save
- * any of the descriptors. In particular, use of the DBRI's CDP command
- * is precluded, but I've not been able to get CDP working reliably anyway.
+ * each one's DMA buffer (if present), flagging the descriptor available,
+ * and signaling its callback routine (if present), before proceeding
+ * to the next one. Stops when the first descriptor is found without
+ * TBC (Transmit Buffer Complete) set, or we've run through them all.
*/
static void transmission_complete_intr(struct dbri *dbri, int pipe)
{
- int td = dbri->pipes[pipe].desc;
+ int td;
int status;
void *buffer;
void (*callback)(void *, int);
+ void *callback_arg;
- dbri->pipes[pipe].desc = -1;
+ td = dbri->pipes[pipe].desc;
- for (; td >= 0; td = dbri->descs[td].next) {
+ while (td >= 0) {
if (td >= DBRI_NO_DESCS) {
printk("DBRI: invalid td on pipe %d\n", pipe);
return;
}
- status = dbri->dma->desc[td].word4;
+ status = DBRI_TD_STATUS(dbri->dma->desc[td].word4);
+
+ if (! (status & DBRI_TD_TBC)) {
+ break;
+ }
+
+ dprintk(D_INT, ("DBRI: TD %d, status 0x%02x\n", td, status));
buffer = dbri->descs[td].buffer;
if (buffer) {
@@ -313,12 +351,16 @@
}
callback = dbri->descs[td].output_callback;
- if (callback != NULL) {
- callback(dbri->descs[td].output_callback_arg,
- DBRI_TD_STATUS(status) & 0xe);
- }
+ callback_arg = dbri->descs[td].output_callback_arg;
dbri->descs[td].inuse = 0;
+
+ td = dbri->descs[td].next;
+ dbri->pipes[pipe].desc = td;
+
+ if (callback != NULL) {
+ callback(callback_arg, status & 0xe);
+ }
}
}
@@ -335,7 +377,7 @@
}
dbri->descs[rd].inuse = 0;
- dbri->pipes[pipe].desc = -1;
+ dbri->pipes[pipe].desc = dbri->descs[rd].next;
status = dbri->dma->desc[rd].word1;
buffer = dbri->descs[rd].buffer;
@@ -351,95 +393,170 @@
DBRI_RD_STATUS(status),
DBRI_RD_CNT(status)-2);
}
+
+ dprintk(D_INT, ("DBRI: Recv RD %d, status 0x%02x, len %d\n",
+ rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status)));
}
-static void dbri_intr(int irq, void *opaque, struct pt_regs *regs)
+static void dbri_process_one_interrupt(struct dbri *dbri, int x)
{
- struct dbri *dbri = (struct dbri *)opaque;
- int x;
-
- /*
- * Read it, so the interrupt goes away.
- */
- x = dbri->regs->reg1;
+ int val = D_INTR_GETVAL(x);
+ int channel = D_INTR_GETCHAN(x);
+ int command = D_INTR_GETCMD(x);
+ int code = D_INTR_GETCODE(x);
+ int rval = D_INTR_GETRVAL(x);
+
+ if (channel == D_INTR_CMD) {
+ dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n",
+ cmds[command], val));
+ } else {
+ dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
+ channel, code, rval));
+ }
- if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
- /*
- * What should I do here ?
- */
- if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
- if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
- if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
- if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+ if (channel == D_INTR_CMD && command == D_WAIT) {
+ dbri->wait_seen ++;
}
- if (!(x & D_IR)) /* Not for us */
- return;
+ if (code == D_INTR_SBRI) {
- x = dbri->dma->intr[dbri->dbri_irqp];
- while (x != 0) {
- int val = D_INTR_GETVAL(x);
- int channel = D_INTR_GETCHAN(x);
+ /* SBRI - BRI status change */
- dbri->dma->intr[dbri->dbri_irqp] = 0;
+ int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
+ dbri->liu_state = liu_states[val & 0x7];
+ if (dbri->liu_callback)
+ dbri->liu_callback(dbri->liu_callback_arg);
+ }
- if(D_INTR_GETCHAN(x) == D_INTR_CMD) {
- dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n",
- cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x)));
- } else {
- dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
- D_INTR_GETCHAN(x), D_INTR_GETCODE(x),
- D_INTR_GETRVAL(x)));
- }
+ if (code == D_INTR_BRDY) {
+ reception_complete_intr(dbri, channel);
+ }
- if (D_INTR_GETCODE(x) == D_INTR_SBRI) {
+ if (code == D_INTR_XCMP) {
+ transmission_complete_intr(dbri, channel);
+ }
- /* SBRI - BRI status change */
+ if (code == D_INTR_UNDR) {
- int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
- dbri->liu_state = liu_states[val & 0x7];
- if (dbri->liu_callback)
- dbri->liu_callback(dbri->liu_callback_arg);
- }
+ /* UNDR - Transmission underrun
+ * resend SDP command with clear pipe bit (C) set
+ */
- if (D_INTR_GETCODE(x) == D_INTR_BRDY) {
- reception_complete_intr(dbri, channel);
- }
+ volatile int *cmd;
+ int pipe = channel;
+ int td = dbri->pipes[pipe].desc;
+
+ dbri->dma->desc[td].word4 = 0;
+
+ cmd = dbri_cmdlock(dbri);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[pipe].sdp
+ | D_SDP_P | D_SDP_C | D_SDP_2SAME);
+ *(cmd++) = (int) & dbri->dma_dvma->desc[td];
+ dbri_cmdsend(dbri, cmd);
+ }
+
+ if (code == D_INTR_FXDT) {
+
+ /* FXDT - Fixed data change */
- if (D_INTR_GETCODE(x) == D_INTR_XCMP) {
- transmission_complete_intr(dbri, channel);
+ if (dbri->pipes[channel].sdp & D_SDP_MSB) {
+ val = reverse_bytes(val, dbri->pipes[channel].length);
}
- if (D_INTR_GETCODE(x) == D_INTR_FXDT) {
+ if (dbri->pipes[channel].recv_fixed_ptr) {
+ * dbri->pipes[channel].recv_fixed_ptr = val;
+ }
+ }
+}
- /* FXDT - Fixed data change */
+/* 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.
+ */
- if (dbri->pipes[D_INTR_GETCHAN(x)].sdp & D_SDP_MSB) {
- val = reverse_bytes(val, dbri->pipes[channel].length);
- }
+static void dbri_process_interrupt_buffer(struct dbri *dbri)
+{
+ int x;
- if (dbri->pipes[D_INTR_GETCHAN(x)].recv_fixed_ptr) {
- * dbri->pipes[channel].recv_fixed_ptr = val;
- }
- }
+ 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))
dbri->dbri_irqp = 1;
else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0)
dbri->dbri_irqp++;
- x = dbri->dma->intr[dbri->dbri_irqp];
+
+ dbri_process_one_interrupt(dbri, x);
}
}
+static void dbri_intr(int irq, void *opaque, struct pt_regs *regs)
+{
+ struct dbri *dbri = (struct dbri *)opaque;
+ int x;
+
+ /*
+ * Read it, so the interrupt goes away.
+ */
+ x = dbri->regs->reg1;
+
+ dprintk(D_INT, ("DBRI: Interrupt! (reg1=0x%08x)\n", x));
+
+ if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
+
+ if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
+ if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
+ if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
+ if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+
+ /* Some of these SBus errors cause the chip's SBus circuitry
+ * to be disabled, so just re-enable and try to keep going.
+ *
+ * The only one I've seen is MRR, which will be triggered
+ * if you let a transmit pipe underrun, then try to CDP it.
+ *
+ * If these things persist, we should probably reset
+ * and re-init the chip.
+ */
+
+ dbri->regs->reg0 &= ~D_D;
+ }
+
+#if 0
+ if (!(x & D_IR)) /* Not for us */
+ return;
+#endif
+
+ dbri_process_interrupt_buffer(dbri);
+}
+
/*
****************************************************************************
************************** DBRI data pipe management ***********************
****************************************************************************
+
+While DBRI control functions use the command and interrupt buffers, the
+main data path takes the form of data pipes, which can be short (command
+and interrupt driven), or long (attached to DMA buffers). These functions
+provide a rudimentary means of setting up and managing the DBRI's pipes,
+but the calling functions have to make sure they respect the pipes' linked
+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 dbri *dbri, int pipe)
+{
+ return (dbri->pipes[pipe].desc != -1);
+}
+
/* reset_pipe(dbri, pipe)
*
@@ -449,6 +566,7 @@
static void reset_pipe(struct dbri *dbri, int pipe)
{
int sdp;
+ int desc;
volatile int *cmd;
if (pipe < 0 || pipe > 31) {
@@ -467,6 +585,35 @@
*(cmd++) = 0;
dbri_cmdsend(dbri, cmd);
+ desc = dbri->pipes[pipe].desc;
+ while (desc != -1) {
+ void *buffer = dbri->descs[desc].buffer;
+ void (*output_callback) (void *, int)
+ = dbri->descs[desc].output_callback;
+ void *output_callback_arg
+ = dbri->descs[desc].output_callback_arg;
+ void (*input_callback) (void *, int, unsigned int)
+ = dbri->descs[desc].input_callback;
+ void *input_callback_arg
+ = dbri->descs[desc].input_callback_arg;
+
+ if (buffer) {
+ mmu_release_scsi_one(sbus_dvma_addr(buffer),
+ dbri->descs[desc].len,
+ dbri->sdev->my_bus);
+ }
+
+ dbri->descs[desc].inuse = 0;
+ desc = dbri->descs[desc].next;
+
+ if (output_callback) {
+ output_callback(output_callback_arg, -1);
+ }
+ if (input_callback) {
+ input_callback(input_callback_arg, -1, 0);
+ }
+ }
+
dbri->pipes[pipe].desc = -1;
}
@@ -482,140 +629,179 @@
/* sdp &= 0xf800; */
}
+ /* If this is a fixed receive pipe, arrange for an interrupt
+ * every time its data changes
+ */
+
+ if (D_SDP_MODE(sdp) == D_SDP_FIXED && ! (sdp & D_SDP_TO_SER)) {
+ sdp |= D_SDP_CHANGE;
+ }
+
sdp |= D_PIPE(pipe);
dbri->pipes[pipe].sdp = sdp;
+ dbri->pipes[pipe].desc = -1;
reset_pipe(dbri, pipe);
}
-enum master_or_slave { CHImaster, CHIslave };
-
-static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave,
- int bits_per_frame)
+static void link_time_slot(struct dbri *dbri, int pipe,
+ enum in_or_out direction, int basepipe,
+ int length, int cycle)
{
volatile int *cmd;
int val;
+ int prevpipe;
+ int nextpipe;
- cmd = dbri_cmdlock(dbri);
+ if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
+ printk("DBRI: link_time_slot called with illegal pipe number\n");
+ return;
+ }
- /* Set CHI Anchor: Pipe 16 */
+ if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
+ printk("DBRI: link_time_slot called on uninitialized pipe\n");
+ return;
+ }
- 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);
- *(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
-
- dbri->pipes[16].sdp = 1;
- dbri->pipes[16].nextpipe = 16;
-
- if (master_or_slave == CHIslave) {
- /* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
- *
- * CHICM = 0 (slave mode, 8 kHz frame rate)
- * IR = give immediate CHI status interrupt
- * EN = give CHI status interrupt upon change
- */
- *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)
- | D_CHI_IR | D_CHI_EN);
+ /* 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 {
- /* 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
+
+ /* 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.
*/
- int clockrate = bits_per_frame * 8;
- int divisor = 12288 / clockrate;
+ 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;
- if (divisor > 255 || divisor * clockrate != 12288) {
- printk("DBRI: illegal bits_per_frame in setup_chi\n");
+ while (dbri->pipes[nextpipe].cycle < cycle
+ && dbri->pipes[nextpipe].nextpipe != basepipe) {
+ prevpipe = nextpipe;
+ nextpipe = dbri->pipes[nextpipe].nextpipe;
}
+ }
- *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
- | D_CHI_IR | D_CHI_EN
- | D_CHI_BPF(bits_per_frame));
+ if (prevpipe == 16) {
+ if (direction == PIPEinput) {
+ dbri->chi_in_pipe = pipe;
+ } else {
+ dbri->chi_out_pipe = pipe;
+ }
+ } else {
+ dbri->pipes[prevpipe].nextpipe = pipe;
}
- /* CHI Data Mode
- *
- * RCE = 0 - receive on falling edge of CHICK
- * XCE = 1 - transmit on rising edge of CHICK
- * XEN = 1 - enable transmitter
- * REN = 1 - enable receiver
- */
+ dbri->pipes[pipe].nextpipe = nextpipe;
+ dbri->pipes[pipe].cycle = cycle;
+ dbri->pipes[pipe].length = length;
- *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ cmd = dbri_cmdlock(dbri);
- *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+ if (direction == PIPEinput) {
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(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;
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = 0;
+ *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ }
dbri_cmdsend(dbri, cmd);
}
-enum in_or_out { PIPEinput, PIPEoutput };
+/* I don't use this function, so it's basically untested. */
-static void link_time_slot(struct dbri *dbri, int pipe,
- enum in_or_out direction, int prevpipe,
- int length, int cycle)
+static void unlink_time_slot(struct dbri *dbri, int pipe,
+ enum in_or_out direction, int prevpipe,
+ int nextpipe)
{
volatile int *cmd;
int val;
- int nextpipe;
if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
- printk("DBRI: link_time_slot called with illegal pipe number\n");
+ printk("DBRI: unlink_time_slot called with illegal pipe number\n");
return;
}
- if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[prevpipe].sdp == 0) {
- printk("DBRI: link_time_slot called on uninitialized pipe\n");
- return;
- }
-
- if (pipe == prevpipe) {
- nextpipe = pipe;
- } else {
- nextpipe = dbri->pipes[prevpipe].nextpipe;
- }
-
- dbri->pipes[pipe].nextpipe = nextpipe;
- dbri->pipes[pipe].cycle = cycle;
- dbri->pipes[pipe].length = length;
-
cmd = dbri_cmdlock(dbri);
if (direction == PIPEinput) {
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+ val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
*(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ *(cmd++) = D_TS_NEXT(nextpipe);
*(cmd++) = 0;
} else {
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+ val = D_DTS_VO | D_DTS_DEL | 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++) = D_TS_NEXT(nextpipe);
}
dbri_cmdsend(dbri, cmd);
}
+/* xmit_fixed() / recv_fixed()
+ *
+ * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not
+ * expected to change much, and which we don't need to buffer.
+ * The DBRI only interrupts us when the data changes (receive pipes),
+ * or only changes the data when this function is called (transmit pipes).
+ * Only short pipes (numbers 16-31) can be used in fixed data mode.
+ *
+ * These function operate on a 32-bit field, no matter how large
+ * 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()
+ */
+
static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data)
{
volatile int *cmd;
if (pipe < 16 || pipe > 31) {
- printk("DBRI: xmit_fixed called with illegal pipe number\n");
+ printk("DBRI: xmit_fixed: Illegal pipe number\n");
+ return;
+ }
+
+ if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
+ printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe);
return;
}
if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
- printk("DBRI: xmit_fixed called on non-fixed pipe\n");
+ printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
return;
}
if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: xmit_fixed called on receive pipe\n");
+ printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe);
return;
}
@@ -633,21 +819,7 @@
dbri_cmdsend(dbri, cmd);
}
-/* recv_fixed()
- *
- * Receive data on a "fixed" pipe - i.e, one whose contents are not
- * expected to change much, and which we don't need to read constantly
- * into a buffer. The DBRI only interrupts us when the data changes.
- * Only short pipes (numbers 16-31) can be used in fixed data mode.
- *
- * Pass this function a pointer to a 32-bit field, no matter how large
- * 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()
- */
-
-static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr)
+static void recv_fixed(struct dbri *dbri, int pipe, volatile __u32 *ptr)
{
if (pipe < 16 || pipe > 31) {
printk("DBRI: recv_fixed called with illegal pipe number\n");
@@ -655,12 +827,12 @@
}
if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
- printk("DBRI: recv_fixed called on non-fixed pipe\n");
+ printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe);
return;
}
if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: recv_fixed called on transmit pipe\n");
+ printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe);
return;
}
@@ -668,37 +840,46 @@
}
+/* xmit_on_pipe() / recv_on_pipe()
+ *
+ * Transmit/receive data on a "long" pipe - i.e, one associated
+ * with a DMA buffer.
+ *
+ * Only pipe numbers 0-15 can be used in this mode.
+ *
+ * Both functions take pointer/len arguments pointing to a data buffer,
+ * and both provide callback functions (may be NULL) to notify higher
+ * level code when transmission/reception is complete.
+ *
+ * Both work by building chains of descriptors which identify the
+ * data buffers. Buffers too large for a single descriptor will
+ * be spread across multiple descriptors.
+ */
+
static void xmit_on_pipe(struct dbri *dbri, int pipe,
void * buffer, unsigned int len,
void (*callback)(void *, int), void * callback_arg)
{
volatile int *cmd;
+ register unsigned int flags;
int td = 0;
int first_td = -1;
- int last_td;
+ int last_td = -1;
__u32 dvma_buffer;
if (pipe < 0 || pipe > 15) {
- printk("DBRI: xmit_on_pipe called with illegal pipe number\n");
+ printk("DBRI: xmit_on_pipe: Illegal pipe number\n");
return;
}
if (dbri->pipes[pipe].sdp == 0) {
- printk("DBRI: xmit_on_pipe called on uninitialized pipe\n");
+ printk("DBRI: xmit_on_pipe: Uninitialized pipe %d\n", pipe);
return;
}
if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: xmit_on_pipe called on receive pipe\n");
- return;
- }
-
- /* XXX Fix this XXX
- * Should be able to queue multiple buffers to send on a pipe
- */
-
- if (dbri->pipes[pipe].desc != -1) {
- printk("DBRI: xmit_on_pipe called on active pipe\n");
+ printk("DBRI: xmit_on_pipe: Called on receive pipe %d\n",
+ pipe);
return;
}
@@ -707,10 +888,11 @@
while (len > 0) {
int mylen;
- for (td; td < DBRI_NO_DESCS; td ++) {
+ for (; td < DBRI_NO_DESCS; td ++) {
if (! dbri->descs[td].inuse) break;
}
if (td == DBRI_NO_DESCS) {
+ printk("DBRI: xmit_on_pipe: No descriptors\n");
break;
}
@@ -744,15 +926,10 @@
len -= mylen;
}
- if (first_td == -1) {
- printk("xmit_on_pipe: No descriptors available\n");
+ if (first_td == -1 || last_td == -1) {
return;
}
- if (len > 0) {
- printk("xmit_on_pipe: Insufficient descriptors; data truncated\n");
- }
-
dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
dbri->descs[last_td].buffer = buffer;
@@ -760,14 +937,54 @@
dbri->descs[last_td].output_callback = callback;
dbri->descs[last_td].output_callback_arg = callback_arg;
- dbri->pipes[pipe].desc = first_td;
+ for (td=first_td; td != -1; td = dbri->descs[td].next) {
+ dprintk(D_DESC, ("DBRI TD %d: %08x %08x %08x %08x\n",
+ td,
+ dbri->dma->desc[td].word1,
+ dbri->dma->desc[td].ba,
+ dbri->dma->desc[td].nda,
+ dbri->dma->desc[td].word4));
+ }
- cmd = dbri_cmdlock(dbri);
+ save_and_cli(flags);
- *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C);
- *(cmd++) = (int) & dbri->dma_dvma->desc[first_td];
+ if (pipe_active(dbri, pipe)) {
- dbri_cmdsend(dbri, cmd);
+ /* Pipe is already active - find last TD in use
+ * and link our first TD onto its end. Then issue
+ * a CDP command to let the DBRI know there's more data.
+ */
+
+ last_td = dbri->pipes[pipe].desc;
+ while (dbri->descs[last_td].next != -1)
+ last_td = dbri->descs[last_td].next;
+
+ dbri->descs[last_td].next = first_td;
+ dbri->dma->desc[last_td].nda =
+ (int) & dbri->dma_dvma->desc[first_td];
+
+ cmd = dbri_cmdlock(dbri);
+ *(cmd++) = DBRI_CMD(D_CDP, 0, pipe);
+ dbri_cmdsend(dbri,cmd);
+
+ } else {
+
+ /* Pipe isn't active - issue an SDP command to start
+ * our chain of TDs running.
+ */
+
+ dbri->pipes[pipe].desc = first_td;
+
+ cmd = dbri_cmdlock(dbri);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[pipe].sdp
+ | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+ *(cmd++) = (int) & dbri->dma_dvma->desc[first_td];
+ dbri_cmdsend(dbri, cmd);
+
+ }
+
+ restore_flags(flags);
}
static void recv_on_pipe(struct dbri *dbri, int pipe,
@@ -776,69 +993,107 @@
void * callback_arg)
{
volatile int *cmd;
+ int first_rd = -1;
+ int last_rd = -1;
int rd;
+ __u32 bus_buffer;
if (pipe < 0 || pipe > 15) {
- printk("DBRI: recv_on_pipe called with illegal pipe number\n");
+ printk("DBRI: recv_on_pipe: Illegal pipe number\n");
return;
}
if (dbri->pipes[pipe].sdp == 0) {
- printk("DBRI: recv_on_pipe called on uninitialized pipe\n");
+ printk("DBRI: recv_on_pipe: Uninitialized pipe %d\n", pipe);
return;
}
if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: recv_on_pipe called on transmit pipe\n");
+ printk("DBRI: recv_on_pipe: Called on transmit pipe %d\n",
+ pipe);
return;
}
/* XXX Fix this XXX
- * Should be able to queue multiple buffers to send on a pipe
+ * Should be able to queue multiple buffers to receive on a pipe
*/
if (dbri->pipes[pipe].desc != -1) {
- printk("DBRI: recv_on_pipe called on active pipe\n");
+ printk("DBRI: recv_on_pipe: Called on active pipe %d\n", pipe);
return;
}
- /* XXX Fix this XXX
- * Use multiple descriptors, if needed, to fit in all the data
- */
-
- if (len > (1 << 13) - 1) {
- printk("recv_on_pipe called with len=%d; truncated\n", len);
- len = (1 << 13) - 1;
- }
-
/* Make sure buffer size is multiple of four */
len &= ~3;
- for (rd = 0; rd < DBRI_NO_DESCS; rd ++) {
- if (! dbri->descs[rd].inuse) break;
+ bus_buffer = mmu_get_scsi_one(buffer, len, dbri->sdev->my_bus);
+
+ while (len > 0) {
+ int rd;
+ int mylen;
+
+ if (len > (1 << 13) - 4) {
+ mylen = (1 << 13) - 4;
+ } else {
+ mylen = len;
+ }
+
+ for (rd = 0; rd < DBRI_NO_DESCS; rd ++) {
+ if (! dbri->descs[rd].inuse) break;
+ }
+ if (rd == DBRI_NO_DESCS) {
+ printk("DBRI recv_on_pipe: No descriptors\n");
+ break;
+ }
+
+ dbri->dma->desc[rd].word1 = 0;
+ dbri->dma->desc[rd].ba = bus_buffer;
+ dbri->dma->desc[rd].nda = 0;
+ dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(mylen);
+
+ dbri->descs[rd].buffer = NULL;
+ dbri->descs[rd].len = 0;
+ dbri->descs[rd].input_callback = NULL;
+ dbri->descs[rd].output_callback = NULL;
+ dbri->descs[rd].next = -1;
+ dbri->descs[rd].inuse = 1;
+
+ if (first_rd == -1) first_rd = rd;
+ if (last_rd != -1) {
+ dbri->dma->desc[last_rd].nda =
+ (int) & dbri->dma_dvma->desc[rd];
+ dbri->descs[last_rd].next = rd;
+ }
+ last_rd = rd;
+
+ bus_buffer += mylen;
+ len -= mylen;
}
- if (rd == DBRI_NO_DESCS) {
- printk("DBRI xmit_on_pipe: No descriptors available\n");
+
+ if (last_rd == -1 || first_rd == -1) {
return;
}
- dbri->dma->desc[rd].word1 = 0;
- dbri->dma->desc[rd].ba = mmu_get_scsi_one(buffer, len,
- dbri->sdev->my_bus);
- dbri->dma->desc[rd].nda = 0;
- dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(len);
-
- dbri->descs[rd].buffer = buffer;
- dbri->descs[rd].len = len;
- dbri->descs[rd].input_callback = callback;
- dbri->descs[rd].input_callback_arg = callback_arg;
+ for (rd=first_rd; rd != -1; rd = dbri->descs[rd].next) {
+ dprintk(D_DESC, ("DBRI RD %d: %08x %08x %08x %08x\n",
+ rd,
+ dbri->dma->desc[rd].word1,
+ dbri->dma->desc[rd].ba,
+ dbri->dma->desc[rd].nda,
+ dbri->dma->desc[rd].word4));
+ }
+
+ dbri->descs[last_rd].buffer = buffer;
+ dbri->descs[last_rd].len = len;
+ dbri->descs[last_rd].input_callback = callback;
+ dbri->descs[last_rd].input_callback_arg = callback_arg;
- dbri->pipes[pipe].desc = rd;
+ dbri->pipes[pipe].desc = first_rd;
cmd = dbri_cmdlock(dbri);
- *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P);
- *(cmd++) = (int) & dbri->dma_dvma->desc[rd];
+ *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C);
+ *(cmd++) = (int) & dbri->dma_dvma->desc[first_rd];
dbri_cmdsend(dbri, cmd);
}
@@ -846,8 +1101,123 @@
/*
****************************************************************************
+************************** DBRI - CHI interface ****************************
+****************************************************************************
+
+The CHI is a four-wire (clock, frame sync, data in, data out) time-division
+multiplexed serial interface which the DBRI can operate in either master
+(give clock/frame sync) or slave (take clock/frame sync) mode.
+
+*/
+
+enum master_or_slave { CHImaster, CHIslave };
+
+static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave,
+ int bits_per_frame)
+{
+ volatile int *cmd;
+ int val;
+ static int chi_initialized=0;
+
+ if (!chi_initialized) {
+
+ cmd = dbri_cmdlock(dbri);
+
+ /* 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;
+
+ 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);
+ }
+
+ dbri->chi_in_pipe = 16;
+ dbri->chi_out_pipe = 16;
+
+ cmd = dbri_cmdlock(dbri);
+
+ }
+
+ if (master_or_slave == CHIslave) {
+ /* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
+ *
+ * CHICM = 0 (slave mode, 8 kHz frame rate)
+ * IR = give immediate CHI status interrupt
+ * EN = give CHI status interrupt upon change
+ */
+ *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
+ } 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
+ */
+
+ int clockrate = bits_per_frame * 8;
+ int divisor = 12288 / clockrate;
+
+ if (divisor > 255 || divisor * clockrate != 12288) {
+ printk("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));
+ }
+
+ dbri->chi_bpf = bits_per_frame;
+
+ /* CHI Data Mode
+ *
+ * RCE = 0 - receive on falling edge of CHICK
+ * XCE = 1 - transmit on rising edge of CHICK
+ * XEN = 1 - enable transmitter
+ * REN = 1 - enable receiver
+ */
+
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+
+ *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+
+ dbri_cmdsend(dbri, cmd);
+}
+
+/*
+****************************************************************************
*********************** CS4215 audio codec management **********************
****************************************************************************
+
+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.
+
*/
@@ -872,21 +1242,83 @@
* 2: Serial enable, CHI master, 128 bits per frame, clock 1
* 3: Tests disabled
*/
- mm->ctrl[0] = CS4215_RSRVD_1;
+ mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB;
mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
mm->ctrl[2] = CS4215_XCLK |
CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
mm->ctrl[3] = 0;
}
+static void mmcodec_setup_pipes(struct dbri *dbri)
+{
+ /*
+ * Data mode:
+ * Pipe 4: Send timeslots 1-4 (audio data)
+ * Pipe 20: Send timeslots 5-8 (part of ctrl data)
+ * Pipe 6: Receive timeslots 1-4 (audio data)
+ * Pipe 21: 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).
+ *
+ * Control mode:
+ * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+ * Pipe 18: Receive timeslot 1 (clb).
+ * Pipe 19: Receive timeslot 7 (version).
+ */
+
+ setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
+ setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+ setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB);
+ setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+
+ 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);
+
+ dbri->mm.status = 0;
+
+ recv_fixed(dbri, 18, & dbri->mm.status);
+ recv_fixed(dbri, 19, & dbri->mm.version);
+}
+
+static void mmcodec_setgain(struct dbri *dbri, int muted)
+{
+ if (muted || dbri->perchip_info.output_muted) {
+ dbri->mm.data[0] = 63;
+ dbri->mm.data[1] = 63;
+ } else {
+ int left_gain = (dbri->perchip_info.play.gain / 4) % 64;
+ int right_gain = (dbri->perchip_info.play.gain / 4) % 64;
+
+ if (dbri->perchip_info.play.balance < AUDIO_MID_BALANCE) {
+ right_gain *= dbri->perchip_info.play.balance;
+ right_gain /= AUDIO_MID_BALANCE;
+ } else {
+ left_gain *= AUDIO_RIGHT_BALANCE
+ - dbri->perchip_info.play.balance;
+ left_gain /= AUDIO_MID_BALANCE;
+ }
+
+ dprintk(D_MM, ("DBRI: Setting codec gain left: %d right: %d\n",
+ left_gain, right_gain));
+
+ dbri->mm.data[0] = CS4215_LE | CS4215_HE | (63 - left_gain);
+ dbri->mm.data[1] = CS4215_SE | (63 - right_gain);
+ }
+
+ xmit_fixed(dbri, 20, *(int *)dbri->mm.data);
+}
+
static void mmcodec_init_data(struct dbri *dbri)
{
+ int data_width;
+
/*
* Data mode:
* Pipe 4: Send timeslots 1-4 (audio data)
- * Pipe 17: Send timeslots 5-8 (part of ctrl data)
+ * Pipe 20: Send timeslots 5-8 (part of ctrl data)
* Pipe 6: Receive timeslots 1-4 (audio data)
- * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via
+ * Pipe 21: 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).
*
@@ -896,35 +1328,55 @@
*/
+ dbri->regs->reg0 &= ~D_C; /* Disable CHI */
+
/* 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);
- reset_chi(dbri, CHIslave, 0);
+ reset_chi(dbri, CHIslave, 128);
+
+ /* Note: this next doesn't work for 8-bit stereo, because the two
+ * channels would be on timeslots 1 and 3, with 2 and 4 idle.
+ * (See CS4215 datasheet Fig 15)
+ *
+ * DBRI non-contiguous mode would be required to make this work.
+ */
- setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
- setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
- setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB);
- setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+ data_width = dbri->perchip_info.play.channels
+ * dbri->perchip_info.play.precision;
- /* Pipes 4 and 6 - Single time slot, 8 bit mono */
+ 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, 17, PIPEoutput, 16, 32, 32);
- link_time_slot(dbri, 4, PIPEoutput, 17, 8, 128);
- link_time_slot(dbri, 6, PIPEinput, 16, 8, 0);
- link_time_slot(dbri, 20, PIPEinput, 6, 16, 40);
+ mmcodec_setgain(dbri, 0);
- xmit_fixed(dbri, 17, *(int *)dbri->mm.data);
+ dbri->regs->reg0 |= D_C; /* Enable CHI */
}
/*
* Send the control information (i.e. audio format)
*/
-static void mmcodec_setctrl(struct dbri *dbri)
+static int mmcodec_setctrl(struct dbri *dbri)
{
int i, val;
+ /* XXX - let the CPU do something useful during these delays */
+
+ /* Temporarily mute outputs, and wait 1/8000 sec (125 us)
+ * to make sure this takes. This avoids clicking noises.
+ */
+
+ mmcodec_setgain(dbri, 1);
+ udelay(125);
+
/*
* 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
@@ -952,6 +1404,8 @@
* frame sync signal by eight clock cycles. Anybody know why?
*/
+ dbri->regs->reg0 &= ~D_C; /* Disable CHI */
+
reset_chi(dbri, CHImaster, 128);
/*
@@ -961,27 +1415,26 @@
* Pipe 19: Receive timeslot 7 (version).
*/
- setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
- setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
- setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
-
- link_time_slot(dbri, 17, PIPEoutput, 16, 32, 128);
- link_time_slot(dbri, 18, PIPEinput, 16, 8, 0);
- link_time_slot(dbri, 19, PIPEinput, 18, 8, 48);
-
- recv_fixed(dbri, 18, & dbri->mm.status);
- recv_fixed(dbri, 19, & dbri->mm.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);
/* 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);
- i = 1000000;
- while ((! dbri->mm.status & CS4215_CLB) && i--);
+ dbri->regs->reg0 |= D_C; /* Enable CHI */
+
+ i = 10;
+ while (((dbri->mm.status & 0xe4) != 0x20) && --i) udelay(125);
if (i == 0) {
- printk("CS4215 didn't respond to CLB\n");
- return;
+ dprintk(D_MM, ("DBRI: CS4215 didn't respond to CLB (0x%02x)\n",
+ dbri->mm.status));
+ return -1;
}
/* Terminate CS4215 control mode - data sheet says
@@ -993,6 +1446,10 @@
/* Two frames of control info @ 8kHz frame rate = 250 us delay */
udelay(250);
+
+ mmcodec_setgain(dbri, 0);
+
+ return 0;
}
static int mmcodec_init(struct sparcaudio_driver *drv)
@@ -1024,16 +1481,24 @@
}
- /* Now talk to our baby */
- dbri->regs->reg0 |= D_C; /* Enable CHI */
+ mmcodec_setup_pipes(dbri);
mmcodec_default(&dbri->mm);
dbri->mm.version = 0xff;
- mmcodec_setctrl(dbri);
- if(dbri->mm.version == 0xff)
+ dbri->mm.offset = dbri->mm.onboard ? 0 : 8;
+ if (mmcodec_setctrl(dbri) == -1 || dbri->mm.version == 0xff) {
+ dprintk(D_MM, ("DBRI: CS4215 failed probe at offset %d\n",
+ dbri->mm.offset));
return -EIO;
+ }
+
+ dprintk(D_MM, ("DBRI: Found CS4215 at offset %d\n", dbri->mm.offset));
+ dbri->perchip_info.play.channels = 1;
+ dbri->perchip_info.play.precision = 8;
+ dbri->perchip_info.play.gain = 255;
+ dbri->perchip_info.play.balance = AUDIO_MID_BALANCE;
mmcodec_init_data(dbri);
return 0;
@@ -1044,37 +1509,25 @@
****************************************************************************
******************** Interface with sparcaudio midlevel ********************
****************************************************************************
-*/
+The sparcaudio midlevel is contained in the file audio.c. It interfaces
+to the user process and performs buffering, intercepts SunOS-style ioctl's,
+etc. It interfaces to a abstract audio device via a struct sparcaudio_driver.
+This code presents such an interface for the DBRI with an attached CS4215.
+All our routines are defined, and then comes our struct sparcaudio_driver.
-static int dbri_open(struct inode * inode, struct file * file,
- struct sparcaudio_driver *drv)
-{
- struct dbri *dbri = (struct dbri *)drv->private;
-
- MOD_INC_USE_COUNT;
+*/
- return 0;
-}
+/******************* sparcaudio midlevel - audio output *******************/
-static void dbri_release(struct inode * inode, struct file * file,
- struct sparcaudio_driver *drv)
-{
- MOD_DEC_USE_COUNT;
-}
-
-static int dbri_ioctl(struct inode * inode, struct file * file,
- unsigned int x, unsigned long y,
- struct sparcaudio_driver *drv)
-{
- return 0;
-}
static void dbri_audio_output_callback(void * callback_arg, int status)
{
struct sparcaudio_driver *drv = callback_arg;
- sparcaudio_output_done(drv, 1);
+ if (status != -1) {
+ sparcaudio_output_done(drv, 1);
+ }
}
static void dbri_start_output(struct sparcaudio_driver *drv,
@@ -1082,8 +1535,31 @@
{
struct dbri *dbri = (struct dbri *)drv->private;
+ dprintk(D_USR, ("DBRI: start audio output buf=%lx/%ld\n",
+ (unsigned long) buffer, count));
+
/* Pipe 4 is audio transmit */
- xmit_on_pipe(dbri, 4, buffer, count, &dbri_audio_output_callback, drv);
+
+ xmit_on_pipe(dbri, 4, buffer, count,
+ &dbri_audio_output_callback, drv);
+
+#if 0
+ /* Notify midlevel that we're a DMA-capable driver that
+ * can accept another buffer immediately. We should probably
+ * check that we've got enough resources (i.e, descriptors)
+ * available before doing this, but the default midlevel
+ * settings only buffer 64KB, which we can handle with 16
+ * of our DBRI_NO_DESCS (64) descriptors.
+ *
+ * This code is #ifdef'ed out because it's caused me more
+ * problems than it solved. It'd be nice to provide the
+ * DBRI with a chain of buffers, but the midlevel code is
+ * so tricky that I really don't want to deal with it.
+ */
+
+ sparcaudio_output_done(drv, 2);
+#endif
+
}
static void dbri_stop_output(struct sparcaudio_driver *drv)
@@ -1093,28 +1569,55 @@
reset_pipe(dbri, 4);
}
+/******************* sparcaudio midlevel - audio input ********************/
+
+static void dbri_audio_input_callback(void * callback_arg, int status,
+ unsigned int len)
+{
+ struct sparcaudio_driver * drv =
+ (struct sparcaudio_driver *) callback_arg;
+
+ if (status != -1) {
+ sparcaudio_input_done(drv, 3);
+ }
+}
+
static void dbri_start_input(struct sparcaudio_driver *drv,
__u8 * buffer, unsigned long len)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ /* Pipe 6 is audio receive */
+ recv_on_pipe(dbri, 6, buffer, len,
+ &dbri_audio_input_callback, (void *)drv);
+ dprintk(D_USR, ("DBRI: start audio input buf=%lx/%ld\n",
+ (unsigned long) buffer, len));
}
static void dbri_stop_input(struct sparcaudio_driver *drv)
{
-}
+ struct dbri *dbri = (struct dbri *)drv->private;
-static void dbri_audio_getdev(struct sparcaudio_driver *drv,
- audio_device_t *devptr)
-{
+ reset_pipe(dbri, 6);
}
+/******************* sparcaudio midlevel - volume & balance ***************/
+
static int dbri_set_output_volume(struct sparcaudio_driver *drv, int volume)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ dbri->perchip_info.play.gain = volume;
+ mmcodec_setgain(dbri, 0);
+
return 0;
}
static int dbri_get_output_volume(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.gain;
}
static int dbri_set_input_volume(struct sparcaudio_driver *drv, int volume)
@@ -1139,12 +1642,19 @@
static int dbri_set_output_balance(struct sparcaudio_driver *drv, int balance)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ dbri->perchip_info.play.balance = balance;
+ mmcodec_setgain(dbri, 0);
+
return 0;
}
static int dbri_get_output_balance(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.balance;
}
static int dbri_set_input_balance(struct sparcaudio_driver *drv, int balance)
@@ -1157,107 +1667,205 @@
return 0;
}
+static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ dbri->perchip_info.output_muted = mute;
+
+ return 0;
+}
+
+static int dbri_get_output_muted(struct sparcaudio_driver *drv)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.output_muted;
+}
+
+/******************* sparcaudio midlevel - encoding format ****************/
+
static int dbri_set_output_channels(struct sparcaudio_driver *drv, int chan)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ switch (chan) {
+ case 0:
+ return 0;
+ case 1:
+ dbri->mm.ctrl[1] &= ~CS4215_DFR_STEREO;
+ break;
+ case 2:
+ dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
+ break;
+ default:
+ return -1;
+ }
+
+ dbri->perchip_info.play.channels = chan;
+ mmcodec_setctrl(dbri);
+ mmcodec_init_data(dbri);
return 0;
}
static int dbri_get_output_channels(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.channels;
}
static int dbri_set_input_channels(struct sparcaudio_driver *drv, int chan)
{
- return 0;
+ return dbri_set_output_channels(drv, chan);
}
static int dbri_get_input_channels(struct sparcaudio_driver *drv)
{
- return 0;
+ return dbri_get_output_channels(drv);
}
static int dbri_set_output_precision(struct sparcaudio_driver *drv, int prec)
{
- return 8;
+ return 0;
}
static int dbri_get_output_precision(struct sparcaudio_driver *drv)
{
- return 8;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.precision;
}
static int dbri_set_input_precision(struct sparcaudio_driver *drv, int prec)
{
- return 8;
+ return 0;
}
static int dbri_get_input_precision(struct sparcaudio_driver *drv)
{
- return 8;
-}
+ struct dbri *dbri = (struct dbri *)drv->private;
-static int dbri_set_output_port(struct sparcaudio_driver *drv, int port)
-{
- return 0;
+ return dbri->perchip_info.play.precision;
}
-static int dbri_get_output_port(struct sparcaudio_driver *drv)
+static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ /* For ULAW and ALAW, audio.c enforces precision = 8,
+ * for LINEAR, precision must be 16
+ */
+
+ switch (enc) {
+ case AUDIO_ENCODING_NONE:
+ return 0;
+ case AUDIO_ENCODING_ULAW:
+ dbri->mm.ctrl[1] &= ~3;
+ dbri->mm.ctrl[1] |= CS4215_DFR_ULAW;
+ dbri->perchip_info.play.encoding = enc;
+ dbri->perchip_info.play.precision = 8;
+ break;
+ case AUDIO_ENCODING_ALAW:
+ dbri->mm.ctrl[1] &= ~3;
+ dbri->mm.ctrl[1] |= CS4215_DFR_ALAW;
+ dbri->perchip_info.play.encoding = enc;
+ dbri->perchip_info.play.precision = 8;
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ dbri->mm.ctrl[1] &= ~3;
+ dbri->mm.ctrl[1] |= CS4215_DFR_LINEAR16;
+ dbri->perchip_info.play.encoding = enc;
+ dbri->perchip_info.play.precision = 16;
+ break;
+ default:
+ return -1;
+ }
+ mmcodec_setctrl(dbri);
+ mmcodec_init_data(dbri);
return 0;
}
-static int dbri_set_input_port(struct sparcaudio_driver *drv, int port)
+static int dbri_get_output_encoding(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.encoding;
}
-static int dbri_get_input_port(struct sparcaudio_driver *drv)
+static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc)
{
- return 0;
+ return dbri_set_output_encoding(drv, enc);
}
-static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc)
+static int dbri_get_input_encoding(struct sparcaudio_driver *drv)
{
- return 0;
+ return dbri_get_output_encoding(drv);
}
-static int dbri_get_output_encoding(struct sparcaudio_driver *drv)
+static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+ int i;
+
+ if (rate == 0) {
+ return 0;
+ }
+
+ for (i=0; CS4215_FREQ[i].freq; i++) {
+ if (CS4215_FREQ[i].freq == rate) break;
+ }
+ if (CS4215_FREQ[i].freq == 0) {
+ return -1;
+ }
+
+ dbri->mm.ctrl[1] &= ~ 0x38;
+ dbri->mm.ctrl[1] |= CS4215_FREQ[i].csval;
+ dbri->mm.ctrl[2] &= ~ 0x70;
+ dbri->mm.ctrl[2] |= CS4215_FREQ[i].xtal;
+
+ dbri->perchip_info.play.sample_rate = rate;
+
+ mmcodec_setctrl(dbri);
+ mmcodec_init_data(dbri);
return 0;
}
-static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc)
+static int dbri_get_output_rate(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.sample_rate;
}
-static int dbri_get_input_encoding(struct sparcaudio_driver *drv)
+static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate)
{
- return 0;
+ return dbri_set_output_rate(drv, rate);
}
-static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate)
+static int dbri_get_input_rate(struct sparcaudio_driver *drv)
{
- return 0;
+ return dbri_get_output_rate(drv);
}
-static int dbri_get_output_rate(struct sparcaudio_driver *drv)
+/******************* sparcaudio midlevel - ports ***********************/
+
+static int dbri_set_output_port(struct sparcaudio_driver *drv, int port)
{
return 0;
}
-static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate)
+static int dbri_get_output_port(struct sparcaudio_driver *drv)
{
return 0;
}
-static int dbri_get_input_rate(struct sparcaudio_driver *drv)
+static int dbri_set_input_port(struct sparcaudio_driver *drv, int port)
{
return 0;
}
-static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
+static int dbri_get_input_port(struct sparcaudio_driver *drv)
{
return 0;
}
@@ -1272,17 +1880,66 @@
return 0;
}
-static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute)
+/******************* sparcaudio midlevel - driver ID ********************/
+
+static void dbri_audio_getdev(struct sparcaudio_driver *drv,
+ audio_device_t *audinfo)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ strncpy(audinfo->name, "SUNW,DBRI", sizeof(audinfo->name) - 1);
+
+ audinfo->version[0] = dbri->dbri_version;
+ audinfo->version[1] = '\0';
+
+ strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1);
}
-static int dbri_get_output_muted(struct sparcaudio_driver *drv)
+static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
{
- return 0;
+ return AUDIO_DEV_CODEC;
}
+/******************* sparcaudio midlevel - open & close ******************/
+static int dbri_open(struct inode * inode, struct file * file,
+ struct sparcaudio_driver *drv)
+{
+ MOD_INC_USE_COUNT;
+
+ /* SunOS 5.5.1 audio(7I) man page says:
+ * "Upon the initial open() of the audio device, the driver
+ * will reset the data format of the device to the default
+ * state of 8-bit, 8KHz, mono u-law data."
+ *
+ * I've also taken the liberty of setting half gain and
+ * mid balance, to put the codec in a known state.
+ */
+
+ dbri_set_output_channels(drv, 1);
+ dbri_set_output_encoding(drv, AUDIO_ENCODING_ULAW);
+ dbri_set_output_rate(drv, 8000);
+
+ dbri_set_output_balance(drv, AUDIO_MID_BALANCE);
+ dbri_set_output_volume(drv, AUDIO_MAX_GAIN/2);
+
+ return 0;
+}
+
+static void dbri_release(struct inode * inode, struct file * file,
+ struct sparcaudio_driver *drv)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static int dbri_ioctl(struct inode * inode, struct file * file,
+ unsigned int x, unsigned long y,
+ struct sparcaudio_driver *drv)
+{
+ return -EINVAL;
+}
+
+/*********** sparcaudio midlevel - struct sparcaudio_driver ************/
static struct sparcaudio_operations dbri_ops = {
dbri_open,
@@ -1357,8 +2014,8 @@
setup_pipe(dbri,11, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB);
link_time_slot(dbri, 0, PIPEinput, 0, 2, 17);
- link_time_slot(dbri, 8, PIPEinput, 8, 8, 0);
- link_time_slot(dbri, 9, PIPEinput, 9, 8, 8);
+ link_time_slot(dbri, 8, PIPEinput, 0, 8, 0);
+ link_time_slot(dbri, 9, PIPEinput, 8, 8, 8);
link_time_slot(dbri, 1, PIPEoutput, 1, 2, 17);
link_time_slot(dbri, 10, PIPEoutput, 1, 8, 0);
@@ -1375,6 +2032,8 @@
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_get_irqnum()\n"));
+
/* On the sparc, the cpu's irq number is only part of the "irq" */
return (dbri->irq & NR_IRQS);
}
@@ -1389,6 +2048,8 @@
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_get_liu_state() returns %d\n", dbri->liu_state));
+
return dbri->liu_state;
}
@@ -1404,12 +2065,14 @@
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_liu_init()\n"));
+
/* Set callback for LIU state change */
- dbri->liu_callback = callback;
+ dbri->liu_callback = callback;
dbri->liu_callback_arg = callback_arg;
- dbri_isdn_init(dbri);
- dbri_liu_activate(dev, 0);
+ dbri_isdn_init(dbri);
+ dbri_liu_activate(dev, 0);
}
void dbri_liu_activate(int dev, int priority)
@@ -1424,16 +2087,24 @@
dbri = (struct dbri *) drivers[dev].private;
- cmd = dbri_cmdlock(dbri);
+ tprintk(("dbri_liu_activate()\n"));
- /* Turn on the ISDN TE interface and request activation */
- val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
- *(cmd++) = DBRI_CMD(D_TE, 0, val);
+ if (dbri->liu_state <= 3) {
- dbri_cmdsend(dbri, cmd);
+ cmd = dbri_cmdlock(dbri);
+
+ /* Turn on the ISDN TE interface and request activation */
+ val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
+#ifdef LOOPBACK_D
+ val |= D_NT_LLB(4);
+#endif
+ *(cmd++) = DBRI_CMD(D_TE, 0, val);
- /* Activate the interface */
- dbri->regs->reg0 |= D_T;
+ dbri_cmdsend(dbri, cmd);
+
+ /* Activate the interface */
+ dbri->regs->reg0 |= D_T;
+ }
}
void dbri_liu_deactivate(int dev)
@@ -1446,8 +2117,14 @@
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_liu_deactivate()\n"));
+
+#if 0
/* Turn off the ISDN TE interface */
dbri->regs->reg0 &= ~D_T;
+
+ dbri->liu_state = 0;
+#endif
}
void dbri_dxmit(int dev, __u8 *buffer, unsigned int count,
@@ -1613,6 +2290,11 @@
"DBRI DMA Cmd Block", &dma_dvma);
dbri->dma_dvma = (struct dbri_dma *) dma_dvma;
+ memset((void *) dbri->dma, 0, sizeof(struct dbri_dma));
+
+ dprintk(D_GEN, ("DBRI: DMA Cmd Block 0x%08x (0x%08x)\n",
+ (int)dbri->dma, (int)dbri->dma_dvma));
+
dbri->dbri_version = sdev->prom_name[9];
dbri->sdev = sdev;
@@ -1625,6 +2307,8 @@
"DBRI Registers", sdev->reg_addrs[0].which_io, 0);
if (!dbri->regs) {
printk(KERN_ERR "DBRI: could not allocate registers\n");
+ release_region((unsigned long) dbri->dma,
+ sizeof(struct dbri_dma));
kfree(drv->private);
return -EIO;
}
@@ -1637,6 +2321,8 @@
if (err) {
printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
sparc_free_io(dbri->regs, dbri->regs_size);
+ release_region((unsigned long) dbri->dma,
+ sizeof(struct dbri_dma));
kfree(drv->private);
return err;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)