18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for DBRI sound chip found on Sparcs.
48c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2005 Martin Habets (mhabets@users.sourceforge.net)
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Converted to ring buffered version by Krzysztof Helt (krzysztof.h1@wp.pl)
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Based entirely upon drivers/sbus/audio/dbri.c which is:
98c2ecf20Sopenharmony_ci * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
108c2ecf20Sopenharmony_ci * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This is the low level driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
138c2ecf20Sopenharmony_ci * on Sun SPARCStation 10, 20, LX and Voyager models.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel
168c2ecf20Sopenharmony_ci *   data time multiplexer with ISDN support (aka T7259)
178c2ecf20Sopenharmony_ci *   Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel.
188c2ecf20Sopenharmony_ci *   CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?).
198c2ecf20Sopenharmony_ci *   Documentation:
208c2ecf20Sopenharmony_ci *   - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Transceiver" from
218c2ecf20Sopenharmony_ci *     Sparc Technology Business (courtesy of Sun Support)
228c2ecf20Sopenharmony_ci *   - Data sheet of the T7903, a newer but very similar ISA bus equivalent
238c2ecf20Sopenharmony_ci *     available from the Lucent (formerly AT&T microelectronics) home
248c2ecf20Sopenharmony_ci *     page.
258c2ecf20Sopenharmony_ci *   - https://www.freesoft.org/Linux/DBRI/
268c2ecf20Sopenharmony_ci * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec
278c2ecf20Sopenharmony_ci *   Interfaces: CHI, Audio In & Out, 2 bits parallel
288c2ecf20Sopenharmony_ci *   Documentation: from the Crystal Semiconductor home page.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * The DBRI is a 32 pipe machine, each pipe can transfer some bits between
318c2ecf20Sopenharmony_ci * memory and a serial device (long pipes, no. 0-15) or between two serial
328c2ecf20Sopenharmony_ci * devices (short pipes, no. 16-31), or simply send a fixed data to a serial
338c2ecf20Sopenharmony_ci * device (short pipes).
348c2ecf20Sopenharmony_ci * A timeslot defines the bit-offset and no. of bits read from a serial device.
358c2ecf20Sopenharmony_ci * The timeslots are linked to 6 circular lists, one for each direction for
368c2ecf20Sopenharmony_ci * each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes
378c2ecf20Sopenharmony_ci * (the second one is a monitor/tee pipe, valid only for serial input).
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * The mmcodec is connected via the CHI bus and needs the data & some
408c2ecf20Sopenharmony_ci * parameters (volume, output selection) time multiplexed in 8 byte
418c2ecf20Sopenharmony_ci * chunks. It also has a control mode, which serves for audio format setting.
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
448c2ecf20Sopenharmony_ci * the same CHI bus, so I thought perhaps it is possible to use the on-board
458c2ecf20Sopenharmony_ci * & the speakerbox codec simultaneously, giving 2 (not very independent :-)
468c2ecf20Sopenharmony_ci * audio devices. But the SUN HW group decided against it, at least on my
478c2ecf20Sopenharmony_ci * LX the speakerbox connector has at least 1 pin missing and 1 wrongly
488c2ecf20Sopenharmony_ci * connected.
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * I've tried to stick to the following function naming conventions:
518c2ecf20Sopenharmony_ci * snd_*	ALSA stuff
528c2ecf20Sopenharmony_ci * cs4215_*	CS4215 codec specific stuff
538c2ecf20Sopenharmony_ci * dbri_*	DBRI high-level stuff
548c2ecf20Sopenharmony_ci * other	DBRI low-level stuff
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
588c2ecf20Sopenharmony_ci#include <linux/delay.h>
598c2ecf20Sopenharmony_ci#include <linux/irq.h>
608c2ecf20Sopenharmony_ci#include <linux/io.h>
618c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
628c2ecf20Sopenharmony_ci#include <linux/gfp.h>
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#include <sound/core.h>
658c2ecf20Sopenharmony_ci#include <sound/pcm.h>
668c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
678c2ecf20Sopenharmony_ci#include <sound/info.h>
688c2ecf20Sopenharmony_ci#include <sound/control.h>
698c2ecf20Sopenharmony_ci#include <sound/initval.h>
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#include <linux/of.h>
728c2ecf20Sopenharmony_ci#include <linux/of_device.h>
738c2ecf20Sopenharmony_ci#include <linux/atomic.h>
748c2ecf20Sopenharmony_ci#include <linux/module.h>
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun DBRI");
788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
798c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Sun,DBRI}}");
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
828c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
838c2ecf20Sopenharmony_ci/* Enable this card */
848c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
878c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Sun DBRI soundcard.");
888c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
898c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Sun DBRI soundcard.");
908c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
918c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard.");
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#undef DBRI_DEBUG
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define D_INT	(1<<0)
968c2ecf20Sopenharmony_ci#define D_GEN	(1<<1)
978c2ecf20Sopenharmony_ci#define D_CMD	(1<<2)
988c2ecf20Sopenharmony_ci#define D_MM	(1<<3)
998c2ecf20Sopenharmony_ci#define D_USR	(1<<4)
1008c2ecf20Sopenharmony_ci#define D_DESC	(1<<5)
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int dbri_debug;
1038c2ecf20Sopenharmony_cimodule_param(dbri_debug, int, 0644);
1048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard.");
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#ifdef DBRI_DEBUG
1078c2ecf20Sopenharmony_cistatic const char * const cmds[] = {
1088c2ecf20Sopenharmony_ci	"WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
1098c2ecf20Sopenharmony_ci	"SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci#define dprintk(a, x...) if (dbri_debug & a) printk(KERN_DEBUG x)
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#else
1158c2ecf20Sopenharmony_ci#define dprintk(a, x...) do { } while (0)
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci#endif				/* DBRI_DEBUG */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci#define DBRI_CMD(cmd, intr, value) ((cmd << 28) |	\
1208c2ecf20Sopenharmony_ci				    (intr << 27) |	\
1218c2ecf20Sopenharmony_ci				    value)
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/***************************************************************************
1248c2ecf20Sopenharmony_ci	CS4215 specific definitions and structures
1258c2ecf20Sopenharmony_ci****************************************************************************/
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistruct cs4215 {
1288c2ecf20Sopenharmony_ci	__u8 data[4];		/* Data mode: Time slots 5-8 */
1298c2ecf20Sopenharmony_ci	__u8 ctrl[4];		/* Ctrl mode: Time slots 1-4 */
1308c2ecf20Sopenharmony_ci	__u8 onboard;
1318c2ecf20Sopenharmony_ci	__u8 offset;		/* Bit offset from frame sync to time slot 1 */
1328c2ecf20Sopenharmony_ci	volatile __u32 status;
1338c2ecf20Sopenharmony_ci	volatile __u32 version;
1348c2ecf20Sopenharmony_ci	__u8 precision;		/* In bits, either 8 or 16 */
1358c2ecf20Sopenharmony_ci	__u8 channels;		/* 1 or 2 */
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/*
1398c2ecf20Sopenharmony_ci * Control mode first
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/* Time Slot 1, Status register */
1438c2ecf20Sopenharmony_ci#define CS4215_CLB	(1<<2)	/* Control Latch Bit */
1448c2ecf20Sopenharmony_ci#define CS4215_OLB	(1<<3)	/* 1: line: 2.0V, speaker 4V */
1458c2ecf20Sopenharmony_ci				/* 0: line: 2.8V, speaker 8V */
1468c2ecf20Sopenharmony_ci#define CS4215_MLB	(1<<4)	/* 1: Microphone: 20dB gain disabled */
1478c2ecf20Sopenharmony_ci#define CS4215_RSRVD_1  (1<<5)
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/* Time Slot 2, Data Format Register */
1508c2ecf20Sopenharmony_ci#define CS4215_DFR_LINEAR16	0
1518c2ecf20Sopenharmony_ci#define CS4215_DFR_ULAW		1
1528c2ecf20Sopenharmony_ci#define CS4215_DFR_ALAW		2
1538c2ecf20Sopenharmony_ci#define CS4215_DFR_LINEAR8	3
1548c2ecf20Sopenharmony_ci#define CS4215_DFR_STEREO	(1<<2)
1558c2ecf20Sopenharmony_cistatic struct {
1568c2ecf20Sopenharmony_ci	unsigned short freq;
1578c2ecf20Sopenharmony_ci	unsigned char xtal;
1588c2ecf20Sopenharmony_ci	unsigned char csval;
1598c2ecf20Sopenharmony_ci} CS4215_FREQ[] = {
1608c2ecf20Sopenharmony_ci	{  8000, (1 << 4), (0 << 3) },
1618c2ecf20Sopenharmony_ci	{ 16000, (1 << 4), (1 << 3) },
1628c2ecf20Sopenharmony_ci	{ 27429, (1 << 4), (2 << 3) },	/* Actually 24428.57 */
1638c2ecf20Sopenharmony_ci	{ 32000, (1 << 4), (3 << 3) },
1648c2ecf20Sopenharmony_ci     /* {    NA, (1 << 4), (4 << 3) }, */
1658c2ecf20Sopenharmony_ci     /* {    NA, (1 << 4), (5 << 3) }, */
1668c2ecf20Sopenharmony_ci	{ 48000, (1 << 4), (6 << 3) },
1678c2ecf20Sopenharmony_ci	{  9600, (1 << 4), (7 << 3) },
1688c2ecf20Sopenharmony_ci	{  5512, (2 << 4), (0 << 3) },	/* Actually 5512.5 */
1698c2ecf20Sopenharmony_ci	{ 11025, (2 << 4), (1 << 3) },
1708c2ecf20Sopenharmony_ci	{ 18900, (2 << 4), (2 << 3) },
1718c2ecf20Sopenharmony_ci	{ 22050, (2 << 4), (3 << 3) },
1728c2ecf20Sopenharmony_ci	{ 37800, (2 << 4), (4 << 3) },
1738c2ecf20Sopenharmony_ci	{ 44100, (2 << 4), (5 << 3) },
1748c2ecf20Sopenharmony_ci	{ 33075, (2 << 4), (6 << 3) },
1758c2ecf20Sopenharmony_ci	{  6615, (2 << 4), (7 << 3) },
1768c2ecf20Sopenharmony_ci	{ 0, 0, 0}
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci#define CS4215_HPF	(1<<7)	/* High Pass Filter, 1: Enabled */
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci#define CS4215_12_MASK	0xfcbf	/* Mask off reserved bits in slot 1 & 2 */
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/* Time Slot 3, Serial Port Control register */
1848c2ecf20Sopenharmony_ci#define CS4215_XEN	(1<<0)	/* 0: Enable serial output */
1858c2ecf20Sopenharmony_ci#define CS4215_XCLK	(1<<1)	/* 1: Master mode: Generate SCLK */
1868c2ecf20Sopenharmony_ci#define CS4215_BSEL_64	(0<<2)	/* Bitrate: 64 bits per frame */
1878c2ecf20Sopenharmony_ci#define CS4215_BSEL_128	(1<<2)
1888c2ecf20Sopenharmony_ci#define CS4215_BSEL_256	(2<<2)
1898c2ecf20Sopenharmony_ci#define CS4215_MCK_MAST (0<<4)	/* Master clock */
1908c2ecf20Sopenharmony_ci#define CS4215_MCK_XTL1 (1<<4)	/* 24.576 MHz clock source */
1918c2ecf20Sopenharmony_ci#define CS4215_MCK_XTL2 (2<<4)	/* 16.9344 MHz clock source */
1928c2ecf20Sopenharmony_ci#define CS4215_MCK_CLK1 (3<<4)	/* Clockin, 256 x Fs */
1938c2ecf20Sopenharmony_ci#define CS4215_MCK_CLK2 (4<<4)	/* Clockin, see DFR */
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/* Time Slot 4, Test Register */
1968c2ecf20Sopenharmony_ci#define CS4215_DAD	(1<<0)	/* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */
1978c2ecf20Sopenharmony_ci#define CS4215_ENL	(1<<1)	/* Enable Loopback Testing */
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/* Time Slot 5, Parallel Port Register */
2008c2ecf20Sopenharmony_ci/* Read only here and the same as the in data mode */
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* Time Slot 6, Reserved  */
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* Time Slot 7, Version Register  */
2058c2ecf20Sopenharmony_ci#define CS4215_VERSION_MASK 0xf	/* Known versions 0/C, 1/D, 2/E */
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* Time Slot 8, Reserved  */
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/*
2108c2ecf20Sopenharmony_ci * Data mode
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_ci/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data  */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/* Time Slot 5, Output Setting  */
2158c2ecf20Sopenharmony_ci#define CS4215_LO(v)	v	/* Left Output Attenuation 0x3f: -94.5 dB */
2168c2ecf20Sopenharmony_ci#define CS4215_LE	(1<<6)	/* Line Out Enable */
2178c2ecf20Sopenharmony_ci#define CS4215_HE	(1<<7)	/* Headphone Enable */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/* Time Slot 6, Output Setting  */
2208c2ecf20Sopenharmony_ci#define CS4215_RO(v)	v	/* Right Output Attenuation 0x3f: -94.5 dB */
2218c2ecf20Sopenharmony_ci#define CS4215_SE	(1<<6)	/* Speaker Enable */
2228c2ecf20Sopenharmony_ci#define CS4215_ADI	(1<<7)	/* A/D Data Invalid: Busy in calibration */
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/* Time Slot 7, Input Setting */
2258c2ecf20Sopenharmony_ci#define CS4215_LG(v)	v	/* Left Gain Setting 0xf: 22.5 dB */
2268c2ecf20Sopenharmony_ci#define CS4215_IS	(1<<4)	/* Input Select: 1=Microphone, 0=Line */
2278c2ecf20Sopenharmony_ci#define CS4215_OVR	(1<<5)	/* 1: Over range condition occurred */
2288c2ecf20Sopenharmony_ci#define CS4215_PIO0	(1<<6)	/* Parallel I/O 0 */
2298c2ecf20Sopenharmony_ci#define CS4215_PIO1	(1<<7)
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/* Time Slot 8, Input Setting */
2328c2ecf20Sopenharmony_ci#define CS4215_RG(v)	v	/* Right Gain Setting 0xf: 22.5 dB */
2338c2ecf20Sopenharmony_ci#define CS4215_MA(v)	(v<<4)	/* Monitor Path Attenuation 0xf: mute */
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/***************************************************************************
2368c2ecf20Sopenharmony_ci		DBRI specific definitions and structures
2378c2ecf20Sopenharmony_ci****************************************************************************/
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/* DBRI main registers */
2408c2ecf20Sopenharmony_ci#define REG0	0x00		/* Status and Control */
2418c2ecf20Sopenharmony_ci#define REG1	0x04		/* Mode and Interrupt */
2428c2ecf20Sopenharmony_ci#define REG2	0x08		/* Parallel IO */
2438c2ecf20Sopenharmony_ci#define REG3	0x0c		/* Test */
2448c2ecf20Sopenharmony_ci#define REG8	0x20		/* Command Queue Pointer */
2458c2ecf20Sopenharmony_ci#define REG9	0x24		/* Interrupt Queue Pointer */
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci#define DBRI_NO_CMDS	64
2488c2ecf20Sopenharmony_ci#define DBRI_INT_BLK	64
2498c2ecf20Sopenharmony_ci#define DBRI_NO_DESCS	64
2508c2ecf20Sopenharmony_ci#define DBRI_NO_PIPES	32
2518c2ecf20Sopenharmony_ci#define DBRI_MAX_PIPE	(DBRI_NO_PIPES - 1)
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci#define DBRI_REC	0
2548c2ecf20Sopenharmony_ci#define DBRI_PLAY	1
2558c2ecf20Sopenharmony_ci#define DBRI_NO_STREAMS	2
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/* One transmit/receive descriptor */
2588c2ecf20Sopenharmony_ci/* When ba != 0 descriptor is used */
2598c2ecf20Sopenharmony_cistruct dbri_mem {
2608c2ecf20Sopenharmony_ci	volatile __u32 word1;
2618c2ecf20Sopenharmony_ci	__u32 ba;	/* Transmit/Receive Buffer Address */
2628c2ecf20Sopenharmony_ci	__u32 nda;	/* Next Descriptor Address */
2638c2ecf20Sopenharmony_ci	volatile __u32 word4;
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/* This structure is in a DMA region where it can accessed by both
2678c2ecf20Sopenharmony_ci * the CPU and the DBRI
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_cistruct dbri_dma {
2708c2ecf20Sopenharmony_ci	s32 cmd[DBRI_NO_CMDS];			/* Place for commands */
2718c2ecf20Sopenharmony_ci	volatile s32 intr[DBRI_INT_BLK];	/* Interrupt field  */
2728c2ecf20Sopenharmony_ci	struct dbri_mem desc[DBRI_NO_DESCS];	/* Xmit/receive descriptors */
2738c2ecf20Sopenharmony_ci};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci#define dbri_dma_off(member, elem)	\
2768c2ecf20Sopenharmony_ci	((u32)(unsigned long)		\
2778c2ecf20Sopenharmony_ci	 (&(((struct dbri_dma *)0)->member[elem])))
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cienum in_or_out { PIPEinput, PIPEoutput };
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistruct dbri_pipe {
2828c2ecf20Sopenharmony_ci	u32 sdp;		/* SDP command word */
2838c2ecf20Sopenharmony_ci	int nextpipe;		/* Next pipe in linked list */
2848c2ecf20Sopenharmony_ci	int length;		/* Length of timeslot (bits) */
2858c2ecf20Sopenharmony_ci	int first_desc;		/* Index of first descriptor */
2868c2ecf20Sopenharmony_ci	int desc;		/* Index of active descriptor */
2878c2ecf20Sopenharmony_ci	volatile __u32 *recv_fixed_ptr;	/* Ptr to receive fixed data */
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/* Per stream (playback or record) information */
2918c2ecf20Sopenharmony_cistruct dbri_streaminfo {
2928c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
2938c2ecf20Sopenharmony_ci	u32 dvma_buffer;	/* Device view of ALSA DMA buffer */
2948c2ecf20Sopenharmony_ci	int size;		/* Size of DMA buffer             */
2958c2ecf20Sopenharmony_ci	size_t offset;		/* offset in user buffer          */
2968c2ecf20Sopenharmony_ci	int pipe;		/* Data pipe used                 */
2978c2ecf20Sopenharmony_ci	int left_gain;		/* mixer elements                 */
2988c2ecf20Sopenharmony_ci	int right_gain;
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/* This structure holds the information for both chips (DBRI & CS4215) */
3028c2ecf20Sopenharmony_cistruct snd_dbri {
3038c2ecf20Sopenharmony_ci	int regs_size, irq;	/* Needed for unload */
3048c2ecf20Sopenharmony_ci	struct platform_device *op;	/* OF device info */
3058c2ecf20Sopenharmony_ci	spinlock_t lock;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	struct dbri_dma *dma;	/* Pointer to our DMA block */
3088c2ecf20Sopenharmony_ci	dma_addr_t dma_dvma;	/* DBRI visible DMA address */
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	void __iomem *regs;	/* dbri HW regs */
3118c2ecf20Sopenharmony_ci	int dbri_irqp;		/* intr queue pointer */
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	struct dbri_pipe pipes[DBRI_NO_PIPES];	/* DBRI's 32 data pipes */
3148c2ecf20Sopenharmony_ci	int next_desc[DBRI_NO_DESCS];		/* Index of next desc, or -1 */
3158c2ecf20Sopenharmony_ci	spinlock_t cmdlock;	/* Protects cmd queue accesses */
3168c2ecf20Sopenharmony_ci	s32 *cmdptr;		/* Pointer to the last queued cmd */
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	int chi_bpf;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	struct cs4215 mm;	/* mmcodec special info */
3218c2ecf20Sopenharmony_ci				/* per stream (playback/record) info */
3228c2ecf20Sopenharmony_ci	struct dbri_streaminfo stream_info[DBRI_NO_STREAMS];
3238c2ecf20Sopenharmony_ci};
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci#define DBRI_MAX_VOLUME		63	/* Output volume */
3268c2ecf20Sopenharmony_ci#define DBRI_MAX_GAIN		15	/* Input gain */
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
3298c2ecf20Sopenharmony_ci#define D_P		(1<<15)	/* Program command & queue pointer valid */
3308c2ecf20Sopenharmony_ci#define D_G		(1<<14)	/* Allow 4-Word SBus Burst */
3318c2ecf20Sopenharmony_ci#define D_S		(1<<13)	/* Allow 16-Word SBus Burst */
3328c2ecf20Sopenharmony_ci#define D_E		(1<<12)	/* Allow 8-Word SBus Burst */
3338c2ecf20Sopenharmony_ci#define D_X		(1<<7)	/* Sanity Timer Disable */
3348c2ecf20Sopenharmony_ci#define D_T		(1<<6)	/* Permit activation of the TE interface */
3358c2ecf20Sopenharmony_ci#define D_N		(1<<5)	/* Permit activation of the NT interface */
3368c2ecf20Sopenharmony_ci#define D_C		(1<<4)	/* Permit activation of the CHI interface */
3378c2ecf20Sopenharmony_ci#define D_F		(1<<3)	/* Force Sanity Timer Time-Out */
3388c2ecf20Sopenharmony_ci#define D_D		(1<<2)	/* Disable Master Mode */
3398c2ecf20Sopenharmony_ci#define D_H		(1<<1)	/* Halt for Analysis */
3408c2ecf20Sopenharmony_ci#define D_R		(1<<0)	/* Soft Reset */
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */
3438c2ecf20Sopenharmony_ci#define D_LITTLE_END	(1<<8)	/* Byte Order */
3448c2ecf20Sopenharmony_ci#define D_BIG_END	(0<<8)	/* Byte Order */
3458c2ecf20Sopenharmony_ci#define D_MRR		(1<<4)	/* Multiple Error Ack on SBus (read only) */
3468c2ecf20Sopenharmony_ci#define D_MLE		(1<<3)	/* Multiple Late Error on SBus (read only) */
3478c2ecf20Sopenharmony_ci#define D_LBG		(1<<2)	/* Lost Bus Grant on SBus (read only) */
3488c2ecf20Sopenharmony_ci#define D_MBE		(1<<1)	/* Burst Error on SBus (read only) */
3498c2ecf20Sopenharmony_ci#define D_IR		(1<<0)	/* Interrupt Indicator (read only) */
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */
3528c2ecf20Sopenharmony_ci#define D_ENPIO3	(1<<7)	/* Enable Pin 3 */
3538c2ecf20Sopenharmony_ci#define D_ENPIO2	(1<<6)	/* Enable Pin 2 */
3548c2ecf20Sopenharmony_ci#define D_ENPIO1	(1<<5)	/* Enable Pin 1 */
3558c2ecf20Sopenharmony_ci#define D_ENPIO0	(1<<4)	/* Enable Pin 0 */
3568c2ecf20Sopenharmony_ci#define D_ENPIO		(0xf0)	/* Enable all the pins */
3578c2ecf20Sopenharmony_ci#define D_PIO3		(1<<3)	/* Pin 3: 1: Data mode, 0: Ctrl mode */
3588c2ecf20Sopenharmony_ci#define D_PIO2		(1<<2)	/* Pin 2: 1: Onboard PDN */
3598c2ecf20Sopenharmony_ci#define D_PIO1		(1<<1)	/* Pin 1: 0: Reset */
3608c2ecf20Sopenharmony_ci#define D_PIO0		(1<<0)	/* Pin 0: 1: Speakerbox PDN */
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/* DBRI Commands (Page 20) */
3638c2ecf20Sopenharmony_ci#define D_WAIT		0x0	/* Stop execution */
3648c2ecf20Sopenharmony_ci#define D_PAUSE		0x1	/* Flush long pipes */
3658c2ecf20Sopenharmony_ci#define D_JUMP		0x2	/* New command queue */
3668c2ecf20Sopenharmony_ci#define D_IIQ		0x3	/* Initialize Interrupt Queue */
3678c2ecf20Sopenharmony_ci#define D_REX		0x4	/* Report command execution via interrupt */
3688c2ecf20Sopenharmony_ci#define D_SDP		0x5	/* Setup Data Pipe */
3698c2ecf20Sopenharmony_ci#define D_CDP		0x6	/* Continue Data Pipe (reread NULL Pointer) */
3708c2ecf20Sopenharmony_ci#define D_DTS		0x7	/* Define Time Slot */
3718c2ecf20Sopenharmony_ci#define D_SSP		0x8	/* Set short Data Pipe */
3728c2ecf20Sopenharmony_ci#define D_CHI		0x9	/* Set CHI Global Mode */
3738c2ecf20Sopenharmony_ci#define D_NT		0xa	/* NT Command */
3748c2ecf20Sopenharmony_ci#define D_TE		0xb	/* TE Command */
3758c2ecf20Sopenharmony_ci#define D_CDEC		0xc	/* Codec setup */
3768c2ecf20Sopenharmony_ci#define D_TEST		0xd	/* No comment */
3778c2ecf20Sopenharmony_ci#define D_CDM		0xe	/* CHI Data mode command */
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/* Special bits for some commands */
3808c2ecf20Sopenharmony_ci#define D_PIPE(v)      ((v)<<0)	/* Pipe No.: 0-15 long, 16-21 short */
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci/* Setup Data Pipe */
3838c2ecf20Sopenharmony_ci/* IRM */
3848c2ecf20Sopenharmony_ci#define D_SDP_2SAME	(1<<18)	/* Report 2nd time in a row value received */
3858c2ecf20Sopenharmony_ci#define D_SDP_CHANGE	(2<<18)	/* Report any changes */
3868c2ecf20Sopenharmony_ci#define D_SDP_EVERY	(3<<18)	/* Report any changes */
3878c2ecf20Sopenharmony_ci#define D_SDP_EOL	(1<<17)	/* EOL interrupt enable */
3888c2ecf20Sopenharmony_ci#define D_SDP_IDLE	(1<<16)	/* HDLC idle interrupt enable */
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci/* Pipe data MODE */
3918c2ecf20Sopenharmony_ci#define D_SDP_MEM	(0<<13)	/* To/from memory */
3928c2ecf20Sopenharmony_ci#define D_SDP_HDLC	(2<<13)
3938c2ecf20Sopenharmony_ci#define D_SDP_HDLC_D	(3<<13)	/* D Channel (prio control) */
3948c2ecf20Sopenharmony_ci#define D_SDP_SER	(4<<13)	/* Serial to serial */
3958c2ecf20Sopenharmony_ci#define D_SDP_FIXED	(6<<13)	/* Short only */
3968c2ecf20Sopenharmony_ci#define D_SDP_MODE(v)	((v)&(7<<13))
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci#define D_SDP_TO_SER	(1<<12)	/* Direction */
3998c2ecf20Sopenharmony_ci#define D_SDP_FROM_SER	(0<<12)	/* Direction */
4008c2ecf20Sopenharmony_ci#define D_SDP_MSB	(1<<11)	/* Bit order within Byte */
4018c2ecf20Sopenharmony_ci#define D_SDP_LSB	(0<<11)	/* Bit order within Byte */
4028c2ecf20Sopenharmony_ci#define D_SDP_P		(1<<10)	/* Pointer Valid */
4038c2ecf20Sopenharmony_ci#define D_SDP_A		(1<<8)	/* Abort */
4048c2ecf20Sopenharmony_ci#define D_SDP_C		(1<<7)	/* Clear */
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/* Define Time Slot */
4078c2ecf20Sopenharmony_ci#define D_DTS_VI	(1<<17)	/* Valid Input Time-Slot Descriptor */
4088c2ecf20Sopenharmony_ci#define D_DTS_VO	(1<<16)	/* Valid Output Time-Slot Descriptor */
4098c2ecf20Sopenharmony_ci#define D_DTS_INS	(1<<15)	/* Insert Time Slot */
4108c2ecf20Sopenharmony_ci#define D_DTS_DEL	(0<<15)	/* Delete Time Slot */
4118c2ecf20Sopenharmony_ci#define D_DTS_PRVIN(v) ((v)<<10)	/* Previous In Pipe */
4128c2ecf20Sopenharmony_ci#define D_DTS_PRVOUT(v)        ((v)<<5)	/* Previous Out Pipe */
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/* Time Slot defines */
4158c2ecf20Sopenharmony_ci#define D_TS_LEN(v)	((v)<<24)	/* Number of bits in this time slot */
4168c2ecf20Sopenharmony_ci#define D_TS_CYCLE(v)	((v)<<14)	/* Bit Count at start of TS */
4178c2ecf20Sopenharmony_ci#define D_TS_DI		(1<<13)	/* Data Invert */
4188c2ecf20Sopenharmony_ci#define D_TS_1CHANNEL	(0<<10)	/* Single Channel / Normal mode */
4198c2ecf20Sopenharmony_ci#define D_TS_MONITOR	(2<<10)	/* Monitor pipe */
4208c2ecf20Sopenharmony_ci#define D_TS_NONCONTIG	(3<<10)	/* Non contiguous mode */
4218c2ecf20Sopenharmony_ci#define D_TS_ANCHOR	(7<<10)	/* Starting short pipes */
4228c2ecf20Sopenharmony_ci#define D_TS_MON(v)    ((v)<<5)	/* Monitor Pipe */
4238c2ecf20Sopenharmony_ci#define D_TS_NEXT(v)   ((v)<<0)	/* Pipe no.: 0-15 long, 16-21 short */
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci/* Concentration Highway Interface Modes */
4268c2ecf20Sopenharmony_ci#define D_CHI_CHICM(v)	((v)<<16)	/* Clock mode */
4278c2ecf20Sopenharmony_ci#define D_CHI_IR	(1<<15)	/* Immediate Interrupt Report */
4288c2ecf20Sopenharmony_ci#define D_CHI_EN	(1<<14)	/* CHIL Interrupt enabled */
4298c2ecf20Sopenharmony_ci#define D_CHI_OD	(1<<13)	/* Open Drain Enable */
4308c2ecf20Sopenharmony_ci#define D_CHI_FE	(1<<12)	/* Sample CHIFS on Rising Frame Edge */
4318c2ecf20Sopenharmony_ci#define D_CHI_FD	(1<<11)	/* Frame Drive */
4328c2ecf20Sopenharmony_ci#define D_CHI_BPF(v)	((v)<<0)	/* Bits per Frame */
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci/* NT: These are here for completeness */
4358c2ecf20Sopenharmony_ci#define D_NT_FBIT	(1<<17)	/* Frame Bit */
4368c2ecf20Sopenharmony_ci#define D_NT_NBF	(1<<16)	/* Number of bad frames to loose framing */
4378c2ecf20Sopenharmony_ci#define D_NT_IRM_IMM	(1<<15)	/* Interrupt Report & Mask: Immediate */
4388c2ecf20Sopenharmony_ci#define D_NT_IRM_EN	(1<<14)	/* Interrupt Report & Mask: Enable */
4398c2ecf20Sopenharmony_ci#define D_NT_ISNT	(1<<13)	/* Configure interface as NT */
4408c2ecf20Sopenharmony_ci#define D_NT_FT		(1<<12)	/* Fixed Timing */
4418c2ecf20Sopenharmony_ci#define D_NT_EZ		(1<<11)	/* Echo Channel is Zeros */
4428c2ecf20Sopenharmony_ci#define D_NT_IFA	(1<<10)	/* Inhibit Final Activation */
4438c2ecf20Sopenharmony_ci#define D_NT_ACT	(1<<9)	/* Activate Interface */
4448c2ecf20Sopenharmony_ci#define D_NT_MFE	(1<<8)	/* Multiframe Enable */
4458c2ecf20Sopenharmony_ci#define D_NT_RLB(v)	((v)<<5)	/* Remote Loopback */
4468c2ecf20Sopenharmony_ci#define D_NT_LLB(v)	((v)<<2)	/* Local Loopback */
4478c2ecf20Sopenharmony_ci#define D_NT_FACT	(1<<1)	/* Force Activation */
4488c2ecf20Sopenharmony_ci#define D_NT_ABV	(1<<0)	/* Activate Bipolar Violation */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/* Codec Setup */
4518c2ecf20Sopenharmony_ci#define D_CDEC_CK(v)	((v)<<24)	/* Clock Select */
4528c2ecf20Sopenharmony_ci#define D_CDEC_FED(v)	((v)<<12)	/* FSCOD Falling Edge Delay */
4538c2ecf20Sopenharmony_ci#define D_CDEC_RED(v)	((v)<<0)	/* FSCOD Rising Edge Delay */
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci/* Test */
4568c2ecf20Sopenharmony_ci#define D_TEST_RAM(v)	((v)<<16)	/* RAM Pointer */
4578c2ecf20Sopenharmony_ci#define D_TEST_SIZE(v)	((v)<<11)	/* */
4588c2ecf20Sopenharmony_ci#define D_TEST_ROMONOFF	0x5	/* Toggle ROM opcode monitor on/off */
4598c2ecf20Sopenharmony_ci#define D_TEST_PROC	0x6	/* Microprocessor test */
4608c2ecf20Sopenharmony_ci#define D_TEST_SER	0x7	/* Serial-Controller test */
4618c2ecf20Sopenharmony_ci#define D_TEST_RAMREAD	0x8	/* Copy from Ram to system memory */
4628c2ecf20Sopenharmony_ci#define D_TEST_RAMWRITE	0x9	/* Copy into Ram from system memory */
4638c2ecf20Sopenharmony_ci#define D_TEST_RAMBIST	0xa	/* RAM Built-In Self Test */
4648c2ecf20Sopenharmony_ci#define D_TEST_MCBIST	0xb	/* Microcontroller Built-In Self Test */
4658c2ecf20Sopenharmony_ci#define D_TEST_DUMP	0xe	/* ROM Dump */
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci/* CHI Data Mode */
4688c2ecf20Sopenharmony_ci#define D_CDM_THI	(1 << 8)	/* Transmit Data on CHIDR Pin */
4698c2ecf20Sopenharmony_ci#define D_CDM_RHI	(1 << 7)	/* Receive Data on CHIDX Pin */
4708c2ecf20Sopenharmony_ci#define D_CDM_RCE	(1 << 6)	/* Receive on Rising Edge of CHICK */
4718c2ecf20Sopenharmony_ci#define D_CDM_XCE	(1 << 2) /* Transmit Data on Rising Edge of CHICK */
4728c2ecf20Sopenharmony_ci#define D_CDM_XEN	(1 << 1)	/* Transmit Highway Enable */
4738c2ecf20Sopenharmony_ci#define D_CDM_REN	(1 << 0)	/* Receive Highway Enable */
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/* The Interrupts */
4768c2ecf20Sopenharmony_ci#define D_INTR_BRDY	1	/* Buffer Ready for processing */
4778c2ecf20Sopenharmony_ci#define D_INTR_MINT	2	/* Marked Interrupt in RD/TD */
4788c2ecf20Sopenharmony_ci#define D_INTR_IBEG	3	/* Flag to idle transition detected (HDLC) */
4798c2ecf20Sopenharmony_ci#define D_INTR_IEND	4	/* Idle to flag transition detected (HDLC) */
4808c2ecf20Sopenharmony_ci#define D_INTR_EOL	5	/* End of List */
4818c2ecf20Sopenharmony_ci#define D_INTR_CMDI	6	/* Command has bean read */
4828c2ecf20Sopenharmony_ci#define D_INTR_XCMP	8	/* Transmission of frame complete */
4838c2ecf20Sopenharmony_ci#define D_INTR_SBRI	9	/* BRI status change info */
4848c2ecf20Sopenharmony_ci#define D_INTR_FXDT	10	/* Fixed data change */
4858c2ecf20Sopenharmony_ci#define D_INTR_CHIL	11	/* CHI lost frame sync (channel 36 only) */
4868c2ecf20Sopenharmony_ci#define D_INTR_COLL	11	/* Unrecoverable D-Channel collision */
4878c2ecf20Sopenharmony_ci#define D_INTR_DBYT	12	/* Dropped by frame slip */
4888c2ecf20Sopenharmony_ci#define D_INTR_RBYT	13	/* Repeated by frame slip */
4898c2ecf20Sopenharmony_ci#define D_INTR_LINT	14	/* Lost Interrupt */
4908c2ecf20Sopenharmony_ci#define D_INTR_UNDR	15	/* DMA underrun */
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci#define D_INTR_TE	32
4938c2ecf20Sopenharmony_ci#define D_INTR_NT	34
4948c2ecf20Sopenharmony_ci#define D_INTR_CHI	36
4958c2ecf20Sopenharmony_ci#define D_INTR_CMD	38
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci#define D_INTR_GETCHAN(v)	(((v) >> 24) & 0x3f)
4988c2ecf20Sopenharmony_ci#define D_INTR_GETCODE(v)	(((v) >> 20) & 0xf)
4998c2ecf20Sopenharmony_ci#define D_INTR_GETCMD(v)	(((v) >> 16) & 0xf)
5008c2ecf20Sopenharmony_ci#define D_INTR_GETVAL(v)	((v) & 0xffff)
5018c2ecf20Sopenharmony_ci#define D_INTR_GETRVAL(v)	((v) & 0xfffff)
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci#define D_P_0		0	/* TE receive anchor */
5048c2ecf20Sopenharmony_ci#define D_P_1		1	/* TE transmit anchor */
5058c2ecf20Sopenharmony_ci#define D_P_2		2	/* NT transmit anchor */
5068c2ecf20Sopenharmony_ci#define D_P_3		3	/* NT receive anchor */
5078c2ecf20Sopenharmony_ci#define D_P_4		4	/* CHI send data */
5088c2ecf20Sopenharmony_ci#define D_P_5		5	/* CHI receive data */
5098c2ecf20Sopenharmony_ci#define D_P_6		6	/* */
5108c2ecf20Sopenharmony_ci#define D_P_7		7	/* */
5118c2ecf20Sopenharmony_ci#define D_P_8		8	/* */
5128c2ecf20Sopenharmony_ci#define D_P_9		9	/* */
5138c2ecf20Sopenharmony_ci#define D_P_10		10	/* */
5148c2ecf20Sopenharmony_ci#define D_P_11		11	/* */
5158c2ecf20Sopenharmony_ci#define D_P_12		12	/* */
5168c2ecf20Sopenharmony_ci#define D_P_13		13	/* */
5178c2ecf20Sopenharmony_ci#define D_P_14		14	/* */
5188c2ecf20Sopenharmony_ci#define D_P_15		15	/* */
5198c2ecf20Sopenharmony_ci#define D_P_16		16	/* CHI anchor pipe */
5208c2ecf20Sopenharmony_ci#define D_P_17		17	/* CHI send */
5218c2ecf20Sopenharmony_ci#define D_P_18		18	/* CHI receive */
5228c2ecf20Sopenharmony_ci#define D_P_19		19	/* CHI receive */
5238c2ecf20Sopenharmony_ci#define D_P_20		20	/* CHI receive */
5248c2ecf20Sopenharmony_ci#define D_P_21		21	/* */
5258c2ecf20Sopenharmony_ci#define D_P_22		22	/* */
5268c2ecf20Sopenharmony_ci#define D_P_23		23	/* */
5278c2ecf20Sopenharmony_ci#define D_P_24		24	/* */
5288c2ecf20Sopenharmony_ci#define D_P_25		25	/* */
5298c2ecf20Sopenharmony_ci#define D_P_26		26	/* */
5308c2ecf20Sopenharmony_ci#define D_P_27		27	/* */
5318c2ecf20Sopenharmony_ci#define D_P_28		28	/* */
5328c2ecf20Sopenharmony_ci#define D_P_29		29	/* */
5338c2ecf20Sopenharmony_ci#define D_P_30		30	/* */
5348c2ecf20Sopenharmony_ci#define D_P_31		31	/* */
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci/* Transmit descriptor defines */
5378c2ecf20Sopenharmony_ci#define DBRI_TD_F	(1 << 31)	/* End of Frame */
5388c2ecf20Sopenharmony_ci#define DBRI_TD_D	(1 << 30)	/* Do not append CRC */
5398c2ecf20Sopenharmony_ci#define DBRI_TD_CNT(v)	((v) << 16) /* Number of valid bytes in the buffer */
5408c2ecf20Sopenharmony_ci#define DBRI_TD_B	(1 << 15)	/* Final interrupt */
5418c2ecf20Sopenharmony_ci#define DBRI_TD_M	(1 << 14)	/* Marker interrupt */
5428c2ecf20Sopenharmony_ci#define DBRI_TD_I	(1 << 13)	/* Transmit Idle Characters */
5438c2ecf20Sopenharmony_ci#define DBRI_TD_FCNT(v)	(v)		/* Flag Count */
5448c2ecf20Sopenharmony_ci#define DBRI_TD_UNR	(1 << 3) /* Underrun: transmitter is out of data */
5458c2ecf20Sopenharmony_ci#define DBRI_TD_ABT	(1 << 2)	/* Abort: frame aborted */
5468c2ecf20Sopenharmony_ci#define DBRI_TD_TBC	(1 << 0)	/* Transmit buffer Complete */
5478c2ecf20Sopenharmony_ci#define DBRI_TD_STATUS(v)       ((v) & 0xff)	/* Transmit status */
5488c2ecf20Sopenharmony_ci			/* Maximum buffer size per TD: almost 8KB */
5498c2ecf20Sopenharmony_ci#define DBRI_TD_MAXCNT	((1 << 13) - 4)
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci/* Receive descriptor defines */
5528c2ecf20Sopenharmony_ci#define DBRI_RD_F	(1 << 31)	/* End of Frame */
5538c2ecf20Sopenharmony_ci#define DBRI_RD_C	(1 << 30)	/* Completed buffer */
5548c2ecf20Sopenharmony_ci#define DBRI_RD_B	(1 << 15)	/* Final interrupt */
5558c2ecf20Sopenharmony_ci#define DBRI_RD_M	(1 << 14)	/* Marker interrupt */
5568c2ecf20Sopenharmony_ci#define DBRI_RD_BCNT(v)	(v)		/* Buffer size */
5578c2ecf20Sopenharmony_ci#define DBRI_RD_CRC	(1 << 7)	/* 0: CRC is correct */
5588c2ecf20Sopenharmony_ci#define DBRI_RD_BBC	(1 << 6)	/* 1: Bad Byte received */
5598c2ecf20Sopenharmony_ci#define DBRI_RD_ABT	(1 << 5)	/* Abort: frame aborted */
5608c2ecf20Sopenharmony_ci#define DBRI_RD_OVRN	(1 << 3)	/* Overrun: data lost */
5618c2ecf20Sopenharmony_ci#define DBRI_RD_STATUS(v)      ((v) & 0xff)	/* Receive status */
5628c2ecf20Sopenharmony_ci#define DBRI_RD_CNT(v) (((v) >> 16) & 0x1fff)	/* Valid bytes in the buffer */
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci/* stream_info[] access */
5658c2ecf20Sopenharmony_ci/* Translate the ALSA direction into the array index */
5668c2ecf20Sopenharmony_ci#define DBRI_STREAMNO(substream)				\
5678c2ecf20Sopenharmony_ci		(substream->stream ==				\
5688c2ecf20Sopenharmony_ci		 SNDRV_PCM_STREAM_PLAYBACK ? DBRI_PLAY: DBRI_REC)
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci/* Return a pointer to dbri_streaminfo */
5718c2ecf20Sopenharmony_ci#define DBRI_STREAM(dbri, substream)	\
5728c2ecf20Sopenharmony_ci		&dbri->stream_info[DBRI_STREAMNO(substream)]
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci/*
5758c2ecf20Sopenharmony_ci * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
5768c2ecf20Sopenharmony_ci * So we have to reverse the bits. Note: not all bit lengths are supported
5778c2ecf20Sopenharmony_ci */
5788c2ecf20Sopenharmony_cistatic __u32 reverse_bytes(__u32 b, int len)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	switch (len) {
5818c2ecf20Sopenharmony_ci	case 32:
5828c2ecf20Sopenharmony_ci		b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
5838c2ecf20Sopenharmony_ci		fallthrough;
5848c2ecf20Sopenharmony_ci	case 16:
5858c2ecf20Sopenharmony_ci		b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8);
5868c2ecf20Sopenharmony_ci		fallthrough;
5878c2ecf20Sopenharmony_ci	case 8:
5888c2ecf20Sopenharmony_ci		b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4);
5898c2ecf20Sopenharmony_ci		fallthrough;
5908c2ecf20Sopenharmony_ci	case 4:
5918c2ecf20Sopenharmony_ci		b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2);
5928c2ecf20Sopenharmony_ci		fallthrough;
5938c2ecf20Sopenharmony_ci	case 2:
5948c2ecf20Sopenharmony_ci		b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
5958c2ecf20Sopenharmony_ci	case 1:
5968c2ecf20Sopenharmony_ci	case 0:
5978c2ecf20Sopenharmony_ci		break;
5988c2ecf20Sopenharmony_ci	default:
5998c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI reverse_bytes: unsupported length\n");
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return b;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci/*
6068c2ecf20Sopenharmony_ci****************************************************************************
6078c2ecf20Sopenharmony_ci************** DBRI initialization and command synchronization *************
6088c2ecf20Sopenharmony_ci****************************************************************************
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ciCommands are sent to the DBRI by building a list of them in memory,
6118c2ecf20Sopenharmony_cithen writing the address of the first list item to DBRI register 8.
6128c2ecf20Sopenharmony_ciThe list is terminated with a WAIT command, which generates a
6138c2ecf20Sopenharmony_ciCPU interrupt to signal completion.
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ciSince the DBRI can run in parallel with the CPU, several means of
6168c2ecf20Sopenharmony_cisynchronization present themselves. The method implemented here uses
6178c2ecf20Sopenharmony_cithe dbri_cmdwait() to wait for execution of batch of sent commands.
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ciA circular command buffer is used here. A new command is being added
6208c2ecf20Sopenharmony_ciwhile another can be executed. The scheme works by adding two WAIT commands
6218c2ecf20Sopenharmony_ciafter each sent batch of commands. When the next batch is prepared it is
6228c2ecf20Sopenharmony_ciadded after the WAIT commands then the WAITs are replaced with single JUMP
6238c2ecf20Sopenharmony_cicommand to the new batch. Then the DBRI is forced to reread the last WAIT
6248c2ecf20Sopenharmony_cicommand (replaced by the JUMP by then). If the DBRI is still executing
6258c2ecf20Sopenharmony_ciprevious commands the request to reread the WAIT command is ignored.
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ciEvery time a routine wants to write commands to the DBRI, it must
6288c2ecf20Sopenharmony_cifirst call dbri_cmdlock() and get pointer to a free space in
6298c2ecf20Sopenharmony_cidbri->dma->cmd buffer. After this, the commands can be written to
6308c2ecf20Sopenharmony_cithe buffer, and dbri_cmdsend() is called with the final pointer value
6318c2ecf20Sopenharmony_cito send them to the DBRI.
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci*/
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci#define MAXLOOPS 20
6368c2ecf20Sopenharmony_ci/*
6378c2ecf20Sopenharmony_ci * Wait for the current command string to execute
6388c2ecf20Sopenharmony_ci */
6398c2ecf20Sopenharmony_cistatic void dbri_cmdwait(struct snd_dbri *dbri)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	int maxloops = MAXLOOPS;
6428c2ecf20Sopenharmony_ci	unsigned long flags;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* Delay if previous commands are still being processed */
6458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
6468c2ecf20Sopenharmony_ci	while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P)) {
6478c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dbri->lock, flags);
6488c2ecf20Sopenharmony_ci		msleep_interruptible(1);
6498c2ecf20Sopenharmony_ci		spin_lock_irqsave(&dbri->lock, flags);
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (maxloops == 0)
6548c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
6558c2ecf20Sopenharmony_ci	else
6568c2ecf20Sopenharmony_ci		dprintk(D_CMD, "Chip completed command buffer (%d)\n",
6578c2ecf20Sopenharmony_ci			MAXLOOPS - maxloops - 1);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci/*
6608c2ecf20Sopenharmony_ci * Lock the command queue and return pointer to space for len cmd words
6618c2ecf20Sopenharmony_ci * It locks the cmdlock spinlock.
6628c2ecf20Sopenharmony_ci */
6638c2ecf20Sopenharmony_cistatic s32 *dbri_cmdlock(struct snd_dbri *dbri, int len)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	/* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */
6688c2ecf20Sopenharmony_ci	len += 2;
6698c2ecf20Sopenharmony_ci	spin_lock(&dbri->cmdlock);
6708c2ecf20Sopenharmony_ci	if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2)
6718c2ecf20Sopenharmony_ci		return dbri->cmdptr + 2;
6728c2ecf20Sopenharmony_ci	else if (len < sbus_readl(dbri->regs + REG8) - dvma_addr)
6738c2ecf20Sopenharmony_ci		return dbri->dma->cmd;
6748c2ecf20Sopenharmony_ci	else
6758c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: no space for commands.");
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	return NULL;
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci/*
6818c2ecf20Sopenharmony_ci * Send prepared cmd string. It works by writing a JUMP cmd into
6828c2ecf20Sopenharmony_ci * the last WAIT cmd and force DBRI to reread the cmd.
6838c2ecf20Sopenharmony_ci * The JUMP cmd points to the new cmd string.
6848c2ecf20Sopenharmony_ci * It also releases the cmdlock spinlock.
6858c2ecf20Sopenharmony_ci *
6868c2ecf20Sopenharmony_ci * Lock must be held before calling this.
6878c2ecf20Sopenharmony_ci */
6888c2ecf20Sopenharmony_cistatic void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
6918c2ecf20Sopenharmony_ci	s32 tmp, addr;
6928c2ecf20Sopenharmony_ci	static int wait_id = 0;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	wait_id++;
6958c2ecf20Sopenharmony_ci	wait_id &= 0xffff;	/* restrict it to a 16 bit counter. */
6968c2ecf20Sopenharmony_ci	*(cmd) = DBRI_CMD(D_WAIT, 1, wait_id);
6978c2ecf20Sopenharmony_ci	*(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/* Replace the last command with JUMP */
7008c2ecf20Sopenharmony_ci	addr = dvma_addr + (cmd - len - dbri->dma->cmd) * sizeof(s32);
7018c2ecf20Sopenharmony_ci	*(dbri->cmdptr+1) = addr;
7028c2ecf20Sopenharmony_ci	*(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci#ifdef DBRI_DEBUG
7058c2ecf20Sopenharmony_ci	if (cmd > dbri->cmdptr) {
7068c2ecf20Sopenharmony_ci		s32 *ptr;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++)
7098c2ecf20Sopenharmony_ci			dprintk(D_CMD, "cmd: %lx:%08x\n",
7108c2ecf20Sopenharmony_ci				(unsigned long)ptr, *ptr);
7118c2ecf20Sopenharmony_ci	} else {
7128c2ecf20Sopenharmony_ci		s32 *ptr = dbri->cmdptr;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
7158c2ecf20Sopenharmony_ci		ptr++;
7168c2ecf20Sopenharmony_ci		dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
7178c2ecf20Sopenharmony_ci		for (ptr = dbri->dma->cmd; ptr < cmd+2; ptr++)
7188c2ecf20Sopenharmony_ci			dprintk(D_CMD, "cmd: %lx:%08x\n",
7198c2ecf20Sopenharmony_ci				(unsigned long)ptr, *ptr);
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci#endif
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	/* Reread the last command */
7248c2ecf20Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
7258c2ecf20Sopenharmony_ci	tmp |= D_P;
7268c2ecf20Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	dbri->cmdptr = cmd;
7298c2ecf20Sopenharmony_ci	spin_unlock(&dbri->cmdlock);
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci/* Lock must be held when calling this */
7338c2ecf20Sopenharmony_cistatic void dbri_reset(struct snd_dbri *dbri)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	int i;
7368c2ecf20Sopenharmony_ci	u32 tmp;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n",
7398c2ecf20Sopenharmony_ci		sbus_readl(dbri->regs + REG0),
7408c2ecf20Sopenharmony_ci		sbus_readl(dbri->regs + REG2),
7418c2ecf20Sopenharmony_ci		sbus_readl(dbri->regs + REG8), sbus_readl(dbri->regs + REG9));
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	sbus_writel(D_R, dbri->regs + REG0);	/* Soft Reset */
7448c2ecf20Sopenharmony_ci	for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++)
7458c2ecf20Sopenharmony_ci		udelay(10);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	/* A brute approach - DBRI falls back to working burst size by itself
7488c2ecf20Sopenharmony_ci	 * On SS20 D_S does not work, so do not try so high. */
7498c2ecf20Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
7508c2ecf20Sopenharmony_ci	tmp |= D_G | D_E;
7518c2ecf20Sopenharmony_ci	tmp &= ~D_S;
7528c2ecf20Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci/* Lock must not be held before calling this */
7568c2ecf20Sopenharmony_cistatic void dbri_initialize(struct snd_dbri *dbri)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
7598c2ecf20Sopenharmony_ci	s32 *cmd;
7608c2ecf20Sopenharmony_ci	u32 dma_addr;
7618c2ecf20Sopenharmony_ci	unsigned long flags;
7628c2ecf20Sopenharmony_ci	int n;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	dbri_reset(dbri);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* Initialize pipes */
7698c2ecf20Sopenharmony_ci	for (n = 0; n < DBRI_NO_PIPES; n++)
7708c2ecf20Sopenharmony_ci		dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	spin_lock_init(&dbri->cmdlock);
7738c2ecf20Sopenharmony_ci	/*
7748c2ecf20Sopenharmony_ci	 * Initialize the interrupt ring buffer.
7758c2ecf20Sopenharmony_ci	 */
7768c2ecf20Sopenharmony_ci	dma_addr = dvma_addr + dbri_dma_off(intr, 0);
7778c2ecf20Sopenharmony_ci	dbri->dma->intr[0] = dma_addr;
7788c2ecf20Sopenharmony_ci	dbri->dbri_irqp = 1;
7798c2ecf20Sopenharmony_ci	/*
7808c2ecf20Sopenharmony_ci	 * Set up the interrupt queue
7818c2ecf20Sopenharmony_ci	 */
7828c2ecf20Sopenharmony_ci	spin_lock(&dbri->cmdlock);
7838c2ecf20Sopenharmony_ci	cmd = dbri->cmdptr = dbri->dma->cmd;
7848c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
7858c2ecf20Sopenharmony_ci	*(cmd++) = dma_addr;
7868c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
7878c2ecf20Sopenharmony_ci	dbri->cmdptr = cmd;
7888c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
7898c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
7908c2ecf20Sopenharmony_ci	dma_addr = dvma_addr + dbri_dma_off(cmd, 0);
7918c2ecf20Sopenharmony_ci	sbus_writel(dma_addr, dbri->regs + REG8);
7928c2ecf20Sopenharmony_ci	spin_unlock(&dbri->cmdlock);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
7958c2ecf20Sopenharmony_ci	dbri_cmdwait(dbri);
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci/*
7998c2ecf20Sopenharmony_ci****************************************************************************
8008c2ecf20Sopenharmony_ci************************** DBRI data pipe management ***********************
8018c2ecf20Sopenharmony_ci****************************************************************************
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ciWhile DBRI control functions use the command and interrupt buffers, the
8048c2ecf20Sopenharmony_cimain data path takes the form of data pipes, which can be short (command
8058c2ecf20Sopenharmony_ciand interrupt driven), or long (attached to DMA buffers).  These functions
8068c2ecf20Sopenharmony_ciprovide a rudimentary means of setting up and managing the DBRI's pipes,
8078c2ecf20Sopenharmony_cibut the calling functions have to make sure they respect the pipes' linked
8088c2ecf20Sopenharmony_cilist ordering, among other things.  The transmit and receive functions
8098c2ecf20Sopenharmony_cihere interface closely with the transmit and receive interrupt code.
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci*/
8128c2ecf20Sopenharmony_cistatic inline int pipe_active(struct snd_dbri *dbri, int pipe)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	return ((pipe >= 0) && (dbri->pipes[pipe].desc != -1));
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci/* reset_pipe(dbri, pipe)
8188c2ecf20Sopenharmony_ci *
8198c2ecf20Sopenharmony_ci * Called on an in-use pipe to clear anything being transmitted or received
8208c2ecf20Sopenharmony_ci * Lock must be held before calling this.
8218c2ecf20Sopenharmony_ci */
8228c2ecf20Sopenharmony_cistatic void reset_pipe(struct snd_dbri *dbri, int pipe)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	int sdp;
8258c2ecf20Sopenharmony_ci	int desc;
8268c2ecf20Sopenharmony_ci	s32 *cmd;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
8298c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: reset_pipe called with "
8308c2ecf20Sopenharmony_ci			"illegal pipe number\n");
8318c2ecf20Sopenharmony_ci		return;
8328c2ecf20Sopenharmony_ci	}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	sdp = dbri->pipes[pipe].sdp;
8358c2ecf20Sopenharmony_ci	if (sdp == 0) {
8368c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: reset_pipe called "
8378c2ecf20Sopenharmony_ci			"on uninitialized pipe\n");
8388c2ecf20Sopenharmony_ci		return;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 3);
8428c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
8438c2ecf20Sopenharmony_ci	*(cmd++) = 0;
8448c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
8458c2ecf20Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 3);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	desc = dbri->pipes[pipe].first_desc;
8488c2ecf20Sopenharmony_ci	if (desc >= 0)
8498c2ecf20Sopenharmony_ci		do {
8508c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].ba = 0;
8518c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].nda = 0;
8528c2ecf20Sopenharmony_ci			desc = dbri->next_desc[desc];
8538c2ecf20Sopenharmony_ci		} while (desc != -1 && desc != dbri->pipes[pipe].first_desc);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	dbri->pipes[pipe].desc = -1;
8568c2ecf20Sopenharmony_ci	dbri->pipes[pipe].first_desc = -1;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci/*
8608c2ecf20Sopenharmony_ci * Lock must be held before calling this.
8618c2ecf20Sopenharmony_ci */
8628c2ecf20Sopenharmony_cistatic void setup_pipe(struct snd_dbri *dbri, int pipe, int sdp)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
8658c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_pipe called "
8668c2ecf20Sopenharmony_ci			"with illegal pipe number\n");
8678c2ecf20Sopenharmony_ci		return;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	if ((sdp & 0xf800) != sdp) {
8718c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_pipe called "
8728c2ecf20Sopenharmony_ci			"with strange SDP value\n");
8738c2ecf20Sopenharmony_ci		/* sdp &= 0xf800; */
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* If this is a fixed receive pipe, arrange for an interrupt
8778c2ecf20Sopenharmony_ci	 * every time its data changes
8788c2ecf20Sopenharmony_ci	 */
8798c2ecf20Sopenharmony_ci	if (D_SDP_MODE(sdp) == D_SDP_FIXED && !(sdp & D_SDP_TO_SER))
8808c2ecf20Sopenharmony_ci		sdp |= D_SDP_CHANGE;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	sdp |= D_PIPE(pipe);
8838c2ecf20Sopenharmony_ci	dbri->pipes[pipe].sdp = sdp;
8848c2ecf20Sopenharmony_ci	dbri->pipes[pipe].desc = -1;
8858c2ecf20Sopenharmony_ci	dbri->pipes[pipe].first_desc = -1;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	reset_pipe(dbri, pipe);
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci/*
8918c2ecf20Sopenharmony_ci * Lock must be held before calling this.
8928c2ecf20Sopenharmony_ci */
8938c2ecf20Sopenharmony_cistatic void link_time_slot(struct snd_dbri *dbri, int pipe,
8948c2ecf20Sopenharmony_ci			   int prevpipe, int nextpipe,
8958c2ecf20Sopenharmony_ci			   int length, int cycle)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	s32 *cmd;
8988c2ecf20Sopenharmony_ci	int val;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE
9018c2ecf20Sopenharmony_ci			|| prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
9028c2ecf20Sopenharmony_ci			|| nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
9038c2ecf20Sopenharmony_ci		printk(KERN_ERR
9048c2ecf20Sopenharmony_ci		    "DBRI: link_time_slot called with illegal pipe number\n");
9058c2ecf20Sopenharmony_ci		return;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	if (dbri->pipes[pipe].sdp == 0
9098c2ecf20Sopenharmony_ci			|| dbri->pipes[prevpipe].sdp == 0
9108c2ecf20Sopenharmony_ci			|| dbri->pipes[nextpipe].sdp == 0) {
9118c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: link_time_slot called "
9128c2ecf20Sopenharmony_ci			"on uninitialized pipe\n");
9138c2ecf20Sopenharmony_ci		return;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	dbri->pipes[prevpipe].nextpipe = pipe;
9178c2ecf20Sopenharmony_ci	dbri->pipes[pipe].nextpipe = nextpipe;
9188c2ecf20Sopenharmony_ci	dbri->pipes[pipe].length = length;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
9238c2ecf20Sopenharmony_ci		/* Deal with CHI special case:
9248c2ecf20Sopenharmony_ci		 * "If transmission on edges 0 or 1 is desired, then cycle n
9258c2ecf20Sopenharmony_ci		 *  (where n = # of bit times per frame...) must be used."
9268c2ecf20Sopenharmony_ci		 *                  - DBRI data sheet, page 11
9278c2ecf20Sopenharmony_ci		 */
9288c2ecf20Sopenharmony_ci		if (prevpipe == 16 && cycle == 0)
9298c2ecf20Sopenharmony_ci			cycle = dbri->chi_bpf;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci		val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
9328c2ecf20Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
9338c2ecf20Sopenharmony_ci		*(cmd++) = 0;
9348c2ecf20Sopenharmony_ci		*(cmd++) =
9358c2ecf20Sopenharmony_ci		    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
9368c2ecf20Sopenharmony_ci	} else {
9378c2ecf20Sopenharmony_ci		val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
9388c2ecf20Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
9398c2ecf20Sopenharmony_ci		*(cmd++) =
9408c2ecf20Sopenharmony_ci		    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
9418c2ecf20Sopenharmony_ci		*(cmd++) = 0;
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci#if 0
9498c2ecf20Sopenharmony_ci/*
9508c2ecf20Sopenharmony_ci * Lock must be held before calling this.
9518c2ecf20Sopenharmony_ci */
9528c2ecf20Sopenharmony_cistatic void unlink_time_slot(struct snd_dbri *dbri, int pipe,
9538c2ecf20Sopenharmony_ci			     enum in_or_out direction, int prevpipe,
9548c2ecf20Sopenharmony_ci			     int nextpipe)
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	s32 *cmd;
9578c2ecf20Sopenharmony_ci	int val;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (pipe < 0 || pipe > DBRI_MAX_PIPE
9608c2ecf20Sopenharmony_ci			|| prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
9618c2ecf20Sopenharmony_ci			|| nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
9628c2ecf20Sopenharmony_ci		printk(KERN_ERR
9638c2ecf20Sopenharmony_ci		    "DBRI: unlink_time_slot called with illegal pipe number\n");
9648c2ecf20Sopenharmony_ci		return;
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	if (direction == PIPEinput) {
9708c2ecf20Sopenharmony_ci		val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
9718c2ecf20Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
9728c2ecf20Sopenharmony_ci		*(cmd++) = D_TS_NEXT(nextpipe);
9738c2ecf20Sopenharmony_ci		*(cmd++) = 0;
9748c2ecf20Sopenharmony_ci	} else {
9758c2ecf20Sopenharmony_ci		val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe;
9768c2ecf20Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
9778c2ecf20Sopenharmony_ci		*(cmd++) = 0;
9788c2ecf20Sopenharmony_ci		*(cmd++) = D_TS_NEXT(nextpipe);
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
9838c2ecf20Sopenharmony_ci}
9848c2ecf20Sopenharmony_ci#endif
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci/* xmit_fixed() / recv_fixed()
9878c2ecf20Sopenharmony_ci *
9888c2ecf20Sopenharmony_ci * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not
9898c2ecf20Sopenharmony_ci * expected to change much, and which we don't need to buffer.
9908c2ecf20Sopenharmony_ci * The DBRI only interrupts us when the data changes (receive pipes),
9918c2ecf20Sopenharmony_ci * or only changes the data when this function is called (transmit pipes).
9928c2ecf20Sopenharmony_ci * Only short pipes (numbers 16-31) can be used in fixed data mode.
9938c2ecf20Sopenharmony_ci *
9948c2ecf20Sopenharmony_ci * These function operate on a 32-bit field, no matter how large
9958c2ecf20Sopenharmony_ci * the actual time slot is.  The interrupt handler takes care of bit
9968c2ecf20Sopenharmony_ci * ordering and alignment.  An 8-bit time slot will always end up
9978c2ecf20Sopenharmony_ci * in the low-order 8 bits, filled either MSB-first or LSB-first,
9988c2ecf20Sopenharmony_ci * depending on the settings passed to setup_pipe().
9998c2ecf20Sopenharmony_ci *
10008c2ecf20Sopenharmony_ci * Lock must not be held before calling it.
10018c2ecf20Sopenharmony_ci */
10028c2ecf20Sopenharmony_cistatic void xmit_fixed(struct snd_dbri *dbri, int pipe, unsigned int data)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	s32 *cmd;
10058c2ecf20Sopenharmony_ci	unsigned long flags;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
10088c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n");
10098c2ecf20Sopenharmony_ci		return;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
10138c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: "
10148c2ecf20Sopenharmony_ci			"Uninitialized pipe %d\n", pipe);
10158c2ecf20Sopenharmony_ci		return;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
10198c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
10208c2ecf20Sopenharmony_ci		return;
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	if (!(dbri->pipes[pipe].sdp & D_SDP_TO_SER)) {
10248c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: xmit_fixed: Called on receive pipe %d\n",
10258c2ecf20Sopenharmony_ci			pipe);
10268c2ecf20Sopenharmony_ci		return;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	/* DBRI short pipes always transmit LSB first */
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	if (dbri->pipes[pipe].sdp & D_SDP_MSB)
10328c2ecf20Sopenharmony_ci		data = reverse_bytes(data, dbri->pipes[pipe].length);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 3);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_SSP, 0, pipe);
10378c2ecf20Sopenharmony_ci	*(cmd++) = data;
10388c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
10418c2ecf20Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 3);
10428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
10438c2ecf20Sopenharmony_ci	dbri_cmdwait(dbri);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic void recv_fixed(struct snd_dbri *dbri, int pipe, volatile __u32 *ptr)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
10508c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: recv_fixed called with "
10518c2ecf20Sopenharmony_ci			"illegal pipe number\n");
10528c2ecf20Sopenharmony_ci		return;
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
10568c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: recv_fixed called on "
10578c2ecf20Sopenharmony_ci			"non-fixed pipe %d\n", pipe);
10588c2ecf20Sopenharmony_ci		return;
10598c2ecf20Sopenharmony_ci	}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
10628c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: recv_fixed called on "
10638c2ecf20Sopenharmony_ci			"transmit pipe %d\n", pipe);
10648c2ecf20Sopenharmony_ci		return;
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	dbri->pipes[pipe].recv_fixed_ptr = ptr;
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci/* setup_descs()
10718c2ecf20Sopenharmony_ci *
10728c2ecf20Sopenharmony_ci * Setup transmit/receive data on a "long" pipe - i.e, one associated
10738c2ecf20Sopenharmony_ci * with a DMA buffer.
10748c2ecf20Sopenharmony_ci *
10758c2ecf20Sopenharmony_ci * Only pipe numbers 0-15 can be used in this mode.
10768c2ecf20Sopenharmony_ci *
10778c2ecf20Sopenharmony_ci * This function takes a stream number pointing to a data buffer,
10788c2ecf20Sopenharmony_ci * and work by building chains of descriptors which identify the
10798c2ecf20Sopenharmony_ci * data buffers.  Buffers too large for a single descriptor will
10808c2ecf20Sopenharmony_ci * be spread across multiple descriptors.
10818c2ecf20Sopenharmony_ci *
10828c2ecf20Sopenharmony_ci * All descriptors create a ring buffer.
10838c2ecf20Sopenharmony_ci *
10848c2ecf20Sopenharmony_ci * Lock must be held before calling this.
10858c2ecf20Sopenharmony_ci */
10868c2ecf20Sopenharmony_cistatic int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = &dbri->stream_info[streamno];
10898c2ecf20Sopenharmony_ci	u32 dvma_addr = (u32)dbri->dma_dvma;
10908c2ecf20Sopenharmony_ci	__u32 dvma_buffer;
10918c2ecf20Sopenharmony_ci	int desc;
10928c2ecf20Sopenharmony_ci	int len;
10938c2ecf20Sopenharmony_ci	int first_desc = -1;
10948c2ecf20Sopenharmony_ci	int last_desc = -1;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	if (info->pipe < 0 || info->pipe > 15) {
10978c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_descs: Illegal pipe number\n");
10988c2ecf20Sopenharmony_ci		return -2;
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	if (dbri->pipes[info->pipe].sdp == 0) {
11028c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_descs: Uninitialized pipe %d\n",
11038c2ecf20Sopenharmony_ci		       info->pipe);
11048c2ecf20Sopenharmony_ci		return -2;
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	dvma_buffer = info->dvma_buffer;
11088c2ecf20Sopenharmony_ci	len = info->size;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	if (streamno == DBRI_PLAY) {
11118c2ecf20Sopenharmony_ci		if (!(dbri->pipes[info->pipe].sdp & D_SDP_TO_SER)) {
11128c2ecf20Sopenharmony_ci			printk(KERN_ERR "DBRI: setup_descs: "
11138c2ecf20Sopenharmony_ci				"Called on receive pipe %d\n", info->pipe);
11148c2ecf20Sopenharmony_ci			return -2;
11158c2ecf20Sopenharmony_ci		}
11168c2ecf20Sopenharmony_ci	} else {
11178c2ecf20Sopenharmony_ci		if (dbri->pipes[info->pipe].sdp & D_SDP_TO_SER) {
11188c2ecf20Sopenharmony_ci			printk(KERN_ERR
11198c2ecf20Sopenharmony_ci			    "DBRI: setup_descs: Called on transmit pipe %d\n",
11208c2ecf20Sopenharmony_ci			     info->pipe);
11218c2ecf20Sopenharmony_ci			return -2;
11228c2ecf20Sopenharmony_ci		}
11238c2ecf20Sopenharmony_ci		/* Should be able to queue multiple buffers
11248c2ecf20Sopenharmony_ci		 * to receive on a pipe
11258c2ecf20Sopenharmony_ci		 */
11268c2ecf20Sopenharmony_ci		if (pipe_active(dbri, info->pipe)) {
11278c2ecf20Sopenharmony_ci			printk(KERN_ERR "DBRI: recv_on_pipe: "
11288c2ecf20Sopenharmony_ci				"Called on active pipe %d\n", info->pipe);
11298c2ecf20Sopenharmony_ci			return -2;
11308c2ecf20Sopenharmony_ci		}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci		/* Make sure buffer size is multiple of four */
11338c2ecf20Sopenharmony_ci		len &= ~3;
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	/* Free descriptors if pipe has any */
11378c2ecf20Sopenharmony_ci	desc = dbri->pipes[info->pipe].first_desc;
11388c2ecf20Sopenharmony_ci	if (desc >= 0)
11398c2ecf20Sopenharmony_ci		do {
11408c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].ba = 0;
11418c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].nda = 0;
11428c2ecf20Sopenharmony_ci			desc = dbri->next_desc[desc];
11438c2ecf20Sopenharmony_ci		} while (desc != -1 &&
11448c2ecf20Sopenharmony_ci			 desc != dbri->pipes[info->pipe].first_desc);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	dbri->pipes[info->pipe].desc = -1;
11478c2ecf20Sopenharmony_ci	dbri->pipes[info->pipe].first_desc = -1;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	desc = 0;
11508c2ecf20Sopenharmony_ci	while (len > 0) {
11518c2ecf20Sopenharmony_ci		int mylen;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci		for (; desc < DBRI_NO_DESCS; desc++) {
11548c2ecf20Sopenharmony_ci			if (!dbri->dma->desc[desc].ba)
11558c2ecf20Sopenharmony_ci				break;
11568c2ecf20Sopenharmony_ci		}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci		if (desc == DBRI_NO_DESCS) {
11598c2ecf20Sopenharmony_ci			printk(KERN_ERR "DBRI: setup_descs: No descriptors\n");
11608c2ecf20Sopenharmony_ci			return -1;
11618c2ecf20Sopenharmony_ci		}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci		if (len > DBRI_TD_MAXCNT)
11648c2ecf20Sopenharmony_ci			mylen = DBRI_TD_MAXCNT;	/* 8KB - 4 */
11658c2ecf20Sopenharmony_ci		else
11668c2ecf20Sopenharmony_ci			mylen = len;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci		if (mylen > period)
11698c2ecf20Sopenharmony_ci			mylen = period;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci		dbri->next_desc[desc] = -1;
11728c2ecf20Sopenharmony_ci		dbri->dma->desc[desc].ba = dvma_buffer;
11738c2ecf20Sopenharmony_ci		dbri->dma->desc[desc].nda = 0;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci		if (streamno == DBRI_PLAY) {
11768c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen);
11778c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].word4 = 0;
11788c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].word1 |= DBRI_TD_F | DBRI_TD_B;
11798c2ecf20Sopenharmony_ci		} else {
11808c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].word1 = 0;
11818c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].word4 =
11828c2ecf20Sopenharmony_ci			    DBRI_RD_B | DBRI_RD_BCNT(mylen);
11838c2ecf20Sopenharmony_ci		}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci		if (first_desc == -1)
11868c2ecf20Sopenharmony_ci			first_desc = desc;
11878c2ecf20Sopenharmony_ci		else {
11888c2ecf20Sopenharmony_ci			dbri->next_desc[last_desc] = desc;
11898c2ecf20Sopenharmony_ci			dbri->dma->desc[last_desc].nda =
11908c2ecf20Sopenharmony_ci			    dvma_addr + dbri_dma_off(desc, desc);
11918c2ecf20Sopenharmony_ci		}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci		last_desc = desc;
11948c2ecf20Sopenharmony_ci		dvma_buffer += mylen;
11958c2ecf20Sopenharmony_ci		len -= mylen;
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	if (first_desc == -1 || last_desc == -1) {
11998c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: setup_descs: "
12008c2ecf20Sopenharmony_ci			" Not enough descriptors available\n");
12018c2ecf20Sopenharmony_ci		return -1;
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	dbri->dma->desc[last_desc].nda =
12058c2ecf20Sopenharmony_ci	    dvma_addr + dbri_dma_off(desc, first_desc);
12068c2ecf20Sopenharmony_ci	dbri->next_desc[last_desc] = first_desc;
12078c2ecf20Sopenharmony_ci	dbri->pipes[info->pipe].first_desc = first_desc;
12088c2ecf20Sopenharmony_ci	dbri->pipes[info->pipe].desc = first_desc;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci#ifdef DBRI_DEBUG
12118c2ecf20Sopenharmony_ci	for (desc = first_desc; desc != -1;) {
12128c2ecf20Sopenharmony_ci		dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n",
12138c2ecf20Sopenharmony_ci			desc,
12148c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].word1,
12158c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].ba,
12168c2ecf20Sopenharmony_ci			dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4);
12178c2ecf20Sopenharmony_ci			desc = dbri->next_desc[desc];
12188c2ecf20Sopenharmony_ci			if (desc == first_desc)
12198c2ecf20Sopenharmony_ci				break;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci#endif
12228c2ecf20Sopenharmony_ci	return 0;
12238c2ecf20Sopenharmony_ci}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci/*
12268c2ecf20Sopenharmony_ci****************************************************************************
12278c2ecf20Sopenharmony_ci************************** DBRI - CHI interface ****************************
12288c2ecf20Sopenharmony_ci****************************************************************************
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ciThe CHI is a four-wire (clock, frame sync, data in, data out) time-division
12318c2ecf20Sopenharmony_cimultiplexed serial interface which the DBRI can operate in either master
12328c2ecf20Sopenharmony_ci(give clock/frame sync) or slave (take clock/frame sync) mode.
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci*/
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cienum master_or_slave { CHImaster, CHIslave };
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci/*
12398c2ecf20Sopenharmony_ci * Lock must not be held before calling it.
12408c2ecf20Sopenharmony_ci */
12418c2ecf20Sopenharmony_cistatic void reset_chi(struct snd_dbri *dbri,
12428c2ecf20Sopenharmony_ci		      enum master_or_slave master_or_slave,
12438c2ecf20Sopenharmony_ci		      int bits_per_frame)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	s32 *cmd;
12468c2ecf20Sopenharmony_ci	int val;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	/* Set CHI Anchor: Pipe 16 */
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
12518c2ecf20Sopenharmony_ci	val = D_DTS_VO | D_DTS_VI | D_DTS_INS
12528c2ecf20Sopenharmony_ci		| D_DTS_PRVIN(16) | D_PIPE(16) | D_DTS_PRVOUT(16);
12538c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_DTS, 0, val);
12548c2ecf20Sopenharmony_ci	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
12558c2ecf20Sopenharmony_ci	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
12568c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
12578c2ecf20Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	dbri->pipes[16].sdp = 1;
12608c2ecf20Sopenharmony_ci	dbri->pipes[16].nextpipe = 16;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	cmd = dbri_cmdlock(dbri, 4);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	if (master_or_slave == CHIslave) {
12658c2ecf20Sopenharmony_ci		/* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
12668c2ecf20Sopenharmony_ci		 *
12678c2ecf20Sopenharmony_ci		 * CHICM  = 0 (slave mode, 8 kHz frame rate)
12688c2ecf20Sopenharmony_ci		 * IR     = give immediate CHI status interrupt
12698c2ecf20Sopenharmony_ci		 * EN     = give CHI status interrupt upon change
12708c2ecf20Sopenharmony_ci		 */
12718c2ecf20Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
12728c2ecf20Sopenharmony_ci	} else {
12738c2ecf20Sopenharmony_ci		/* Setup DBRI for CHI Master - generate clock, FS
12748c2ecf20Sopenharmony_ci		 *
12758c2ecf20Sopenharmony_ci		 * BPF				=  bits per 8 kHz frame
12768c2ecf20Sopenharmony_ci		 * 12.288 MHz / CHICM_divisor	= clock rate
12778c2ecf20Sopenharmony_ci		 * FD = 1 - drive CHIFS on rising edge of CHICK
12788c2ecf20Sopenharmony_ci		 */
12798c2ecf20Sopenharmony_ci		int clockrate = bits_per_frame * 8;
12808c2ecf20Sopenharmony_ci		int divisor = 12288 / clockrate;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci		if (divisor > 255 || divisor * clockrate != 12288)
12838c2ecf20Sopenharmony_ci			printk(KERN_ERR "DBRI: illegal bits_per_frame "
12848c2ecf20Sopenharmony_ci				"in setup_chi\n");
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
12878c2ecf20Sopenharmony_ci				    | D_CHI_BPF(bits_per_frame));
12888c2ecf20Sopenharmony_ci	}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	dbri->chi_bpf = bits_per_frame;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	/* CHI Data Mode
12938c2ecf20Sopenharmony_ci	 *
12948c2ecf20Sopenharmony_ci	 * RCE   =  0 - receive on falling edge of CHICK
12958c2ecf20Sopenharmony_ci	 * XCE   =  1 - transmit on rising edge of CHICK
12968c2ecf20Sopenharmony_ci	 * XEN   =  1 - enable transmitter
12978c2ecf20Sopenharmony_ci	 * REN   =  1 - enable receiver
12988c2ecf20Sopenharmony_ci	 */
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
13018c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN);
13028c2ecf20Sopenharmony_ci	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	dbri_cmdsend(dbri, cmd, 4);
13058c2ecf20Sopenharmony_ci}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci/*
13088c2ecf20Sopenharmony_ci****************************************************************************
13098c2ecf20Sopenharmony_ci*********************** CS4215 audio codec management **********************
13108c2ecf20Sopenharmony_ci****************************************************************************
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ciIn the standard SPARC audio configuration, the CS4215 codec is attached
13138c2ecf20Sopenharmony_cito the DBRI via the CHI interface and few of the DBRI's PIO pins.
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci * Lock must not be held before calling it.
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci*/
13188c2ecf20Sopenharmony_cistatic void cs4215_setup_pipes(struct snd_dbri *dbri)
13198c2ecf20Sopenharmony_ci{
13208c2ecf20Sopenharmony_ci	unsigned long flags;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
13238c2ecf20Sopenharmony_ci	/*
13248c2ecf20Sopenharmony_ci	 * Data mode:
13258c2ecf20Sopenharmony_ci	 * Pipe  4: Send timeslots 1-4 (audio data)
13268c2ecf20Sopenharmony_ci	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
13278c2ecf20Sopenharmony_ci	 * Pipe  6: Receive timeslots 1-4 (audio data)
13288c2ecf20Sopenharmony_ci	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
13298c2ecf20Sopenharmony_ci	 *          interrupt, and the rest of the data (slot 5 and 8) is
13308c2ecf20Sopenharmony_ci	 *          not relevant for us (only for doublechecking).
13318c2ecf20Sopenharmony_ci	 *
13328c2ecf20Sopenharmony_ci	 * Control mode:
13338c2ecf20Sopenharmony_ci	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only)
13348c2ecf20Sopenharmony_ci	 * Pipe 18: Receive timeslot 1 (clb).
13358c2ecf20Sopenharmony_ci	 * Pipe 19: Receive timeslot 7 (version).
13368c2ecf20Sopenharmony_ci	 */
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
13398c2ecf20Sopenharmony_ci	setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
13408c2ecf20Sopenharmony_ci	setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB);
13418c2ecf20Sopenharmony_ci	setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
13448c2ecf20Sopenharmony_ci	setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
13458c2ecf20Sopenharmony_ci	setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
13468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	dbri_cmdwait(dbri);
13498c2ecf20Sopenharmony_ci}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cistatic int cs4215_init_data(struct cs4215 *mm)
13528c2ecf20Sopenharmony_ci{
13538c2ecf20Sopenharmony_ci	/*
13548c2ecf20Sopenharmony_ci	 * No action, memory resetting only.
13558c2ecf20Sopenharmony_ci	 *
13568c2ecf20Sopenharmony_ci	 * Data Time Slot 5-8
13578c2ecf20Sopenharmony_ci	 * Speaker,Line and Headphone enable. Gain set to the half.
13588c2ecf20Sopenharmony_ci	 * Input is mike.
13598c2ecf20Sopenharmony_ci	 */
13608c2ecf20Sopenharmony_ci	mm->data[0] = CS4215_LO(0x20) | CS4215_HE | CS4215_LE;
13618c2ecf20Sopenharmony_ci	mm->data[1] = CS4215_RO(0x20) | CS4215_SE;
13628c2ecf20Sopenharmony_ci	mm->data[2] = CS4215_LG(0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1;
13638c2ecf20Sopenharmony_ci	mm->data[3] = CS4215_RG(0x8) | CS4215_MA(0xf);
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	/*
13668c2ecf20Sopenharmony_ci	 * Control Time Slot 1-4
13678c2ecf20Sopenharmony_ci	 * 0: Default I/O voltage scale
13688c2ecf20Sopenharmony_ci	 * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
13698c2ecf20Sopenharmony_ci	 * 2: Serial enable, CHI master, 128 bits per frame, clock 1
13708c2ecf20Sopenharmony_ci	 * 3: Tests disabled
13718c2ecf20Sopenharmony_ci	 */
13728c2ecf20Sopenharmony_ci	mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB;
13738c2ecf20Sopenharmony_ci	mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
13748c2ecf20Sopenharmony_ci	mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
13758c2ecf20Sopenharmony_ci	mm->ctrl[3] = 0;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	mm->status = 0;
13788c2ecf20Sopenharmony_ci	mm->version = 0xff;
13798c2ecf20Sopenharmony_ci	mm->precision = 8;	/* For ULAW */
13808c2ecf20Sopenharmony_ci	mm->channels = 1;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	return 0;
13838c2ecf20Sopenharmony_ci}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_cistatic void cs4215_setdata(struct snd_dbri *dbri, int muted)
13868c2ecf20Sopenharmony_ci{
13878c2ecf20Sopenharmony_ci	if (muted) {
13888c2ecf20Sopenharmony_ci		dbri->mm.data[0] |= 63;
13898c2ecf20Sopenharmony_ci		dbri->mm.data[1] |= 63;
13908c2ecf20Sopenharmony_ci		dbri->mm.data[2] &= ~15;
13918c2ecf20Sopenharmony_ci		dbri->mm.data[3] &= ~15;
13928c2ecf20Sopenharmony_ci	} else {
13938c2ecf20Sopenharmony_ci		/* Start by setting the playback attenuation. */
13948c2ecf20Sopenharmony_ci		struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
13958c2ecf20Sopenharmony_ci		int left_gain = info->left_gain & 0x3f;
13968c2ecf20Sopenharmony_ci		int right_gain = info->right_gain & 0x3f;
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci		dbri->mm.data[0] &= ~0x3f;	/* Reset the volume bits */
13998c2ecf20Sopenharmony_ci		dbri->mm.data[1] &= ~0x3f;
14008c2ecf20Sopenharmony_ci		dbri->mm.data[0] |= (DBRI_MAX_VOLUME - left_gain);
14018c2ecf20Sopenharmony_ci		dbri->mm.data[1] |= (DBRI_MAX_VOLUME - right_gain);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci		/* Now set the recording gain. */
14048c2ecf20Sopenharmony_ci		info = &dbri->stream_info[DBRI_REC];
14058c2ecf20Sopenharmony_ci		left_gain = info->left_gain & 0xf;
14068c2ecf20Sopenharmony_ci		right_gain = info->right_gain & 0xf;
14078c2ecf20Sopenharmony_ci		dbri->mm.data[2] |= CS4215_LG(left_gain);
14088c2ecf20Sopenharmony_ci		dbri->mm.data[3] |= CS4215_RG(right_gain);
14098c2ecf20Sopenharmony_ci	}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	xmit_fixed(dbri, 20, *(int *)dbri->mm.data);
14128c2ecf20Sopenharmony_ci}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci/*
14158c2ecf20Sopenharmony_ci * Set the CS4215 to data mode.
14168c2ecf20Sopenharmony_ci */
14178c2ecf20Sopenharmony_cistatic void cs4215_open(struct snd_dbri *dbri)
14188c2ecf20Sopenharmony_ci{
14198c2ecf20Sopenharmony_ci	int data_width;
14208c2ecf20Sopenharmony_ci	u32 tmp;
14218c2ecf20Sopenharmony_ci	unsigned long flags;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	dprintk(D_MM, "cs4215_open: %d channels, %d bits\n",
14248c2ecf20Sopenharmony_ci		dbri->mm.channels, dbri->mm.precision);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
14278c2ecf20Sopenharmony_ci	 * to make sure this takes.  This avoids clicking noises.
14288c2ecf20Sopenharmony_ci	 */
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	cs4215_setdata(dbri, 1);
14318c2ecf20Sopenharmony_ci	udelay(125);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	/*
14348c2ecf20Sopenharmony_ci	 * Data mode:
14358c2ecf20Sopenharmony_ci	 * Pipe  4: Send timeslots 1-4 (audio data)
14368c2ecf20Sopenharmony_ci	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
14378c2ecf20Sopenharmony_ci	 * Pipe  6: Receive timeslots 1-4 (audio data)
14388c2ecf20Sopenharmony_ci	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
14398c2ecf20Sopenharmony_ci	 *          interrupt, and the rest of the data (slot 5 and 8) is
14408c2ecf20Sopenharmony_ci	 *          not relevant for us (only for doublechecking).
14418c2ecf20Sopenharmony_ci	 *
14428c2ecf20Sopenharmony_ci	 * Just like in control mode, the time slots are all offset by eight
14438c2ecf20Sopenharmony_ci	 * bits.  The CS4215, it seems, observes TSIN (the delayed signal)
14448c2ecf20Sopenharmony_ci	 * even if it's the CHI master.  Don't ask me...
14458c2ecf20Sopenharmony_ci	 */
14468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
14478c2ecf20Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
14488c2ecf20Sopenharmony_ci	tmp &= ~(D_C);		/* Disable CHI */
14498c2ecf20Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/* Switch CS4215 to data mode - set PIO3 to 1 */
14528c2ecf20Sopenharmony_ci	sbus_writel(D_ENPIO | D_PIO1 | D_PIO3 |
14538c2ecf20Sopenharmony_ci		    (dbri->mm.onboard ? D_PIO0 : D_PIO2), dbri->regs + REG2);
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	reset_chi(dbri, CHIslave, 128);
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	/* Note: this next doesn't work for 8-bit stereo, because the two
14588c2ecf20Sopenharmony_ci	 * channels would be on timeslots 1 and 3, with 2 and 4 idle.
14598c2ecf20Sopenharmony_ci	 * (See CS4215 datasheet Fig 15)
14608c2ecf20Sopenharmony_ci	 *
14618c2ecf20Sopenharmony_ci	 * DBRI non-contiguous mode would be required to make this work.
14628c2ecf20Sopenharmony_ci	 */
14638c2ecf20Sopenharmony_ci	data_width = dbri->mm.channels * dbri->mm.precision;
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	link_time_slot(dbri, 4, 16, 16, data_width, dbri->mm.offset);
14668c2ecf20Sopenharmony_ci	link_time_slot(dbri, 20, 4, 16, 32, dbri->mm.offset + 32);
14678c2ecf20Sopenharmony_ci	link_time_slot(dbri, 6, 16, 16, data_width, dbri->mm.offset);
14688c2ecf20Sopenharmony_ci	link_time_slot(dbri, 21, 6, 16, 16, dbri->mm.offset + 40);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	/* FIXME: enable CHI after _setdata? */
14718c2ecf20Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
14728c2ecf20Sopenharmony_ci	tmp |= D_C;		/* Enable CHI */
14738c2ecf20Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
14748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	cs4215_setdata(dbri, 0);
14778c2ecf20Sopenharmony_ci}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci/*
14808c2ecf20Sopenharmony_ci * Send the control information (i.e. audio format)
14818c2ecf20Sopenharmony_ci */
14828c2ecf20Sopenharmony_cistatic int cs4215_setctrl(struct snd_dbri *dbri)
14838c2ecf20Sopenharmony_ci{
14848c2ecf20Sopenharmony_ci	int i, val;
14858c2ecf20Sopenharmony_ci	u32 tmp;
14868c2ecf20Sopenharmony_ci	unsigned long flags;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	/* FIXME - let the CPU do something useful during these delays */
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
14918c2ecf20Sopenharmony_ci	 * to make sure this takes.  This avoids clicking noises.
14928c2ecf20Sopenharmony_ci	 */
14938c2ecf20Sopenharmony_ci	cs4215_setdata(dbri, 1);
14948c2ecf20Sopenharmony_ci	udelay(125);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	/*
14978c2ecf20Sopenharmony_ci	 * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
14988c2ecf20Sopenharmony_ci	 * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
14998c2ecf20Sopenharmony_ci	 */
15008c2ecf20Sopenharmony_ci	val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2);
15018c2ecf20Sopenharmony_ci	sbus_writel(val, dbri->regs + REG2);
15028c2ecf20Sopenharmony_ci	dprintk(D_MM, "cs4215_setctrl: reg2=0x%x\n", val);
15038c2ecf20Sopenharmony_ci	udelay(34);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	/* In Control mode, the CS4215 is a slave device, so the DBRI must
15068c2ecf20Sopenharmony_ci	 * operate as CHI master, supplying clocking and frame synchronization.
15078c2ecf20Sopenharmony_ci	 *
15088c2ecf20Sopenharmony_ci	 * In Data mode, however, the CS4215 must be CHI master to insure
15098c2ecf20Sopenharmony_ci	 * that its data stream is synchronous with its codec.
15108c2ecf20Sopenharmony_ci	 *
15118c2ecf20Sopenharmony_ci	 * The upshot of all this?  We start by putting the DBRI into master
15128c2ecf20Sopenharmony_ci	 * mode, program the CS4215 in Control mode, then switch the CS4215
15138c2ecf20Sopenharmony_ci	 * into Data mode and put the DBRI into slave mode.  Various timing
15148c2ecf20Sopenharmony_ci	 * requirements must be observed along the way.
15158c2ecf20Sopenharmony_ci	 *
15168c2ecf20Sopenharmony_ci	 * Oh, and one more thing, on a SPARCStation 20 (and maybe
15178c2ecf20Sopenharmony_ci	 * others?), the addressing of the CS4215's time slots is
15188c2ecf20Sopenharmony_ci	 * offset by eight bits, so we add eight to all the "cycle"
15198c2ecf20Sopenharmony_ci	 * values in the Define Time Slot (DTS) commands.  This is
15208c2ecf20Sopenharmony_ci	 * done in hardware by a TI 248 that delays the DBRI->4215
15218c2ecf20Sopenharmony_ci	 * frame sync signal by eight clock cycles.  Anybody know why?
15228c2ecf20Sopenharmony_ci	 */
15238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
15248c2ecf20Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
15258c2ecf20Sopenharmony_ci	tmp &= ~D_C;		/* Disable CHI */
15268c2ecf20Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	reset_chi(dbri, CHImaster, 128);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	/*
15318c2ecf20Sopenharmony_ci	 * Control mode:
15328c2ecf20Sopenharmony_ci	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are read only)
15338c2ecf20Sopenharmony_ci	 * Pipe 18: Receive timeslot 1 (clb).
15348c2ecf20Sopenharmony_ci	 * Pipe 19: Receive timeslot 7 (version).
15358c2ecf20Sopenharmony_ci	 */
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	link_time_slot(dbri, 17, 16, 16, 32, dbri->mm.offset);
15388c2ecf20Sopenharmony_ci	link_time_slot(dbri, 18, 16, 16, 8, dbri->mm.offset);
15398c2ecf20Sopenharmony_ci	link_time_slot(dbri, 19, 18, 16, 8, dbri->mm.offset + 48);
15408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	/* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
15438c2ecf20Sopenharmony_ci	dbri->mm.ctrl[0] &= ~CS4215_CLB;
15448c2ecf20Sopenharmony_ci	xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
15478c2ecf20Sopenharmony_ci	tmp = sbus_readl(dbri->regs + REG0);
15488c2ecf20Sopenharmony_ci	tmp |= D_C;		/* Enable CHI */
15498c2ecf20Sopenharmony_ci	sbus_writel(tmp, dbri->regs + REG0);
15508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i)
15538c2ecf20Sopenharmony_ci		msleep_interruptible(1);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	if (i == 0) {
15568c2ecf20Sopenharmony_ci		dprintk(D_MM, "CS4215 didn't respond to CLB (0x%02x)\n",
15578c2ecf20Sopenharmony_ci			dbri->mm.status);
15588c2ecf20Sopenharmony_ci		return -1;
15598c2ecf20Sopenharmony_ci	}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	/* Disable changes to our copy of the version number, as we are about
15628c2ecf20Sopenharmony_ci	 * to leave control mode.
15638c2ecf20Sopenharmony_ci	 */
15648c2ecf20Sopenharmony_ci	recv_fixed(dbri, 19, NULL);
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	/* Terminate CS4215 control mode - data sheet says
15678c2ecf20Sopenharmony_ci	 * "Set CLB=1 and send two more frames of valid control info"
15688c2ecf20Sopenharmony_ci	 */
15698c2ecf20Sopenharmony_ci	dbri->mm.ctrl[0] |= CS4215_CLB;
15708c2ecf20Sopenharmony_ci	xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	/* Two frames of control info @ 8kHz frame rate = 250 us delay */
15738c2ecf20Sopenharmony_ci	udelay(250);
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	cs4215_setdata(dbri, 0);
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	return 0;
15788c2ecf20Sopenharmony_ci}
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci/*
15818c2ecf20Sopenharmony_ci * Setup the codec with the sampling rate, audio format and number of
15828c2ecf20Sopenharmony_ci * channels.
15838c2ecf20Sopenharmony_ci * As part of the process we resend the settings for the data
15848c2ecf20Sopenharmony_ci * timeslots as well.
15858c2ecf20Sopenharmony_ci */
15868c2ecf20Sopenharmony_cistatic int cs4215_prepare(struct snd_dbri *dbri, unsigned int rate,
15878c2ecf20Sopenharmony_ci			  snd_pcm_format_t format, unsigned int channels)
15888c2ecf20Sopenharmony_ci{
15898c2ecf20Sopenharmony_ci	int freq_idx;
15908c2ecf20Sopenharmony_ci	int ret = 0;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	/* Lookup index for this rate */
15938c2ecf20Sopenharmony_ci	for (freq_idx = 0; CS4215_FREQ[freq_idx].freq != 0; freq_idx++) {
15948c2ecf20Sopenharmony_ci		if (CS4215_FREQ[freq_idx].freq == rate)
15958c2ecf20Sopenharmony_ci			break;
15968c2ecf20Sopenharmony_ci	}
15978c2ecf20Sopenharmony_ci	if (CS4215_FREQ[freq_idx].freq != rate) {
15988c2ecf20Sopenharmony_ci		printk(KERN_WARNING "DBRI: Unsupported rate %d Hz\n", rate);
15998c2ecf20Sopenharmony_ci		return -1;
16008c2ecf20Sopenharmony_ci	}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	switch (format) {
16038c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_MU_LAW:
16048c2ecf20Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_ULAW;
16058c2ecf20Sopenharmony_ci		dbri->mm.precision = 8;
16068c2ecf20Sopenharmony_ci		break;
16078c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_A_LAW:
16088c2ecf20Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_ALAW;
16098c2ecf20Sopenharmony_ci		dbri->mm.precision = 8;
16108c2ecf20Sopenharmony_ci		break;
16118c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_U8:
16128c2ecf20Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_LINEAR8;
16138c2ecf20Sopenharmony_ci		dbri->mm.precision = 8;
16148c2ecf20Sopenharmony_ci		break;
16158c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
16168c2ecf20Sopenharmony_ci		dbri->mm.ctrl[1] = CS4215_DFR_LINEAR16;
16178c2ecf20Sopenharmony_ci		dbri->mm.precision = 16;
16188c2ecf20Sopenharmony_ci		break;
16198c2ecf20Sopenharmony_ci	default:
16208c2ecf20Sopenharmony_ci		printk(KERN_WARNING "DBRI: Unsupported format %d\n", format);
16218c2ecf20Sopenharmony_ci		return -1;
16228c2ecf20Sopenharmony_ci	}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	/* Add rate parameters */
16258c2ecf20Sopenharmony_ci	dbri->mm.ctrl[1] |= CS4215_FREQ[freq_idx].csval;
16268c2ecf20Sopenharmony_ci	dbri->mm.ctrl[2] = CS4215_XCLK |
16278c2ecf20Sopenharmony_ci	    CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	dbri->mm.channels = channels;
16308c2ecf20Sopenharmony_ci	if (channels == 2)
16318c2ecf20Sopenharmony_ci		dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	ret = cs4215_setctrl(dbri);
16348c2ecf20Sopenharmony_ci	if (ret == 0)
16358c2ecf20Sopenharmony_ci		cs4215_open(dbri);	/* set codec to data mode */
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	return ret;
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci/*
16418c2ecf20Sopenharmony_ci *
16428c2ecf20Sopenharmony_ci */
16438c2ecf20Sopenharmony_cistatic int cs4215_init(struct snd_dbri *dbri)
16448c2ecf20Sopenharmony_ci{
16458c2ecf20Sopenharmony_ci	u32 reg2 = sbus_readl(dbri->regs + REG2);
16468c2ecf20Sopenharmony_ci	dprintk(D_MM, "cs4215_init: reg2=0x%x\n", reg2);
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	/* Look for the cs4215 chips */
16498c2ecf20Sopenharmony_ci	if (reg2 & D_PIO2) {
16508c2ecf20Sopenharmony_ci		dprintk(D_MM, "Onboard CS4215 detected\n");
16518c2ecf20Sopenharmony_ci		dbri->mm.onboard = 1;
16528c2ecf20Sopenharmony_ci	}
16538c2ecf20Sopenharmony_ci	if (reg2 & D_PIO0) {
16548c2ecf20Sopenharmony_ci		dprintk(D_MM, "Speakerbox detected\n");
16558c2ecf20Sopenharmony_ci		dbri->mm.onboard = 0;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci		if (reg2 & D_PIO2) {
16588c2ecf20Sopenharmony_ci			printk(KERN_INFO "DBRI: Using speakerbox / "
16598c2ecf20Sopenharmony_ci			       "ignoring onboard mmcodec.\n");
16608c2ecf20Sopenharmony_ci			sbus_writel(D_ENPIO2, dbri->regs + REG2);
16618c2ecf20Sopenharmony_ci		}
16628c2ecf20Sopenharmony_ci	}
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	if (!(reg2 & (D_PIO0 | D_PIO2))) {
16658c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: no mmcodec found.\n");
16668c2ecf20Sopenharmony_ci		return -EIO;
16678c2ecf20Sopenharmony_ci	}
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	cs4215_setup_pipes(dbri);
16708c2ecf20Sopenharmony_ci	cs4215_init_data(&dbri->mm);
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	/* Enable capture of the status & version timeslots. */
16738c2ecf20Sopenharmony_ci	recv_fixed(dbri, 18, &dbri->mm.status);
16748c2ecf20Sopenharmony_ci	recv_fixed(dbri, 19, &dbri->mm.version);
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	dbri->mm.offset = dbri->mm.onboard ? 0 : 8;
16778c2ecf20Sopenharmony_ci	if (cs4215_setctrl(dbri) == -1 || dbri->mm.version == 0xff) {
16788c2ecf20Sopenharmony_ci		dprintk(D_MM, "CS4215 failed probe at offset %d\n",
16798c2ecf20Sopenharmony_ci			dbri->mm.offset);
16808c2ecf20Sopenharmony_ci		return -EIO;
16818c2ecf20Sopenharmony_ci	}
16828c2ecf20Sopenharmony_ci	dprintk(D_MM, "Found CS4215 at offset %d\n", dbri->mm.offset);
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	return 0;
16858c2ecf20Sopenharmony_ci}
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci/*
16888c2ecf20Sopenharmony_ci****************************************************************************
16898c2ecf20Sopenharmony_ci*************************** DBRI interrupt handler *************************
16908c2ecf20Sopenharmony_ci****************************************************************************
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ciThe DBRI communicates with the CPU mainly via a circular interrupt
16938c2ecf20Sopenharmony_cibuffer.  When an interrupt is signaled, the CPU walks through the
16948c2ecf20Sopenharmony_cibuffer and calls dbri_process_one_interrupt() for each interrupt word.
16958c2ecf20Sopenharmony_ciComplicated interrupts are handled by dedicated functions (which
16968c2ecf20Sopenharmony_ciappear first in this file).  Any pending interrupts can be serviced by
16978c2ecf20Sopenharmony_cicalling dbri_process_interrupt_buffer(), which works even if the CPU's
16988c2ecf20Sopenharmony_ciinterrupts are disabled.
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci*/
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci/* xmit_descs()
17038c2ecf20Sopenharmony_ci *
17048c2ecf20Sopenharmony_ci * Starts transmitting the current TD's for recording/playing.
17058c2ecf20Sopenharmony_ci * For playback, ALSA has filled the DMA memory with new data (we hope).
17068c2ecf20Sopenharmony_ci */
17078c2ecf20Sopenharmony_cistatic void xmit_descs(struct snd_dbri *dbri)
17088c2ecf20Sopenharmony_ci{
17098c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info;
17108c2ecf20Sopenharmony_ci	u32 dvma_addr;
17118c2ecf20Sopenharmony_ci	s32 *cmd;
17128c2ecf20Sopenharmony_ci	unsigned long flags;
17138c2ecf20Sopenharmony_ci	int first_td;
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	if (dbri == NULL)
17168c2ecf20Sopenharmony_ci		return;		/* Disabled */
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	dvma_addr = (u32)dbri->dma_dvma;
17198c2ecf20Sopenharmony_ci	info = &dbri->stream_info[DBRI_REC];
17208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	if (info->pipe >= 0) {
17238c2ecf20Sopenharmony_ci		first_td = dbri->pipes[info->pipe].first_desc;
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci		dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td);
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci		/* Stream could be closed by the time we run. */
17288c2ecf20Sopenharmony_ci		if (first_td >= 0) {
17298c2ecf20Sopenharmony_ci			cmd = dbri_cmdlock(dbri, 2);
17308c2ecf20Sopenharmony_ci			*(cmd++) = DBRI_CMD(D_SDP, 0,
17318c2ecf20Sopenharmony_ci					    dbri->pipes[info->pipe].sdp
17328c2ecf20Sopenharmony_ci					    | D_SDP_P | D_SDP_EVERY | D_SDP_C);
17338c2ecf20Sopenharmony_ci			*(cmd++) = dvma_addr +
17348c2ecf20Sopenharmony_ci				   dbri_dma_off(desc, first_td);
17358c2ecf20Sopenharmony_ci			dbri_cmdsend(dbri, cmd, 2);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci			/* Reset our admin of the pipe. */
17388c2ecf20Sopenharmony_ci			dbri->pipes[info->pipe].desc = first_td;
17398c2ecf20Sopenharmony_ci		}
17408c2ecf20Sopenharmony_ci	}
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	info = &dbri->stream_info[DBRI_PLAY];
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	if (info->pipe >= 0) {
17458c2ecf20Sopenharmony_ci		first_td = dbri->pipes[info->pipe].first_desc;
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci		dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td);
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci		/* Stream could be closed by the time we run. */
17508c2ecf20Sopenharmony_ci		if (first_td >= 0) {
17518c2ecf20Sopenharmony_ci			cmd = dbri_cmdlock(dbri, 2);
17528c2ecf20Sopenharmony_ci			*(cmd++) = DBRI_CMD(D_SDP, 0,
17538c2ecf20Sopenharmony_ci					    dbri->pipes[info->pipe].sdp
17548c2ecf20Sopenharmony_ci					    | D_SDP_P | D_SDP_EVERY | D_SDP_C);
17558c2ecf20Sopenharmony_ci			*(cmd++) = dvma_addr +
17568c2ecf20Sopenharmony_ci				   dbri_dma_off(desc, first_td);
17578c2ecf20Sopenharmony_ci			dbri_cmdsend(dbri, cmd, 2);
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci			/* Reset our admin of the pipe. */
17608c2ecf20Sopenharmony_ci			dbri->pipes[info->pipe].desc = first_td;
17618c2ecf20Sopenharmony_ci		}
17628c2ecf20Sopenharmony_ci	}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
17658c2ecf20Sopenharmony_ci}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci/* transmission_complete_intr()
17688c2ecf20Sopenharmony_ci *
17698c2ecf20Sopenharmony_ci * Called by main interrupt handler when DBRI signals transmission complete
17708c2ecf20Sopenharmony_ci * on a pipe (interrupt triggered by the B bit in a transmit descriptor).
17718c2ecf20Sopenharmony_ci *
17728c2ecf20Sopenharmony_ci * Walks through the pipe's list of transmit buffer descriptors and marks
17738c2ecf20Sopenharmony_ci * them as available. Stops when the first descriptor is found without
17748c2ecf20Sopenharmony_ci * TBC (Transmit Buffer Complete) set, or we've run through them all.
17758c2ecf20Sopenharmony_ci *
17768c2ecf20Sopenharmony_ci * The DMA buffers are not released. They form a ring buffer and
17778c2ecf20Sopenharmony_ci * they are filled by ALSA while others are transmitted by DMA.
17788c2ecf20Sopenharmony_ci *
17798c2ecf20Sopenharmony_ci */
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_cistatic void transmission_complete_intr(struct snd_dbri *dbri, int pipe)
17828c2ecf20Sopenharmony_ci{
17838c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
17848c2ecf20Sopenharmony_ci	int td = dbri->pipes[pipe].desc;
17858c2ecf20Sopenharmony_ci	int status;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	while (td >= 0) {
17888c2ecf20Sopenharmony_ci		if (td >= DBRI_NO_DESCS) {
17898c2ecf20Sopenharmony_ci			printk(KERN_ERR "DBRI: invalid td on pipe %d\n", pipe);
17908c2ecf20Sopenharmony_ci			return;
17918c2ecf20Sopenharmony_ci		}
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci		status = DBRI_TD_STATUS(dbri->dma->desc[td].word4);
17948c2ecf20Sopenharmony_ci		if (!(status & DBRI_TD_TBC))
17958c2ecf20Sopenharmony_ci			break;
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci		dprintk(D_INT, "TD %d, status 0x%02x\n", td, status);
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci		dbri->dma->desc[td].word4 = 0;	/* Reset it for next time. */
18008c2ecf20Sopenharmony_ci		info->offset += DBRI_RD_CNT(dbri->dma->desc[td].word1);
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci		td = dbri->next_desc[td];
18038c2ecf20Sopenharmony_ci		dbri->pipes[pipe].desc = td;
18048c2ecf20Sopenharmony_ci	}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	/* Notify ALSA */
18078c2ecf20Sopenharmony_ci	spin_unlock(&dbri->lock);
18088c2ecf20Sopenharmony_ci	snd_pcm_period_elapsed(info->substream);
18098c2ecf20Sopenharmony_ci	spin_lock(&dbri->lock);
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_cistatic void reception_complete_intr(struct snd_dbri *dbri, int pipe)
18138c2ecf20Sopenharmony_ci{
18148c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info;
18158c2ecf20Sopenharmony_ci	int rd = dbri->pipes[pipe].desc;
18168c2ecf20Sopenharmony_ci	s32 status;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (rd < 0 || rd >= DBRI_NO_DESCS) {
18198c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: invalid rd on pipe %d\n", pipe);
18208c2ecf20Sopenharmony_ci		return;
18218c2ecf20Sopenharmony_ci	}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	dbri->pipes[pipe].desc = dbri->next_desc[rd];
18248c2ecf20Sopenharmony_ci	status = dbri->dma->desc[rd].word1;
18258c2ecf20Sopenharmony_ci	dbri->dma->desc[rd].word1 = 0;	/* Reset it for next time. */
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci	info = &dbri->stream_info[DBRI_REC];
18288c2ecf20Sopenharmony_ci	info->offset += DBRI_RD_CNT(status);
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	/* FIXME: Check status */
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n",
18338c2ecf20Sopenharmony_ci		rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status));
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	/* Notify ALSA */
18368c2ecf20Sopenharmony_ci	spin_unlock(&dbri->lock);
18378c2ecf20Sopenharmony_ci	snd_pcm_period_elapsed(info->substream);
18388c2ecf20Sopenharmony_ci	spin_lock(&dbri->lock);
18398c2ecf20Sopenharmony_ci}
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_cistatic void dbri_process_one_interrupt(struct snd_dbri *dbri, int x)
18428c2ecf20Sopenharmony_ci{
18438c2ecf20Sopenharmony_ci	int val = D_INTR_GETVAL(x);
18448c2ecf20Sopenharmony_ci	int channel = D_INTR_GETCHAN(x);
18458c2ecf20Sopenharmony_ci	int command = D_INTR_GETCMD(x);
18468c2ecf20Sopenharmony_ci	int code = D_INTR_GETCODE(x);
18478c2ecf20Sopenharmony_ci#ifdef DBRI_DEBUG
18488c2ecf20Sopenharmony_ci	int rval = D_INTR_GETRVAL(x);
18498c2ecf20Sopenharmony_ci#endif
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	if (channel == D_INTR_CMD) {
18528c2ecf20Sopenharmony_ci		dprintk(D_CMD, "INTR: Command: %-5s  Value:%d\n",
18538c2ecf20Sopenharmony_ci			cmds[command], val);
18548c2ecf20Sopenharmony_ci	} else {
18558c2ecf20Sopenharmony_ci		dprintk(D_INT, "INTR: Chan:%d Code:%d Val:%#x\n",
18568c2ecf20Sopenharmony_ci			channel, code, rval);
18578c2ecf20Sopenharmony_ci	}
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	switch (code) {
18608c2ecf20Sopenharmony_ci	case D_INTR_CMDI:
18618c2ecf20Sopenharmony_ci		if (command != D_WAIT)
18628c2ecf20Sopenharmony_ci			printk(KERN_ERR "DBRI: Command read interrupt\n");
18638c2ecf20Sopenharmony_ci		break;
18648c2ecf20Sopenharmony_ci	case D_INTR_BRDY:
18658c2ecf20Sopenharmony_ci		reception_complete_intr(dbri, channel);
18668c2ecf20Sopenharmony_ci		break;
18678c2ecf20Sopenharmony_ci	case D_INTR_XCMP:
18688c2ecf20Sopenharmony_ci	case D_INTR_MINT:
18698c2ecf20Sopenharmony_ci		transmission_complete_intr(dbri, channel);
18708c2ecf20Sopenharmony_ci		break;
18718c2ecf20Sopenharmony_ci	case D_INTR_UNDR:
18728c2ecf20Sopenharmony_ci		/* UNDR - Transmission underrun
18738c2ecf20Sopenharmony_ci		 * resend SDP command with clear pipe bit (C) set
18748c2ecf20Sopenharmony_ci		 */
18758c2ecf20Sopenharmony_ci		{
18768c2ecf20Sopenharmony_ci	/* FIXME: do something useful in case of underrun */
18778c2ecf20Sopenharmony_ci			printk(KERN_ERR "DBRI: Underrun error\n");
18788c2ecf20Sopenharmony_ci#if 0
18798c2ecf20Sopenharmony_ci			s32 *cmd;
18808c2ecf20Sopenharmony_ci			int pipe = channel;
18818c2ecf20Sopenharmony_ci			int td = dbri->pipes[pipe].desc;
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci			dbri->dma->desc[td].word4 = 0;
18848c2ecf20Sopenharmony_ci			cmd = dbri_cmdlock(dbri, NoGetLock);
18858c2ecf20Sopenharmony_ci			*(cmd++) = DBRI_CMD(D_SDP, 0,
18868c2ecf20Sopenharmony_ci					    dbri->pipes[pipe].sdp
18878c2ecf20Sopenharmony_ci					    | D_SDP_P | D_SDP_C | D_SDP_2SAME);
18888c2ecf20Sopenharmony_ci			*(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td);
18898c2ecf20Sopenharmony_ci			dbri_cmdsend(dbri, cmd);
18908c2ecf20Sopenharmony_ci#endif
18918c2ecf20Sopenharmony_ci		}
18928c2ecf20Sopenharmony_ci		break;
18938c2ecf20Sopenharmony_ci	case D_INTR_FXDT:
18948c2ecf20Sopenharmony_ci		/* FXDT - Fixed data change */
18958c2ecf20Sopenharmony_ci		if (dbri->pipes[channel].sdp & D_SDP_MSB)
18968c2ecf20Sopenharmony_ci			val = reverse_bytes(val, dbri->pipes[channel].length);
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci		if (dbri->pipes[channel].recv_fixed_ptr)
18998c2ecf20Sopenharmony_ci			*(dbri->pipes[channel].recv_fixed_ptr) = val;
19008c2ecf20Sopenharmony_ci		break;
19018c2ecf20Sopenharmony_ci	default:
19028c2ecf20Sopenharmony_ci		if (channel != D_INTR_CMD)
19038c2ecf20Sopenharmony_ci			printk(KERN_WARNING
19048c2ecf20Sopenharmony_ci			       "DBRI: Ignored Interrupt: %d (0x%x)\n", code, x);
19058c2ecf20Sopenharmony_ci	}
19068c2ecf20Sopenharmony_ci}
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
19098c2ecf20Sopenharmony_ci * buffer until it finds a zero word (indicating nothing more to do
19108c2ecf20Sopenharmony_ci * right now).  Non-zero words require processing and are handed off
19118c2ecf20Sopenharmony_ci * to dbri_process_one_interrupt AFTER advancing the pointer.
19128c2ecf20Sopenharmony_ci */
19138c2ecf20Sopenharmony_cistatic void dbri_process_interrupt_buffer(struct snd_dbri *dbri)
19148c2ecf20Sopenharmony_ci{
19158c2ecf20Sopenharmony_ci	s32 x;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
19188c2ecf20Sopenharmony_ci		dbri->dma->intr[dbri->dbri_irqp] = 0;
19198c2ecf20Sopenharmony_ci		dbri->dbri_irqp++;
19208c2ecf20Sopenharmony_ci		if (dbri->dbri_irqp == DBRI_INT_BLK)
19218c2ecf20Sopenharmony_ci			dbri->dbri_irqp = 1;
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci		dbri_process_one_interrupt(dbri, x);
19248c2ecf20Sopenharmony_ci	}
19258c2ecf20Sopenharmony_ci}
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_cistatic irqreturn_t snd_dbri_interrupt(int irq, void *dev_id)
19288c2ecf20Sopenharmony_ci{
19298c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = dev_id;
19308c2ecf20Sopenharmony_ci	static int errcnt = 0;
19318c2ecf20Sopenharmony_ci	int x;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	if (dbri == NULL)
19348c2ecf20Sopenharmony_ci		return IRQ_NONE;
19358c2ecf20Sopenharmony_ci	spin_lock(&dbri->lock);
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	/*
19388c2ecf20Sopenharmony_ci	 * Read it, so the interrupt goes away.
19398c2ecf20Sopenharmony_ci	 */
19408c2ecf20Sopenharmony_ci	x = sbus_readl(dbri->regs + REG1);
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	if (x & (D_MRR | D_MLE | D_LBG | D_MBE)) {
19438c2ecf20Sopenharmony_ci		u32 tmp;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci		if (x & D_MRR)
19468c2ecf20Sopenharmony_ci			printk(KERN_ERR
19478c2ecf20Sopenharmony_ci			       "DBRI: Multiple Error Ack on SBus reg1=0x%x\n",
19488c2ecf20Sopenharmony_ci			       x);
19498c2ecf20Sopenharmony_ci		if (x & D_MLE)
19508c2ecf20Sopenharmony_ci			printk(KERN_ERR
19518c2ecf20Sopenharmony_ci			       "DBRI: Multiple Late Error on SBus reg1=0x%x\n",
19528c2ecf20Sopenharmony_ci			       x);
19538c2ecf20Sopenharmony_ci		if (x & D_LBG)
19548c2ecf20Sopenharmony_ci			printk(KERN_ERR
19558c2ecf20Sopenharmony_ci			       "DBRI: Lost Bus Grant on SBus reg1=0x%x\n", x);
19568c2ecf20Sopenharmony_ci		if (x & D_MBE)
19578c2ecf20Sopenharmony_ci			printk(KERN_ERR
19588c2ecf20Sopenharmony_ci			       "DBRI: Burst Error on SBus reg1=0x%x\n", x);
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci		/* Some of these SBus errors cause the chip's SBus circuitry
19618c2ecf20Sopenharmony_ci		 * to be disabled, so just re-enable and try to keep going.
19628c2ecf20Sopenharmony_ci		 *
19638c2ecf20Sopenharmony_ci		 * The only one I've seen is MRR, which will be triggered
19648c2ecf20Sopenharmony_ci		 * if you let a transmit pipe underrun, then try to CDP it.
19658c2ecf20Sopenharmony_ci		 *
19668c2ecf20Sopenharmony_ci		 * If these things persist, we reset the chip.
19678c2ecf20Sopenharmony_ci		 */
19688c2ecf20Sopenharmony_ci		if ((++errcnt) % 10 == 0) {
19698c2ecf20Sopenharmony_ci			dprintk(D_INT, "Interrupt errors exceeded.\n");
19708c2ecf20Sopenharmony_ci			dbri_reset(dbri);
19718c2ecf20Sopenharmony_ci		} else {
19728c2ecf20Sopenharmony_ci			tmp = sbus_readl(dbri->regs + REG0);
19738c2ecf20Sopenharmony_ci			tmp &= ~(D_D);
19748c2ecf20Sopenharmony_ci			sbus_writel(tmp, dbri->regs + REG0);
19758c2ecf20Sopenharmony_ci		}
19768c2ecf20Sopenharmony_ci	}
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci	dbri_process_interrupt_buffer(dbri);
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	spin_unlock(&dbri->lock);
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
19838c2ecf20Sopenharmony_ci}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci/****************************************************************************
19868c2ecf20Sopenharmony_ci		PCM Interface
19878c2ecf20Sopenharmony_ci****************************************************************************/
19888c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_dbri_pcm_hw = {
19898c2ecf20Sopenharmony_ci	.info		= SNDRV_PCM_INFO_MMAP |
19908c2ecf20Sopenharmony_ci			  SNDRV_PCM_INFO_INTERLEAVED |
19918c2ecf20Sopenharmony_ci			  SNDRV_PCM_INFO_BLOCK_TRANSFER |
19928c2ecf20Sopenharmony_ci			  SNDRV_PCM_INFO_MMAP_VALID |
19938c2ecf20Sopenharmony_ci			  SNDRV_PCM_INFO_BATCH,
19948c2ecf20Sopenharmony_ci	.formats	= SNDRV_PCM_FMTBIT_MU_LAW |
19958c2ecf20Sopenharmony_ci			  SNDRV_PCM_FMTBIT_A_LAW |
19968c2ecf20Sopenharmony_ci			  SNDRV_PCM_FMTBIT_U8 |
19978c2ecf20Sopenharmony_ci			  SNDRV_PCM_FMTBIT_S16_BE,
19988c2ecf20Sopenharmony_ci	.rates		= SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_5512,
19998c2ecf20Sopenharmony_ci	.rate_min		= 5512,
20008c2ecf20Sopenharmony_ci	.rate_max		= 48000,
20018c2ecf20Sopenharmony_ci	.channels_min		= 1,
20028c2ecf20Sopenharmony_ci	.channels_max		= 2,
20038c2ecf20Sopenharmony_ci	.buffer_bytes_max	= 64 * 1024,
20048c2ecf20Sopenharmony_ci	.period_bytes_min	= 1,
20058c2ecf20Sopenharmony_ci	.period_bytes_max	= DBRI_TD_MAXCNT,
20068c2ecf20Sopenharmony_ci	.periods_min		= 1,
20078c2ecf20Sopenharmony_ci	.periods_max		= 1024,
20088c2ecf20Sopenharmony_ci};
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_cistatic int snd_hw_rule_format(struct snd_pcm_hw_params *params,
20118c2ecf20Sopenharmony_ci			      struct snd_pcm_hw_rule *rule)
20128c2ecf20Sopenharmony_ci{
20138c2ecf20Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
20148c2ecf20Sopenharmony_ci				SNDRV_PCM_HW_PARAM_CHANNELS);
20158c2ecf20Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
20168c2ecf20Sopenharmony_ci	struct snd_mask fmt;
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	snd_mask_any(&fmt);
20198c2ecf20Sopenharmony_ci	if (c->min > 1) {
20208c2ecf20Sopenharmony_ci		fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_BE;
20218c2ecf20Sopenharmony_ci		return snd_mask_refine(f, &fmt);
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci	return 0;
20248c2ecf20Sopenharmony_ci}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_cistatic int snd_hw_rule_channels(struct snd_pcm_hw_params *params,
20278c2ecf20Sopenharmony_ci				struct snd_pcm_hw_rule *rule)
20288c2ecf20Sopenharmony_ci{
20298c2ecf20Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
20308c2ecf20Sopenharmony_ci				SNDRV_PCM_HW_PARAM_CHANNELS);
20318c2ecf20Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
20328c2ecf20Sopenharmony_ci	struct snd_interval ch;
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	snd_interval_any(&ch);
20358c2ecf20Sopenharmony_ci	if (!(f->bits[0] & SNDRV_PCM_FMTBIT_S16_BE)) {
20368c2ecf20Sopenharmony_ci		ch.min = 1;
20378c2ecf20Sopenharmony_ci		ch.max = 1;
20388c2ecf20Sopenharmony_ci		ch.integer = 1;
20398c2ecf20Sopenharmony_ci		return snd_interval_refine(c, &ch);
20408c2ecf20Sopenharmony_ci	}
20418c2ecf20Sopenharmony_ci	return 0;
20428c2ecf20Sopenharmony_ci}
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_cistatic int snd_dbri_open(struct snd_pcm_substream *substream)
20458c2ecf20Sopenharmony_ci{
20468c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
20478c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20488c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
20498c2ecf20Sopenharmony_ci	unsigned long flags;
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci	dprintk(D_USR, "open audio output.\n");
20528c2ecf20Sopenharmony_ci	runtime->hw = snd_dbri_pcm_hw;
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dbri->lock, flags);
20558c2ecf20Sopenharmony_ci	info->substream = substream;
20568c2ecf20Sopenharmony_ci	info->offset = 0;
20578c2ecf20Sopenharmony_ci	info->dvma_buffer = 0;
20588c2ecf20Sopenharmony_ci	info->pipe = -1;
20598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dbri->lock, flags);
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
20628c2ecf20Sopenharmony_ci			    snd_hw_rule_format, NULL, SNDRV_PCM_HW_PARAM_FORMAT,
20638c2ecf20Sopenharmony_ci			    -1);
20648c2ecf20Sopenharmony_ci	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
20658c2ecf20Sopenharmony_ci			    snd_hw_rule_channels, NULL,
20668c2ecf20Sopenharmony_ci			    SNDRV_PCM_HW_PARAM_CHANNELS,
20678c2ecf20Sopenharmony_ci			    -1);
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	cs4215_open(dbri);
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci	return 0;
20728c2ecf20Sopenharmony_ci}
20738c2ecf20Sopenharmony_ci
20748c2ecf20Sopenharmony_cistatic int snd_dbri_close(struct snd_pcm_substream *substream)
20758c2ecf20Sopenharmony_ci{
20768c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
20778c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	dprintk(D_USR, "close audio output.\n");
20808c2ecf20Sopenharmony_ci	info->substream = NULL;
20818c2ecf20Sopenharmony_ci	info->offset = 0;
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	return 0;
20848c2ecf20Sopenharmony_ci}
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_cistatic int snd_dbri_hw_params(struct snd_pcm_substream *substream,
20878c2ecf20Sopenharmony_ci			      struct snd_pcm_hw_params *hw_params)
20888c2ecf20Sopenharmony_ci{
20898c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20908c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
20918c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
20928c2ecf20Sopenharmony_ci	int direction;
20938c2ecf20Sopenharmony_ci	int ret;
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci	/* set sampling rate, audio format and number of channels */
20968c2ecf20Sopenharmony_ci	ret = cs4215_prepare(dbri, params_rate(hw_params),
20978c2ecf20Sopenharmony_ci			     params_format(hw_params),
20988c2ecf20Sopenharmony_ci			     params_channels(hw_params));
20998c2ecf20Sopenharmony_ci	if (ret != 0)
21008c2ecf20Sopenharmony_ci		return ret;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	/* hw_params can get called multiple times. Only map the DMA once.
21038c2ecf20Sopenharmony_ci	 */
21048c2ecf20Sopenharmony_ci	if (info->dvma_buffer == 0) {
21058c2ecf20Sopenharmony_ci		if (DBRI_STREAMNO(substream) == DBRI_PLAY)
21068c2ecf20Sopenharmony_ci			direction = DMA_TO_DEVICE;
21078c2ecf20Sopenharmony_ci		else
21088c2ecf20Sopenharmony_ci			direction = DMA_FROM_DEVICE;
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci		info->dvma_buffer =
21118c2ecf20Sopenharmony_ci			dma_map_single(&dbri->op->dev,
21128c2ecf20Sopenharmony_ci				       runtime->dma_area,
21138c2ecf20Sopenharmony_ci				       params_buffer_bytes(hw_params),
21148c2ecf20Sopenharmony_ci				       direction);
21158c2ecf20Sopenharmony_ci	}
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	direction = params_buffer_bytes(hw_params);
21188c2ecf20Sopenharmony_ci	dprintk(D_USR, "hw_params: %d bytes, dvma=%x\n",
21198c2ecf20Sopenharmony_ci		direction, info->dvma_buffer);
21208c2ecf20Sopenharmony_ci	return 0;
21218c2ecf20Sopenharmony_ci}
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_cistatic int snd_dbri_hw_free(struct snd_pcm_substream *substream)
21248c2ecf20Sopenharmony_ci{
21258c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
21268c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
21278c2ecf20Sopenharmony_ci	int direction;
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	dprintk(D_USR, "hw_free.\n");
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	/* hw_free can get called multiple times. Only unmap the DMA once.
21328c2ecf20Sopenharmony_ci	 */
21338c2ecf20Sopenharmony_ci	if (info->dvma_buffer) {
21348c2ecf20Sopenharmony_ci		if (DBRI_STREAMNO(substream) == DBRI_PLAY)
21358c2ecf20Sopenharmony_ci			direction = DMA_TO_DEVICE;
21368c2ecf20Sopenharmony_ci		else
21378c2ecf20Sopenharmony_ci			direction = DMA_FROM_DEVICE;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci		dma_unmap_single(&dbri->op->dev, info->dvma_buffer,
21408c2ecf20Sopenharmony_ci				 substream->runtime->buffer_size, direction);
21418c2ecf20Sopenharmony_ci		info->dvma_buffer = 0;
21428c2ecf20Sopenharmony_ci	}
21438c2ecf20Sopenharmony_ci	if (info->pipe != -1) {
21448c2ecf20Sopenharmony_ci		reset_pipe(dbri, info->pipe);
21458c2ecf20Sopenharmony_ci		info->pipe = -1;
21468c2ecf20Sopenharmony_ci	}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	return 0;
21498c2ecf20Sopenharmony_ci}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_cistatic int snd_dbri_prepare(struct snd_pcm_substream *substream)
21528c2ecf20Sopenharmony_ci{
21538c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
21548c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
21558c2ecf20Sopenharmony_ci	int ret;
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	info->size = snd_pcm_lib_buffer_bytes(substream);
21588c2ecf20Sopenharmony_ci	if (DBRI_STREAMNO(substream) == DBRI_PLAY)
21598c2ecf20Sopenharmony_ci		info->pipe = 4;	/* Send pipe */
21608c2ecf20Sopenharmony_ci	else
21618c2ecf20Sopenharmony_ci		info->pipe = 6;	/* Receive pipe */
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	spin_lock_irq(&dbri->lock);
21648c2ecf20Sopenharmony_ci	info->offset = 0;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	/* Setup the all the transmit/receive descriptors to cover the
21678c2ecf20Sopenharmony_ci	 * whole DMA buffer.
21688c2ecf20Sopenharmony_ci	 */
21698c2ecf20Sopenharmony_ci	ret = setup_descs(dbri, DBRI_STREAMNO(substream),
21708c2ecf20Sopenharmony_ci			  snd_pcm_lib_period_bytes(substream));
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_ci	spin_unlock_irq(&dbri->lock);
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci	dprintk(D_USR, "prepare audio output. %d bytes\n", info->size);
21758c2ecf20Sopenharmony_ci	return ret;
21768c2ecf20Sopenharmony_ci}
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_cistatic int snd_dbri_trigger(struct snd_pcm_substream *substream, int cmd)
21798c2ecf20Sopenharmony_ci{
21808c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
21818c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
21828c2ecf20Sopenharmony_ci	int ret = 0;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	switch (cmd) {
21858c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
21868c2ecf20Sopenharmony_ci		dprintk(D_USR, "start audio, period is %d bytes\n",
21878c2ecf20Sopenharmony_ci			(int)snd_pcm_lib_period_bytes(substream));
21888c2ecf20Sopenharmony_ci		/* Re-submit the TDs. */
21898c2ecf20Sopenharmony_ci		xmit_descs(dbri);
21908c2ecf20Sopenharmony_ci		break;
21918c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
21928c2ecf20Sopenharmony_ci		dprintk(D_USR, "stop audio.\n");
21938c2ecf20Sopenharmony_ci		reset_pipe(dbri, info->pipe);
21948c2ecf20Sopenharmony_ci		break;
21958c2ecf20Sopenharmony_ci	default:
21968c2ecf20Sopenharmony_ci		ret = -EINVAL;
21978c2ecf20Sopenharmony_ci	}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	return ret;
22008c2ecf20Sopenharmony_ci}
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_dbri_pointer(struct snd_pcm_substream *substream)
22038c2ecf20Sopenharmony_ci{
22048c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
22058c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
22068c2ecf20Sopenharmony_ci	snd_pcm_uframes_t ret;
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	ret = bytes_to_frames(substream->runtime, info->offset)
22098c2ecf20Sopenharmony_ci		% substream->runtime->buffer_size;
22108c2ecf20Sopenharmony_ci	dprintk(D_USR, "I/O pointer: %ld frames of %ld.\n",
22118c2ecf20Sopenharmony_ci		ret, substream->runtime->buffer_size);
22128c2ecf20Sopenharmony_ci	return ret;
22138c2ecf20Sopenharmony_ci}
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_dbri_ops = {
22168c2ecf20Sopenharmony_ci	.open = snd_dbri_open,
22178c2ecf20Sopenharmony_ci	.close = snd_dbri_close,
22188c2ecf20Sopenharmony_ci	.hw_params = snd_dbri_hw_params,
22198c2ecf20Sopenharmony_ci	.hw_free = snd_dbri_hw_free,
22208c2ecf20Sopenharmony_ci	.prepare = snd_dbri_prepare,
22218c2ecf20Sopenharmony_ci	.trigger = snd_dbri_trigger,
22228c2ecf20Sopenharmony_ci	.pointer = snd_dbri_pointer,
22238c2ecf20Sopenharmony_ci};
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_cistatic int snd_dbri_pcm(struct snd_card *card)
22268c2ecf20Sopenharmony_ci{
22278c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
22288c2ecf20Sopenharmony_ci	int err;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(card,
22318c2ecf20Sopenharmony_ci			       /* ID */		    "sun_dbri",
22328c2ecf20Sopenharmony_ci			       /* device */	    0,
22338c2ecf20Sopenharmony_ci			       /* playback count */ 1,
22348c2ecf20Sopenharmony_ci			       /* capture count */  1, &pcm)) < 0)
22358c2ecf20Sopenharmony_ci		return err;
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dbri_ops);
22388c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_dbri_ops);
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	pcm->private_data = card->private_data;
22418c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
22428c2ecf20Sopenharmony_ci	strcpy(pcm->name, card->shortname);
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
22458c2ecf20Sopenharmony_ci				       NULL, 64 * 1024, 64 * 1024);
22468c2ecf20Sopenharmony_ci	return 0;
22478c2ecf20Sopenharmony_ci}
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci/*****************************************************************************
22508c2ecf20Sopenharmony_ci			Mixer interface
22518c2ecf20Sopenharmony_ci*****************************************************************************/
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_cistatic int snd_cs4215_info_volume(struct snd_kcontrol *kcontrol,
22548c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
22558c2ecf20Sopenharmony_ci{
22568c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
22578c2ecf20Sopenharmony_ci	uinfo->count = 2;
22588c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
22598c2ecf20Sopenharmony_ci	if (kcontrol->private_value == DBRI_PLAY)
22608c2ecf20Sopenharmony_ci		uinfo->value.integer.max = DBRI_MAX_VOLUME;
22618c2ecf20Sopenharmony_ci	else
22628c2ecf20Sopenharmony_ci		uinfo->value.integer.max = DBRI_MAX_GAIN;
22638c2ecf20Sopenharmony_ci	return 0;
22648c2ecf20Sopenharmony_ci}
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_cistatic int snd_cs4215_get_volume(struct snd_kcontrol *kcontrol,
22678c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
22688c2ecf20Sopenharmony_ci{
22698c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
22708c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info;
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!dbri))
22738c2ecf20Sopenharmony_ci		return -EINVAL;
22748c2ecf20Sopenharmony_ci	info = &dbri->stream_info[kcontrol->private_value];
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = info->left_gain;
22778c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = info->right_gain;
22788c2ecf20Sopenharmony_ci	return 0;
22798c2ecf20Sopenharmony_ci}
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_cistatic int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
22828c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
22838c2ecf20Sopenharmony_ci{
22848c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
22858c2ecf20Sopenharmony_ci	struct dbri_streaminfo *info =
22868c2ecf20Sopenharmony_ci				&dbri->stream_info[kcontrol->private_value];
22878c2ecf20Sopenharmony_ci	unsigned int vol[2];
22888c2ecf20Sopenharmony_ci	int changed = 0;
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci	vol[0] = ucontrol->value.integer.value[0];
22918c2ecf20Sopenharmony_ci	vol[1] = ucontrol->value.integer.value[1];
22928c2ecf20Sopenharmony_ci	if (kcontrol->private_value == DBRI_PLAY) {
22938c2ecf20Sopenharmony_ci		if (vol[0] > DBRI_MAX_VOLUME || vol[1] > DBRI_MAX_VOLUME)
22948c2ecf20Sopenharmony_ci			return -EINVAL;
22958c2ecf20Sopenharmony_ci	} else {
22968c2ecf20Sopenharmony_ci		if (vol[0] > DBRI_MAX_GAIN || vol[1] > DBRI_MAX_GAIN)
22978c2ecf20Sopenharmony_ci			return -EINVAL;
22988c2ecf20Sopenharmony_ci	}
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci	if (info->left_gain != vol[0]) {
23018c2ecf20Sopenharmony_ci		info->left_gain = vol[0];
23028c2ecf20Sopenharmony_ci		changed = 1;
23038c2ecf20Sopenharmony_ci	}
23048c2ecf20Sopenharmony_ci	if (info->right_gain != vol[1]) {
23058c2ecf20Sopenharmony_ci		info->right_gain = vol[1];
23068c2ecf20Sopenharmony_ci		changed = 1;
23078c2ecf20Sopenharmony_ci	}
23088c2ecf20Sopenharmony_ci	if (changed) {
23098c2ecf20Sopenharmony_ci		/* First mute outputs, and wait 1/8000 sec (125 us)
23108c2ecf20Sopenharmony_ci		 * to make sure this takes.  This avoids clicking noises.
23118c2ecf20Sopenharmony_ci		 */
23128c2ecf20Sopenharmony_ci		cs4215_setdata(dbri, 1);
23138c2ecf20Sopenharmony_ci		udelay(125);
23148c2ecf20Sopenharmony_ci		cs4215_setdata(dbri, 0);
23158c2ecf20Sopenharmony_ci	}
23168c2ecf20Sopenharmony_ci	return changed;
23178c2ecf20Sopenharmony_ci}
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_cistatic int snd_cs4215_info_single(struct snd_kcontrol *kcontrol,
23208c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
23218c2ecf20Sopenharmony_ci{
23228c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci	uinfo->type = (mask == 1) ?
23258c2ecf20Sopenharmony_ci	    SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
23268c2ecf20Sopenharmony_ci	uinfo->count = 1;
23278c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
23288c2ecf20Sopenharmony_ci	uinfo->value.integer.max = mask;
23298c2ecf20Sopenharmony_ci	return 0;
23308c2ecf20Sopenharmony_ci}
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_cistatic int snd_cs4215_get_single(struct snd_kcontrol *kcontrol,
23338c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
23348c2ecf20Sopenharmony_ci{
23358c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
23368c2ecf20Sopenharmony_ci	int elem = kcontrol->private_value & 0xff;
23378c2ecf20Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
23388c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
23398c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 1;
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!dbri))
23428c2ecf20Sopenharmony_ci		return -EINVAL;
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	if (elem < 4)
23458c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] =
23468c2ecf20Sopenharmony_ci		    (dbri->mm.data[elem] >> shift) & mask;
23478c2ecf20Sopenharmony_ci	else
23488c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] =
23498c2ecf20Sopenharmony_ci		    (dbri->mm.ctrl[elem - 4] >> shift) & mask;
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	if (invert == 1)
23528c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] =
23538c2ecf20Sopenharmony_ci		    mask - ucontrol->value.integer.value[0];
23548c2ecf20Sopenharmony_ci	return 0;
23558c2ecf20Sopenharmony_ci}
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_cistatic int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
23588c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
23598c2ecf20Sopenharmony_ci{
23608c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
23618c2ecf20Sopenharmony_ci	int elem = kcontrol->private_value & 0xff;
23628c2ecf20Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
23638c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
23648c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 1;
23658c2ecf20Sopenharmony_ci	int changed = 0;
23668c2ecf20Sopenharmony_ci	unsigned short val;
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!dbri))
23698c2ecf20Sopenharmony_ci		return -EINVAL;
23708c2ecf20Sopenharmony_ci
23718c2ecf20Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
23728c2ecf20Sopenharmony_ci	if (invert == 1)
23738c2ecf20Sopenharmony_ci		val = mask - val;
23748c2ecf20Sopenharmony_ci	val <<= shift;
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci	if (elem < 4) {
23778c2ecf20Sopenharmony_ci		dbri->mm.data[elem] = (dbri->mm.data[elem] &
23788c2ecf20Sopenharmony_ci				       ~(mask << shift)) | val;
23798c2ecf20Sopenharmony_ci		changed = (val != dbri->mm.data[elem]);
23808c2ecf20Sopenharmony_ci	} else {
23818c2ecf20Sopenharmony_ci		dbri->mm.ctrl[elem - 4] = (dbri->mm.ctrl[elem - 4] &
23828c2ecf20Sopenharmony_ci					   ~(mask << shift)) | val;
23838c2ecf20Sopenharmony_ci		changed = (val != dbri->mm.ctrl[elem - 4]);
23848c2ecf20Sopenharmony_ci	}
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci	dprintk(D_GEN, "put_single: mask=0x%x, changed=%d, "
23878c2ecf20Sopenharmony_ci		"mixer-value=%ld, mm-value=0x%x\n",
23888c2ecf20Sopenharmony_ci		mask, changed, ucontrol->value.integer.value[0],
23898c2ecf20Sopenharmony_ci		dbri->mm.data[elem & 3]);
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci	if (changed) {
23928c2ecf20Sopenharmony_ci		/* First mute outputs, and wait 1/8000 sec (125 us)
23938c2ecf20Sopenharmony_ci		 * to make sure this takes.  This avoids clicking noises.
23948c2ecf20Sopenharmony_ci		 */
23958c2ecf20Sopenharmony_ci		cs4215_setdata(dbri, 1);
23968c2ecf20Sopenharmony_ci		udelay(125);
23978c2ecf20Sopenharmony_ci		cs4215_setdata(dbri, 0);
23988c2ecf20Sopenharmony_ci	}
23998c2ecf20Sopenharmony_ci	return changed;
24008c2ecf20Sopenharmony_ci}
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci/* Entries 0-3 map to the 4 data timeslots, entries 4-7 map to the 4 control
24038c2ecf20Sopenharmony_ci   timeslots. Shift is the bit offset in the timeslot, mask defines the
24048c2ecf20Sopenharmony_ci   number of bits. invert is a boolean for use with attenuation.
24058c2ecf20Sopenharmony_ci */
24068c2ecf20Sopenharmony_ci#define CS4215_SINGLE(xname, entry, shift, mask, invert)	\
24078c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),		\
24088c2ecf20Sopenharmony_ci  .info = snd_cs4215_info_single,				\
24098c2ecf20Sopenharmony_ci  .get = snd_cs4215_get_single, .put = snd_cs4215_put_single,	\
24108c2ecf20Sopenharmony_ci  .private_value = (entry) | ((shift) << 8) | ((mask) << 16) |	\
24118c2ecf20Sopenharmony_ci			((invert) << 24) },
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new dbri_controls[] = {
24148c2ecf20Sopenharmony_ci	{
24158c2ecf20Sopenharmony_ci	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24168c2ecf20Sopenharmony_ci	 .name  = "Playback Volume",
24178c2ecf20Sopenharmony_ci	 .info  = snd_cs4215_info_volume,
24188c2ecf20Sopenharmony_ci	 .get   = snd_cs4215_get_volume,
24198c2ecf20Sopenharmony_ci	 .put   = snd_cs4215_put_volume,
24208c2ecf20Sopenharmony_ci	 .private_value = DBRI_PLAY,
24218c2ecf20Sopenharmony_ci	 },
24228c2ecf20Sopenharmony_ci	CS4215_SINGLE("Headphone switch", 0, 7, 1, 0)
24238c2ecf20Sopenharmony_ci	CS4215_SINGLE("Line out switch", 0, 6, 1, 0)
24248c2ecf20Sopenharmony_ci	CS4215_SINGLE("Speaker switch", 1, 6, 1, 0)
24258c2ecf20Sopenharmony_ci	{
24268c2ecf20Sopenharmony_ci	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24278c2ecf20Sopenharmony_ci	 .name  = "Capture Volume",
24288c2ecf20Sopenharmony_ci	 .info  = snd_cs4215_info_volume,
24298c2ecf20Sopenharmony_ci	 .get   = snd_cs4215_get_volume,
24308c2ecf20Sopenharmony_ci	 .put   = snd_cs4215_put_volume,
24318c2ecf20Sopenharmony_ci	 .private_value = DBRI_REC,
24328c2ecf20Sopenharmony_ci	 },
24338c2ecf20Sopenharmony_ci	/* FIXME: mic/line switch */
24348c2ecf20Sopenharmony_ci	CS4215_SINGLE("Line in switch", 2, 4, 1, 0)
24358c2ecf20Sopenharmony_ci	CS4215_SINGLE("High Pass Filter switch", 5, 7, 1, 0)
24368c2ecf20Sopenharmony_ci	CS4215_SINGLE("Monitor Volume", 3, 4, 0xf, 1)
24378c2ecf20Sopenharmony_ci	CS4215_SINGLE("Mic boost", 4, 4, 1, 1)
24388c2ecf20Sopenharmony_ci};
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_cistatic int snd_dbri_mixer(struct snd_card *card)
24418c2ecf20Sopenharmony_ci{
24428c2ecf20Sopenharmony_ci	int idx, err;
24438c2ecf20Sopenharmony_ci	struct snd_dbri *dbri;
24448c2ecf20Sopenharmony_ci
24458c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!card || !card->private_data))
24468c2ecf20Sopenharmony_ci		return -EINVAL;
24478c2ecf20Sopenharmony_ci	dbri = card->private_data;
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	strcpy(card->mixername, card->shortname);
24508c2ecf20Sopenharmony_ci
24518c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(dbri_controls); idx++) {
24528c2ecf20Sopenharmony_ci		err = snd_ctl_add(card,
24538c2ecf20Sopenharmony_ci				snd_ctl_new1(&dbri_controls[idx], dbri));
24548c2ecf20Sopenharmony_ci		if (err < 0)
24558c2ecf20Sopenharmony_ci			return err;
24568c2ecf20Sopenharmony_ci	}
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) {
24598c2ecf20Sopenharmony_ci		dbri->stream_info[idx].left_gain = 0;
24608c2ecf20Sopenharmony_ci		dbri->stream_info[idx].right_gain = 0;
24618c2ecf20Sopenharmony_ci	}
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	return 0;
24648c2ecf20Sopenharmony_ci}
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci/****************************************************************************
24678c2ecf20Sopenharmony_ci			/proc interface
24688c2ecf20Sopenharmony_ci****************************************************************************/
24698c2ecf20Sopenharmony_cistatic void dbri_regs_read(struct snd_info_entry *entry,
24708c2ecf20Sopenharmony_ci			   struct snd_info_buffer *buffer)
24718c2ecf20Sopenharmony_ci{
24728c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = entry->private_data;
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "REG0: 0x%x\n", sbus_readl(dbri->regs + REG0));
24758c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "REG2: 0x%x\n", sbus_readl(dbri->regs + REG2));
24768c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "REG8: 0x%x\n", sbus_readl(dbri->regs + REG8));
24778c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "REG9: 0x%x\n", sbus_readl(dbri->regs + REG9));
24788c2ecf20Sopenharmony_ci}
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_ci#ifdef DBRI_DEBUG
24818c2ecf20Sopenharmony_cistatic void dbri_debug_read(struct snd_info_entry *entry,
24828c2ecf20Sopenharmony_ci			    struct snd_info_buffer *buffer)
24838c2ecf20Sopenharmony_ci{
24848c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = entry->private_data;
24858c2ecf20Sopenharmony_ci	int pipe;
24868c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "debug=%d\n", dbri_debug);
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	for (pipe = 0; pipe < 32; pipe++) {
24898c2ecf20Sopenharmony_ci		if (pipe_active(dbri, pipe)) {
24908c2ecf20Sopenharmony_ci			struct dbri_pipe *pptr = &dbri->pipes[pipe];
24918c2ecf20Sopenharmony_ci			snd_iprintf(buffer,
24928c2ecf20Sopenharmony_ci				    "Pipe %d: %s SDP=0x%x desc=%d, "
24938c2ecf20Sopenharmony_ci				    "len=%d next %d\n",
24948c2ecf20Sopenharmony_ci				    pipe,
24958c2ecf20Sopenharmony_ci				   (pptr->sdp & D_SDP_TO_SER) ? "output" :
24968c2ecf20Sopenharmony_ci								 "input",
24978c2ecf20Sopenharmony_ci				    pptr->sdp, pptr->desc,
24988c2ecf20Sopenharmony_ci				    pptr->length, pptr->nextpipe);
24998c2ecf20Sopenharmony_ci		}
25008c2ecf20Sopenharmony_ci	}
25018c2ecf20Sopenharmony_ci}
25028c2ecf20Sopenharmony_ci#endif
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_cistatic void snd_dbri_proc(struct snd_card *card)
25058c2ecf20Sopenharmony_ci{
25068c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = card->private_data;
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(card, "regs", dbri, dbri_regs_read);
25098c2ecf20Sopenharmony_ci#ifdef DBRI_DEBUG
25108c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(card, "debug", dbri, dbri_debug_read);
25118c2ecf20Sopenharmony_ci#endif
25128c2ecf20Sopenharmony_ci}
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci/*
25158c2ecf20Sopenharmony_ci****************************************************************************
25168c2ecf20Sopenharmony_ci**************************** Initialization ********************************
25178c2ecf20Sopenharmony_ci****************************************************************************
25188c2ecf20Sopenharmony_ci*/
25198c2ecf20Sopenharmony_cistatic void snd_dbri_free(struct snd_dbri *dbri);
25208c2ecf20Sopenharmony_ci
25218c2ecf20Sopenharmony_cistatic int snd_dbri_create(struct snd_card *card,
25228c2ecf20Sopenharmony_ci			   struct platform_device *op,
25238c2ecf20Sopenharmony_ci			   int irq, int dev)
25248c2ecf20Sopenharmony_ci{
25258c2ecf20Sopenharmony_ci	struct snd_dbri *dbri = card->private_data;
25268c2ecf20Sopenharmony_ci	int err;
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_ci	spin_lock_init(&dbri->lock);
25298c2ecf20Sopenharmony_ci	dbri->op = op;
25308c2ecf20Sopenharmony_ci	dbri->irq = irq;
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_ci	dbri->dma = dma_alloc_coherent(&op->dev, sizeof(struct dbri_dma),
25338c2ecf20Sopenharmony_ci				       &dbri->dma_dvma, GFP_KERNEL);
25348c2ecf20Sopenharmony_ci	if (!dbri->dma)
25358c2ecf20Sopenharmony_ci		return -ENOMEM;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	dprintk(D_GEN, "DMA Cmd Block 0x%p (%pad)\n",
25388c2ecf20Sopenharmony_ci		dbri->dma, dbri->dma_dvma);
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_ci	/* Map the registers into memory. */
25418c2ecf20Sopenharmony_ci	dbri->regs_size = resource_size(&op->resource[0]);
25428c2ecf20Sopenharmony_ci	dbri->regs = of_ioremap(&op->resource[0], 0,
25438c2ecf20Sopenharmony_ci				dbri->regs_size, "DBRI Registers");
25448c2ecf20Sopenharmony_ci	if (!dbri->regs) {
25458c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: could not allocate registers\n");
25468c2ecf20Sopenharmony_ci		dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
25478c2ecf20Sopenharmony_ci				  (void *)dbri->dma, dbri->dma_dvma);
25488c2ecf20Sopenharmony_ci		return -EIO;
25498c2ecf20Sopenharmony_ci	}
25508c2ecf20Sopenharmony_ci
25518c2ecf20Sopenharmony_ci	err = request_irq(dbri->irq, snd_dbri_interrupt, IRQF_SHARED,
25528c2ecf20Sopenharmony_ci			  "DBRI audio", dbri);
25538c2ecf20Sopenharmony_ci	if (err) {
25548c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
25558c2ecf20Sopenharmony_ci		of_iounmap(&op->resource[0], dbri->regs, dbri->regs_size);
25568c2ecf20Sopenharmony_ci		dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
25578c2ecf20Sopenharmony_ci				  (void *)dbri->dma, dbri->dma_dvma);
25588c2ecf20Sopenharmony_ci		return err;
25598c2ecf20Sopenharmony_ci	}
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	/* Do low level initialization of the DBRI and CS4215 chips */
25628c2ecf20Sopenharmony_ci	dbri_initialize(dbri);
25638c2ecf20Sopenharmony_ci	err = cs4215_init(dbri);
25648c2ecf20Sopenharmony_ci	if (err) {
25658c2ecf20Sopenharmony_ci		snd_dbri_free(dbri);
25668c2ecf20Sopenharmony_ci		return err;
25678c2ecf20Sopenharmony_ci	}
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci	return 0;
25708c2ecf20Sopenharmony_ci}
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_cistatic void snd_dbri_free(struct snd_dbri *dbri)
25738c2ecf20Sopenharmony_ci{
25748c2ecf20Sopenharmony_ci	dprintk(D_GEN, "snd_dbri_free\n");
25758c2ecf20Sopenharmony_ci	dbri_reset(dbri);
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci	if (dbri->irq)
25788c2ecf20Sopenharmony_ci		free_irq(dbri->irq, dbri);
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_ci	if (dbri->regs)
25818c2ecf20Sopenharmony_ci		of_iounmap(&dbri->op->resource[0], dbri->regs, dbri->regs_size);
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	if (dbri->dma)
25848c2ecf20Sopenharmony_ci		dma_free_coherent(&dbri->op->dev,
25858c2ecf20Sopenharmony_ci				  sizeof(struct dbri_dma),
25868c2ecf20Sopenharmony_ci				  (void *)dbri->dma, dbri->dma_dvma);
25878c2ecf20Sopenharmony_ci}
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_cistatic int dbri_probe(struct platform_device *op)
25908c2ecf20Sopenharmony_ci{
25918c2ecf20Sopenharmony_ci	struct snd_dbri *dbri;
25928c2ecf20Sopenharmony_ci	struct resource *rp;
25938c2ecf20Sopenharmony_ci	struct snd_card *card;
25948c2ecf20Sopenharmony_ci	static int dev = 0;
25958c2ecf20Sopenharmony_ci	int irq;
25968c2ecf20Sopenharmony_ci	int err;
25978c2ecf20Sopenharmony_ci
25988c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
25998c2ecf20Sopenharmony_ci		return -ENODEV;
26008c2ecf20Sopenharmony_ci	if (!enable[dev]) {
26018c2ecf20Sopenharmony_ci		dev++;
26028c2ecf20Sopenharmony_ci		return -ENOENT;
26038c2ecf20Sopenharmony_ci	}
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	irq = op->archdata.irqs[0];
26068c2ecf20Sopenharmony_ci	if (irq <= 0) {
26078c2ecf20Sopenharmony_ci		printk(KERN_ERR "DBRI-%d: No IRQ.\n", dev);
26088c2ecf20Sopenharmony_ci		return -ENODEV;
26098c2ecf20Sopenharmony_ci	}
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	err = snd_card_new(&op->dev, index[dev], id[dev], THIS_MODULE,
26128c2ecf20Sopenharmony_ci			   sizeof(struct snd_dbri), &card);
26138c2ecf20Sopenharmony_ci	if (err < 0)
26148c2ecf20Sopenharmony_ci		return err;
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	strcpy(card->driver, "DBRI");
26178c2ecf20Sopenharmony_ci	strcpy(card->shortname, "Sun DBRI");
26188c2ecf20Sopenharmony_ci	rp = &op->resource[0];
26198c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
26208c2ecf20Sopenharmony_ci		card->shortname,
26218c2ecf20Sopenharmony_ci		rp->flags & 0xffL, (unsigned long long)rp->start, irq);
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci	err = snd_dbri_create(card, op, irq, dev);
26248c2ecf20Sopenharmony_ci	if (err < 0) {
26258c2ecf20Sopenharmony_ci		snd_card_free(card);
26268c2ecf20Sopenharmony_ci		return err;
26278c2ecf20Sopenharmony_ci	}
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci	dbri = card->private_data;
26308c2ecf20Sopenharmony_ci	err = snd_dbri_pcm(card);
26318c2ecf20Sopenharmony_ci	if (err < 0)
26328c2ecf20Sopenharmony_ci		goto _err;
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci	err = snd_dbri_mixer(card);
26358c2ecf20Sopenharmony_ci	if (err < 0)
26368c2ecf20Sopenharmony_ci		goto _err;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	/* /proc file handling */
26398c2ecf20Sopenharmony_ci	snd_dbri_proc(card);
26408c2ecf20Sopenharmony_ci	dev_set_drvdata(&op->dev, card);
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	err = snd_card_register(card);
26438c2ecf20Sopenharmony_ci	if (err < 0)
26448c2ecf20Sopenharmony_ci		goto _err;
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci	printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
26478c2ecf20Sopenharmony_ci	       dev, dbri->regs,
26488c2ecf20Sopenharmony_ci	       dbri->irq, op->dev.of_node->name[9], dbri->mm.version);
26498c2ecf20Sopenharmony_ci	dev++;
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	return 0;
26528c2ecf20Sopenharmony_ci
26538c2ecf20Sopenharmony_ci_err:
26548c2ecf20Sopenharmony_ci	snd_dbri_free(dbri);
26558c2ecf20Sopenharmony_ci	snd_card_free(card);
26568c2ecf20Sopenharmony_ci	return err;
26578c2ecf20Sopenharmony_ci}
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_cistatic int dbri_remove(struct platform_device *op)
26608c2ecf20Sopenharmony_ci{
26618c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(&op->dev);
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	snd_dbri_free(card->private_data);
26648c2ecf20Sopenharmony_ci	snd_card_free(card);
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci	return 0;
26678c2ecf20Sopenharmony_ci}
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_cistatic const struct of_device_id dbri_match[] = {
26708c2ecf20Sopenharmony_ci	{
26718c2ecf20Sopenharmony_ci		.name = "SUNW,DBRIe",
26728c2ecf20Sopenharmony_ci	},
26738c2ecf20Sopenharmony_ci	{
26748c2ecf20Sopenharmony_ci		.name = "SUNW,DBRIf",
26758c2ecf20Sopenharmony_ci	},
26768c2ecf20Sopenharmony_ci	{},
26778c2ecf20Sopenharmony_ci};
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, dbri_match);
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_cistatic struct platform_driver dbri_sbus_driver = {
26828c2ecf20Sopenharmony_ci	.driver = {
26838c2ecf20Sopenharmony_ci		.name = "dbri",
26848c2ecf20Sopenharmony_ci		.of_match_table = dbri_match,
26858c2ecf20Sopenharmony_ci	},
26868c2ecf20Sopenharmony_ci	.probe		= dbri_probe,
26878c2ecf20Sopenharmony_ci	.remove		= dbri_remove,
26888c2ecf20Sopenharmony_ci};
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_cimodule_platform_driver(dbri_sbus_driver);
2691