summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2005-06-30 18:26:20 +0200
committerJaroslav Kysela <perex@suse.cz>2005-07-28 12:21:18 +0200
commit1bd9debf25b8a5f5029d7619f43e4a9a775973d3 (patch)
treebb3f77f6216e67e125ac932c8133ddc2331f7c90 /sound
parentab79509a95b1d22c40d4a87823b6a48bc9a12af5 (diff)
[ALSA] Add DBRI driver on Sparcs
Documentation,SPARC,/sparc/Makefile Add the DBRI driver on Sparcs by Martin Habets <mhabets@users.sourceforge.net> (moved from alsa-driver tree). Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/sparc/Kconfig12
-rw-r--r--sound/sparc/Makefile4
-rw-r--r--sound/sparc/dbri.c2729
3 files changed, 2741 insertions, 4 deletions
diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig
index f0fce1daf67d..25a8a558ef92 100644
--- a/sound/sparc/Kconfig
+++ b/sound/sparc/Kconfig
@@ -13,7 +13,6 @@ config SND_SUN_AMD7930
To compile this driver as a module, choose M here: the module
will be called snd-sun-amd7930.
-# dep_tristate 'Sun DBRI' CONFIG_SND_SUN_DBRI $CONFIG_SND
config SND_SUN_CS4231
tristate "Sun CS4231"
depends on SND
@@ -24,5 +23,14 @@ config SND_SUN_CS4231
To compile this driver as a module, choose M here: the module
will be called snd-sun-cs4231.
-endmenu
+config SND_SUN_DBRI
+ tristate "Sun DBRI"
+ depends on SND && SBUS
+ select SND_PCM
+ help
+ Say Y here to include support for DBRI sound device on Sun.
+ To compile this driver as a module, choose M here: the module
+ will be called snd-sun-dbri.
+
+endmenu
diff --git a/sound/sparc/Makefile b/sound/sparc/Makefile
index 6809cc92d276..3cd89c67c2f2 100644
--- a/sound/sparc/Makefile
+++ b/sound/sparc/Makefile
@@ -4,9 +4,9 @@
#
snd-sun-amd7930-objs := amd7930.o
-#snd-sun-dbri-objs := dbri.o
snd-sun-cs4231-objs := cs4231.o
+snd-sun-dbri-objs := dbri.o
obj-$(CONFIG_SND_SUN_AMD7930) += snd-sun-amd7930.o
-#obj-$(CONFIG_SND_SUN_DBRI) += snd-sun-dbri.o
obj-$(CONFIG_SND_SUN_CS4231) += snd-sun-cs4231.o
+obj-$(CONFIG_SND_SUN_DBRI) += snd-sun-dbri.o
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
new file mode 100644
index 000000000000..941c7b1e7ebb
--- /dev/null
+++ b/sound/sparc/dbri.c
@@ -0,0 +1,2729 @@
+/*
+ * Driver for DBRI sound chip found on Sparcs.
+ * Copyright (C) 2004 Martin Habets (mhabets@users.sourceforge.net)
+ *
+ * Based entirely upon drivers/sbus/audio/dbri.c which is:
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
+ *
+ * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
+ * on Sun SPARCstation 10, 20, LX and Voyager models.
+ *
+ * - 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.
+ * - http://www.freesoft.org/Linux/DBRI/
+ * - 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 <sound/driver.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/sbus.h>
+#include <asm/atomic.h>
+
+MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
+MODULE_DESCRIPTION("Sun DBRI");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Sun,DBRI}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Sun DBRI soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Sun DBRI soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard.");
+
+#define DBRI_DEBUG
+
+#define D_INT (1<<0)
+#define D_GEN (1<<1)
+#define D_CMD (1<<2)
+#define D_MM (1<<3)
+#define D_USR (1<<4)
+#define D_DESC (1<<5)
+
+static int dbri_debug = 0;
+module_param(dbri_debug, int, 0444);
+MODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard.");
+
+#ifdef DBRI_DEBUG
+static char *cmds[] = {
+ "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
+ "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
+};
+
+#define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x)
+
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
+ (1 << 27) | \
+ value)
+#else
+#define dprintk(a, x...)
+
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
+ (intr << 27) | \
+ value)
+#endif /* DBRI_DEBUG */
+
+/***************************************************************************
+ CS4215 specific definitions and structures
+****************************************************************************/
+
+struct cs4215 {
+ __u8 data[4]; /* Data mode: Time slots 5-8 */
+ __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */
+ __u8 onboard;
+ __u8 offset; /* Bit offset from frame sync to time slot 1 */
+ volatile __u32 status;
+ volatile __u32 version;
+ __u8 precision; /* In bits, either 8 or 16 */
+ __u8 channels; /* 1 or 2 */
+};
+
+/*
+ * 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 << 4), (0 << 3) },
+ { 16000, (1 << 4), (1 << 3) },
+ { 27429, (1 << 4), (2 << 3) }, /* Actually 24428.57 */
+ { 32000, (1 << 4), (3 << 3) },
+ /* { NA, (1 << 4), (4 << 3) }, */
+ /* { NA, (1 << 4), (5 << 3) }, */
+ { 48000, (1 << 4), (6 << 3) },
+ { 9600, (1 << 4), (7 << 3) },
+ { 5513, (2 << 4), (0 << 3) }, /* Actually 5512.5 */
+ { 11025, (2 << 4), (1 << 3) },
+ { 18900, (2 << 4), (2 << 3) },
+ { 22050, (2 << 4), (3 << 3) },
+ { 37800, (2 << 4), (4 << 3) },
+ { 44100, (2 << 4), (5 << 3) },
+ { 33075, (2 << 4), (6 << 3) },
+ { 6615, (2 << 4), (7 << 3) },
+ { 0, 0, 0}
+};
+
+#define CS4215_HPF (1<<7) /* High Pass Filter, 1: Enabled */
+
+#define CS4215_12_MASK 0xfcbf /* Mask off reserved 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) /* Speaker 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 occurred */
+#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 */
+
+/***************************************************************************
+ DBRI specific definitions and structures
+****************************************************************************/
+
+/* DBRI main registers */
+#define REG0 0x00UL /* Status and Control */
+#define REG1 0x04UL /* Mode and Interrupt */
+#define REG2 0x08UL /* Parallel IO */
+#define REG3 0x0cUL /* Test */
+#define REG8 0x20UL /* Command Queue Pointer */
+#define REG9 0x24UL /* Interrupt Queue Pointer */
+
+#define DBRI_NO_CMDS 64
+#define DBRI_NO_INTS 1 /* Note: the value of this define was
+ * originally 2. The ringbuffer to store
+ * interrupts in dma is currently broken.
+ * This is a temporary fix until the ringbuffer
+ * is fixed.
+ */
+#define DBRI_INT_BLK 64
+#define DBRI_NO_DESCS 64
+#define DBRI_NO_PIPES 32
+
+#define DBRI_MM_ONB 1
+#define DBRI_MM_SB 2
+
+#define DBRI_REC 0
+#define DBRI_PLAY 1
+#define DBRI_NO_STREAMS 2
+
+/* One transmit/receive descriptor */
+struct dbri_mem {
+ volatile __u32 word1;
+ volatile __u32 ba; /* Transmit/Receive Buffer Address */
+ volatile __u32 nda; /* Next Descriptor Address */
+ volatile __u32 word4;
+};
+
+/* This structure is in a DMA region where it can accessed by both
+ * the CPU and the DBRI
+ */
+struct dbri_dma {
+ volatile s32 cmd[DBRI_NO_CMDS]; /* Place for commands */
+ volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */
+ struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */
+};
+
+#define dbri_dma_off(member, elem) \
+ ((u32)(unsigned long) \
+ (&(((struct dbri_dma *)0)->member[elem])))
+
+enum in_or_out { PIPEinput, PIPEoutput };
+
+struct dbri_pipe {
+ u32 sdp; /* SDP command word */
+ enum in_or_out direction;
+ int nextpipe; /* Next pipe in linked list */
+ int prevpipe;
+ int cycle; /* Offset of timeslot (bits) */
+ int length; /* Length of timeslot (bits) */
+ int first_desc; /* Index of first descriptor */
+ int desc; /* Index of active descriptor */
+ volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */
+};
+
+struct dbri_desc {
+ int inuse; /* Boolean flag */
+ int next; /* Index of next desc, or -1 */
+ unsigned int len;
+};
+
+/* Per stream (playback or record) information */
+typedef struct dbri_streaminfo {
+ snd_pcm_substream_t *substream;
+ u32 dvma_buffer; /* Device view of Alsa DMA buffer */
+ int left; /* # of bytes left in DMA buffer */
+ int size; /* Size of DMA buffer */
+ size_t offset; /* offset in user buffer */
+ int pipe; /* Data pipe used */
+ int left_gain; /* mixer elements */
+ int right_gain;
+ int balance;
+} dbri_streaminfo_t;
+
+/* This structure holds the information for both chips (DBRI & CS4215) */
+typedef struct snd_dbri {
+ snd_card_t *card; /* ALSA card */
+ snd_pcm_t *pcm;
+
+ int regs_size, irq; /* Needed for unload */
+ struct sbus_dev *sdev; /* SBUS device info */
+ spinlock_t lock;
+
+ volatile struct dbri_dma *dma; /* Pointer to our DMA block */
+ u32 dma_dvma; /* DBRI visible DMA address */
+
+ void __iomem *regs; /* dbri HW regs */
+ int dbri_version; /* 'e' and up is OK */
+ int dbri_irqp; /* intr queue pointer */
+ int wait_seen;
+
+ struct dbri_pipe pipes[DBRI_NO_PIPES]; /* DBRI's 32 data pipes */
+ struct dbri_desc descs[DBRI_NO_DESCS];
+
+ int chi_in_pipe;
+ int chi_out_pipe;
+ int chi_bpf;
+
+ struct cs4215 mm; /* mmcodec special info */
+ /* per stream (playback/record) info */
+ struct dbri_streaminfo stream_info[DBRI_NO_STREAMS];
+
+ struct snd_dbri *next;
+} snd_dbri_t;
+
+/* Needed for the ALSA macros to work */
+#define chip_t snd_dbri_t
+
+#define DBRI_MAX_VOLUME 63 /* Output volume */
+#define DBRI_MAX_GAIN 15 /* Input gain */
+#define DBRI_RIGHT_BALANCE 255
+#define DBRI_MID_BALANCE (DBRI_RIGHT_BALANCE >> 1)
+
+/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
+#define D_P (1<<15) /* Program command & queue pointer valid */
+#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 */
+
+/* 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_MODE(v) ((v)&(7<<13))
+
+#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 (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) ((v)<<5) /* Remote Loopback */
+#define D_NT_LLB(v) ((v)<<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<<30) /* Do not append CRC */
+#define DBRI_TD_CNT(v) ((v)<<16) /* Number of valid bytes in the buffer */
+#define DBRI_TD_B (1<<15) /* Final interrupt */
+#define DBRI_TD_M (1<<14) /* Marker interrupt */
+#define DBRI_TD_I (1<<13) /* Transmit Idle Characters */
+#define DBRI_TD_FCNT(v) (v) /* Flag Count */
+#define DBRI_TD_UNR (1<<3) /* Underrun: transmitter is out of data */
+#define DBRI_TD_ABT (1<<2) /* Abort: frame aborted */
+#define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */
+#define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */
+ /* Maximum buffer size per TD: almost 8Kb */
+#define DBRI_TD_MAXCNT ((1 << 13) - 1)
+
+/* Receive descriptor defines */
+#define DBRI_RD_F (1<<31) /* End of Frame */
+#define DBRI_RD_C (1<<30) /* Completed buffer */
+#define DBRI_RD_B (1<<15) /* Final interrupt */
+#define DBRI_RD_M (1<<14) /* Marker interrupt */
+#define DBRI_RD_BCNT(v) (v) /* Buffer size */
+#define DBRI_RD_CRC (1<<7) /* 0: CRC is correct */
+#define DBRI_RD_BBC (1<<6) /* 1: Bad Byte received */
+#define DBRI_RD_ABT (1<<5) /* Abort: frame aborted */
+#define DBRI_RD_OVRN (1<<3) /* Overrun: data lost */
+#define DBRI_RD_STATUS(v) ((v)&0xff) /* Receive status */
+#define DBRI_RD_CNT(v) (((v)>>16)&0x1fff) /* Valid bytes in the buffer */
+
+/* stream_info[] access */
+/* Translate the ALSA direction into the array index */
+#define DBRI_STREAMNO(substream) \
+ (substream->stream == \
+ SNDRV_PCM_STREAM_PLAYBACK? DBRI_PLAY: DBRI_REC)
+
+/* Return a pointer to dbri_streaminfo */
+#define DBRI_STREAM(dbri, substream) &dbri->stream_info[DBRI_STREAMNO(substream)]
+
+static snd_dbri_t *dbri_list = NULL; /* All DBRI devices */
+
+/*
+ * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
+ * So we have to reverse the bits. Note: not all bit lengths are supported
+ */
+static __u32 reverse_bytes(__u32 b, int len)
+{
+ switch (len) {
+ case 32:
+ b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
+ case 16:
+ b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8);
+ case 8:
+ b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4);
+ case 4:
+ b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2);
+ case 2:
+ b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
+ case 1:
+ case 0:
+ break;
+ default:
+ printk(KERN_ERR "DBRI reverse_bytes: unsupported length\n");
+ };
+
+ return b;
+}
+
+/*
+****************************************************************************
+************** DBRI initialization and command synchronization *************
+****************************************************************************
+
+Commands are sent to the DBRI by building a list of them in memory,
+then writing the address of the first list item to DBRI register 8.
+The list is terminated with a WAIT command, which can generate a
+CPU interrupt if required.
+
+Since the DBRI can run in parallel with the CPU, several means of
+synchronization present themselves. The original scheme (Rudolf's)
+was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
+an interrupt signaled completion, and wait on a wait_queue if a routine
+attempted to cmdlock while the flag was set. The problems arose when
+we tried to cmdlock from inside an interrupt handler, which might
+cause scheduling in an interrupt (if we waited), etc, etc
+
+A more sophisticated scheme might involve a circular command buffer
+or an array of command buffers. A routine could fill one with
+commands and link it onto a list. When a interrupt signaled
+completion of the current command buffer, look on the list for
+the next one.
+
+I've decided to implement something much simpler - after each command,
+the CPU waits for the DBRI to finish the command by polling the P bit
+in DBRI register 0. I've tried to implement this in such a way
+that might make implementing a more sophisticated scheme easier.
+
+Every time a routine wants to write commands to the DBRI, it must
+first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
+in return. After the commands have been writen, dbri_cmdsend() is
+called with the final pointer value.
+
+*/
+
+enum dbri_lock_t { NoGetLock, GetLock };
+
+static volatile s32 *dbri_cmdlock(snd_dbri_t * dbri, enum dbri_lock_t get)
+{
+#ifndef SMP
+ if ((get == GetLock) && spin_is_locked(&dbri->lock)) {
+ printk(KERN_ERR "DBRI: cmdlock called while in spinlock.");
+ }
+#endif
+
+ /*if (get == GetLock) spin_lock(&dbri->lock); */
+ return &dbri->dma->cmd[0];
+}
+
+static void dbri_process_interrupt_buffer(snd_dbri_t *);
+
+static void dbri_cmdsend(snd_dbri_t * dbri, volatile s32 * cmd)
+{
+ int MAXLOOPS = 1000000;
+ int maxloops = MAXLOOPS;
+ volatile s32 *ptr;
+
+ for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) {
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+ }
+
+ if ((cmd - &dbri->dma->cmd[0]) >= DBRI_NO_CMDS - 1) {
+ printk("DBRI: Command buffer overflow! (bug in driver)\n");
+ /* Ignore the last part. */
+ cmd = &dbri->dma->cmd[DBRI_NO_CMDS - 3];
+ }
+
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+ dbri->wait_seen = 0;
+ sbus_writel(dbri->dma_dvma, dbri->regs + REG8);
+ while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P))
+ barrier();
+ if (maxloops == 0) {
+ printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
+ dprintk(D_CMD, "DBRI: Chip never completed command buffer\n");
+ } else {
+ while ((--maxloops) > 0 && (!dbri->wait_seen))
+ dbri_process_interrupt_buffer(dbri);
+ if (maxloops == 0) {
+ printk(KERN_ERR "DBRI: Chip never acked WAIT\n");
+ dprintk(D_CMD, "DBRI: Chip never acked WAIT\n");
+ } else {
+ dprintk(D_CMD, "Chip completed command "
+ "buffer (%d)\n", MAXLOOPS - maxloops);
+ }
+ }
+
+ /*spin_unlock(&dbri->lock); */
+}
+
+/* Lock must be held when calling this */
+static void dbri_reset(snd_dbri_t * dbri)
+{
+ int i;
+
+ dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n",
+ sbus_readl(dbri->regs + REG0),
+ sbus_readl(dbri->regs + REG2),
+ sbus_readl(dbri->regs + REG8), sbus_readl(dbri->regs + REG9));
+
+ sbus_writel(D_R, dbri->regs + REG0); /* Soft Reset */
+ for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++)
+ udelay(10);
+}
+
+/* Lock must not be held before calling this */
+static void dbri_initialize(snd_dbri_t * dbri)
+{
+ volatile s32 *cmd;
+ u32 dma_addr, tmp;
+ unsigned long flags;
+ int n;
+
+ spin_lock_irqsave(&dbri->lock, flags);
+
+ dbri_reset(dbri);
+
+ cmd = dbri_cmdlock(dbri, NoGetLock);
+ dprintk(D_GEN, "init: cmd: %p, int: %p\n",
+ &dbri->dma->cmd[0], &dbri->dma->intr[0]);
+
+ /*
+ * Initialize the interrupt ringbuffer.
+ */
+ for (n = 0; n < DBRI_NO_INTS - 1; n++) {
+ dma_addr = dbri->dma_dvma;
+ dma_addr += dbri_dma_off(intr, ((n + 1) & DBRI_INT_BLK));
+ dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
+ }
+ dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+ dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
+ dbri->dbri_irqp = 1;
+
+ /* Initialize pipes */
+ for (n = 0; n < DBRI_NO_PIPES; n++)
+ dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
+
+ /* We should query the openprom to see what burst sizes this
+ * SBus supports. For now, just disable all SBus bursts */
+ tmp = sbus_readl(dbri->regs + REG0);
+ tmp &= ~(D_G | D_S | D_E);
+ sbus_writel(tmp, dbri->regs + REG0);
+
+ /*
+ * Set up the interrupt queue
+ */
+ dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+ *(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
+ *(cmd++) = dma_addr;
+
+ dbri_cmdsend(dbri, cmd);
+ spin_unlock_irqrestore(&dbri->lock, flags);
+}
+
+/*
+****************************************************************************
+************************** DBRI data pipe management ***********************
+****************************************************************************
+
+While DBRI control functions use the command and interrupt buffers, the
+main data path takes the form of data pipes, which can be short (command
+and interrupt driven), or long (attached to DMA buffers). These functions
+provide a rudimentary means of setting up and managing the DBRI's pipes,
+but the calling functions have to make sure they respect the pipes' linked
+list ordering, among other things. The transmit and receive functions
+here interface closely with the transmit and receive interrupt code.
+
+*/
+static int pipe_active(snd_dbri_t * dbri, int pipe)
+{
+ return ((pipe >= 0) && (dbri->pipes[pipe].desc != -1));
+}
+
+/* reset_pipe(dbri, pipe)
+ *
+ * Called on an in-use pipe to clear anything being transmitted or received
+ * Lock must be held before calling this.
+ */
+static void reset_pipe(snd_dbri_t * dbri, int pipe)
+{
+ int sdp;
+ int desc;
+ volatile int *cmd;
+
+ if (pipe < 0 || pipe > 31) {
+ printk("DBRI: reset_pipe called with illegal pipe number\n");
+ return;
+ }
+
+ sdp = dbri->pipes[pipe].sdp;
+ if (sdp == 0) {
+ printk("DBRI: reset_pipe called on uninitialized pipe\n");
+ return;
+ }
+
+ cmd = dbri_cmdlock(dbri, NoGetLock);
+ *(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
+ *(cmd++) = 0;
+ dbri_cmdsend(dbri, cmd);
+
+ desc = dbri->pipes[pipe].first_desc;
+ while (desc != -1) {
+ dbri->descs[desc].inuse = 0;
+ desc = dbri->descs[desc].next;
+ }
+
+ dbri->pipes[pipe].desc = -1;
+ dbri->pipes[pipe].first_desc = -1;
+}
+
+/* FIXME: direction as an argument? */
+static void setup_pipe(snd_dbri_t * dbri, int pipe, int sdp)
+{
+ if (pipe < 0 || pipe > 31) {
+ printk("DBRI: setup_pipe called with illegal pipe number\n");
+ return;
+ }
+
+ if ((sdp & 0xf800) != sdp) {
+ printk("DBRI: setup_pipe called with strange SDP value\n");
+ /* sdp &= 0xf800; */
+ }
+
+ /* If this is a fixed receive pipe, arrange for an interrupt
+ * every time its data changes
+ */
+ if (D_SDP_MODE(sdp) == D_SDP_FIXED && !(sdp & D_SDP_TO_SER))
+ sdp |= D_SDP_CHANGE;
+
+ sdp |= D_PIPE(pipe);
+ dbri->pipes[pipe].sdp = sdp;
+ dbri->pipes[pipe].desc = -1;
+ dbri->pipes[pipe].first_desc = -1;
+ if (sdp & D_SDP_TO_SER)
+ dbri->pipes[pipe].direction = PIPEoutput;
+ else
+ dbri->pipes[pipe].direction = PIPEinput;
+
+ reset_pipe(dbri, pipe);
+}
+
+/* FIXME: direction not needed */
+static void link_time_slot(snd_dbri_t * dbri, int pipe,
+ enum in_or_out direction, int basepipe,
+ int length, int cycle)
+{
+ volatile s32 *cmd;
+ int val;
+ int prevpipe;
+ int nextpipe;
+
+ if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
+ printk
+ ("DBRI: link_time_slot called with illegal pipe number\n");
+ return;
+ }
+
+ if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
+ printk("DBRI: link_time_slot called on uninitialized pipe\n");
+ return;
+ }
+
+ /* Deal with CHI special case:
+ * "If transmission on edges 0 or 1 is desired, then cycle n
+ * (where n = # of bit times per frame...) must be used."
+ * - DBRI data sheet, page 11
+ */
+ if (basepipe == 16 && direction == PIPEoutput && cycle == 0)
+ cycle = dbri->chi_bpf;
+
+ if (basepipe == pipe) {
+ prevpipe = pipe;
+ nextpipe = pipe;
+ } else {
+ /* We're not initializing a new linked list (basepipe != pipe),
+ * so run through the linked list and find where this pipe
+ * should be sloted in, based on its cycle. CHI confuses
+ * things a bit, since it has a single anchor for both its
+ * transmit and receive lists.
+ */
+ if (basepipe == 16) {
+ if (direction == PIPEinput) {
+ prevpipe = dbri->chi_in_pipe;
+ } else {
+ prevpipe = dbri->chi_out_pipe;
+ }
+ } else {
+ prevpipe = basepipe;
+ }
+
+ nextpipe = dbri->pipes[prevpipe].nextpipe;
+
+ while (dbri->pipes[nextpipe].cycle < cycle
+ && dbri->pipes[nextpipe].nextpipe != basepipe) {
+ prevpipe = nextpipe;
+ nextpipe = dbri->pipes[nextpipe].nextpipe;
+ }
+ }
+
+ if (prevpipe == 16) {
+ if (direction == PIPEinput) {
+ dbri->chi_in_pipe = pipe;
+ } else {
+ dbri->chi_out_pipe = pipe;
+ }
+ } else {
+ dbri->pipes[prevpipe].nextpipe = pipe;
+ }
+
+ dbri->pipes[pipe].nextpipe = nextpipe;
+ dbri->pipes[pipe].cycle = cycle;
+ dbri->pipes[pipe].length = length;