summaryrefslogtreecommitdiffstats
path: root/drivers/sbus/audio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sbus/audio')
-rw-r--r--drivers/sbus/audio/Makefile20
-rw-r--r--drivers/sbus/audio/amd7930.c1160
-rw-r--r--drivers/sbus/audio/amd7930.h129
-rw-r--r--drivers/sbus/audio/audio.c585
-rw-r--r--drivers/sbus/audio/audio.h119
-rw-r--r--drivers/sbus/audio/cs4215.h120
-rw-r--r--drivers/sbus/audio/cs4231.c107
-rw-r--r--drivers/sbus/audio/dbri.c715
-rw-r--r--drivers/sbus/audio/dbri.h293
9 files changed, 3037 insertions, 211 deletions
diff --git a/drivers/sbus/audio/Makefile b/drivers/sbus/audio/Makefile
index f870e0da5..3dcb06f82 100644
--- a/drivers/sbus/audio/Makefile
+++ b/drivers/sbus/audio/Makefile
@@ -16,37 +16,37 @@ O_OBJS :=
M_OBJS :=
ifeq ($(CONFIG_SPARCAUDIO),y)
-M=y
+SBUS_AUDIO=y
else
ifeq ($(CONFIG_SPARCAUDIO),m)
- MM=y
+ SBUS_AUDIO_MODULE=y
endif
endif
ifeq ($(CONFIG_SPARCAUDIO_AMD7930),y)
-M=y
-O_OBJS += amd7930.o
+SBUS_AUDIO=y
+OX_OBJS += amd7930.o
else
ifeq ($(CONFIG_SPARCAUDIO_AMD7930),m)
- MM=y
- M_OBJS += amd7930.o
+ SBUS_AUDIO_MODULE=y
+ MX_OBJS += amd7930.o
endif
endif
ifeq ($(CONFIG_SPARCAUDIO_CS4231),y)
-M=y
+SBUS_AUDIO=y
O_OBJS += cs4231.o
else
ifeq ($(CONFIG_SPARCAUDIO_CS4231),m)
- MM=y
+ SBUS_AUDIO_MODULE=y
M_OBJS += cs4231.o
endif
endif
-ifdef M
+ifdef SBUS_AUDIO
OX_OBJS += audio.o
else
- ifdef MM
+ ifdef SBUS_AUDIO_MODULE
MX_OBJS += audio.o
endif
endif
diff --git a/drivers/sbus/audio/amd7930.c b/drivers/sbus/audio/amd7930.c
index 1134eb74e..6e54cbf79 100644
--- a/drivers/sbus/audio/amd7930.c
+++ b/drivers/sbus/audio/amd7930.c
@@ -1,15 +1,20 @@
/*
* drivers/sbus/audio/amd7930.c
*
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
+ * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
*
* This is the lowlevel driver for the AMD7930 audio chip found on all
* sun4c machines and some sun4m machines.
*
- * XXX Add note about the fun of getting the docs.
+ * The amd7930 is actually an ISDN chip which has a very simple
+ * integrated audio encoder/decoder. When Sun decided on what chip to
+ * use for audio, they had the brilliant idea of using the amd7930 and
+ * only connecting the audio encoder/decoder pins.
+ *
+ * Thanks to the AMD engineer who was able to get us the AMD79C30
+ * databook which has all the programming information and gain tables.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -29,26 +34,78 @@
#define MAX_DRIVERS 1
-/* Private information we store for each amd7930 chip. */
-struct amd7930_info {
- /* Current buffer that the driver is playing. */
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static int num_drivers;
+
+/* Each amd7930 chip has two bi-directional B channels and a D
+ * channel available to the uproc. This structure handles all
+ * the buffering needed to transmit and receive via a single channel.
+ */
+
+#define CHANNEL_AVAILABLE 0x00
+#define CHANNEL_INUSE_AUDIO_IN 0x01
+#define CHANNEL_INUSE_AUDIO_OUT 0x02
+#define CHANNEL_INUSE_ISDN_B1 0x04
+#define CHANNEL_INUSE_ISDN_B2 0x08
+#define CHANNEL_INUSE 0xff
+
+struct amd7930_channel {
+ /* Channel status */
+ unsigned char channel_status;
+
+ /* Current buffer that the driver is playing on channel */
volatile __u8 * output_ptr;
volatile unsigned long output_count;
+ unsigned char xmit_idle_char;
- /* Current record buffer. */
+ /* Callback routine (and argument) when output is done on */
+ void (*output_callback)();
+ void * output_callback_arg;
+
+ /* Current buffer that the driver is recording on channel */
volatile __u8 * input_ptr;
volatile unsigned long input_count;
+ volatile unsigned long input_limit;
+
+ /* Callback routine (and argument) when input is done on */
+ void (*input_callback)();
+ void * input_callback_arg;
+};
+
+/* Private information we store for each amd7930 chip. */
+struct amd7930_info {
+ struct amd7930_channel D;
+ struct amd7930_channel Bb;
+ struct amd7930_channel Bc;
+
+ /* Pointers to which B channels are being used for what
+ * These three fields (Baudio, Bisdn[0], and Bisdn[1]) will either
+ * be NULL or point to one of the Bb/Bc structures above.
+ */
+ struct amd7930_channel *Baudio;
+ struct amd7930_channel *Bisdn[2];
/* Device registers information. */
struct amd7930 *regs;
unsigned long regs_size;
struct amd7930_map map;
+ /* Volume information. */
+ int pgain, rgain, mgain;
+
/* Device interrupt information. */
int irq;
volatile int ints_on;
+
+
+ /* Someone to signal when the ISDN LIU state changes */
+ int liu_state;
+ void (*liu_callback)(void *);
+ void *liu_callback_arg;
};
+
+
/* Output a 16-bit quantity in the order that the amd7930 expects. */
#define amd7930_out16(regs,v) ({ regs->dr = v & 0xFF; regs->dr = (v >> 8) & 0xFF; })
@@ -120,9 +177,8 @@ static __const__ __u16 ger_coeff[] = {
#define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
/* Enable amd7930 interrupts atomically. */
-static __inline__ void amd7930_enable_ints(struct sparcaudio_driver *drv)
+static __inline__ void amd7930_enable_ints(struct amd7930_info *info)
{
- struct amd7930_info *info = (struct amd7930_info *)drv->private;
register unsigned long flags;
if (info->ints_on)
@@ -137,9 +193,8 @@ static __inline__ void amd7930_enable_ints(struct sparcaudio_driver *drv)
}
/* Disable amd7930 interrupts atomically. */
-static __inline__ void amd7930_disable_ints(struct sparcaudio_driver *drv)
+static __inline__ void amd7930_disable_ints(struct amd7930_info *info)
{
- struct amd7930_info *info = (struct amd7930_info *)drv->private;
register unsigned long flags;
if (!info->ints_on)
@@ -153,8 +208,24 @@ static __inline__ void amd7930_disable_ints(struct sparcaudio_driver *drv)
info->ints_on = 0;
}
+/* Idle amd7930 (no interrupts, no audio, no data) */
+static __inline__ void amd7930_idle(struct amd7930_info *info)
+{
+ register unsigned long flags;
+
+ if (!info->ints_on)
+ return;
+
+ save_and_cli(flags);
+ info->regs->cr = AMR_INIT;
+ info->regs->dr = 0;
+ restore_flags(flags);
+
+ info->ints_on = 0;
+}
+
/* Commit the local copy of the MAP registers to the amd7930. */
-static void amd7930_commit_map(struct sparcaudio_driver *drv)
+static void amd7930_write_map(struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
struct amd7930 *regs = info->regs;
@@ -184,71 +255,257 @@ static void amd7930_commit_map(struct sparcaudio_driver *drv)
restore_flags(flags);
}
-/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */
-static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs)
+/* Update the MAP registers with new settings. */
+static void amd7930_update_map(struct sparcaudio_driver *drv)
{
- struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
struct amd7930_info *info = (struct amd7930_info *)drv->private;
- struct amd7930 *regs = info->regs;
+ struct amd7930_map *map = &info->map;
+ int level;
+
+ map->gx = gx_coeff[info->rgain];
+ map->stgr = gx_coeff[info->mgain];
+
+ level = (info->pgain * (256 + NR_GER_COEFFS)) >> 8;
+ if (level >= 256) {
+ map->ger = ger_coeff[level - 256];
+ map->gr = gx_coeff[255];
+ } else {
+ map->ger = ger_coeff[0];
+ map->gr = gx_coeff[level];
+ }
+
+ /* force output to speaker for now */
+ map->mmr2 |= AM_MAP_MMR2_LS;
+
+ /* input from external microphone */
+ map->mmr2 |= AM_MAP_MMR2_AINB;
+
+ amd7930_write_map(drv);
+}
+
+/* Bit of a hack here - if the HISAX ISDN driver has got INTSTAT debugging
+ * turned on, we send debugging characters to the ISDN driver:
+ *
+ * i# - Interrupt received - number from 0 to 7 is low three bits of IR
+ * > - Loaded a single char into the Dchan xmit FIFO
+ * + - Finished loading an xmit packet into the Dchan xmit FIFO
+ * < - Read a single char from the Dchan recv FIFO
+ * ! - Finished reading a packet from the Dchan recv FIFO
+ *
+ * This code needs to be removed if anything other than HISAX uses the ISDN
+ * driver, since D.output_callback_arg is assumed to be a certain struct ptr
+ */
+
+#include "../../isdn/hisax/hisax.h"
+#include "../../isdn/hisax/isdnl1.h"
+
+#ifdef L2FRAME_DEBUG
+
+inline void debug_info(struct amd7930_info *info, char c) {
+ struct IsdnCardState *cs;
+
+ if (!info || !info->D.output_callback_arg) return;
+
+ cs = (struct IsdnCardState *)info->D.output_callback_arg;
+
+ if (!cs || !cs->status_write) return;
+
+ if (cs->debug & L1_DEB_INTSTAT) {
+ *(cs->status_write++) = c;
+ if (cs->status_write > cs->status_end)
+ cs->status_write = cs->status_buf;
+ }
+}
+
+#else
+
+#define debug_info(info,c)
+
+#endif
+
+
+static void fill_D_xmit_fifo(struct amd7930_info *info)
+{
+ /* Send next byte(s) of outgoing data. */
+ while (info->D.output_ptr && info->D.output_count > 0 &&
+ (info->regs->dsr2 & AMR_DSR2_TBE)) {
+
+ /* Send the next byte and advance buffer pointer. */
+ info->regs->dctb = *(info->D.output_ptr);
+ info->D.output_ptr++;
+ info->D.output_count--;
+
+ debug_info(info, '>');
+ }
+}
+
+static void transceive_Dchannel(struct amd7930_info *info)
+{
__u8 dummy;
+ int lbrp=0; /* Last Byte of Received Packet (LBRP) */
+
+#define D_XMIT_ERRORS (AMR_DER_COLLISION | AMR_DER_UNRN)
+#define D_RECV_ERRORS (AMR_DER_RABRT | AMR_DER_RFRAME | AMR_DER_FCS | \
+ AMR_DER_OVFL | AMR_DER_UNFL | AMR_DER_OVRN)
+
+ /* Transmit if we can */
+ fill_D_xmit_fifo(info);
+
+ /* Done with the xmit buffer? Notify the midlevel driver. */
+ if (info->D.output_ptr != NULL && info->D.output_count == 0) {
+ info->D.output_ptr = NULL;
+ info->D.output_count = 0;
+ debug_info(info, '+');
+ if (info->D.output_callback)
+ (*info->D.output_callback)
+ (info->D.output_callback_arg,
+ info->regs->der);
+ /* info->regs->der & D_XMIT_ERRORS); */
+ }
- /* Clear the interrupt. */
- dummy = regs->ir;
+ /* Read the next byte(s) of incoming data. */
+
+ while (info->regs->dsr2 & AMR_DSR2_RBA) {
+
+ if (info->D.input_ptr &&
+ (info->D.input_count < info->D.input_limit)) {
+
+ /* Get the next byte and advance buffer pointer. */
+
+ *(info->D.input_ptr) = info->regs->dcrb;
+ info->D.input_ptr++;
+ info->D.input_count++;
+
+ } else {
+ /* Overflow - should be detected by chip via RBLR
+ * so we'll just consume data until we see LBRP
+ */
+
+ dummy = info->regs->dcrb;
+
+ }
+
+ debug_info(info, '<');
+
+ if (info->regs->dsr2 & AMR_DSR2_LBRP) {
+
+ /* End of recv packet? Notify the midlevel driver. */
+
+ __u8 der;
+
+ debug_info(info, '!');
+
+ info->D.input_ptr = NULL;
+
+ der = info->regs->der & D_RECV_ERRORS;
+
+ /* Read receive byte count - advances FIFOs */
+ info->regs->cr = AMR_DLC_DRCR;
+ dummy = info->regs->dr;
+ dummy = info->regs->dr;
+
+ if (info->D.input_callback)
+ (*info->D.input_callback)
+ (info->D.input_callback_arg, der,
+ info->D.input_count);
+ }
+
+ }
+}
+
+long amd7930_xmit_idles=0;
+
+static void transceive_Bchannel(struct amd7930_channel *channel,
+ __volatile__ __u8 *io_reg)
+{
/* Send the next byte of outgoing data. */
- if (info->output_ptr && info->output_count > 0) {
+ if (channel->output_ptr && channel->output_count > 0) {
+
/* Send the next byte and advance buffer pointer. */
- regs->bbtb = *(info->output_ptr);
- info->output_ptr++;
- info->output_count--;
+ *io_reg = *(channel->output_ptr);
+ channel->output_ptr++;
+ channel->output_count--;
/* Done with the buffer? Notify the midlevel driver. */
- if (info->output_count == 0) {
- info->output_ptr = NULL;
- info->output_count = 0;
- sparcaudio_output_done(drv);
+ if (channel->output_count == 0) {
+ channel->output_ptr = NULL;
+ channel->output_count = 0;
+ if (channel->output_callback)
+ (*channel->output_callback)
+ (channel->output_callback_arg);
}
- }
+ } else {
+ *io_reg = channel->xmit_idle_char;
+ amd7930_xmit_idles++;
+ }
/* Read the next byte of incoming data. */
- if (info->input_ptr && info->input_count > 0) {
+ if (channel->input_ptr && channel->input_count > 0) {
+
/* Get the next byte and advance buffer pointer. */
- *(info->input_ptr) = regs->bbrb;
- info->input_ptr++;
- info->input_count--;
+ *(channel->input_ptr) = *io_reg;
+ channel->input_ptr++;
+ channel->input_count--;
/* Done with the buffer? Notify the midlevel driver. */
- if (info->input_count == 0) {
- info->input_ptr = NULL;
- info->input_count = 0;
- sparcaudio_input_done(drv);
+ if (channel->input_count == 0) {
+ channel->input_ptr = NULL;
+ channel->input_count = 0;
+ if (channel->input_callback)
+ (*channel->input_callback)
+ (channel->input_callback_arg);
}
}
}
-
-static int amd7930_open(struct inode * inode, struct file * file,
- struct sparcaudio_driver *drv)
+/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */
+static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs)
{
+ struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
struct amd7930_info *info = (struct amd7930_info *)drv->private;
- int level;
+ struct amd7930 *regs = info->regs;
+ __u8 ir;
+ __u8 lsr;
- /* Set the default audio parameters. */
- info->map.gx = gx_coeff[128];
- info->map.stgr = gx_coeff[0];
+ /* Clear the interrupt. */
+ ir = regs->ir;
- level = (128 * (256 + NR_GER_COEFFS)) >> 8;
- if (level >= 256) {
- info->map.ger = ger_coeff[level-256];
- info->map.gr = gx_coeff[255];
- } else {
- info->map.ger = ger_coeff[0];
- info->map.gr = gx_coeff[level];
+ if (ir & AMR_IR_BBUF) {
+ if (info->Bb.channel_status == CHANNEL_INUSE)
+ transceive_Bchannel(&info->Bb, &info->regs->bbtb);
+ if (info->Bc.channel_status == CHANNEL_INUSE)
+ transceive_Bchannel(&info->Bc, &info->regs->bctb);
+ }
+
+ if (ir & (AMR_IR_DRTHRSH | AMR_IR_DTTHRSH | AMR_IR_DSRI)) {
+ debug_info(info, 'i');
+ debug_info(info, '0' + (ir&7));
+ transceive_Dchannel(info);
}
- info->map.mmr2 |= AM_MAP_MMR2_LS;
+ if (ir & AMR_IR_LSRI) {
+ regs->cr = AMR_LIU_LSR;
+ lsr = regs->dr;
- amd7930_commit_map(drv);
+ info->liu_state = (lsr&0x7) + 2;
+
+ if (info->liu_callback)
+ (*info->liu_callback)(info->liu_callback_arg);
+ }
+}
+
+
+static int amd7930_open(struct inode * inode, struct file * file,
+ struct sparcaudio_driver *drv)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ /* Set the default audio parameters. */
+ info->rgain = 128;
+ info->pgain = 200;
+ info->mgain = 0;
+ amd7930_update_map(drv);
MOD_INC_USE_COUNT;
@@ -258,29 +515,87 @@ static int amd7930_open(struct inode * inode, struct file * file,
static void amd7930_release(struct inode * inode, struct file * file,
struct sparcaudio_driver *drv)
{
- amd7930_disable_ints(drv);
+ /* amd7930_disable_ints(drv->private); */
MOD_DEC_USE_COUNT;
}
+static void request_Baudio(struct amd7930_info *info)
+{
+ if (info->Bb.channel_status == CHANNEL_AVAILABLE) {
+
+ info->Bb.channel_status = CHANNEL_INUSE;
+ info->Baudio = &info->Bb;
+
+ /* Multiplexor map - audio (Ba) to Bb */
+ info->regs->cr = AMR_MUX_MCR1;
+ info->regs->dr = AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bb << 4);
+
+ /* Enable B channel interrupts */
+ info->regs->cr = AMR_MUX_MCR4;
+ info->regs->dr = AM_MUX_MCR4_ENABLE_INTS;
+
+ } else if (info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+ info->Bc.channel_status = CHANNEL_INUSE;
+ info->Baudio = &info->Bc;
+
+ /* Multiplexor map - audio (Ba) to Bc */
+ info->regs->cr = AMR_MUX_MCR1;
+ info->regs->dr = AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bc << 4);
+
+ /* Enable B channel interrupts */
+ info->regs->cr = AMR_MUX_MCR4;
+ info->regs->dr = AM_MUX_MCR4_ENABLE_INTS;
+
+ }
+}
+
+static void release_Baudio(struct amd7930_info *info)
+{
+ if (info->Baudio) {
+ info->Baudio->channel_status = CHANNEL_AVAILABLE;
+ info->regs->cr = AMR_MUX_MCR1;
+ info->regs->dr = 0;
+ info->Baudio = NULL;
+
+ if (info->Bb.channel_status == CHANNEL_AVAILABLE &&
+ info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+ /* Disable B channel interrupts */
+ info->regs->cr = AMR_MUX_MCR4;
+ info->regs->dr = 0;
+ }
+ }
+}
+
static void amd7930_start_output(struct sparcaudio_driver *drv,
__u8 * buffer, unsigned long count)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
- info->output_ptr = buffer;
- info->output_count = count;
- amd7930_enable_ints(drv);
+ if (! info->Baudio) {
+ request_Baudio(info);
+ }
+
+ if (info->Baudio) {
+ info->Baudio->output_ptr = buffer;
+ info->Baudio->output_count = count;
+ info->Baudio->output_callback = (void *) &sparcaudio_output_done;
+ info->Baudio->output_callback_arg = (void *) drv;
+ info->Baudio->xmit_idle_char = 0;
+ }
}
static void amd7930_stop_output(struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
- info->output_ptr = NULL;
- info->output_count = 0;
-
- if (!info->input_ptr)
- amd7930_disable_ints(drv);
+ if (info->Baudio) {
+ info->Baudio->output_ptr = NULL;
+ info->Baudio->output_count = 0;
+ if (! info->Baudio->input_ptr)
+ release_Baudio(info);
+ }
}
static void amd7930_start_input(struct sparcaudio_driver *drv,
@@ -288,20 +603,29 @@ static void amd7930_start_input(struct sparcaudio_driver *drv,
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
- info->input_ptr = buffer;
- info->input_count = count;
- amd7930_enable_ints(drv);
+ if (! info->Baudio) {
+ request_Baudio(info);
+ }
+
+ if (info->Baudio) {
+ info->Baudio->input_ptr = buffer;
+ info->Baudio->input_count = count;
+ info->Baudio->input_callback = (void *) &sparcaudio_input_done;
+ info->Baudio->input_callback_arg = (void *) drv;
+ }
}
static void amd7930_stop_input(struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
- info->input_ptr = NULL;
- info->input_count = 0;
+ if (info->Baudio) {
+ info->Baudio->input_ptr = NULL;
+ info->Baudio->input_count = 0;
+ if (! info->Baudio->output_ptr)
+ release_Baudio(info);
+ }
- if (!info->output_ptr)
- amd7930_disable_ints(drv);
}
static void amd7930_sunaudio_getdev(struct sparcaudio_driver *drv,
@@ -312,23 +636,686 @@ static void amd7930_sunaudio_getdev(struct sparcaudio_driver *drv,
strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1);
}
+static int amd7930_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
+{
+ return AUDIO_DEV_AMD;
+}
+
+static int amd7930_set_output_volume(struct sparcaudio_driver *drv, int vol)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ info->pgain = vol;
+ amd7930_update_map(drv);
+ return 0;
+}
+
+static int amd7930_get_output_volume(struct sparcaudio_driver *drv)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ return info->pgain;
+}
+
+static int amd7930_set_input_volume(struct sparcaudio_driver *drv, int vol)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ info->rgain = vol;
+ amd7930_update_map(drv);
+ return 0;
+}
+
+static int amd7930_get_input_volume(struct sparcaudio_driver *drv)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ return info->rgain;
+}
+
+static int amd7930_set_monitor_volume(struct sparcaudio_driver *drv, int vol)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ info->mgain = vol;
+ amd7930_update_map(drv);
+ return 0;
+}
+
+/* Cheats. The amd has the minimum capabilities we support */
+static int amd7930_get_output_balance(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MID_BALANCE;
+}
+
+static int amd7930_get_input_balance(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MID_BALANCE;
+}
+
+static int amd7930_get_output_channels(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MIN_PLAY_CHANNELS;
+}
+
+static int amd7930_get_input_channels(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MIN_REC_CHANNELS;
+}
+
+static int amd7930_get_output_precision(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MIN_PLAY_PRECISION;
+}
+
+static int amd7930_get_input_precision(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MIN_REC_PRECISION;
+}
+
+/* This should eventually be made to DTRT, whatever that ends up */
+static int amd7930_get_output_port(struct sparcaudio_driver *drv)
+{
+ return AUDIO_SPEAKER; /* some of these have only HEADPHONE */
+}
+
+/* Only a microphone here, so no troubles */
+static int amd7930_get_input_port(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MICROPHONE;
+}
+
+/* This chip also supports AUDIO_ENCODING_ALAW, add support later */
+static int amd7930_get_output_encoding(struct sparcaudio_driver *drv)
+{
+ return AUDIO_ENCODING_ULAW;
+}
+
+static int amd7930_get_input_encoding(struct sparcaudio_driver *drv)
+{
+ return AUDIO_ENCODING_ULAW;
+}
+
+/* This is what you get. Take it or leave it */
+static int amd7930_get_output_rate(struct sparcaudio_driver *drv)
+{
+ return AMD7930_RATE;
+}
+
+static int amd7930_get_input_rate(struct sparcaudio_driver *drv)
+{
+ return AMD7930_RATE;
+}
+
+static int amd7930_get_output_muted(struct sparcaudio_driver *drv)
+{
+ return 0;
+}
+
+static int amd7930_get_output_ports(struct sparcaudio_driver *drv)
+{
+ return AUDIO_SPEAKER | AUDIO_HEADPHONE;
+}
+
+static int amd7930_get_input_ports(struct sparcaudio_driver *drv)
+{
+ return AUDIO_MICROPHONE;
+}
+
+static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ return info->mgain;
+}
+
/*
- * Device detection and initialization.
+ * ISDN operations
+ *
+ * Many of these routines take an "int dev" argument, which is simply
+ * an index into the drivers[] array. Currently, we only support a
+ * single AMD 7930 chip, so the value should always be 0. B channel
+ * operations require an "int chan", which should be 0 for channel B1
+ * and 1 for channel B2
+ *
+ * int amd7930_get_irqnum(int dev)
+ *
+ * returns the interrupt number being used by the chip. ISDN4linux
+ * uses this number to watch the interrupt during initialization and
+ * make sure something is happening.
+ *
+ * int amd7930_get_liu_state(int dev)
+ *
+ * returns the current state of the ISDN Line Interface Unit (LIU)
+ * as a number between 2 (state F2) and 7 (state F7). 0 may also be
+ * returned if the chip doesn't exist or the LIU hasn't been
+ * activated. The meanings of the states are defined in I.430, ISDN
+ * BRI Physical Layer Interface. The most important two states are
+ * F3 (shutdown) and F7 (syncronized).
+ *
+ * void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
+ *
+ * initializes the LIU and optionally registers a callback to be
+ * signaled upon a change of LIU state. The callback will be called
+ * with a single opaque callback_arg Once the callback has been
+ * triggered, amd7930_get_liu_state can be used to determine the LIU
+ * current state.
+ *
+ * void amd7930_liu_activate(int dev, int priority)
+ *
+ * requests LIU activation at a given D-channel priority.
+ * Successful activatation is achieved upon entering state F7, which
+ * will trigger any callback previously registered with
+ * amd7930_liu_init.
+ *
+ * void amd7930_liu_deactivate(int dev)
+ *
+ * deactivates LIU. Outstanding D and B channel transactions are
+ * terminated rudely and without callback notification. LIU change
+ * of state callback will be triggered, however.
+ *
+ * void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
+ * void (*callback)(void *, int), void *callback_arg)
+ *
+ * transmits a packet - specified with buffer, count - over the D-channel
+ * interface. Buffer should begin with the LAPD address field and
+ * end with the information field. FCS and flag sequences should not
+ * be included, nor is bit-stuffing required - all these functions are
+ * performed by the chip. The callback function will be called
+ * DURING THE TOP HALF OF AN INTERRUPT HANDLER and will be passed
+ * both the arbitrary callback_arg and an integer error indication:
+ *
+ * 0 - successful transmission; ready for next packet
+ * non-0 - error value from chip's DER (D-Channel Error Register):
+ * 4 - collision detect
+ * 128 - underrun; irq routine didn't service chip fast enough
+ *
+ * The callback routine should defer any time-consuming operations
+ * to a bottom-half handler; however, amd7930_dxmit may be called
+ * from within the callback to request back-to-back transmission of
+ * a second packet (without repeating the priority/collision mechanism)
+ *
+ * A comment about the "collision detect" error, which is signalled
+ * whenever the echoed D-channel data didn't match the transmitted
+ * data. This is part of ISDN's normal multi-drop T-interface
+ * operation, indicating that another device has attempted simultaneous
+ * transmission, but can also result from line noise. An immediate
+ * requeue via amd7930_dxmit is suggested, but repeated collision
+ * errors may indicate a more serious problem.
+ *
+ * void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
+ * void (*callback)(void *, int, unsigned int),
+ * void *callback_arg)
+ *
+ * register a buffer - buffer, size - into which a D-channel packet
+ * can be received. The callback function will be called DURING
+ * THE TOP HALF OF AN INTERRUPT HANDLER and will be passed an
+ * arbitrary callback_arg, an integer error indication and the length
+ * of the received packet, which will start with the address field,
+ * end with the information field, and not contain flag or FCS
+ * bytes. Bit-stuffing will already have been corrected for.
+ * Possible values of second callback argument "error":
+ *
+ * 0 - successful reception
+ * non-0 - error value from chip's DER (D-Channel Error Register):
+ * 1 - recieved packet abort
+ * 2 - framing error; non-integer number of bytes received
+ * 8 - FCS error; CRC sequence indicated corrupted data
+ * 16 - overflow error; packet exceeded size of buffer
+ * 32 - underflow error; packet smaller than required five bytes
+ * 64 - overrun error; irq routine didn't service chip fast enough
+ *
+ * int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
+ *
+ * This function should be called before any other operations on a B
+ * channel. In addition to arranging for interrupt handling and
+ * channel multiplexing, it sets the xmit_idle_char which is
+ * transmitted on the interface when no data buffer is available.
+ * Suggested values are: 0 for ISDN audio; FF for HDLC mark idle; 7E
+ * for HDLC flag idle. Returns 0 on a successful open; -1 on error,
+ * which is quite possible if audio and the other ISDN channel are
+ * already in use, since the Am7930 can only send two of the three
+ * channels to the processor
+ *
+ * void amd7930_bclose(int dev, int chan)
+ *
+ * Shuts down a B channel when no longer in use.
+ *
+ * void amd7930_bxmit(int dev, int chan, __u8 *buffer, unsigned int count,
+ * void (*callback)(void *), void *callback_arg)
+ *
+ * transmits a raw data block - specified with buffer, count - over
+ * the B channel interface specified by dev/chan. The callback
+ * function will be called DURING THE TOP HALF OF AN INTERRUPT
+ * HANDLER and will be passed the arbitrary callback_arg
+ *
+ * The callback routine should defer any time-consuming operations
+ * to a bottom-half handler; however, amd7930_bxmit may be called
+ * from within the callback to request back-to-back transmission of
+ * another data block
+ *
+ * void amd7930_brecv(int dev, int chan, __u8 *buffer, unsigned int size,
+ * void (*callback)(void *), void *callback_arg)
+ *
+ * receive a raw data block - specified with buffer, size - over the
+ * B channel interface specified by dev/chan. The callback function
+ * will be called DURING THE TOP HALF OF AN INTERRUPT HANDLER and
+ * will be passed the arbitrary callback_arg
+ *
+ * The callback routine should defer any time-consuming operations
+ * to a bottom-half handler; however, amd7930_brecv may be called
+ * from within the callback to register another buffer and ensure
+ * continuous B channel reception without loss of data
+ *
*/
-static struct sparcaudio_driver drivers[MAX_DRIVERS];
-static int num_drivers;
+
+int amd7930_get_irqnum(int dev)
+{
+ struct amd7930_info *info;
+
+ if (dev > num_drivers) {
+ return(0);
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ return info->irq;
+}
+
+int amd7930_get_liu_state(int dev)
+{
+ struct amd7930_info *info;
+
+ if (dev > num_drivers) {
+ return(0);
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ return info->liu_state;
+}
+
+void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
+{
+ struct amd7930_info *info;
+ register unsigned long flags;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ save_and_cli(flags);
+
+ /* Set callback for LIU state change */
+ info->liu_callback = callback;
+ info->liu_callback_arg = callback_arg;
+
+ /* De-activate the ISDN Line Interface Unit (LIU) */
+ info->regs->cr = AMR_LIU_LMR1;
+ info->regs->dr = 0;
+
+ /* Request interrupt when LIU changes state from/to F3/F7/F8 */
+ info->regs->cr = AMR_LIU_LMR2;
+ info->regs->dr = AM_LIU_LMR2_EN_F3_INT |
+ AM_LIU_LMR2_EN_F7_INT | AM_LIU_LMR2_EN_F8_INT;
+
+ /* amd7930_enable_ints(info); */
+
+ /* Activate the ISDN Line Interface Unit (LIU) */
+ info->regs->cr = AMR_LIU_LMR1;
+ info->regs->dr = AM_LIU_LMR1_LIU_ENABL;
+
+ restore_flags(flags);
+}
+
+void amd7930_liu_activate(int dev, int priority)
+{
+ struct amd7930_info *info;
+ register unsigned long flags;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ save_and_cli(flags);
+
+ /* Set D-channel access priority
+ *
+ * I.430 defines a priority mechanism based on counting 1s
+ * in the echo channel before transmitting
+ *
+ * Priority 0 is eight 1s; priority 1 is ten 1s; etc
+ */
+
+ info->regs->cr = AMR_LIU_LPR;
+ info->regs->dr = priority & 0x0f;
+
+ /* request LIU activation */
+
+ info->regs->cr = AMR_LIU_LMR1;
+ info->regs->dr = AM_LIU_LMR1_LIU_ENABL | AM_LIU_LMR1_REQ_ACTIV;
+
+ restore_flags(flags);
+}
+
+void amd7930_liu_deactivate(int dev)
+{
+ struct amd7930_info *info;
+ register unsigned long flags;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ save_and_cli(flags);
+
+ /* deactivate LIU */
+
+ info->regs->cr = AMR_LIU_LMR1;
+ info->regs->dr = 0;
+
+ restore_flags(flags);
+}
+
+void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
+ void (*callback)(void *, int), void *callback_arg)
+{
+ struct amd7930_info *info;
+ register unsigned long flags;
+ __u8 dmr1;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ save_and_cli(flags);
+
+ if (info->D.output_ptr) {
+ restore_flags(flags);
+ printk("amd7930_dxmit: transmitter in use\n");
+ return;
+ }
+
+ info->D.output_ptr = buffer;
+ info->D.output_count = count;
+ info->D.output_callback = callback;
+ info->D.output_callback_arg = callback_arg;
+
+ /* Enable D-channel Transmit Threshold interrupt; disable addressing */
+ info->regs->cr = AMR_DLC_DMR1;
+ dmr1 = info->regs->dr;
+ dmr1 |= AMR_DLC_DMR1_DTTHRSH_INT;
+ dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS;
+ info->regs->dr = dmr1;
+
+ /* Begin xmit by setting D-channel Transmit Byte Count Reg (DTCR) */
+ info->regs->cr = AMR_DLC_DTCR;
+ info->regs->dr = count & 0xff;
+ info->regs->dr = (count >> 8) & 0xff;
+
+ /* Prime xmit FIFO */
+ /* fill_D_xmit_fifo(info); */
+ transceive_Dchannel(info);
+
+ restore_flags(flags);
+}
+
+void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
+ void (*callback)(void *, int, unsigned int),
+ void *callback_arg)
+{
+ struct amd7930_info *info;
+ register unsigned long flags;
+ __u8 dmr1;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ save_and_cli(flags);
+
+ if (info->D.input_ptr) {
+ restore_flags(flags);
+ printk("amd7930_drecv: receiver already has buffer!\n");
+ return;
+ }
+
+ info->D.input_ptr = buffer;
+ info->D.input_count = 0;
+ info->D.input_limit = size;
+ info->D.input_callback = callback;
+ info->D.input_callback_arg = callback_arg;
+
+ /* Enable D-channel Receive Threshold interrupt;
+ * Enable D-channel End of Receive Packet interrupt;
+ * Disable address recognition
+ */
+ info->regs->cr = AMR_DLC_DMR1;
+ dmr1 = info->regs->dr;
+ dmr1 |= AMR_DLC_DMR1_DRTHRSH_INT | AMR_DLC_DMR1_EORP_INT;
+ dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS;
+ info->regs->dr = dmr1;
+
+ /* Set D-channel Receive Byte Count Limit Register */
+ info->regs->cr = AMR_DLC_DRCR;
+ info->regs->dr = size & 0xff;
+ info->regs->dr = (size >> 8) & 0xff;
+
+ restore_flags(flags);
+}
+
+int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
+{
+ struct amd7930_info *info;
+ register unsigned long flags;
+
+ if (dev > num_drivers || chan<0 || chan>1) {
+ return -1;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ save_and_cli(flags);
+
+ if (info->Bb.channel_status == CHANNEL_AVAILABLE) {
+
+ info->Bb.channel_status = CHANNEL_INUSE;
+ info->Bb.xmit_idle_char = xmit_idle_char;
+ info->Bisdn[chan] = &info->Bb;
+
+ /* Multiplexor map - isdn (B1/2) to Bb */
+ info->regs->cr = AMR_MUX_MCR2 + chan;
+ info->regs->dr = (AM_MUX_CHANNEL_B1 + chan) |
+ (AM_MUX_CHANNEL_Bb << 4);
+
+ } else if (info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+ info->Bc.channel_status = CHANNEL_INUSE;
+ info->Bc.xmit_idle_char = xmit_idle_char;
+ info->Bisdn[chan] = &info->Bc;
+
+ /* Multiplexor map - isdn (B1/2) to Bc */
+ info->regs->cr = AMR_MUX_MCR2 + chan;
+ info->regs->dr = (AM_MUX_CHANNEL_B1 + chan) |
+ (AM_MUX_CHANNEL_Bc << 4);
+
+ } else {
+ restore_flags(flags);
+ return (-1);
+ }
+
+ /* Enable B channel transmit */
+ info->regs->cr = AMR_LIU_LMR1;
+ info->regs->dr |= AM_LIU_LMR1_B1_ENABL + chan;
+
+ /* Enable B channel interrupts */
+ info->regs->cr = AMR_MUX_MCR4;
+ info->regs->dr = AM_MUX_MCR4_ENABLE_INTS | AM_MUX_MCR4_REVERSE_Bb | AM_MUX_MCR4_REVERSE_Bc;
+
+ restore_flags(flags);
+ return 0;
+}
+
+void amd7930_bclose(int dev, int chan)
+{
+ struct amd7930_info *info;
+ register unsigned long flags;
+
+ if (dev > num_drivers || chan<0 || chan>1) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+
+ save_and_cli(flags);
+
+ if (info->Bisdn[chan]) {
+ info->Bisdn[chan]->channel_status = CHANNEL_AVAILABLE;
+ info->regs->cr = AMR_MUX_MCR2 + chan;
+ info->regs->dr = 0;
+ info->Bisdn[chan] = NULL;
+
+ /* Disable B channel transmit */
+ info->regs->cr = AMR_LIU_LMR1;
+ info->regs->dr &= ~(AM_LIU_LMR1_B1_ENABL + chan);
+
+ if (info->Bb.channel_status == CHANNEL_AVAILABLE &&
+ info->Bc.channel_status == CHANNEL_AVAILABLE) {
+
+ /* Disable B channel interrupts */
+ info->regs->cr = AMR_MUX_MCR4;
+ info->regs->dr = 0;
+ }
+ }
+
+ restore_flags(flags);
+}
+
+void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count,
+ void (*callback)(void *), void *callback_arg)
+{
+ struct amd7930_info *info;
+ struct amd7930_channel *Bchan;
+ register unsigned long flags;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+ Bchan = info->Bisdn[chan];
+
+ if (Bchan) {
+ save_and_cli(flags);
+
+ Bchan->output_ptr = buffer;
+ Bchan->output_count = count;
+ Bchan->output_callback = (void *) callback;
+ Bchan->output_callback_arg = callback_arg;
+
+ restore_flags(flags);
+ }
+}
+
+void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size,
+ void (*callback)(void *), void *callback_arg)
+{
+ struct amd7930_info *info;
+ struct amd7930_channel *Bchan;
+ register unsigned long flags;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ info = (struct amd7930_info *) drivers[dev].private;
+ Bchan = info->Bisdn[chan];
+
+ if (Bchan) {
+ save_and_cli(flags);
+
+ Bchan->input_ptr = buffer;
+ Bchan->input_count = size;
+ Bchan->input_callback = (void *) callback;
+ Bchan->input_callback_arg = callback_arg;
+
+ restore_flags(flags);
+ }
+}
+
+EXPORT_SYMBOL(amd7930_get_irqnum);
+EXPORT_SYMBOL(amd7930_get_liu_state);
+EXPORT_SYMBOL(amd7930_liu_init);
+EXPORT_SYMBOL(amd7930_liu_activate);
+EXPORT_SYMBOL(amd7930_liu_deactivate);
+EXPORT_SYMBOL(amd7930_dxmit);
+EXPORT_SYMBOL(amd7930_drecv);
+EXPORT_SYMBOL(amd7930_bopen);
+EXPORT_SYMBOL(amd7930_bclose);
+EXPORT_SYMBOL(amd7930_bxmit);
+EXPORT_SYMBOL(amd7930_brecv);
+
+
+/*
+ * Device detection and initialization.
+ */
static struct sparcaudio_operations amd7930_ops = {
amd7930_open,
amd7930_release,
- NULL, /* amd7930_ioctl */
+ NULL, /* amd7930_ioctl */
amd7930_start_output,
amd7930_stop_output,
amd7930_start_input,
amd7930_stop_input,
amd7930_sunaudio_getdev,
+ amd7930_set_output_volume,
+ amd7930_get_output_volume,
+ amd7930_set_input_volume,
+ amd7930_get_input_volume,
+ amd7930_set_monitor_volume,
+ amd7930_get_monitor_volume,
+ NULL, /* amd7930_set_output_balance */
+ amd7930_get_output_balance,
+ NULL, /* amd7930_set_input_balance */
+ amd7930_get_input_balance,
+ NULL, /* amd7930_set_output_channels */
+ amd7930_get_output_channels,
+ NULL, /* amd7930_set_input_channels */
+ amd7930_get_input_channels,
+ NULL, /* amd7930_set_output_precision */
+ amd7930_get_output_precision,
+ NULL, /* amd7930_set_input_precision */
+ amd7930_get_input_precision,
+ NULL, /* amd7930_set_output_port */
+ amd7930_get_output_port,
+ NULL, /* amd7930_set_input_port */
+ amd7930_get_input_port,
+ NULL, /* amd7930_set_output_encoding */
+ amd7930_get_output_encoding,
+ NULL, /* amd7930_set_input_encoding */
+ amd7930_get_input_encoding,
+ NULL, /* amd7930_set_output_rate */
+ amd7930_get_output_rate,
+ NULL, /* amd7930_set_input_rate */
+ amd7930_get_input_rate,
+ amd7930_sunaudio_getdev_sunos,
+ amd7930_get_output_ports,
+ amd7930_get_input_ports,
+ NULL, /* amd7930_set_output_muted */
+ amd7930_get_output_muted,
};
/* Attach to an amd7930 chip given its PROM node. */
@@ -348,8 +1335,10 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node,
/* Point at the information structure and initialize it. */
drv->ops = &amd7930_ops;
info = (struct amd7930_info *)drv->private;
- info->output_ptr = info->input_ptr = NULL;
- info->output_count = info->input_count = 0;
+ info->Bb.output_ptr = info->Bb.input_ptr = NULL;
+ info->Bb.output_count = info->Bb.input_count = 0;
+ info->Bc.output_ptr = info->Bc.input_ptr = NULL;
+ info->Bc.output_count = info->Bc.input_count = 0;
info->ints_on = 1; /* force disable below */
/* Map the registers into memory. */
@@ -365,15 +1354,14 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node,
return -EIO;
}
- /* Disable amd7930 interrupt generation. */
- amd7930_disable_ints(drv);
+ /* Put amd7930 in idle mode (interrupts disabled) */
+ amd7930_idle(info);
- /* Initialize the MUX unit to connect the MAP to the CPU. */
- info->regs->cr = AMR_MUX_1_4;
- info->regs->dr = (AM_MUX_CHANNEL_Bb << 4) | AM_MUX_CHANNEL_Ba;
- info->regs->dr = 0;
- info->regs->dr = 0;
- info->regs->dr = AM_MUX_MCR4_ENABLE_INTS;
+ /* Enable extended FIFO operation on D-channel */
+ info->regs->cr = AMR_DLC_EFCR;
+ info->regs->dr = AMR_DLC_EFCR_EXTEND_FIFO;
+ info->regs->cr = AMR_DLC_DMR4;
+ info->regs->dr = /* AMR_DLC_DMR4_RCV_30 | */ AMR_DLC_DMR4_XMT_14;
/* Attach the interrupt handler to the audio interrupt. */
prom_getproperty(node, "intr", (char *)&irq, sizeof(irq));
@@ -381,6 +1369,7 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node,
request_irq(info->irq, amd7930_interrupt,
SA_INTERRUPT, "amd7930", drv);
enable_irq(info->irq);
+ amd7930_enable_ints(info);
/* Initalize the local copy of the MAP registers. */
memset(&info->map, 0, sizeof(info->map));
@@ -413,7 +1402,7 @@ static void amd7930_detach(struct sparcaudio_driver *drv)
struct amd7930_info *info = (struct amd7930_info *)drv->private;
unregister_sparcaudio_driver(drv);
- amd7930_disable_ints(drv);
+ amd7930_idle(info);
disable_irq(info->irq);
free_irq(info->irq, drv);
sparc_free_io(info->regs, info->regs_size);
@@ -421,7 +1410,6 @@ static void amd7930_detach(struct sparcaudio_driver *drv)
}
#endif
-
/* Probe for the amd7930 chip and then attach the driver. */
#ifdef MODULE
int init_module(void)
@@ -433,12 +1421,6 @@ __initfunc(int amd7930_init(void))
struct linux_sbus_device *sdev;
int node;
-#if 0
-#ifdef MODULE
- register_symtab(0);
-#endif
-#endif
-
/* Try to find the sun4c "audio" node first. */
node = prom_getchild(prom_root_node);
node = prom_searchsiblings(node, "audio");
diff --git a/drivers/sbus/audio/amd7930.h b/drivers/sbus/audio/amd7930.h
index be860d10c..8df197061 100644
--- a/drivers/sbus/audio/amd7930.h
+++ b/drivers/sbus/audio/amd7930.h
@@ -15,6 +15,26 @@
#include <linux/types.h>
+/* Exported ISDN functions */
+
+int amd7930_get_irqnum(int dev);
+int amd7930_get_liu_state(int dev);
+void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg);
+void amd7930_liu_activate(int dev, int priority);
+void amd7930_liu_deactivate(int dev);
+void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
+ void (*callback)(void *, int), void *callback_arg);
+void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
+ void (*callback)(void *, int, unsigned int),
+ void *callback_arg);
+int amd7930_bopen(int dev, int chan, u_char xmit_idle_char);
+void amd7930_bclose(int dev, int chan);
+void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count,
+ void (*callback)(void *), void *callback_arg);
+void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size,
+ void (*callback)(void *), void *callback_arg);
+
+
/* Register interface presented to the CPU by the amd7930. */
struct amd7930
{
@@ -48,6 +68,20 @@ struct amd7930_map {
};
+/* After an amd7930 interrupt, reading the Interrupt Register (ir)
+ * clears the interrupt and returns a bitmask indicated which
+ * interrupt source(s) require service
+ */
+
+#define AMR_IR_DTTHRSH 0x01 /* D-channel xmit threshold */
+#define AMR_IR_DRTHRSH 0x02 /* D-channel recv threshold */
+#define AMR_IR_DSRI 0x04 /* D-channel packet status */
+#define AMR_IR_DERI 0x08 /* D-channel error */
+#define AMR_IR_BBUF 0x10 /* B-channel data xfer */
+#define AMR_IR_LSRI 0x20 /* LIU status */
+#define AMR_IR_DSR2I 0x40 /* D-channel buffer status */
+#define AMR_IR_MLTFRMI 0x80 /* multiframe or PP */
+
/* The amd7930 has "indirect registers" which are accessed by writing
* the register number into the Command Register and then reading or
* writing values from the Data Register as appropriate. We define the
@@ -67,9 +101,29 @@ struct amd7930_map {
/* Line Interface Unit */
#define AMR_LIU_LSR 0xA1
+#define AM_LIU_LSR_STATE 0x07
+#define AM_LIU_LSR_F3 0x08
+#define AM_LIU_LSR_F7 0x10
+#define AM_LIU_LSR_F8 0x20
+#define AM_LIU_LSR_HSW 0x40
+#define AM_LIU_LSR_HSW_CHG 0x80
#define AMR_LIU_LPR 0xA2
#define AMR_LIU_LMR1 0xA3
+#define AM_LIU_LMR1_B1_ENABL 0x01
+#define AM_LIU_LMR1_B2_ENABL 0x02
+#define AM_LIU_LMR1_F_DISABL 0x04
+#define AM_LIU_LMR1_FA_DISABL 0x08
+#define AM_LIU_LMR1_REQ_ACTIV 0x10
+#define AM_LIU_LMR1_F8_F3 0x20
+#define AM_LIU_LMR1_LIU_ENABL 0x40
#define AMR_LIU_LMR2 0xA4
+#define AM_LIU_LMR2_DECHO 0x01
+#define AM_LIU_LMR2_DLOOP 0x02
+#define AM_LIU_LMR2_DBACKOFF 0x04
+#define AM_LIU_LMR2_EN_F3_INT 0x08
+#define AM_LIU_LMR2_EN_F8_INT 0x10
+#define AM_LIU_LMR2_EN_HSW_INT 0x20
+#define AM_LIU_LMR2_EN_F7_INT 0x40
#define AMR_LIU_2_4 0xA5
#define AMR_LIU_MF 0xA6
#define AMR_LIU_MFSB 0xA7
@@ -134,7 +188,24 @@ struct amd7930_map {
#define AMR_DLC_DRLR 0x84
#define AMR_DLC_DTCR 0x85
#define AMR_DLC_DMR1 0x86
+#define AMR_DLC_DMR1_DTTHRSH_INT 0x01
+#define AMR_DLC_DMR1_DRTHRSH_INT 0x02
+#define AMR_DLC_DMR1_TAR_ENABL 0x04
+#define AMR_DLC_DMR1_EORP_INT 0x08
+#define AMR_DLC_DMR1_EN_ADDR1 0x10
+#define AMR_DLC_DMR1_EN_ADDR2 0x20
+#define AMR_DLC_DMR1_EN_ADDR3 0x40
+#define AMR_DLC_DMR1_EN_ADDR4 0x80
+#define AMR_DLC_DMR1_EN_ADDRS 0xf0
#define AMR_DLC_DMR2 0x87
+#define AMR_DLC_DMR2_RABRT_INT 0x01
+#define AMR_DLC_DMR2_RESID_INT 0x02
+#define AMR_DLC_DMR2_COLL_INT 0x04
+#define AMR_DLC_DMR2_FCS_INT 0x08
+#define AMR_DLC_DMR2_OVFL_INT 0x10
+#define AMR_DLC_DMR2_UNFL_INT 0x20
+#define AMR_DLC_DMR2_OVRN_INT 0x40
+#define AMR_DLC_DMR2_UNRN_INT 0x80
#define AMR_DLC_1_7 0x88
#define AMR_DLC_DRCR 0x89
#define AMR_DLC_RNGR1 0x8A
@@ -142,10 +213,66 @@ struct amd7930_map {
#define AMR_DLC_FRAR4 0x8C
#define AMR_DLC_SRAR4 0x8D
#define AMR_DLC_DMR3 0x8E
+#define AMR_DLC_DMR3_VA_INT 0x01
+#define AMR_DLC_DMR3_EOTP_INT 0x02
+#define AMR_DLC_DMR3_LBRP_INT 0x04
+#define AMR_DLC_DMR3_RBA_INT 0x08
+#define AMR_DLC_DMR3_LBT_INT 0x10
+#define AMR_DLC_DMR3_TBE_INT 0x20
+#define AMR_DLC_DMR3_RPLOST_INT 0x40
+#define AMR_DLC_DMR3_KEEP_FCS 0x80
#define AMR_DLC_DMR4 0x8F
+#define AMR_DLC_DMR4_RCV_1 0x00
+#define AMR_DLC_DMR4_RCV_2 0x01
+#define AMR_DLC_DMR4_RCV_4 0x02
+#define AMR_DLC_DMR4_RCV_8 0x03
+#define AMR_DLC_DMR4_RCV_16 0x01
+#define AMR_DLC_DMR4_RCV_24 0x02
+#define AMR_DLC_DMR4_RCV_30 0x03
+#define AMR_DLC_DMR4_XMT_1 0x00
+#define AMR_DLC_DMR4_XMT_2 0x04
+#define AMR_DLC_DMR4_XMT_4 0x08
+#define AMR_DLC_DMR4_XMT_8 0x0c
+#define AMR_DLC_DMR4_XMT_10 0x08
+#define AMR_DLC_DMR4_XMT_14 0x0c
+#define AMR_DLC_DMR4_IDLE_MARK 0x00
+#define AMR_DLC_DMR4_IDLE_FLAG 0x10
+#define AMR_DLC_DMR4_ADDR_BOTH 0x00
+#define AMR_DLC_DMR4_ADDR_1ST 0x20
+#define AMR_DLC_DMR4_ADDR_2ND 0xa0
+#define AMR_DLC_DMR4_CR_ENABLE 0x40
#define AMR_DLC_12_15 0x90
#define AMR_DLC_ASR 0x91
#define AMR_DLC_EFCR 0x92
+#define AMR_DLC_EFCR_EXTEND_FIFO 0x01
+#define AMR_DLC_EFCR_SEC_PKT_INT 0x02
+
+#define AMR_DSR1_VADDR 0x01
+#define AMR_DSR1_EORP 0x02
+#define AMR_DSR1_PKT_IP 0x04
+#define AMR_DSR1_DECHO_ON 0x08
+#define AMR_DSR1_DLOOP_ON 0x10
+#define AMR_DSR1_DBACK_OFF 0x20
+#define AMR_DSR1_EOTP 0x40
+#define AMR_DSR1_CXMT_ABRT 0x80
+
+#define AMR_DSR2_LBRP 0x01
+#define AMR_DSR2_RBA 0x02
+#define AMR_DSR2_RPLOST 0x04
+#define AMR_DSR2_LAST_BYTE 0x08
+#define AMR_DSR2_TBE 0x10
+#define AMR_DSR2_MARK_IDLE 0x20
+#define AMR_DSR2_FLAG_IDLE 0x40
+#define AMR_DSR2_SECOND_PKT 0x80
+
+#define AMR_DER_RABRT 0x01
+#define AMR_DER_RFRAME 0x02
+#define AMR_DER_COLLISION 0x04
+#define AMR_DER_FCS 0x08
+#define AMR_DER_OVFL 0x10
+#define AMR_DER_UNFL 0x20
+#define AMR_DER_OVRN 0x40
+#define AMR_DER_UNRN 0x80
/* Peripheral Port */
#define AMR_PP_PPCR1 0xC0
@@ -160,4 +287,6 @@ struct amd7930_map {
#define AMR_PP_PPCR2 0xC8
#define AMR_PP_PPCR3 0xC9
+/* Give this chip a "default" sample rate */
+#define AMD7930_RATE (8000)
#endif
diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c
index ae77b1455..ba9563449 100644
--- a/drivers/sbus/audio/audio.c
+++ b/drivers/sbus/audio/audio.c
@@ -1,7 +1,7 @@
/*
* drivers/sbus/audio/audio.c
*
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
+ * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
*
* This is the audio midlayer that sits between the VFS character
* devices and the low-level audio hardware device drivers.
@@ -19,6 +19,8 @@
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <asm/uaccess.h>
#include "audio.h"
@@ -42,33 +44,47 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv)
if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output)
return -EINVAL;
- /* Setup the circular queue of output buffers. */
+ /* Setup the circular queues of output and input buffers
+ *
+ * Each buffer is a single page, but output buffers might
+ * be partially filled (by a write with count < PAGE_SIZE),
+ * so each output buffer also has a paired output size.
+ *
+ * Input buffers, on the other hand, always fill completely,
+ * so we don't need input counts - each contains PAGE_SIZE
+ * bytes of audio data.
+ *
+ * TODO: Make number of input/output buffers tunable parameters
+ */
+
drv->num_output_buffers = 32;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_active = 0;
- drv->output_buffers = kmalloc(32 * sizeof(__u8 *), GFP_KERNEL);
- drv->output_sizes = kmalloc(32 * sizeof(size_t), GFP_KERNEL);
- if (!drv->output_buffers || !drv->output_sizes) {
- if (drv->output_buffers)
- kfree(drv->output_buffers);
- if (drv->output_sizes)
- kfree(drv->output_sizes);
- return -ENOMEM;
- }
+ drv->output_buffers = kmalloc(drv->num_output_buffers * sizeof(__u8 *), GFP_KERNEL);
+ drv->output_sizes = kmalloc(drv->num_output_buffers * sizeof(size_t), GFP_KERNEL);
+ if (!drv->output_buffers || !drv->output_sizes) goto kmalloc_failed1;
/* Allocate the pages for each output buffer. */
for (i = 0; i < drv->num_output_buffers; i++) {
drv->output_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
- if (!drv->output_buffers[i]) {
- int j;
- for (j = 0; j < i; j++)
- free_page((unsigned long) drv->output_buffers[j]);
- kfree(drv->output_buffers);
- kfree(drv->output_sizes);
- return -ENOMEM;
- }
+ if (!drv->output_buffers[i]) goto kmalloc_failed2;
+ }
+
+ /* Setup the circular queue of input buffers. */
+ drv->num_input_buffers = 32;
+ drv->input_front = 0;
+ drv->input_rear = 0;
+ drv->input_count = 0;
+ drv->input_active = 0;
+ drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *), GFP_KERNEL);
+ if (!drv->input_buffers) goto kmalloc_failed3;
+
+ /* Allocate the pages for each input buffer. */
+ for (i = 0; i < drv->num_input_buffers; i++) {
+ drv->input_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
+ if (!drv->input_buffers[i]) goto kmalloc_failed4;
}
/* Ensure that the driver is marked as not being open. */
@@ -78,6 +94,28 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv)
driver = drv;
return 0;
+
+
+kmalloc_failed4:
+ for (i--; i >= 0; i--)
+ free_page((unsigned long) drv->input_buffers[i]);
+
+kmalloc_failed3:
+ if (drv->input_buffers)
+ kfree(drv->input_buffers);
+ i = drv->num_output_buffers;
+
+kmalloc_failed2:
+ for (i--; i >= 0; i--)
+ free_page((unsigned long) drv->output_buffers[i]);
+
+kmalloc_failed1:
+ if (drv->output_buffers)
+ kfree(drv->output_buffers);
+ if (drv->output_sizes)
+ kfree(drv->output_sizes);
+
+ return -ENOMEM;
}
int unregister_sparcaudio_driver(struct sparcaudio_driver *drv)
@@ -94,6 +132,11 @@ int unregister_sparcaudio_driver(struct sparcaudio_driver *drv)
kfree(driver->output_buffers);
kfree(driver->output_sizes);
+ /* Deallocate the queue of input buffers. */
+ for (i = 0; i < driver->num_input_buffers; i++)
+ free_page((unsigned long) driver->input_buffers[i]);
+ kfree(driver->input_buffers);
+
MOD_DEC_USE_COUNT;
driver = NULL;
@@ -116,6 +159,7 @@ static void sparcaudio_output_done_task(void * arg)
void sparcaudio_output_done(struct sparcaudio_driver * drv)
{
/* Point the queue after the "done" buffer. */
+ drv->output_size -= drv->output_sizes[drv->output_front];
drv->output_front = (drv->output_front + 1) % drv->num_output_buffers;
drv->output_count--;
@@ -146,7 +190,23 @@ void sparcaudio_output_done(struct sparcaudio_driver * drv)
void sparcaudio_input_done(struct sparcaudio_driver * drv)
{
- /* XXX Implement! */
+ /* Point the queue after the "done" buffer. */
+ drv->input_front = (drv->input_front + 1) % drv->num_input_buffers;
+ drv->input_count++;
+
+ /* If the input queue is full, shutdown the driver. */
+ if (drv->input_count == drv->num_input_buffers) {
+ /* Stop the lowlevel driver from inputing. */
+ drv->ops->stop_input(drv);
+ drv->input_active = 0;
+ } else {
+ /* Otherwise, give the driver the next buffer. */
+ drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
+ PAGE_SIZE);
+ }
+
+ /* Wake up any tasks that are waiting. */
+ wake_up_interruptible(&drv->input_read_wait);
}
@@ -155,34 +215,85 @@ void sparcaudio_input_done(struct sparcaudio_driver * drv)
* VFS layer interface
*/
-static int sparcaudio_lseek(struct inode * inode, struct file * file,
- off_t offset, int origin)
+static loff_t sparcaudio_llseek(struct file * file, loff_t offset, int origin)
{
return -ESPIPE;
}
-static int sparcaudio_read(struct inode * inode, struct file * file,
- char *buf, int count)
+static ssize_t sparcaudio_read(struct file * file,
+ char *buf, size_t count, loff_t *ppos)
{
- /* XXX Implement me! */
- return -EINVAL;
+ int bytes_to_copy;
+
+ if (! file->f_mode & FMODE_READ)
+ return -EINVAL;
+
+ if (driver->input_count == 0) {
+ interruptible_sleep_on(&driver->input_read_wait);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+
+ bytes_to_copy = PAGE_SIZE - driver->input_offset;
+ if (bytes_to_copy > count)
+ bytes_to_copy = count;
+
+ copy_to_user_ret(buf, driver->input_buffers[driver->input_rear]+driver->input_offset,
+ bytes_to_copy, -EFAULT);
+ driver->input_offset += bytes_to_copy;
+
+ if (driver->input_offset >= PAGE_SIZE) {
+ driver->input_rear = (driver->input_rear + 1) % driver->num_input_buffers;
+ driver->input_count--;
+ driver->input_offset = 0;
+ }
+
+ return bytes_to_copy;
+}
+
+static void sparcaudio_reorganize_buffers(struct sparcaudio_driver * driver)
+{
+ /* It may never matter but if it does this routine will pack */
+ /* buffers to free space for more data */
}
-static int sparcaudio_write(struct inode * inode, struct file * file,
- const char *buf, int count)
+static void sparcaudio_sync_output(struct sparcaudio_driver * driver)
{
unsigned long flags;
- int bytes_written = 0, bytes_to_copy, err;
+
+ /* If the low-level driver is not active, activate it. */
+ save_and_cli(flags);
+ if (! driver->output_active) {
+ driver->ops->start_output(driver,
+ driver->output_buffers[driver->output_front],
+ driver->output_sizes[driver->output_front]);
+ driver->output_active = 1;
+ }
+ restore_flags(flags);
+}
+
+static ssize_t sparcaudio_write(struct file * file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ int bytes_written = 0, bytes_to_copy;
/* Ensure that we have something to write. */
- if (count < 1)
+ if (count < 1) {
+ sparcaudio_sync_output(driver);
return 0;
+ }
/* Loop until all output is written to device. */
while (count > 0) {
/* Check to make sure that an output buffer is available. */
+ /* If not, make valiant attempt */
+ if (driver->output_count == driver->num_output_buffers)
+ sparcaudio_reorganize_buffers(driver);
+
if (driver->output_count == driver->num_output_buffers) {
- interruptible_sleep_on(&driver->output_write_wait);
+ /* We need buffers, so... */
+ sparcaudio_sync_output(driver);
+ interruptible_sleep_on(&driver->output_write_wait);
if (signal_pending(current))
return bytes_written > 0 ? bytes_written : -EINTR;
}
@@ -202,15 +313,11 @@ static int sparcaudio_write(struct inode * inode, struct file * file,
driver->output_sizes[driver->output_rear] = bytes_to_copy;
driver->output_rear = (driver->output_rear + 1) % driver->num_output_buffers;
driver->output_count++;
+ driver->output_size += bytes_to_copy;
- /* If the low-level driver is not active, activate it. */
- save_and_cli(flags);
- if (! driver->output_active) {
- driver->ops->start_output(driver, driver->output_buffers[driver->output_front],
- driver->output_sizes[driver->output_front]);
- driver->output_active = 1;
- }
- restore_flags(flags);
+ /* Activate the driver if more than page of data is waiting. */
+ if (driver->output_size > 4096)
+ sparcaudio_sync_output(driver);
}
/* Return the number of bytes written to the caller. */
@@ -221,8 +328,10 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
int retval = 0;
+ struct audio_info ainfo;
switch (cmd) {
+ case SNDCTL_DSP_SYNC:
case AUDIO_DRAIN:
if (driver->output_count > 0) {
interruptible_sleep_on(&driver->output_drain_wait);
@@ -239,18 +348,381 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file,
copy_to_user_ret((audio_device_t *)arg, &tmp, sizeof(tmp), -EFAULT);
} else
retval = -EINVAL;
+
+ printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV\n");
break;
+ case AUDIO_GETDEV_SUNOS:
+ if (driver->ops->sunaudio_getdev_sunos) {
+ int tmp=driver->ops->sunaudio_getdev_sunos(driver);
+
+ copy_to_user_ret((int *)arg, &tmp, sizeof(tmp), -EFAULT);
+ } else
+ retval = -EINVAL;
+
+ printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV_SUNOS\n");
+ break;
+
+ case AUDIO_GETINFO:
+
+ AUDIO_INITINFO(&ainfo);
+
+ if (driver->ops->get_input_rate)
+ ainfo.record.sample_rate =
+ driver->ops->get_input_rate(driver);
+ if (driver->ops->get_input_channels)
+ ainfo.record.channels =
+ driver->ops->get_input_channels(driver);
+ if (driver->ops->get_input_precision)
+ ainfo.record.precision =
+ driver->ops->get_input_precision(driver);
+ if (driver->ops->get_input_encoding)
+ ainfo.record.encoding =
+ driver->ops->get_input_encoding(driver);
+ if (driver->ops->get_input_volume)
+ ainfo.record.gain =
+ driver->ops->get_input_volume(driver);
+ if (driver->ops->get_input_port)
+ ainfo.record.port =
+ driver->ops->get_input_port(driver);
+ if (driver->ops->get_input_ports)
+ ainfo.record.avail_ports =
+ driver->ops->get_input_ports(driver);
+ ainfo.record.buffer_size = PAGE_SIZE;
+ ainfo.record.samples = 0;
+ ainfo.record.eof = 0;
+ ainfo.record.pause = 0;
+ ainfo.record.error = 0;
+ ainfo.record.waiting = 0;
+ if (driver->ops->get_input_balance)
+ ainfo.record.balance =
+ driver->ops->get_input_balance(driver);
+ ainfo.record.minordev = 4;
+ ainfo.record.open = 1;
+ ainfo.record.active = 0;
+
+ if (driver->ops->get_output_rate)
+ ainfo.play.sample_rate =
+ driver->ops->get_output_rate(driver);
+ if (driver->ops->get_output_channels)
+ ainfo.play.channels =
+ driver->ops->get_output_channels(driver);
+ if (driver->ops->get_output_precision)
+ ainfo.play.precision =
+ driver->ops->get_output_precision(driver);
+ if (driver->ops->get_output_encoding)
+ ainfo.play.encoding =
+ driver->ops->get_output_encoding(driver);
+ if (driver->ops->get_output_volume)
+ ainfo.play.gain =
+ driver->ops->get_output_volume(driver);
+ if (driver->ops->get_output_port)
+ ainfo.play.port =
+ driver->ops->get_output_port(driver);
+ if (driver->ops->get_output_ports)
+ ainfo.play.avail_ports =
+ driver->ops->get_output_ports(driver);
+ ainfo.play.buffer_size = PAGE_SIZE;
+ ainfo.play.samples = 0;
+ ainfo.play.eof = 0;
+ ainfo.play.pause = 0;
+ ainfo.play.error = 0;
+ ainfo.play.waiting = waitqueue_active(&driver->open_wait);
+ if (driver->ops->get_output_balance)
+ ainfo.play.balance =
+ driver->ops->get_output_balance(driver);
+ ainfo.play.minordev = 4;
+ ainfo.play.open = 1;
+ ainfo.play.active = driver->output_active;
+
+ if (driver->ops->get_monitor_volume)
+ ainfo.monitor_gain =
+ driver->ops->get_monitor_volume(driver);
+
+ if (driver->ops->get_output_muted)
+ ainfo.output_muted =
+ driver->ops->get_output_muted(driver);
+
+ printk("sparcaudio_ioctl: AUDIO_GETINFO\n");
+
+ copy_to_user_ret((struct audio_info *)arg, &ainfo,
+ sizeof(ainfo), -EFAULT);
+
+ break;
+
+ case AUDIO_SETINFO:
+ {
+ audio_info_t curinfo;
+
+ copy_from_user_ret(&ainfo, (audio_info_t *) arg, sizeof(audio_info_t), -EFAULT);
+
+ /* Without these there's no point in trying */
+ if (!driver->ops->get_input_precision ||
+ !driver->ops->get_input_channels ||
+ !driver->ops->get_input_rate ||
+ !driver->ops->get_input_encoding ||
+ !driver->ops->get_output_precision ||
+ !driver->ops->get_output_channels ||
+ !driver->ops->get_output_rate ||
+ !driver->ops->get_output_encoding)
+ {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* Do bounds checking for things which always apply.
+ * Follow with enforcement of basic tenets of certain
+ * encodings. Everything over and above generic is
+ * enforced by the driver, which can assume that
+ * Martian cases are taken care of here. */
+ if (Modify(ainfo.play.gain) &&
+ ((ainfo.play.gain > AUDIO_MAX_GAIN) ||
+ (ainfo.play.gain < AUDIO_MIN_GAIN))) {
+ /* Need to differentiate this from e.g. the above error */
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.record.gain) &&
+ ((ainfo.record.gain > AUDIO_MAX_GAIN) ||
+ (ainfo.record.gain < AUDIO_MIN_GAIN))) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.monitor_gain) &&
+ ((ainfo.monitor_gain > AUDIO_MAX_GAIN) ||
+ (ainfo.monitor_gain < AUDIO_MIN_GAIN))) {
+ retval = -EINVAL;
+ break;
+ }
+ /* Don't need to check less than zero on these */
+ if (Modifyc(ainfo.play.balance) &&
+ (ainfo.play.balance > AUDIO_RIGHT_BALANCE)) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modifyc(ainfo.record.balance) &&
+ (ainfo.record.balance > AUDIO_RIGHT_BALANCE)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* If any of these changed, record them all, then make
+ * changes atomically. If something fails, back it all out. */
+ if (Modify(ainfo.record.precision) ||
+ Modify(ainfo.record.sample_rate) ||
+ Modify(ainfo.record.channels) ||
+ Modify(ainfo.record.encoding) ||
+ Modify(ainfo.play.precision) ||
+ Modify(ainfo.play.sample_rate) ||
+ Modify(ainfo.play.channels) ||
+ Modify(ainfo.play.encoding))
+ {
+ /* If they're trying to change something we
+ * have no routine for, they lose */
+ if ((!driver->ops->set_input_encoding &&
+ Modify(ainfo.record.encoding)) ||
+ (!driver->ops->set_input_rate &&
+ Modify(ainfo.record.sample_rate)) ||
+ (!driver->ops->set_input_precision &&
+ Modify(ainfo.record.precision)) ||
+ (!driver->ops->set_input_channels &&
+ Modify(ainfo.record.channels))) {
+ retval = -EINVAL;
+ break;
+ }
+
+ curinfo.record.encoding = (Modify(ainfo.record.encoding) ?
+ ainfo.record.encoding :
+ driver->ops->get_input_encoding(driver));
+ curinfo.record.sample_rate = (Modify(ainfo.record.sample_rate) ?
+ ainfo.record.sample_rate :
+ driver->ops->get_input_rate(driver));
+ curinfo.record.precision = (Modify(ainfo.record.precision) ?
+ ainfo.record.precision :
+ driver->ops->get_input_precision(driver));
+ curinfo.record.channels = (Modify(ainfo.record.channels) ?
+ ainfo.record.channels :
+ driver->ops->get_input_channels(driver));
+ switch (curinfo.record.encoding) {
+ case AUDIO_ENCODING_ALAW:
+ case AUDIO_ENCODING_ULAW:
+ if (Modify(ainfo.record.precision) &&
+ ainfo.record.precision != 8) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.record.channels) &&
+ ainfo.record.channels != 1) {
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ case AUDIO_ENCODING_LINEARLE:
+ if (Modify(ainfo.record.precision) &&
+ ainfo.record.precision != 16) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.record.channels) &&
+ (ainfo.record.channels != 1 &&
+ ainfo.record.channels != 2))
+ {
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR8:
+ if (Modify(ainfo.record.precision) &&
+ ainfo.record.precision != 8) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.record.channels) &&
+ (ainfo.record.channels != 1 &&
+ ainfo.record.channels != 2))
+ {
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ if (retval < 0)
+ break;
+
+ /* If they're trying to change something we
+ * have no routine for, they lose */
+ if ((!driver->ops->set_output_encoding &&
+ Modify(ainfo.play.encoding)) ||
+ (!driver->ops->set_output_rate &&
+ Modify(ainfo.play.sample_rate)) ||
+ (!driver->ops->set_output_precision &&
+ Modify(ainfo.play.precision)) ||
+ (!driver->ops->set_output_channels &&
+ Modify(ainfo.play.channels))) {
+ retval = -EINVAL;
+ break;
+ }
+
+ curinfo.play.encoding = (Modify(ainfo.play.encoding) ?
+ ainfo.play.encoding :
+ driver->ops->get_output_encoding(driver));
+ curinfo.play.sample_rate = (Modify(ainfo.play.sample_rate) ?
+ ainfo.play.sample_rate :
+ driver->ops->get_output_rate(driver));
+ curinfo.play.precision = (Modify(ainfo.play.precision) ?
+ ainfo.play.precision :
+ driver->ops->get_output_precision(driver));
+ curinfo.play.channels = (Modify(ainfo.play.channels) ?
+ ainfo.play.channels :
+ driver->ops->get_output_channels(driver));
+ switch (curinfo.play.encoding) {
+ case AUDIO_ENCODING_ALAW:
+ case AUDIO_ENCODING_ULAW:
+ if (Modify(ainfo.play.precision) &&
+ ainfo.play.precision != 8) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.play.channels) &&
+ ainfo.play.channels != 1) {
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ case AUDIO_ENCODING_LINEARLE:
+ if (Modify(ainfo.play.precision) &&
+ ainfo.play.precision != 16) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.play.channels) &&
+ (ainfo.play.channels != 1 &&
+ ainfo.play.channels != 2))
+ {
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR8:
+ if (Modify(ainfo.play.precision) &&
+ ainfo.play.precision != 8) {
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.play.channels) &&
+ (ainfo.play.channels != 1 &&
+ ainfo.play.channels != 2))
+ {
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ if (retval < 0)
+ break;
+
+ /* If we got this far, we're at least sane with
+ * respect to generics. Try the changes. */
+ if ((driver->ops->set_input_precision(driver, ainfo.record.precision) < 0) ||
+ (driver->ops->set_output_precision(driver, ainfo.play.precision) < 0) ||
+ (driver->ops->set_input_channels(driver, ainfo.record.channels) < 0) ||
+ (driver->ops->set_output_channels(driver, ainfo.play.channels) < 0) ||
+ (driver->ops->set_input_rate(driver, ainfo.record.sample_rate) < 0) ||
+ (driver->ops->set_output_rate(driver, ainfo.play.sample_rate) < 0) ||
+ (driver->ops->set_input_encoding(driver, ainfo.record.encoding) < 0) ||
+ (driver->ops->set_output_encoding(driver, ainfo.play.encoding) < 0))
+ {
+ /* Pray we can set it all back. If not, uh... */
+ driver->ops->set_input_precision(driver, curinfo.record.precision);
+ driver->ops->set_output_precision(driver, curinfo.play.precision);
+ driver->ops->set_input_channels(driver, curinfo.record.channels);
+ driver->ops->set_output_channels(driver, curinfo.play.channels);
+ driver->ops->set_input_rate(driver, curinfo.record.sample_rate);
+ driver->ops->set_output_rate(driver, curinfo.play.sample_rate);
+ driver->ops->set_input_encoding(driver, curinfo.record.encoding);
+ driver->ops->set_output_encoding(driver, curinfo.play.encoding);
+ }
+
+ }
+
+ printk("sparcaudio_ioctl: AUDIO_SETINFO\n");
+ break;
+ }
+
default:
if (driver->ops->ioctl)
retval = driver->ops->ioctl(inode,file,cmd,arg,driver);
- else
+ else {
retval = -EINVAL;
+
+ printk("sparcaudio_ioctl: 0x%x\n", cmd);
+ }
}
return retval;
}
+static int sparcaudioctl_release(struct inode * inode, struct file * file)
+{
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct file_operations sparcaudioctl_fops = {
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* sparcaudio_readdir */
+ NULL, /* sparcaudio_select */
+ sparcaudio_ioctl,
+ NULL, /* sparcaudio_mmap */
+ NULL,
+ sparcaudioctl_release
+};
+
static int sparcaudio_open(struct inode * inode, struct file * file)
{
int err;
@@ -259,6 +731,15 @@ static int sparcaudio_open(struct inode * inode, struct file * file)
if (!driver)
return -ENODEV;
+ if (MINOR(inode->i_rdev) == 5) {
+
+ file->f_op = &sparcaudioctl_fops;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+ }
+
/* We only support minor #4 (/dev/audio) right now. */
if (MINOR(inode->i_rdev) != 4)
return -ENXIO;
@@ -285,9 +766,18 @@ static int sparcaudio_open(struct inode * inode, struct file * file)
}
/* Mark the driver as locked for read and/or write. */
- if (file->f_mode & FMODE_READ)
+ if (file->f_mode & FMODE_READ) {
+ driver->input_offset = 0;
+ driver->input_front = 0;
+ driver->input_rear = 0;
+ driver->input_count = 0;
+ driver->ops->start_input(driver, driver->input_buffers[driver->input_front],
+ PAGE_SIZE);
+ driver->input_active = 1;
driver->flags |= SDF_OPEN_READ;
+ }
if (file->f_mode & FMODE_WRITE) {
+ driver->output_size = 0;
driver->output_front = 0;
driver->output_rear = 0;
driver->output_count = 0;
@@ -308,8 +798,15 @@ static int sparcaudio_open(struct inode * inode, struct file * file)
return 0;
}
-static void sparcaudio_release(struct inode * inode, struct file * file)
+static int sparcaudio_release(struct inode * inode, struct file * file)
{
+ /* Anything in the queue? */
+ sparcaudio_sync_output(driver);
+
+ /* Stop input */
+ driver->ops->stop_input(driver);
+ driver->input_active = 0;
+
/* Wait for any output still in the queue to be played. */
if (driver->output_count > 0)
interruptible_sleep_on(&driver->output_drain_wait);
@@ -331,10 +828,12 @@ static void sparcaudio_release(struct inode * inode, struct file * file)
MOD_DEC_USE_COUNT;
wake_up_interruptible(&driver->open_wait);
+
+ return 0;
}
static struct file_operations sparcaudio_fops = {
- sparcaudio_lseek,
+ sparcaudio_llseek,
sparcaudio_read,
sparcaudio_write,
NULL, /* sparcaudio_readdir */
diff --git a/drivers/sbus/audio/audio.h b/drivers/sbus/audio/audio.h
index 127700ab5..5884aeaab 100644
--- a/drivers/sbus/audio/audio.h
+++ b/drivers/sbus/audio/audio.h
@@ -82,12 +82,13 @@ typedef struct audio_info {
/*
* Audio encoding types
*/
-#define AUDIO_ENCODING_NONE (0) /* no encoding assigned */
-#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */
-#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */
-#define AUDIO_ENCODING_LINEAR (3) /* Linear PCM encoding */
-#define AUDIO_ENCODING_DVI (104) /* DVI ADPCM */
-#define AUDIO_ENCODING_LINEAR8 (105) /* 8 bit UNSIGNED */
+#define AUDIO_ENCODING_NONE (0) /* no encoding assigned */
+#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */
+#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */
+#define AUDIO_ENCODING_LINEAR (3) /* Linear PCM encoding */
+#define AUDIO_ENCODING_DVI (104) /* DVI ADPCM */
+#define AUDIO_ENCODING_LINEAR8 (105) /* 8 bit UNSIGNED */
+#define AUDIO_ENCODING_LINEARLE (106) /* Linear PCM LE encoding */
/*
* These ranges apply to record, play, and monitor gain values
@@ -136,6 +137,8 @@ typedef struct audio_info {
#define AUDIO_LINE_IN 0x02 /* input from line in */
#define AUDIO_CD 0x04 /* input from on-board CD inputs */
#define AUDIO_INTERNAL_CD_IN AUDIO_CD /* input from internal CDROM */
+/* Supposedly an undocumented feature of the 4231 */
+#define AUDIO_ANALOG_LOOPBACK 0x40
/*
@@ -152,6 +155,14 @@ typedef struct audio_info {
/*
+ * These allow testing for what the user wants to set
+ */
+#define AUD_INITVALUE (~0)
+#define Modify(X) ((unsigned int)(X) != AUD_INITVALUE)
+#define Modifys(X) ((X) != (unsigned short)AUD_INITVALUE)
+#define Modifyc(X) ((X) != (unsigned char)AUD_INITVALUE)
+
+/*
* Parameter for the AUDIO_GETDEV ioctl to determine current
* audio devices.
*/
@@ -192,6 +203,16 @@ typedef struct audio_device {
#define AUDIO_SETINFO _IOWR('A', 2, audio_info_t)
#define AUDIO_DRAIN _IO('A', 3)
#define AUDIO_GETDEV _IOR('A', 4, audio_device_t)
+#define AUDIO_GETDEV_SUNOS _IOR('A', 4, int)
+
+/* Define possible audio hardware configurations for
+ * old SunOS-style AUDIO_GETDEV ioctl */
+
+#define AUDIO_DEV_UNKNOWN (0) /* not defined */
+#define AUDIO_DEV_AMD (1) /* audioamd device */
+#define AUDIO_DEV_SPEAKERBOX (2) /* dbri device with speakerbox */
+#define AUDIO_DEV_CODEC (3) /* dbri device (internal speaker) */
+#define AUDIO_DEV_CS4231 (5) /* cs4231 device */
/*
* The following ioctl sets the audio device into an internal loopback mode,
@@ -214,8 +235,6 @@ struct audtrace_hdr {
};
#endif
-
-
/*
* Linux kernel internal implementation.
*/
@@ -245,10 +264,17 @@ struct sparcaudio_driver
/* Support for a circular queue of output buffers. */
__u8 **output_buffers;
- size_t *output_sizes;
+ size_t *output_sizes, output_size;
int num_output_buffers, output_front, output_rear;
int output_count, output_active;
struct wait_queue *output_write_wait, *output_drain_wait;
+
+ /* Support for a circular queue of input buffers. */
+ __u8 **input_buffers;
+ int input_offset;
+ int num_input_buffers, input_front, input_rear;
+ int input_count, input_active;
+ struct wait_queue *input_read_wait;
};
struct sparcaudio_operations
@@ -272,6 +298,77 @@ struct sparcaudio_operations
/* Return driver name/version to caller. (/dev/audio specific) */
void (*sunaudio_getdev)(struct sparcaudio_driver *, audio_device_t *);
+
+ /* Get and set the output volume. (0-255) */
+ int (*set_output_volume)(struct sparcaudio_driver *, int);
+ int (*get_output_volume)(struct sparcaudio_driver *);
+
+ /* Get and set the input volume. (0-255) */
+ int (*set_input_volume)(struct sparcaudio_driver *, int);
+ int (*get_input_volume)(struct sparcaudio_driver *);
+
+ /* Get and set the monitor volume. (0-255) */
+ int (*set_monitor_volume)(struct sparcaudio_driver *, int);
+ int (*get_monitor_volume)(struct sparcaudio_driver *);
+
+ /* Get and set the output balance. (0-64) */
+ int (*set_output_balance)(struct sparcaudio_driver *, int);
+ int (*get_output_balance)(struct sparcaudio_driver *);
+
+ /* Get and set the input balance. (0-64) */
+ int (*set_input_balance)(struct sparcaudio_driver *, int);
+ int (*get_input_balance)(struct sparcaudio_driver *);
+
+ /* Get and set the output channels. (1-4) */
+ int (*set_output_channels)(struct sparcaudio_driver *, int);
+ int (*get_output_channels)(struct sparcaudio_driver *);
+
+ /* Get and set the input channels. (1-4) */
+ int (*set_input_channels)(struct sparcaudio_driver *, int);
+ int (*get_input_channels)(struct sparcaudio_driver *);
+
+ /* Get and set the output precision. (8-32) */
+ int (*set_output_precision)(struct sparcaudio_driver *, int);
+ int (*get_output_precision)(struct sparcaudio_driver *);
+
+ /* Get and set the input precision. (8-32) */
+ int (*set_input_precision)(struct sparcaudio_driver *, int);
+ int (*get_input_precision)(struct sparcaudio_driver *);
+
+ /* Get and set the output port. () */
+ int (*set_output_port)(struct sparcaudio_driver *, int);
+ int (*get_output_port)(struct sparcaudio_driver *);
+
+ /* Get and set the input port. () */
+ int (*set_input_port)(struct sparcaudio_driver *, int);
+ int (*get_input_port)(struct sparcaudio_driver *);
+
+ /* Get and set the output encoding. () */
+ int (*set_output_encoding)(struct sparcaudio_driver *, int);
+ int (*get_output_encoding)(struct sparcaudio_driver *);
+
+ /* Get and set the input encoding. () */
+ int (*set_input_encoding)(struct sparcaudio_driver *, int);
+ int (*get_input_encoding)(struct sparcaudio_driver *);
+
+ /* Get and set the output rate. () */
+ int (*set_output_rate)(struct sparcaudio_driver *, int);
+ int (*get_output_rate)(struct sparcaudio_driver *);
+
+ /* Get and set the input rate. () */
+ int (*set_input_rate)(struct sparcaudio_driver *, int);
+ int (*get_input_rate)(struct sparcaudio_driver *);
+
+ /* Return driver number to caller. (SunOS /dev/audio specific) */
+ int (*sunaudio_getdev_sunos)(struct sparcaudio_driver *);
+
+ /* Get available ports */
+ int (*get_output_ports)(struct sparcaudio_driver *);
+ int (*get_input_ports)(struct sparcaudio_driver *);
+
+ /* Get and set output mute */
+ int (*set_output_muted)(struct sparcaudio_driver *, int);
+ int (*get_output_muted)(struct sparcaudio_driver *);
};
extern int register_sparcaudio_driver(struct sparcaudio_driver *);
@@ -282,6 +379,6 @@ extern int sparcaudio_init(void);
extern int amd7930_init(void);
extern int cs4231_init(void);
-#endif
+#endif /* __KERNEL__ */
-#endif
+#endif /* _AUDIO_H */
diff --git a/drivers/sbus/audio/cs4215.h b/drivers/sbus/audio/cs4215.h
new file mode 100644
index 000000000..966339a02
--- /dev/null
+++ b/drivers/sbus/audio/cs4215.h
@@ -0,0 +1,120 @@
+/*
+ * drivers/sbus/audio/cs4215.h
+ *
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ * Used with dbri.h
+ */
+
+#ifndef _CS4215_H_
+#define _CS4215_H_
+
+struct cs4215 {
+ __u8 data[4]; /* Data mode: Time slots 5-8 */
+ __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */
+ __volatile__ struct dbri_mem td;
+ __volatile__ struct dbri_mem rd;
+ __u8 version;
+ __u8 onboard;
+};
+
+
+/*
+ * Control mode first
+ */
+
+/* Time Slot 1, Status register */
+#define CS4215_CLB (1<<2) /* Control Latch Bit */
+#define CS4215_OLB (1<<3) /* 1: line: 2.0V, speaker 4V */
+ /* 0: line: 2.8V, speaker 8V */
+#define CS4215_MLB (1<<4) /* 1: Microphone: 20dB gain disabled */
+#define CS4215_RSRVD_1 (1<<5)
+
+
+/* Time Slot 2, Data Format Register */
+#define CS4215_DFR_LINEAR16 0
+#define CS4215_DFR_ULAW 1
+#define CS4215_DFR_ALAW 2
+#define CS4215_DFR_LINEAR8 3
+#define CS4215_DFR_STEREO (1<<2)
+static struct {
+ unsigned short freq;
+ unsigned char xtal;
+ unsigned char csval;
+} CS4215_FREQ[] = {
+ { 8000, 1, (0<<3) },
+ { 16000, 1, (1<<3) },
+ { 27429, 1, (2<<3) }, /* Actually 24428.57 */
+ { 32000, 1, (3<<3) },
+ /* { NA, 1, (4<<3) }, */
+ /* { NA, 1, (5<<3) }, */
+ { 48000, 1, (6<<3) },
+ { 9600, 1, (7<<3) },
+ { 5513, 2, (0<<3) }, /* Actually 5512.5 */
+ { 11025, 2, (1<<3) },
+ { 18900, 2, (2<<3) },
+ { 22050, 2, (3<<3) },
+ { 37800, 2, (4<<3) },
+ { 44100, 2, (5<<3) },
+ { 33075, 2, (6<<3) },
+ { 6615, 2, (7<<3) },
+ { 0, 0, 0 }
+};
+#define CS4215_HPF (1<<7) /* High Pass Filter, 1: Enabled */
+
+#define CS4215_12_MASK 0xfcbf /* Mask off reseved bits in slot 1 & 2 */
+
+/* Time Slot 3, Serial Port Control register */
+#define CS4215_XEN (1<<0) /* 0: Enable serial output */
+#define CS4215_XCLK (1<<1) /* 1: Master mode: Generate SCLK */
+#define CS4215_BSEL_64 (0<<2) /* Bitrate: 64 bits per frame */
+#define CS4215_BSEL_128 (1<<2)
+#define CS4215_BSEL_256 (2<<2)
+#define CS4215_MCK_MAST (0<<4) /* Master clock */
+#define CS4215_MCK_XTL1 (1<<4) /* 24.576 MHz clock source */
+#define CS4215_MCK_XTL2 (2<<4) /* 16.9344 MHz clock source */
+#define CS4215_MCK_CLK1 (3<<4) /* Clockin, 256 x Fs */
+#define CS4215_MCK_CLK2 (4<<4) /* Clockin, see DFR */
+
+/* Time Slot 4, Test Register */
+#define CS4215_DAD (1<<0) /* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */
+#define CS4215_ENL (1<<1) /* Enable Loopback Testing */
+
+/* Time Slot 5, Parallel Port Register */
+/* Read only here and the same as the in data mode */
+
+/* Time Slot 6, Reserved */
+
+/* Time Slot 7, Version Register */
+#define CS4215_VERSION_MASK 0xf /* Known versions 0/C, 1/D, 2/E */
+
+/* Time Slot 8, Reserved */
+
+
+
+/*
+ * Data mode
+ */
+/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data */
+
+/* Time Slot 5, Output Setting */
+#define CS4215_LO(v) v /* Left Output Attenuation 0x3f: -94.5 dB */
+#define CS4215_LE (1<<6) /* Line Out Enable */
+#define CS4215_HE (1<<7) /* Headphone Enable */
+
+/* Time Slot 6, Output Setting */
+#define CS4215_RO(v) v /* Right Output Attenuation 0x3f: -94.5 dB */
+#define CS4215_SE (1<<6) /* Line Out Enable */
+#define CS4215_ADI (1<<7) /* A/D Data Invalid: Busy in calibration */
+
+/* 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 occured */
+#define CS4215_PIO0 (1<<6) /* Parallel I/O 0 */
+#define CS4215_PIO1 (1<<7)
+
+/* Time Slot 8, Input Setting */
+#define CS4215_RG(v) v /* Right Gain Setting 0xf: 22.5 dB */
+#define CS4215_MA(v) (v<<4) /* Monitor Path Attenuation 0xf: mute */
+
+#endif
diff --git a/drivers/sbus/audio/cs4231.c b/drivers/sbus/audio/cs4231.c
index 5c361a0b0..346f07185 100644
--- a/drivers/sbus/audio/cs4231.c
+++ b/drivers/sbus/audio/cs4231.c
@@ -8,7 +8,6 @@
* sun4m machines.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -44,8 +43,6 @@ static int cs4231_recintr(struct sparcaudio_driver *drv);
static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value);
static void cs4231_mute(struct sparcaudio_driver *drv);
static void cs4231_pollinput(struct sparcaudio_driver *drv);
-static int cs4231_attach(struct sparcaudio_driver *drv, int node,
- struct linux_sbus *sbus);
#define CHIP_BUG udelay(100); cs4231_ready(drv); udelay(1000);
@@ -623,56 +620,16 @@ static struct sparcaudio_operations cs4231_ops = {
cs4231_audio_getdev,
};
-/* Probe for the cs4231 chip and then attach the driver. */
-#ifdef MODULE
-int init_module(void)
-#else
-__initfunc(int cs4231_init(void))
-#endif
-{
- struct linux_sbus *bus;
- struct linux_sbus_device *sdev;
- int cs4231_node;
-
- /* Find the PROM CS4231 node. */
- /* There's an easier way, and I should FIXME */
- cs4231_node = prom_getchild(prom_root_node);
- cs4231_node = prom_searchsiblings(cs4231_node,"iommu");
- cs4231_node = prom_getchild(cs4231_node);
- cs4231_node = prom_searchsiblings(cs4231_node,"sbus");
- cs4231_node = prom_getchild(cs4231_node);
- cs4231_node = prom_searchsiblings(cs4231_node,"SUNW,CS4231");
-
- if (cs4231_node && cs4231_attach(&drivers[0], cs4231_node, NULL) == 0)
- num_drivers = 1;
- else
- num_drivers = 0;
-
- /* Probe each SBUS for cs4231 chips. */
- for_all_sbusdev(sdev,bus) {
- if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
- /* Don't go over the max number of drivers. */
- if (num_drivers >= MAX_DRIVERS)
- continue;
-
- if (cs4231_attach(&drivers[num_drivers],
- sdev->prom_node, sdev->my_bus) == 0)
- num_drivers++;
- }
- }
-
- /* Only return success if we found some cs4231 chips. */
- return (num_drivers > 0) ? 0 : -EIO;
-}
-
/* Attach to an cs4231 chip given its PROM node. */
-static int cs4231_attach(struct sparcaudio_driver *drv, int node,
- struct linux_sbus *sbus)
+static inline int
+cs4231_attach(struct sparcaudio_driver *drv, struct linux_sbus_device *sdev)
{
- struct linux_prom_registers regs;
- struct linux_prom_irqs irq;
struct cs4231_chip *cs4231_chip;
int err;
+ struct linux_sbus *sbus = sdev->my_bus;
+#ifdef __sparc_v9__
+ struct devid_cookie dcookie;
+#endif
/* Allocate our private information structure. */
drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL);
@@ -690,12 +647,11 @@ static int cs4231_attach(struct sparcaudio_driver *drv, int node,
#endif
/* Map the registers into memory. */
- prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
- if (sbus)
- prom_apply_sbus_ranges(sbus, &regs, 1);
- cs4231_chip->regs_size = regs.reg_size;
- cs4231_chip->pioregs = sparc_alloc_io(regs.phys_addr, 0, regs.reg_size,
- "cs4231", regs.which_io, 0);
+ prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev);
+ cs4231_chip->regs_size = sdev->reg_addrs[0].reg_size;
+ cs4231_chip->pioregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+ sdev->reg_addrs[0].reg_size,
+ "cs4231", sdev->reg_addrs[0].which_io, 0);
if (!cs4231_chip->pioregs) {
printk(KERN_ERR "cs4231: could not allocate registers\n");
kfree(drv->private);
@@ -706,9 +662,18 @@ static int cs4231_attach(struct sparcaudio_driver *drv, int node,
cs4231_reset(drv);
/* Attach the interrupt handler to the audio interrupt. */
- prom_getproperty(node, "intr", (char *)&irq, sizeof(irq));
- cs4231_chip->irq = irq.pri;
- request_irq(cs4231_chip->irq, cs4231_interrupt, SA_INTERRUPT, "cs4231", NULL);
+ cs4231_chip->irq = sdev->irqs[0].pri;
+
+#ifndef __sparc_v9__
+ request_irq(cs4231_chip->irq, cs4231_interrupt, SA_SHIRQ, "cs4231", drv);
+#else
+ dcookie.real_dev_id = s;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = sdev->my_bus;
+ request_irq (cs4231_chip->irq, cs4231_interrupt, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "cs4231", drv);
+ cs4231_chip->irq = dcookie.ret_ino;
+#endif
enable_irq(cs4231_chip->irq);
/* Register ourselves with the midlevel audio driver. */
@@ -730,6 +695,32 @@ static int cs4231_attach(struct sparcaudio_driver *drv, int node,
return 0;
}
+/* Probe for the cs4231 chip and then attach the driver. */
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int cs4231_init(void))
+#endif
+{
+ struct linux_sbus *bus;
+ struct linux_sbus_device *sdev;
+
+ /* Probe each SBUS for cs4231 chips. */
+ for_all_sbusdev(sdev,bus) {
+ if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
+ /* Don't go over the max number of drivers. */
+ if (num_drivers >= MAX_DRIVERS)
+ continue;
+
+ if (cs4231_attach(&drivers[num_drivers], sdev) == 0)
+ num_drivers++;
+ }
+ }
+
+ /* Only return success if we found some cs4231 chips. */
+ return (num_drivers > 0) ? 0 : -EIO;
+}
+
#ifdef MODULE
/* Detach from an cs4231 chip given the device structure. */
static void cs4231_detach(struct sparcaudio_driver *drv)
diff --git a/drivers/sbus/audio/dbri.c b/drivers/sbus/audio/dbri.c
new file mode 100644
index 000000000..0456282ef
--- /dev/null
+++ b/drivers/sbus/audio/dbri.c
@@ -0,0 +1,715 @@
+/*
+ * drivers/sbus/audio/dbri.c
+ *
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ * The SparcLinux interface was adopted from the CS4231 driver.
+ *
+ * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
+ * on Sun SPARCstation 10, 20, LX and Voyager models.
+ * NOTE: This driver only supports audio for now, there is NO SUPPORT for ISDN.
+ *
+ * - 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
+ * 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
+ * page.
+ * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec
+ * Interfaces: CHI, Audio In & Out, 2 bits parallel
+ * 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
+ * device (short pipes).
+ * A timeslot defines the bit-offset and nr 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
+ * 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 :-)
+ * 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.
+ */
+
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/sbus.h>
+
+#include "audio.h"
+#include "dbri.h"
+
+
+
+#define DBRI_DEBUG
+
+#ifdef DBRI_DEBUG
+
+#define dprintk(a, x) if(dbri_debug & a) printk x
+#define D_GEN (1<<0)
+#define D_INT (1<<1)
+#define D_CMD (1<<2)
+#define D_MM (1<<3)
+#define D_USR (1<<4)
+
+static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR;
+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->cmd[i]); }
+
+#else
+
+#define dprintk(a, x)
+#define dumpcmd
+
+#endif /* DBRI_DEBUG */
+
+
+
+#define MAX_DRIVERS 2 /* Increase this if need more than 2 DBRI's */
+
+#define WAIT_INTR1 0xbe
+#define WAIT_INTR2 0xbf
+
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static char drv_name[] = "DBRI/audio";
+static int num_drivers;
+static int dbri_cmdlocked = 0;
+
+
+/*
+ * Make sure, that we can send a command to the dbri
+ */
+static int dbri_cmdlock(struct dbri *dbri)
+{
+ unsigned long flags;
+ int was_sleeping = 0;
+
+ save_flags(flags);
+ cli();
+
+ if(dbri_cmdlocked) {
+ interruptible_sleep_on(&dbri->wait);
+ was_sleeping = 1;
+ }
+ if(dbri_cmdlocked)
+ return -EINTR;
+ dbri_cmdlocked = 1;
+
+ restore_flags(flags);
+
+ if(was_sleeping)
+ dprintk(D_INT, ("DBRI: Just woke up\n"));
+ 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;
+ int i;
+
+ dprintk(D_GEN, ("DBRI: reset 0:%x 2:%x 8:%x 9:%x\n",
+ dbri->regs->reg0, dbri->regs->reg2,
+ dbri->regs->reg8, dbri->regs->reg9));
+
+ dbri->regs->reg0 = D_R; /* Soft Reset */
+ for(i = 0; (dbri->regs->reg0 & D_R) && i < 10; i++)
+ udelay(10);
+}
+
+static void dbri_detach(struct sparcaudio_driver *drv)
+{
+ struct dbri *info = (struct dbri *)drv->private;
+
+ dbri_reset(drv);
+ unregister_sparcaudio_driver(drv);
+ free_irq(info->irq, drv);
+ sparc_free_io(info->regs, info->regs_size);
+ kfree(drv->private);
+}
+
+
+static void dbri_init(struct sparcaudio_driver *drv)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+ int n;
+
+ dbri_reset(drv);
+ dbri->wait = NULL;
+
+ dprintk(D_GEN, ("DBRI: init: cmd: %x, int: %x\n",
+ (int)dbri->cmd, (int)dbri->intr));
+
+ /*
+ * Initialize the interrupt ringbuffer.
+ */
+ for(n = 0; n < DBRI_NO_INTS-1; n++)
+ dbri->intr[n * DBRI_INT_BLK] =
+ (int)(&dbri->intr[(n+1)*DBRI_INT_BLK]);
+ dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr);
+ dbri->dbri_irqp = 1;
+
+ dbri->regs->reg0 |= (D_G|D_S|D_E);
+
+ /*
+ * Set up the interrupt queue
+ */
+ (void)dbri_cmdlock(dbri);
+
+ n = 0;
+ dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0);
+ dbri->cmd[n++] = (int)(dbri->intr);
+ dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1);
+ dbri->regs->reg8 = (int)dbri->cmd;
+}
+
+
+
+/*
+ * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
+ * So we have to reverse the bits. Note: only 1, 2 or 4 bytes are supported.
+ */
+static __u32 reverse_bytes(__u32 b, int len)
+{
+ switch(len) {
+ case 4: b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
+ case 2: b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8);
+ case 1: b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4);
+ b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2);
+ b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
+ }
+ return b;
+}
+
+
+
+static void mmcodec_default(struct cs4215 *mm)
+{
+ /*
+ * No action, memory resetting only.
+ *
+ * Data Time Slot 5-8
+ * Speaker,Line and Headphone enable. Gain set to the half.
+ * Input is mike.
+ */
+ 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[3] = CS4215_RG( 0x8) | CS4215_MA(0xf);
+
+ /*
+ * Control Time Slot 1-4
+ * 0: Default I/O voltage scale
+ * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
+ * 2: Serial enable, CHI master, 1 CHI device (64bit), clock 1
+ * 3: Tests disabled
+ */
+ mm->ctrl[0] = CS4215_RSRVD_1;
+ mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
+ mm->ctrl[2] = CS4215_XEN | CS4215_XCLK |
+ CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
+ mm->ctrl[3] = 0;
+}
+
+static void mmcodec_init_data(struct dbri *dbri)
+{
+ int val, n = 0;
+
+ dbri_cmdlock(dbri);
+
+ /*
+ * Data mode:
+ * 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
+ * interrupt, and the rest of the data (slot 5 and 8) is
+ * not relevant for us (only for doublechecking).
+ */
+
+ /* 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);
+ 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 */
+ 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);
+
+ 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);
+ 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);
+ 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_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;
+
+ dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
+
+ dbri->regs->reg8 = (int)dbri->cmd;
+}
+
+
+/*
+ * Send the control information (i.e. audio format)
+ */
+static void mmcodec_setctrl(struct dbri *dbri)
+{
+ int n = 0, val;
+
+ /*
+ * Enable Command mode: Set PIO3 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);
+
+ dbri_cmdlock(dbri);
+
+ /*
+ * 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).
+ */
+
+ /* Set CHI Anchor: Pipe 16. This should take care of the rest. */
+ 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);
+
+
+ /* Setup the pipes first */
+ val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_17);
+ 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);
+ 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);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ /* Fill in the data to send */
+ 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_PAUSE, 0, 0);
+
+
+
+ /* Link the timeslots */
+ 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);
+
+ 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++] = 0;
+
+ 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++] = 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);
+ dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+
+ dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0);
+ 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);
+
+
+ /* Now switch back to data mode */
+ n = 0;
+ /* 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_WAIT, 1, 0x17);
+ dbri->regs->reg8 = (int)dbri->cmd;
+
+ dbri->regs->reg2 = D_ENPIO | D_PIO3 |
+ (dbri->mm.onboard ? D_PIO0 : D_PIO2);
+#endif
+
+ /* We are ready */
+ dbri_cmdlocked = 0;
+ wake_up(&dbri->wait);
+}
+
+static int mmcodec_init(struct sparcaudio_driver *drv)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+ int reg2 = dbri->regs->reg2;
+
+
+ /* Look for the cs4215 chips */
+ if(reg2 & D_PIO2) {
+ dprintk(D_MM, ("DBRI: Onboard CS4215 detected\n"));
+ dbri->mm.onboard = 1;
+ }
+ if(reg2 & D_PIO0) {
+ dprintk(D_MM, ("DBRI: Speakerbox detected\n"));
+ dbri->mm.onboard = 0;
+ }
+
+
+ /* Using the Speakerbox, if both are attached. */
+ if((reg2 & D_PIO2) && (reg2 & D_PIO0)) {
+ printk("DBRI: Using speakerbox / ignoring onboard mmcodec.\n");
+ dbri->regs->reg2 = D_ENPIO2;
+ dbri->mm.onboard = 0;
+ }
+ if( !(reg2 & (D_PIO0|D_PIO2)) ) {
+ printk("DBRI: no mmcodec found.\n");
+ return -EIO;
+ }
+
+
+ /* Now talk to our baby */
+ dbri->regs->reg0 |= D_C; /* Enable CHI */
+
+ mmcodec_default(&dbri->mm);
+
+ dbri->mm.version = 0xff;
+ mmcodec_setctrl(dbri);
+ if(dbri->mm.version == 0xff)
+ return -EIO;
+
+ /*
+ mmcodec_init_data(dbri, &n);
+ */
+
+ return 0;
+}
+
+void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
+ struct dbri *dbri = (struct dbri *)drv->private;
+ int x, val;
+ static int numint = 0;
+
+ /*
+ * Read it, so the interrupt goes away.
+ */
+ x = dbri->regs->reg1;
+ if(numint++ > 20) {
+ dbri->regs->reg0 = D_R; /* Soft Reset */
+ numint = 0;
+ printk("Soft reset\n");
+ }
+
+ 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 (!(x & D_IR)) /* Not for us */
+ return;
+
+ x = dbri->intr[dbri->dbri_irqp];
+ while (x != 0) {
+ dbri->intr[dbri->dbri_irqp] = 0;
+
+ 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)));
+ }
+
+ val = D_INTR_GETVAL(x);
+
+ switch(D_INTR_GETCHAN(x)) {
+ case D_INTR_CMD:
+ if(D_INTR_GETCMD(x) == D_WAIT)
+ if(val == WAIT_INTR1) {
+ dbri_cmdlocked = 0;
+ wake_up(&dbri->wait);
+ }
+ if(val == WAIT_INTR2)
+ wake_up(&dbri->int_wait);
+ break;
+ case D_P_18:
+ if(val != 0) {
+ x = reverse_bytes(val,2)&CS4215_12_MASK;
+printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl);
+ if(x == (*(int *)dbri->mm.ctrl >> 16))
+{
+printk("Comp ok\n");
+ wake_up(&dbri->int_wait);
+}
+ }
+ break;
+ case D_P_19:
+ if(val != 0) {
+ dbri->mm.version =
+ reverse_bytes(val, 1) & 0xf;
+ }
+ break;
+ }
+
+ 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->intr[dbri->dbri_irqp];
+ }
+}
+
+
+
+
+
+static int dbri_attach(struct sparcaudio_driver *drv,
+ struct linux_sbus_device *sdev)
+{
+ struct dbri *dbri;
+ struct linux_prom_irqs 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;
+ }
+
+ drv->ops = &dbri_ops;
+ drv->private = kmalloc(sizeof(struct dbri), GFP_KERNEL);
+ if (!drv->private)
+ return -ENOMEM;
+ dbri = (struct dbri *)drv->private;
+
+ memset(dbri, 0, sizeof(*dbri));
+
+ dbri->dbri_version = sdev->prom_name[9];
+
+ /* Map the registers into memory. */
+ prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0],
+ sdev->num_registers, sdev);
+ dbri->regs_size = sdev->reg_addrs[0].reg_size;
+ dbri->regs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+ sdev->reg_addrs[0].reg_size,
+ drv_name, sdev->reg_addrs[0].which_io, 0);
+ if (!dbri->regs) {
+ printk(KERN_ERR "DBRI: could not allocate registers\n");
+ kfree(drv->private);
+ return -EIO;
+ }
+
+ prom_getproperty(sdev->prom_node, "intr", (char *)&irq, sizeof(irq));
+ dbri->irq = irq.pri;
+
+ err = request_irq(dbri->irq, dbri_intr, SA_SHIRQ, "DBRI/audio", drv);
+ if (err) {
+ printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
+ sparc_free_io(dbri->regs, dbri->regs_size);
+ kfree(drv->private);
+ return err;
+ }
+
+ /* Register ourselves with the midlevel audio driver. */
+ err = register_sparcaudio_driver(drv);
+ if (err) {
+ printk(KERN_ERR "DBRI: unable to register audio\n");
+ free_irq(dbri->irq, drv);
+ sparc_free_io(dbri->regs, dbri->regs_size);
+ kfree(drv->private);
+ return err;
+ }
+
+ dbri_init(drv);
+ err = mmcodec_init(drv);
+ if(err) {
+ dbri_detach(drv);
+ return err;
+ }
+
+
+ dbri->perchip_info.play.active = dbri->perchip_info.play.pause = 0;
+ dbri->perchip_info.record.active = dbri->perchip_info.record.pause = 0;
+
+ printk(KERN_INFO "audio%d at 0x%lx (irq %d) is DBRI(%c)+CS4215(%d)\n",
+ num_drivers, (unsigned long)dbri->regs,
+ dbri->irq, dbri->dbri_version, dbri->mm.version);
+
+ return 0;
+}
+
+/* Probe for the dbri chip and then attach the driver. */
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int dbri_init(void))
+#endif
+{
+ struct linux_sbus *bus;
+ struct linux_sbus_device *sdev;
+
+ num_drivers = 0;
+
+ /* Probe each SBUS for the DBRI chip(s). */
+ for_all_sbusdev(sdev,bus) {
+ /*
+ * 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));
+ if (num_drivers >= MAX_DRIVERS) {
+ printk("DBRI: Ignoring slot %d\n", sdev->slot);
+ continue;
+ }
+
+ if (dbri_attach(&drivers[num_drivers], sdev) == 0)
+ num_drivers++;
+ }
+ }
+
+ return (num_drivers > 0) ? 0 : -EIO;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ register int i;
+
+ for (i = 0; i < num_drivers; i++) {
+ dbri_detach(&drivers[i]);
+ num_drivers--;
+ }
+}
+#endif
diff --git a/drivers/sbus/audio/dbri.h b/drivers/sbus/audio/dbri.h
new file mode 100644
index 000000000..098616ac8
--- /dev/null
+++ b/drivers/sbus/audio/dbri.h
@@ -0,0 +1,293 @@
+/*
+ * drivers/sbus/audio/cs4231.h
+ *
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ */
+
+#ifndef _DBRI_H_
+#define _DBRI_H_
+
+#include <linux/types.h>
+
+struct dbri_regs {
+ __volatile__ __u32 reg0; /* Status & Control */
+ __volatile__ __u32 reg1; /* Mode & Interrupt */
+ __volatile__ __u32 reg2; /* Parallel IO */
+ __volatile__ __u32 reg3; /* Test */
+ __volatile__ __u32 unused[4];
+ __volatile__ __u32 reg8; /* Command Queue Pointer */
+ __volatile__ __u32 reg9; /* Interrupt Queue Pointer */
+};
+
+#define DBRI_NO_CMDS 64
+#define DBRI_NO_INTS 2
+#define DBRI_INT_BLK 64
+
+#define DBRI_MM_ONB 1
+#define DBRI_MM_SB 2
+
+struct dbri_mem {
+ __u32 flags;
+ __u32 ba; /* Transmit/Receive Buffer Address */
+ __u32 nda; /* Next Descriptor Address */
+ __u32 status;
+};
+
+#include "cs4215.h"
+
+/* This structure holds the information for both chips (DBRI & CS4215) */
+struct dbri {
+ int regs_size, irq; /* Needed for unload */
+
+ struct dbri_regs *regs; /* dbri HW regs */
+ int dbri_version; /* 'e' and up is OK */
+ int dbri_irqp; /* intr queue pointer */
+ __volatile__ int cmd[DBRI_NO_CMDS]; /* Place for commands */
+ __volatile__ int intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */
+
+ struct cs4215 mm; /* mmcodec special info */
+
+ struct wait_queue *wait, *int_wait; /* Where to sleep if busy */
+ struct audio_info perchip_info;
+};
+
+
+/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
+#define D_P (1<<15) /* Program command & queue pointer valid */
+#define D_G (1<<14) /* Allow 4-Word SBus Burst */
+#define D_S (1<<13) /* Allow 16-Word SBus Burst */
+#define D_E (1<<12) /* Allow 8-Word SBus Burst */
+#define D_X (1<<7) /* Sanity Timer Disable */
+#define D_T (1<<6) /* Permit activation of the TE interface */
+#define D_N (1<<5) /* Permit activation of the NT interface */
+#define D_C (1<<4) /* Permit activation of the CHI interface */
+#define D_F (1<<3) /* Force Sanity Timer Time-Out */
+#define D_D (1<<2) /* Disable Master Mode */
+#define D_H (1<<1) /* Halt for Analysis */
+#define D_R (1<<0) /* Soft Reset */
+
+
+/* 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) */
+
+
+/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */
+#define D_ENPIO3 (1<<7) /* Enable Pin 3 */
+#define D_ENPIO2 (1<<6) /* Enable Pin 2 */
+#define D_ENPIO1 (1<<5) /* Enable Pin 1 */
+#define D_ENPIO0 (1<<4) /* Enable Pin 0 */
+#define D_ENPIO (0xf0) /* Enable all the pins */
+#define D_PIO3 (1<<3) /* Pin 3: 1: Data mode, 0: Ctrl mode */
+#define D_PIO2 (1<<2) /* Pin 2: 1: Onboard PDN */
+#define D_PIO1 (1<<1) /* Pin 1: 0: Reset */
+#define D_PIO0 (1<<0) /* Pin 0: 1: Speakerbox PDN */
+
+
+/* DBRI Commands (Page 20) */
+#define D_WAIT 0x0 /* Stop execution */
+#define D_PAUSE 0x1 /* Flush long pipes */
+#define D_JUMP 0x2 /* New command queue */
+#define D_IIQ 0x3 /* Initialize Interrupt Queue */
+#define D_REX 0x4 /* Report command execution via interrupt */
+#define D_SDP 0x5 /* Setup Data Pipe */
+#define D_CDP 0x6 /* Continue Data Pipe (reread NULL Pointer) */
+#define D_DTS 0x7 /* Define Time Slot */
+#define D_SSP 0x8 /* Set short Data Pipe */
+#define D_CHI 0x9 /* Set CHI Global Mode */
+#define D_NT 0xa /* NT Command */
+#define D_TE 0xb /* TE Command */
+#define D_CDEC 0xc /* Codec setup */
+#define D_TEST 0xd /* No comment */
+#define D_CDM 0xe /* CHI Data mode command */
+
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
+
+
+/* Special bits for some commands */
+#define D_PIPE(v) (v<<0) /* Pipe Nr: 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_CHANGE (2<<18) /* Report any changes */
+#define D_SDP_EVERY (3<<18) /* Report any changes */
+#define D_SDP_EOL (1<<17) /* EOL interrupt enable */
+#define D_SDP_IDLE (1<<16) /* HDLC idle interrupt enable */
+
+/* Pipe data MODE */
+#define D_SDP_MEM (0<<13) /* To/from memory */
+#define D_SDP_HDLC (2<<13)
+#define D_SDP_HDLC_D (3<<13) /* D Channel (prio control)*/
+#define D_SDP_SER (4<<13) /* Serial to serial */
+#define D_SDP_FIXED (6<<13) /* Short only */
+
+#define D_SDP_TO_SER (1<<12) /* Direction */
+#define D_SDP_FROM_SER (0<<12) /* Direction */
+#define D_SDP_MSB (1<<11) /* Bit order within Byte */
+#define D_SDP_LSB (0<<11) /* Bit order within Byte */
+#define D_SDP_P (1<<10) /* Pointer Valid */
+#define D_SDP_A (1<<8) /* Abort */
+#define D_SDP_C (1<<7) /* Clear */
+
+/* Define Time Slot */
+#define D_DTS_VI (1<<17) /* Valid Input Time-Slot Descriptor */
+#define D_DTS_VO (1<<16) /* Valid Output Time-Slot Descriptor */
+#define D_DTS_INS (1<<15) /* Insert Time Slot */
+#define D_DTS_DEL (0<<15) /* Delete Time Slot */
+#define D_DTS_PRVIN(v) (v<<10) /* Previous In Pipe */
+#define D_DTS_PRVOUT(v) (v<<5) /* Previous Out Pipe */
+
+/* Time Slot defines */
+#define D_TS_LEN(v) (v<<24) /* Number of bits in this time slot */
+#define D_TS_CYCLE(v) (v<<14) /* Bit Count at start of TS */
+#define D_TS_DI(v) (1<<13) /* Data Invert */
+#define D_TS_1CHANNEL (0<<10) /* Single Channel / Normal mode */
+#define D_TS_MONITOR (2<<10) /* Monitor pipe */
+#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 */
+
+/* Concentration Highway Interface Modes */
+#define D_CHI_CHICM(v) (v<<16) /* Clock mode */
+#define D_CHI_IR (1<<15) /* Immediate Interrupt Report */
+#define D_CHI_EN (1<<14) /* CHIL Interrupt enabled */
+#define D_CHI_OD (1<<13) /* Open Drain Enable */
+#define D_CHI_FE (1<<12) /* Sample CHIFS on Rising Frame Edge */
+#define D_CHI_FD (1<<11) /* Frame Drive */
+#define D_CHI_BPF(v) (v<<0) /* Bits per Frame */
+
+/* NT: These are here for completeness */
+#define D_NT_FBIT (1<<17) /* Frame Bit */
+#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_FT (1<<12) /* Fixed Timing */
+#define D_NT_EZ (1<<11) /* Echo Channel is Zeros */
+#define D_NT_IFA (1<<10) /* Inhibit Final Activation */
+#define D_NT_ACT (1<<9) /* Activate Interface */
+#define D_NT_MFE (1<<8) /* Multiframe Enable */
+#define D_NT_RLB(v) (1<<5) /* Remote Loopback */
+#define D_NT_LLB(v) (1<<2) /* Local Loopback */
+#define D_NT_FACT (1<<1) /* Force Activation */
+#define D_NT_ABV (1<<0) /* Activate Bipolar Violation */
+
+/* Codec Setup */
+#define D_CDEC_CK(v) (v<<24) /* Clock Select */
+#define D_CDEC_FED(v) (v<<12) /* FSCOD Falling Edge Delay */
+#define D_CDEC_RED(v) (v<<0) /* FSCOD Rising Edge Delay */
+
+/* Test */
+#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_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 */
+#define D_TEST_RAMBIST 0xa /* RAM Built-In Self Test */
+#define D_TEST_MCBIST 0xb /* Microcontroller Built-In Self Test */
+#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 */
+
+/* The Interrupts */
+#define D_INTR_BRDY 1 /* Buffer Ready for processing */
+#define D_INTR_MINT 2 /* Marked Interrupt in RD/TD */
+#define D_INTR_IBEG 3 /* Flag to idle transition detected (HDLC) */
+#define D_INTR_IEND 4 /* Idle to flag transition detected (HDLC) */
+#define D_INTR_EOL 5 /* End of List */
+#define D_INTR_CMDI 6 /* Command has bean read */
+#define D_INTR_XCMP 8 /* Transmission of frame complete */
+#define D_INTR_SBRI 9 /* BRI status change info */
+#define D_INTR_FXDT 10 /* Fixed data change */
+#define D_INTR_CHIL 11 /* CHI lost frame sync (channel 36 only) */
+#define D_INTR_COLL 11 /* Unrecoverable D-Channel collision */
+#define D_INTR_DBYT 12 /* Dropped by frame slip */
+#define D_INTR_RBYT 13 /* Repeated by frame slip */
+#define D_INTR_LINT 14 /* Lost Interrupt */
+#define D_INTR_UNDR 15 /* DMA underrun */
+
+#define D_INTR_TE 32
+#define D_INTR_NT 34
+#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_GETVAL(v) (v & 0xffff)
+#define D_INTR_GETRVAL(v) (v & 0xfffff)
+
+#define D_P_0 0 /* TE receive anchor */
+#define D_P_1 1 /* TE transmit anchor */
+#define D_P_2 2 /* NT transmit anchor */
+#define D_P_3 3 /* NT receive anchor */
+#define D_P_4 4 /* CHI send data */
+#define D_P_5 5 /* CHI receive data */
+#define D_P_6 6 /* */
+#define D_P_7 7 /* */
+#define D_P_8 8 /* */
+#define D_P_9 9 /* */
+#define D_P_10 10 /* */
+#define D_P_11 11 /* */
+#define D_P_12 12 /* */
+#define D_P_13 13 /* */
+#define D_P_14 14 /* */
+#define D_P_15 15 /* */
+#define D_P_16 16 /* CHI anchor pipe */
+#define D_P_17 17 /* CHI send */
+#define D_P_18 18 /* CHI receive */
+#define D_P_19 19 /* CHI receive */
+#define D_P_20 20 /* CHI receive */
+#define D_P_21 21 /* */
+#define D_P_22 22 /* */
+#define D_P_23 23 /* */
+#define D_P_24 24 /* */
+#define D_P_25 25 /* */
+#define D_P_26 26 /* */
+#define D_P_27 27 /* */
+#define D_P_28 28 /* */
+#define D_P_29 29 /* */
+#define D_P_30 30 /* */
+#define D_P_31 31 /* */
+
+
+/* Transmit descriptor defines */
+#define DBRI_TD_F (1<<31) /* End of Frame */
+#define DBRI_TD_D (1<<31) /* 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 */
+
+/* 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_CNT(v) (v<<16) /* Number of valid bytes in the buffer */
+#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 recieved */
+#define DBRI_RD_ABT (1<<5) /* Abort: frame aborted */
+#define DBRI_RD_OVRN (1<<3) /* Overrun: data lost */
+
+#endif /* _DBRI_H_ */