diff options
author | Takashi Iwai <tiwai@suse.de> | 2005-06-30 18:26:20 +0200 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-07-28 12:21:18 +0200 |
commit | 1bd9debf25b8a5f5029d7619f43e4a9a775973d3 (patch) | |
tree | bb3f77f6216e67e125ac932c8133ddc2331f7c90 /sound | |
parent | ab79509a95b1d22c40d4a87823b6a48bc9a12af5 (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/Kconfig | 12 | ||||
-rw-r--r-- | sound/sparc/Makefile | 4 | ||||
-rw-r--r-- | sound/sparc/dbri.c | 2729 |
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; |