162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for DBRI sound chip found on Sparcs.
462306a36Sopenharmony_ci * Copyright (C) 2004, 2005 Martin Habets (mhabets@users.sourceforge.net)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Converted to ring buffered version by Krzysztof Helt (krzysztof.h1@wp.pl)
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based entirely upon drivers/sbus/audio/dbri.c which is:
962306a36Sopenharmony_ci * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
1062306a36Sopenharmony_ci * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * This is the low level driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
1362306a36Sopenharmony_ci * on Sun SPARCStation 10, 20, LX and Voyager models.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel
1662306a36Sopenharmony_ci *   data time multiplexer with ISDN support (aka T7259)
1762306a36Sopenharmony_ci *   Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel.
1862306a36Sopenharmony_ci *   CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?).
1962306a36Sopenharmony_ci *   Documentation:
2062306a36Sopenharmony_ci *   - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Transceiver" from
2162306a36Sopenharmony_ci *     Sparc Technology Business (courtesy of Sun Support)
2262306a36Sopenharmony_ci *   - Data sheet of the T7903, a newer but very similar ISA bus equivalent
2362306a36Sopenharmony_ci *     available from the Lucent (formerly AT&T microelectronics) home
2462306a36Sopenharmony_ci *     page.
2562306a36Sopenharmony_ci *   - https://www.freesoft.org/Linux/DBRI/
2662306a36Sopenharmony_ci * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec
2762306a36Sopenharmony_ci *   Interfaces: CHI, Audio In & Out, 2 bits parallel
2862306a36Sopenharmony_ci *   Documentation: from the Crystal Semiconductor home page.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * The DBRI is a 32 pipe machine, each pipe can transfer some bits between
3162306a36Sopenharmony_ci * memory and a serial device (long pipes, no. 0-15) or between two serial
3262306a36Sopenharmony_ci * devices (short pipes, no. 16-31), or simply send a fixed data to a serial
3362306a36Sopenharmony_ci * device (short pipes).
3462306a36Sopenharmony_ci * A timeslot defines the bit-offset and no. of bits read from a serial device.
3562306a36Sopenharmony_ci * The timeslots are linked to 6 circular lists, one for each direction for
3662306a36Sopenharmony_ci * each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes
3762306a36Sopenharmony_ci * (the second one is a monitor/tee pipe, valid only for serial input).
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * The mmcodec is connected via the CHI bus and needs the data & some
4062306a36Sopenharmony_ci * parameters (volume, output selection) time multiplexed in 8 byte
4162306a36Sopenharmony_ci * chunks. It also has a control mode, which serves for audio format setting.
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
4462306a36Sopenharmony_ci * the same CHI bus, so I thought perhaps it is possible to use the on-board
4562306a36Sopenharmony_ci * & the speakerbox codec simultaneously, giving 2 (not very independent :-)
4662306a36Sopenharmony_ci * audio devices. But the SUN HW group decided against it, at least on my
4762306a36Sopenharmony_ci * LX the speakerbox connector has at least 1 pin missing and 1 wrongly
4862306a36Sopenharmony_ci * connected.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * I've tried to stick to the following function naming conventions:
5162306a36Sopenharmony_ci * snd_*	ALSA stuff
5262306a36Sopenharmony_ci * cs4215_*	CS4215 codec specific stuff
5362306a36Sopenharmony_ci * dbri_*	DBRI high-level stuff
5462306a36Sopenharmony_ci * other	DBRI low-level stuff
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#include <linux/interrupt.h>
5862306a36Sopenharmony_ci#include <linux/delay.h>
5962306a36Sopenharmony_ci#include <linux/irq.h>
6062306a36Sopenharmony_ci#include <linux/io.h>
6162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
6262306a36Sopenharmony_ci#include <linux/gfp.h>
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#include <sound/core.h>
6562306a36Sopenharmony_ci#include <sound/pcm.h>
6662306a36Sopenharmony_ci#include <sound/pcm_params.h>
6762306a36Sopenharmony_ci#include <sound/info.h>
6862306a36Sopenharmony_ci#include <sound/control.h>
6962306a36Sopenharmony_ci#include <sound/initval.h>
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#include <linux/of.h>
7262306a36Sopenharmony_ci#include <linux/platform_device.h>
7362306a36Sopenharmony_ci#include <linux/atomic.h>
7462306a36Sopenharmony_ci#include <linux/module.h>
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciMODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
7762306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun DBRI");
7862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
8162306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
8262306a36Sopenharmony_ci/* Enable this card */
8362306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
8662306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Sun DBRI soundcard.");
8762306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
8862306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Sun DBRI soundcard.");
8962306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
9062306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard.");
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#undef DBRI_DEBUG
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define D_INT	(1<<0)
9562306a36Sopenharmony_ci#define D_GEN	(1<<1)
9662306a36Sopenharmony_ci#define D_CMD	(1<<2)
9762306a36Sopenharmony_ci#define D_MM	(1<<3)
9862306a36Sopenharmony_ci#define D_USR	(1<<4)
9962306a36Sopenharmony_ci#define D_DESC	(1<<5)
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int dbri_debug;
10262306a36Sopenharmony_cimodule_param(dbri_debug, int, 0644);
10362306a36Sopenharmony_ciMODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard.");
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#ifdef DBRI_DEBUG
10662306a36Sopenharmony_cistatic const char * const cmds[] = {
10762306a36Sopenharmony_ci	"WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
10862306a36Sopenharmony_ci	"SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define dprintk(a, x...) if (dbri_debug & a) printk(KERN_DEBUG x)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#else
11462306a36Sopenharmony_ci#define dprintk(a, x...) do { } while (0)
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#endif				/* DBRI_DEBUG */
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define DBRI_CMD(cmd, intr, value) ((cmd << 28) |	\
11962306a36Sopenharmony_ci				    (intr << 27) |	\
12062306a36Sopenharmony_ci				    value)
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/***************************************************************************
12362306a36Sopenharmony_ci	CS4215 specific definitions and structures
12462306a36Sopenharmony_ci****************************************************************************/
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistruct cs4215 {
12762306a36Sopenharmony_ci	__u8 data[4];		/* Data mode: Time slots 5-8 */
12862306a36Sopenharmony_ci	__u8 ctrl[4];		/* Ctrl mode: Time slots 1-4 */
12962306a36Sopenharmony_ci	__u8 onboard;
13062306a36Sopenharmony_ci	__u8 offset;		/* Bit offset from frame sync to time slot 1 */
13162306a36Sopenharmony_ci	volatile __u32 status;
13262306a36Sopenharmony_ci	volatile __u32 version;
13362306a36Sopenharmony_ci	__u8 precision;		/* In bits, either 8 or 16 */
13462306a36Sopenharmony_ci	__u8 channels;		/* 1 or 2 */
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*
13862306a36Sopenharmony_ci * Control mode first
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/* Time Slot 1, Status register */
14262306a36Sopenharmony_ci#define CS4215_CLB	(1<<2)	/* Control Latch Bit */
14362306a36Sopenharmony_ci#define CS4215_OLB	(1<<3)	/* 1: line: 2.0V, speaker 4V */
14462306a36Sopenharmony_ci				/* 0: line: 2.8V, speaker 8V */
14562306a36Sopenharmony_ci#define CS4215_MLB	(1<<4)	/* 1: Microphone: 20dB gain disabled */
14662306a36Sopenharmony_ci#define CS4215_RSRVD_1  (1<<5)
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* Time Slot 2, Data Format Register */
14962306a36Sopenharmony_ci#define CS4215_DFR_LINEAR16	0
15062306a36Sopenharmony_ci#define CS4215_DFR_ULAW		1
15162306a36Sopenharmony_ci#define CS4215_DFR_ALAW		2
15262306a36Sopenharmony_ci#define CS4215_DFR_LINEAR8	3
15362306a36Sopenharmony_ci#define CS4215_DFR_STEREO	(1<<2)
15462306a36Sopenharmony_cistatic struct {
15562306a36Sopenharmony_ci	unsigned short freq;
15662306a36Sopenharmony_ci	unsigned char xtal;
15762306a36Sopenharmony_ci	unsigned char csval;
15862306a36Sopenharmony_ci} CS4215_FREQ[] = {
15962306a36Sopenharmony_ci	{  8000, (1 << 4), (0 << 3) },
16062306a36Sopenharmony_ci	{ 16000, (1 << 4), (1 << 3) },
16162306a36Sopenharmony_ci	{ 27429, (1 << 4), (2 << 3) },	/* Actually 24428.57 */
16262306a36Sopenharmony_ci	{ 32000, (1 << 4), (3 << 3) },
16362306a36Sopenharmony_ci     /* {    NA, (1 << 4), (4 << 3) }, */
16462306a36Sopenharmony_ci     /* {    NA, (1 << 4), (5 << 3) }, */
16562306a36Sopenharmony_ci	{ 48000, (1 << 4), (6 << 3) },
16662306a36Sopenharmony_ci	{  9600, (1 << 4), (7 << 3) },
16762306a36Sopenharmony_ci	{  5512, (2 << 4), (0 << 3) },	/* Actually 5512.5 */
16862306a36Sopenharmony_ci	{ 11025, (2 << 4), (1 << 3) },
16962306a36Sopenharmony_ci	{ 18900, (2 << 4), (2 << 3) },
17062306a36Sopenharmony_ci	{ 22050, (2 << 4), (3 << 3) },
17162306a36Sopenharmony_ci	{ 37800, (2 << 4), (4 << 3) },
17262306a36Sopenharmony_ci	{ 44100, (2 << 4), (5 << 3) },
17362306a36Sopenharmony_ci	{ 33075, (2 << 4), (6 << 3) },
17462306a36Sopenharmony_ci	{  6615, (2 << 4), (7 << 3) },
17562306a36Sopenharmony_ci	{ 0, 0, 0}
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci#define CS4215_HPF	(1<<7)	/* High Pass Filter, 1: Enabled */
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci#define CS4215_12_MASK	0xfcbf	/* Mask off reserved bits in slot 1 & 2 */
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Time Slot 3, Serial Port Control register */
18362306a36Sopenharmony_ci#define CS4215_XEN	(1<<0)	/* 0: Enable serial output */
18462306a36Sopenharmony_ci#define CS4215_XCLK	(1<<1)	/* 1: Master mode: Generate SCLK */
18562306a36Sopenharmony_ci#define CS4215_BSEL_64	(0<<2)	/* Bitrate: 64 bits per frame */
18662306a36Sopenharmony_ci#define CS4215_BSEL_128	(1<<2)
18762306a36Sopenharmony_ci#define CS4215_BSEL_256	(2<<2)
18862306a36Sopenharmony_ci#define CS4215_MCK_MAST (0<<4)	/* Master clock */
18962306a36Sopenharmony_ci#define CS4215_MCK_XTL1 (1<<4)	/* 24.576 MHz clock source */
19062306a36Sopenharmony_ci#define CS4215_MCK_XTL2 (2<<4)	/* 16.9344 MHz clock source */
19162306a36Sopenharmony_ci#define CS4215_MCK_CLK1 (3<<4)	/* Clockin, 256 x Fs */
19262306a36Sopenharmony_ci#define CS4215_MCK_CLK2 (4<<4)	/* Clockin, see DFR */
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/* Time Slot 4, Test Register */
19562306a36Sopenharmony_ci#define CS4215_DAD	(1<<0)	/* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */
19662306a36Sopenharmony_ci#define CS4215_ENL	(1<<1)	/* Enable Loopback Testing */
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/* Time Slot 5, Parallel Port Register */
19962306a36Sopenharmony_ci/* Read only here and the same as the in data mode */
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/* Time Slot 6, Reserved  */
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/* Time Slot 7, Version Register  */
20462306a36Sopenharmony_ci#define CS4215_VERSION_MASK 0xf	/* Known versions 0/C, 1/D, 2/E */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* Time Slot 8, Reserved  */
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/*
20962306a36Sopenharmony_ci * Data mode
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_ci/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data  */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/* Time Slot 5, Output Setting  */
21462306a36Sopenharmony_ci#define CS4215_LO(v)	v	/* Left Output Attenuation 0x3f: -94.5 dB */
21562306a36Sopenharmony_ci#define CS4215_LE	(1<<6)	/* Line Out Enable */
21662306a36Sopenharmony_ci#define CS4215_HE	(1<<7)	/* Headphone Enable */
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/* Time Slot 6, Output Setting  */
21962306a36Sopenharmony_ci#define CS4215_RO(v)	v	/* Right Output Attenuation 0x3f: -94.5 dB */
22062306a36Sopenharmony_ci#define CS4215_SE	(1<<6)	/* Speaker Enable */
22162306a36Sopenharmony_ci#define CS4215_ADI	(1<<7)	/* A/D Data Invalid: Busy in calibration */
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/* Time Slot 7, Input Setting */
22462306a36Sopenharmony_ci#define CS4215_LG(v)	v	/* Left Gain Setting 0xf: 22.5 dB */
22562306a36Sopenharmony_ci#define CS4215_IS	(1<<4)	/* Input Select: 1=Microphone, 0=Line */
22662306a36Sopenharmony_ci#define CS4215_OVR	(1<<5)	/* 1: Over range condition occurred */
22762306a36Sopenharmony_ci#define CS4215_PIO0	(1<<6)	/* Parallel I/O 0 */
22862306a36Sopenharmony_ci#define CS4215_PIO1	(1<<7)
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/* Time Slot 8, Input Setting */
23162306a36Sopenharmony_ci#define CS4215_RG(v)	v	/* Right Gain Setting 0xf: 22.5 dB */
23262306a36Sopenharmony_ci#define CS4215_MA(v)	(v<<4)	/* Monitor Path Attenuation 0xf: mute */
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/***************************************************************************
23562306a36Sopenharmony_ci		DBRI specific definitions and structures
23662306a36Sopenharmony_ci****************************************************************************/
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/* DBRI main registers */
23962306a36Sopenharmony_ci#define REG0	0x00		/* Status and Control */
24062306a36Sopenharmony_ci#define REG1	0x04		/* Mode and Interrupt */
24162306a36Sopenharmony_ci#define REG2	0x08		/* Parallel IO */
24262306a36Sopenharmony_ci#define REG3	0x0c		/* Test */
24362306a36Sopenharmony_ci#define REG8	0x20		/* Command Queue Pointer */
24462306a36Sopenharmony_ci#define REG9	0x24		/* Interrupt Queue Pointer */
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci#define DBRI_NO_CMDS	64
24762306a36Sopenharmony_ci#define DBRI_INT_BLK	64
24862306a36Sopenharmony_ci#define DBRI_NO_DESCS	64
24962306a36Sopenharmony_ci#define DBRI_NO_PIPES	32
25062306a36Sopenharmony_ci#define DBRI_MAX_PIPE	(DBRI_NO_PIPES - 1)
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci#define DBRI_REC	0
25362306a36Sopenharmony_ci#define DBRI_PLAY	1
25462306a36Sopenharmony_ci#define DBRI_NO_STREAMS	2
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/* One transmit/receive descriptor */
25762306a36Sopenharmony_ci/* When ba != 0 descriptor is used */
25862306a36Sopenharmony_cistruct dbri_mem {
25962306a36Sopenharmony_ci	volatile __u32 word1;
26062306a36Sopenharmony_ci	__u32 ba;	/* Transmit/Receive Buffer Address */
26162306a36Sopenharmony_ci	__u32 nda;	/* Next Descriptor Address */
26262306a36Sopenharmony_ci	volatile __u32 word4;
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/* This structure is in a DMA region where it can accessed by both
26662306a36Sopenharmony_ci * the CPU and the DBRI
26762306a36Sopenharmony_ci */
26862306a36Sopenharmony_cistruct dbri_dma {
26962306a36Sopenharmony_ci	s32 cmd[DBRI_NO_CMDS];			/* Place for commands */
27062306a36Sopenharmony_ci	volatile s32 intr[DBRI_INT_BLK];	/* Interrupt field  */
27162306a36Sopenharmony_ci	struct dbri_mem desc[DBRI_NO_DESCS];	/* Xmit/receive descriptors */
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci#define dbri_dma_off(member, elem)	\
27562306a36Sopenharmony_ci	((u32)(unsigned long)		\
27662306a36Sopenharmony_ci	 (&(((struct dbri_dma *)0)->member[elem])))
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cienum in_or_out { PIPEinput, PIPEoutput };
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistruct dbri_pipe {
28162306a36Sopenharmony_ci	u32 sdp;		/* SDP command word */
28262306a36Sopenharmony_ci	int nextpipe;		/* Next pipe in linked list */
28362306a36Sopenharmony_ci	int length;		/* Length of timeslot (bits) */
28462306a36Sopenharmony_ci	int first_desc;		/* Index of first descriptor */
28562306a36Sopenharmony_ci	int desc;		/* Index of active descriptor */
28662306a36Sopenharmony_ci	volatile __u32 *recv_fixed_ptr;	/* Ptr to receive fixed data */
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/* Per stream (playback or record) information */
29062306a36Sopenharmony_cistruct dbri_streaminfo {
29162306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
29262306a36Sopenharmony_ci	u32 dvma_buffer;	/* Device view of ALSA DMA buffer */
29362306a36Sopenharmony_ci	int size;		/* Size of DMA buffer             */
29462306a36Sopenharmony_ci	size_t offset;		/* offset in user buffer          */
29562306a36Sopenharmony_ci	int pipe;		/* Data pipe used                 */
29662306a36Sopenharmony_ci	int left_gain;		/* mixer elements                 */
29762306a36Sopenharmony_ci	int right_gain;
29862306a36Sopenharmony_ci};
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/* This structure holds the information for both chips (DBRI & CS4215) */
30162306a36Sopenharmony_cistruct snd_dbri {
30262306a36Sopenharmony_ci	int regs_size, irq;	/* Needed for unload */
30362306a36Sopenharmony_ci	struct platform_device *op;	/* OF device info */
30462306a36Sopenharmony_ci	spinlock_t lock;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	struct dbri_dma *dma;	/* Pointer to our DMA block */
30762306a36Sopenharmony_ci	dma_addr_t dma_dvma;	/* DBRI visible DMA address */
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	void __iomem *regs;	/* dbri HW regs */
31062306a36Sopenharmony_ci	int dbri_irqp;		/* intr queue pointer */
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	struct dbri_pipe pipes[DBRI_NO_PIPES];	/* DBRI's 32 data pipes */
31362306a36Sopenharmony_ci	int next_desc[DBRI_NO_DESCS];		/* Index of next desc, or -1 */
31462306a36Sopenharmony_ci	spinlock_t cmdlock;	/* Protects cmd queue accesses */
31562306a36Sopenharmony_ci	s32 *cmdptr;		/* Pointer to the last queued cmd */
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	int chi_bpf;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	struct cs4215 mm;	/* mmcodec special info */
32062306a36Sopenharmony_ci				/* per stream (playback/record) info */
32162306a36Sopenharmony_ci	struct dbri_streaminfo stream_info[DBRI_NO_STREAMS];
32262306a36Sopenharmony_ci};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci#define DBRI_MAX_VOLUME		63	/* Output volume */
32562306a36Sopenharmony_ci#define DBRI_MAX_GAIN		15	/* Input gain */
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
32862306a36Sopenharmony_ci#define D_P		(1<<15)	/* Program command & queue pointer valid */
32962306a36Sopenharmony_ci#define D_G		(1<<14)	/* Allow 4-Word SBus Burst */
33062306a36Sopenharmony_ci#define D_S		(1<<13)	/* Allow 16-Word SBus Burst */
33162306a36Sopenharmony_ci#define D_E		(1<<12)	/* Allow 8-Word SBus Burst */
33262306a36Sopenharmony_ci#define D_X		(1<<7)	/* Sanity Timer Disable */
33362306a36Sopenharmony_ci#define D_T		(1<<6)	/* Permit activation of the TE interface */
33462306a36Sopenharmony_ci#define D_N		(1<<5)	/* Permit activation of the NT interface */
33562306a36Sopenharmony_ci#define D_C		(1<<4)	/* Permit activation of the CHI interface */
33662306a36Sopenharmony_ci#define D_F		(1<<3)	/* Force Sanity Timer Time-Out */
33762306a36Sopenharmony_ci#define D_D		(1<<2)	/* Disable Master Mode */
33862306a36Sopenharmony_ci#define D_H		(1<<1)	/* Halt for Analysis */
33962306a36Sopenharmony_ci#define D_R		(1<<0)	/* Soft Reset */
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */
34262306a36Sopenharmony_ci#define D_LITTLE_END	(1<<8)	/* Byte Order */
34362306a36Sopenharmony_ci#define D_BIG_END	(0<<8)	/* Byte Order */
34462306a36Sopenharmony_ci#define D_MRR		(1<<4)	/* Multiple Error Ack on SBus (read only) */
34562306a36Sopenharmony_ci#define D_MLE		(1<<3)	/* Multiple Late Error on SBus (read only) */
34662306a36Sopenharmony_ci#define D_LBG		(1<<2)	/* Lost Bus Grant on SBus (read only) */
34762306a36Sopenharmony_ci#define D_MBE		(1<<1)	/* Burst Error on SBus (read only) */
34862306a36Sopenharmony_ci#define D_IR		(1<<0)	/* Interrupt Indicator (read only) */
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */
35162306a36Sopenharmony_ci#define D_ENPIO3	(1<<7)	/* Enable Pin 3 */
35262306a36Sopenharmony_ci#define D_ENPIO2	(1<<6)	/* Enable Pin 2 */
35362306a36Sopenharmony_ci#define D_ENPIO1	(1<<5)	/* Enable Pin 1 */
35462306a36Sopenharmony_ci#define D_ENPIO0	(1<<4)	/* Enable Pin 0 */
35562306a36Sopenharmony_ci#define D_ENPIO		(0xf0)	/* Enable all the pins */
35662306a36Sopenharmony_ci#define D_PIO3		(1<<3)	/* Pin 3: 1: Data mode, 0: Ctrl mode */
35762306a36Sopenharmony_ci#define D_PIO2		(1<<2)	/* Pin 2: 1: Onboard PDN */
35862306a36Sopenharmony_ci#define D_PIO1		(1<<1)	/* Pin 1: 0: Reset */
35962306a36Sopenharmony_ci#define D_PIO0		(1<<0)	/* Pin 0: 1: Speakerbox PDN */
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci/* DBRI Commands (Page 20) */
36262306a36Sopenharmony_ci#define D_WAIT		0x0	/* Stop execution */
36362306a36Sopenharmony_ci#define D_PAUSE		0x1	/* Flush long pipes */
36462306a36Sopenharmony_ci#define D_JUMP		0x2	/* New command queue */
36562306a36Sopenharmony_ci#define D_IIQ		0x3	/* Initialize Interrupt Queue */
36662306a36Sopenharmony_ci#define D_REX		0x4	/* Report command execution via interrupt */
36762306a36Sopenharmony_ci#define D_SDP		0x5	/* Setup Data Pipe */
36862306a36Sopenharmony_ci#define D_CDP		0x6	/* Continue Data Pipe (reread NULL Pointer) */
36962306a36Sopenharmony_ci#define D_DTS		0x7	/* Define Time Slot */
37062306a36Sopenharmony_ci#define D_SSP		0x8	/* Set short Data Pipe */
37162306a36Sopenharmony_ci#define D_CHI		0x9	/* Set CHI Global Mode */
37262306a36Sopenharmony_ci#define D_NT		0xa	/* NT Command */
37362306a36Sopenharmony_ci#define D_TE		0xb	/* TE Command */
37462306a36Sopenharmony_ci#define D_CDEC		0xc	/* Codec setup */
37562306a36Sopenharmony_ci#define D_TEST		0xd	/* No comment */
37662306a36Sopenharmony_ci#define D_CDM		0xe	/* CHI Data mode command */
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/* Special bits for some commands */
37962306a36Sopenharmony_ci#define D_PIPE(v)      ((v)<<0)	/* Pipe No.: 0-15 long, 16-21 short */
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/* Setup Data Pipe */
38262306a36Sopenharmony_ci/* IRM */
38362306a36Sopenharmony_ci#define D_SDP_2SAME	(1<<18)	/* Report 2nd time in a row value received */
38462306a36Sopenharmony_ci#define D_SDP_CHANGE	(2<<18)	/* Report any changes */
38562306a36Sopenharmony_ci#define D_SDP_EVERY	(3<<18)	/* Report any changes */
38662306a36Sopenharmony_ci#define D_SDP_EOL	(1<<17)	/* EOL interrupt enable */
38762306a36Sopenharmony_ci#define D_SDP_IDLE	(1<<16)	/* HDLC idle interrupt enable */
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/* Pipe data MODE */
39062306a36Sopenharmony_ci#define D_SDP_MEM	(0<<13)	/* To/from memory */
39162306a36Sopenharmony_ci#define D_SDP_HDLC	(2<<13)
39262306a36Sopenharmony_ci#define D_SDP_HDLC_D	(3<<13)	/* D Channel (prio control) */
39362306a36Sopenharmony_ci#define D_SDP_SER	(4<<13)	/* Serial to serial */
39462306a36Sopenharmony_ci#define D_SDP_FIXED	(6<<13)	/* Short only */
39562306a36Sopenharmony_ci#define D_SDP_MODE(v)	((v)&(7<<13))
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci#define D_SDP_TO_SER	(1<<12)	/* Direction */
39862306a36Sopenharmony_ci#define D_SDP_FROM_SER	(0<<12)	/* Direction */
39962306a36Sopenharmony_ci#define D_SDP_MSB	(1<<11)	/* Bit order within Byte */
40062306a36Sopenharmony_ci#define D_SDP_LSB	(0<<11)	/* Bit order within Byte */
40162306a36Sopenharmony_ci#define D_SDP_P		(1<<10)	/* Pointer Valid */
40262306a36Sopenharmony_ci#define D_SDP_A		(1<<8)	/* Abort */
40362306a36Sopenharmony_ci#define D_SDP_C		(1<<7)	/* Clear */
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/* Define Time Slot */
40662306a36Sopenharmony_ci#define D_DTS_VI	(1<<17)	/* Valid Input Time-Slot Descriptor */
40762306a36Sopenharmony_ci#define D_DTS_VO	(1<<16)	/* Valid Output Time-Slot Descriptor */
40862306a36Sopenharmony_ci#define D_DTS_INS	(1<<15)	/* Insert Time Slot */
40962306a36Sopenharmony_ci#define D_DTS_DEL	(0<<15)	/* Delete Time Slot */
41062306a36Sopenharmony_ci#define D_DTS_PRVIN(v) ((v)<<10)	/* Previous In Pipe */
41162306a36Sopenharmony_ci#define D_DTS_PRVOUT(v)        ((v)<<5)	/* Previous Out Pipe */
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/* Time Slot defines */
41462306a36Sopenharmony_ci#define D_TS_LEN(v)	((v)<<24)	/* Number of bits in this time slot */
41562306a36Sopenharmony_ci#define D_TS_CYCLE(v)	((v)<<14)	/* Bit Count at start of TS */
41662306a36Sopenharmony_ci#define D_TS_DI		(1<<13)	/* Data Invert */
41762306a36Sopenharmony_ci#define D_TS_1CHANNEL	(0<<10)	/* Single Channel / Normal mode */
41862306a36Sopenharmony_ci#define D_TS_MONITOR	(2<<10)	/* Monitor pipe */
41962306a36Sopenharmony_ci#define D_TS_NONCONTIG	(3<<10)	/* Non contiguous mode */
42062306a36Sopenharmony_ci#define D_TS_ANCHOR	(7<<10)	/* Starting short pipes */
42162306a36Sopenharmony_ci#define D_TS_MON(v)    ((v)<<5)	/* Monitor Pipe */
42262306a36Sopenharmony_ci#define D_TS_NEXT(v)   ((v)<<0)	/* Pipe no.: 0-15 long, 16-21 short */
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci/* Concentration Highway Interface Modes */
42562306a36Sopenharmony_ci#define D_CHI_CHICM(v)	((v)<<16)	/* Clock mode */
42662306a36Sopenharmony_ci#define D_CHI_IR	(1<<15)	/* Immediate Interrupt Report */
42762306a36Sopenharmony_ci#define D_CHI_EN	(1<<14)	/* CHIL Interrupt enabled */
42862306a36Sopenharmony_ci#define D_CHI_OD	(1<<13)	/* Open Drain Enable */
42962306a36Sopenharmony_ci#define D_CHI_FE	(1<<12)	/* Sample CHIFS on Rising Frame Edge */
43062306a36Sopenharmony_ci#define D_CHI_FD	(1<<11)	/* Frame Drive */
43162306a36Sopenharmony_ci#define D_CHI_BPF(v)	((v)<<0)	/* Bits per Frame */
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/* NT: These are here for completeness */
43462306a36Sopenharmony_ci#define D_NT_FBIT	(1<<17)	/* Frame Bit */
43562306a36Sopenharmony_ci#define D_NT_NBF	(1<<16)	/* Number of bad frames to loose framing */
43662306a36Sopenharmony_ci#define D_NT_IRM_IMM	(1<<15)	/* Interrupt Report & Mask: Immediate */
43762306a36Sopenharmony_ci#define D_NT_IRM_EN	(1<<14)	/* Interrupt Report & Mask: Enable */
43862306a36Sopenharmony_ci#define D_NT_ISNT	(1<<13)	/* Configure interface as NT */
43962306a36Sopenharmony_ci#define D_NT_FT		(1<<12)	/* Fixed Timing */
44062306a36Sopenharmony_ci#define D_NT_EZ		(1<<11)	/* Echo Channel is Zeros */
44162306a36Sopenharmony_ci#define D_NT_IFA	(1<<10)	/* Inhibit Final Activation */
44262306a36Sopenharmony_ci#define D_NT_ACT	(1<<9)	/* Activate Interface */
44362306a36Sopenharmony_ci#define D_NT_MFE	(1<<8)	/* Multiframe Enable */
44462306a36Sopenharmony_ci#define D_NT_RLB(v)	((v)<<5)	/* Remote Loopback */
44562306a36Sopenharmony_ci#define D_NT_LLB(v)	((v)<<2)	/* Local Loopback */
44662306a36Sopenharmony_ci#define D_NT_FACT	(1<<1)	/* Force Activation */
44762306a36Sopenharmony_ci#define D_NT_ABV	(1<<0)	/* Activate Bipolar Violation */
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/* Codec Setup */
45062306a36Sopenharmony_ci#define D_CDEC_CK(v)	((v)<<24)	/* Clock Select */
45162306a36Sopenharmony_ci#define D_CDEC_FED(v)	((v)<<12)	/* FSCOD Falling Edge Delay */
45262306a36Sopenharmony_ci#define D_CDEC_RED(v)	((v)<<0)	/* FSCOD Rising Edge Delay */
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/* Test */
45562306a36Sopenharmony_ci#define D_TEST_RAM(v)	((v)<<16)	/* RAM Pointer */
45662306a36Sopenharmony_ci#define D_TEST_SIZE(v)	((v)<<11)	/* */
45762306a36Sopenharmony_ci#define D_TEST_ROMONOFF	0x5	/* Toggle ROM opcode monitor on/off */
45862306a36Sopenharmony_ci#define D_TEST_PROC	0x6	/* Microprocessor test */
45962306a36Sopenharmony_ci#define D_TEST_SER	0x7	/* Serial-Controller test */
46062306a36Sopenharmony_ci#define D_TEST_RAMREAD	0x8	/* Copy from Ram to system memory */
46162306a36Sopenharmony_ci#define D_TEST_RAMWRITE	0x9	/* Copy into Ram from system memory */
46262306a36Sopenharmony_ci#define D_TEST_RAMBIST	0xa	/* RAM Built-In Self Test */
46362306a36Sopenharmony_ci#define D_TEST_MCBIST	0xb	/* Microcontroller Built-In Self Test */
46462306a36Sopenharmony_ci#define D_TEST_DUMP	0xe	/* ROM Dump */
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci/* CHI Data Mode */
46762306a36Sopenharmony_ci#define D_CDM_THI	(1 << 8)	/* Transmit Data on CHIDR Pin */
46862306a36Sopenharmony_ci#define D_CDM_RHI	(1 << 7)	/* Receive Data on CHIDX Pin */
46962306a36Sopenharmony_ci#define D_CDM_RCE	(1 << 6)	/* Receive on Rising Edge of CHICK */
47062306a36Sopenharmony_ci#define D_CDM_XCE	(1 << 2) /* Transmit Data on Rising Edge of CHICK */
47162306a36Sopenharmony_ci#define D_CDM_XEN	(1 << 1)	/* Transmit Highway Enable */
47262306a36Sopenharmony_ci#define D_CDM_REN	(1 << 0)	/* Receive Highway Enable */
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/* The Interrupts */
47562306a36Sopenharmony_ci#define D_INTR_BRDY	1	/* Buffer Ready for processing */
47662306a36Sopenharmony_ci#define D_INTR_MINT	2	/* Marked Interrupt in RD/TD */
47762306a36Sopenharmony_ci#define D_INTR_IBEG	3	/* Flag to idle transition detected (HDLC) */
47862306a36Sopenharmony_ci#define D_INTR_IEND	4	/* Idle to flag transition detected (HDLC) */
47962306a36Sopenharmony_ci#define D_INTR_EOL	5	/* End of List */
48062306a36Sopenharmony_ci#define D_INTR_CMDI	6	/* Command has bean read */
48162306a36Sopenharmony_ci#define D_INTR_XCMP	8	/* Transmission of frame complete */
48262306a36Sopenharmony_ci#define D_INTR_SBRI	9	/* BRI status change info */
48362306a36Sopenharmony_ci#define D_INTR_FXDT	10	/* Fixed data change */
48462306a36Sopenharmony_ci#define D_INTR_CHIL	11	/* CHI lost frame sync (channel 36 only) */
48562306a36Sopenharmony_ci#define D_INTR_COLL	11	/* Unrecoverable D-Channel collision */
48662306a36Sopenharmony_ci#define D_INTR_DBYT	12	/* Dropped by frame slip */
48762306a36Sopenharmony_ci#define D_INTR_RBYT	13	/* Repeated by frame slip */
48862306a36Sopenharmony_ci#define D_INTR_LINT	14	/* Lost Interrupt */
48962306a36Sopenharmony_ci#define D_INTR_UNDR	15	/* DMA underrun */
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci#define D_INTR_TE	32
49262306a36Sopenharmony_ci#define D_INTR_NT	34
49362306a36Sopenharmony_ci#define D_INTR_CHI	36
49462306a36Sopenharmony_ci#define D_INTR_CMD	38
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci#define D_INTR_GETCHAN(v)	(((v) >> 24) & 0x3f)
49762306a36Sopenharmony_ci#define D_INTR_GETCODE(v)	(((v) >> 20) & 0xf)
49862306a36Sopenharmony_ci#define D_INTR_GETCMD(v)	(((v) >> 16) & 0xf)
49962306a36Sopenharmony_ci#define D_INTR_GETVAL(v)	((v) & 0xffff)
50062306a36Sopenharmony_ci#define D_INTR_GETRVAL(v)	((v) & 0xfffff)
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci#define D_P_0		0	/* TE receive anchor */
50362306a36Sopenharmony_ci#define D_P_1		1	/* TE transmit anchor */
50462306a36Sopenharmony_ci#define D_P_2		2	/* NT transmit anchor */
50562306a36Sopenharmony_ci#define D_P_3		3	/* NT receive anchor */
50662306a36Sopenharmony_ci#define D_P_4		4	/* CHI send data */
50762306a36Sopenharmony_ci#define D_P_5		5	/* CHI receive data */
50862306a36Sopenharmony_ci#define D_P_6		6	/* */
50962306a36Sopenharmony_ci#define D_P_7		7	/* */
51062306a36Sopenharmony_ci#define D_P_8		8	/* */
51162306a36Sopenharmony_ci#define D_P_9		9	/* */
51262306a36Sopenharmony_ci#define D_P_10		10	/* */
51362306a36Sopenharmony_ci#define D_P_11		11	/* */
51462306a36Sopenharmony_ci#define D_P_12		12	/* */
51562306a36Sopenharmony_ci#define D_P_13		13	/* */
51662306a36Sopenharmony_ci#define D_P_14		14	/* */
51762306a36Sopenharmony_ci#define D_P_15		15	/* */
51862306a36Sopenharmony_ci#define D_P_16		16	/* CHI anchor pipe */
51962306a36Sopenharmony_ci#define D_P_17		17	/* CHI send */
52062306a36Sopenharmony_ci#define D_P_18		18	/* CHI receive */
52162306a36Sopenharmony_ci#define D_P_19		19	/* CHI receive */
52262306a36Sopenharmony_ci#define D_P_20		20	/* CHI receive */
52362306a36Sopenharmony_ci#define D_P_21		21	/* */
52462306a36Sopenharmony_ci#define D_P_22		22	/* */
52562306a36Sopenharmony_ci#define D_P_23		23	/* */
52662306a36Sopenharmony_ci#define D_P_24		24	/* */
52762306a36Sopenharmony_ci#define D_P_25		25	/* */
52862306a36Sopenharmony_ci#define D_P_26		26	/* */
52962306a36Sopenharmony_ci#define D_P_27		27	/* */
53062306a36Sopenharmony_ci#define D_P_28		28	/* */
53162306a36Sopenharmony_ci#define D_P_29		29	/* */
53262306a36Sopenharmony_ci#define D_P_30		30	/* */
53362306a36Sopenharmony_ci#define D_P_31		31	/* */
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/* Transmit descriptor defines */
53662306a36Sopenharmony_ci#define DBRI_TD_F	(1 << 31)	/* End of Frame */
53762306a36Sopenharmony_ci#define DBRI_TD_D	(1 << 30)	/* Do not append CRC */
53862306a36Sopenharmony_ci#define DBRI_TD_CNT(v)	((v) << 16) /* Number of valid bytes in the buffer */
53962306a36Sopenharmony_ci#define DBRI_TD_B	(1 << 15)	/* Final interrupt */
54062306a36Sopenharmony_ci#define DBRI_TD_M	(1 << 14)	/* Marker interrupt */
54162306a36Sopenharmony_ci#define DBRI_TD_I	(1 << 13)	/* Transmit Idle Characters */
54262306a36Sopenharmony_ci#define DBRI_TD_FCNT(v)	(v)		/* Flag Count */
54362306a36Sopenharmony_ci#define DBRI_TD_UNR	(1 << 3) /* Underrun: transmitter is out of data */
54462306a36Sopenharmony_ci#define DBRI_TD_ABT	(1 << 2)	/* Abort: frame aborted */
54562306a36Sopenharmony_ci#define DBRI_TD_TBC	(1 << 0)	/* Transmit buffer Complete */
54662306a36Sopenharmony_ci#define DBRI_TD_STATUS(v)       ((v) & 0xff)	/* Transmit status */
54762306a36Sopenharmony_ci			/* Maximum buffer size per TD: almost 8KB */
54862306a36Sopenharmony_ci#define DBRI_TD_MAXCNT	((1 << 13) - 4)
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/* Receive descriptor defines */
55162306a36Sopenharmony_ci#define DBRI_RD_F	(1 << 31)	/* End of Frame */
55262306a36Sopenharmony_ci#define DBRI_RD_C	(1 << 30)	/* Completed buffer */
55362306a36Sopenharmony_ci#define DBRI_RD_B	(1 << 15)	/* Final interrupt */
55462306a36Sopenharmony_ci#define DBRI_RD_M	(1 << 14)	/* Marker interrupt */
55562306a36Sopenharmony_ci#define DBRI_RD_BCNT(v)	(v)		/* Buffer size */
55662306a36Sopenharmony_ci#define DBRI_RD_CRC	(1 << 7)	/* 0: CRC is correct */
55762306a36Sopenharmony_ci#define DBRI_RD_BBC	(1 << 6)	/* 1: Bad Byte received */
55862306a36Sopenharmony_ci#define DBRI_RD_ABT	(1 << 5)	/* Abort: frame aborted */
55962306a36Sopenharmony_ci#define DBRI_RD_OVRN	(1 << 3)	/* Overrun: data lost */
56062306a36Sopenharmony_ci#define DBRI_RD_STATUS(v)      ((v) & 0xff)	/* Receive status */
56162306a36Sopenharmony_ci#define DBRI_RD_CNT(v) (((v) >> 16) & 0x1fff)	/* Valid bytes in the buffer */
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* stream_info[] access */
56462306a36Sopenharmony_ci/* Translate the ALSA direction into the array index */
56562306a36Sopenharmony_ci#define DBRI_STREAMNO(substream)				\
56662306a36Sopenharmony_ci		(substream->stream ==				\
56762306a36Sopenharmony_ci		 SNDRV_PCM_STREAM_PLAYBACK ? DBRI_PLAY: DBRI_REC)
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/* Return a pointer to dbri_streaminfo */
57062306a36Sopenharmony_ci#define DBRI_STREAM(dbri, substream)	\
57162306a36Sopenharmony_ci		&dbri->stream_info[DBRI_STREAMNO(substream)]
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/*
57462306a36Sopenharmony_ci * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
57562306a36Sopenharmony_ci * So we have to reverse the bits. Note: not all bit lengths are supported
57662306a36Sopenharmony_ci */
57762306a36Sopenharmony_cistatic __u32 reverse_bytes(__u32 b, int len)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	switch (len) {
58062306a36Sopenharmony_ci	case 32:
58162306a36Sopenharmony_ci		b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
58262306a36Sopenharmony_ci		fallthrough;
58362306a36Sopenharmony_ci	case 16:
58462306a36Sopenharmony_ci		b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8);
58562306a36Sopenharmony_ci		fallthrough;
58662306a36Sopenharmony_ci	case 8:
58762306a36Sopenharmony_ci		b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4);
58862306a36Sopenharmony_ci		fallthrough;
58962306a36Sopenharmony_ci	case 4:
59062306a36Sopenharmony_ci		b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2);
59162306a36Sopenharmony_ci		fallthrough;
59262306a36Sopenharmony_ci	case 2:
59362306a36Sopenharmony_ci		b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
59462306a36Sopenharmony_ci	case 1:
59562306a36Sopenharmony_ci	case 0:
59662306a36Sopenharmony_ci		break;
59762306a36Sopenharmony_ci	default:
59862306a36Sopenharmony_ci		printk(KERN_ERR "DBRI reverse_bytes: unsupported length\n");
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return b;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/*
60562306a36Sopenharmony_ci****************************************************************************
60662306a36Sopenharmony_ci************** DBRI initialization and command synchronization *************
60762306a36Sopenharmony_ci****************************************************************************
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ciCommands are sent to the DBRI by building a list of them in memory,
61062306a36Sopenharmony_cithen writing the address of the first list item to DBRI register 8.
61162306a36Sopenharmony_ciThe list is terminated with a WAIT command, which generates a
61262306a36Sopenharmony_ciCPU interrupt to signal completion.
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ciSince the DBRI can run in parallel with the CPU, several means of
61562306a36Sopenharmony_cisynchronization present themselves. The method implemented here uses
61662306a36Sopenharmony_cithe dbri_cmdwait() to wait for execution of batch of sent commands.
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ciA circular command buffer is used here. A new command is being added
61962306a36Sopenharmony_ciwhile another can be executed. The scheme works by adding two WAIT commands
62062306a36Sopenharmony_ciafter each sent batch of commands. When the next batch is prepared it is
62162306a36Sopenharmony_ciadded after the WAIT commands then the WAITs are replaced with single JUMP
62262306a36Sopenharmony_cicommand to the new batch. Then the DBRI is forced to reread the last WAIT
62362306a36Sopenharmony_cicommand (replaced by the JUMP by then). If the DBRI is still executing
62462306a36Sopenharmony_ciprevious commands the request to reread the WAIT command is ignored.
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ciEvery time a routine wants to write commands to the DBRI, it must
62762306a36Sopenharmony_cifirst call dbri_cmdlock() and get pointer to a free space in
62862306a36Sopenharmony_cidbri->dma->cmd buffer. After this, the commands can be written to
62962306a36Sopenharmony_cithe buffer, and dbri_cmdsend() is called with the final pointer value
63062306a36Sopenharmony_cito send them to the DBRI.
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci*/
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci#define MAXLOOPS 20
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * Wait for the current command string to execute
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_cistatic void dbri_cmdwait(struct snd_dbri *dbri)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	int maxloops = MAXLOOPS;
64162306a36Sopenharmony_ci	unsigned long flags;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* Delay if previous commands are still being processed */
64462306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
64562306a36Sopenharmony_ci	while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P)) {
64662306a36Sopenharmony_ci		spin_unlock_irqrestore(&dbri->lock, flags);
64762306a36Sopenharmony_ci		msleep_interruptible(1);
64862306a36Sopenharmony_ci		spin_lock_irqsave(&dbri->lock, flags);
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (maxloops == 0)
65362306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
65462306a36Sopenharmony_ci	else
65562306a36Sopenharmony_ci		dprintk(D_CMD, "Chip completed command buffer (%d)\n",
65662306a36Sopenharmony_ci			MAXLOOPS - maxloops - 1);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci/*
65962306a36Sopenharmony_ci * Lock the command queue and return pointer to space for len cmd words
66062306a36Sopenharmony_ci * It locks the cmdlock spinlock.
66162306a36Sopenharmony_ci */
66262306a36Sopenharmony_cistatic s32 *dbri_cmdlock(struct snd_dbri *dbri, int len)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */
66762306a36Sopenharmony_ci	len += 2;
66862306a36Sopenharmony_ci	spin_lock(&dbri->cmdlock);
66962306a36Sopenharmony_ci	if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2)
67062306a36Sopenharmony_ci		return dbri->cmdptr + 2;
67162306a36Sopenharmony_ci	else if (len < sbus_readl(dbri->regs + REG8) - dvma_addr)
67262306a36Sopenharmony_ci		return dbri->dma->cmd;
67362306a36Sopenharmony_ci	else
67462306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: no space for commands.");
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	return NULL;
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/*
68062306a36Sopenharmony_ci * Send prepared cmd string. It works by writing a JUMP cmd into
68162306a36Sopenharmony_ci * the last WAIT cmd and force DBRI to reread the cmd.
68262306a36Sopenharmony_ci * The JUMP cmd points to the new cmd string.
68362306a36Sopenharmony_ci * It also releases the cmdlock spinlock.
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * Lock must be held before calling this.
68662306a36Sopenharmony_ci */
68762306a36Sopenharmony_cistatic void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
69062306a36Sopenharmony_ci	s32 tmp, addr;
69162306a36Sopenharmony_ci	static int wait_id;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	wait_id++;
69462306a36Sopenharmony_ci	wait_id &= 0xffff;	/* restrict it to a 16 bit counter. */
69562306a36Sopenharmony_ci	*(cmd) = DBRI_CMD(D_WAIT, 1, wait_id);
69662306a36Sopenharmony_ci	*(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* Replace the last command with JUMP */
69962306a36Sopenharmony_ci	addr = dvma_addr + (cmd - len - dbri->dma->cmd) * sizeof(s32);
70062306a36Sopenharmony_ci	*(dbri->cmdptr+1) = addr;
70162306a36Sopenharmony_ci	*(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci#ifdef DBRI_DEBUG
70462306a36Sopenharmony_ci	if (cmd > dbri->cmdptr) {
70562306a36Sopenharmony_ci		s32 *ptr;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++)
70862306a36Sopenharmony_ci			dprintk(D_CMD, "cmd: %lx:%08x\n",
70962306a36Sopenharmony_ci				(unsigned long)ptr, *ptr);
71062306a36Sopenharmony_ci	} else {
71162306a36Sopenharmony_ci		s32 *ptr = dbri->cmdptr;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
71462306a36Sopenharmony_ci		ptr++;
71562306a36Sopenharmony_ci		dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
71662306a36Sopenharmony_ci		for (ptr = dbri->dma->cmd; ptr < cmd+2; ptr++)
71762306a36Sopenharmony_ci			dprintk(D_CMD, "cmd: %lx:%08x\n",
71862306a36Sopenharmony_ci				(unsigned long)ptr, *ptr);
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci#endif
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Reread the last command */
72362306a36Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
72462306a36Sopenharmony_ci	tmp |= D_P;
72562306a36Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	dbri->cmdptr = cmd;
72862306a36Sopenharmony_ci	spin_unlock(&dbri->cmdlock);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/* Lock must be held when calling this */
73262306a36Sopenharmony_cistatic void dbri_reset(struct snd_dbri *dbri)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	int i;
73562306a36Sopenharmony_ci	u32 tmp;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n",
73862306a36Sopenharmony_ci		sbus_readl(dbri->regs + REG0),
73962306a36Sopenharmony_ci		sbus_readl(dbri->regs + REG2),
74062306a36Sopenharmony_ci		sbus_readl(dbri->regs + REG8), sbus_readl(dbri->regs + REG9));
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	sbus_writel(D_R, dbri->regs + REG0);	/* Soft Reset */
74362306a36Sopenharmony_ci	for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++)
74462306a36Sopenharmony_ci		udelay(10);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* A brute approach - DBRI falls back to working burst size by itself
74762306a36Sopenharmony_ci	 * On SS20 D_S does not work, so do not try so high. */
74862306a36Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
74962306a36Sopenharmony_ci	tmp |= D_G | D_E;
75062306a36Sopenharmony_ci	tmp &= ~D_S;
75162306a36Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci/* Lock must not be held before calling this */
75562306a36Sopenharmony_cistatic void dbri_initialize(struct snd_dbri *dbri)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
75862306a36Sopenharmony_ci	s32 *cmd;
75962306a36Sopenharmony_ci	u32 dma_addr;
76062306a36Sopenharmony_ci	unsigned long flags;
76162306a36Sopenharmony_ci	int n;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	dbri_reset(dbri);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* Initialize pipes */
76862306a36Sopenharmony_ci	for (n = 0; n < DBRI_NO_PIPES; n++)
76962306a36Sopenharmony_ci		dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	spin_lock_init(&dbri->cmdlock);
77262306a36Sopenharmony_ci	/*
77362306a36Sopenharmony_ci	 * Initialize the interrupt ring buffer.
77462306a36Sopenharmony_ci	 */
77562306a36Sopenharmony_ci	dma_addr = dvma_addr + dbri_dma_off(intr, 0);
77662306a36Sopenharmony_ci	dbri->dma->intr[0] = dma_addr;
77762306a36Sopenharmony_ci	dbri->dbri_irqp = 1;
77862306a36Sopenharmony_ci	/*
77962306a36Sopenharmony_ci	 * Set up the interrupt queue
78062306a36Sopenharmony_ci	 */
78162306a36Sopenharmony_ci	spin_lock(&dbri->cmdlock);
78262306a36Sopenharmony_ci	cmd = dbri->cmdptr = dbri->dma->cmd;
78362306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
78462306a36Sopenharmony_ci	*(cmd++) = dma_addr;
78562306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
78662306a36Sopenharmony_ci	dbri->cmdptr = cmd;
78762306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
78862306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
78962306a36Sopenharmony_ci	dma_addr = dvma_addr + dbri_dma_off(cmd, 0);
79062306a36Sopenharmony_ci	sbus_writel(dma_addr, dbri->regs + REG8);
79162306a36Sopenharmony_ci	spin_unlock(&dbri->cmdlock);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
79462306a36Sopenharmony_ci	dbri_cmdwait(dbri);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci/*
79862306a36Sopenharmony_ci****************************************************************************
79962306a36Sopenharmony_ci************************** DBRI data pipe management ***********************
80062306a36Sopenharmony_ci****************************************************************************
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ciWhile DBRI control functions use the command and interrupt buffers, the
80362306a36Sopenharmony_cimain data path takes the form of data pipes, which can be short (command
80462306a36Sopenharmony_ciand interrupt driven), or long (attached to DMA buffers).  These functions
80562306a36Sopenharmony_ciprovide a rudimentary means of setting up and managing the DBRI's pipes,
80662306a36Sopenharmony_cibut the calling functions have to make sure they respect the pipes' linked
80762306a36Sopenharmony_cilist ordering, among other things.  The transmit and receive functions
80862306a36Sopenharmony_cihere interface closely with the transmit and receive interrupt code.
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci*/
81162306a36Sopenharmony_cistatic inline int pipe_active(struct snd_dbri *dbri, int pipe)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	return ((pipe >= 0) && (dbri->pipes[pipe].desc != -1));
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci/* reset_pipe(dbri, pipe)
81762306a36Sopenharmony_ci *
81862306a36Sopenharmony_ci * Called on an in-use pipe to clear anything being transmitted or received
81962306a36Sopenharmony_ci * Lock must be held before calling this.
82062306a36Sopenharmony_ci */
82162306a36Sopenharmony_cistatic void reset_pipe(struct snd_dbri *dbri, int pipe)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	int sdp;
82462306a36Sopenharmony_ci	int desc;
82562306a36Sopenharmony_ci	s32 *cmd;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
82862306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: reset_pipe called with "
82962306a36Sopenharmony_ci			"illegal pipe number\n");
83062306a36Sopenharmony_ci		return;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	sdp = dbri->pipes[pipe].sdp;
83462306a36Sopenharmony_ci	if (sdp == 0) {
83562306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: reset_pipe called "
83662306a36Sopenharmony_ci			"on uninitialized pipe\n");
83762306a36Sopenharmony_ci		return;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 3);
84162306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
84262306a36Sopenharmony_ci	*(cmd++) = 0;
84362306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
84462306a36Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 3);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	desc = dbri->pipes[pipe].first_desc;
84762306a36Sopenharmony_ci	if (desc >= 0)
84862306a36Sopenharmony_ci		do {
84962306a36Sopenharmony_ci			dbri->dma->desc[desc].ba = 0;
85062306a36Sopenharmony_ci			dbri->dma->desc[desc].nda = 0;
85162306a36Sopenharmony_ci			desc = dbri->next_desc[desc];
85262306a36Sopenharmony_ci		} while (desc != -1 && desc != dbri->pipes[pipe].first_desc);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	dbri->pipes[pipe].desc = -1;
85562306a36Sopenharmony_ci	dbri->pipes[pipe].first_desc = -1;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci/*
85962306a36Sopenharmony_ci * Lock must be held before calling this.
86062306a36Sopenharmony_ci */
86162306a36Sopenharmony_cistatic void setup_pipe(struct snd_dbri *dbri, int pipe, int sdp)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
86462306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_pipe called "
86562306a36Sopenharmony_ci			"with illegal pipe number\n");
86662306a36Sopenharmony_ci		return;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if ((sdp & 0xf800) != sdp) {
87062306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_pipe called "
87162306a36Sopenharmony_ci			"with strange SDP value\n");
87262306a36Sopenharmony_ci		/* sdp &= 0xf800; */
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	/* If this is a fixed receive pipe, arrange for an interrupt
87662306a36Sopenharmony_ci	 * every time its data changes
87762306a36Sopenharmony_ci	 */
87862306a36Sopenharmony_ci	if (D_SDP_MODE(sdp) == D_SDP_FIXED && !(sdp & D_SDP_TO_SER))
87962306a36Sopenharmony_ci		sdp |= D_SDP_CHANGE;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	sdp |= D_PIPE(pipe);
88262306a36Sopenharmony_ci	dbri->pipes[pipe].sdp = sdp;
88362306a36Sopenharmony_ci	dbri->pipes[pipe].desc = -1;
88462306a36Sopenharmony_ci	dbri->pipes[pipe].first_desc = -1;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	reset_pipe(dbri, pipe);
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci/*
89062306a36Sopenharmony_ci * Lock must be held before calling this.
89162306a36Sopenharmony_ci */
89262306a36Sopenharmony_cistatic void link_time_slot(struct snd_dbri *dbri, int pipe,
89362306a36Sopenharmony_ci			   int prevpipe, int nextpipe,
89462306a36Sopenharmony_ci			   int length, int cycle)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	s32 *cmd;
89762306a36Sopenharmony_ci	int val;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE
90062306a36Sopenharmony_ci			|| prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
90162306a36Sopenharmony_ci			|| nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
90262306a36Sopenharmony_ci		printk(KERN_ERR
90362306a36Sopenharmony_ci		    "DBRI: link_time_slot called with illegal pipe number\n");
90462306a36Sopenharmony_ci		return;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (dbri->pipes[pipe].sdp == 0
90862306a36Sopenharmony_ci			|| dbri->pipes[prevpipe].sdp == 0
90962306a36Sopenharmony_ci			|| dbri->pipes[nextpipe].sdp == 0) {
91062306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: link_time_slot called "
91162306a36Sopenharmony_ci			"on uninitialized pipe\n");
91262306a36Sopenharmony_ci		return;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	dbri->pipes[prevpipe].nextpipe = pipe;
91662306a36Sopenharmony_ci	dbri->pipes[pipe].nextpipe = nextpipe;
91762306a36Sopenharmony_ci	dbri->pipes[pipe].length = length;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
92262306a36Sopenharmony_ci		/* Deal with CHI special case:
92362306a36Sopenharmony_ci		 * "If transmission on edges 0 or 1 is desired, then cycle n
92462306a36Sopenharmony_ci		 *  (where n = # of bit times per frame...) must be used."
92562306a36Sopenharmony_ci		 *                  - DBRI data sheet, page 11
92662306a36Sopenharmony_ci		 */
92762306a36Sopenharmony_ci		if (prevpipe == 16 && cycle == 0)
92862306a36Sopenharmony_ci			cycle = dbri->chi_bpf;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
93162306a36Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
93262306a36Sopenharmony_ci		*(cmd++) = 0;
93362306a36Sopenharmony_ci		*(cmd++) =
93462306a36Sopenharmony_ci		    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
93562306a36Sopenharmony_ci	} else {
93662306a36Sopenharmony_ci		val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
93762306a36Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
93862306a36Sopenharmony_ci		*(cmd++) =
93962306a36Sopenharmony_ci		    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
94062306a36Sopenharmony_ci		*(cmd++) = 0;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci#if 0
94862306a36Sopenharmony_ci/*
94962306a36Sopenharmony_ci * Lock must be held before calling this.
95062306a36Sopenharmony_ci */
95162306a36Sopenharmony_cistatic void unlink_time_slot(struct snd_dbri *dbri, int pipe,
95262306a36Sopenharmony_ci			     enum in_or_out direction, int prevpipe,
95362306a36Sopenharmony_ci			     int nextpipe)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	s32 *cmd;
95662306a36Sopenharmony_ci	int val;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE
95962306a36Sopenharmony_ci			|| prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
96062306a36Sopenharmony_ci			|| nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
96162306a36Sopenharmony_ci		printk(KERN_ERR
96262306a36Sopenharmony_ci		    "DBRI: unlink_time_slot called with illegal pipe number\n");
96362306a36Sopenharmony_ci		return;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (direction == PIPEinput) {
96962306a36Sopenharmony_ci		val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
97062306a36Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
97162306a36Sopenharmony_ci		*(cmd++) = D_TS_NEXT(nextpipe);
97262306a36Sopenharmony_ci		*(cmd++) = 0;
97362306a36Sopenharmony_ci	} else {
97462306a36Sopenharmony_ci		val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe;
97562306a36Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
97662306a36Sopenharmony_ci		*(cmd++) = 0;
97762306a36Sopenharmony_ci		*(cmd++) = D_TS_NEXT(nextpipe);
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci#endif
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci/* xmit_fixed() / recv_fixed()
98662306a36Sopenharmony_ci *
98762306a36Sopenharmony_ci * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not
98862306a36Sopenharmony_ci * expected to change much, and which we don't need to buffer.
98962306a36Sopenharmony_ci * The DBRI only interrupts us when the data changes (receive pipes),
99062306a36Sopenharmony_ci * or only changes the data when this function is called (transmit pipes).
99162306a36Sopenharmony_ci * Only short pipes (numbers 16-31) can be used in fixed data mode.
99262306a36Sopenharmony_ci *
99362306a36Sopenharmony_ci * These function operate on a 32-bit field, no matter how large
99462306a36Sopenharmony_ci * the actual time slot is.  The interrupt handler takes care of bit
99562306a36Sopenharmony_ci * ordering and alignment.  An 8-bit time slot will always end up
99662306a36Sopenharmony_ci * in the low-order 8 bits, filled either MSB-first or LSB-first,
99762306a36Sopenharmony_ci * depending on the settings passed to setup_pipe().
99862306a36Sopenharmony_ci *
99962306a36Sopenharmony_ci * Lock must not be held before calling it.
100062306a36Sopenharmony_ci */
100162306a36Sopenharmony_cistatic void xmit_fixed(struct snd_dbri *dbri, int pipe, unsigned int data)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	s32 *cmd;
100462306a36Sopenharmony_ci	unsigned long flags;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
100762306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n");
100862306a36Sopenharmony_ci		return;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
101262306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: "
101362306a36Sopenharmony_ci			"Uninitialized pipe %d\n", pipe);
101462306a36Sopenharmony_ci		return;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
101862306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
101962306a36Sopenharmony_ci		return;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (!(dbri->pipes[pipe].sdp & D_SDP_TO_SER)) {
102362306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: Called on receive pipe %d\n",
102462306a36Sopenharmony_ci			pipe);
102562306a36Sopenharmony_ci		return;
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* DBRI short pipes always transmit LSB first */
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (dbri->pipes[pipe].sdp & D_SDP_MSB)
103162306a36Sopenharmony_ci		data = reverse_bytes(data, dbri->pipes[pipe].length);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 3);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_SSP, 0, pipe);
103662306a36Sopenharmony_ci	*(cmd++) = data;
103762306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
104062306a36Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 3);
104162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
104262306a36Sopenharmony_ci	dbri_cmdwait(dbri);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic void recv_fixed(struct snd_dbri *dbri, int pipe, volatile __u32 *ptr)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
104962306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: recv_fixed called with "
105062306a36Sopenharmony_ci			"illegal pipe number\n");
105162306a36Sopenharmony_ci		return;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
105562306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: recv_fixed called on "
105662306a36Sopenharmony_ci			"non-fixed pipe %d\n", pipe);
105762306a36Sopenharmony_ci		return;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
106162306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: recv_fixed called on "
106262306a36Sopenharmony_ci			"transmit pipe %d\n", pipe);
106362306a36Sopenharmony_ci		return;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	dbri->pipes[pipe].recv_fixed_ptr = ptr;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci/* setup_descs()
107062306a36Sopenharmony_ci *
107162306a36Sopenharmony_ci * Setup transmit/receive data on a "long" pipe - i.e, one associated
107262306a36Sopenharmony_ci * with a DMA buffer.
107362306a36Sopenharmony_ci *
107462306a36Sopenharmony_ci * Only pipe numbers 0-15 can be used in this mode.
107562306a36Sopenharmony_ci *
107662306a36Sopenharmony_ci * This function takes a stream number pointing to a data buffer,
107762306a36Sopenharmony_ci * and work by building chains of descriptors which identify the
107862306a36Sopenharmony_ci * data buffers.  Buffers too large for a single descriptor will
107962306a36Sopenharmony_ci * be spread across multiple descriptors.
108062306a36Sopenharmony_ci *
108162306a36Sopenharmony_ci * All descriptors create a ring buffer.
108262306a36Sopenharmony_ci *
108362306a36Sopenharmony_ci * Lock must be held before calling this.
108462306a36Sopenharmony_ci */
108562306a36Sopenharmony_cistatic int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct dbri_streaminfo *info = &dbri->stream_info[streamno];
108862306a36Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
108962306a36Sopenharmony_ci	__u32 dvma_buffer;
109062306a36Sopenharmony_ci	int desc;
109162306a36Sopenharmony_ci	int len;
109262306a36Sopenharmony_ci	int first_desc = -1;
109362306a36Sopenharmony_ci	int last_desc = -1;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (info->pipe < 0 || info->pipe > 15) {
109662306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_descs: Illegal pipe number\n");
109762306a36Sopenharmony_ci		return -2;
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	if (dbri->pipes[info->pipe].sdp == 0) {
110162306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_descs: Uninitialized pipe %d\n",
110262306a36Sopenharmony_ci		       info->pipe);
110362306a36Sopenharmony_ci		return -2;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	dvma_buffer = info->dvma_buffer;
110762306a36Sopenharmony_ci	len = info->size;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	if (streamno == DBRI_PLAY) {
111062306a36Sopenharmony_ci		if (!(dbri->pipes[info->pipe].sdp & D_SDP_TO_SER)) {
111162306a36Sopenharmony_ci			printk(KERN_ERR "DBRI: setup_descs: "
111262306a36Sopenharmony_ci				"Called on receive pipe %d\n", info->pipe);
111362306a36Sopenharmony_ci			return -2;
111462306a36Sopenharmony_ci		}
111562306a36Sopenharmony_ci	} else {
111662306a36Sopenharmony_ci		if (dbri->pipes[info->pipe].sdp & D_SDP_TO_SER) {
111762306a36Sopenharmony_ci			printk(KERN_ERR
111862306a36Sopenharmony_ci			    "DBRI: setup_descs: Called on transmit pipe %d\n",
111962306a36Sopenharmony_ci			     info->pipe);
112062306a36Sopenharmony_ci			return -2;
112162306a36Sopenharmony_ci		}
112262306a36Sopenharmony_ci		/* Should be able to queue multiple buffers
112362306a36Sopenharmony_ci		 * to receive on a pipe
112462306a36Sopenharmony_ci		 */
112562306a36Sopenharmony_ci		if (pipe_active(dbri, info->pipe)) {
112662306a36Sopenharmony_ci			printk(KERN_ERR "DBRI: recv_on_pipe: "
112762306a36Sopenharmony_ci				"Called on active pipe %d\n", info->pipe);
112862306a36Sopenharmony_ci			return -2;
112962306a36Sopenharmony_ci		}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci		/* Make sure buffer size is multiple of four */
113262306a36Sopenharmony_ci		len &= ~3;
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	/* Free descriptors if pipe has any */
113662306a36Sopenharmony_ci	desc = dbri->pipes[info->pipe].first_desc;
113762306a36Sopenharmony_ci	if (desc >= 0)
113862306a36Sopenharmony_ci		do {
113962306a36Sopenharmony_ci			dbri->dma->desc[desc].ba = 0;
114062306a36Sopenharmony_ci			dbri->dma->desc[desc].nda = 0;
114162306a36Sopenharmony_ci			desc = dbri->next_desc[desc];
114262306a36Sopenharmony_ci		} while (desc != -1 &&
114362306a36Sopenharmony_ci			 desc != dbri->pipes[info->pipe].first_desc);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	dbri->pipes[info->pipe].desc = -1;
114662306a36Sopenharmony_ci	dbri->pipes[info->pipe].first_desc = -1;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	desc = 0;
114962306a36Sopenharmony_ci	while (len > 0) {
115062306a36Sopenharmony_ci		int mylen;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci		for (; desc < DBRI_NO_DESCS; desc++) {
115362306a36Sopenharmony_ci			if (!dbri->dma->desc[desc].ba)
115462306a36Sopenharmony_ci				break;
115562306a36Sopenharmony_ci		}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		if (desc == DBRI_NO_DESCS) {
115862306a36Sopenharmony_ci			printk(KERN_ERR "DBRI: setup_descs: No descriptors\n");
115962306a36Sopenharmony_ci			return -1;
116062306a36Sopenharmony_ci		}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		if (len > DBRI_TD_MAXCNT)
116362306a36Sopenharmony_ci			mylen = DBRI_TD_MAXCNT;	/* 8KB - 4 */
116462306a36Sopenharmony_ci		else
116562306a36Sopenharmony_ci			mylen = len;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci		if (mylen > period)
116862306a36Sopenharmony_ci			mylen = period;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci		dbri->next_desc[desc] = -1;
117162306a36Sopenharmony_ci		dbri->dma->desc[desc].ba = dvma_buffer;
117262306a36Sopenharmony_ci		dbri->dma->desc[desc].nda = 0;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		if (streamno == DBRI_PLAY) {
117562306a36Sopenharmony_ci			dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen);
117662306a36Sopenharmony_ci			dbri->dma->desc[desc].word4 = 0;
117762306a36Sopenharmony_ci			dbri->dma->desc[desc].word1 |= DBRI_TD_F | DBRI_TD_B;
117862306a36Sopenharmony_ci		} else {
117962306a36Sopenharmony_ci			dbri->dma->desc[desc].word1 = 0;
118062306a36Sopenharmony_ci			dbri->dma->desc[desc].word4 =
118162306a36Sopenharmony_ci			    DBRI_RD_B | DBRI_RD_BCNT(mylen);
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		if (first_desc == -1)
118562306a36Sopenharmony_ci			first_desc = desc;
118662306a36Sopenharmony_ci		else {
118762306a36Sopenharmony_ci			dbri->next_desc[last_desc] = desc;
118862306a36Sopenharmony_ci			dbri->dma->desc[last_desc].nda =
118962306a36Sopenharmony_ci			    dvma_addr + dbri_dma_off(desc, desc);
119062306a36Sopenharmony_ci		}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		last_desc = desc;
119362306a36Sopenharmony_ci		dvma_buffer += mylen;
119462306a36Sopenharmony_ci		len -= mylen;
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (first_desc == -1 || last_desc == -1) {
119862306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_descs: "
119962306a36Sopenharmony_ci			" Not enough descriptors available\n");
120062306a36Sopenharmony_ci		return -1;
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	dbri->dma->desc[last_desc].nda =
120462306a36Sopenharmony_ci	    dvma_addr + dbri_dma_off(desc, first_desc);
120562306a36Sopenharmony_ci	dbri->next_desc[last_desc] = first_desc;
120662306a36Sopenharmony_ci	dbri->pipes[info->pipe].first_desc = first_desc;
120762306a36Sopenharmony_ci	dbri->pipes[info->pipe].desc = first_desc;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci#ifdef DBRI_DEBUG
121062306a36Sopenharmony_ci	for (desc = first_desc; desc != -1;) {
121162306a36Sopenharmony_ci		dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n",
121262306a36Sopenharmony_ci			desc,
121362306a36Sopenharmony_ci			dbri->dma->desc[desc].word1,
121462306a36Sopenharmony_ci			dbri->dma->desc[desc].ba,
121562306a36Sopenharmony_ci			dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4);
121662306a36Sopenharmony_ci			desc = dbri->next_desc[desc];
121762306a36Sopenharmony_ci			if (desc == first_desc)
121862306a36Sopenharmony_ci				break;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci#endif
122162306a36Sopenharmony_ci	return 0;
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci/*
122562306a36Sopenharmony_ci****************************************************************************
122662306a36Sopenharmony_ci************************** DBRI - CHI interface ****************************
122762306a36Sopenharmony_ci****************************************************************************
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ciThe CHI is a four-wire (clock, frame sync, data in, data out) time-division
123062306a36Sopenharmony_cimultiplexed serial interface which the DBRI can operate in either master
123162306a36Sopenharmony_ci(give clock/frame sync) or slave (take clock/frame sync) mode.
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci*/
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cienum master_or_slave { CHImaster, CHIslave };
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci/*
123862306a36Sopenharmony_ci * Lock must not be held before calling it.
123962306a36Sopenharmony_ci */
124062306a36Sopenharmony_cistatic void reset_chi(struct snd_dbri *dbri,
124162306a36Sopenharmony_ci		      enum master_or_slave master_or_slave,
124262306a36Sopenharmony_ci		      int bits_per_frame)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	s32 *cmd;
124562306a36Sopenharmony_ci	int val;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	/* Set CHI Anchor: Pipe 16 */
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
125062306a36Sopenharmony_ci	val = D_DTS_VO | D_DTS_VI | D_DTS_INS
125162306a36Sopenharmony_ci		| D_DTS_PRVIN(16) | D_PIPE(16) | D_DTS_PRVOUT(16);
125262306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_DTS, 0, val);
125362306a36Sopenharmony_ci	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
125462306a36Sopenharmony_ci	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
125562306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
125662306a36Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	dbri->pipes[16].sdp = 1;
125962306a36Sopenharmony_ci	dbri->pipes[16].nextpipe = 16;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (master_or_slave == CHIslave) {
126462306a36Sopenharmony_ci		/* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
126562306a36Sopenharmony_ci		 *
126662306a36Sopenharmony_ci		 * CHICM  = 0 (slave mode, 8 kHz frame rate)
126762306a36Sopenharmony_ci		 * IR     = give immediate CHI status interrupt
126862306a36Sopenharmony_ci		 * EN     = give CHI status interrupt upon change
126962306a36Sopenharmony_ci		 */
127062306a36Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
127162306a36Sopenharmony_ci	} else {
127262306a36Sopenharmony_ci		/* Setup DBRI for CHI Master - generate clock, FS
127362306a36Sopenharmony_ci		 *
127462306a36Sopenharmony_ci		 * BPF				=  bits per 8 kHz frame
127562306a36Sopenharmony_ci		 * 12.288 MHz / CHICM_divisor	= clock rate
127662306a36Sopenharmony_ci		 * FD = 1 - drive CHIFS on rising edge of CHICK
127762306a36Sopenharmony_ci		 */
127862306a36Sopenharmony_ci		int clockrate = bits_per_frame * 8;
127962306a36Sopenharmony_ci		int divisor = 12288 / clockrate;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		if (divisor > 255 || divisor * clockrate != 12288)
128262306a36Sopenharmony_ci			printk(KERN_ERR "DBRI: illegal bits_per_frame "
128362306a36Sopenharmony_ci				"in setup_chi\n");
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
128662306a36Sopenharmony_ci				    | D_CHI_BPF(bits_per_frame));
128762306a36Sopenharmony_ci	}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	dbri->chi_bpf = bits_per_frame;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	/* CHI Data Mode
129262306a36Sopenharmony_ci	 *
129362306a36Sopenharmony_ci	 * RCE   =  0 - receive on falling edge of CHICK
129462306a36Sopenharmony_ci	 * XCE   =  1 - transmit on rising edge of CHICK
129562306a36Sopenharmony_ci	 * XEN   =  1 - enable transmitter
129662306a36Sopenharmony_ci	 * REN   =  1 - enable receiver
129762306a36Sopenharmony_ci	 */
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
130062306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN);
130162306a36Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci/*
130762306a36Sopenharmony_ci****************************************************************************
130862306a36Sopenharmony_ci*********************** CS4215 audio codec management **********************
130962306a36Sopenharmony_ci****************************************************************************
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ciIn the standard SPARC audio configuration, the CS4215 codec is attached
131262306a36Sopenharmony_cito the DBRI via the CHI interface and few of the DBRI's PIO pins.
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci * Lock must not be held before calling it.
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci*/
131762306a36Sopenharmony_cistatic void cs4215_setup_pipes(struct snd_dbri *dbri)
131862306a36Sopenharmony_ci{
131962306a36Sopenharmony_ci	unsigned long flags;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
132262306a36Sopenharmony_ci	/*
132362306a36Sopenharmony_ci	 * Data mode:
132462306a36Sopenharmony_ci	 * Pipe  4: Send timeslots 1-4 (audio data)
132562306a36Sopenharmony_ci	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
132662306a36Sopenharmony_ci	 * Pipe  6: Receive timeslots 1-4 (audio data)
132762306a36Sopenharmony_ci	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
132862306a36Sopenharmony_ci	 *          interrupt, and the rest of the data (slot 5 and 8) is
132962306a36Sopenharmony_ci	 *          not relevant for us (only for doublechecking).
133062306a36Sopenharmony_ci	 *
133162306a36Sopenharmony_ci	 * Control mode:
133262306a36Sopenharmony_ci	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only)
133362306a36Sopenharmony_ci	 * Pipe 18: Receive timeslot 1 (clb).
133462306a36Sopenharmony_ci	 * Pipe 19: Receive timeslot 7 (version).
133562306a36Sopenharmony_ci	 */
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
133862306a36Sopenharmony_ci	setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
133962306a36Sopenharmony_ci	setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB);
134062306a36Sopenharmony_ci	setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
134362306a36Sopenharmony_ci	setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
134462306a36Sopenharmony_ci	setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
134562306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	dbri_cmdwait(dbri);
134862306a36Sopenharmony_ci}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_cistatic int cs4215_init_data(struct cs4215 *mm)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	/*
135362306a36Sopenharmony_ci	 * No action, memory resetting only.
135462306a36Sopenharmony_ci	 *
135562306a36Sopenharmony_ci	 * Data Time Slot 5-8
135662306a36Sopenharmony_ci	 * Speaker,Line and Headphone enable. Gain set to the half.
135762306a36Sopenharmony_ci	 * Input is mike.
135862306a36Sopenharmony_ci	 */
135962306a36Sopenharmony_ci	mm->data[0] = CS4215_LO(0x20) | CS4215_HE | CS4215_LE;
136062306a36Sopenharmony_ci	mm->data[1] = CS4215_RO(0x20) | CS4215_SE;
136162306a36Sopenharmony_ci	mm->data[2] = CS4215_LG(0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1;
136262306a36Sopenharmony_ci	mm->data[3] = CS4215_RG(0x8) | CS4215_MA(0xf);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	/*
136562306a36Sopenharmony_ci	 * Control Time Slot 1-4
136662306a36Sopenharmony_ci	 * 0: Default I/O voltage scale
136762306a36Sopenharmony_ci	 * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
136862306a36Sopenharmony_ci	 * 2: Serial enable, CHI master, 128 bits per frame, clock 1
136962306a36Sopenharmony_ci	 * 3: Tests disabled
137062306a36Sopenharmony_ci	 */
137162306a36Sopenharmony_ci	mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB;
137262306a36Sopenharmony_ci	mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
137362306a36Sopenharmony_ci	mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
137462306a36Sopenharmony_ci	mm->ctrl[3] = 0;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	mm->status = 0;
137762306a36Sopenharmony_ci	mm->version = 0xff;
137862306a36Sopenharmony_ci	mm->precision = 8;	/* For ULAW */
137962306a36Sopenharmony_ci	mm->channels = 1;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	return 0;
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic void cs4215_setdata(struct snd_dbri *dbri, int muted)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	if (muted) {
138762306a36Sopenharmony_ci		dbri->mm.data[0] |= 63;
138862306a36Sopenharmony_ci		dbri->mm.data[1] |= 63;
138962306a36Sopenharmony_ci		dbri->mm.data[2] &= ~15;
139062306a36Sopenharmony_ci		dbri->mm.data[3] &= ~15;
139162306a36Sopenharmony_ci	} else {
139262306a36Sopenharmony_ci		/* Start by setting the playback attenuation. */
139362306a36Sopenharmony_ci		struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
139462306a36Sopenharmony_ci		int left_gain = info->left_gain & 0x3f;
139562306a36Sopenharmony_ci		int right_gain = info->right_gain & 0x3f;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci		dbri->mm.data[0] &= ~0x3f;	/* Reset the volume bits */
139862306a36Sopenharmony_ci		dbri->mm.data[1] &= ~0x3f;
139962306a36Sopenharmony_ci		dbri->mm.data[0] |= (DBRI_MAX_VOLUME - left_gain);
140062306a36Sopenharmony_ci		dbri->mm.data[1] |= (DBRI_MAX_VOLUME - right_gain);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci		/* Now set the recording gain. */
140362306a36Sopenharmony_ci		info = &dbri->stream_info[DBRI_REC];
140462306a36Sopenharmony_ci		left_gain = info->left_gain & 0xf;
140562306a36Sopenharmony_ci		right_gain = info->right_gain & 0xf;
140662306a36Sopenharmony_ci		dbri->mm.data[2] |= CS4215_LG(left_gain);
140762306a36Sopenharmony_ci		dbri->mm.data[3] |= CS4215_RG(right_gain);
140862306a36Sopenharmony_ci	}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	xmit_fixed(dbri, 20, *(int *)dbri->mm.data);
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci/*
141462306a36Sopenharmony_ci * Set the CS4215 to data mode.
141562306a36Sopenharmony_ci */
141662306a36Sopenharmony_cistatic void cs4215_open(struct snd_dbri *dbri)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	int data_width;
141962306a36Sopenharmony_ci	u32 tmp;
142062306a36Sopenharmony_ci	unsigned long flags;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	dprintk(D_MM, "cs4215_open: %d channels, %d bits\n",
142362306a36Sopenharmony_ci		dbri->mm.channels, dbri->mm.precision);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
142662306a36Sopenharmony_ci	 * to make sure this takes.  This avoids clicking noises.
142762306a36Sopenharmony_ci	 */
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	cs4215_setdata(dbri, 1);
143062306a36Sopenharmony_ci	udelay(125);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/*
143362306a36Sopenharmony_ci	 * Data mode:
143462306a36Sopenharmony_ci	 * Pipe  4: Send timeslots 1-4 (audio data)
143562306a36Sopenharmony_ci	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
143662306a36Sopenharmony_ci	 * Pipe  6: Receive timeslots 1-4 (audio data)
143762306a36Sopenharmony_ci	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
143862306a36Sopenharmony_ci	 *          interrupt, and the rest of the data (slot 5 and 8) is
143962306a36Sopenharmony_ci	 *          not relevant for us (only for doublechecking).
144062306a36Sopenharmony_ci	 *
144162306a36Sopenharmony_ci	 * Just like in control mode, the time slots are all offset by eight
144262306a36Sopenharmony_ci	 * bits.  The CS4215, it seems, observes TSIN (the delayed signal)
144362306a36Sopenharmony_ci	 * even if it's the CHI master.  Don't ask me...
144462306a36Sopenharmony_ci	 */
144562306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
144662306a36Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
144762306a36Sopenharmony_ci	tmp &= ~(D_C);		/* Disable CHI */
144862306a36Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	/* Switch CS4215 to data mode - set PIO3 to 1 */
145162306a36Sopenharmony_ci	sbus_writel(D_ENPIO | D_PIO1 | D_PIO3 |
145262306a36Sopenharmony_ci		    (dbri->mm.onboard ? D_PIO0 : D_PIO2), dbri->regs + REG2);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	reset_chi(dbri, CHIslave, 128);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* Note: this next doesn't work for 8-bit stereo, because the two
145762306a36Sopenharmony_ci	 * channels would be on timeslots 1 and 3, with 2 and 4 idle.
145862306a36Sopenharmony_ci	 * (See CS4215 datasheet Fig 15)
145962306a36Sopenharmony_ci	 *
146062306a36Sopenharmony_ci	 * DBRI non-contiguous mode would be required to make this work.
146162306a36Sopenharmony_ci	 */
146262306a36Sopenharmony_ci	data_width = dbri->mm.channels * dbri->mm.precision;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	link_time_slot(dbri, 4, 16, 16, data_width, dbri->mm.offset);
146562306a36Sopenharmony_ci	link_time_slot(dbri, 20, 4, 16, 32, dbri->mm.offset + 32);
146662306a36Sopenharmony_ci	link_time_slot(dbri, 6, 16, 16, data_width, dbri->mm.offset);
146762306a36Sopenharmony_ci	link_time_slot(dbri, 21, 6, 16, 16, dbri->mm.offset + 40);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	/* FIXME: enable CHI after _setdata? */
147062306a36Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
147162306a36Sopenharmony_ci	tmp |= D_C;		/* Enable CHI */
147262306a36Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
147362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	cs4215_setdata(dbri, 0);
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci/*
147962306a36Sopenharmony_ci * Send the control information (i.e. audio format)
148062306a36Sopenharmony_ci */
148162306a36Sopenharmony_cistatic int cs4215_setctrl(struct snd_dbri *dbri)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	int i, val;
148462306a36Sopenharmony_ci	u32 tmp;
148562306a36Sopenharmony_ci	unsigned long flags;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	/* FIXME - let the CPU do something useful during these delays */
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
149062306a36Sopenharmony_ci	 * to make sure this takes.  This avoids clicking noises.
149162306a36Sopenharmony_ci	 */
149262306a36Sopenharmony_ci	cs4215_setdata(dbri, 1);
149362306a36Sopenharmony_ci	udelay(125);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	/*
149662306a36Sopenharmony_ci	 * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
149762306a36Sopenharmony_ci	 * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
149862306a36Sopenharmony_ci	 */
149962306a36Sopenharmony_ci	val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2);
150062306a36Sopenharmony_ci	sbus_writel(val, dbri->regs + REG2);
150162306a36Sopenharmony_ci	dprintk(D_MM, "cs4215_setctrl: reg2=0x%x\n", val);
150262306a36Sopenharmony_ci	udelay(34);
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	/* In Control mode, the CS4215 is a slave device, so the DBRI must
150562306a36Sopenharmony_ci	 * operate as CHI master, supplying clocking and frame synchronization.
150662306a36Sopenharmony_ci	 *
150762306a36Sopenharmony_ci	 * In Data mode, however, the CS4215 must be CHI master to insure
150862306a36Sopenharmony_ci	 * that its data stream is synchronous with its codec.
150962306a36Sopenharmony_ci	 *
151062306a36Sopenharmony_ci	 * The upshot of all this?  We start by putting the DBRI into master
151162306a36Sopenharmony_ci	 * mode, program the CS4215 in Control mode, then switch the CS4215
151262306a36Sopenharmony_ci	 * into Data mode and put the DBRI into slave mode.  Various timing
151362306a36Sopenharmony_ci	 * requirements must be observed along the way.
151462306a36Sopenharmony_ci	 *
151562306a36Sopenharmony_ci	 * Oh, and one more thing, on a SPARCStation 20 (and maybe
151662306a36Sopenharmony_ci	 * others?), the addressing of the CS4215's time slots is
151762306a36Sopenharmony_ci	 * offset by eight bits, so we add eight to all the "cycle"
151862306a36Sopenharmony_ci	 * values in the Define Time Slot (DTS) commands.  This is
151962306a36Sopenharmony_ci	 * done in hardware by a TI 248 that delays the DBRI->4215
152062306a36Sopenharmony_ci	 * frame sync signal by eight clock cycles.  Anybody know why?
152162306a36Sopenharmony_ci	 */
152262306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
152362306a36Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
152462306a36Sopenharmony_ci	tmp &= ~D_C;		/* Disable CHI */
152562306a36Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	reset_chi(dbri, CHImaster, 128);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	/*
153062306a36Sopenharmony_ci	 * Control mode:
153162306a36Sopenharmony_ci	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only)
153262306a36Sopenharmony_ci	 * Pipe 18: Receive timeslot 1 (clb).
153362306a36Sopenharmony_ci	 * Pipe 19: Receive timeslot 7 (version).
153462306a36Sopenharmony_ci	 */
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	link_time_slot(dbri, 17, 16, 16, 32, dbri->mm.offset);
153762306a36Sopenharmony_ci	link_time_slot(dbri, 18, 16, 16, 8, dbri->mm.offset);
153862306a36Sopenharmony_ci	link_time_slot(dbri, 19, 18, 16, 8, dbri->mm.offset + 48);
153962306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
154262306a36Sopenharmony_ci	dbri->mm.ctrl[0] &= ~CS4215_CLB;
154362306a36Sopenharmony_ci	xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
154662306a36Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
154762306a36Sopenharmony_ci	tmp |= D_C;		/* Enable CHI */
154862306a36Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
154962306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i)
155262306a36Sopenharmony_ci		msleep_interruptible(1);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	if (i == 0) {
155562306a36Sopenharmony_ci		dprintk(D_MM, "CS4215 didn't respond to CLB (0x%02x)\n",
155662306a36Sopenharmony_ci			dbri->mm.status);
155762306a36Sopenharmony_ci		return -1;
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	/* Disable changes to our copy of the version number, as we are about
156162306a36Sopenharmony_ci	 * to leave control mode.
156262306a36Sopenharmony_ci	 */
156362306a36Sopenharmony_ci	recv_fixed(dbri, 19, NULL);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	/* Terminate CS4215 control mode - data sheet says
156662306a36Sopenharmony_ci	 * "Set CLB=1 and send two more frames of valid control info"
156762306a36Sopenharmony_ci	 */
156862306a36Sopenharmony_ci	dbri->mm.ctrl[0] |= CS4215_CLB;
156962306a36Sopenharmony_ci	xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	/* Two frames of control info @ 8kHz frame rate = 250 us delay */
157262306a36Sopenharmony_ci	udelay(250);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	cs4215_setdata(dbri, 0);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	return 0;
157762306a36Sopenharmony_ci}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci/*
158062306a36Sopenharmony_ci * Setup the codec with the sampling rate, audio format and number of
158162306a36Sopenharmony_ci * channels.
158262306a36Sopenharmony_ci * As part of the process we resend the settings for the data
158362306a36Sopenharmony_ci * timeslots as well.
158462306a36Sopenharmony_ci */
158562306a36Sopenharmony_cistatic int cs4215_prepare(struct snd_dbri *dbri, unsigned int rate,
158662306a36Sopenharmony_ci			  snd_pcm_format_t format, unsigned int channels)
158762306a36Sopenharmony_ci{
158862306a36Sopenharmony_ci	int freq_idx;
158962306a36Sopenharmony_ci	int ret = 0;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	/* Lookup index for this rate */
159262306a36Sopenharmony_ci	for (freq_idx = 0; CS4215_FREQ[freq_idx].freq != 0; freq_idx++) {
159362306a36Sopenharmony_ci		if (CS4215_FREQ[freq_idx].freq == rate)
159462306a36Sopenharmony_ci			break;
159562306a36Sopenharmony_ci	}
159662306a36Sopenharmony_ci	if (CS4215_FREQ[freq_idx].freq != rate) {
159762306a36Sopenharmony_ci		printk(KERN_WARNING "DBRI: Unsupported rate %d Hz\n", rate);
159862306a36Sopenharmony_ci		return -1;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	switch (format) {
160262306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_MU_LAW:
160362306a36Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_ULAW;
160462306a36Sopenharmony_ci		dbri->mm.precision = 8;
160562306a36Sopenharmony_ci		break;
160662306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_A_LAW:
160762306a36Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_ALAW;
160862306a36Sopenharmony_ci		dbri->mm.precision = 8;
160962306a36Sopenharmony_ci		break;
161062306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_U8:
161162306a36Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_LINEAR8;
161262306a36Sopenharmony_ci		dbri->mm.precision = 8;
161362306a36Sopenharmony_ci		break;
161462306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
161562306a36Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_LINEAR16;
161662306a36Sopenharmony_ci		dbri->mm.precision = 16;
161762306a36Sopenharmony_ci		break;
161862306a36Sopenharmony_ci	default:
161962306a36Sopenharmony_ci		printk(KERN_WARNING "DBRI: Unsupported format %d\n", format);
162062306a36Sopenharmony_ci		return -1;
162162306a36Sopenharmony_ci	}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	/* Add rate parameters */
162462306a36Sopenharmony_ci	dbri->mm.ctrl[1] |= CS4215_FREQ[freq_idx].csval;
162562306a36Sopenharmony_ci	dbri->mm.ctrl[2] = CS4215_XCLK |
162662306a36Sopenharmony_ci	    CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	dbri->mm.channels = channels;
162962306a36Sopenharmony_ci	if (channels == 2)
163062306a36Sopenharmony_ci		dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	ret = cs4215_setctrl(dbri);
163362306a36Sopenharmony_ci	if (ret == 0)
163462306a36Sopenharmony_ci		cs4215_open(dbri);	/* set codec to data mode */
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	return ret;
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci/*
164062306a36Sopenharmony_ci *
164162306a36Sopenharmony_ci */
164262306a36Sopenharmony_cistatic int cs4215_init(struct snd_dbri *dbri)
164362306a36Sopenharmony_ci{
164462306a36Sopenharmony_ci	u32 reg2 = sbus_readl(dbri->regs + REG2);
164562306a36Sopenharmony_ci	dprintk(D_MM, "cs4215_init: reg2=0x%x\n", reg2);
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	/* Look for the cs4215 chips */
164862306a36Sopenharmony_ci	if (reg2 & D_PIO2) {
164962306a36Sopenharmony_ci		dprintk(D_MM, "Onboard CS4215 detected\n");
165062306a36Sopenharmony_ci		dbri->mm.onboard = 1;
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci	if (reg2 & D_PIO0) {
165362306a36Sopenharmony_ci		dprintk(D_MM, "Speakerbox detected\n");
165462306a36Sopenharmony_ci		dbri->mm.onboard = 0;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci		if (reg2 & D_PIO2) {
165762306a36Sopenharmony_ci			printk(KERN_INFO "DBRI: Using speakerbox / "
165862306a36Sopenharmony_ci			       "ignoring onboard mmcodec.\n");
165962306a36Sopenharmony_ci			sbus_writel(D_ENPIO2, dbri->regs + REG2);
166062306a36Sopenharmony_ci		}
166162306a36Sopenharmony_ci	}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	if (!(reg2 & (D_PIO0 | D_PIO2))) {
166462306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: no mmcodec found.\n");
166562306a36Sopenharmony_ci		return -EIO;
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	cs4215_setup_pipes(dbri);
166962306a36Sopenharmony_ci	cs4215_init_data(&dbri->mm);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	/* Enable capture of the status & version timeslots. */
167262306a36Sopenharmony_ci	recv_fixed(dbri, 18, &dbri->mm.status);
167362306a36Sopenharmony_ci	recv_fixed(dbri, 19, &dbri->mm.version);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	dbri->mm.offset = dbri->mm.onboard ? 0 : 8;
167662306a36Sopenharmony_ci	if (cs4215_setctrl(dbri) == -1 || dbri->mm.version == 0xff) {
167762306a36Sopenharmony_ci		dprintk(D_MM, "CS4215 failed probe at offset %d\n",
167862306a36Sopenharmony_ci			dbri->mm.offset);
167962306a36Sopenharmony_ci		return -EIO;
168062306a36Sopenharmony_ci	}
168162306a36Sopenharmony_ci	dprintk(D_MM, "Found CS4215 at offset %d\n", dbri->mm.offset);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	return 0;
168462306a36Sopenharmony_ci}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci/*
168762306a36Sopenharmony_ci****************************************************************************
168862306a36Sopenharmony_ci*************************** DBRI interrupt handler *************************
168962306a36Sopenharmony_ci****************************************************************************
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ciThe DBRI communicates with the CPU mainly via a circular interrupt
169262306a36Sopenharmony_cibuffer.  When an interrupt is signaled, the CPU walks through the
169362306a36Sopenharmony_cibuffer and calls dbri_process_one_interrupt() for each interrupt word.
169462306a36Sopenharmony_ciComplicated interrupts are handled by dedicated functions (which
169562306a36Sopenharmony_ciappear first in this file).  Any pending interrupts can be serviced by
169662306a36Sopenharmony_cicalling dbri_process_interrupt_buffer(), which works even if the CPU's
169762306a36Sopenharmony_ciinterrupts are disabled.
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci*/
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci/* xmit_descs()
170262306a36Sopenharmony_ci *
170362306a36Sopenharmony_ci * Starts transmitting the current TD's for recording/playing.
170462306a36Sopenharmony_ci * For playback, ALSA has filled the DMA memory with new data (we hope).
170562306a36Sopenharmony_ci */
170662306a36Sopenharmony_cistatic void xmit_descs(struct snd_dbri *dbri)
170762306a36Sopenharmony_ci{
170862306a36Sopenharmony_ci	struct dbri_streaminfo *info;
170962306a36Sopenharmony_ci	u32 dvma_addr;
171062306a36Sopenharmony_ci	s32 *cmd;
171162306a36Sopenharmony_ci	unsigned long flags;
171262306a36Sopenharmony_ci	int first_td;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	if (dbri == NULL)
171562306a36Sopenharmony_ci		return;		/* Disabled */
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	dvma_addr = (u32)dbri->dma_dvma;
171862306a36Sopenharmony_ci	info = &dbri->stream_info[DBRI_REC];
171962306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (info->pipe >= 0) {
172262306a36Sopenharmony_ci		first_td = dbri->pipes[info->pipe].first_desc;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci		dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci		/* Stream could be closed by the time we run. */
172762306a36Sopenharmony_ci		if (first_td >= 0) {
172862306a36Sopenharmony_ci			cmd = dbri_cmdlock(dbri, 2);
172962306a36Sopenharmony_ci			*(cmd++) = DBRI_CMD(D_SDP, 0,
173062306a36Sopenharmony_ci					    dbri->pipes[info->pipe].sdp
173162306a36Sopenharmony_ci					    | D_SDP_P | D_SDP_EVERY | D_SDP_C);
173262306a36Sopenharmony_ci			*(cmd++) = dvma_addr +
173362306a36Sopenharmony_ci				   dbri_dma_off(desc, first_td);
173462306a36Sopenharmony_ci			dbri_cmdsend(dbri, cmd, 2);
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci			/* Reset our admin of the pipe. */
173762306a36Sopenharmony_ci			dbri->pipes[info->pipe].desc = first_td;
173862306a36Sopenharmony_ci		}
173962306a36Sopenharmony_ci	}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	info = &dbri->stream_info[DBRI_PLAY];
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	if (info->pipe >= 0) {
174462306a36Sopenharmony_ci		first_td = dbri->pipes[info->pipe].first_desc;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci		dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci		/* Stream could be closed by the time we run. */
174962306a36Sopenharmony_ci		if (first_td >= 0) {
175062306a36Sopenharmony_ci			cmd = dbri_cmdlock(dbri, 2);
175162306a36Sopenharmony_ci			*(cmd++) = DBRI_CMD(D_SDP, 0,
175262306a36Sopenharmony_ci					    dbri->pipes[info->pipe].sdp
175362306a36Sopenharmony_ci					    | D_SDP_P | D_SDP_EVERY | D_SDP_C);
175462306a36Sopenharmony_ci			*(cmd++) = dvma_addr +
175562306a36Sopenharmony_ci				   dbri_dma_off(desc, first_td);
175662306a36Sopenharmony_ci			dbri_cmdsend(dbri, cmd, 2);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci			/* Reset our admin of the pipe. */
175962306a36Sopenharmony_ci			dbri->pipes[info->pipe].desc = first_td;
176062306a36Sopenharmony_ci		}
176162306a36Sopenharmony_ci	}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
176462306a36Sopenharmony_ci}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci/* transmission_complete_intr()
176762306a36Sopenharmony_ci *
176862306a36Sopenharmony_ci * Called by main interrupt handler when DBRI signals transmission complete
176962306a36Sopenharmony_ci * on a pipe (interrupt triggered by the B bit in a transmit descriptor).
177062306a36Sopenharmony_ci *
177162306a36Sopenharmony_ci * Walks through the pipe's list of transmit buffer descriptors and marks
177262306a36Sopenharmony_ci * them as available. Stops when the first descriptor is found without
177362306a36Sopenharmony_ci * TBC (Transmit Buffer Complete) set, or we've run through them all.
177462306a36Sopenharmony_ci *
177562306a36Sopenharmony_ci * The DMA buffers are not released. They form a ring buffer and
177662306a36Sopenharmony_ci * they are filled by ALSA while others are transmitted by DMA.
177762306a36Sopenharmony_ci *
177862306a36Sopenharmony_ci */
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_cistatic void transmission_complete_intr(struct snd_dbri *dbri, int pipe)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
178362306a36Sopenharmony_ci	int td = dbri->pipes[pipe].desc;
178462306a36Sopenharmony_ci	int status;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	while (td >= 0) {
178762306a36Sopenharmony_ci		if (td >= DBRI_NO_DESCS) {
178862306a36Sopenharmony_ci			printk(KERN_ERR "DBRI: invalid td on pipe %d\n", pipe);
178962306a36Sopenharmony_ci			return;
179062306a36Sopenharmony_ci		}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci		status = DBRI_TD_STATUS(dbri->dma->desc[td].word4);
179362306a36Sopenharmony_ci		if (!(status & DBRI_TD_TBC))
179462306a36Sopenharmony_ci			break;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci		dprintk(D_INT, "TD %d, status 0x%02x\n", td, status);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci		dbri->dma->desc[td].word4 = 0;	/* Reset it for next time. */
179962306a36Sopenharmony_ci		info->offset += DBRI_RD_CNT(dbri->dma->desc[td].word1);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci		td = dbri->next_desc[td];
180262306a36Sopenharmony_ci		dbri->pipes[pipe].desc = td;
180362306a36Sopenharmony_ci	}
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	/* Notify ALSA */
180662306a36Sopenharmony_ci	spin_unlock(&dbri->lock);
180762306a36Sopenharmony_ci	snd_pcm_period_elapsed(info->substream);
180862306a36Sopenharmony_ci	spin_lock(&dbri->lock);
180962306a36Sopenharmony_ci}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_cistatic void reception_complete_intr(struct snd_dbri *dbri, int pipe)
181262306a36Sopenharmony_ci{
181362306a36Sopenharmony_ci	struct dbri_streaminfo *info;
181462306a36Sopenharmony_ci	int rd = dbri->pipes[pipe].desc;
181562306a36Sopenharmony_ci	s32 status;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	if (rd < 0 || rd >= DBRI_NO_DESCS) {
181862306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: invalid rd on pipe %d\n", pipe);
181962306a36Sopenharmony_ci		return;
182062306a36Sopenharmony_ci	}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	dbri->pipes[pipe].desc = dbri->next_desc[rd];
182362306a36Sopenharmony_ci	status = dbri->dma->desc[rd].word1;
182462306a36Sopenharmony_ci	dbri->dma->desc[rd].word1 = 0;	/* Reset it for next time. */
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	info = &dbri->stream_info[DBRI_REC];
182762306a36Sopenharmony_ci	info->offset += DBRI_RD_CNT(status);
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	/* FIXME: Check status */
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n",
183262306a36Sopenharmony_ci		rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status));
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	/* Notify ALSA */
183562306a36Sopenharmony_ci	spin_unlock(&dbri->lock);
183662306a36Sopenharmony_ci	snd_pcm_period_elapsed(info->substream);
183762306a36Sopenharmony_ci	spin_lock(&dbri->lock);
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic void dbri_process_one_interrupt(struct snd_dbri *dbri, int x)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	int val = D_INTR_GETVAL(x);
184362306a36Sopenharmony_ci	int channel = D_INTR_GETCHAN(x);
184462306a36Sopenharmony_ci	int command = D_INTR_GETCMD(x);
184562306a36Sopenharmony_ci	int code = D_INTR_GETCODE(x);
184662306a36Sopenharmony_ci#ifdef DBRI_DEBUG
184762306a36Sopenharmony_ci	int rval = D_INTR_GETRVAL(x);
184862306a36Sopenharmony_ci#endif
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	if (channel == D_INTR_CMD) {
185162306a36Sopenharmony_ci		dprintk(D_CMD, "INTR: Command: %-5s  Value:%d\n",
185262306a36Sopenharmony_ci			cmds[command], val);
185362306a36Sopenharmony_ci	} else {
185462306a36Sopenharmony_ci		dprintk(D_INT, "INTR: Chan:%d Code:%d Val:%#x\n",
185562306a36Sopenharmony_ci			channel, code, rval);
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	switch (code) {
185962306a36Sopenharmony_ci	case D_INTR_CMDI:
186062306a36Sopenharmony_ci		if (command != D_WAIT)
186162306a36Sopenharmony_ci			printk(KERN_ERR "DBRI: Command read interrupt\n");
186262306a36Sopenharmony_ci		break;
186362306a36Sopenharmony_ci	case D_INTR_BRDY:
186462306a36Sopenharmony_ci		reception_complete_intr(dbri, channel);
186562306a36Sopenharmony_ci		break;
186662306a36Sopenharmony_ci	case D_INTR_XCMP:
186762306a36Sopenharmony_ci	case D_INTR_MINT:
186862306a36Sopenharmony_ci		transmission_complete_intr(dbri, channel);
186962306a36Sopenharmony_ci		break;
187062306a36Sopenharmony_ci	case D_INTR_UNDR:
187162306a36Sopenharmony_ci		/* UNDR - Transmission underrun
187262306a36Sopenharmony_ci		 * resend SDP command with clear pipe bit (C) set
187362306a36Sopenharmony_ci		 */
187462306a36Sopenharmony_ci		{
187562306a36Sopenharmony_ci	/* FIXME: do something useful in case of underrun */
187662306a36Sopenharmony_ci			printk(KERN_ERR "DBRI: Underrun error\n");
187762306a36Sopenharmony_ci#if 0
187862306a36Sopenharmony_ci			s32 *cmd;
187962306a36Sopenharmony_ci			int pipe = channel;
188062306a36Sopenharmony_ci			int td = dbri->pipes[pipe].desc;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci			dbri->dma->desc[td].word4 = 0;
188362306a36Sopenharmony_ci			cmd = dbri_cmdlock(dbri, NoGetLock);
188462306a36Sopenharmony_ci			*(cmd++) = DBRI_CMD(D_SDP, 0,
188562306a36Sopenharmony_ci					    dbri->pipes[pipe].sdp
188662306a36Sopenharmony_ci					    | D_SDP_P | D_SDP_C | D_SDP_2SAME);
188762306a36Sopenharmony_ci			*(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td);
188862306a36Sopenharmony_ci			dbri_cmdsend(dbri, cmd);
188962306a36Sopenharmony_ci#endif
189062306a36Sopenharmony_ci		}
189162306a36Sopenharmony_ci		break;
189262306a36Sopenharmony_ci	case D_INTR_FXDT:
189362306a36Sopenharmony_ci		/* FXDT - Fixed data change */
189462306a36Sopenharmony_ci		if (dbri->pipes[channel].sdp & D_SDP_MSB)
189562306a36Sopenharmony_ci			val = reverse_bytes(val, dbri->pipes[channel].length);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci		if (dbri->pipes[channel].recv_fixed_ptr)
189862306a36Sopenharmony_ci			*(dbri->pipes[channel].recv_fixed_ptr) = val;
189962306a36Sopenharmony_ci		break;
190062306a36Sopenharmony_ci	default:
190162306a36Sopenharmony_ci		if (channel != D_INTR_CMD)
190262306a36Sopenharmony_ci			printk(KERN_WARNING
190362306a36Sopenharmony_ci			       "DBRI: Ignored Interrupt: %d (0x%x)\n", code, x);
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
190862306a36Sopenharmony_ci * buffer until it finds a zero word (indicating nothing more to do
190962306a36Sopenharmony_ci * right now).  Non-zero words require processing and are handed off
191062306a36Sopenharmony_ci * to dbri_process_one_interrupt AFTER advancing the pointer.
191162306a36Sopenharmony_ci */
191262306a36Sopenharmony_cistatic void dbri_process_interrupt_buffer(struct snd_dbri *dbri)
191362306a36Sopenharmony_ci{
191462306a36Sopenharmony_ci	s32 x;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
191762306a36Sopenharmony_ci		dbri->dma->intr[dbri->dbri_irqp] = 0;
191862306a36Sopenharmony_ci		dbri->dbri_irqp++;
191962306a36Sopenharmony_ci		if (dbri->dbri_irqp == DBRI_INT_BLK)
192062306a36Sopenharmony_ci			dbri->dbri_irqp = 1;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci		dbri_process_one_interrupt(dbri, x);
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_cistatic irqreturn_t snd_dbri_interrupt(int irq, void *dev_id)
192762306a36Sopenharmony_ci{
192862306a36Sopenharmony_ci	struct snd_dbri *dbri = dev_id;
192962306a36Sopenharmony_ci	static int errcnt;
193062306a36Sopenharmony_ci	int x;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	if (dbri == NULL)
193362306a36Sopenharmony_ci		return IRQ_NONE;
193462306a36Sopenharmony_ci	spin_lock(&dbri->lock);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	/*
193762306a36Sopenharmony_ci	 * Read it, so the interrupt goes away.
193862306a36Sopenharmony_ci	 */
193962306a36Sopenharmony_ci	x = sbus_readl(dbri->regs + REG1);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	if (x & (D_MRR | D_MLE | D_LBG | D_MBE)) {
194262306a36Sopenharmony_ci		u32 tmp;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci		if (x & D_MRR)
194562306a36Sopenharmony_ci			printk(KERN_ERR
194662306a36Sopenharmony_ci			       "DBRI: Multiple Error Ack on SBus reg1=0x%x\n",
194762306a36Sopenharmony_ci			       x);
194862306a36Sopenharmony_ci		if (x & D_MLE)
194962306a36Sopenharmony_ci			printk(KERN_ERR
195062306a36Sopenharmony_ci			       "DBRI: Multiple Late Error on SBus reg1=0x%x\n",
195162306a36Sopenharmony_ci			       x);
195262306a36Sopenharmony_ci		if (x & D_LBG)
195362306a36Sopenharmony_ci			printk(KERN_ERR
195462306a36Sopenharmony_ci			       "DBRI: Lost Bus Grant on SBus reg1=0x%x\n", x);
195562306a36Sopenharmony_ci		if (x & D_MBE)
195662306a36Sopenharmony_ci			printk(KERN_ERR
195762306a36Sopenharmony_ci			       "DBRI: Burst Error on SBus reg1=0x%x\n", x);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci		/* Some of these SBus errors cause the chip's SBus circuitry
196062306a36Sopenharmony_ci		 * to be disabled, so just re-enable and try to keep going.
196162306a36Sopenharmony_ci		 *
196262306a36Sopenharmony_ci		 * The only one I've seen is MRR, which will be triggered
196362306a36Sopenharmony_ci		 * if you let a transmit pipe underrun, then try to CDP it.
196462306a36Sopenharmony_ci		 *
196562306a36Sopenharmony_ci		 * If these things persist, we reset the chip.
196662306a36Sopenharmony_ci		 */
196762306a36Sopenharmony_ci		if ((++errcnt) % 10 == 0) {
196862306a36Sopenharmony_ci			dprintk(D_INT, "Interrupt errors exceeded.\n");
196962306a36Sopenharmony_ci			dbri_reset(dbri);
197062306a36Sopenharmony_ci		} else {
197162306a36Sopenharmony_ci			tmp = sbus_readl(dbri->regs + REG0);
197262306a36Sopenharmony_ci			tmp &= ~(D_D);
197362306a36Sopenharmony_ci			sbus_writel(tmp, dbri->regs + REG0);
197462306a36Sopenharmony_ci		}
197562306a36Sopenharmony_ci	}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	dbri_process_interrupt_buffer(dbri);
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	spin_unlock(&dbri->lock);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	return IRQ_HANDLED;
198262306a36Sopenharmony_ci}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci/****************************************************************************
198562306a36Sopenharmony_ci		PCM Interface
198662306a36Sopenharmony_ci****************************************************************************/
198762306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_dbri_pcm_hw = {
198862306a36Sopenharmony_ci	.info		= SNDRV_PCM_INFO_MMAP |
198962306a36Sopenharmony_ci			  SNDRV_PCM_INFO_INTERLEAVED |
199062306a36Sopenharmony_ci			  SNDRV_PCM_INFO_BLOCK_TRANSFER |
199162306a36Sopenharmony_ci			  SNDRV_PCM_INFO_MMAP_VALID |
199262306a36Sopenharmony_ci			  SNDRV_PCM_INFO_BATCH,
199362306a36Sopenharmony_ci	.formats	= SNDRV_PCM_FMTBIT_MU_LAW |
199462306a36Sopenharmony_ci			  SNDRV_PCM_FMTBIT_A_LAW |
199562306a36Sopenharmony_ci			  SNDRV_PCM_FMTBIT_U8 |
199662306a36Sopenharmony_ci			  SNDRV_PCM_FMTBIT_S16_BE,
199762306a36Sopenharmony_ci	.rates		= SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_5512,
199862306a36Sopenharmony_ci	.rate_min		= 5512,
199962306a36Sopenharmony_ci	.rate_max		= 48000,
200062306a36Sopenharmony_ci	.channels_min		= 1,
200162306a36Sopenharmony_ci	.channels_max		= 2,
200262306a36Sopenharmony_ci	.buffer_bytes_max	= 64 * 1024,
200362306a36Sopenharmony_ci	.period_bytes_min	= 1,
200462306a36Sopenharmony_ci	.period_bytes_max	= DBRI_TD_MAXCNT,
200562306a36Sopenharmony_ci	.periods_min		= 1,
200662306a36Sopenharmony_ci	.periods_max		= 1024,
200762306a36Sopenharmony_ci};
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_cistatic int snd_hw_rule_format(struct snd_pcm_hw_params *params,
201062306a36Sopenharmony_ci			      struct snd_pcm_hw_rule *rule)
201162306a36Sopenharmony_ci{
201262306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
201362306a36Sopenharmony_ci				SNDRV_PCM_HW_PARAM_CHANNELS);
201462306a36Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
201562306a36Sopenharmony_ci	struct snd_mask fmt;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	snd_mask_any(&fmt);
201862306a36Sopenharmony_ci	if (c->min > 1) {
201962306a36Sopenharmony_ci		fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_BE;
202062306a36Sopenharmony_ci		return snd_mask_refine(f, &fmt);
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci	return 0;
202362306a36Sopenharmony_ci}
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_cistatic int snd_hw_rule_channels(struct snd_pcm_hw_params *params,
202662306a36Sopenharmony_ci				struct snd_pcm_hw_rule *rule)
202762306a36Sopenharmony_ci{
202862306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
202962306a36Sopenharmony_ci				SNDRV_PCM_HW_PARAM_CHANNELS);
203062306a36Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
203162306a36Sopenharmony_ci	struct snd_interval ch;
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	snd_interval_any(&ch);
203462306a36Sopenharmony_ci	if (!(f->bits[0] & SNDRV_PCM_FMTBIT_S16_BE)) {
203562306a36Sopenharmony_ci		ch.min = 1;
203662306a36Sopenharmony_ci		ch.max = 1;
203762306a36Sopenharmony_ci		ch.integer = 1;
203862306a36Sopenharmony_ci		return snd_interval_refine(c, &ch);
203962306a36Sopenharmony_ci	}
204062306a36Sopenharmony_ci	return 0;
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic int snd_dbri_open(struct snd_pcm_substream *substream)
204462306a36Sopenharmony_ci{
204562306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
204662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
204762306a36Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
204862306a36Sopenharmony_ci	unsigned long flags;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	dprintk(D_USR, "open audio output.\n");
205162306a36Sopenharmony_ci	runtime->hw = snd_dbri_pcm_hw;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
205462306a36Sopenharmony_ci	info->substream = substream;
205562306a36Sopenharmony_ci	info->offset = 0;
205662306a36Sopenharmony_ci	info->dvma_buffer = 0;
205762306a36Sopenharmony_ci	info->pipe = -1;
205862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
206162306a36Sopenharmony_ci			    snd_hw_rule_format, NULL, SNDRV_PCM_HW_PARAM_FORMAT,
206262306a36Sopenharmony_ci			    -1);
206362306a36Sopenharmony_ci	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
206462306a36Sopenharmony_ci			    snd_hw_rule_channels, NULL,
206562306a36Sopenharmony_ci			    SNDRV_PCM_HW_PARAM_CHANNELS,
206662306a36Sopenharmony_ci			    -1);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	cs4215_open(dbri);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	return 0;
207162306a36Sopenharmony_ci}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_cistatic int snd_dbri_close(struct snd_pcm_substream *substream)
207462306a36Sopenharmony_ci{
207562306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
207662306a36Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	dprintk(D_USR, "close audio output.\n");
207962306a36Sopenharmony_ci	info->substream = NULL;
208062306a36Sopenharmony_ci	info->offset = 0;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	return 0;
208362306a36Sopenharmony_ci}
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_cistatic int snd_dbri_hw_params(struct snd_pcm_substream *substream,
208662306a36Sopenharmony_ci			      struct snd_pcm_hw_params *hw_params)
208762306a36Sopenharmony_ci{
208862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
208962306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
209062306a36Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
209162306a36Sopenharmony_ci	int direction;
209262306a36Sopenharmony_ci	int ret;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	/* set sampling rate, audio format and number of channels */
209562306a36Sopenharmony_ci	ret = cs4215_prepare(dbri, params_rate(hw_params),
209662306a36Sopenharmony_ci			     params_format(hw_params),
209762306a36Sopenharmony_ci			     params_channels(hw_params));
209862306a36Sopenharmony_ci	if (ret != 0)
209962306a36Sopenharmony_ci		return ret;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	/* hw_params can get called multiple times. Only map the DMA once.
210262306a36Sopenharmony_ci	 */
210362306a36Sopenharmony_ci	if (info->dvma_buffer == 0) {
210462306a36Sopenharmony_ci		if (DBRI_STREAMNO(substream) == DBRI_PLAY)
210562306a36Sopenharmony_ci			direction = DMA_TO_DEVICE;
210662306a36Sopenharmony_ci		else
210762306a36Sopenharmony_ci			direction = DMA_FROM_DEVICE;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci		info->dvma_buffer =
211062306a36Sopenharmony_ci			dma_map_single(&dbri->op->dev,
211162306a36Sopenharmony_ci				       runtime->dma_area,
211262306a36Sopenharmony_ci				       params_buffer_bytes(hw_params),
211362306a36Sopenharmony_ci				       direction);
211462306a36Sopenharmony_ci	}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	direction = params_buffer_bytes(hw_params);
211762306a36Sopenharmony_ci	dprintk(D_USR, "hw_params: %d bytes, dvma=%x\n",
211862306a36Sopenharmony_ci		direction, info->dvma_buffer);
211962306a36Sopenharmony_ci	return 0;
212062306a36Sopenharmony_ci}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_cistatic int snd_dbri_hw_free(struct snd_pcm_substream *substream)
212362306a36Sopenharmony_ci{
212462306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
212562306a36Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
212662306a36Sopenharmony_ci	int direction;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	dprintk(D_USR, "hw_free.\n");
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	/* hw_free can get called multiple times. Only unmap the DMA once.
213162306a36Sopenharmony_ci	 */
213262306a36Sopenharmony_ci	if (info->dvma_buffer) {
213362306a36Sopenharmony_ci		if (DBRI_STREAMNO(substream) == DBRI_PLAY)
213462306a36Sopenharmony_ci			direction = DMA_TO_DEVICE;
213562306a36Sopenharmony_ci		else
213662306a36Sopenharmony_ci			direction = DMA_FROM_DEVICE;
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci		dma_unmap_single(&dbri->op->dev, info->dvma_buffer,
213962306a36Sopenharmony_ci				 substream->runtime->buffer_size, direction);
214062306a36Sopenharmony_ci		info->dvma_buffer = 0;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci	if (info->pipe != -1) {
214362306a36Sopenharmony_ci		reset_pipe(dbri, info->pipe);
214462306a36Sopenharmony_ci		info->pipe = -1;
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	return 0;
214862306a36Sopenharmony_ci}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_cistatic int snd_dbri_prepare(struct snd_pcm_substream *substream)
215162306a36Sopenharmony_ci{
215262306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
215362306a36Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
215462306a36Sopenharmony_ci	int ret;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	info->size = snd_pcm_lib_buffer_bytes(substream);
215762306a36Sopenharmony_ci	if (DBRI_STREAMNO(substream) == DBRI_PLAY)
215862306a36Sopenharmony_ci		info->pipe = 4;	/* Send pipe */
215962306a36Sopenharmony_ci	else
216062306a36Sopenharmony_ci		info->pipe = 6;	/* Receive pipe */
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	spin_lock_irq(&dbri->lock);
216362306a36Sopenharmony_ci	info->offset = 0;
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	/* Setup the all the transmit/receive descriptors to cover the
216662306a36Sopenharmony_ci	 * whole DMA buffer.
216762306a36Sopenharmony_ci	 */
216862306a36Sopenharmony_ci	ret = setup_descs(dbri, DBRI_STREAMNO(substream),
216962306a36Sopenharmony_ci			  snd_pcm_lib_period_bytes(substream));
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	spin_unlock_irq(&dbri->lock);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	dprintk(D_USR, "prepare audio output. %d bytes\n", info->size);
217462306a36Sopenharmony_ci	return ret;
217562306a36Sopenharmony_ci}
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_cistatic int snd_dbri_trigger(struct snd_pcm_substream *substream, int cmd)
217862306a36Sopenharmony_ci{
217962306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
218062306a36Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
218162306a36Sopenharmony_ci	int ret = 0;
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	switch (cmd) {
218462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
218562306a36Sopenharmony_ci		dprintk(D_USR, "start audio, period is %d bytes\n",
218662306a36Sopenharmony_ci			(int)snd_pcm_lib_period_bytes(substream));
218762306a36Sopenharmony_ci		/* Re-submit the TDs. */
218862306a36Sopenharmony_ci		xmit_descs(dbri);
218962306a36Sopenharmony_ci		break;
219062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
219162306a36Sopenharmony_ci		dprintk(D_USR, "stop audio.\n");
219262306a36Sopenharmony_ci		reset_pipe(dbri, info->pipe);
219362306a36Sopenharmony_ci		break;
219462306a36Sopenharmony_ci	default:
219562306a36Sopenharmony_ci		ret = -EINVAL;
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	return ret;
219962306a36Sopenharmony_ci}
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_dbri_pointer(struct snd_pcm_substream *substream)
220262306a36Sopenharmony_ci{
220362306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
220462306a36Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
220562306a36Sopenharmony_ci	snd_pcm_uframes_t ret;
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	ret = bytes_to_frames(substream->runtime, info->offset)
220862306a36Sopenharmony_ci		% substream->runtime->buffer_size;
220962306a36Sopenharmony_ci	dprintk(D_USR, "I/O pointer: %ld frames of %ld.\n",
221062306a36Sopenharmony_ci		ret, substream->runtime->buffer_size);
221162306a36Sopenharmony_ci	return ret;
221262306a36Sopenharmony_ci}
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_dbri_ops = {
221562306a36Sopenharmony_ci	.open = snd_dbri_open,
221662306a36Sopenharmony_ci	.close = snd_dbri_close,
221762306a36Sopenharmony_ci	.hw_params = snd_dbri_hw_params,
221862306a36Sopenharmony_ci	.hw_free = snd_dbri_hw_free,
221962306a36Sopenharmony_ci	.prepare = snd_dbri_prepare,
222062306a36Sopenharmony_ci	.trigger = snd_dbri_trigger,
222162306a36Sopenharmony_ci	.pointer = snd_dbri_pointer,
222262306a36Sopenharmony_ci};
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cistatic int snd_dbri_pcm(struct snd_card *card)
222562306a36Sopenharmony_ci{
222662306a36Sopenharmony_ci	struct snd_pcm *pcm;
222762306a36Sopenharmony_ci	int err;
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	err = snd_pcm_new(card,
223062306a36Sopenharmony_ci			  /* ID */	    "sun_dbri",
223162306a36Sopenharmony_ci			  /* device */	    0,
223262306a36Sopenharmony_ci			  /* playback count */ 1,
223362306a36Sopenharmony_ci			  /* capture count */  1, &pcm);
223462306a36Sopenharmony_ci	if (err < 0)
223562306a36Sopenharmony_ci		return err;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dbri_ops);
223862306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_dbri_ops);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	pcm->private_data = card->private_data;
224162306a36Sopenharmony_ci	pcm->info_flags = 0;
224262306a36Sopenharmony_ci	strcpy(pcm->name, card->shortname);
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
224562306a36Sopenharmony_ci				       NULL, 64 * 1024, 64 * 1024);
224662306a36Sopenharmony_ci	return 0;
224762306a36Sopenharmony_ci}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci/*****************************************************************************
225062306a36Sopenharmony_ci			Mixer interface
225162306a36Sopenharmony_ci*****************************************************************************/
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_cistatic int snd_cs4215_info_volume(struct snd_kcontrol *kcontrol,
225462306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
225762306a36Sopenharmony_ci	uinfo->count = 2;
225862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
225962306a36Sopenharmony_ci	if (kcontrol->private_value == DBRI_PLAY)
226062306a36Sopenharmony_ci		uinfo->value.integer.max = DBRI_MAX_VOLUME;
226162306a36Sopenharmony_ci	else
226262306a36Sopenharmony_ci		uinfo->value.integer.max = DBRI_MAX_GAIN;
226362306a36Sopenharmony_ci	return 0;
226462306a36Sopenharmony_ci}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_cistatic int snd_cs4215_get_volume(struct snd_kcontrol *kcontrol,
226762306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
226862306a36Sopenharmony_ci{
226962306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
227062306a36Sopenharmony_ci	struct dbri_streaminfo *info;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	if (snd_BUG_ON(!dbri))
227362306a36Sopenharmony_ci		return -EINVAL;
227462306a36Sopenharmony_ci	info = &dbri->stream_info[kcontrol->private_value];
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = info->left_gain;
227762306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = info->right_gain;
227862306a36Sopenharmony_ci	return 0;
227962306a36Sopenharmony_ci}
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_cistatic int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
228262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
228362306a36Sopenharmony_ci{
228462306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
228562306a36Sopenharmony_ci	struct dbri_streaminfo *info =
228662306a36Sopenharmony_ci				&dbri->stream_info[kcontrol->private_value];
228762306a36Sopenharmony_ci	unsigned int vol[2];
228862306a36Sopenharmony_ci	int changed = 0;
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	vol[0] = ucontrol->value.integer.value[0];
229162306a36Sopenharmony_ci	vol[1] = ucontrol->value.integer.value[1];
229262306a36Sopenharmony_ci	if (kcontrol->private_value == DBRI_PLAY) {
229362306a36Sopenharmony_ci		if (vol[0] > DBRI_MAX_VOLUME || vol[1] > DBRI_MAX_VOLUME)
229462306a36Sopenharmony_ci			return -EINVAL;
229562306a36Sopenharmony_ci	} else {
229662306a36Sopenharmony_ci		if (vol[0] > DBRI_MAX_GAIN || vol[1] > DBRI_MAX_GAIN)
229762306a36Sopenharmony_ci			return -EINVAL;
229862306a36Sopenharmony_ci	}
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	if (info->left_gain != vol[0]) {
230162306a36Sopenharmony_ci		info->left_gain = vol[0];
230262306a36Sopenharmony_ci		changed = 1;
230362306a36Sopenharmony_ci	}
230462306a36Sopenharmony_ci	if (info->right_gain != vol[1]) {
230562306a36Sopenharmony_ci		info->right_gain = vol[1];
230662306a36Sopenharmony_ci		changed = 1;
230762306a36Sopenharmony_ci	}
230862306a36Sopenharmony_ci	if (changed) {
230962306a36Sopenharmony_ci		/* First mute outputs, and wait 1/8000 sec (125 us)
231062306a36Sopenharmony_ci		 * to make sure this takes.  This avoids clicking noises.
231162306a36Sopenharmony_ci		 */
231262306a36Sopenharmony_ci		cs4215_setdata(dbri, 1);
231362306a36Sopenharmony_ci		udelay(125);
231462306a36Sopenharmony_ci		cs4215_setdata(dbri, 0);
231562306a36Sopenharmony_ci	}
231662306a36Sopenharmony_ci	return changed;
231762306a36Sopenharmony_ci}
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_cistatic int snd_cs4215_info_single(struct snd_kcontrol *kcontrol,
232062306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
232162306a36Sopenharmony_ci{
232262306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	uinfo->type = (mask == 1) ?
232562306a36Sopenharmony_ci	    SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
232662306a36Sopenharmony_ci	uinfo->count = 1;
232762306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
232862306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
232962306a36Sopenharmony_ci	return 0;
233062306a36Sopenharmony_ci}
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_cistatic int snd_cs4215_get_single(struct snd_kcontrol *kcontrol,
233362306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
233462306a36Sopenharmony_ci{
233562306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
233662306a36Sopenharmony_ci	int elem = kcontrol->private_value & 0xff;
233762306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
233862306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
233962306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 1;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	if (snd_BUG_ON(!dbri))
234262306a36Sopenharmony_ci		return -EINVAL;
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	if (elem < 4)
234562306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
234662306a36Sopenharmony_ci		    (dbri->mm.data[elem] >> shift) & mask;
234762306a36Sopenharmony_ci	else
234862306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
234962306a36Sopenharmony_ci		    (dbri->mm.ctrl[elem - 4] >> shift) & mask;
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	if (invert == 1)
235262306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
235362306a36Sopenharmony_ci		    mask - ucontrol->value.integer.value[0];
235462306a36Sopenharmony_ci	return 0;
235562306a36Sopenharmony_ci}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_cistatic int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
235862306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
235962306a36Sopenharmony_ci{
236062306a36Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
236162306a36Sopenharmony_ci	int elem = kcontrol->private_value & 0xff;
236262306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
236362306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
236462306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 1;
236562306a36Sopenharmony_ci	int changed = 0;
236662306a36Sopenharmony_ci	unsigned short val;
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	if (snd_BUG_ON(!dbri))
236962306a36Sopenharmony_ci		return -EINVAL;
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
237262306a36Sopenharmony_ci	if (invert == 1)
237362306a36Sopenharmony_ci		val = mask - val;
237462306a36Sopenharmony_ci	val <<= shift;
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	if (elem < 4) {
237762306a36Sopenharmony_ci		dbri->mm.data[elem] = (dbri->mm.data[elem] &
237862306a36Sopenharmony_ci				       ~(mask << shift)) | val;
237962306a36Sopenharmony_ci		changed = (val != dbri->mm.data[elem]);
238062306a36Sopenharmony_ci	} else {
238162306a36Sopenharmony_ci		dbri->mm.ctrl[elem - 4] = (dbri->mm.ctrl[elem - 4] &
238262306a36Sopenharmony_ci					   ~(mask << shift)) | val;
238362306a36Sopenharmony_ci		changed = (val != dbri->mm.ctrl[elem - 4]);
238462306a36Sopenharmony_ci	}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	dprintk(D_GEN, "put_single: mask=0x%x, changed=%d, "
238762306a36Sopenharmony_ci		"mixer-value=%ld, mm-value=0x%x\n",
238862306a36Sopenharmony_ci		mask, changed, ucontrol->value.integer.value[0],
238962306a36Sopenharmony_ci		dbri->mm.data[elem & 3]);
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	if (changed) {
239262306a36Sopenharmony_ci		/* First mute outputs, and wait 1/8000 sec (125 us)
239362306a36Sopenharmony_ci		 * to make sure this takes.  This avoids clicking noises.
239462306a36Sopenharmony_ci		 */
239562306a36Sopenharmony_ci		cs4215_setdata(dbri, 1);
239662306a36Sopenharmony_ci		udelay(125);
239762306a36Sopenharmony_ci		cs4215_setdata(dbri, 0);
239862306a36Sopenharmony_ci	}
239962306a36Sopenharmony_ci	return changed;
240062306a36Sopenharmony_ci}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci/* Entries 0-3 map to the 4 data timeslots, entries 4-7 map to the 4 control
240362306a36Sopenharmony_ci   timeslots. Shift is the bit offset in the timeslot, mask defines the
240462306a36Sopenharmony_ci   number of bits. invert is a boolean for use with attenuation.
240562306a36Sopenharmony_ci */
240662306a36Sopenharmony_ci#define CS4215_SINGLE(xname, entry, shift, mask, invert)	\
240762306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),		\
240862306a36Sopenharmony_ci  .info = snd_cs4215_info_single,				\
240962306a36Sopenharmony_ci  .get = snd_cs4215_get_single, .put = snd_cs4215_put_single,	\
241062306a36Sopenharmony_ci  .private_value = (entry) | ((shift) << 8) | ((mask) << 16) |	\
241162306a36Sopenharmony_ci			((invert) << 24) },
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_cistatic const struct snd_kcontrol_new dbri_controls[] = {
241462306a36Sopenharmony_ci	{
241562306a36Sopenharmony_ci	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
241662306a36Sopenharmony_ci	 .name  = "Playback Volume",
241762306a36Sopenharmony_ci	 .info  = snd_cs4215_info_volume,
241862306a36Sopenharmony_ci	 .get   = snd_cs4215_get_volume,
241962306a36Sopenharmony_ci	 .put   = snd_cs4215_put_volume,
242062306a36Sopenharmony_ci	 .private_value = DBRI_PLAY,
242162306a36Sopenharmony_ci	 },
242262306a36Sopenharmony_ci	CS4215_SINGLE("Headphone switch", 0, 7, 1, 0)
242362306a36Sopenharmony_ci	CS4215_SINGLE("Line out switch", 0, 6, 1, 0)
242462306a36Sopenharmony_ci	CS4215_SINGLE("Speaker switch", 1, 6, 1, 0)
242562306a36Sopenharmony_ci	{
242662306a36Sopenharmony_ci	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
242762306a36Sopenharmony_ci	 .name  = "Capture Volume",
242862306a36Sopenharmony_ci	 .info  = snd_cs4215_info_volume,
242962306a36Sopenharmony_ci	 .get   = snd_cs4215_get_volume,
243062306a36Sopenharmony_ci	 .put   = snd_cs4215_put_volume,
243162306a36Sopenharmony_ci	 .private_value = DBRI_REC,
243262306a36Sopenharmony_ci	 },
243362306a36Sopenharmony_ci	/* FIXME: mic/line switch */
243462306a36Sopenharmony_ci	CS4215_SINGLE("Line in switch", 2, 4, 1, 0)
243562306a36Sopenharmony_ci	CS4215_SINGLE("High Pass Filter switch", 5, 7, 1, 0)
243662306a36Sopenharmony_ci	CS4215_SINGLE("Monitor Volume", 3, 4, 0xf, 1)
243762306a36Sopenharmony_ci	CS4215_SINGLE("Mic boost", 4, 4, 1, 1)
243862306a36Sopenharmony_ci};
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_cistatic int snd_dbri_mixer(struct snd_card *card)
244162306a36Sopenharmony_ci{
244262306a36Sopenharmony_ci	int idx, err;
244362306a36Sopenharmony_ci	struct snd_dbri *dbri;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	if (snd_BUG_ON(!card || !card->private_data))
244662306a36Sopenharmony_ci		return -EINVAL;
244762306a36Sopenharmony_ci	dbri = card->private_data;
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	strcpy(card->mixername, card->shortname);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(dbri_controls); idx++) {
245262306a36Sopenharmony_ci		err = snd_ctl_add(card,
245362306a36Sopenharmony_ci				snd_ctl_new1(&dbri_controls[idx], dbri));
245462306a36Sopenharmony_ci		if (err < 0)
245562306a36Sopenharmony_ci			return err;
245662306a36Sopenharmony_ci	}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) {
245962306a36Sopenharmony_ci		dbri->stream_info[idx].left_gain = 0;
246062306a36Sopenharmony_ci		dbri->stream_info[idx].right_gain = 0;
246162306a36Sopenharmony_ci	}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	return 0;
246462306a36Sopenharmony_ci}
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci/****************************************************************************
246762306a36Sopenharmony_ci			/proc interface
246862306a36Sopenharmony_ci****************************************************************************/
246962306a36Sopenharmony_cistatic void dbri_regs_read(struct snd_info_entry *entry,
247062306a36Sopenharmony_ci			   struct snd_info_buffer *buffer)
247162306a36Sopenharmony_ci{
247262306a36Sopenharmony_ci	struct snd_dbri *dbri = entry->private_data;
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	snd_iprintf(buffer, "REG0: 0x%x\n", sbus_readl(dbri->regs + REG0));
247562306a36Sopenharmony_ci	snd_iprintf(buffer, "REG2: 0x%x\n", sbus_readl(dbri->regs + REG2));
247662306a36Sopenharmony_ci	snd_iprintf(buffer, "REG8: 0x%x\n", sbus_readl(dbri->regs + REG8));
247762306a36Sopenharmony_ci	snd_iprintf(buffer, "REG9: 0x%x\n", sbus_readl(dbri->regs + REG9));
247862306a36Sopenharmony_ci}
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci#ifdef DBRI_DEBUG
248162306a36Sopenharmony_cistatic void dbri_debug_read(struct snd_info_entry *entry,
248262306a36Sopenharmony_ci			    struct snd_info_buffer *buffer)
248362306a36Sopenharmony_ci{
248462306a36Sopenharmony_ci	struct snd_dbri *dbri = entry->private_data;
248562306a36Sopenharmony_ci	int pipe;
248662306a36Sopenharmony_ci	snd_iprintf(buffer, "debug=%d\n", dbri_debug);
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	for (pipe = 0; pipe < 32; pipe++) {
248962306a36Sopenharmony_ci		if (pipe_active(dbri, pipe)) {
249062306a36Sopenharmony_ci			struct dbri_pipe *pptr = &dbri->pipes[pipe];
249162306a36Sopenharmony_ci			snd_iprintf(buffer,
249262306a36Sopenharmony_ci				    "Pipe %d: %s SDP=0x%x desc=%d, "
249362306a36Sopenharmony_ci				    "len=%d next %d\n",
249462306a36Sopenharmony_ci				    pipe,
249562306a36Sopenharmony_ci				   (pptr->sdp & D_SDP_TO_SER) ? "output" :
249662306a36Sopenharmony_ci								 "input",
249762306a36Sopenharmony_ci				    pptr->sdp, pptr->desc,
249862306a36Sopenharmony_ci				    pptr->length, pptr->nextpipe);
249962306a36Sopenharmony_ci		}
250062306a36Sopenharmony_ci	}
250162306a36Sopenharmony_ci}
250262306a36Sopenharmony_ci#endif
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_cistatic void snd_dbri_proc(struct snd_card *card)
250562306a36Sopenharmony_ci{
250662306a36Sopenharmony_ci	struct snd_dbri *dbri = card->private_data;
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	snd_card_ro_proc_new(card, "regs", dbri, dbri_regs_read);
250962306a36Sopenharmony_ci#ifdef DBRI_DEBUG
251062306a36Sopenharmony_ci	snd_card_ro_proc_new(card, "debug", dbri, dbri_debug_read);
251162306a36Sopenharmony_ci#endif
251262306a36Sopenharmony_ci}
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci/*
251562306a36Sopenharmony_ci****************************************************************************
251662306a36Sopenharmony_ci**************************** Initialization ********************************
251762306a36Sopenharmony_ci****************************************************************************
251862306a36Sopenharmony_ci*/
251962306a36Sopenharmony_cistatic void snd_dbri_free(struct snd_dbri *dbri);
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_cistatic int snd_dbri_create(struct snd_card *card,
252262306a36Sopenharmony_ci			   struct platform_device *op,
252362306a36Sopenharmony_ci			   int irq, int dev)
252462306a36Sopenharmony_ci{
252562306a36Sopenharmony_ci	struct snd_dbri *dbri = card->private_data;
252662306a36Sopenharmony_ci	int err;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	spin_lock_init(&dbri->lock);
252962306a36Sopenharmony_ci	dbri->op = op;
253062306a36Sopenharmony_ci	dbri->irq = irq;
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	dbri->dma = dma_alloc_coherent(&op->dev, sizeof(struct dbri_dma),
253362306a36Sopenharmony_ci				       &dbri->dma_dvma, GFP_KERNEL);
253462306a36Sopenharmony_ci	if (!dbri->dma)
253562306a36Sopenharmony_ci		return -ENOMEM;
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	dprintk(D_GEN, "DMA Cmd Block 0x%p (%pad)\n",
253862306a36Sopenharmony_ci		dbri->dma, dbri->dma_dvma);
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	/* Map the registers into memory. */
254162306a36Sopenharmony_ci	dbri->regs_size = resource_size(&op->resource[0]);
254262306a36Sopenharmony_ci	dbri->regs = of_ioremap(&op->resource[0], 0,
254362306a36Sopenharmony_ci				dbri->regs_size, "DBRI Registers");
254462306a36Sopenharmony_ci	if (!dbri->regs) {
254562306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: could not allocate registers\n");
254662306a36Sopenharmony_ci		dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
254762306a36Sopenharmony_ci				  (void *)dbri->dma, dbri->dma_dvma);
254862306a36Sopenharmony_ci		return -EIO;
254962306a36Sopenharmony_ci	}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	err = request_irq(dbri->irq, snd_dbri_interrupt, IRQF_SHARED,
255262306a36Sopenharmony_ci			  "DBRI audio", dbri);
255362306a36Sopenharmony_ci	if (err) {
255462306a36Sopenharmony_ci		printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
255562306a36Sopenharmony_ci		of_iounmap(&op->resource[0], dbri->regs, dbri->regs_size);
255662306a36Sopenharmony_ci		dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
255762306a36Sopenharmony_ci				  (void *)dbri->dma, dbri->dma_dvma);
255862306a36Sopenharmony_ci		return err;
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	/* Do low level initialization of the DBRI and CS4215 chips */
256262306a36Sopenharmony_ci	dbri_initialize(dbri);
256362306a36Sopenharmony_ci	err = cs4215_init(dbri);
256462306a36Sopenharmony_ci	if (err) {
256562306a36Sopenharmony_ci		snd_dbri_free(dbri);
256662306a36Sopenharmony_ci		return err;
256762306a36Sopenharmony_ci	}
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	return 0;
257062306a36Sopenharmony_ci}
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_cistatic void snd_dbri_free(struct snd_dbri *dbri)
257362306a36Sopenharmony_ci{
257462306a36Sopenharmony_ci	dprintk(D_GEN, "snd_dbri_free\n");
257562306a36Sopenharmony_ci	dbri_reset(dbri);
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	if (dbri->irq)
257862306a36Sopenharmony_ci		free_irq(dbri->irq, dbri);
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	if (dbri->regs)
258162306a36Sopenharmony_ci		of_iounmap(&dbri->op->resource[0], dbri->regs, dbri->regs_size);
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	if (dbri->dma)
258462306a36Sopenharmony_ci		dma_free_coherent(&dbri->op->dev,
258562306a36Sopenharmony_ci				  sizeof(struct dbri_dma),
258662306a36Sopenharmony_ci				  (void *)dbri->dma, dbri->dma_dvma);
258762306a36Sopenharmony_ci}
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_cistatic int dbri_probe(struct platform_device *op)
259062306a36Sopenharmony_ci{
259162306a36Sopenharmony_ci	struct snd_dbri *dbri;
259262306a36Sopenharmony_ci	struct resource *rp;
259362306a36Sopenharmony_ci	struct snd_card *card;
259462306a36Sopenharmony_ci	static int dev;
259562306a36Sopenharmony_ci	int irq;
259662306a36Sopenharmony_ci	int err;
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
259962306a36Sopenharmony_ci		return -ENODEV;
260062306a36Sopenharmony_ci	if (!enable[dev]) {
260162306a36Sopenharmony_ci		dev++;
260262306a36Sopenharmony_ci		return -ENOENT;
260362306a36Sopenharmony_ci	}
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	irq = op->archdata.irqs[0];
260662306a36Sopenharmony_ci	if (irq <= 0) {
260762306a36Sopenharmony_ci		printk(KERN_ERR "DBRI-%d: No IRQ.\n", dev);
260862306a36Sopenharmony_ci		return -ENODEV;
260962306a36Sopenharmony_ci	}
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	err = snd_card_new(&op->dev, index[dev], id[dev], THIS_MODULE,
261262306a36Sopenharmony_ci			   sizeof(struct snd_dbri), &card);
261362306a36Sopenharmony_ci	if (err < 0)
261462306a36Sopenharmony_ci		return err;
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	strcpy(card->driver, "DBRI");
261762306a36Sopenharmony_ci	strcpy(card->shortname, "Sun DBRI");
261862306a36Sopenharmony_ci	rp = &op->resource[0];
261962306a36Sopenharmony_ci	sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
262062306a36Sopenharmony_ci		card->shortname,
262162306a36Sopenharmony_ci		rp->flags & 0xffL, (unsigned long long)rp->start, irq);
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	err = snd_dbri_create(card, op, irq, dev);
262462306a36Sopenharmony_ci	if (err < 0) {
262562306a36Sopenharmony_ci		snd_card_free(card);
262662306a36Sopenharmony_ci		return err;
262762306a36Sopenharmony_ci	}
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	dbri = card->private_data;
263062306a36Sopenharmony_ci	err = snd_dbri_pcm(card);
263162306a36Sopenharmony_ci	if (err < 0)
263262306a36Sopenharmony_ci		goto _err;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	err = snd_dbri_mixer(card);
263562306a36Sopenharmony_ci	if (err < 0)
263662306a36Sopenharmony_ci		goto _err;
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	/* /proc file handling */
263962306a36Sopenharmony_ci	snd_dbri_proc(card);
264062306a36Sopenharmony_ci	dev_set_drvdata(&op->dev, card);
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci	err = snd_card_register(card);
264362306a36Sopenharmony_ci	if (err < 0)
264462306a36Sopenharmony_ci		goto _err;
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
264762306a36Sopenharmony_ci	       dev, dbri->regs,
264862306a36Sopenharmony_ci	       dbri->irq, op->dev.of_node->name[9], dbri->mm.version);
264962306a36Sopenharmony_ci	dev++;
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci	return 0;
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci_err:
265462306a36Sopenharmony_ci	snd_dbri_free(dbri);
265562306a36Sopenharmony_ci	snd_card_free(card);
265662306a36Sopenharmony_ci	return err;
265762306a36Sopenharmony_ci}
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_cistatic void dbri_remove(struct platform_device *op)
266062306a36Sopenharmony_ci{
266162306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(&op->dev);
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	snd_dbri_free(card->private_data);
266462306a36Sopenharmony_ci	snd_card_free(card);
266562306a36Sopenharmony_ci}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_cistatic const struct of_device_id dbri_match[] = {
266862306a36Sopenharmony_ci	{
266962306a36Sopenharmony_ci		.name = "SUNW,DBRIe",
267062306a36Sopenharmony_ci	},
267162306a36Sopenharmony_ci	{
267262306a36Sopenharmony_ci		.name = "SUNW,DBRIf",
267362306a36Sopenharmony_ci	},
267462306a36Sopenharmony_ci	{},
267562306a36Sopenharmony_ci};
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dbri_match);
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_cistatic struct platform_driver dbri_sbus_driver = {
268062306a36Sopenharmony_ci	.driver = {
268162306a36Sopenharmony_ci		.name = "dbri",
268262306a36Sopenharmony_ci		.of_match_table = dbri_match,
268362306a36Sopenharmony_ci	},
268462306a36Sopenharmony_ci	.probe		= dbri_probe,
268562306a36Sopenharmony_ci	.remove_new	= dbri_remove,
268662306a36Sopenharmony_ci};
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_cimodule_platform_driver(dbri_sbus_driver);
2689