xref: /openbmc/linux/sound/pci/rme9652/hdspm.c (revision 4ab69a2b3b84415085d261bcf848b7a95c010c9d)
1ef5fa1a4STakashi Iwai /*
2763f356cSTakashi Iwai  *   ALSA driver for RME Hammerfall DSP MADI audio interface(s)
3763f356cSTakashi Iwai  *
4763f356cSTakashi Iwai  *      Copyright (c) 2003 Winfried Ritsch (IEM)
5763f356cSTakashi Iwai  *      code based on hdsp.c   Paul Davis
6763f356cSTakashi Iwai  *                             Marcus Andersson
7763f356cSTakashi Iwai  *                             Thomas Charbonnel
83cee5a60SRemy Bruno  *      Modified 2006-06-01 for AES32 support by Remy Bruno
93cee5a60SRemy Bruno  *                                               <remy.bruno@trinnov.com>
10763f356cSTakashi Iwai  *
110dca1793SAdrian Knoth  *      Modified 2009-04-13 for proper metering by Florian Faber
120dca1793SAdrian Knoth  *                                               <faber@faberman.de>
130dca1793SAdrian Knoth  *
140dca1793SAdrian Knoth  *      Modified 2009-04-14 for native float support by Florian Faber
150dca1793SAdrian Knoth  *                                               <faber@faberman.de>
160dca1793SAdrian Knoth  *
170dca1793SAdrian Knoth  *      Modified 2009-04-26 fixed bug in rms metering by Florian Faber
180dca1793SAdrian Knoth  *                                               <faber@faberman.de>
190dca1793SAdrian Knoth  *
200dca1793SAdrian Knoth  *      Modified 2009-04-30 added hw serial number support by Florian Faber
210dca1793SAdrian Knoth  *
220dca1793SAdrian Knoth  *      Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth
230dca1793SAdrian Knoth  *
240dca1793SAdrian Knoth  *	Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
250dca1793SAdrian Knoth  *
26763f356cSTakashi Iwai  *   This program is free software; you can redistribute it and/or modify
27763f356cSTakashi Iwai  *   it under the terms of the GNU General Public License as published by
28763f356cSTakashi Iwai  *   the Free Software Foundation; either version 2 of the License, or
29763f356cSTakashi Iwai  *   (at your option) any later version.
30763f356cSTakashi Iwai  *
31763f356cSTakashi Iwai  *   This program is distributed in the hope that it will be useful,
32763f356cSTakashi Iwai  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
33763f356cSTakashi Iwai  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34763f356cSTakashi Iwai  *   GNU General Public License for more details.
35763f356cSTakashi Iwai  *
36763f356cSTakashi Iwai  *   You should have received a copy of the GNU General Public License
37763f356cSTakashi Iwai  *   along with this program; if not, write to the Free Software
38763f356cSTakashi Iwai  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
39763f356cSTakashi Iwai  *
40763f356cSTakashi Iwai  */
41763f356cSTakashi Iwai #include <linux/init.h>
42763f356cSTakashi Iwai #include <linux/delay.h>
43763f356cSTakashi Iwai #include <linux/interrupt.h>
44763f356cSTakashi Iwai #include <linux/moduleparam.h>
45763f356cSTakashi Iwai #include <linux/slab.h>
46763f356cSTakashi Iwai #include <linux/pci.h>
473f7440a6STakashi Iwai #include <linux/math64.h>
48763f356cSTakashi Iwai #include <asm/io.h>
49763f356cSTakashi Iwai 
50763f356cSTakashi Iwai #include <sound/core.h>
51763f356cSTakashi Iwai #include <sound/control.h>
52763f356cSTakashi Iwai #include <sound/pcm.h>
530dca1793SAdrian Knoth #include <sound/pcm_params.h>
54763f356cSTakashi Iwai #include <sound/info.h>
55763f356cSTakashi Iwai #include <sound/asoundef.h>
56763f356cSTakashi Iwai #include <sound/rawmidi.h>
57763f356cSTakashi Iwai #include <sound/hwdep.h>
58763f356cSTakashi Iwai #include <sound/initval.h>
59763f356cSTakashi Iwai 
60763f356cSTakashi Iwai #include <sound/hdspm.h>
61763f356cSTakashi Iwai 
62763f356cSTakashi Iwai static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	  /* Index 0-MAX */
63763f356cSTakashi Iwai static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	  /* ID for this card */
64763f356cSTakashi Iwai static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
65763f356cSTakashi Iwai 
66763f356cSTakashi Iwai module_param_array(index, int, NULL, 0444);
67763f356cSTakashi Iwai MODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
68763f356cSTakashi Iwai 
69763f356cSTakashi Iwai module_param_array(id, charp, NULL, 0444);
70763f356cSTakashi Iwai MODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
71763f356cSTakashi Iwai 
72763f356cSTakashi Iwai module_param_array(enable, bool, NULL, 0444);
73763f356cSTakashi Iwai MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
74763f356cSTakashi Iwai 
75763f356cSTakashi Iwai 
76763f356cSTakashi Iwai MODULE_AUTHOR
770dca1793SAdrian Knoth (
780dca1793SAdrian Knoth 	"Winfried Ritsch <ritsch_AT_iem.at>, "
79ef5fa1a4STakashi Iwai 	"Paul Davis <paul@linuxaudiosystems.com>, "
803cee5a60SRemy Bruno 	"Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
810dca1793SAdrian Knoth 	"Remy Bruno <remy.bruno@trinnov.com>, "
820dca1793SAdrian Knoth 	"Florian Faber <faberman@linuxproaudio.org>, "
830dca1793SAdrian Knoth 	"Adrian Knoth <adi@drcomp.erfurt.thur.de>"
840dca1793SAdrian Knoth );
85763f356cSTakashi Iwai MODULE_DESCRIPTION("RME HDSPM");
86763f356cSTakashi Iwai MODULE_LICENSE("GPL");
87763f356cSTakashi Iwai MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
88763f356cSTakashi Iwai 
89763f356cSTakashi Iwai /* --- Write registers. ---
90763f356cSTakashi Iwai   These are defined as byte-offsets from the iobase value.  */
91763f356cSTakashi Iwai 
920dca1793SAdrian Knoth #define HDSPM_WR_SETTINGS             0
930dca1793SAdrian Knoth #define HDSPM_outputBufferAddress    32
940dca1793SAdrian Knoth #define HDSPM_inputBufferAddress     36
95763f356cSTakashi Iwai #define HDSPM_controlRegister	     64
96763f356cSTakashi Iwai #define HDSPM_interruptConfirmation  96
97763f356cSTakashi Iwai #define HDSPM_control2Reg	     256  /* not in specs ???????? */
98ffb2c3c0SRemy Bruno #define HDSPM_freqReg                256  /* for AES32 */
99763f356cSTakashi Iwai #define HDSPM_midiDataOut0	     352  /* just believe in old code */
100763f356cSTakashi Iwai #define HDSPM_midiDataOut1	     356
101ffb2c3c0SRemy Bruno #define HDSPM_eeprom_wr		     384  /* for AES32 */
102763f356cSTakashi Iwai 
103763f356cSTakashi Iwai /* DMA enable for 64 channels, only Bit 0 is relevant */
104763f356cSTakashi Iwai #define HDSPM_outputEnableBase       512  /* 512-767  input  DMA */
105763f356cSTakashi Iwai #define HDSPM_inputEnableBase        768  /* 768-1023 output DMA */
106763f356cSTakashi Iwai 
107763f356cSTakashi Iwai /* 16 page addresses for each of the 64 channels DMA buffer in and out
108763f356cSTakashi Iwai    (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
109763f356cSTakashi Iwai #define HDSPM_pageAddressBufferOut       8192
110763f356cSTakashi Iwai #define HDSPM_pageAddressBufferIn        (HDSPM_pageAddressBufferOut+64*16*4)
111763f356cSTakashi Iwai 
112763f356cSTakashi Iwai #define HDSPM_MADI_mixerBase    32768	/* 32768-65535 for 2x64x64 Fader */
113763f356cSTakashi Iwai 
114763f356cSTakashi Iwai #define HDSPM_MATRIX_MIXER_SIZE  8192	/* = 2*64*64 * 4 Byte => 32kB */
115763f356cSTakashi Iwai 
116763f356cSTakashi Iwai /* --- Read registers. ---
117763f356cSTakashi Iwai    These are defined as byte-offsets from the iobase value */
118763f356cSTakashi Iwai #define HDSPM_statusRegister    0
1193cee5a60SRemy Bruno /*#define HDSPM_statusRegister2  96 */
1203cee5a60SRemy Bruno /* after RME Windows driver sources, status2 is 4-byte word # 48 = word at
1213cee5a60SRemy Bruno  * offset 192, for AES32 *and* MADI
1223cee5a60SRemy Bruno  * => need to check that offset 192 is working on MADI */
1233cee5a60SRemy Bruno #define HDSPM_statusRegister2  192
1243cee5a60SRemy Bruno #define HDSPM_timecodeRegister 128
125763f356cSTakashi Iwai 
1260dca1793SAdrian Knoth /* AIO, RayDAT */
1270dca1793SAdrian Knoth #define HDSPM_RD_STATUS_0 0
1280dca1793SAdrian Knoth #define HDSPM_RD_STATUS_1 64
1290dca1793SAdrian Knoth #define HDSPM_RD_STATUS_2 128
1300dca1793SAdrian Knoth #define HDSPM_RD_STATUS_3 192
1310dca1793SAdrian Knoth 
1320dca1793SAdrian Knoth #define HDSPM_RD_TCO           256
1330dca1793SAdrian Knoth #define HDSPM_RD_PLL_FREQ      512
1340dca1793SAdrian Knoth #define HDSPM_WR_TCO           128
1350dca1793SAdrian Knoth 
1360dca1793SAdrian Knoth #define HDSPM_TCO1_TCO_lock			0x00000001
1370dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_Range_LSB		0x00000002
1380dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_Range_MSB		0x00000004
1390dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Input_valid		0x00000008
1400dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_valid		0x00000010
1410dca1793SAdrian Knoth #define HDSPM_TCO1_Video_Input_Format_NTSC	0x00000020
1420dca1793SAdrian Knoth #define HDSPM_TCO1_Video_Input_Format_PAL	0x00000040
1430dca1793SAdrian Knoth 
1440dca1793SAdrian Knoth #define HDSPM_TCO1_set_TC			0x00000100
1450dca1793SAdrian Knoth #define HDSPM_TCO1_set_drop_frame_flag		0x00000200
1460dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Format_LSB		0x00000400
1470dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Format_MSB		0x00000800
1480dca1793SAdrian Knoth 
1490dca1793SAdrian Knoth #define HDSPM_TCO2_TC_run			0x00010000
1500dca1793SAdrian Knoth #define HDSPM_TCO2_WCK_IO_ratio_LSB		0x00020000
1510dca1793SAdrian Knoth #define HDSPM_TCO2_WCK_IO_ratio_MSB		0x00040000
1520dca1793SAdrian Knoth #define HDSPM_TCO2_set_num_drop_frames_LSB	0x00080000
1530dca1793SAdrian Knoth #define HDSPM_TCO2_set_num_drop_frames_MSB	0x00100000
1540dca1793SAdrian Knoth #define HDSPM_TCO2_set_jam_sync			0x00200000
1550dca1793SAdrian Knoth #define HDSPM_TCO2_set_flywheel			0x00400000
1560dca1793SAdrian Knoth 
1570dca1793SAdrian Knoth #define HDSPM_TCO2_set_01_4			0x01000000
1580dca1793SAdrian Knoth #define HDSPM_TCO2_set_pull_down		0x02000000
1590dca1793SAdrian Knoth #define HDSPM_TCO2_set_pull_up			0x04000000
1600dca1793SAdrian Knoth #define HDSPM_TCO2_set_freq			0x08000000
1610dca1793SAdrian Knoth #define HDSPM_TCO2_set_term_75R			0x10000000
1620dca1793SAdrian Knoth #define HDSPM_TCO2_set_input_LSB		0x20000000
1630dca1793SAdrian Knoth #define HDSPM_TCO2_set_input_MSB		0x40000000
1640dca1793SAdrian Knoth #define HDSPM_TCO2_set_freq_from_app		0x80000000
1650dca1793SAdrian Knoth 
1660dca1793SAdrian Knoth 
1670dca1793SAdrian Knoth #define HDSPM_midiDataOut0    352
1680dca1793SAdrian Knoth #define HDSPM_midiDataOut1    356
1690dca1793SAdrian Knoth #define HDSPM_midiDataOut2    368
1700dca1793SAdrian Knoth 
171763f356cSTakashi Iwai #define HDSPM_midiDataIn0     360
172763f356cSTakashi Iwai #define HDSPM_midiDataIn1     364
1730dca1793SAdrian Knoth #define HDSPM_midiDataIn2     372
1740dca1793SAdrian Knoth #define HDSPM_midiDataIn3     376
175763f356cSTakashi Iwai 
176763f356cSTakashi Iwai /* status is data bytes in MIDI-FIFO (0-128) */
177763f356cSTakashi Iwai #define HDSPM_midiStatusOut0  384
178763f356cSTakashi Iwai #define HDSPM_midiStatusOut1  388
1790dca1793SAdrian Knoth #define HDSPM_midiStatusOut2  400
1800dca1793SAdrian Knoth 
181763f356cSTakashi Iwai #define HDSPM_midiStatusIn0   392
182763f356cSTakashi Iwai #define HDSPM_midiStatusIn1   396
1830dca1793SAdrian Knoth #define HDSPM_midiStatusIn2   404
1840dca1793SAdrian Knoth #define HDSPM_midiStatusIn3   408
185763f356cSTakashi Iwai 
186763f356cSTakashi Iwai 
187763f356cSTakashi Iwai /* the meters are regular i/o-mapped registers, but offset
188763f356cSTakashi Iwai    considerably from the rest. the peak registers are reset
189763f356cSTakashi Iwai    when read; the least-significant 4 bits are full-scale counters;
190763f356cSTakashi Iwai    the actual peak value is in the most-significant 24 bits.
191763f356cSTakashi Iwai */
1920dca1793SAdrian Knoth 
1930dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_PEAK		4096
1940dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_PEAK	4352
1950dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_PEAK		4608
1960dca1793SAdrian Knoth 
1970dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_RMS_L		6144
1980dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_RMS_L	6400
1990dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_RMS_L		6656
2000dca1793SAdrian Knoth 
2010dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_RMS_H		7168
2020dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_RMS_H	7424
2030dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_RMS_H		7680
204763f356cSTakashi Iwai 
205763f356cSTakashi Iwai /* --- Control Register bits --------- */
206763f356cSTakashi Iwai #define HDSPM_Start                (1<<0) /* start engine */
207763f356cSTakashi Iwai 
208763f356cSTakashi Iwai #define HDSPM_Latency0             (1<<1) /* buffer size = 2^n */
209763f356cSTakashi Iwai #define HDSPM_Latency1             (1<<2) /* where n is defined */
210763f356cSTakashi Iwai #define HDSPM_Latency2             (1<<3) /* by Latency{2,1,0} */
211763f356cSTakashi Iwai 
2120dca1793SAdrian Knoth #define HDSPM_ClockModeMaster      (1<<4) /* 1=Master, 0=Autosync */
2130dca1793SAdrian Knoth #define HDSPM_c0Master		0x1    /* Master clock bit in settings
2140dca1793SAdrian Knoth 					  register [RayDAT, AIO] */
215763f356cSTakashi Iwai 
216763f356cSTakashi Iwai #define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
217763f356cSTakashi Iwai 
218763f356cSTakashi Iwai #define HDSPM_Frequency0  (1<<6)  /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */
219763f356cSTakashi Iwai #define HDSPM_Frequency1  (1<<7)  /* 0=32kHz/64kHz */
220763f356cSTakashi Iwai #define HDSPM_DoubleSpeed (1<<8)  /* 0=normal speed, 1=double speed */
2213cee5a60SRemy Bruno #define HDSPM_QuadSpeed   (1<<31) /* quad speed bit */
222763f356cSTakashi Iwai 
2233cee5a60SRemy Bruno #define HDSPM_Professional (1<<9) /* Professional */ /* AES32 ONLY */
224763f356cSTakashi Iwai #define HDSPM_TX_64ch     (1<<10) /* Output 64channel MODE=1,
2253cee5a60SRemy Bruno 				     56channelMODE=0 */ /* MADI ONLY*/
2263cee5a60SRemy Bruno #define HDSPM_Emphasis    (1<<10) /* Emphasis */ /* AES32 ONLY */
227763f356cSTakashi Iwai 
228763f356cSTakashi Iwai #define HDSPM_AutoInp     (1<<11) /* Auto Input (takeover) == Safe Mode,
2293cee5a60SRemy Bruno                                      0=off, 1=on  */ /* MADI ONLY */
2303cee5a60SRemy Bruno #define HDSPM_Dolby       (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
231763f356cSTakashi Iwai 
232ef5fa1a4STakashi Iwai #define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax
233ef5fa1a4STakashi Iwai 				    * -- MADI ONLY
234ef5fa1a4STakashi Iwai 				    */
235763f356cSTakashi Iwai #define HDSPM_InputSelect1 (1<<15) /* should be 0 */
236763f356cSTakashi Iwai 
2373cee5a60SRemy Bruno #define HDSPM_SyncRef2     (1<<13)
2383cee5a60SRemy Bruno #define HDSPM_SyncRef3     (1<<25)
239763f356cSTakashi Iwai 
2403cee5a60SRemy Bruno #define HDSPM_SMUX         (1<<18) /* Frame ??? */ /* MADI ONY */
241763f356cSTakashi Iwai #define HDSPM_clr_tms      (1<<19) /* clear track marker, do not use
242763f356cSTakashi Iwai                                       AES additional bits in
243763f356cSTakashi Iwai 				      lower 5 Audiodatabits ??? */
2443cee5a60SRemy Bruno #define HDSPM_taxi_reset   (1<<20) /* ??? */ /* MADI ONLY ? */
2453cee5a60SRemy Bruno #define HDSPM_WCK48        (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
246763f356cSTakashi Iwai 
2470dca1793SAdrian Knoth #define HDSPM_Midi0InterruptEnable 0x0400000
2480dca1793SAdrian Knoth #define HDSPM_Midi1InterruptEnable 0x0800000
2490dca1793SAdrian Knoth #define HDSPM_Midi2InterruptEnable 0x0200000
2500dca1793SAdrian Knoth #define HDSPM_Midi3InterruptEnable 0x4000000
251763f356cSTakashi Iwai 
252763f356cSTakashi Iwai #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
2530dca1793SAdrian Knoth #define HDSPe_FLOAT_FORMAT         0x2000000
254763f356cSTakashi Iwai 
2553cee5a60SRemy Bruno #define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
2563cee5a60SRemy Bruno #define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
2573cee5a60SRemy Bruno #define HDSPM_QS_QuadWire   (1<<28) /* AES32 ONLY */
2583cee5a60SRemy Bruno 
2593cee5a60SRemy Bruno #define HDSPM_wclk_sel (1<<30)
260763f356cSTakashi Iwai 
261763f356cSTakashi Iwai /* --- bit helper defines */
262763f356cSTakashi Iwai #define HDSPM_LatencyMask    (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
263ef5fa1a4STakashi Iwai #define HDSPM_FrequencyMask  (HDSPM_Frequency0|HDSPM_Frequency1|\
264ef5fa1a4STakashi Iwai 			      HDSPM_DoubleSpeed|HDSPM_QuadSpeed)
265763f356cSTakashi Iwai #define HDSPM_InputMask      (HDSPM_InputSelect0|HDSPM_InputSelect1)
266763f356cSTakashi Iwai #define HDSPM_InputOptical   0
267763f356cSTakashi Iwai #define HDSPM_InputCoaxial   (HDSPM_InputSelect0)
268ef5fa1a4STakashi Iwai #define HDSPM_SyncRefMask    (HDSPM_SyncRef0|HDSPM_SyncRef1|\
269ef5fa1a4STakashi Iwai 			      HDSPM_SyncRef2|HDSPM_SyncRef3)
2700dca1793SAdrian Knoth 
2710dca1793SAdrian Knoth #define HDSPM_c0_SyncRef0      0x2
2720dca1793SAdrian Knoth #define HDSPM_c0_SyncRef1      0x4
2730dca1793SAdrian Knoth #define HDSPM_c0_SyncRef2      0x8
2740dca1793SAdrian Knoth #define HDSPM_c0_SyncRef3      0x10
2750dca1793SAdrian Knoth #define HDSPM_c0_SyncRefMask   (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\
2760dca1793SAdrian Knoth 				HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3)
277763f356cSTakashi Iwai 
278763f356cSTakashi Iwai #define HDSPM_SYNC_FROM_WORD    0	/* Preferred sync reference */
279763f356cSTakashi Iwai #define HDSPM_SYNC_FROM_MADI    1	/* choices - used by "pref_sync_ref" */
2800dca1793SAdrian Knoth #define HDSPM_SYNC_FROM_TCO     2
2810dca1793SAdrian Knoth #define HDSPM_SYNC_FROM_SYNC_IN 3
282763f356cSTakashi Iwai 
283763f356cSTakashi Iwai #define HDSPM_Frequency32KHz    HDSPM_Frequency0
284763f356cSTakashi Iwai #define HDSPM_Frequency44_1KHz  HDSPM_Frequency1
285763f356cSTakashi Iwai #define HDSPM_Frequency48KHz   (HDSPM_Frequency1|HDSPM_Frequency0)
286763f356cSTakashi Iwai #define HDSPM_Frequency64KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency0)
287763f356cSTakashi Iwai #define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
288ef5fa1a4STakashi Iwai #define HDSPM_Frequency96KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency1|\
289ef5fa1a4STakashi Iwai 				HDSPM_Frequency0)
2903cee5a60SRemy Bruno #define HDSPM_Frequency128KHz   (HDSPM_QuadSpeed|HDSPM_Frequency0)
2913cee5a60SRemy Bruno #define HDSPM_Frequency176_4KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1)
292ef5fa1a4STakashi Iwai #define HDSPM_Frequency192KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1|\
293ef5fa1a4STakashi Iwai 				 HDSPM_Frequency0)
294763f356cSTakashi Iwai 
295763f356cSTakashi Iwai 
296763f356cSTakashi Iwai /* Synccheck Status */
297763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_NO_LOCK 0
298763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_LOCK    1
299763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_SYNC	 2
300763f356cSTakashi Iwai 
301763f356cSTakashi Iwai /* AutoSync References - used by "autosync_ref" control switch */
302763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_FROM_WORD      0
303763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_FROM_MADI      1
3040dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_TCO       2
3050dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_SYNC_IN   3
3060dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_NONE      4
307763f356cSTakashi Iwai 
308763f356cSTakashi Iwai /* Possible sources of MADI input */
309763f356cSTakashi Iwai #define HDSPM_OPTICAL 0		/* optical   */
310763f356cSTakashi Iwai #define HDSPM_COAXIAL 1		/* BNC */
311763f356cSTakashi Iwai 
312763f356cSTakashi Iwai #define hdspm_encode_latency(x)       (((x)<<1) & HDSPM_LatencyMask)
3130dca1793SAdrian Knoth #define hdspm_decode_latency(x)       ((((x) & HDSPM_LatencyMask)>>1))
314763f356cSTakashi Iwai 
315763f356cSTakashi Iwai #define hdspm_encode_in(x) (((x)&0x3)<<14)
316763f356cSTakashi Iwai #define hdspm_decode_in(x) (((x)>>14)&0x3)
317763f356cSTakashi Iwai 
318763f356cSTakashi Iwai /* --- control2 register bits --- */
319763f356cSTakashi Iwai #define HDSPM_TMS             (1<<0)
320763f356cSTakashi Iwai #define HDSPM_TCK             (1<<1)
321763f356cSTakashi Iwai #define HDSPM_TDI             (1<<2)
322763f356cSTakashi Iwai #define HDSPM_JTAG            (1<<3)
323763f356cSTakashi Iwai #define HDSPM_PWDN            (1<<4)
324763f356cSTakashi Iwai #define HDSPM_PROGRAM	      (1<<5)
325763f356cSTakashi Iwai #define HDSPM_CONFIG_MODE_0   (1<<6)
326763f356cSTakashi Iwai #define HDSPM_CONFIG_MODE_1   (1<<7)
327763f356cSTakashi Iwai /*#define HDSPM_VERSION_BIT     (1<<8) not defined any more*/
328763f356cSTakashi Iwai #define HDSPM_BIGENDIAN_MODE  (1<<9)
329763f356cSTakashi Iwai #define HDSPM_RD_MULTIPLE     (1<<10)
330763f356cSTakashi Iwai 
3313cee5a60SRemy Bruno /* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and
332ef5fa1a4STakashi Iwai      that do not conflict with specific bits for AES32 seem to be valid also
333ef5fa1a4STakashi Iwai      for the AES32
334ef5fa1a4STakashi Iwai  */
335763f356cSTakashi Iwai #define HDSPM_audioIRQPending    (1<<0)	/* IRQ is high and pending */
336ef5fa1a4STakashi Iwai #define HDSPM_RX_64ch            (1<<1)	/* Input 64chan. MODE=1, 56chn MODE=0 */
337ef5fa1a4STakashi Iwai #define HDSPM_AB_int             (1<<2)	/* InputChannel Opt=0, Coax=1
338ef5fa1a4STakashi Iwai 					 * (like inp0)
339ef5fa1a4STakashi Iwai 					 */
3400dca1793SAdrian Knoth 
341763f356cSTakashi Iwai #define HDSPM_madiLock           (1<<3)	/* MADI Locked =1, no=0 */
3420dca1793SAdrian Knoth #define HDSPM_madiSync          (1<<18) /* MADI is in sync */
3430dca1793SAdrian Knoth 
3440dca1793SAdrian Knoth #define HDSPM_tcoLock    0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */
3450dca1793SAdrian Knoth #define HDSPM_tcoSync    0x10000000 /* Optional TCO sync status */
3460dca1793SAdrian Knoth 
3470dca1793SAdrian Knoth #define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */
3480dca1793SAdrian Knoth #define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */
349763f356cSTakashi Iwai 
350763f356cSTakashi Iwai #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
3510dca1793SAdrian Knoth 			/* since 64byte accurate, last 6 bits are not used */
352763f356cSTakashi Iwai 
3530dca1793SAdrian Knoth 
3540dca1793SAdrian Knoth 
355763f356cSTakashi Iwai #define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
356763f356cSTakashi Iwai 
357763f356cSTakashi Iwai #define HDSPM_madiFreq0         (1<<22)	/* system freq 0=error */
358763f356cSTakashi Iwai #define HDSPM_madiFreq1         (1<<23)	/* 1=32, 2=44.1 3=48 */
359763f356cSTakashi Iwai #define HDSPM_madiFreq2         (1<<24)	/* 4=64, 5=88.2 6=96 */
360763f356cSTakashi Iwai #define HDSPM_madiFreq3         (1<<25)	/* 7=128, 8=176.4 9=192 */
361763f356cSTakashi Iwai 
362ef5fa1a4STakashi Iwai #define HDSPM_BufferID          (1<<26)	/* (Double)Buffer ID toggles with
363ef5fa1a4STakashi Iwai 					 * Interrupt
364ef5fa1a4STakashi Iwai 					 */
3650dca1793SAdrian Knoth #define HDSPM_tco_detect         0x08000000
3660dca1793SAdrian Knoth #define HDSPM_tco_lock	         0x20000000
3670dca1793SAdrian Knoth 
3680dca1793SAdrian Knoth #define HDSPM_s2_tco_detect      0x00000040
3690dca1793SAdrian Knoth #define HDSPM_s2_AEBO_D          0x00000080
3700dca1793SAdrian Knoth #define HDSPM_s2_AEBI_D          0x00000100
3710dca1793SAdrian Knoth 
3720dca1793SAdrian Knoth 
3730dca1793SAdrian Knoth #define HDSPM_midi0IRQPending    0x40000000
3740dca1793SAdrian Knoth #define HDSPM_midi1IRQPending    0x80000000
3750dca1793SAdrian Knoth #define HDSPM_midi2IRQPending    0x20000000
3760dca1793SAdrian Knoth #define HDSPM_midi2IRQPendingAES 0x00000020
3770dca1793SAdrian Knoth #define HDSPM_midi3IRQPending    0x00200000
378763f356cSTakashi Iwai 
379763f356cSTakashi Iwai /* --- status bit helpers */
380ef5fa1a4STakashi Iwai #define HDSPM_madiFreqMask  (HDSPM_madiFreq0|HDSPM_madiFreq1|\
381ef5fa1a4STakashi Iwai 			     HDSPM_madiFreq2|HDSPM_madiFreq3)
382763f356cSTakashi Iwai #define HDSPM_madiFreq32    (HDSPM_madiFreq0)
383763f356cSTakashi Iwai #define HDSPM_madiFreq44_1  (HDSPM_madiFreq1)
384763f356cSTakashi Iwai #define HDSPM_madiFreq48    (HDSPM_madiFreq0|HDSPM_madiFreq1)
385763f356cSTakashi Iwai #define HDSPM_madiFreq64    (HDSPM_madiFreq2)
386763f356cSTakashi Iwai #define HDSPM_madiFreq88_2  (HDSPM_madiFreq0|HDSPM_madiFreq2)
387763f356cSTakashi Iwai #define HDSPM_madiFreq96    (HDSPM_madiFreq1|HDSPM_madiFreq2)
388763f356cSTakashi Iwai #define HDSPM_madiFreq128   (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2)
389763f356cSTakashi Iwai #define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
390763f356cSTakashi Iwai #define HDSPM_madiFreq192   (HDSPM_madiFreq3|HDSPM_madiFreq0)
391763f356cSTakashi Iwai 
3923cee5a60SRemy Bruno /* Status2 Register bits */ /* MADI ONLY */
393763f356cSTakashi Iwai 
394763f356cSTakashi Iwai #define HDSPM_version0 (1<<0)	/* not realy defined but I guess */
395763f356cSTakashi Iwai #define HDSPM_version1 (1<<1)	/* in former cards it was ??? */
396763f356cSTakashi Iwai #define HDSPM_version2 (1<<2)
397763f356cSTakashi Iwai 
398763f356cSTakashi Iwai #define HDSPM_wcLock (1<<3)	/* Wordclock is detected and locked */
399763f356cSTakashi Iwai #define HDSPM_wcSync (1<<4)	/* Wordclock is in sync with systemclock */
400763f356cSTakashi Iwai 
401763f356cSTakashi Iwai #define HDSPM_wc_freq0 (1<<5)	/* input freq detected via autosync  */
402763f356cSTakashi Iwai #define HDSPM_wc_freq1 (1<<6)	/* 001=32, 010==44.1, 011=48, */
403763f356cSTakashi Iwai #define HDSPM_wc_freq2 (1<<7)	/* 100=64, 101=88.2, 110=96, */
404763f356cSTakashi Iwai /* missing Bit   for               111=128, 1000=176.4, 1001=192 */
405763f356cSTakashi Iwai 
4060dca1793SAdrian Knoth #define HDSPM_SyncRef0 0x10000  /* Sync Reference */
4070dca1793SAdrian Knoth #define HDSPM_SyncRef1 0x20000
4080dca1793SAdrian Knoth 
4090dca1793SAdrian Knoth #define HDSPM_SelSyncRef0 (1<<8)	/* AutoSync Source */
410763f356cSTakashi Iwai #define HDSPM_SelSyncRef1 (1<<9)	/* 000=word, 001=MADI, */
411763f356cSTakashi Iwai #define HDSPM_SelSyncRef2 (1<<10)	/* 111=no valid signal */
412763f356cSTakashi Iwai 
413763f356cSTakashi Iwai #define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
414763f356cSTakashi Iwai 
415763f356cSTakashi Iwai #define HDSPM_wcFreqMask  (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
416763f356cSTakashi Iwai #define HDSPM_wcFreq32    (HDSPM_wc_freq0)
417763f356cSTakashi Iwai #define HDSPM_wcFreq44_1  (HDSPM_wc_freq1)
418763f356cSTakashi Iwai #define HDSPM_wcFreq48    (HDSPM_wc_freq0|HDSPM_wc_freq1)
419763f356cSTakashi Iwai #define HDSPM_wcFreq64    (HDSPM_wc_freq2)
420763f356cSTakashi Iwai #define HDSPM_wcFreq88_2  (HDSPM_wc_freq0|HDSPM_wc_freq2)
421763f356cSTakashi Iwai #define HDSPM_wcFreq96    (HDSPM_wc_freq1|HDSPM_wc_freq2)
422763f356cSTakashi Iwai 
4230dca1793SAdrian Knoth #define HDSPM_status1_F_0 0x0400000
4240dca1793SAdrian Knoth #define HDSPM_status1_F_1 0x0800000
4250dca1793SAdrian Knoth #define HDSPM_status1_F_2 0x1000000
4260dca1793SAdrian Knoth #define HDSPM_status1_F_3 0x2000000
4270dca1793SAdrian Knoth #define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3)
4280dca1793SAdrian Knoth 
429763f356cSTakashi Iwai 
430ef5fa1a4STakashi Iwai #define HDSPM_SelSyncRefMask       (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
431ef5fa1a4STakashi Iwai 				    HDSPM_SelSyncRef2)
432763f356cSTakashi Iwai #define HDSPM_SelSyncRef_WORD      0
433763f356cSTakashi Iwai #define HDSPM_SelSyncRef_MADI      (HDSPM_SelSyncRef0)
4340dca1793SAdrian Knoth #define HDSPM_SelSyncRef_TCO       (HDSPM_SelSyncRef1)
4350dca1793SAdrian Knoth #define HDSPM_SelSyncRef_SyncIn    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1)
436ef5fa1a4STakashi Iwai #define HDSPM_SelSyncRef_NVALID    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
437ef5fa1a4STakashi Iwai 				    HDSPM_SelSyncRef2)
438763f356cSTakashi Iwai 
4393cee5a60SRemy Bruno /*
4403cee5a60SRemy Bruno    For AES32, bits for status, status2 and timecode are different
4413cee5a60SRemy Bruno */
4423cee5a60SRemy Bruno /* status */
4433cee5a60SRemy Bruno #define HDSPM_AES32_wcLock	0x0200000
4443cee5a60SRemy Bruno #define HDSPM_AES32_wcFreq_bit  22
4453cee5a60SRemy Bruno /* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
4463cee5a60SRemy Bruno   HDSPM_bit2freq */
4473cee5a60SRemy Bruno #define HDSPM_AES32_syncref_bit  16
4483cee5a60SRemy Bruno /* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
4493cee5a60SRemy Bruno 
4503cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_WORD 0
4513cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES1 1
4523cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES2 2
4533cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES3 3
4543cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES4 4
4553cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES5 5
4563cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
4573cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
4583cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
4596534599dSRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_NONE 9
4603cee5a60SRemy Bruno 
4613cee5a60SRemy Bruno /*  status2 */
4623cee5a60SRemy Bruno /* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
4633cee5a60SRemy Bruno #define HDSPM_LockAES   0x80
4643cee5a60SRemy Bruno #define HDSPM_LockAES1  0x80
4653cee5a60SRemy Bruno #define HDSPM_LockAES2  0x40
4663cee5a60SRemy Bruno #define HDSPM_LockAES3  0x20
4673cee5a60SRemy Bruno #define HDSPM_LockAES4  0x10
4683cee5a60SRemy Bruno #define HDSPM_LockAES5  0x8
4693cee5a60SRemy Bruno #define HDSPM_LockAES6  0x4
4703cee5a60SRemy Bruno #define HDSPM_LockAES7  0x2
4713cee5a60SRemy Bruno #define HDSPM_LockAES8  0x1
4723cee5a60SRemy Bruno /*
4733cee5a60SRemy Bruno    Timecode
4743cee5a60SRemy Bruno    After windows driver sources, bits 4*i to 4*i+3 give the input frequency on
4753cee5a60SRemy Bruno    AES i+1
4763cee5a60SRemy Bruno  bits 3210
4773cee5a60SRemy Bruno       0001  32kHz
4783cee5a60SRemy Bruno       0010  44.1kHz
4793cee5a60SRemy Bruno       0011  48kHz
4803cee5a60SRemy Bruno       0100  64kHz
4813cee5a60SRemy Bruno       0101  88.2kHz
4823cee5a60SRemy Bruno       0110  96kHz
4833cee5a60SRemy Bruno       0111  128kHz
4843cee5a60SRemy Bruno       1000  176.4kHz
4853cee5a60SRemy Bruno       1001  192kHz
4863cee5a60SRemy Bruno   NB: Timecode register doesn't seem to work on AES32 card revision 230
4873cee5a60SRemy Bruno */
4883cee5a60SRemy Bruno 
489763f356cSTakashi Iwai /* Mixer Values */
490763f356cSTakashi Iwai #define UNITY_GAIN          32768	/* = 65536/2 */
491763f356cSTakashi Iwai #define MINUS_INFINITY_GAIN 0
492763f356cSTakashi Iwai 
493763f356cSTakashi Iwai /* Number of channels for different Speed Modes */
494763f356cSTakashi Iwai #define MADI_SS_CHANNELS       64
495763f356cSTakashi Iwai #define MADI_DS_CHANNELS       32
496763f356cSTakashi Iwai #define MADI_QS_CHANNELS       16
497763f356cSTakashi Iwai 
4980dca1793SAdrian Knoth #define RAYDAT_SS_CHANNELS     36
4990dca1793SAdrian Knoth #define RAYDAT_DS_CHANNELS     20
5000dca1793SAdrian Knoth #define RAYDAT_QS_CHANNELS     12
5010dca1793SAdrian Knoth 
5020dca1793SAdrian Knoth #define AIO_IN_SS_CHANNELS        14
5030dca1793SAdrian Knoth #define AIO_IN_DS_CHANNELS        10
5040dca1793SAdrian Knoth #define AIO_IN_QS_CHANNELS        8
5050dca1793SAdrian Knoth #define AIO_OUT_SS_CHANNELS        16
5060dca1793SAdrian Knoth #define AIO_OUT_DS_CHANNELS        12
5070dca1793SAdrian Knoth #define AIO_OUT_QS_CHANNELS        10
5080dca1793SAdrian Knoth 
509763f356cSTakashi Iwai /* the size of a substream (1 mono data stream) */
510763f356cSTakashi Iwai #define HDSPM_CHANNEL_BUFFER_SAMPLES  (16*1024)
511763f356cSTakashi Iwai #define HDSPM_CHANNEL_BUFFER_BYTES    (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
512763f356cSTakashi Iwai 
513763f356cSTakashi Iwai /* the size of the area we need to allocate for DMA transfers. the
514763f356cSTakashi Iwai    size is the same regardless of the number of channels, and
515763f356cSTakashi Iwai    also the latency to use.
516763f356cSTakashi Iwai    for one direction !!!
517763f356cSTakashi Iwai */
518ffb2c3c0SRemy Bruno #define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
519763f356cSTakashi Iwai #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
520763f356cSTakashi Iwai 
5213cee5a60SRemy Bruno /* revisions >= 230 indicate AES32 card */
5220dca1793SAdrian Knoth #define HDSPM_MADI_REV		210
5230dca1793SAdrian Knoth #define HDSPM_RAYDAT_REV	211
5240dca1793SAdrian Knoth #define HDSPM_AIO_REV		212
5250dca1793SAdrian Knoth #define HDSPM_MADIFACE_REV	213
5260dca1793SAdrian Knoth #define HDSPM_AES_REV		240
5273cee5a60SRemy Bruno 
5286534599dSRemy Bruno /* speed factor modes */
5296534599dSRemy Bruno #define HDSPM_SPEED_SINGLE 0
5306534599dSRemy Bruno #define HDSPM_SPEED_DOUBLE 1
5316534599dSRemy Bruno #define HDSPM_SPEED_QUAD   2
5320dca1793SAdrian Knoth 
5336534599dSRemy Bruno /* names for speed modes */
5346534599dSRemy Bruno static char *hdspm_speed_names[] = { "single", "double", "quad" };
5356534599dSRemy Bruno 
5360dca1793SAdrian Knoth static char *texts_autosync_aes_tco[] = { "Word Clock",
5370dca1793SAdrian Knoth 					  "AES1", "AES2", "AES3", "AES4",
5380dca1793SAdrian Knoth 					  "AES5", "AES6", "AES7", "AES8",
5390dca1793SAdrian Knoth 					  "TCO" };
5400dca1793SAdrian Knoth static char *texts_autosync_aes[] = { "Word Clock",
5410dca1793SAdrian Knoth 				      "AES1", "AES2", "AES3", "AES4",
5420dca1793SAdrian Knoth 				      "AES5", "AES6", "AES7", "AES8" };
5430dca1793SAdrian Knoth static char *texts_autosync_madi_tco[] = { "Word Clock",
5440dca1793SAdrian Knoth 					   "MADI", "TCO", "Sync In" };
5450dca1793SAdrian Knoth static char *texts_autosync_madi[] = { "Word Clock",
5460dca1793SAdrian Knoth 				       "MADI", "Sync In" };
5470dca1793SAdrian Knoth 
5480dca1793SAdrian Knoth static char *texts_autosync_raydat_tco[] = {
5490dca1793SAdrian Knoth 	"Word Clock",
5500dca1793SAdrian Knoth 	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
5510dca1793SAdrian Knoth 	"AES", "SPDIF", "TCO", "Sync In"
5520dca1793SAdrian Knoth };
5530dca1793SAdrian Knoth static char *texts_autosync_raydat[] = {
5540dca1793SAdrian Knoth 	"Word Clock",
5550dca1793SAdrian Knoth 	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
5560dca1793SAdrian Knoth 	"AES", "SPDIF", "Sync In"
5570dca1793SAdrian Knoth };
5580dca1793SAdrian Knoth static char *texts_autosync_aio_tco[] = {
5590dca1793SAdrian Knoth 	"Word Clock",
5600dca1793SAdrian Knoth 	"ADAT", "AES", "SPDIF", "TCO", "Sync In"
5610dca1793SAdrian Knoth };
5620dca1793SAdrian Knoth static char *texts_autosync_aio[] = { "Word Clock",
5630dca1793SAdrian Knoth 				      "ADAT", "AES", "SPDIF", "Sync In" };
5640dca1793SAdrian Knoth 
5650dca1793SAdrian Knoth static char *texts_freq[] = {
5660dca1793SAdrian Knoth 	"No Lock",
5670dca1793SAdrian Knoth 	"32 kHz",
5680dca1793SAdrian Knoth 	"44.1 kHz",
5690dca1793SAdrian Knoth 	"48 kHz",
5700dca1793SAdrian Knoth 	"64 kHz",
5710dca1793SAdrian Knoth 	"88.2 kHz",
5720dca1793SAdrian Knoth 	"96 kHz",
5730dca1793SAdrian Knoth 	"128 kHz",
5740dca1793SAdrian Knoth 	"176.4 kHz",
5750dca1793SAdrian Knoth 	"192 kHz"
5760dca1793SAdrian Knoth };
5770dca1793SAdrian Knoth 
5780dca1793SAdrian Knoth static char *texts_ports_madi[] = {
5790dca1793SAdrian Knoth 	"MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6",
5800dca1793SAdrian Knoth 	"MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12",
5810dca1793SAdrian Knoth 	"MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18",
5820dca1793SAdrian Knoth 	"MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24",
5830dca1793SAdrian Knoth 	"MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30",
5840dca1793SAdrian Knoth 	"MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36",
5850dca1793SAdrian Knoth 	"MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42",
5860dca1793SAdrian Knoth 	"MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48",
5870dca1793SAdrian Knoth 	"MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54",
5880dca1793SAdrian Knoth 	"MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60",
5890dca1793SAdrian Knoth 	"MADI.61", "MADI.62", "MADI.63", "MADI.64",
5900dca1793SAdrian Knoth };
5910dca1793SAdrian Knoth 
5920dca1793SAdrian Knoth 
5930dca1793SAdrian Knoth static char *texts_ports_raydat_ss[] = {
5940dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6",
5950dca1793SAdrian Knoth 	"ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
5960dca1793SAdrian Knoth 	"ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2",
5970dca1793SAdrian Knoth 	"ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8",
5980dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6",
5990dca1793SAdrian Knoth 	"ADAT4.7", "ADAT4.8",
6000dca1793SAdrian Knoth 	"AES.L", "AES.R",
6010dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
6020dca1793SAdrian Knoth };
6030dca1793SAdrian Knoth 
6040dca1793SAdrian Knoth static char *texts_ports_raydat_ds[] = {
6050dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4",
6060dca1793SAdrian Knoth 	"ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
6070dca1793SAdrian Knoth 	"ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4",
6080dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4",
6090dca1793SAdrian Knoth 	"AES.L", "AES.R",
6100dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
6110dca1793SAdrian Knoth };
6120dca1793SAdrian Knoth 
6130dca1793SAdrian Knoth static char *texts_ports_raydat_qs[] = {
6140dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2",
6150dca1793SAdrian Knoth 	"ADAT2.1", "ADAT2.2",
6160dca1793SAdrian Knoth 	"ADAT3.1", "ADAT3.2",
6170dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2",
6180dca1793SAdrian Knoth 	"AES.L", "AES.R",
6190dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
6200dca1793SAdrian Knoth };
6210dca1793SAdrian Knoth 
6220dca1793SAdrian Knoth 
6230dca1793SAdrian Knoth static char *texts_ports_aio_in_ss[] = {
6240dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
6250dca1793SAdrian Knoth 	"AES.L", "AES.R",
6260dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
6270dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
6280dca1793SAdrian Knoth 	"ADAT.7", "ADAT.8"
6290dca1793SAdrian Knoth };
6300dca1793SAdrian Knoth 
6310dca1793SAdrian Knoth static char *texts_ports_aio_out_ss[] = {
6320dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
6330dca1793SAdrian Knoth 	"AES.L", "AES.R",
6340dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
6350dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
6360dca1793SAdrian Knoth 	"ADAT.7", "ADAT.8",
6370dca1793SAdrian Knoth 	"Phone.L", "Phone.R"
6380dca1793SAdrian Knoth };
6390dca1793SAdrian Knoth 
6400dca1793SAdrian Knoth static char *texts_ports_aio_in_ds[] = {
6410dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
6420dca1793SAdrian Knoth 	"AES.L", "AES.R",
6430dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
6440dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
6450dca1793SAdrian Knoth };
6460dca1793SAdrian Knoth 
6470dca1793SAdrian Knoth static char *texts_ports_aio_out_ds[] = {
6480dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
6490dca1793SAdrian Knoth 	"AES.L", "AES.R",
6500dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
6510dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
6520dca1793SAdrian Knoth 	"Phone.L", "Phone.R"
6530dca1793SAdrian Knoth };
6540dca1793SAdrian Knoth 
6550dca1793SAdrian Knoth static char *texts_ports_aio_in_qs[] = {
6560dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
6570dca1793SAdrian Knoth 	"AES.L", "AES.R",
6580dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
6590dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
6600dca1793SAdrian Knoth };
6610dca1793SAdrian Knoth 
6620dca1793SAdrian Knoth static char *texts_ports_aio_out_qs[] = {
6630dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
6640dca1793SAdrian Knoth 	"AES.L", "AES.R",
6650dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
6660dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
6670dca1793SAdrian Knoth 	"Phone.L", "Phone.R"
6680dca1793SAdrian Knoth };
6690dca1793SAdrian Knoth 
670432d2500SAdrian Knoth static char *texts_ports_aes32[] = {
671432d2500SAdrian Knoth 	"AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7",
672432d2500SAdrian Knoth 	"AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14",
673432d2500SAdrian Knoth 	"AES.15", "AES.16"
674432d2500SAdrian Knoth };
675432d2500SAdrian Knoth 
67655a57606SAdrian Knoth /* These tables map the ALSA channels 1..N to the channels that we
67755a57606SAdrian Knoth    need to use in order to find the relevant channel buffer. RME
67855a57606SAdrian Knoth    refers to this kind of mapping as between "the ADAT channel and
67955a57606SAdrian Knoth    the DMA channel." We index it using the logical audio channel,
68055a57606SAdrian Knoth    and the value is the DMA channel (i.e. channel buffer number)
68155a57606SAdrian Knoth    where the data for that channel can be read/written from/to.
68255a57606SAdrian Knoth */
68355a57606SAdrian Knoth 
68455a57606SAdrian Knoth static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
68555a57606SAdrian Knoth 	0, 1, 2, 3, 4, 5, 6, 7,
68655a57606SAdrian Knoth 	8, 9, 10, 11, 12, 13, 14, 15,
68755a57606SAdrian Knoth 	16, 17, 18, 19, 20, 21, 22, 23,
68855a57606SAdrian Knoth 	24, 25, 26, 27, 28, 29, 30, 31,
68955a57606SAdrian Knoth 	32, 33, 34, 35, 36, 37, 38, 39,
69055a57606SAdrian Knoth 	40, 41, 42, 43, 44, 45, 46, 47,
69155a57606SAdrian Knoth 	48, 49, 50, 51, 52, 53, 54, 55,
69255a57606SAdrian Knoth 	56, 57, 58, 59, 60, 61, 62, 63
69355a57606SAdrian Knoth };
69455a57606SAdrian Knoth 
69555a57606SAdrian Knoth static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
69655a57606SAdrian Knoth 	4, 5, 6, 7, 8, 9, 10, 11,	/* ADAT 1 */
69755a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT 2 */
69855a57606SAdrian Knoth 	20, 21, 22, 23, 24, 25, 26, 27,	/* ADAT 3 */
69955a57606SAdrian Knoth 	28, 29, 30, 31, 32, 33, 34, 35,	/* ADAT 4 */
70055a57606SAdrian Knoth 	0, 1,			/* AES */
70155a57606SAdrian Knoth 	2, 3,			/* SPDIF */
70255a57606SAdrian Knoth 	-1, -1, -1, -1,
70355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
70455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
70555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
70655a57606SAdrian Knoth };
70755a57606SAdrian Knoth 
70855a57606SAdrian Knoth static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
70955a57606SAdrian Knoth 	4, 5, 6, 7,		/* ADAT 1 */
71055a57606SAdrian Knoth 	8, 9, 10, 11,		/* ADAT 2 */
71155a57606SAdrian Knoth 	12, 13, 14, 15,		/* ADAT 3 */
71255a57606SAdrian Knoth 	16, 17, 18, 19,		/* ADAT 4 */
71355a57606SAdrian Knoth 	0, 1,			/* AES */
71455a57606SAdrian Knoth 	2, 3,			/* SPDIF */
71555a57606SAdrian Knoth 	-1, -1, -1, -1,
71655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
71755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
71855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
71955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
72055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
72155a57606SAdrian Knoth };
72255a57606SAdrian Knoth 
72355a57606SAdrian Knoth static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
72455a57606SAdrian Knoth 	4, 5,			/* ADAT 1 */
72555a57606SAdrian Knoth 	6, 7,			/* ADAT 2 */
72655a57606SAdrian Knoth 	8, 9,			/* ADAT 3 */
72755a57606SAdrian Knoth 	10, 11,			/* ADAT 4 */
72855a57606SAdrian Knoth 	0, 1,			/* AES */
72955a57606SAdrian Knoth 	2, 3,			/* SPDIF */
73055a57606SAdrian Knoth 	-1, -1, -1, -1,
73155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
73255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
73355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
73455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
73555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
73655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
73755a57606SAdrian Knoth };
73855a57606SAdrian Knoth 
73955a57606SAdrian Knoth static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
74055a57606SAdrian Knoth 	0, 1,			/* line in */
74155a57606SAdrian Knoth 	8, 9,			/* aes in, */
74255a57606SAdrian Knoth 	10, 11,			/* spdif in */
74355a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT in */
74455a57606SAdrian Knoth 	-1, -1,
74555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
74655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
74755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
74855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
74955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
75055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
75155a57606SAdrian Knoth };
75255a57606SAdrian Knoth 
75355a57606SAdrian Knoth static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
75455a57606SAdrian Knoth 	0, 1,			/* line out */
75555a57606SAdrian Knoth 	8, 9,			/* aes out */
75655a57606SAdrian Knoth 	10, 11,			/* spdif out */
75755a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT out */
75855a57606SAdrian Knoth 	6, 7,			/* phone out */
75955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
76055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
76155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
76255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
76355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
76455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
76555a57606SAdrian Knoth };
76655a57606SAdrian Knoth 
76755a57606SAdrian Knoth static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
76855a57606SAdrian Knoth 	0, 1,			/* line in */
76955a57606SAdrian Knoth 	8, 9,			/* aes in */
77055a57606SAdrian Knoth 	10, 11,			/* spdif in */
77155a57606SAdrian Knoth 	12, 14, 16, 18,		/* adat in */
77255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1,
77355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
77455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
77555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
77655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
77755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
77855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
77955a57606SAdrian Knoth };
78055a57606SAdrian Knoth 
78155a57606SAdrian Knoth static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
78255a57606SAdrian Knoth 	0, 1,			/* line out */
78355a57606SAdrian Knoth 	8, 9,			/* aes out */
78455a57606SAdrian Knoth 	10, 11,			/* spdif out */
78555a57606SAdrian Knoth 	12, 14, 16, 18,		/* adat out */
78655a57606SAdrian Knoth 	6, 7,			/* phone out */
78755a57606SAdrian Knoth 	-1, -1, -1, -1,
78855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
78955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
79055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
79155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
79255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
79355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
79455a57606SAdrian Knoth };
79555a57606SAdrian Knoth 
79655a57606SAdrian Knoth static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
79755a57606SAdrian Knoth 	0, 1,			/* line in */
79855a57606SAdrian Knoth 	8, 9,			/* aes in */
79955a57606SAdrian Knoth 	10, 11,			/* spdif in */
80055a57606SAdrian Knoth 	12, 16,			/* adat in */
80155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
80255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
80355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
80455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
80555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
80655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
80755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
80855a57606SAdrian Knoth };
80955a57606SAdrian Knoth 
81055a57606SAdrian Knoth static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
81155a57606SAdrian Knoth 	0, 1,			/* line out */
81255a57606SAdrian Knoth 	8, 9,			/* aes out */
81355a57606SAdrian Knoth 	10, 11,			/* spdif out */
81455a57606SAdrian Knoth 	12, 16,			/* adat out */
81555a57606SAdrian Knoth 	6, 7,			/* phone out */
81655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1,
81755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
81855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
81955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
82055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
82155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
82255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
82355a57606SAdrian Knoth };
82455a57606SAdrian Knoth 
825432d2500SAdrian Knoth static char channel_map_aes32[HDSPM_MAX_CHANNELS] = {
826432d2500SAdrian Knoth 	0, 1, 2, 3, 4, 5, 6, 7,
827432d2500SAdrian Knoth 	8, 9, 10, 11, 12, 13, 14, 15,
828432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
829432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
830432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
831432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
832432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
833432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
834432d2500SAdrian Knoth };
835432d2500SAdrian Knoth 
83698274f07STakashi Iwai struct hdspm_midi {
83798274f07STakashi Iwai 	struct hdspm *hdspm;
838763f356cSTakashi Iwai 	int id;
83998274f07STakashi Iwai 	struct snd_rawmidi *rmidi;
84098274f07STakashi Iwai 	struct snd_rawmidi_substream *input;
84198274f07STakashi Iwai 	struct snd_rawmidi_substream *output;
842763f356cSTakashi Iwai 	char istimer;		/* timer in use */
843763f356cSTakashi Iwai 	struct timer_list timer;
844763f356cSTakashi Iwai 	spinlock_t lock;
845763f356cSTakashi Iwai 	int pending;
8460dca1793SAdrian Knoth 	int dataIn;
8470dca1793SAdrian Knoth 	int statusIn;
8480dca1793SAdrian Knoth 	int dataOut;
8490dca1793SAdrian Knoth 	int statusOut;
8500dca1793SAdrian Knoth 	int ie;
8510dca1793SAdrian Knoth 	int irq;
8520dca1793SAdrian Knoth };
8530dca1793SAdrian Knoth 
8540dca1793SAdrian Knoth struct hdspm_tco {
8550dca1793SAdrian Knoth 	int input;
8560dca1793SAdrian Knoth 	int framerate;
8570dca1793SAdrian Knoth 	int wordclock;
8580dca1793SAdrian Knoth 	int samplerate;
8590dca1793SAdrian Knoth 	int pull;
8600dca1793SAdrian Knoth 	int term; /* 0 = off, 1 = on */
861763f356cSTakashi Iwai };
862763f356cSTakashi Iwai 
86398274f07STakashi Iwai struct hdspm {
864763f356cSTakashi Iwai         spinlock_t lock;
865ef5fa1a4STakashi Iwai 	/* only one playback and/or capture stream */
866ef5fa1a4STakashi Iwai         struct snd_pcm_substream *capture_substream;
867ef5fa1a4STakashi Iwai         struct snd_pcm_substream *playback_substream;
868763f356cSTakashi Iwai 
869763f356cSTakashi Iwai 	char *card_name;	     /* for procinfo */
8703cee5a60SRemy Bruno 	unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
8713cee5a60SRemy Bruno 
8720dca1793SAdrian Knoth 	uint8_t io_type;
873763f356cSTakashi Iwai 
874763f356cSTakashi Iwai 	int monitor_outs;	/* set up monitoring outs init flag */
875763f356cSTakashi Iwai 
876763f356cSTakashi Iwai 	u32 control_register;	/* cached value */
877763f356cSTakashi Iwai 	u32 control2_register;	/* cached value */
8780dca1793SAdrian Knoth 	u32 settings_register;
879763f356cSTakashi Iwai 
8800dca1793SAdrian Knoth 	struct hdspm_midi midi[4];
881763f356cSTakashi Iwai 	struct tasklet_struct midi_tasklet;
882763f356cSTakashi Iwai 
883763f356cSTakashi Iwai 	size_t period_bytes;
8840dca1793SAdrian Knoth 	unsigned char ss_in_channels;
8850dca1793SAdrian Knoth 	unsigned char ds_in_channels;
8860dca1793SAdrian Knoth 	unsigned char qs_in_channels;
8870dca1793SAdrian Knoth 	unsigned char ss_out_channels;
8880dca1793SAdrian Knoth 	unsigned char ds_out_channels;
8890dca1793SAdrian Knoth 	unsigned char qs_out_channels;
8900dca1793SAdrian Knoth 
8910dca1793SAdrian Knoth 	unsigned char max_channels_in;
8920dca1793SAdrian Knoth 	unsigned char max_channels_out;
8930dca1793SAdrian Knoth 
8940dca1793SAdrian Knoth 	char *channel_map_in;
8950dca1793SAdrian Knoth 	char *channel_map_out;
8960dca1793SAdrian Knoth 
8970dca1793SAdrian Knoth 	char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
8980dca1793SAdrian Knoth 	char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
8990dca1793SAdrian Knoth 
9000dca1793SAdrian Knoth 	char **port_names_in;
9010dca1793SAdrian Knoth 	char **port_names_out;
9020dca1793SAdrian Knoth 
9030dca1793SAdrian Knoth 	char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs;
9040dca1793SAdrian Knoth 	char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs;
905763f356cSTakashi Iwai 
906763f356cSTakashi Iwai 	unsigned char *playback_buffer;	/* suitably aligned address */
907763f356cSTakashi Iwai 	unsigned char *capture_buffer;	/* suitably aligned address */
908763f356cSTakashi Iwai 
909763f356cSTakashi Iwai 	pid_t capture_pid;	/* process id which uses capture */
910763f356cSTakashi Iwai 	pid_t playback_pid;	/* process id which uses capture */
911763f356cSTakashi Iwai 	int running;		/* running status */
912763f356cSTakashi Iwai 
913763f356cSTakashi Iwai 	int last_external_sample_rate;	/* samplerate mystic ... */
914763f356cSTakashi Iwai 	int last_internal_sample_rate;
915763f356cSTakashi Iwai 	int system_sample_rate;
916763f356cSTakashi Iwai 
917763f356cSTakashi Iwai 	int dev;		/* Hardware vars... */
918763f356cSTakashi Iwai 	int irq;
919763f356cSTakashi Iwai 	unsigned long port;
920763f356cSTakashi Iwai 	void __iomem *iobase;
921763f356cSTakashi Iwai 
922763f356cSTakashi Iwai 	int irq_count;		/* for debug */
9230dca1793SAdrian Knoth 	int midiPorts;
924763f356cSTakashi Iwai 
92598274f07STakashi Iwai 	struct snd_card *card;	/* one card */
92698274f07STakashi Iwai 	struct snd_pcm *pcm;		/* has one pcm */
92798274f07STakashi Iwai 	struct snd_hwdep *hwdep;	/* and a hwdep for additional ioctl */
928763f356cSTakashi Iwai 	struct pci_dev *pci;	/* and an pci info */
929763f356cSTakashi Iwai 
930763f356cSTakashi Iwai 	/* Mixer vars */
931ef5fa1a4STakashi Iwai 	/* fast alsa mixer */
932ef5fa1a4STakashi Iwai 	struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS];
933ef5fa1a4STakashi Iwai 	/* but input to much, so not used */
934ef5fa1a4STakashi Iwai 	struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS];
9350dca1793SAdrian Knoth 	/* full mixer accessable over mixer ioctl or hwdep-device */
936ef5fa1a4STakashi Iwai 	struct hdspm_mixer *mixer;
937763f356cSTakashi Iwai 
9380dca1793SAdrian Knoth 	struct hdspm_tco *tco;  /* NULL if no TCO detected */
939763f356cSTakashi Iwai 
9400dca1793SAdrian Knoth 	char **texts_autosync;
9410dca1793SAdrian Knoth 	int texts_autosync_items;
942763f356cSTakashi Iwai 
9430dca1793SAdrian Knoth 	cycles_t last_interrupt;
944730a5865SJaroslav Kysela 
945730a5865SJaroslav Kysela 	struct hdspm_peak_rms peak_rms;
946763f356cSTakashi Iwai };
947763f356cSTakashi Iwai 
948763f356cSTakashi Iwai 
949cebe41d4SAlexey Dobriyan static DEFINE_PCI_DEVICE_TABLE(snd_hdspm_ids) = {
950763f356cSTakashi Iwai 	{
951763f356cSTakashi Iwai 	 .vendor = PCI_VENDOR_ID_XILINX,
952763f356cSTakashi Iwai 	 .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
953763f356cSTakashi Iwai 	 .subvendor = PCI_ANY_ID,
954763f356cSTakashi Iwai 	 .subdevice = PCI_ANY_ID,
955763f356cSTakashi Iwai 	 .class = 0,
956763f356cSTakashi Iwai 	 .class_mask = 0,
957763f356cSTakashi Iwai 	 .driver_data = 0},
958763f356cSTakashi Iwai 	{0,}
959763f356cSTakashi Iwai };
960763f356cSTakashi Iwai 
961763f356cSTakashi Iwai MODULE_DEVICE_TABLE(pci, snd_hdspm_ids);
962763f356cSTakashi Iwai 
963763f356cSTakashi Iwai /* prototypes */
96498274f07STakashi Iwai static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
96598274f07STakashi Iwai 						   struct hdspm * hdspm);
96698274f07STakashi Iwai static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
96798274f07STakashi Iwai 					  struct hdspm * hdspm);
968763f356cSTakashi Iwai 
96998274f07STakashi Iwai static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
97098274f07STakashi Iwai static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
97198274f07STakashi Iwai static int hdspm_autosync_ref(struct hdspm *hdspm);
97298274f07STakashi Iwai static int snd_hdspm_set_defaults(struct hdspm *hdspm);
97377a23f26STakashi Iwai static void hdspm_set_sgbuf(struct hdspm *hdspm,
97477a23f26STakashi Iwai 			    struct snd_pcm_substream *substream,
975763f356cSTakashi Iwai 			     unsigned int reg, int channels);
976763f356cSTakashi Iwai 
9773cee5a60SRemy Bruno static inline int HDSPM_bit2freq(int n)
9783cee5a60SRemy Bruno {
97962cef821SDenys Vlasenko 	static const int bit2freq_tab[] = {
98062cef821SDenys Vlasenko 		0, 32000, 44100, 48000, 64000, 88200,
9813cee5a60SRemy Bruno 		96000, 128000, 176400, 192000 };
9823cee5a60SRemy Bruno 	if (n < 1 || n > 9)
9833cee5a60SRemy Bruno 		return 0;
9843cee5a60SRemy Bruno 	return bit2freq_tab[n];
9853cee5a60SRemy Bruno }
9863cee5a60SRemy Bruno 
9870dca1793SAdrian Knoth /* Write/read to/from HDSPM with Adresses in Bytes
988763f356cSTakashi Iwai    not words but only 32Bit writes are allowed */
989763f356cSTakashi Iwai 
99098274f07STakashi Iwai static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg,
991763f356cSTakashi Iwai 			       unsigned int val)
992763f356cSTakashi Iwai {
993763f356cSTakashi Iwai 	writel(val, hdspm->iobase + reg);
994763f356cSTakashi Iwai }
995763f356cSTakashi Iwai 
99698274f07STakashi Iwai static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg)
997763f356cSTakashi Iwai {
998763f356cSTakashi Iwai 	return readl(hdspm->iobase + reg);
999763f356cSTakashi Iwai }
1000763f356cSTakashi Iwai 
1001763f356cSTakashi Iwai /* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
1002763f356cSTakashi Iwai    mixer is write only on hardware so we have to cache him for read
1003763f356cSTakashi Iwai    each fader is a u32, but uses only the first 16 bit */
1004763f356cSTakashi Iwai 
100598274f07STakashi Iwai static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan,
1006763f356cSTakashi Iwai 				     unsigned int in)
1007763f356cSTakashi Iwai {
10085bab2482SAdrian Bunk 	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
1009763f356cSTakashi Iwai 		return 0;
1010763f356cSTakashi Iwai 
1011763f356cSTakashi Iwai 	return hdspm->mixer->ch[chan].in[in];
1012763f356cSTakashi Iwai }
1013763f356cSTakashi Iwai 
101498274f07STakashi Iwai static inline int hdspm_read_pb_gain(struct hdspm * hdspm, unsigned int chan,
1015763f356cSTakashi Iwai 				     unsigned int pb)
1016763f356cSTakashi Iwai {
10175bab2482SAdrian Bunk 	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
1018763f356cSTakashi Iwai 		return 0;
1019763f356cSTakashi Iwai 	return hdspm->mixer->ch[chan].pb[pb];
1020763f356cSTakashi Iwai }
1021763f356cSTakashi Iwai 
102262cef821SDenys Vlasenko static int hdspm_write_in_gain(struct hdspm *hdspm, unsigned int chan,
1023763f356cSTakashi Iwai 				      unsigned int in, unsigned short data)
1024763f356cSTakashi Iwai {
1025763f356cSTakashi Iwai 	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
1026763f356cSTakashi Iwai 		return -1;
1027763f356cSTakashi Iwai 
1028763f356cSTakashi Iwai 	hdspm_write(hdspm,
1029763f356cSTakashi Iwai 		    HDSPM_MADI_mixerBase +
1030763f356cSTakashi Iwai 		    ((in + 128 * chan) * sizeof(u32)),
1031763f356cSTakashi Iwai 		    (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF));
1032763f356cSTakashi Iwai 	return 0;
1033763f356cSTakashi Iwai }
1034763f356cSTakashi Iwai 
103562cef821SDenys Vlasenko static int hdspm_write_pb_gain(struct hdspm *hdspm, unsigned int chan,
1036763f356cSTakashi Iwai 				      unsigned int pb, unsigned short data)
1037763f356cSTakashi Iwai {
1038763f356cSTakashi Iwai 	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
1039763f356cSTakashi Iwai 		return -1;
1040763f356cSTakashi Iwai 
1041763f356cSTakashi Iwai 	hdspm_write(hdspm,
1042763f356cSTakashi Iwai 		    HDSPM_MADI_mixerBase +
1043763f356cSTakashi Iwai 		    ((64 + pb + 128 * chan) * sizeof(u32)),
1044763f356cSTakashi Iwai 		    (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF));
1045763f356cSTakashi Iwai 	return 0;
1046763f356cSTakashi Iwai }
1047763f356cSTakashi Iwai 
1048763f356cSTakashi Iwai 
1049763f356cSTakashi Iwai /* enable DMA for specific channels, now available for DSP-MADI */
105098274f07STakashi Iwai static inline void snd_hdspm_enable_in(struct hdspm * hdspm, int i, int v)
1051763f356cSTakashi Iwai {
1052763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_inputEnableBase + (4 * i), v);
1053763f356cSTakashi Iwai }
1054763f356cSTakashi Iwai 
105598274f07STakashi Iwai static inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v)
1056763f356cSTakashi Iwai {
1057763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_outputEnableBase + (4 * i), v);
1058763f356cSTakashi Iwai }
1059763f356cSTakashi Iwai 
1060763f356cSTakashi Iwai /* check if same process is writing and reading */
106162cef821SDenys Vlasenko static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
1062763f356cSTakashi Iwai {
1063763f356cSTakashi Iwai 	unsigned long flags;
1064763f356cSTakashi Iwai 	int ret = 1;
1065763f356cSTakashi Iwai 
1066763f356cSTakashi Iwai 	spin_lock_irqsave(&hdspm->lock, flags);
1067763f356cSTakashi Iwai 	if ((hdspm->playback_pid != hdspm->capture_pid) &&
1068763f356cSTakashi Iwai 	    (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) {
1069763f356cSTakashi Iwai 		ret = 0;
1070763f356cSTakashi Iwai 	}
1071763f356cSTakashi Iwai 	spin_unlock_irqrestore(&hdspm->lock, flags);
1072763f356cSTakashi Iwai 	return ret;
1073763f356cSTakashi Iwai }
1074763f356cSTakashi Iwai 
1075763f356cSTakashi Iwai /* check for external sample rate */
107662cef821SDenys Vlasenko static int hdspm_external_sample_rate(struct hdspm *hdspm)
1077763f356cSTakashi Iwai {
10780dca1793SAdrian Knoth 	unsigned int status, status2, timecode;
10790dca1793SAdrian Knoth 	int syncref, rate = 0, rate_bits;
10803cee5a60SRemy Bruno 
10810dca1793SAdrian Knoth 	switch (hdspm->io_type) {
10820dca1793SAdrian Knoth 	case AES32:
10830dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
10840dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
10850dca1793SAdrian Knoth 		timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
10860dca1793SAdrian Knoth 
10870dca1793SAdrian Knoth 		syncref = hdspm_autosync_ref(hdspm);
10883cee5a60SRemy Bruno 
10893cee5a60SRemy Bruno 		if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD &&
10903cee5a60SRemy Bruno 				status & HDSPM_AES32_wcLock)
10910dca1793SAdrian Knoth 			return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF);
10920dca1793SAdrian Knoth 
10933cee5a60SRemy Bruno 		if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 &&
10943cee5a60SRemy Bruno 				syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
10953cee5a60SRemy Bruno 				status2 & (HDSPM_LockAES >>
10963cee5a60SRemy Bruno 				(syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
10970dca1793SAdrian Knoth 			return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
10983cee5a60SRemy Bruno 		return 0;
10990dca1793SAdrian Knoth 		break;
11000dca1793SAdrian Knoth 
11010dca1793SAdrian Knoth 	case MADIface:
11020dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
11030dca1793SAdrian Knoth 
11040dca1793SAdrian Knoth 		if (!(status & HDSPM_madiLock)) {
11050dca1793SAdrian Knoth 			rate = 0;  /* no lock */
11063cee5a60SRemy Bruno 		} else {
11070dca1793SAdrian Knoth 			switch (status & (HDSPM_status1_freqMask)) {
11080dca1793SAdrian Knoth 			case HDSPM_status1_F_0*1:
11090dca1793SAdrian Knoth 				rate = 32000; break;
11100dca1793SAdrian Knoth 			case HDSPM_status1_F_0*2:
11110dca1793SAdrian Knoth 				rate = 44100; break;
11120dca1793SAdrian Knoth 			case HDSPM_status1_F_0*3:
11130dca1793SAdrian Knoth 				rate = 48000; break;
11140dca1793SAdrian Knoth 			case HDSPM_status1_F_0*4:
11150dca1793SAdrian Knoth 				rate = 64000; break;
11160dca1793SAdrian Knoth 			case HDSPM_status1_F_0*5:
11170dca1793SAdrian Knoth 				rate = 88200; break;
11180dca1793SAdrian Knoth 			case HDSPM_status1_F_0*6:
11190dca1793SAdrian Knoth 				rate = 96000; break;
11200dca1793SAdrian Knoth 			case HDSPM_status1_F_0*7:
11210dca1793SAdrian Knoth 				rate = 128000; break;
11220dca1793SAdrian Knoth 			case HDSPM_status1_F_0*8:
11230dca1793SAdrian Knoth 				rate = 176400; break;
11240dca1793SAdrian Knoth 			case HDSPM_status1_F_0*9:
11250dca1793SAdrian Knoth 				rate = 192000; break;
11260dca1793SAdrian Knoth 			default:
11270dca1793SAdrian Knoth 				rate = 0; break;
11280dca1793SAdrian Knoth 			}
11290dca1793SAdrian Knoth 		}
11300dca1793SAdrian Knoth 
11310dca1793SAdrian Knoth 		break;
11320dca1793SAdrian Knoth 
11330dca1793SAdrian Knoth 	case MADI:
11340dca1793SAdrian Knoth 	case AIO:
11350dca1793SAdrian Knoth 	case RayDAT:
11360dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
11370dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
11380dca1793SAdrian Knoth 		rate = 0;
1139763f356cSTakashi Iwai 
1140763f356cSTakashi Iwai 		/* if wordclock has synced freq and wordclock is valid */
1141763f356cSTakashi Iwai 		if ((status2 & HDSPM_wcLock) != 0 &&
1142763f356cSTakashi Iwai 				(status & HDSPM_SelSyncRef0) == 0) {
1143763f356cSTakashi Iwai 
1144763f356cSTakashi Iwai 			rate_bits = status2 & HDSPM_wcFreqMask;
1145763f356cSTakashi Iwai 
11460dca1793SAdrian Knoth 
1147763f356cSTakashi Iwai 			switch (rate_bits) {
1148763f356cSTakashi Iwai 			case HDSPM_wcFreq32:
1149763f356cSTakashi Iwai 				rate = 32000;
1150763f356cSTakashi Iwai 				break;
1151763f356cSTakashi Iwai 			case HDSPM_wcFreq44_1:
1152763f356cSTakashi Iwai 				rate = 44100;
1153763f356cSTakashi Iwai 				break;
1154763f356cSTakashi Iwai 			case HDSPM_wcFreq48:
1155763f356cSTakashi Iwai 				rate = 48000;
1156763f356cSTakashi Iwai 				break;
1157763f356cSTakashi Iwai 			case HDSPM_wcFreq64:
1158763f356cSTakashi Iwai 				rate = 64000;
1159763f356cSTakashi Iwai 				break;
1160763f356cSTakashi Iwai 			case HDSPM_wcFreq88_2:
1161763f356cSTakashi Iwai 				rate = 88200;
1162763f356cSTakashi Iwai 				break;
1163763f356cSTakashi Iwai 			case HDSPM_wcFreq96:
1164763f356cSTakashi Iwai 				rate = 96000;
1165763f356cSTakashi Iwai 				break;
1166763f356cSTakashi Iwai 			default:
1167763f356cSTakashi Iwai 				rate = 0;
1168763f356cSTakashi Iwai 				break;
1169763f356cSTakashi Iwai 			}
1170763f356cSTakashi Iwai 		}
1171763f356cSTakashi Iwai 
1172ef5fa1a4STakashi Iwai 		/* if rate detected and Syncref is Word than have it,
1173ef5fa1a4STakashi Iwai 		 * word has priority to MADI
1174ef5fa1a4STakashi Iwai 		 */
11753cee5a60SRemy Bruno 		if (rate != 0 &&
11763cee5a60SRemy Bruno 		(status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
1177763f356cSTakashi Iwai 			return rate;
1178763f356cSTakashi Iwai 
11790dca1793SAdrian Knoth 		/* maybe a madi input (which is taken if sel sync is madi) */
1180763f356cSTakashi Iwai 		if (status & HDSPM_madiLock) {
1181763f356cSTakashi Iwai 			rate_bits = status & HDSPM_madiFreqMask;
1182763f356cSTakashi Iwai 
1183763f356cSTakashi Iwai 			switch (rate_bits) {
1184763f356cSTakashi Iwai 			case HDSPM_madiFreq32:
1185763f356cSTakashi Iwai 				rate = 32000;
1186763f356cSTakashi Iwai 				break;
1187763f356cSTakashi Iwai 			case HDSPM_madiFreq44_1:
1188763f356cSTakashi Iwai 				rate = 44100;
1189763f356cSTakashi Iwai 				break;
1190763f356cSTakashi Iwai 			case HDSPM_madiFreq48:
1191763f356cSTakashi Iwai 				rate = 48000;
1192763f356cSTakashi Iwai 				break;
1193763f356cSTakashi Iwai 			case HDSPM_madiFreq64:
1194763f356cSTakashi Iwai 				rate = 64000;
1195763f356cSTakashi Iwai 				break;
1196763f356cSTakashi Iwai 			case HDSPM_madiFreq88_2:
1197763f356cSTakashi Iwai 				rate = 88200;
1198763f356cSTakashi Iwai 				break;
1199763f356cSTakashi Iwai 			case HDSPM_madiFreq96:
1200763f356cSTakashi Iwai 				rate = 96000;
1201763f356cSTakashi Iwai 				break;
1202763f356cSTakashi Iwai 			case HDSPM_madiFreq128:
1203763f356cSTakashi Iwai 				rate = 128000;
1204763f356cSTakashi Iwai 				break;
1205763f356cSTakashi Iwai 			case HDSPM_madiFreq176_4:
1206763f356cSTakashi Iwai 				rate = 176400;
1207763f356cSTakashi Iwai 				break;
1208763f356cSTakashi Iwai 			case HDSPM_madiFreq192:
1209763f356cSTakashi Iwai 				rate = 192000;
1210763f356cSTakashi Iwai 				break;
1211763f356cSTakashi Iwai 			default:
1212763f356cSTakashi Iwai 				rate = 0;
1213763f356cSTakashi Iwai 				break;
1214763f356cSTakashi Iwai 			}
1215763f356cSTakashi Iwai 		}
12160dca1793SAdrian Knoth 		break;
1217763f356cSTakashi Iwai 	}
12180dca1793SAdrian Knoth 
12190dca1793SAdrian Knoth 	return rate;
12203cee5a60SRemy Bruno }
1221763f356cSTakashi Iwai 
1222763f356cSTakashi Iwai /* Latency function */
122398274f07STakashi Iwai static inline void hdspm_compute_period_size(struct hdspm *hdspm)
1224763f356cSTakashi Iwai {
12250dca1793SAdrian Knoth 	hdspm->period_bytes = 1 << ((hdspm_decode_latency(hdspm->control_register) + 8));
1226763f356cSTakashi Iwai }
1227763f356cSTakashi Iwai 
12280dca1793SAdrian Knoth 
122998274f07STakashi Iwai static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm)
1230763f356cSTakashi Iwai {
1231763f356cSTakashi Iwai 	int position;
1232763f356cSTakashi Iwai 
1233763f356cSTakashi Iwai 	position = hdspm_read(hdspm, HDSPM_statusRegister);
1234483cee77SAdrian Knoth 
1235483cee77SAdrian Knoth 	switch (hdspm->io_type) {
1236483cee77SAdrian Knoth 	case RayDAT:
1237483cee77SAdrian Knoth 	case AIO:
1238763f356cSTakashi Iwai 		position &= HDSPM_BufferPositionMask;
12390dca1793SAdrian Knoth 		position /= 4; /* Bytes per sample */
1240483cee77SAdrian Knoth 		break;
1241483cee77SAdrian Knoth 	default:
1242483cee77SAdrian Knoth 		position = (position & HDSPM_BufferID) ?
1243483cee77SAdrian Knoth 			(hdspm->period_bytes / 4) : 0;
1244483cee77SAdrian Knoth 	}
1245763f356cSTakashi Iwai 
1246763f356cSTakashi Iwai 	return position;
1247763f356cSTakashi Iwai }
1248763f356cSTakashi Iwai 
1249763f356cSTakashi Iwai 
125098274f07STakashi Iwai static inline void hdspm_start_audio(struct hdspm * s)
1251763f356cSTakashi Iwai {
1252763f356cSTakashi Iwai 	s->control_register |= (HDSPM_AudioInterruptEnable | HDSPM_Start);
1253763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1254763f356cSTakashi Iwai }
1255763f356cSTakashi Iwai 
125698274f07STakashi Iwai static inline void hdspm_stop_audio(struct hdspm * s)
1257763f356cSTakashi Iwai {
1258763f356cSTakashi Iwai 	s->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable);
1259763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1260763f356cSTakashi Iwai }
1261763f356cSTakashi Iwai 
1262763f356cSTakashi Iwai /* should I silence all or only opened ones ? doit all for first even is 4MB*/
126362cef821SDenys Vlasenko static void hdspm_silence_playback(struct hdspm *hdspm)
1264763f356cSTakashi Iwai {
1265763f356cSTakashi Iwai 	int i;
1266763f356cSTakashi Iwai 	int n = hdspm->period_bytes;
1267763f356cSTakashi Iwai 	void *buf = hdspm->playback_buffer;
1268763f356cSTakashi Iwai 
12693cee5a60SRemy Bruno 	if (buf == NULL)
12703cee5a60SRemy Bruno 		return;
1271763f356cSTakashi Iwai 
1272763f356cSTakashi Iwai 	for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
1273763f356cSTakashi Iwai 		memset(buf, 0, n);
1274763f356cSTakashi Iwai 		buf += HDSPM_CHANNEL_BUFFER_BYTES;
1275763f356cSTakashi Iwai 	}
1276763f356cSTakashi Iwai }
1277763f356cSTakashi Iwai 
127898274f07STakashi Iwai static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
1279763f356cSTakashi Iwai {
1280763f356cSTakashi Iwai 	int n;
1281763f356cSTakashi Iwai 
1282763f356cSTakashi Iwai 	spin_lock_irq(&s->lock);
1283763f356cSTakashi Iwai 
1284763f356cSTakashi Iwai 	frames >>= 7;
1285763f356cSTakashi Iwai 	n = 0;
1286763f356cSTakashi Iwai 	while (frames) {
1287763f356cSTakashi Iwai 		n++;
1288763f356cSTakashi Iwai 		frames >>= 1;
1289763f356cSTakashi Iwai 	}
1290763f356cSTakashi Iwai 	s->control_register &= ~HDSPM_LatencyMask;
1291763f356cSTakashi Iwai 	s->control_register |= hdspm_encode_latency(n);
1292763f356cSTakashi Iwai 
1293763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1294763f356cSTakashi Iwai 
1295763f356cSTakashi Iwai 	hdspm_compute_period_size(s);
1296763f356cSTakashi Iwai 
1297763f356cSTakashi Iwai 	spin_unlock_irq(&s->lock);
1298763f356cSTakashi Iwai 
1299763f356cSTakashi Iwai 	return 0;
1300763f356cSTakashi Iwai }
1301763f356cSTakashi Iwai 
13020dca1793SAdrian Knoth static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period)
13030dca1793SAdrian Knoth {
13040dca1793SAdrian Knoth 	u64 freq_const;
13050dca1793SAdrian Knoth 
13060dca1793SAdrian Knoth 	if (period == 0)
13070dca1793SAdrian Knoth 		return 0;
13080dca1793SAdrian Knoth 
13090dca1793SAdrian Knoth 	switch (hdspm->io_type) {
13100dca1793SAdrian Knoth 	case MADI:
13110dca1793SAdrian Knoth 	case AES32:
13120dca1793SAdrian Knoth 		freq_const = 110069313433624ULL;
13130dca1793SAdrian Knoth 		break;
13140dca1793SAdrian Knoth 	case RayDAT:
13150dca1793SAdrian Knoth 	case AIO:
13160dca1793SAdrian Knoth 		freq_const = 104857600000000ULL;
13170dca1793SAdrian Knoth 		break;
13180dca1793SAdrian Knoth 	case MADIface:
13190dca1793SAdrian Knoth 		freq_const = 131072000000000ULL;
13200dca1793SAdrian Knoth 	}
13210dca1793SAdrian Knoth 
13220dca1793SAdrian Knoth 	return div_u64(freq_const, period);
13230dca1793SAdrian Knoth }
13240dca1793SAdrian Knoth 
13250dca1793SAdrian Knoth 
1326ffb2c3c0SRemy Bruno static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
1327ffb2c3c0SRemy Bruno {
1328ffb2c3c0SRemy Bruno 	u64 n;
1329ffb2c3c0SRemy Bruno 
1330ffb2c3c0SRemy Bruno 	if (rate >= 112000)
1331ffb2c3c0SRemy Bruno 		rate /= 4;
1332ffb2c3c0SRemy Bruno 	else if (rate >= 56000)
1333ffb2c3c0SRemy Bruno 		rate /= 2;
1334ffb2c3c0SRemy Bruno 
13350dca1793SAdrian Knoth 	switch (hdspm->io_type) {
13360dca1793SAdrian Knoth 	case MADIface:
13370dca1793SAdrian Knoth 	  n = 131072000000000ULL;  /* 125 MHz */
13380dca1793SAdrian Knoth 	  break;
13390dca1793SAdrian Knoth 	case MADI:
13400dca1793SAdrian Knoth 	case AES32:
13410dca1793SAdrian Knoth 	  n = 110069313433624ULL;  /* 105 MHz */
13420dca1793SAdrian Knoth 	  break;
13430dca1793SAdrian Knoth 	case RayDAT:
13440dca1793SAdrian Knoth 	case AIO:
13450dca1793SAdrian Knoth 	  n = 104857600000000ULL;  /* 100 MHz */
13460dca1793SAdrian Knoth 	  break;
13470dca1793SAdrian Knoth 	}
13480dca1793SAdrian Knoth 
13493f7440a6STakashi Iwai 	n = div_u64(n, rate);
1350ffb2c3c0SRemy Bruno 	/* n should be less than 2^32 for being written to FREQ register */
1351da3cec35STakashi Iwai 	snd_BUG_ON(n >> 32);
1352ffb2c3c0SRemy Bruno 	hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
1353ffb2c3c0SRemy Bruno }
1354763f356cSTakashi Iwai 
1355763f356cSTakashi Iwai /* dummy set rate lets see what happens */
135698274f07STakashi Iwai static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
1357763f356cSTakashi Iwai {
1358763f356cSTakashi Iwai 	int current_rate;
1359763f356cSTakashi Iwai 	int rate_bits;
1360763f356cSTakashi Iwai 	int not_set = 0;
13616534599dSRemy Bruno 	int current_speed, target_speed;
1362763f356cSTakashi Iwai 
1363763f356cSTakashi Iwai 	/* ASSUMPTION: hdspm->lock is either set, or there is no need for
1364763f356cSTakashi Iwai 	   it (e.g. during module initialization).
1365763f356cSTakashi Iwai 	 */
1366763f356cSTakashi Iwai 
1367763f356cSTakashi Iwai 	if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
1368763f356cSTakashi Iwai 
1369763f356cSTakashi Iwai 		/* SLAVE --- */
1370763f356cSTakashi Iwai 		if (called_internally) {
1371763f356cSTakashi Iwai 
1372763f356cSTakashi Iwai 			/* request from ctl or card initialization
1373763f356cSTakashi Iwai 			   just make a warning an remember setting
1374763f356cSTakashi Iwai 			   for future master mode switching */
1375763f356cSTakashi Iwai 
1376ef5fa1a4STakashi Iwai 			snd_printk(KERN_WARNING "HDSPM: "
1377ef5fa1a4STakashi Iwai 				   "Warning: device is not running "
1378ef5fa1a4STakashi Iwai 				   "as a clock master.\n");
1379763f356cSTakashi Iwai 			not_set = 1;
1380763f356cSTakashi Iwai 		} else {
1381763f356cSTakashi Iwai 
1382763f356cSTakashi Iwai 			/* hw_param request while in AutoSync mode */
1383763f356cSTakashi Iwai 			int external_freq =
1384763f356cSTakashi Iwai 			    hdspm_external_sample_rate(hdspm);
1385763f356cSTakashi Iwai 
1386ef5fa1a4STakashi Iwai 			if (hdspm_autosync_ref(hdspm) ==
1387ef5fa1a4STakashi Iwai 			    HDSPM_AUTOSYNC_FROM_NONE) {
1388763f356cSTakashi Iwai 
1389ef5fa1a4STakashi Iwai 				snd_printk(KERN_WARNING "HDSPM: "
1390ef5fa1a4STakashi Iwai 					   "Detected no Externel Sync \n");
1391763f356cSTakashi Iwai 				not_set = 1;
1392763f356cSTakashi Iwai 
1393763f356cSTakashi Iwai 			} else if (rate != external_freq) {
1394763f356cSTakashi Iwai 
1395ef5fa1a4STakashi Iwai 				snd_printk(KERN_WARNING "HDSPM: "
1396ef5fa1a4STakashi Iwai 					   "Warning: No AutoSync source for "
1397ef5fa1a4STakashi Iwai 					   "requested rate\n");
1398763f356cSTakashi Iwai 				not_set = 1;
1399763f356cSTakashi Iwai 			}
1400763f356cSTakashi Iwai 		}
1401763f356cSTakashi Iwai 	}
1402763f356cSTakashi Iwai 
1403763f356cSTakashi Iwai 	current_rate = hdspm->system_sample_rate;
1404763f356cSTakashi Iwai 
1405763f356cSTakashi Iwai 	/* Changing between Singe, Double and Quad speed is not
1406763f356cSTakashi Iwai 	   allowed if any substreams are open. This is because such a change
1407763f356cSTakashi Iwai 	   causes a shift in the location of the DMA buffers and a reduction
1408763f356cSTakashi Iwai 	   in the number of available buffers.
1409763f356cSTakashi Iwai 
1410763f356cSTakashi Iwai 	   Note that a similar but essentially insoluble problem exists for
1411763f356cSTakashi Iwai 	   externally-driven rate changes. All we can do is to flag rate
1412763f356cSTakashi Iwai 	   changes in the read/write routines.
1413763f356cSTakashi Iwai 	 */
1414763f356cSTakashi Iwai 
14156534599dSRemy Bruno 	if (current_rate <= 48000)
14166534599dSRemy Bruno 		current_speed = HDSPM_SPEED_SINGLE;
14176534599dSRemy Bruno 	else if (current_rate <= 96000)
14186534599dSRemy Bruno 		current_speed = HDSPM_SPEED_DOUBLE;
14196534599dSRemy Bruno 	else
14206534599dSRemy Bruno 		current_speed = HDSPM_SPEED_QUAD;
14216534599dSRemy Bruno 
14226534599dSRemy Bruno 	if (rate <= 48000)
14236534599dSRemy Bruno 		target_speed = HDSPM_SPEED_SINGLE;
14246534599dSRemy Bruno 	else if (rate <= 96000)
14256534599dSRemy Bruno 		target_speed = HDSPM_SPEED_DOUBLE;
14266534599dSRemy Bruno 	else
14276534599dSRemy Bruno 		target_speed = HDSPM_SPEED_QUAD;
14283cee5a60SRemy Bruno 
1429763f356cSTakashi Iwai 	switch (rate) {
1430763f356cSTakashi Iwai 	case 32000:
1431763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency32KHz;
1432763f356cSTakashi Iwai 		break;
1433763f356cSTakashi Iwai 	case 44100:
1434763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency44_1KHz;
1435763f356cSTakashi Iwai 		break;
1436763f356cSTakashi Iwai 	case 48000:
1437763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency48KHz;
1438763f356cSTakashi Iwai 		break;
1439763f356cSTakashi Iwai 	case 64000:
1440763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency64KHz;
1441763f356cSTakashi Iwai 		break;
1442763f356cSTakashi Iwai 	case 88200:
1443763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency88_2KHz;
1444763f356cSTakashi Iwai 		break;
1445763f356cSTakashi Iwai 	case 96000:
1446763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency96KHz;
1447763f356cSTakashi Iwai 		break;
14483cee5a60SRemy Bruno 	case 128000:
14493cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency128KHz;
14503cee5a60SRemy Bruno 		break;
14513cee5a60SRemy Bruno 	case 176400:
14523cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency176_4KHz;
14533cee5a60SRemy Bruno 		break;
14543cee5a60SRemy Bruno 	case 192000:
14553cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency192KHz;
14563cee5a60SRemy Bruno 		break;
1457763f356cSTakashi Iwai 	default:
1458763f356cSTakashi Iwai 		return -EINVAL;
1459763f356cSTakashi Iwai 	}
1460763f356cSTakashi Iwai 
14616534599dSRemy Bruno 	if (current_speed != target_speed
1462763f356cSTakashi Iwai 	    && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
1463763f356cSTakashi Iwai 		snd_printk
1464ef5fa1a4STakashi Iwai 		    (KERN_ERR "HDSPM: "
14656534599dSRemy Bruno 		     "cannot change from %s speed to %s speed mode "
1466ef5fa1a4STakashi Iwai 		     "(capture PID = %d, playback PID = %d)\n",
14676534599dSRemy Bruno 		     hdspm_speed_names[current_speed],
14686534599dSRemy Bruno 		     hdspm_speed_names[target_speed],
1469763f356cSTakashi Iwai 		     hdspm->capture_pid, hdspm->playback_pid);
1470763f356cSTakashi Iwai 		return -EBUSY;
1471763f356cSTakashi Iwai 	}
1472763f356cSTakashi Iwai 
1473763f356cSTakashi Iwai 	hdspm->control_register &= ~HDSPM_FrequencyMask;
1474763f356cSTakashi Iwai 	hdspm->control_register |= rate_bits;
1475763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
1476763f356cSTakashi Iwai 
1477ffb2c3c0SRemy Bruno 	/* For AES32, need to set DDS value in FREQ register
1478ffb2c3c0SRemy Bruno 	   For MADI, also apparently */
1479ffb2c3c0SRemy Bruno 	hdspm_set_dds_value(hdspm, rate);
1480ffb2c3c0SRemy Bruno 
14810dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type && rate != current_rate)
1482ffb2c3c0SRemy Bruno 		hdspm_write(hdspm, HDSPM_eeprom_wr, 0);
1483ffb2c3c0SRemy Bruno 
1484763f356cSTakashi Iwai 	hdspm->system_sample_rate = rate;
1485763f356cSTakashi Iwai 
14860dca1793SAdrian Knoth 	if (rate <= 48000) {
14870dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_ss;
14880dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_ss;
14890dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->ss_in_channels;
14900dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->ss_out_channels;
14910dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_ss;
14920dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_ss;
14930dca1793SAdrian Knoth 	} else if (rate <= 96000) {
14940dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_ds;
14950dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_ds;
14960dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->ds_in_channels;
14970dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->ds_out_channels;
14980dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_ds;
14990dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_ds;
15000dca1793SAdrian Knoth 	} else {
15010dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_qs;
15020dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_qs;
15030dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->qs_in_channels;
15040dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->qs_out_channels;
15050dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_qs;
15060dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_qs;
15070dca1793SAdrian Knoth 	}
15080dca1793SAdrian Knoth 
1509763f356cSTakashi Iwai 	if (not_set != 0)
1510763f356cSTakashi Iwai 		return -1;
1511763f356cSTakashi Iwai 
1512763f356cSTakashi Iwai 	return 0;
1513763f356cSTakashi Iwai }
1514763f356cSTakashi Iwai 
1515763f356cSTakashi Iwai /* mainly for init to 0 on load */
151698274f07STakashi Iwai static void all_in_all_mixer(struct hdspm * hdspm, int sgain)
1517763f356cSTakashi Iwai {
1518763f356cSTakashi Iwai 	int i, j;
1519ef5fa1a4STakashi Iwai 	unsigned int gain;
1520ef5fa1a4STakashi Iwai 
1521ef5fa1a4STakashi Iwai 	if (sgain > UNITY_GAIN)
1522ef5fa1a4STakashi Iwai 		gain = UNITY_GAIN;
1523ef5fa1a4STakashi Iwai 	else if (sgain < 0)
1524ef5fa1a4STakashi Iwai 		gain = 0;
1525ef5fa1a4STakashi Iwai 	else
1526ef5fa1a4STakashi Iwai 		gain = sgain;
1527763f356cSTakashi Iwai 
1528763f356cSTakashi Iwai 	for (i = 0; i < HDSPM_MIXER_CHANNELS; i++)
1529763f356cSTakashi Iwai 		for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) {
1530763f356cSTakashi Iwai 			hdspm_write_in_gain(hdspm, i, j, gain);
1531763f356cSTakashi Iwai 			hdspm_write_pb_gain(hdspm, i, j, gain);
1532763f356cSTakashi Iwai 		}
1533763f356cSTakashi Iwai }
1534763f356cSTakashi Iwai 
1535763f356cSTakashi Iwai /*----------------------------------------------------------------------------
1536763f356cSTakashi Iwai    MIDI
1537763f356cSTakashi Iwai   ----------------------------------------------------------------------------*/
1538763f356cSTakashi Iwai 
1539ef5fa1a4STakashi Iwai static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm,
1540ef5fa1a4STakashi Iwai 						      int id)
1541763f356cSTakashi Iwai {
1542763f356cSTakashi Iwai 	/* the hardware already does the relevant bit-mask with 0xff */
15430dca1793SAdrian Knoth 	return hdspm_read(hdspm, hdspm->midi[id].dataIn);
1544763f356cSTakashi Iwai }
1545763f356cSTakashi Iwai 
1546ef5fa1a4STakashi Iwai static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id,
1547ef5fa1a4STakashi Iwai 					      int val)
1548763f356cSTakashi Iwai {
1549763f356cSTakashi Iwai 	/* the hardware already does the relevant bit-mask with 0xff */
15500dca1793SAdrian Knoth 	return hdspm_write(hdspm, hdspm->midi[id].dataOut, val);
1551763f356cSTakashi Iwai }
1552763f356cSTakashi Iwai 
155398274f07STakashi Iwai static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id)
1554763f356cSTakashi Iwai {
15550dca1793SAdrian Knoth 	return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF;
1556763f356cSTakashi Iwai }
1557763f356cSTakashi Iwai 
155898274f07STakashi Iwai static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id)
1559763f356cSTakashi Iwai {
1560763f356cSTakashi Iwai 	int fifo_bytes_used;
1561763f356cSTakashi Iwai 
15620dca1793SAdrian Knoth 	fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF;
1563763f356cSTakashi Iwai 
1564763f356cSTakashi Iwai 	if (fifo_bytes_used < 128)
1565763f356cSTakashi Iwai 		return  128 - fifo_bytes_used;
1566763f356cSTakashi Iwai 	else
1567763f356cSTakashi Iwai 		return 0;
1568763f356cSTakashi Iwai }
1569763f356cSTakashi Iwai 
157062cef821SDenys Vlasenko static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id)
1571763f356cSTakashi Iwai {
1572763f356cSTakashi Iwai 	while (snd_hdspm_midi_input_available (hdspm, id))
1573763f356cSTakashi Iwai 		snd_hdspm_midi_read_byte (hdspm, id);
1574763f356cSTakashi Iwai }
1575763f356cSTakashi Iwai 
157698274f07STakashi Iwai static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
1577763f356cSTakashi Iwai {
1578763f356cSTakashi Iwai 	unsigned long flags;
1579763f356cSTakashi Iwai 	int n_pending;
1580763f356cSTakashi Iwai 	int to_write;
1581763f356cSTakashi Iwai 	int i;
1582763f356cSTakashi Iwai 	unsigned char buf[128];
1583763f356cSTakashi Iwai 
1584763f356cSTakashi Iwai 	/* Output is not interrupt driven */
1585763f356cSTakashi Iwai 
1586763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1587ef5fa1a4STakashi Iwai 	if (hmidi->output &&
1588ef5fa1a4STakashi Iwai 	    !snd_rawmidi_transmit_empty (hmidi->output)) {
1589ef5fa1a4STakashi Iwai 		n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm,
1590ef5fa1a4STakashi Iwai 							    hmidi->id);
1591ef5fa1a4STakashi Iwai 		if (n_pending > 0) {
1592763f356cSTakashi Iwai 			if (n_pending > (int)sizeof (buf))
1593763f356cSTakashi Iwai 				n_pending = sizeof (buf);
1594763f356cSTakashi Iwai 
1595ef5fa1a4STakashi Iwai 			to_write = snd_rawmidi_transmit (hmidi->output, buf,
1596ef5fa1a4STakashi Iwai 							 n_pending);
1597ef5fa1a4STakashi Iwai 			if (to_write > 0) {
1598763f356cSTakashi Iwai 				for (i = 0; i < to_write; ++i)
1599ef5fa1a4STakashi Iwai 					snd_hdspm_midi_write_byte (hmidi->hdspm,
1600ef5fa1a4STakashi Iwai 								   hmidi->id,
1601ef5fa1a4STakashi Iwai 								   buf[i]);
1602763f356cSTakashi Iwai 			}
1603763f356cSTakashi Iwai 		}
1604763f356cSTakashi Iwai 	}
1605763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1606763f356cSTakashi Iwai 	return 0;
1607763f356cSTakashi Iwai }
1608763f356cSTakashi Iwai 
160998274f07STakashi Iwai static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi)
1610763f356cSTakashi Iwai {
1611ef5fa1a4STakashi Iwai 	unsigned char buf[128]; /* this buffer is designed to match the MIDI
1612ef5fa1a4STakashi Iwai 				 * input FIFO size
1613ef5fa1a4STakashi Iwai 				 */
1614763f356cSTakashi Iwai 	unsigned long flags;
1615763f356cSTakashi Iwai 	int n_pending;
1616763f356cSTakashi Iwai 	int i;
1617763f356cSTakashi Iwai 
1618763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1619ef5fa1a4STakashi Iwai 	n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id);
1620ef5fa1a4STakashi Iwai 	if (n_pending > 0) {
1621763f356cSTakashi Iwai 		if (hmidi->input) {
1622ef5fa1a4STakashi Iwai 			if (n_pending > (int)sizeof (buf))
1623763f356cSTakashi Iwai 				n_pending = sizeof (buf);
1624ef5fa1a4STakashi Iwai 			for (i = 0; i < n_pending; ++i)
1625ef5fa1a4STakashi Iwai 				buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm,
1626ef5fa1a4STakashi Iwai 								   hmidi->id);
1627ef5fa1a4STakashi Iwai 			if (n_pending)
1628ef5fa1a4STakashi Iwai 				snd_rawmidi_receive (hmidi->input, buf,
1629ef5fa1a4STakashi Iwai 						     n_pending);
1630763f356cSTakashi Iwai 		} else {
1631763f356cSTakashi Iwai 			/* flush the MIDI input FIFO */
1632ef5fa1a4STakashi Iwai 			while (n_pending--)
1633ef5fa1a4STakashi Iwai 				snd_hdspm_midi_read_byte (hmidi->hdspm,
1634ef5fa1a4STakashi Iwai 							  hmidi->id);
1635763f356cSTakashi Iwai 		}
1636763f356cSTakashi Iwai 	}
1637763f356cSTakashi Iwai 	hmidi->pending = 0;
16380dca1793SAdrian Knoth 
16390dca1793SAdrian Knoth 	hmidi->hdspm->control_register |= hmidi->ie;
1640ef5fa1a4STakashi Iwai 	hdspm_write(hmidi->hdspm, HDSPM_controlRegister,
1641ef5fa1a4STakashi Iwai 		    hmidi->hdspm->control_register);
16420dca1793SAdrian Knoth 
1643763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1644763f356cSTakashi Iwai 	return snd_hdspm_midi_output_write (hmidi);
1645763f356cSTakashi Iwai }
1646763f356cSTakashi Iwai 
1647ef5fa1a4STakashi Iwai static void
1648ef5fa1a4STakashi Iwai snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
1649763f356cSTakashi Iwai {
165098274f07STakashi Iwai 	struct hdspm *hdspm;
165198274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1652763f356cSTakashi Iwai 	unsigned long flags;
1653763f356cSTakashi Iwai 
1654ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1655763f356cSTakashi Iwai 	hdspm = hmidi->hdspm;
16560dca1793SAdrian Knoth 
1657763f356cSTakashi Iwai 	spin_lock_irqsave (&hdspm->lock, flags);
1658763f356cSTakashi Iwai 	if (up) {
16590dca1793SAdrian Knoth 		if (!(hdspm->control_register & hmidi->ie)) {
1660763f356cSTakashi Iwai 			snd_hdspm_flush_midi_input (hdspm, hmidi->id);
16610dca1793SAdrian Knoth 			hdspm->control_register |= hmidi->ie;
1662763f356cSTakashi Iwai 		}
1663763f356cSTakashi Iwai 	} else {
16640dca1793SAdrian Knoth 		hdspm->control_register &= ~hmidi->ie;
1665763f356cSTakashi Iwai 	}
1666763f356cSTakashi Iwai 
1667763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
1668763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hdspm->lock, flags);
1669763f356cSTakashi Iwai }
1670763f356cSTakashi Iwai 
1671763f356cSTakashi Iwai static void snd_hdspm_midi_output_timer(unsigned long data)
1672763f356cSTakashi Iwai {
167398274f07STakashi Iwai 	struct hdspm_midi *hmidi = (struct hdspm_midi *) data;
1674763f356cSTakashi Iwai 	unsigned long flags;
1675763f356cSTakashi Iwai 
1676763f356cSTakashi Iwai 	snd_hdspm_midi_output_write(hmidi);
1677763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1678763f356cSTakashi Iwai 
1679763f356cSTakashi Iwai 	/* this does not bump hmidi->istimer, because the
1680763f356cSTakashi Iwai 	   kernel automatically removed the timer when it
1681763f356cSTakashi Iwai 	   expired, and we are now adding it back, thus
1682763f356cSTakashi Iwai 	   leaving istimer wherever it was set before.
1683763f356cSTakashi Iwai 	*/
1684763f356cSTakashi Iwai 
1685763f356cSTakashi Iwai 	if (hmidi->istimer) {
1686763f356cSTakashi Iwai 		hmidi->timer.expires = 1 + jiffies;
1687763f356cSTakashi Iwai 		add_timer(&hmidi->timer);
1688763f356cSTakashi Iwai 	}
1689763f356cSTakashi Iwai 
1690763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1691763f356cSTakashi Iwai }
1692763f356cSTakashi Iwai 
1693ef5fa1a4STakashi Iwai static void
1694ef5fa1a4STakashi Iwai snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
1695763f356cSTakashi Iwai {
169698274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1697763f356cSTakashi Iwai 	unsigned long flags;
1698763f356cSTakashi Iwai 
1699ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1700763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1701763f356cSTakashi Iwai 	if (up) {
1702763f356cSTakashi Iwai 		if (!hmidi->istimer) {
1703763f356cSTakashi Iwai 			init_timer(&hmidi->timer);
1704763f356cSTakashi Iwai 			hmidi->timer.function = snd_hdspm_midi_output_timer;
1705763f356cSTakashi Iwai 			hmidi->timer.data = (unsigned long) hmidi;
1706763f356cSTakashi Iwai 			hmidi->timer.expires = 1 + jiffies;
1707763f356cSTakashi Iwai 			add_timer(&hmidi->timer);
1708763f356cSTakashi Iwai 			hmidi->istimer++;
1709763f356cSTakashi Iwai 		}
1710763f356cSTakashi Iwai 	} else {
1711ef5fa1a4STakashi Iwai 		if (hmidi->istimer && --hmidi->istimer <= 0)
1712763f356cSTakashi Iwai 			del_timer (&hmidi->timer);
1713763f356cSTakashi Iwai 	}
1714763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1715763f356cSTakashi Iwai 	if (up)
1716763f356cSTakashi Iwai 		snd_hdspm_midi_output_write(hmidi);
1717763f356cSTakashi Iwai }
1718763f356cSTakashi Iwai 
171998274f07STakashi Iwai static int snd_hdspm_midi_input_open(struct snd_rawmidi_substream *substream)
1720763f356cSTakashi Iwai {
172198274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1722763f356cSTakashi Iwai 
1723ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1724763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
1725763f356cSTakashi Iwai 	snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id);
1726763f356cSTakashi Iwai 	hmidi->input = substream;
1727763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
1728763f356cSTakashi Iwai 
1729763f356cSTakashi Iwai 	return 0;
1730763f356cSTakashi Iwai }
1731763f356cSTakashi Iwai 
173298274f07STakashi Iwai static int snd_hdspm_midi_output_open(struct snd_rawmidi_substream *substream)
1733763f356cSTakashi Iwai {
173498274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1735763f356cSTakashi Iwai 
1736ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1737763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
1738763f356cSTakashi Iwai 	hmidi->output = substream;
1739763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
1740763f356cSTakashi Iwai 
1741763f356cSTakashi Iwai 	return 0;
1742763f356cSTakashi Iwai }
1743763f356cSTakashi Iwai 
174498274f07STakashi Iwai static int snd_hdspm_midi_input_close(struct snd_rawmidi_substream *substream)
1745763f356cSTakashi Iwai {
174698274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1747763f356cSTakashi Iwai 
1748763f356cSTakashi Iwai 	snd_hdspm_midi_input_trigger (substream, 0);
1749763f356cSTakashi Iwai 
1750ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1751763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
1752763f356cSTakashi Iwai 	hmidi->input = NULL;
1753763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
1754763f356cSTakashi Iwai 
1755763f356cSTakashi Iwai 	return 0;
1756763f356cSTakashi Iwai }
1757763f356cSTakashi Iwai 
175898274f07STakashi Iwai static int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream)
1759763f356cSTakashi Iwai {
176098274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1761763f356cSTakashi Iwai 
1762763f356cSTakashi Iwai 	snd_hdspm_midi_output_trigger (substream, 0);
1763763f356cSTakashi Iwai 
1764ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1765763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
1766763f356cSTakashi Iwai 	hmidi->output = NULL;
1767763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
1768763f356cSTakashi Iwai 
1769763f356cSTakashi Iwai 	return 0;
1770763f356cSTakashi Iwai }
1771763f356cSTakashi Iwai 
177298274f07STakashi Iwai static struct snd_rawmidi_ops snd_hdspm_midi_output =
1773763f356cSTakashi Iwai {
1774763f356cSTakashi Iwai 	.open =		snd_hdspm_midi_output_open,
1775763f356cSTakashi Iwai 	.close =	snd_hdspm_midi_output_close,
1776763f356cSTakashi Iwai 	.trigger =	snd_hdspm_midi_output_trigger,
1777763f356cSTakashi Iwai };
1778763f356cSTakashi Iwai 
177998274f07STakashi Iwai static struct snd_rawmidi_ops snd_hdspm_midi_input =
1780763f356cSTakashi Iwai {
1781763f356cSTakashi Iwai 	.open =		snd_hdspm_midi_input_open,
1782763f356cSTakashi Iwai 	.close =	snd_hdspm_midi_input_close,
1783763f356cSTakashi Iwai 	.trigger =	snd_hdspm_midi_input_trigger,
1784763f356cSTakashi Iwai };
1785763f356cSTakashi Iwai 
1786ef5fa1a4STakashi Iwai static int __devinit snd_hdspm_create_midi (struct snd_card *card,
1787ef5fa1a4STakashi Iwai 					    struct hdspm *hdspm, int id)
1788763f356cSTakashi Iwai {
1789763f356cSTakashi Iwai 	int err;
1790763f356cSTakashi Iwai 	char buf[32];
1791763f356cSTakashi Iwai 
1792763f356cSTakashi Iwai 	hdspm->midi[id].id = id;
1793763f356cSTakashi Iwai 	hdspm->midi[id].hdspm = hdspm;
1794763f356cSTakashi Iwai 	spin_lock_init (&hdspm->midi[id].lock);
1795763f356cSTakashi Iwai 
17960dca1793SAdrian Knoth 	if (0 == id) {
17970dca1793SAdrian Knoth 		if (MADIface == hdspm->io_type) {
17980dca1793SAdrian Knoth 			/* MIDI-over-MADI on HDSPe MADIface */
17990dca1793SAdrian Knoth 			hdspm->midi[0].dataIn = HDSPM_midiDataIn2;
18000dca1793SAdrian Knoth 			hdspm->midi[0].statusIn = HDSPM_midiStatusIn2;
18010dca1793SAdrian Knoth 			hdspm->midi[0].dataOut = HDSPM_midiDataOut2;
18020dca1793SAdrian Knoth 			hdspm->midi[0].statusOut = HDSPM_midiStatusOut2;
18030dca1793SAdrian Knoth 			hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable;
18040dca1793SAdrian Knoth 			hdspm->midi[0].irq = HDSPM_midi2IRQPending;
18050dca1793SAdrian Knoth 		} else {
18060dca1793SAdrian Knoth 			hdspm->midi[0].dataIn = HDSPM_midiDataIn0;
18070dca1793SAdrian Knoth 			hdspm->midi[0].statusIn = HDSPM_midiStatusIn0;
18080dca1793SAdrian Knoth 			hdspm->midi[0].dataOut = HDSPM_midiDataOut0;
18090dca1793SAdrian Knoth 			hdspm->midi[0].statusOut = HDSPM_midiStatusOut0;
18100dca1793SAdrian Knoth 			hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable;
18110dca1793SAdrian Knoth 			hdspm->midi[0].irq = HDSPM_midi0IRQPending;
18120dca1793SAdrian Knoth 		}
18130dca1793SAdrian Knoth 	} else if (1 == id) {
18140dca1793SAdrian Knoth 		hdspm->midi[1].dataIn = HDSPM_midiDataIn1;
18150dca1793SAdrian Knoth 		hdspm->midi[1].statusIn = HDSPM_midiStatusIn1;
18160dca1793SAdrian Knoth 		hdspm->midi[1].dataOut = HDSPM_midiDataOut1;
18170dca1793SAdrian Knoth 		hdspm->midi[1].statusOut = HDSPM_midiStatusOut1;
18180dca1793SAdrian Knoth 		hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable;
18190dca1793SAdrian Knoth 		hdspm->midi[1].irq = HDSPM_midi1IRQPending;
18200dca1793SAdrian Knoth 	} else if ((2 == id) && (MADI == hdspm->io_type)) {
18210dca1793SAdrian Knoth 		/* MIDI-over-MADI on HDSPe MADI */
18220dca1793SAdrian Knoth 		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
18230dca1793SAdrian Knoth 		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
18240dca1793SAdrian Knoth 		hdspm->midi[2].dataOut = HDSPM_midiDataOut2;
18250dca1793SAdrian Knoth 		hdspm->midi[2].statusOut = HDSPM_midiStatusOut2;
18260dca1793SAdrian Knoth 		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
18270dca1793SAdrian Knoth 		hdspm->midi[2].irq = HDSPM_midi2IRQPending;
18280dca1793SAdrian Knoth 	} else if (2 == id) {
18290dca1793SAdrian Knoth 		/* TCO MTC, read only */
18300dca1793SAdrian Knoth 		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
18310dca1793SAdrian Knoth 		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
18320dca1793SAdrian Knoth 		hdspm->midi[2].dataOut = -1;
18330dca1793SAdrian Knoth 		hdspm->midi[2].statusOut = -1;
18340dca1793SAdrian Knoth 		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
18350dca1793SAdrian Knoth 		hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES;
18360dca1793SAdrian Knoth 	} else if (3 == id) {
18370dca1793SAdrian Knoth 		/* TCO MTC on HDSPe MADI */
18380dca1793SAdrian Knoth 		hdspm->midi[3].dataIn = HDSPM_midiDataIn3;
18390dca1793SAdrian Knoth 		hdspm->midi[3].statusIn = HDSPM_midiStatusIn3;
18400dca1793SAdrian Knoth 		hdspm->midi[3].dataOut = -1;
18410dca1793SAdrian Knoth 		hdspm->midi[3].statusOut = -1;
18420dca1793SAdrian Knoth 		hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable;
18430dca1793SAdrian Knoth 		hdspm->midi[3].irq = HDSPM_midi3IRQPending;
18440dca1793SAdrian Knoth 	}
18450dca1793SAdrian Knoth 
18460dca1793SAdrian Knoth 	if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) ||
18470dca1793SAdrian Knoth 					(MADIface == hdspm->io_type)))) {
18480dca1793SAdrian Knoth 		if ((id == 0) && (MADIface == hdspm->io_type)) {
18490dca1793SAdrian Knoth 			sprintf(buf, "%s MIDIoverMADI", card->shortname);
18500dca1793SAdrian Knoth 		} else if ((id == 2) && (MADI == hdspm->io_type)) {
18510dca1793SAdrian Knoth 			sprintf(buf, "%s MIDIoverMADI", card->shortname);
18520dca1793SAdrian Knoth 		} else {
1853763f356cSTakashi Iwai 			sprintf(buf, "%s MIDI %d", card->shortname, id+1);
18540dca1793SAdrian Knoth 		}
18550dca1793SAdrian Knoth 		err = snd_rawmidi_new(card, buf, id, 1, 1,
18560dca1793SAdrian Knoth 				&hdspm->midi[id].rmidi);
1857ef5fa1a4STakashi Iwai 		if (err < 0)
1858763f356cSTakashi Iwai 			return err;
1859763f356cSTakashi Iwai 
18600dca1793SAdrian Knoth 		sprintf(hdspm->midi[id].rmidi->name, "%s MIDI %d",
18610dca1793SAdrian Knoth 				card->id, id+1);
1862763f356cSTakashi Iwai 		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
1863763f356cSTakashi Iwai 
18640dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
18650dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_OUTPUT,
1866ef5fa1a4STakashi Iwai 				&snd_hdspm_midi_output);
18670dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
18680dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_INPUT,
1869ef5fa1a4STakashi Iwai 				&snd_hdspm_midi_input);
1870763f356cSTakashi Iwai 
18710dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->info_flags |=
18720dca1793SAdrian Knoth 			SNDRV_RAWMIDI_INFO_OUTPUT |
1873763f356cSTakashi Iwai 			SNDRV_RAWMIDI_INFO_INPUT |
1874763f356cSTakashi Iwai 			SNDRV_RAWMIDI_INFO_DUPLEX;
18750dca1793SAdrian Knoth 	} else {
18760dca1793SAdrian Knoth 		/* TCO MTC, read only */
18770dca1793SAdrian Knoth 		sprintf(buf, "%s MTC %d", card->shortname, id+1);
18780dca1793SAdrian Knoth 		err = snd_rawmidi_new(card, buf, id, 1, 1,
18790dca1793SAdrian Knoth 				&hdspm->midi[id].rmidi);
18800dca1793SAdrian Knoth 		if (err < 0)
18810dca1793SAdrian Knoth 			return err;
18820dca1793SAdrian Knoth 
18830dca1793SAdrian Knoth 		sprintf(hdspm->midi[id].rmidi->name,
18840dca1793SAdrian Knoth 				"%s MTC %d", card->id, id+1);
18850dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
18860dca1793SAdrian Knoth 
18870dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
18880dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_INPUT,
18890dca1793SAdrian Knoth 				&snd_hdspm_midi_input);
18900dca1793SAdrian Knoth 
18910dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
18920dca1793SAdrian Knoth 	}
1893763f356cSTakashi Iwai 
1894763f356cSTakashi Iwai 	return 0;
1895763f356cSTakashi Iwai }
1896763f356cSTakashi Iwai 
1897763f356cSTakashi Iwai 
1898763f356cSTakashi Iwai static void hdspm_midi_tasklet(unsigned long arg)
1899763f356cSTakashi Iwai {
190098274f07STakashi Iwai 	struct hdspm *hdspm = (struct hdspm *)arg;
19010dca1793SAdrian Knoth 	int i = 0;
1902763f356cSTakashi Iwai 
19030dca1793SAdrian Knoth 	while (i < hdspm->midiPorts) {
19040dca1793SAdrian Knoth 		if (hdspm->midi[i].pending)
19050dca1793SAdrian Knoth 			snd_hdspm_midi_input_read(&hdspm->midi[i]);
19060dca1793SAdrian Knoth 
19070dca1793SAdrian Knoth 		i++;
19080dca1793SAdrian Knoth 	}
1909763f356cSTakashi Iwai }
1910763f356cSTakashi Iwai 
1911763f356cSTakashi Iwai 
1912763f356cSTakashi Iwai /*-----------------------------------------------------------------------------
1913763f356cSTakashi Iwai   Status Interface
1914763f356cSTakashi Iwai   ----------------------------------------------------------------------------*/
1915763f356cSTakashi Iwai 
1916763f356cSTakashi Iwai /* get the system sample rate which is set */
1917763f356cSTakashi Iwai 
19180dca1793SAdrian Knoth 
19190dca1793SAdrian Knoth /**
19200dca1793SAdrian Knoth  * Calculate the real sample rate from the
19210dca1793SAdrian Knoth  * current DDS value.
19220dca1793SAdrian Knoth  **/
19230dca1793SAdrian Knoth static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
19240dca1793SAdrian Knoth {
19250dca1793SAdrian Knoth 	unsigned int period, rate;
19260dca1793SAdrian Knoth 
19270dca1793SAdrian Knoth 	period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
19280dca1793SAdrian Knoth 	rate = hdspm_calc_dds_value(hdspm, period);
19290dca1793SAdrian Knoth 
19300dca1793SAdrian Knoth 	return rate;
19310dca1793SAdrian Knoth }
19320dca1793SAdrian Knoth 
19330dca1793SAdrian Knoth 
1934763f356cSTakashi Iwai #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
193567ed4161SClemens Ladisch { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1936763f356cSTakashi Iwai   .name = xname, \
1937763f356cSTakashi Iwai   .index = xindex, \
1938763f356cSTakashi Iwai   .access = SNDRV_CTL_ELEM_ACCESS_READ, \
1939763f356cSTakashi Iwai   .info = snd_hdspm_info_system_sample_rate, \
1940763f356cSTakashi Iwai   .get = snd_hdspm_get_system_sample_rate \
1941763f356cSTakashi Iwai }
1942763f356cSTakashi Iwai 
194398274f07STakashi Iwai static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol,
194498274f07STakashi Iwai 					     struct snd_ctl_elem_info *uinfo)
1945763f356cSTakashi Iwai {
1946763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1947763f356cSTakashi Iwai 	uinfo->count = 1;
19480dca1793SAdrian Knoth 	uinfo->value.integer.min = 27000;
19490dca1793SAdrian Knoth 	uinfo->value.integer.max = 207000;
19500dca1793SAdrian Knoth 	uinfo->value.integer.step = 1;
1951763f356cSTakashi Iwai 	return 0;
1952763f356cSTakashi Iwai }
1953763f356cSTakashi Iwai 
19540dca1793SAdrian Knoth 
195598274f07STakashi Iwai static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol,
195698274f07STakashi Iwai 					    struct snd_ctl_elem_value *
1957763f356cSTakashi Iwai 					    ucontrol)
1958763f356cSTakashi Iwai {
195998274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
1960763f356cSTakashi Iwai 
19610dca1793SAdrian Knoth 	ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm);
1962763f356cSTakashi Iwai 	return 0;
1963763f356cSTakashi Iwai }
1964763f356cSTakashi Iwai 
19650dca1793SAdrian Knoth 
19660dca1793SAdrian Knoth /**
19670dca1793SAdrian Knoth  * Returns the WordClock sample rate class for the given card.
19680dca1793SAdrian Knoth  **/
19690dca1793SAdrian Knoth static int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
19700dca1793SAdrian Knoth {
19710dca1793SAdrian Knoth 	int status;
19720dca1793SAdrian Knoth 
19730dca1793SAdrian Knoth 	switch (hdspm->io_type) {
19740dca1793SAdrian Knoth 	case RayDAT:
19750dca1793SAdrian Knoth 	case AIO:
19760dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
19770dca1793SAdrian Knoth 		return (status >> 16) & 0xF;
19780dca1793SAdrian Knoth 		break;
19790dca1793SAdrian Knoth 	default:
19800dca1793SAdrian Knoth 		break;
19810dca1793SAdrian Knoth 	}
19820dca1793SAdrian Knoth 
19830dca1793SAdrian Knoth 
19840dca1793SAdrian Knoth 	return 0;
19850dca1793SAdrian Knoth }
19860dca1793SAdrian Knoth 
19870dca1793SAdrian Knoth 
19880dca1793SAdrian Knoth /**
19890dca1793SAdrian Knoth  * Returns the TCO sample rate class for the given card.
19900dca1793SAdrian Knoth  **/
19910dca1793SAdrian Knoth static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
19920dca1793SAdrian Knoth {
19930dca1793SAdrian Knoth 	int status;
19940dca1793SAdrian Knoth 
19950dca1793SAdrian Knoth 	if (hdspm->tco) {
19960dca1793SAdrian Knoth 		switch (hdspm->io_type) {
19970dca1793SAdrian Knoth 		case RayDAT:
19980dca1793SAdrian Knoth 		case AIO:
19990dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
20000dca1793SAdrian Knoth 			return (status >> 20) & 0xF;
20010dca1793SAdrian Knoth 			break;
20020dca1793SAdrian Knoth 		default:
20030dca1793SAdrian Knoth 			break;
20040dca1793SAdrian Knoth 		}
20050dca1793SAdrian Knoth 	}
20060dca1793SAdrian Knoth 
20070dca1793SAdrian Knoth 	return 0;
20080dca1793SAdrian Knoth }
20090dca1793SAdrian Knoth 
20100dca1793SAdrian Knoth 
20110dca1793SAdrian Knoth /**
20120dca1793SAdrian Knoth  * Returns the SYNC_IN sample rate class for the given card.
20130dca1793SAdrian Knoth  **/
20140dca1793SAdrian Knoth static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
20150dca1793SAdrian Knoth {
20160dca1793SAdrian Knoth 	int status;
20170dca1793SAdrian Knoth 
20180dca1793SAdrian Knoth 	if (hdspm->tco) {
20190dca1793SAdrian Knoth 		switch (hdspm->io_type) {
20200dca1793SAdrian Knoth 		case RayDAT:
20210dca1793SAdrian Knoth 		case AIO:
20220dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
20230dca1793SAdrian Knoth 			return (status >> 12) & 0xF;
20240dca1793SAdrian Knoth 			break;
20250dca1793SAdrian Knoth 		default:
20260dca1793SAdrian Knoth 			break;
20270dca1793SAdrian Knoth 		}
20280dca1793SAdrian Knoth 	}
20290dca1793SAdrian Knoth 
20300dca1793SAdrian Knoth 	return 0;
20310dca1793SAdrian Knoth }
20320dca1793SAdrian Knoth 
20330dca1793SAdrian Knoth 
20340dca1793SAdrian Knoth /**
20350dca1793SAdrian Knoth  * Returns the sample rate class for input source <idx> for
20360dca1793SAdrian Knoth  * 'new style' cards like the AIO and RayDAT.
20370dca1793SAdrian Knoth  **/
20380dca1793SAdrian Knoth static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
20390dca1793SAdrian Knoth {
20400dca1793SAdrian Knoth 	int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
20410dca1793SAdrian Knoth 
20420dca1793SAdrian Knoth 	return (status >> (idx*4)) & 0xF;
20430dca1793SAdrian Knoth }
20440dca1793SAdrian Knoth 
20450dca1793SAdrian Knoth 
20460dca1793SAdrian Knoth 
2047763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
204867ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2049763f356cSTakashi Iwai 	.name = xname, \
20500dca1793SAdrian Knoth 	.private_value = xindex, \
2051763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ, \
2052763f356cSTakashi Iwai 	.info = snd_hdspm_info_autosync_sample_rate, \
2053763f356cSTakashi Iwai 	.get = snd_hdspm_get_autosync_sample_rate \
2054763f356cSTakashi Iwai }
2055763f356cSTakashi Iwai 
20560dca1793SAdrian Knoth 
205798274f07STakashi Iwai static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
205898274f07STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
2059763f356cSTakashi Iwai {
2060763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2061763f356cSTakashi Iwai 	uinfo->count = 1;
2062763f356cSTakashi Iwai 	uinfo->value.enumerated.items = 10;
20630dca1793SAdrian Knoth 
2064763f356cSTakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
20650dca1793SAdrian Knoth 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
2066763f356cSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
20670dca1793SAdrian Knoth 			texts_freq[uinfo->value.enumerated.item]);
2068763f356cSTakashi Iwai 	return 0;
2069763f356cSTakashi Iwai }
2070763f356cSTakashi Iwai 
20710dca1793SAdrian Knoth 
207298274f07STakashi Iwai static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
207398274f07STakashi Iwai 					      struct snd_ctl_elem_value *
2074763f356cSTakashi Iwai 					      ucontrol)
2075763f356cSTakashi Iwai {
207698274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2077763f356cSTakashi Iwai 
20780dca1793SAdrian Knoth 	switch (hdspm->io_type) {
20790dca1793SAdrian Knoth 	case RayDAT:
20800dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
20810dca1793SAdrian Knoth 		case 0:
20820dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
20830dca1793SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
2084763f356cSTakashi Iwai 			break;
20850dca1793SAdrian Knoth 		case 7:
20860dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
20870dca1793SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
2088763f356cSTakashi Iwai 			break;
20890dca1793SAdrian Knoth 		case 8:
20900dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
20910dca1793SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
2092763f356cSTakashi Iwai 			break;
2093763f356cSTakashi Iwai 		default:
20940dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
20950dca1793SAdrian Knoth 				hdspm_get_s1_sample_rate(hdspm,
20960dca1793SAdrian Knoth 						kcontrol->private_value-1);
2097763f356cSTakashi Iwai 		}
20980dca1793SAdrian Knoth 
20990dca1793SAdrian Knoth 	case AIO:
21000dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
21010dca1793SAdrian Knoth 		case 0: /* WC */
21020dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21030dca1793SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
21040dca1793SAdrian Knoth 			break;
21050dca1793SAdrian Knoth 		case 4: /* TCO */
21060dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21070dca1793SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
21080dca1793SAdrian Knoth 			break;
21090dca1793SAdrian Knoth 		case 5: /* SYNC_IN */
21100dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21110dca1793SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
21120dca1793SAdrian Knoth 			break;
21130dca1793SAdrian Knoth 		default:
21140dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21150dca1793SAdrian Knoth 				hdspm_get_s1_sample_rate(hdspm,
21160dca1793SAdrian Knoth 						ucontrol->id.index-1);
21170dca1793SAdrian Knoth 		}
21187c4a95b5SAdrian Knoth 
21197c4a95b5SAdrian Knoth 	case AES32:
21207c4a95b5SAdrian Knoth 
21217c4a95b5SAdrian Knoth 		switch (kcontrol->private_value) {
21227c4a95b5SAdrian Knoth 		case 0: /* WC */
21237c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21247c4a95b5SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
21257c4a95b5SAdrian Knoth 			break;
21267c4a95b5SAdrian Knoth 		case 9: /* TCO */
21277c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21287c4a95b5SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
21297c4a95b5SAdrian Knoth 			break;
21307c4a95b5SAdrian Knoth 		case 10: /* SYNC_IN */
21317c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21327c4a95b5SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
21337c4a95b5SAdrian Knoth 			break;
21347c4a95b5SAdrian Knoth 		default: /* AES1 to AES8 */
21357c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
21367c4a95b5SAdrian Knoth 				hdspm_get_s1_sample_rate(hdspm,
21377c4a95b5SAdrian Knoth 						kcontrol->private_value-1);
21387c4a95b5SAdrian Knoth 			break;
21397c4a95b5SAdrian Knoth 
21407c4a95b5SAdrian Knoth 		}
21410dca1793SAdrian Knoth 	default:
21420dca1793SAdrian Knoth 		break;
21430dca1793SAdrian Knoth 	}
21440dca1793SAdrian Knoth 
2145763f356cSTakashi Iwai 	return 0;
2146763f356cSTakashi Iwai }
2147763f356cSTakashi Iwai 
21480dca1793SAdrian Knoth 
2149763f356cSTakashi Iwai #define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \
215067ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2151763f356cSTakashi Iwai 	.name = xname, \
2152763f356cSTakashi Iwai 	.index = xindex, \
21530dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
21540dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2155763f356cSTakashi Iwai 	.info = snd_hdspm_info_system_clock_mode, \
2156763f356cSTakashi Iwai 	.get = snd_hdspm_get_system_clock_mode, \
21570dca1793SAdrian Knoth 	.put = snd_hdspm_put_system_clock_mode, \
2158763f356cSTakashi Iwai }
2159763f356cSTakashi Iwai 
2160763f356cSTakashi Iwai 
21610dca1793SAdrian Knoth /**
21620dca1793SAdrian Knoth  * Returns the system clock mode for the given card.
21630dca1793SAdrian Knoth  * @returns 0 - master, 1 - slave
21640dca1793SAdrian Knoth  **/
216598274f07STakashi Iwai static int hdspm_system_clock_mode(struct hdspm *hdspm)
2166763f356cSTakashi Iwai {
21670dca1793SAdrian Knoth 	switch (hdspm->io_type) {
21680dca1793SAdrian Knoth 	case AIO:
21690dca1793SAdrian Knoth 	case RayDAT:
21700dca1793SAdrian Knoth 		if (hdspm->settings_register & HDSPM_c0Master)
21710dca1793SAdrian Knoth 			return 0;
21720dca1793SAdrian Knoth 		break;
2173763f356cSTakashi Iwai 
21740dca1793SAdrian Knoth 	default:
2175763f356cSTakashi Iwai 		if (hdspm->control_register & HDSPM_ClockModeMaster)
2176763f356cSTakashi Iwai 			return 0;
21770dca1793SAdrian Knoth 	}
21780dca1793SAdrian Knoth 
2179763f356cSTakashi Iwai 	return 1;
2180763f356cSTakashi Iwai }
2181763f356cSTakashi Iwai 
21820dca1793SAdrian Knoth 
21830dca1793SAdrian Knoth /**
21840dca1793SAdrian Knoth  * Sets the system clock mode.
21850dca1793SAdrian Knoth  * @param mode 0 - master, 1 - slave
21860dca1793SAdrian Knoth  **/
21870dca1793SAdrian Knoth static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
21880dca1793SAdrian Knoth {
21890dca1793SAdrian Knoth 	switch (hdspm->io_type) {
21900dca1793SAdrian Knoth 	case AIO:
21910dca1793SAdrian Knoth 	case RayDAT:
21920dca1793SAdrian Knoth 		if (0 == mode)
21930dca1793SAdrian Knoth 			hdspm->settings_register |= HDSPM_c0Master;
21940dca1793SAdrian Knoth 		else
21950dca1793SAdrian Knoth 			hdspm->settings_register &= ~HDSPM_c0Master;
21960dca1793SAdrian Knoth 
21970dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
21980dca1793SAdrian Knoth 		break;
21990dca1793SAdrian Knoth 
22000dca1793SAdrian Knoth 	default:
22010dca1793SAdrian Knoth 		if (0 == mode)
22020dca1793SAdrian Knoth 			hdspm->control_register |= HDSPM_ClockModeMaster;
22030dca1793SAdrian Knoth 		else
22040dca1793SAdrian Knoth 			hdspm->control_register &= ~HDSPM_ClockModeMaster;
22050dca1793SAdrian Knoth 
22060dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_controlRegister,
22070dca1793SAdrian Knoth 				hdspm->control_register);
22080dca1793SAdrian Knoth 	}
22090dca1793SAdrian Knoth }
22100dca1793SAdrian Knoth 
22110dca1793SAdrian Knoth 
221298274f07STakashi Iwai static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
221398274f07STakashi Iwai 					    struct snd_ctl_elem_info *uinfo)
2214763f356cSTakashi Iwai {
22150dca1793SAdrian Knoth 	static char *texts[] = { "Master", "AutoSync" };
2216763f356cSTakashi Iwai 
2217763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2218763f356cSTakashi Iwai 	uinfo->count = 1;
2219763f356cSTakashi Iwai 	uinfo->value.enumerated.items = 2;
2220763f356cSTakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
2221763f356cSTakashi Iwai 		uinfo->value.enumerated.item =
2222763f356cSTakashi Iwai 		    uinfo->value.enumerated.items - 1;
2223763f356cSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
2224763f356cSTakashi Iwai 	       texts[uinfo->value.enumerated.item]);
2225763f356cSTakashi Iwai 	return 0;
2226763f356cSTakashi Iwai }
2227763f356cSTakashi Iwai 
222898274f07STakashi Iwai static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol,
222998274f07STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
2230763f356cSTakashi Iwai {
223198274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2232763f356cSTakashi Iwai 
22330dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm);
2234763f356cSTakashi Iwai 	return 0;
2235763f356cSTakashi Iwai }
2236763f356cSTakashi Iwai 
22370dca1793SAdrian Knoth static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol,
22380dca1793SAdrian Knoth 					   struct snd_ctl_elem_value *ucontrol)
22390dca1793SAdrian Knoth {
22400dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
22410dca1793SAdrian Knoth 	int val;
22420dca1793SAdrian Knoth 
22430dca1793SAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
22440dca1793SAdrian Knoth 		return -EBUSY;
22450dca1793SAdrian Knoth 
22460dca1793SAdrian Knoth 	val = ucontrol->value.enumerated.item[0];
22470dca1793SAdrian Knoth 	if (val < 0)
22480dca1793SAdrian Knoth 		val = 0;
22490dca1793SAdrian Knoth 	else if (val > 1)
22500dca1793SAdrian Knoth 		val = 1;
22510dca1793SAdrian Knoth 
22520dca1793SAdrian Knoth 	hdspm_set_system_clock_mode(hdspm, val);
22530dca1793SAdrian Knoth 
22540dca1793SAdrian Knoth 	return 0;
22550dca1793SAdrian Knoth }
22560dca1793SAdrian Knoth 
22570dca1793SAdrian Knoth 
22580dca1793SAdrian Knoth #define HDSPM_INTERNAL_CLOCK(xname, xindex) \
225967ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2260763f356cSTakashi Iwai 	.name = xname, \
2261763f356cSTakashi Iwai 	.index = xindex, \
2262763f356cSTakashi Iwai 	.info = snd_hdspm_info_clock_source, \
2263763f356cSTakashi Iwai 	.get = snd_hdspm_get_clock_source, \
2264763f356cSTakashi Iwai 	.put = snd_hdspm_put_clock_source \
2265763f356cSTakashi Iwai }
2266763f356cSTakashi Iwai 
22670dca1793SAdrian Knoth 
226898274f07STakashi Iwai static int hdspm_clock_source(struct hdspm * hdspm)
2269763f356cSTakashi Iwai {
2270763f356cSTakashi Iwai 	switch (hdspm->system_sample_rate) {
22710dca1793SAdrian Knoth 	case 32000: return 0;
22720dca1793SAdrian Knoth 	case 44100: return 1;
22730dca1793SAdrian Knoth 	case 48000: return 2;
22740dca1793SAdrian Knoth 	case 64000: return 3;
22750dca1793SAdrian Knoth 	case 88200: return 4;
22760dca1793SAdrian Knoth 	case 96000: return 5;
22770dca1793SAdrian Knoth 	case 128000: return 6;
22780dca1793SAdrian Knoth 	case 176400: return 7;
22790dca1793SAdrian Knoth 	case 192000: return 8;
2280763f356cSTakashi Iwai 	}
22810dca1793SAdrian Knoth 
22820dca1793SAdrian Knoth 	return -1;
2283763f356cSTakashi Iwai }
2284763f356cSTakashi Iwai 
228598274f07STakashi Iwai static int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
2286763f356cSTakashi Iwai {
2287763f356cSTakashi Iwai 	int rate;
2288763f356cSTakashi Iwai 	switch (mode) {
22890dca1793SAdrian Knoth 	case 0:
22900dca1793SAdrian Knoth 		rate = 32000; break;
22910dca1793SAdrian Knoth 	case 1:
22920dca1793SAdrian Knoth 		rate = 44100; break;
22930dca1793SAdrian Knoth 	case 2:
22940dca1793SAdrian Knoth 		rate = 48000; break;
22950dca1793SAdrian Knoth 	case 3:
22960dca1793SAdrian Knoth 		rate = 64000; break;
22970dca1793SAdrian Knoth 	case 4:
22980dca1793SAdrian Knoth 		rate = 88200; break;
22990dca1793SAdrian Knoth 	case 5:
23000dca1793SAdrian Knoth 		rate = 96000; break;
23010dca1793SAdrian Knoth 	case 6:
23020dca1793SAdrian Knoth 		rate = 128000; break;
23030dca1793SAdrian Knoth 	case 7:
23040dca1793SAdrian Knoth 		rate = 176400; break;
23050dca1793SAdrian Knoth 	case 8:
23060dca1793SAdrian Knoth 		rate = 192000; break;
2307763f356cSTakashi Iwai 	default:
23080dca1793SAdrian Knoth 		rate = 48000;
2309763f356cSTakashi Iwai 	}
2310763f356cSTakashi Iwai 	hdspm_set_rate(hdspm, rate, 1);
2311763f356cSTakashi Iwai 	return 0;
2312763f356cSTakashi Iwai }
2313763f356cSTakashi Iwai 
231498274f07STakashi Iwai static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol,
231598274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
2316763f356cSTakashi Iwai {
2317763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2318763f356cSTakashi Iwai 	uinfo->count = 1;
23190dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 9;
2320763f356cSTakashi Iwai 
2321763f356cSTakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
2322763f356cSTakashi Iwai 		uinfo->value.enumerated.item =
2323763f356cSTakashi Iwai 		    uinfo->value.enumerated.items - 1;
2324763f356cSTakashi Iwai 
2325763f356cSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
23260dca1793SAdrian Knoth 	       texts_freq[uinfo->value.enumerated.item+1]);
2327763f356cSTakashi Iwai 
2328763f356cSTakashi Iwai 	return 0;
2329763f356cSTakashi Iwai }
2330763f356cSTakashi Iwai 
233198274f07STakashi Iwai static int snd_hdspm_get_clock_source(struct snd_kcontrol *kcontrol,
233298274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
2333763f356cSTakashi Iwai {
233498274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2335763f356cSTakashi Iwai 
2336763f356cSTakashi Iwai 	ucontrol->value.enumerated.item[0] = hdspm_clock_source(hdspm);
2337763f356cSTakashi Iwai 	return 0;
2338763f356cSTakashi Iwai }
2339763f356cSTakashi Iwai 
234098274f07STakashi Iwai static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol,
234198274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
2342763f356cSTakashi Iwai {
234398274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2344763f356cSTakashi Iwai 	int change;
2345763f356cSTakashi Iwai 	int val;
2346763f356cSTakashi Iwai 
2347763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2348763f356cSTakashi Iwai 		return -EBUSY;
2349763f356cSTakashi Iwai 	val = ucontrol->value.enumerated.item[0];
2350763f356cSTakashi Iwai 	if (val < 0)
2351763f356cSTakashi Iwai 		val = 0;
23526534599dSRemy Bruno 	if (val > 9)
23536534599dSRemy Bruno 		val = 9;
2354763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2355763f356cSTakashi Iwai 	if (val != hdspm_clock_source(hdspm))
2356763f356cSTakashi Iwai 		change = (hdspm_set_clock_source(hdspm, val) == 0) ? 1 : 0;
2357763f356cSTakashi Iwai 	else
2358763f356cSTakashi Iwai 		change = 0;
2359763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2360763f356cSTakashi Iwai 	return change;
2361763f356cSTakashi Iwai }
2362763f356cSTakashi Iwai 
23630dca1793SAdrian Knoth 
2364763f356cSTakashi Iwai #define HDSPM_PREF_SYNC_REF(xname, xindex) \
236567ed4161SClemens Ladisch {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2366763f356cSTakashi Iwai 	.name = xname, \
2367763f356cSTakashi Iwai 	.index = xindex, \
23680dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
23690dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2370763f356cSTakashi Iwai 	.info = snd_hdspm_info_pref_sync_ref, \
2371763f356cSTakashi Iwai 	.get = snd_hdspm_get_pref_sync_ref, \
2372763f356cSTakashi Iwai 	.put = snd_hdspm_put_pref_sync_ref \
2373763f356cSTakashi Iwai }
2374763f356cSTakashi Iwai 
23750dca1793SAdrian Knoth 
23760dca1793SAdrian Knoth /**
23770dca1793SAdrian Knoth  * Returns the current preferred sync reference setting.
23780dca1793SAdrian Knoth  * The semantics of the return value are depending on the
23790dca1793SAdrian Knoth  * card, please see the comments for clarification.
23800dca1793SAdrian Knoth  **/
238198274f07STakashi Iwai static int hdspm_pref_sync_ref(struct hdspm * hdspm)
2382763f356cSTakashi Iwai {
23830dca1793SAdrian Knoth 	switch (hdspm->io_type) {
23840dca1793SAdrian Knoth 	case AES32:
23853cee5a60SRemy Bruno 		switch (hdspm->control_register & HDSPM_SyncRefMask) {
23860dca1793SAdrian Knoth 		case 0: return 0;  /* WC */
23870dca1793SAdrian Knoth 		case HDSPM_SyncRef0: return 1; /* AES 1 */
23880dca1793SAdrian Knoth 		case HDSPM_SyncRef1: return 2; /* AES 2 */
23890dca1793SAdrian Knoth 		case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */
23900dca1793SAdrian Knoth 		case HDSPM_SyncRef2: return 4; /* AES 4 */
23910dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */
23920dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */
23930dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0:
23940dca1793SAdrian Knoth 						    return 7; /* AES 7 */
23950dca1793SAdrian Knoth 		case HDSPM_SyncRef3: return 8; /* AES 8 */
23960dca1793SAdrian Knoth 		case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */
23970dca1793SAdrian Knoth 		}
23980dca1793SAdrian Knoth 		break;
23990dca1793SAdrian Knoth 
24000dca1793SAdrian Knoth 	case MADI:
24010dca1793SAdrian Knoth 	case MADIface:
24020dca1793SAdrian Knoth 		if (hdspm->tco) {
24030dca1793SAdrian Knoth 			switch (hdspm->control_register & HDSPM_SyncRefMask) {
24040dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
24050dca1793SAdrian Knoth 			case HDSPM_SyncRef0: return 1;  /* MADI */
24060dca1793SAdrian Knoth 			case HDSPM_SyncRef1: return 2;  /* TCO */
24070dca1793SAdrian Knoth 			case HDSPM_SyncRef1+HDSPM_SyncRef0:
24080dca1793SAdrian Knoth 					     return 3;  /* SYNC_IN */
24093cee5a60SRemy Bruno 			}
24103cee5a60SRemy Bruno 		} else {
2411763f356cSTakashi Iwai 			switch (hdspm->control_register & HDSPM_SyncRefMask) {
24120dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
24130dca1793SAdrian Knoth 			case HDSPM_SyncRef0: return 1;  /* MADI */
24140dca1793SAdrian Knoth 			case HDSPM_SyncRef1+HDSPM_SyncRef0:
24150dca1793SAdrian Knoth 					     return 2;  /* SYNC_IN */
24160dca1793SAdrian Knoth 			}
24170dca1793SAdrian Knoth 		}
24180dca1793SAdrian Knoth 		break;
24190dca1793SAdrian Knoth 
24200dca1793SAdrian Knoth 	case RayDAT:
24210dca1793SAdrian Knoth 		if (hdspm->tco) {
24220dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
24230dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
24240dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
24250dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT 1 */
24260dca1793SAdrian Knoth 			case 4: return 2;  /* ADAT 2 */
24270dca1793SAdrian Knoth 			case 5: return 3;  /* ADAT 3 */
24280dca1793SAdrian Knoth 			case 6: return 4;  /* ADAT 4 */
24290dca1793SAdrian Knoth 			case 1: return 5;  /* AES */
24300dca1793SAdrian Knoth 			case 2: return 6;  /* SPDIF */
24310dca1793SAdrian Knoth 			case 9: return 7;  /* TCO */
24320dca1793SAdrian Knoth 			case 10: return 8; /* SYNC_IN */
24330dca1793SAdrian Knoth 			}
24340dca1793SAdrian Knoth 		} else {
24350dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
24360dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
24370dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
24380dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT 1 */
24390dca1793SAdrian Knoth 			case 4: return 2;  /* ADAT 2 */
24400dca1793SAdrian Knoth 			case 5: return 3;  /* ADAT 3 */
24410dca1793SAdrian Knoth 			case 6: return 4;  /* ADAT 4 */
24420dca1793SAdrian Knoth 			case 1: return 5;  /* AES */
24430dca1793SAdrian Knoth 			case 2: return 6;  /* SPDIF */
24440dca1793SAdrian Knoth 			case 10: return 7; /* SYNC_IN */
2445763f356cSTakashi Iwai 			}
24463cee5a60SRemy Bruno 		}
2447763f356cSTakashi Iwai 
24480dca1793SAdrian Knoth 		break;
24490dca1793SAdrian Knoth 
24500dca1793SAdrian Knoth 	case AIO:
24510dca1793SAdrian Knoth 		if (hdspm->tco) {
24520dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
24530dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
24540dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
24550dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT */
24560dca1793SAdrian Knoth 			case 1: return 2;  /* AES */
24570dca1793SAdrian Knoth 			case 2: return 3;  /* SPDIF */
24580dca1793SAdrian Knoth 			case 9: return 4;  /* TCO */
24590dca1793SAdrian Knoth 			case 10: return 5; /* SYNC_IN */
24600dca1793SAdrian Knoth 			}
24610dca1793SAdrian Knoth 		} else {
24620dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
24630dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
24640dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
24650dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT */
24660dca1793SAdrian Knoth 			case 1: return 2;  /* AES */
24670dca1793SAdrian Knoth 			case 2: return 3;  /* SPDIF */
24680dca1793SAdrian Knoth 			case 10: return 4; /* SYNC_IN */
24690dca1793SAdrian Knoth 			}
2470763f356cSTakashi Iwai 		}
2471763f356cSTakashi Iwai 
24720dca1793SAdrian Knoth 		break;
24730dca1793SAdrian Knoth 	}
24740dca1793SAdrian Knoth 
24750dca1793SAdrian Knoth 	return -1;
24760dca1793SAdrian Knoth }
24770dca1793SAdrian Knoth 
24780dca1793SAdrian Knoth 
24790dca1793SAdrian Knoth /**
24800dca1793SAdrian Knoth  * Set the preferred sync reference to <pref>. The semantics
24810dca1793SAdrian Knoth  * of <pref> are depending on the card type, see the comments
24820dca1793SAdrian Knoth  * for clarification.
24830dca1793SAdrian Knoth  **/
248498274f07STakashi Iwai static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
2485763f356cSTakashi Iwai {
24860dca1793SAdrian Knoth 	int p = 0;
2487763f356cSTakashi Iwai 
24880dca1793SAdrian Knoth 	switch (hdspm->io_type) {
24890dca1793SAdrian Knoth 	case AES32:
24900dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPM_SyncRefMask;
24913cee5a60SRemy Bruno 		switch (pref) {
24920dca1793SAdrian Knoth 		case 0: /* WC  */
24933cee5a60SRemy Bruno 			break;
24940dca1793SAdrian Knoth 		case 1: /* AES 1 */
24953cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef0;
24963cee5a60SRemy Bruno 			break;
24970dca1793SAdrian Knoth 		case 2: /* AES 2 */
24983cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef1;
24993cee5a60SRemy Bruno 			break;
25000dca1793SAdrian Knoth 		case 3: /* AES 3 */
25010dca1793SAdrian Knoth 			hdspm->control_register |=
25020dca1793SAdrian Knoth 				HDSPM_SyncRef1+HDSPM_SyncRef0;
25033cee5a60SRemy Bruno 			break;
25040dca1793SAdrian Knoth 		case 4: /* AES 4 */
25053cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef2;
25063cee5a60SRemy Bruno 			break;
25070dca1793SAdrian Knoth 		case 5: /* AES 5 */
25080dca1793SAdrian Knoth 			hdspm->control_register |=
25090dca1793SAdrian Knoth 				HDSPM_SyncRef2+HDSPM_SyncRef0;
25103cee5a60SRemy Bruno 			break;
25110dca1793SAdrian Knoth 		case 6: /* AES 6 */
25120dca1793SAdrian Knoth 			hdspm->control_register |=
25130dca1793SAdrian Knoth 				HDSPM_SyncRef2+HDSPM_SyncRef1;
25143cee5a60SRemy Bruno 			break;
25150dca1793SAdrian Knoth 		case 7: /* AES 7 */
2516ef5fa1a4STakashi Iwai 			hdspm->control_register |=
2517ef5fa1a4STakashi Iwai 				HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
25183cee5a60SRemy Bruno 			break;
25190dca1793SAdrian Knoth 		case 8: /* AES 8 */
25203cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef3;
25213cee5a60SRemy Bruno 			break;
25220dca1793SAdrian Knoth 		case 9: /* TCO */
25230dca1793SAdrian Knoth 			hdspm->control_register |=
25240dca1793SAdrian Knoth 				HDSPM_SyncRef3+HDSPM_SyncRef0;
25250dca1793SAdrian Knoth 			break;
25260dca1793SAdrian Knoth 		default:
25270dca1793SAdrian Knoth 			return -1;
25280dca1793SAdrian Knoth 		}
25290dca1793SAdrian Knoth 
25300dca1793SAdrian Knoth 		break;
25310dca1793SAdrian Knoth 
25320dca1793SAdrian Knoth 	case MADI:
25330dca1793SAdrian Knoth 	case MADIface:
25340dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPM_SyncRefMask;
25350dca1793SAdrian Knoth 		if (hdspm->tco) {
25360dca1793SAdrian Knoth 			switch (pref) {
25370dca1793SAdrian Knoth 			case 0: /* WC */
25380dca1793SAdrian Knoth 				break;
25390dca1793SAdrian Knoth 			case 1: /* MADI */
25400dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef0;
25410dca1793SAdrian Knoth 				break;
25420dca1793SAdrian Knoth 			case 2: /* TCO */
25430dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef1;
25440dca1793SAdrian Knoth 				break;
25450dca1793SAdrian Knoth 			case 3: /* SYNC_IN */
25460dca1793SAdrian Knoth 				hdspm->control_register |=
25470dca1793SAdrian Knoth 					HDSPM_SyncRef0+HDSPM_SyncRef1;
25480dca1793SAdrian Knoth 				break;
25493cee5a60SRemy Bruno 			default:
25503cee5a60SRemy Bruno 				return -1;
25513cee5a60SRemy Bruno 			}
25523cee5a60SRemy Bruno 		} else {
2553763f356cSTakashi Iwai 			switch (pref) {
25540dca1793SAdrian Knoth 			case 0: /* WC */
2555763f356cSTakashi Iwai 				break;
25560dca1793SAdrian Knoth 			case 1: /* MADI */
25570dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef0;
25580dca1793SAdrian Knoth 				break;
25590dca1793SAdrian Knoth 			case 2: /* SYNC_IN */
25600dca1793SAdrian Knoth 				hdspm->control_register |=
25610dca1793SAdrian Knoth 					HDSPM_SyncRef0+HDSPM_SyncRef1;
2562763f356cSTakashi Iwai 				break;
2563763f356cSTakashi Iwai 			default:
2564763f356cSTakashi Iwai 				return -1;
2565763f356cSTakashi Iwai 			}
25663cee5a60SRemy Bruno 		}
25670dca1793SAdrian Knoth 
25680dca1793SAdrian Knoth 		break;
25690dca1793SAdrian Knoth 
25700dca1793SAdrian Knoth 	case RayDAT:
25710dca1793SAdrian Knoth 		if (hdspm->tco) {
25720dca1793SAdrian Knoth 			switch (pref) {
25730dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
25740dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT 1 */
25750dca1793SAdrian Knoth 			case 2: p = 4; break;  /* ADAT 2 */
25760dca1793SAdrian Knoth 			case 3: p = 5; break;  /* ADAT 3 */
25770dca1793SAdrian Knoth 			case 4: p = 6; break;  /* ADAT 4 */
25780dca1793SAdrian Knoth 			case 5: p = 1; break;  /* AES */
25790dca1793SAdrian Knoth 			case 6: p = 2; break;  /* SPDIF */
25800dca1793SAdrian Knoth 			case 7: p = 9; break;  /* TCO */
25810dca1793SAdrian Knoth 			case 8: p = 10; break; /* SYNC_IN */
25820dca1793SAdrian Knoth 			default: return -1;
25830dca1793SAdrian Knoth 			}
25840dca1793SAdrian Knoth 		} else {
25850dca1793SAdrian Knoth 			switch (pref) {
25860dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
25870dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT 1 */
25880dca1793SAdrian Knoth 			case 2: p = 4; break;  /* ADAT 2 */
25890dca1793SAdrian Knoth 			case 3: p = 5; break;  /* ADAT 3 */
25900dca1793SAdrian Knoth 			case 4: p = 6; break;  /* ADAT 4 */
25910dca1793SAdrian Knoth 			case 5: p = 1; break;  /* AES */
25920dca1793SAdrian Knoth 			case 6: p = 2; break;  /* SPDIF */
25930dca1793SAdrian Knoth 			case 7: p = 10; break; /* SYNC_IN */
25940dca1793SAdrian Knoth 			default: return -1;
25950dca1793SAdrian Knoth 			}
25960dca1793SAdrian Knoth 		}
25970dca1793SAdrian Knoth 		break;
25980dca1793SAdrian Knoth 
25990dca1793SAdrian Knoth 	case AIO:
26000dca1793SAdrian Knoth 		if (hdspm->tco) {
26010dca1793SAdrian Knoth 			switch (pref) {
26020dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
26030dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT */
26040dca1793SAdrian Knoth 			case 2: p = 1; break;  /* AES */
26050dca1793SAdrian Knoth 			case 3: p = 2; break;  /* SPDIF */
26060dca1793SAdrian Knoth 			case 4: p = 9; break;  /* TCO */
26070dca1793SAdrian Knoth 			case 5: p = 10; break; /* SYNC_IN */
26080dca1793SAdrian Knoth 			default: return -1;
26090dca1793SAdrian Knoth 			}
26100dca1793SAdrian Knoth 		} else {
26110dca1793SAdrian Knoth 			switch (pref) {
26120dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
26130dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT */
26140dca1793SAdrian Knoth 			case 2: p = 1; break;  /* AES */
26150dca1793SAdrian Knoth 			case 3: p = 2; break;  /* SPDIF */
26160dca1793SAdrian Knoth 			case 4: p = 10; break; /* SYNC_IN */
26170dca1793SAdrian Knoth 			default: return -1;
26180dca1793SAdrian Knoth 			}
26190dca1793SAdrian Knoth 		}
26200dca1793SAdrian Knoth 		break;
26210dca1793SAdrian Knoth 	}
26220dca1793SAdrian Knoth 
26230dca1793SAdrian Knoth 	switch (hdspm->io_type) {
26240dca1793SAdrian Knoth 	case RayDAT:
26250dca1793SAdrian Knoth 	case AIO:
26260dca1793SAdrian Knoth 		hdspm->settings_register &= ~HDSPM_c0_SyncRefMask;
26270dca1793SAdrian Knoth 		hdspm->settings_register |= HDSPM_c0_SyncRef0 * p;
26280dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
26290dca1793SAdrian Knoth 		break;
26300dca1793SAdrian Knoth 
26310dca1793SAdrian Knoth 	case MADI:
26320dca1793SAdrian Knoth 	case MADIface:
26330dca1793SAdrian Knoth 	case AES32:
26340dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_controlRegister,
26350dca1793SAdrian Knoth 				hdspm->control_register);
26360dca1793SAdrian Knoth 	}
26370dca1793SAdrian Knoth 
2638763f356cSTakashi Iwai 	return 0;
2639763f356cSTakashi Iwai }
2640763f356cSTakashi Iwai 
26410dca1793SAdrian Knoth 
264298274f07STakashi Iwai static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
264398274f07STakashi Iwai 					struct snd_ctl_elem_info *uinfo)
2644763f356cSTakashi Iwai {
26453cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
26463cee5a60SRemy Bruno 
26473cee5a60SRemy Bruno 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
26483cee5a60SRemy Bruno 	uinfo->count = 1;
26490dca1793SAdrian Knoth 	uinfo->value.enumerated.items = hdspm->texts_autosync_items;
26503cee5a60SRemy Bruno 
26510dca1793SAdrian Knoth 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
26523cee5a60SRemy Bruno 		uinfo->value.enumerated.item =
26533cee5a60SRemy Bruno 			uinfo->value.enumerated.items - 1;
26540dca1793SAdrian Knoth 
26553cee5a60SRemy Bruno 	strcpy(uinfo->value.enumerated.name,
26560dca1793SAdrian Knoth 			hdspm->texts_autosync[uinfo->value.enumerated.item]);
2657763f356cSTakashi Iwai 
2658763f356cSTakashi Iwai 	return 0;
2659763f356cSTakashi Iwai }
2660763f356cSTakashi Iwai 
266198274f07STakashi Iwai static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol,
266298274f07STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
2663763f356cSTakashi Iwai {
266498274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
26650dca1793SAdrian Knoth 	int psf = hdspm_pref_sync_ref(hdspm);
2666763f356cSTakashi Iwai 
26670dca1793SAdrian Knoth 	if (psf >= 0) {
26680dca1793SAdrian Knoth 		ucontrol->value.enumerated.item[0] = psf;
2669763f356cSTakashi Iwai 		return 0;
2670763f356cSTakashi Iwai 	}
2671763f356cSTakashi Iwai 
26720dca1793SAdrian Knoth 	return -1;
26730dca1793SAdrian Knoth }
26740dca1793SAdrian Knoth 
267598274f07STakashi Iwai static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
267698274f07STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
2677763f356cSTakashi Iwai {
267898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
26790dca1793SAdrian Knoth 	int val, change = 0;
2680763f356cSTakashi Iwai 
2681763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2682763f356cSTakashi Iwai 		return -EBUSY;
2683763f356cSTakashi Iwai 
26840dca1793SAdrian Knoth 	val = ucontrol->value.enumerated.item[0];
26850dca1793SAdrian Knoth 
26860dca1793SAdrian Knoth 	if (val < 0)
26870dca1793SAdrian Knoth 		val = 0;
26880dca1793SAdrian Knoth 	else if (val >= hdspm->texts_autosync_items)
26890dca1793SAdrian Knoth 		val = hdspm->texts_autosync_items-1;
2690763f356cSTakashi Iwai 
2691763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
26920dca1793SAdrian Knoth 	if (val != hdspm_pref_sync_ref(hdspm))
26930dca1793SAdrian Knoth 		change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0;
26940dca1793SAdrian Knoth 
2695763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2696763f356cSTakashi Iwai 	return change;
2697763f356cSTakashi Iwai }
2698763f356cSTakashi Iwai 
26990dca1793SAdrian Knoth 
2700763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_REF(xname, xindex) \
270167ed4161SClemens Ladisch { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2702763f356cSTakashi Iwai   .name = xname, \
2703763f356cSTakashi Iwai   .index = xindex, \
2704763f356cSTakashi Iwai   .access = SNDRV_CTL_ELEM_ACCESS_READ, \
2705763f356cSTakashi Iwai   .info = snd_hdspm_info_autosync_ref, \
2706763f356cSTakashi Iwai   .get = snd_hdspm_get_autosync_ref, \
2707763f356cSTakashi Iwai }
2708763f356cSTakashi Iwai 
270998274f07STakashi Iwai static int hdspm_autosync_ref(struct hdspm *hdspm)
2710763f356cSTakashi Iwai {
27110dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
27123cee5a60SRemy Bruno 		unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
27130dca1793SAdrian Knoth 		unsigned int syncref =
27140dca1793SAdrian Knoth 			(status >> HDSPM_AES32_syncref_bit) & 0xF;
27153cee5a60SRemy Bruno 		if (syncref == 0)
27163cee5a60SRemy Bruno 			return HDSPM_AES32_AUTOSYNC_FROM_WORD;
27173cee5a60SRemy Bruno 		if (syncref <= 8)
27183cee5a60SRemy Bruno 			return syncref;
27193cee5a60SRemy Bruno 		return HDSPM_AES32_AUTOSYNC_FROM_NONE;
27200dca1793SAdrian Knoth 	} else if (MADI == hdspm->io_type) {
2721763f356cSTakashi Iwai 		/* This looks at the autosync selected sync reference */
2722763f356cSTakashi Iwai 		unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
2723763f356cSTakashi Iwai 
2724763f356cSTakashi Iwai 		switch (status2 & HDSPM_SelSyncRefMask) {
2725763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_WORD:
2726763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_WORD;
2727763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_MADI:
2728763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_MADI;
27290dca1793SAdrian Knoth 		case HDSPM_SelSyncRef_TCO:
27300dca1793SAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_TCO;
27310dca1793SAdrian Knoth 		case HDSPM_SelSyncRef_SyncIn:
27320dca1793SAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_SYNC_IN;
2733763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_NVALID:
2734763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_NONE;
2735763f356cSTakashi Iwai 		default:
2736763f356cSTakashi Iwai 			return 0;
2737763f356cSTakashi Iwai 		}
2738763f356cSTakashi Iwai 
27390dca1793SAdrian Knoth 	}
2740763f356cSTakashi Iwai 	return 0;
2741763f356cSTakashi Iwai }
27420dca1793SAdrian Knoth 
2743763f356cSTakashi Iwai 
274498274f07STakashi Iwai static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
274598274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
2746763f356cSTakashi Iwai {
27473cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
27483cee5a60SRemy Bruno 
27490dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
27503cee5a60SRemy Bruno 		static char *texts[] = { "WordClock", "AES1", "AES2", "AES3",
27513cee5a60SRemy Bruno 			"AES4",	"AES5", "AES6", "AES7", "AES8", "None"};
27523cee5a60SRemy Bruno 
27533cee5a60SRemy Bruno 		uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
27543cee5a60SRemy Bruno 		uinfo->count = 1;
27553cee5a60SRemy Bruno 		uinfo->value.enumerated.items = 10;
2756ef5fa1a4STakashi Iwai 		if (uinfo->value.enumerated.item >=
2757ef5fa1a4STakashi Iwai 		    uinfo->value.enumerated.items)
27583cee5a60SRemy Bruno 			uinfo->value.enumerated.item =
27593cee5a60SRemy Bruno 				uinfo->value.enumerated.items - 1;
27603cee5a60SRemy Bruno 		strcpy(uinfo->value.enumerated.name,
27613cee5a60SRemy Bruno 				texts[uinfo->value.enumerated.item]);
27620dca1793SAdrian Knoth 	} else if (MADI == hdspm->io_type) {
27630dca1793SAdrian Knoth 		static char *texts[] = {"Word Clock", "MADI", "TCO",
27640dca1793SAdrian Knoth 			"Sync In", "None" };
2765763f356cSTakashi Iwai 
2766763f356cSTakashi Iwai 		uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2767763f356cSTakashi Iwai 		uinfo->count = 1;
27680dca1793SAdrian Knoth 		uinfo->value.enumerated.items = 5;
2769ef5fa1a4STakashi Iwai 		if (uinfo->value.enumerated.item >=
2770ef5fa1a4STakashi Iwai 				uinfo->value.enumerated.items)
2771763f356cSTakashi Iwai 			uinfo->value.enumerated.item =
2772763f356cSTakashi Iwai 				uinfo->value.enumerated.items - 1;
2773763f356cSTakashi Iwai 		strcpy(uinfo->value.enumerated.name,
2774763f356cSTakashi Iwai 				texts[uinfo->value.enumerated.item]);
27753cee5a60SRemy Bruno 	}
2776763f356cSTakashi Iwai 	return 0;
2777763f356cSTakashi Iwai }
2778763f356cSTakashi Iwai 
277998274f07STakashi Iwai static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
278098274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
2781763f356cSTakashi Iwai {
278298274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2783763f356cSTakashi Iwai 
27846534599dSRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_autosync_ref(hdspm);
2785763f356cSTakashi Iwai 	return 0;
2786763f356cSTakashi Iwai }
2787763f356cSTakashi Iwai 
27880dca1793SAdrian Knoth 
2789763f356cSTakashi Iwai #define HDSPM_LINE_OUT(xname, xindex) \
279067ed4161SClemens Ladisch { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2791763f356cSTakashi Iwai   .name = xname, \
2792763f356cSTakashi Iwai   .index = xindex, \
2793763f356cSTakashi Iwai   .info = snd_hdspm_info_line_out, \
2794763f356cSTakashi Iwai   .get = snd_hdspm_get_line_out, \
2795763f356cSTakashi Iwai   .put = snd_hdspm_put_line_out \
2796763f356cSTakashi Iwai }
2797763f356cSTakashi Iwai 
279898274f07STakashi Iwai static int hdspm_line_out(struct hdspm * hdspm)
2799763f356cSTakashi Iwai {
2800763f356cSTakashi Iwai 	return (hdspm->control_register & HDSPM_LineOut) ? 1 : 0;
2801763f356cSTakashi Iwai }
2802763f356cSTakashi Iwai 
2803763f356cSTakashi Iwai 
280498274f07STakashi Iwai static int hdspm_set_line_output(struct hdspm * hdspm, int out)
2805763f356cSTakashi Iwai {
2806763f356cSTakashi Iwai 	if (out)
2807763f356cSTakashi Iwai 		hdspm->control_register |= HDSPM_LineOut;
2808763f356cSTakashi Iwai 	else
2809763f356cSTakashi Iwai 		hdspm->control_register &= ~HDSPM_LineOut;
2810763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
2811763f356cSTakashi Iwai 
2812763f356cSTakashi Iwai 	return 0;
2813763f356cSTakashi Iwai }
2814763f356cSTakashi Iwai 
2815a5ce8890STakashi Iwai #define snd_hdspm_info_line_out		snd_ctl_boolean_mono_info
2816763f356cSTakashi Iwai 
281798274f07STakashi Iwai static int snd_hdspm_get_line_out(struct snd_kcontrol *kcontrol,
281898274f07STakashi Iwai 				  struct snd_ctl_elem_value *ucontrol)
2819763f356cSTakashi Iwai {
282098274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2821763f356cSTakashi Iwai 
2822763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2823763f356cSTakashi Iwai 	ucontrol->value.integer.value[0] = hdspm_line_out(hdspm);
2824763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2825763f356cSTakashi Iwai 	return 0;
2826763f356cSTakashi Iwai }
2827763f356cSTakashi Iwai 
282898274f07STakashi Iwai static int snd_hdspm_put_line_out(struct snd_kcontrol *kcontrol,
282998274f07STakashi Iwai 				  struct snd_ctl_elem_value *ucontrol)
2830763f356cSTakashi Iwai {
283198274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2832763f356cSTakashi Iwai 	int change;
2833763f356cSTakashi Iwai 	unsigned int val;
2834763f356cSTakashi Iwai 
2835763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2836763f356cSTakashi Iwai 		return -EBUSY;
2837763f356cSTakashi Iwai 	val = ucontrol->value.integer.value[0] & 1;
2838763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2839763f356cSTakashi Iwai 	change = (int) val != hdspm_line_out(hdspm);
2840763f356cSTakashi Iwai 	hdspm_set_line_output(hdspm, val);
2841763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2842763f356cSTakashi Iwai 	return change;
2843763f356cSTakashi Iwai }
2844763f356cSTakashi Iwai 
28450dca1793SAdrian Knoth 
2846763f356cSTakashi Iwai #define HDSPM_TX_64(xname, xindex) \
284767ed4161SClemens Ladisch { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2848763f356cSTakashi Iwai   .name = xname, \
2849763f356cSTakashi Iwai   .index = xindex, \
2850763f356cSTakashi Iwai   .info = snd_hdspm_info_tx_64, \
2851763f356cSTakashi Iwai   .get = snd_hdspm_get_tx_64, \
2852763f356cSTakashi Iwai   .put = snd_hdspm_put_tx_64 \
2853763f356cSTakashi Iwai }
2854763f356cSTakashi Iwai 
285598274f07STakashi Iwai static int hdspm_tx_64(struct hdspm * hdspm)
2856763f356cSTakashi Iwai {
2857763f356cSTakashi Iwai 	return (hdspm->control_register & HDSPM_TX_64ch) ? 1 : 0;
2858763f356cSTakashi Iwai }
2859763f356cSTakashi Iwai 
286098274f07STakashi Iwai static int hdspm_set_tx_64(struct hdspm * hdspm, int out)
2861763f356cSTakashi Iwai {
2862763f356cSTakashi Iwai 	if (out)
2863763f356cSTakashi Iwai 		hdspm->control_register |= HDSPM_TX_64ch;
2864763f356cSTakashi Iwai 	else
2865763f356cSTakashi Iwai 		hdspm->control_register &= ~HDSPM_TX_64ch;
2866763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
2867763f356cSTakashi Iwai 
2868763f356cSTakashi Iwai 	return 0;
2869763f356cSTakashi Iwai }
2870763f356cSTakashi Iwai 
2871a5ce8890STakashi Iwai #define snd_hdspm_info_tx_64		snd_ctl_boolean_mono_info
2872763f356cSTakashi Iwai 
287398274f07STakashi Iwai static int snd_hdspm_get_tx_64(struct snd_kcontrol *kcontrol,
287498274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
2875763f356cSTakashi Iwai {
287698274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2877763f356cSTakashi Iwai 
2878763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2879763f356cSTakashi Iwai 	ucontrol->value.integer.value[0] = hdspm_tx_64(hdspm);
2880763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2881763f356cSTakashi Iwai 	return 0;
2882763f356cSTakashi Iwai }
2883763f356cSTakashi Iwai 
288498274f07STakashi Iwai static int snd_hdspm_put_tx_64(struct snd_kcontrol *kcontrol,
288598274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
2886763f356cSTakashi Iwai {
288798274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2888763f356cSTakashi Iwai 	int change;
2889763f356cSTakashi Iwai 	unsigned int val;
2890763f356cSTakashi Iwai 
2891763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2892763f356cSTakashi Iwai 		return -EBUSY;
2893763f356cSTakashi Iwai 	val = ucontrol->value.integer.value[0] & 1;
2894763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2895763f356cSTakashi Iwai 	change = (int) val != hdspm_tx_64(hdspm);
2896763f356cSTakashi Iwai 	hdspm_set_tx_64(hdspm, val);
2897763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2898763f356cSTakashi Iwai 	return change;
2899763f356cSTakashi Iwai }
2900763f356cSTakashi Iwai 
29010dca1793SAdrian Knoth 
2902763f356cSTakashi Iwai #define HDSPM_C_TMS(xname, xindex) \
290367ed4161SClemens Ladisch { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2904763f356cSTakashi Iwai   .name = xname, \
2905763f356cSTakashi Iwai   .index = xindex, \
2906763f356cSTakashi Iwai   .info = snd_hdspm_info_c_tms, \
2907763f356cSTakashi Iwai   .get = snd_hdspm_get_c_tms, \
2908763f356cSTakashi Iwai   .put = snd_hdspm_put_c_tms \
2909763f356cSTakashi Iwai }
2910763f356cSTakashi Iwai 
291198274f07STakashi Iwai static int hdspm_c_tms(struct hdspm * hdspm)
2912763f356cSTakashi Iwai {
2913763f356cSTakashi Iwai 	return (hdspm->control_register & HDSPM_clr_tms) ? 1 : 0;
2914763f356cSTakashi Iwai }
2915763f356cSTakashi Iwai 
291698274f07STakashi Iwai static int hdspm_set_c_tms(struct hdspm * hdspm, int out)
2917763f356cSTakashi Iwai {
2918763f356cSTakashi Iwai 	if (out)
2919763f356cSTakashi Iwai 		hdspm->control_register |= HDSPM_clr_tms;
2920763f356cSTakashi Iwai 	else
2921763f356cSTakashi Iwai 		hdspm->control_register &= ~HDSPM_clr_tms;
2922763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
2923763f356cSTakashi Iwai 
2924763f356cSTakashi Iwai 	return 0;
2925763f356cSTakashi Iwai }
2926763f356cSTakashi Iwai 
2927a5ce8890STakashi Iwai #define snd_hdspm_info_c_tms		snd_ctl_boolean_mono_info
2928763f356cSTakashi Iwai 
292998274f07STakashi Iwai static int snd_hdspm_get_c_tms(struct snd_kcontrol *kcontrol,
293098274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
2931763f356cSTakashi Iwai {
293298274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2933763f356cSTakashi Iwai 
2934763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2935763f356cSTakashi Iwai 	ucontrol->value.integer.value[0] = hdspm_c_tms(hdspm);
2936763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2937763f356cSTakashi Iwai 	return 0;
2938763f356cSTakashi Iwai }
2939763f356cSTakashi Iwai 
294098274f07STakashi Iwai static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol,
294198274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
2942763f356cSTakashi Iwai {
294398274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2944763f356cSTakashi Iwai 	int change;
2945763f356cSTakashi Iwai 	unsigned int val;
2946763f356cSTakashi Iwai 
2947763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2948763f356cSTakashi Iwai 		return -EBUSY;
2949763f356cSTakashi Iwai 	val = ucontrol->value.integer.value[0] & 1;
2950763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2951763f356cSTakashi Iwai 	change = (int) val != hdspm_c_tms(hdspm);
2952763f356cSTakashi Iwai 	hdspm_set_c_tms(hdspm, val);
2953763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2954763f356cSTakashi Iwai 	return change;
2955763f356cSTakashi Iwai }
2956763f356cSTakashi Iwai 
29570dca1793SAdrian Knoth 
2958763f356cSTakashi Iwai #define HDSPM_SAFE_MODE(xname, xindex) \
295967ed4161SClemens Ladisch { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2960763f356cSTakashi Iwai   .name = xname, \
2961763f356cSTakashi Iwai   .index = xindex, \
2962763f356cSTakashi Iwai   .info = snd_hdspm_info_safe_mode, \
2963763f356cSTakashi Iwai   .get = snd_hdspm_get_safe_mode, \
2964763f356cSTakashi Iwai   .put = snd_hdspm_put_safe_mode \
2965763f356cSTakashi Iwai }
2966763f356cSTakashi Iwai 
296798274f07STakashi Iwai static int hdspm_safe_mode(struct hdspm * hdspm)
2968763f356cSTakashi Iwai {
2969763f356cSTakashi Iwai 	return (hdspm->control_register & HDSPM_AutoInp) ? 1 : 0;
2970763f356cSTakashi Iwai }
2971763f356cSTakashi Iwai 
297298274f07STakashi Iwai static int hdspm_set_safe_mode(struct hdspm * hdspm, int out)
2973763f356cSTakashi Iwai {
2974763f356cSTakashi Iwai 	if (out)
2975763f356cSTakashi Iwai 		hdspm->control_register |= HDSPM_AutoInp;
2976763f356cSTakashi Iwai 	else
2977763f356cSTakashi Iwai 		hdspm->control_register &= ~HDSPM_AutoInp;
2978763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
2979763f356cSTakashi Iwai 
2980763f356cSTakashi Iwai 	return 0;
2981763f356cSTakashi Iwai }
2982763f356cSTakashi Iwai 
2983a5ce8890STakashi Iwai #define snd_hdspm_info_safe_mode	snd_ctl_boolean_mono_info
2984763f356cSTakashi Iwai 
298598274f07STakashi Iwai static int snd_hdspm_get_safe_mode(struct snd_kcontrol *kcontrol,
298698274f07STakashi Iwai 				   struct snd_ctl_elem_value *ucontrol)
2987763f356cSTakashi Iwai {
298898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2989763f356cSTakashi Iwai 
2990763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2991763f356cSTakashi Iwai 	ucontrol->value.integer.value[0] = hdspm_safe_mode(hdspm);
2992763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2993763f356cSTakashi Iwai 	return 0;
2994763f356cSTakashi Iwai }
2995763f356cSTakashi Iwai 
299698274f07STakashi Iwai static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol,
299798274f07STakashi Iwai 				   struct snd_ctl_elem_value *ucontrol)
2998763f356cSTakashi Iwai {
299998274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3000763f356cSTakashi Iwai 	int change;
3001763f356cSTakashi Iwai 	unsigned int val;
3002763f356cSTakashi Iwai 
3003763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3004763f356cSTakashi Iwai 		return -EBUSY;
3005763f356cSTakashi Iwai 	val = ucontrol->value.integer.value[0] & 1;
3006763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3007763f356cSTakashi Iwai 	change = (int) val != hdspm_safe_mode(hdspm);
3008763f356cSTakashi Iwai 	hdspm_set_safe_mode(hdspm, val);
3009763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3010763f356cSTakashi Iwai 	return change;
3011763f356cSTakashi Iwai }
3012763f356cSTakashi Iwai 
30130dca1793SAdrian Knoth 
30143cee5a60SRemy Bruno #define HDSPM_EMPHASIS(xname, xindex) \
30153cee5a60SRemy Bruno { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
30163cee5a60SRemy Bruno   .name = xname, \
30173cee5a60SRemy Bruno   .index = xindex, \
30183cee5a60SRemy Bruno   .info = snd_hdspm_info_emphasis, \
30193cee5a60SRemy Bruno   .get = snd_hdspm_get_emphasis, \
30203cee5a60SRemy Bruno   .put = snd_hdspm_put_emphasis \
30213cee5a60SRemy Bruno }
30223cee5a60SRemy Bruno 
30233cee5a60SRemy Bruno static int hdspm_emphasis(struct hdspm * hdspm)
30243cee5a60SRemy Bruno {
30253cee5a60SRemy Bruno 	return (hdspm->control_register & HDSPM_Emphasis) ? 1 : 0;
30263cee5a60SRemy Bruno }
30273cee5a60SRemy Bruno 
30283cee5a60SRemy Bruno static int hdspm_set_emphasis(struct hdspm * hdspm, int emp)
30293cee5a60SRemy Bruno {
30303cee5a60SRemy Bruno 	if (emp)
30313cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_Emphasis;
30323cee5a60SRemy Bruno 	else
30333cee5a60SRemy Bruno 		hdspm->control_register &= ~HDSPM_Emphasis;
30343cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
30353cee5a60SRemy Bruno 
30363cee5a60SRemy Bruno 	return 0;
30373cee5a60SRemy Bruno }
30383cee5a60SRemy Bruno 
3039a5ce8890STakashi Iwai #define snd_hdspm_info_emphasis		snd_ctl_boolean_mono_info
30403cee5a60SRemy Bruno 
30413cee5a60SRemy Bruno static int snd_hdspm_get_emphasis(struct snd_kcontrol *kcontrol,
30423cee5a60SRemy Bruno 				  struct snd_ctl_elem_value *ucontrol)
30433cee5a60SRemy Bruno {
30443cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
30453cee5a60SRemy Bruno 
30463cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
30473cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_emphasis(hdspm);
30483cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
30493cee5a60SRemy Bruno 	return 0;
30503cee5a60SRemy Bruno }
30513cee5a60SRemy Bruno 
30523cee5a60SRemy Bruno static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol,
30533cee5a60SRemy Bruno 				  struct snd_ctl_elem_value *ucontrol)
30543cee5a60SRemy Bruno {
30553cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
30563cee5a60SRemy Bruno 	int change;
30573cee5a60SRemy Bruno 	unsigned int val;
30583cee5a60SRemy Bruno 
30593cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
30603cee5a60SRemy Bruno 		return -EBUSY;
30613cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0] & 1;
30623cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
30633cee5a60SRemy Bruno 	change = (int) val != hdspm_emphasis(hdspm);
30643cee5a60SRemy Bruno 	hdspm_set_emphasis(hdspm, val);
30653cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
30663cee5a60SRemy Bruno 	return change;
30673cee5a60SRemy Bruno }
30683cee5a60SRemy Bruno 
30690dca1793SAdrian Knoth 
30703cee5a60SRemy Bruno #define HDSPM_DOLBY(xname, xindex) \
30713cee5a60SRemy Bruno { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
30723cee5a60SRemy Bruno   .name = xname, \
30733cee5a60SRemy Bruno   .index = xindex, \
30743cee5a60SRemy Bruno   .info = snd_hdspm_info_dolby, \
30753cee5a60SRemy Bruno   .get = snd_hdspm_get_dolby, \
30763cee5a60SRemy Bruno   .put = snd_hdspm_put_dolby \
30773cee5a60SRemy Bruno }
30783cee5a60SRemy Bruno 
30793cee5a60SRemy Bruno static int hdspm_dolby(struct hdspm * hdspm)
30803cee5a60SRemy Bruno {
30813cee5a60SRemy Bruno 	return (hdspm->control_register & HDSPM_Dolby) ? 1 : 0;
30823cee5a60SRemy Bruno }
30833cee5a60SRemy Bruno 
30843cee5a60SRemy Bruno static int hdspm_set_dolby(struct hdspm * hdspm, int dol)
30853cee5a60SRemy Bruno {
30863cee5a60SRemy Bruno 	if (dol)
30873cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_Dolby;
30883cee5a60SRemy Bruno 	else
30893cee5a60SRemy Bruno 		hdspm->control_register &= ~HDSPM_Dolby;
30903cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
30913cee5a60SRemy Bruno 
30923cee5a60SRemy Bruno 	return 0;
30933cee5a60SRemy Bruno }
30943cee5a60SRemy Bruno 
3095a5ce8890STakashi Iwai #define snd_hdspm_info_dolby		snd_ctl_boolean_mono_info
30963cee5a60SRemy Bruno 
30973cee5a60SRemy Bruno static int snd_hdspm_get_dolby(struct snd_kcontrol *kcontrol,
30983cee5a60SRemy Bruno 			       struct snd_ctl_elem_value *ucontrol)
30993cee5a60SRemy Bruno {
31003cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
31013cee5a60SRemy Bruno 
31023cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
31033cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_dolby(hdspm);
31043cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
31053cee5a60SRemy Bruno 	return 0;
31063cee5a60SRemy Bruno }
31073cee5a60SRemy Bruno 
31083cee5a60SRemy Bruno static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol,
31093cee5a60SRemy Bruno 			       struct snd_ctl_elem_value *ucontrol)
31103cee5a60SRemy Bruno {
31113cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
31123cee5a60SRemy Bruno 	int change;
31133cee5a60SRemy Bruno 	unsigned int val;
31143cee5a60SRemy Bruno 
31153cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
31163cee5a60SRemy Bruno 		return -EBUSY;
31173cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0] & 1;
31183cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
31193cee5a60SRemy Bruno 	change = (int) val != hdspm_dolby(hdspm);
31203cee5a60SRemy Bruno 	hdspm_set_dolby(hdspm, val);
31213cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
31223cee5a60SRemy Bruno 	return change;
31233cee5a60SRemy Bruno }
31243cee5a60SRemy Bruno 
31250dca1793SAdrian Knoth 
31263cee5a60SRemy Bruno #define HDSPM_PROFESSIONAL(xname, xindex) \
31273cee5a60SRemy Bruno { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
31283cee5a60SRemy Bruno   .name = xname, \
31293cee5a60SRemy Bruno   .index = xindex, \
31303cee5a60SRemy Bruno   .info = snd_hdspm_info_professional, \
31313cee5a60SRemy Bruno   .get = snd_hdspm_get_professional, \
31323cee5a60SRemy Bruno   .put = snd_hdspm_put_professional \
31333cee5a60SRemy Bruno }
31343cee5a60SRemy Bruno 
31353cee5a60SRemy Bruno static int hdspm_professional(struct hdspm * hdspm)
31363cee5a60SRemy Bruno {
31373cee5a60SRemy Bruno 	return (hdspm->control_register & HDSPM_Professional) ? 1 : 0;
31383cee5a60SRemy Bruno }
31393cee5a60SRemy Bruno 
31403cee5a60SRemy Bruno static int hdspm_set_professional(struct hdspm * hdspm, int dol)
31413cee5a60SRemy Bruno {
31423cee5a60SRemy Bruno 	if (dol)
31433cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_Professional;
31443cee5a60SRemy Bruno 	else
31453cee5a60SRemy Bruno 		hdspm->control_register &= ~HDSPM_Professional;
31463cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
31473cee5a60SRemy Bruno 
31483cee5a60SRemy Bruno 	return 0;
31493cee5a60SRemy Bruno }
31503cee5a60SRemy Bruno 
3151a5ce8890STakashi Iwai #define snd_hdspm_info_professional	snd_ctl_boolean_mono_info
31523cee5a60SRemy Bruno 
31533cee5a60SRemy Bruno static int snd_hdspm_get_professional(struct snd_kcontrol *kcontrol,
31543cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
31553cee5a60SRemy Bruno {
31563cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
31573cee5a60SRemy Bruno 
31583cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
31593cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_professional(hdspm);
31603cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
31613cee5a60SRemy Bruno 	return 0;
31623cee5a60SRemy Bruno }
31633cee5a60SRemy Bruno 
31643cee5a60SRemy Bruno static int snd_hdspm_put_professional(struct snd_kcontrol *kcontrol,
31653cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
31663cee5a60SRemy Bruno {
31673cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
31683cee5a60SRemy Bruno 	int change;
31693cee5a60SRemy Bruno 	unsigned int val;
31703cee5a60SRemy Bruno 
31713cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
31723cee5a60SRemy Bruno 		return -EBUSY;
31733cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0] & 1;
31743cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
31753cee5a60SRemy Bruno 	change = (int) val != hdspm_professional(hdspm);
31763cee5a60SRemy Bruno 	hdspm_set_professional(hdspm, val);
31773cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
31783cee5a60SRemy Bruno 	return change;
31793cee5a60SRemy Bruno }
31803cee5a60SRemy Bruno 
3181763f356cSTakashi Iwai #define HDSPM_INPUT_SELECT(xname, xindex) \
318267ed4161SClemens Ladisch { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3183763f356cSTakashi Iwai   .name = xname, \
3184763f356cSTakashi Iwai   .index = xindex, \
3185763f356cSTakashi Iwai   .info = snd_hdspm_info_input_select, \
3186763f356cSTakashi Iwai   .get = snd_hdspm_get_input_select, \
3187763f356cSTakashi Iwai   .put = snd_hdspm_put_input_select \
3188763f356cSTakashi Iwai }
3189763f356cSTakashi Iwai 
319098274f07STakashi Iwai static int hdspm_input_select(struct hdspm * hdspm)
3191763f356cSTakashi Iwai {
3192763f356cSTakashi Iwai 	return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
3193763f356cSTakashi Iwai }
3194763f356cSTakashi Iwai 
319598274f07STakashi Iwai static int hdspm_set_input_select(struct hdspm * hdspm, int out)
3196763f356cSTakashi Iwai {
3197763f356cSTakashi Iwai 	if (out)
3198763f356cSTakashi Iwai 		hdspm->control_register |= HDSPM_InputSelect0;
3199763f356cSTakashi Iwai 	else
3200763f356cSTakashi Iwai 		hdspm->control_register &= ~HDSPM_InputSelect0;
3201763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
3202763f356cSTakashi Iwai 
3203763f356cSTakashi Iwai 	return 0;
3204763f356cSTakashi Iwai }
3205763f356cSTakashi Iwai 
320698274f07STakashi Iwai static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
320798274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
3208763f356cSTakashi Iwai {
3209763f356cSTakashi Iwai 	static char *texts[] = { "optical", "coaxial" };
3210763f356cSTakashi Iwai 
3211763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
3212763f356cSTakashi Iwai 	uinfo->count = 1;
3213763f356cSTakashi Iwai 	uinfo->value.enumerated.items = 2;
3214763f356cSTakashi Iwai 
3215763f356cSTakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
3216763f356cSTakashi Iwai 		uinfo->value.enumerated.item =
3217763f356cSTakashi Iwai 		    uinfo->value.enumerated.items - 1;
3218763f356cSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
3219763f356cSTakashi Iwai 	       texts[uinfo->value.enumerated.item]);
3220763f356cSTakashi Iwai 
3221763f356cSTakashi Iwai 	return 0;
3222763f356cSTakashi Iwai }
3223763f356cSTakashi Iwai 
322498274f07STakashi Iwai static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
322598274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3226763f356cSTakashi Iwai {
322798274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3228763f356cSTakashi Iwai 
3229763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3230763f356cSTakashi Iwai 	ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
3231763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3232763f356cSTakashi Iwai 	return 0;
3233763f356cSTakashi Iwai }
3234763f356cSTakashi Iwai 
323598274f07STakashi Iwai static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
323698274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3237763f356cSTakashi Iwai {
323898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3239763f356cSTakashi Iwai 	int change;
3240763f356cSTakashi Iwai 	unsigned int val;
3241763f356cSTakashi Iwai 
3242763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3243763f356cSTakashi Iwai 		return -EBUSY;
3244763f356cSTakashi Iwai 	val = ucontrol->value.integer.value[0] & 1;
3245763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3246763f356cSTakashi Iwai 	change = (int) val != hdspm_input_select(hdspm);
3247763f356cSTakashi Iwai 	hdspm_set_input_select(hdspm, val);
3248763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3249763f356cSTakashi Iwai 	return change;
3250763f356cSTakashi Iwai }
3251763f356cSTakashi Iwai 
32520dca1793SAdrian Knoth 
32533cee5a60SRemy Bruno #define HDSPM_DS_WIRE(xname, xindex) \
32543cee5a60SRemy Bruno { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
32553cee5a60SRemy Bruno   .name = xname, \
32563cee5a60SRemy Bruno   .index = xindex, \
32573cee5a60SRemy Bruno   .info = snd_hdspm_info_ds_wire, \
32583cee5a60SRemy Bruno   .get = snd_hdspm_get_ds_wire, \
32593cee5a60SRemy Bruno   .put = snd_hdspm_put_ds_wire \
32603cee5a60SRemy Bruno }
32613cee5a60SRemy Bruno 
32623cee5a60SRemy Bruno static int hdspm_ds_wire(struct hdspm * hdspm)
32633cee5a60SRemy Bruno {
32643cee5a60SRemy Bruno 	return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
32653cee5a60SRemy Bruno }
32663cee5a60SRemy Bruno 
32673cee5a60SRemy Bruno static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
32683cee5a60SRemy Bruno {
32693cee5a60SRemy Bruno 	if (ds)
32703cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_DS_DoubleWire;
32713cee5a60SRemy Bruno 	else
32723cee5a60SRemy Bruno 		hdspm->control_register &= ~HDSPM_DS_DoubleWire;
32733cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
32743cee5a60SRemy Bruno 
32753cee5a60SRemy Bruno 	return 0;
32763cee5a60SRemy Bruno }
32773cee5a60SRemy Bruno 
32783cee5a60SRemy Bruno static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
32793cee5a60SRemy Bruno 				  struct snd_ctl_elem_info *uinfo)
32803cee5a60SRemy Bruno {
32813cee5a60SRemy Bruno 	static char *texts[] = { "Single", "Double" };
32823cee5a60SRemy Bruno 
32833cee5a60SRemy Bruno 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
32843cee5a60SRemy Bruno 	uinfo->count = 1;
32853cee5a60SRemy Bruno 	uinfo->value.enumerated.items = 2;
32863cee5a60SRemy Bruno 
32873cee5a60SRemy Bruno 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
32883cee5a60SRemy Bruno 		uinfo->value.enumerated.item =
32893cee5a60SRemy Bruno 		    uinfo->value.enumerated.items - 1;
32903cee5a60SRemy Bruno 	strcpy(uinfo->value.enumerated.name,
32913cee5a60SRemy Bruno 	       texts[uinfo->value.enumerated.item]);
32923cee5a60SRemy Bruno 
32933cee5a60SRemy Bruno 	return 0;
32943cee5a60SRemy Bruno }
32953cee5a60SRemy Bruno 
32963cee5a60SRemy Bruno static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
32973cee5a60SRemy Bruno 				 struct snd_ctl_elem_value *ucontrol)
32983cee5a60SRemy Bruno {
32993cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33003cee5a60SRemy Bruno 
33013cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
33023cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
33033cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
33043cee5a60SRemy Bruno 	return 0;
33053cee5a60SRemy Bruno }
33063cee5a60SRemy Bruno 
33073cee5a60SRemy Bruno static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
33083cee5a60SRemy Bruno 				 struct snd_ctl_elem_value *ucontrol)
33093cee5a60SRemy Bruno {
33103cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33113cee5a60SRemy Bruno 	int change;
33123cee5a60SRemy Bruno 	unsigned int val;
33133cee5a60SRemy Bruno 
33143cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
33153cee5a60SRemy Bruno 		return -EBUSY;
33163cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0] & 1;
33173cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
33183cee5a60SRemy Bruno 	change = (int) val != hdspm_ds_wire(hdspm);
33193cee5a60SRemy Bruno 	hdspm_set_ds_wire(hdspm, val);
33203cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
33213cee5a60SRemy Bruno 	return change;
33223cee5a60SRemy Bruno }
33233cee5a60SRemy Bruno 
33240dca1793SAdrian Knoth 
33253cee5a60SRemy Bruno #define HDSPM_QS_WIRE(xname, xindex) \
33263cee5a60SRemy Bruno { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
33273cee5a60SRemy Bruno   .name = xname, \
33283cee5a60SRemy Bruno   .index = xindex, \
33293cee5a60SRemy Bruno   .info = snd_hdspm_info_qs_wire, \
33303cee5a60SRemy Bruno   .get = snd_hdspm_get_qs_wire, \
33313cee5a60SRemy Bruno   .put = snd_hdspm_put_qs_wire \
33323cee5a60SRemy Bruno }
33333cee5a60SRemy Bruno 
33343cee5a60SRemy Bruno static int hdspm_qs_wire(struct hdspm * hdspm)
33353cee5a60SRemy Bruno {
33363cee5a60SRemy Bruno 	if (hdspm->control_register & HDSPM_QS_DoubleWire)
33373cee5a60SRemy Bruno 		return 1;
33383cee5a60SRemy Bruno 	if (hdspm->control_register & HDSPM_QS_QuadWire)
33393cee5a60SRemy Bruno 		return 2;
33403cee5a60SRemy Bruno 	return 0;
33413cee5a60SRemy Bruno }
33423cee5a60SRemy Bruno 
33433cee5a60SRemy Bruno static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
33443cee5a60SRemy Bruno {
33453cee5a60SRemy Bruno 	hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
33463cee5a60SRemy Bruno 	switch (mode) {
33473cee5a60SRemy Bruno 	case 0:
33483cee5a60SRemy Bruno 		break;
33493cee5a60SRemy Bruno 	case 1:
33503cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_QS_DoubleWire;
33513cee5a60SRemy Bruno 		break;
33523cee5a60SRemy Bruno 	case 2:
33533cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_QS_QuadWire;
33543cee5a60SRemy Bruno 		break;
33553cee5a60SRemy Bruno 	}
33563cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
33573cee5a60SRemy Bruno 
33583cee5a60SRemy Bruno 	return 0;
33593cee5a60SRemy Bruno }
33603cee5a60SRemy Bruno 
33613cee5a60SRemy Bruno static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
33623cee5a60SRemy Bruno 				       struct snd_ctl_elem_info *uinfo)
33633cee5a60SRemy Bruno {
33643cee5a60SRemy Bruno 	static char *texts[] = { "Single", "Double", "Quad" };
33653cee5a60SRemy Bruno 
33663cee5a60SRemy Bruno 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
33673cee5a60SRemy Bruno 	uinfo->count = 1;
33683cee5a60SRemy Bruno 	uinfo->value.enumerated.items = 3;
33693cee5a60SRemy Bruno 
33703cee5a60SRemy Bruno 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
33713cee5a60SRemy Bruno 		uinfo->value.enumerated.item =
33723cee5a60SRemy Bruno 		    uinfo->value.enumerated.items - 1;
33733cee5a60SRemy Bruno 	strcpy(uinfo->value.enumerated.name,
33743cee5a60SRemy Bruno 	       texts[uinfo->value.enumerated.item]);
33753cee5a60SRemy Bruno 
33763cee5a60SRemy Bruno 	return 0;
33773cee5a60SRemy Bruno }
33783cee5a60SRemy Bruno 
33793cee5a60SRemy Bruno static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
33803cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
33813cee5a60SRemy Bruno {
33823cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33833cee5a60SRemy Bruno 
33843cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
33853cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
33863cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
33873cee5a60SRemy Bruno 	return 0;
33883cee5a60SRemy Bruno }
33893cee5a60SRemy Bruno 
33903cee5a60SRemy Bruno static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
33913cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
33923cee5a60SRemy Bruno {
33933cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33943cee5a60SRemy Bruno 	int change;
33953cee5a60SRemy Bruno 	int val;
33963cee5a60SRemy Bruno 
33973cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
33983cee5a60SRemy Bruno 		return -EBUSY;
33993cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0];
34003cee5a60SRemy Bruno 	if (val < 0)
34013cee5a60SRemy Bruno 		val = 0;
34023cee5a60SRemy Bruno 	if (val > 2)
34033cee5a60SRemy Bruno 		val = 2;
34043cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
3405ef5fa1a4STakashi Iwai 	change = val != hdspm_qs_wire(hdspm);
34063cee5a60SRemy Bruno 	hdspm_set_qs_wire(hdspm, val);
34073cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
34083cee5a60SRemy Bruno 	return change;
34093cee5a60SRemy Bruno }
34103cee5a60SRemy Bruno 
3411763f356cSTakashi Iwai 
3412763f356cSTakashi Iwai #define HDSPM_MIXER(xname, xindex) \
3413763f356cSTakashi Iwai { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
3414763f356cSTakashi Iwai   .name = xname, \
3415763f356cSTakashi Iwai   .index = xindex, \
341667ed4161SClemens Ladisch   .device = 0, \
3417763f356cSTakashi Iwai   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
3418763f356cSTakashi Iwai 		 SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3419763f356cSTakashi Iwai   .info = snd_hdspm_info_mixer, \
3420763f356cSTakashi Iwai   .get = snd_hdspm_get_mixer, \
3421763f356cSTakashi Iwai   .put = snd_hdspm_put_mixer \
3422763f356cSTakashi Iwai }
3423763f356cSTakashi Iwai 
342498274f07STakashi Iwai static int snd_hdspm_info_mixer(struct snd_kcontrol *kcontrol,
342598274f07STakashi Iwai 				struct snd_ctl_elem_info *uinfo)
3426763f356cSTakashi Iwai {
3427763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3428763f356cSTakashi Iwai 	uinfo->count = 3;
3429763f356cSTakashi Iwai 	uinfo->value.integer.min = 0;
3430763f356cSTakashi Iwai 	uinfo->value.integer.max = 65535;
3431763f356cSTakashi Iwai 	uinfo->value.integer.step = 1;
3432763f356cSTakashi Iwai 	return 0;
3433763f356cSTakashi Iwai }
3434763f356cSTakashi Iwai 
343598274f07STakashi Iwai static int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
343698274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
3437763f356cSTakashi Iwai {
343898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3439763f356cSTakashi Iwai 	int source;
3440763f356cSTakashi Iwai 	int destination;
3441763f356cSTakashi Iwai 
3442763f356cSTakashi Iwai 	source = ucontrol->value.integer.value[0];
3443763f356cSTakashi Iwai 	if (source < 0)
3444763f356cSTakashi Iwai 		source = 0;
3445763f356cSTakashi Iwai 	else if (source >= 2 * HDSPM_MAX_CHANNELS)
3446763f356cSTakashi Iwai 		source = 2 * HDSPM_MAX_CHANNELS - 1;
3447763f356cSTakashi Iwai 
3448763f356cSTakashi Iwai 	destination = ucontrol->value.integer.value[1];
3449763f356cSTakashi Iwai 	if (destination < 0)
3450763f356cSTakashi Iwai 		destination = 0;
3451763f356cSTakashi Iwai 	else if (destination >= HDSPM_MAX_CHANNELS)
3452763f356cSTakashi Iwai 		destination = HDSPM_MAX_CHANNELS - 1;
3453763f356cSTakashi Iwai 
3454763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3455763f356cSTakashi Iwai 	if (source >= HDSPM_MAX_CHANNELS)
3456763f356cSTakashi Iwai 		ucontrol->value.integer.value[2] =
3457763f356cSTakashi Iwai 		    hdspm_read_pb_gain(hdspm, destination,
3458763f356cSTakashi Iwai 				       source - HDSPM_MAX_CHANNELS);
3459763f356cSTakashi Iwai 	else
3460763f356cSTakashi Iwai 		ucontrol->value.integer.value[2] =
3461763f356cSTakashi Iwai 		    hdspm_read_in_gain(hdspm, destination, source);
3462763f356cSTakashi Iwai 
3463763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3464763f356cSTakashi Iwai 
3465763f356cSTakashi Iwai 	return 0;
3466763f356cSTakashi Iwai }
3467763f356cSTakashi Iwai 
346898274f07STakashi Iwai static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
346998274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
3470763f356cSTakashi Iwai {
347198274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3472763f356cSTakashi Iwai 	int change;
3473763f356cSTakashi Iwai 	int source;
3474763f356cSTakashi Iwai 	int destination;
3475763f356cSTakashi Iwai 	int gain;
3476763f356cSTakashi Iwai 
3477763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3478763f356cSTakashi Iwai 		return -EBUSY;
3479763f356cSTakashi Iwai 
3480763f356cSTakashi Iwai 	source = ucontrol->value.integer.value[0];
3481763f356cSTakashi Iwai 	destination = ucontrol->value.integer.value[1];
3482763f356cSTakashi Iwai 
3483763f356cSTakashi Iwai 	if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
3484763f356cSTakashi Iwai 		return -1;
3485763f356cSTakashi Iwai 	if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
3486763f356cSTakashi Iwai 		return -1;
3487763f356cSTakashi Iwai 
3488763f356cSTakashi Iwai 	gain = ucontrol->value.integer.value[2];
3489763f356cSTakashi Iwai 
3490763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3491763f356cSTakashi Iwai 
3492763f356cSTakashi Iwai 	if (source >= HDSPM_MAX_CHANNELS)
3493763f356cSTakashi Iwai 		change = gain != hdspm_read_pb_gain(hdspm, destination,
3494763f356cSTakashi Iwai 						    source -
3495763f356cSTakashi Iwai 						    HDSPM_MAX_CHANNELS);
3496763f356cSTakashi Iwai 	else
3497ef5fa1a4STakashi Iwai 		change = gain != hdspm_read_in_gain(hdspm, destination,
3498ef5fa1a4STakashi Iwai 						    source);
3499763f356cSTakashi Iwai 
3500763f356cSTakashi Iwai 	if (change) {
3501763f356cSTakashi Iwai 		if (source >= HDSPM_MAX_CHANNELS)
3502763f356cSTakashi Iwai 			hdspm_write_pb_gain(hdspm, destination,
3503763f356cSTakashi Iwai 					    source - HDSPM_MAX_CHANNELS,
3504763f356cSTakashi Iwai 					    gain);
3505763f356cSTakashi Iwai 		else
3506763f356cSTakashi Iwai 			hdspm_write_in_gain(hdspm, destination, source,
3507763f356cSTakashi Iwai 					    gain);
3508763f356cSTakashi Iwai 	}
3509763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3510763f356cSTakashi Iwai 
3511763f356cSTakashi Iwai 	return change;
3512763f356cSTakashi Iwai }
3513763f356cSTakashi Iwai 
3514763f356cSTakashi Iwai /* The simple mixer control(s) provide gain control for the
3515763f356cSTakashi Iwai    basic 1:1 mappings of playback streams to output
3516763f356cSTakashi Iwai    streams.
3517763f356cSTakashi Iwai */
3518763f356cSTakashi Iwai 
3519763f356cSTakashi Iwai #define HDSPM_PLAYBACK_MIXER \
3520763f356cSTakashi Iwai { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3521763f356cSTakashi Iwai   .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
3522763f356cSTakashi Iwai 		 SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3523763f356cSTakashi Iwai   .info = snd_hdspm_info_playback_mixer, \
3524763f356cSTakashi Iwai   .get = snd_hdspm_get_playback_mixer, \
3525763f356cSTakashi Iwai   .put = snd_hdspm_put_playback_mixer \
3526763f356cSTakashi Iwai }
3527763f356cSTakashi Iwai 
352898274f07STakashi Iwai static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
352998274f07STakashi Iwai 					 struct snd_ctl_elem_info *uinfo)
3530763f356cSTakashi Iwai {
3531763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3532763f356cSTakashi Iwai 	uinfo->count = 1;
3533763f356cSTakashi Iwai 	uinfo->value.integer.min = 0;
35340dca1793SAdrian Knoth 	uinfo->value.integer.max = 64;
3535763f356cSTakashi Iwai 	uinfo->value.integer.step = 1;
3536763f356cSTakashi Iwai 	return 0;
3537763f356cSTakashi Iwai }
3538763f356cSTakashi Iwai 
353998274f07STakashi Iwai static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
354098274f07STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
3541763f356cSTakashi Iwai {
354298274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3543763f356cSTakashi Iwai 	int channel;
3544763f356cSTakashi Iwai 
3545763f356cSTakashi Iwai 	channel = ucontrol->id.index - 1;
3546763f356cSTakashi Iwai 
3547da3cec35STakashi Iwai 	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
3548da3cec35STakashi Iwai 		return -EINVAL;
3549763f356cSTakashi Iwai 
3550763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3551763f356cSTakashi Iwai 	ucontrol->value.integer.value[0] =
35520dca1793SAdrian Knoth 	  (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
3553763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3554763f356cSTakashi Iwai 
3555763f356cSTakashi Iwai 	return 0;
3556763f356cSTakashi Iwai }
3557763f356cSTakashi Iwai 
355898274f07STakashi Iwai static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
355998274f07STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
3560763f356cSTakashi Iwai {
356198274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3562763f356cSTakashi Iwai 	int change;
3563763f356cSTakashi Iwai 	int channel;
3564763f356cSTakashi Iwai 	int gain;
3565763f356cSTakashi Iwai 
3566763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3567763f356cSTakashi Iwai 		return -EBUSY;
3568763f356cSTakashi Iwai 
3569763f356cSTakashi Iwai 	channel = ucontrol->id.index - 1;
3570763f356cSTakashi Iwai 
3571da3cec35STakashi Iwai 	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
3572da3cec35STakashi Iwai 		return -EINVAL;
3573763f356cSTakashi Iwai 
35740dca1793SAdrian Knoth 	gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
3575763f356cSTakashi Iwai 
3576763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3577763f356cSTakashi Iwai 	change =
35780dca1793SAdrian Knoth 	    gain != hdspm_read_pb_gain(hdspm, channel,
35790dca1793SAdrian Knoth 				       channel);
3580763f356cSTakashi Iwai 	if (change)
35810dca1793SAdrian Knoth 		hdspm_write_pb_gain(hdspm, channel, channel,
3582763f356cSTakashi Iwai 				    gain);
3583763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3584763f356cSTakashi Iwai 	return change;
3585763f356cSTakashi Iwai }
3586763f356cSTakashi Iwai 
35870dca1793SAdrian Knoth #define HDSPM_SYNC_CHECK(xname, xindex) \
358867ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3589763f356cSTakashi Iwai 	.name = xname, \
35900dca1793SAdrian Knoth 	.private_value = xindex, \
3591763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3592763f356cSTakashi Iwai 	.info = snd_hdspm_info_sync_check, \
35930dca1793SAdrian Knoth 	.get = snd_hdspm_get_sync_check \
3594763f356cSTakashi Iwai }
3595763f356cSTakashi Iwai 
35960dca1793SAdrian Knoth 
359798274f07STakashi Iwai static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
359898274f07STakashi Iwai 				     struct snd_ctl_elem_info *uinfo)
3599763f356cSTakashi Iwai {
36000dca1793SAdrian Knoth 	static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" };
3601763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
3602763f356cSTakashi Iwai 	uinfo->count = 1;
36030dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 4;
3604763f356cSTakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
3605763f356cSTakashi Iwai 		uinfo->value.enumerated.item =
3606763f356cSTakashi Iwai 			uinfo->value.enumerated.items - 1;
3607763f356cSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
3608763f356cSTakashi Iwai 			texts[uinfo->value.enumerated.item]);
3609763f356cSTakashi Iwai 	return 0;
3610763f356cSTakashi Iwai }
3611763f356cSTakashi Iwai 
361298274f07STakashi Iwai static int hdspm_wc_sync_check(struct hdspm *hdspm)
3613763f356cSTakashi Iwai {
36140dca1793SAdrian Knoth 	int status, status2;
36150dca1793SAdrian Knoth 
36160dca1793SAdrian Knoth 	switch (hdspm->io_type) {
36170dca1793SAdrian Knoth 	case AES32:
36180dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
36190dca1793SAdrian Knoth 		if (status & HDSPM_wcSync)
36203cee5a60SRemy Bruno 			return 2;
36210dca1793SAdrian Knoth 		else if (status & HDSPM_wcLock)
36220dca1793SAdrian Knoth 			return 1;
36233cee5a60SRemy Bruno 		return 0;
36240dca1793SAdrian Knoth 		break;
36250dca1793SAdrian Knoth 
36260dca1793SAdrian Knoth 	case MADI:
36270dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
3628763f356cSTakashi Iwai 		if (status2 & HDSPM_wcLock) {
3629763f356cSTakashi Iwai 			if (status2 & HDSPM_wcSync)
3630763f356cSTakashi Iwai 				return 2;
3631763f356cSTakashi Iwai 			else
3632763f356cSTakashi Iwai 				return 1;
3633763f356cSTakashi Iwai 		}
3634763f356cSTakashi Iwai 		return 0;
36350dca1793SAdrian Knoth 		break;
3636763f356cSTakashi Iwai 
36370dca1793SAdrian Knoth 	case RayDAT:
36380dca1793SAdrian Knoth 	case AIO:
36390dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
3640763f356cSTakashi Iwai 
36410dca1793SAdrian Knoth 		if (status & 0x2000000)
36420dca1793SAdrian Knoth 			return 2;
36430dca1793SAdrian Knoth 		else if (status & 0x1000000)
36440dca1793SAdrian Knoth 			return 1;
3645763f356cSTakashi Iwai 		return 0;
36460dca1793SAdrian Knoth 
36470dca1793SAdrian Knoth 		break;
36480dca1793SAdrian Knoth 
36490dca1793SAdrian Knoth 	case MADIface:
36500dca1793SAdrian Knoth 		break;
3651763f356cSTakashi Iwai 	}
3652763f356cSTakashi Iwai 
3653763f356cSTakashi Iwai 
36540dca1793SAdrian Knoth 	return 3;
3655763f356cSTakashi Iwai }
3656763f356cSTakashi Iwai 
36570dca1793SAdrian Knoth 
36580dca1793SAdrian Knoth static int hdspm_madi_sync_check(struct hdspm *hdspm)
3659763f356cSTakashi Iwai {
3660763f356cSTakashi Iwai 	int status = hdspm_read(hdspm, HDSPM_statusRegister);
3661763f356cSTakashi Iwai 	if (status & HDSPM_madiLock) {
3662763f356cSTakashi Iwai 		if (status & HDSPM_madiSync)
3663763f356cSTakashi Iwai 			return 2;
3664763f356cSTakashi Iwai 		else
3665763f356cSTakashi Iwai 			return 1;
3666763f356cSTakashi Iwai 	}
3667763f356cSTakashi Iwai 	return 0;
3668763f356cSTakashi Iwai }
3669763f356cSTakashi Iwai 
3670763f356cSTakashi Iwai 
36710dca1793SAdrian Knoth static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx)
36720dca1793SAdrian Knoth {
36730dca1793SAdrian Knoth 	int status, lock, sync;
36740dca1793SAdrian Knoth 
36750dca1793SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
36760dca1793SAdrian Knoth 
36770dca1793SAdrian Knoth 	lock = (status & (0x1<<idx)) ? 1 : 0;
36780dca1793SAdrian Knoth 	sync = (status & (0x100<<idx)) ? 1 : 0;
36790dca1793SAdrian Knoth 
36800dca1793SAdrian Knoth 	if (lock && sync)
36810dca1793SAdrian Knoth 		return 2;
36820dca1793SAdrian Knoth 	else if (lock)
36830dca1793SAdrian Knoth 		return 1;
3684763f356cSTakashi Iwai 	return 0;
3685763f356cSTakashi Iwai }
3686763f356cSTakashi Iwai 
3687763f356cSTakashi Iwai 
36880dca1793SAdrian Knoth static int hdspm_sync_in_sync_check(struct hdspm *hdspm)
36890dca1793SAdrian Knoth {
36900dca1793SAdrian Knoth 	int status, lock = 0, sync = 0;
36910dca1793SAdrian Knoth 
36920dca1793SAdrian Knoth 	switch (hdspm->io_type) {
36930dca1793SAdrian Knoth 	case RayDAT:
36940dca1793SAdrian Knoth 	case AIO:
36950dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_RD_STATUS_3);
36960dca1793SAdrian Knoth 		lock = (status & 0x400) ? 1 : 0;
36970dca1793SAdrian Knoth 		sync = (status & 0x800) ? 1 : 0;
36980dca1793SAdrian Knoth 		break;
36990dca1793SAdrian Knoth 
37000dca1793SAdrian Knoth 	case MADI:
37010dca1793SAdrian Knoth 	case AES32:
37020dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister2);
37030dca1793SAdrian Knoth 		lock = (status & 0x400000) ? 1 : 0;
37040dca1793SAdrian Knoth 		sync = (status & 0x800000) ? 1 : 0;
37050dca1793SAdrian Knoth 		break;
37060dca1793SAdrian Knoth 
37070dca1793SAdrian Knoth 	case MADIface:
37080dca1793SAdrian Knoth 		break;
37090dca1793SAdrian Knoth 	}
37100dca1793SAdrian Knoth 
37110dca1793SAdrian Knoth 	if (lock && sync)
37120dca1793SAdrian Knoth 		return 2;
37130dca1793SAdrian Knoth 	else if (lock)
37140dca1793SAdrian Knoth 		return 1;
37150dca1793SAdrian Knoth 
37160dca1793SAdrian Knoth 	return 0;
37173cee5a60SRemy Bruno }
37183cee5a60SRemy Bruno 
37193cee5a60SRemy Bruno static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx)
37203cee5a60SRemy Bruno {
37210dca1793SAdrian Knoth 	int status2, lock, sync;
37220dca1793SAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
37230dca1793SAdrian Knoth 
37240dca1793SAdrian Knoth 	lock = (status2 & (0x0080 >> idx)) ? 1 : 0;
37250dca1793SAdrian Knoth 	sync = (status2 & (0x8000 >> idx)) ? 1 : 0;
37260dca1793SAdrian Knoth 
37270dca1793SAdrian Knoth 	if (sync)
37283cee5a60SRemy Bruno 		return 2;
37290dca1793SAdrian Knoth 	else if (lock)
37300dca1793SAdrian Knoth 		return 1;
37313cee5a60SRemy Bruno 	return 0;
37323cee5a60SRemy Bruno }
37333cee5a60SRemy Bruno 
37340dca1793SAdrian Knoth 
37350dca1793SAdrian Knoth static int hdspm_tco_sync_check(struct hdspm *hdspm)
37360dca1793SAdrian Knoth {
37370dca1793SAdrian Knoth 	int status;
37380dca1793SAdrian Knoth 
37390dca1793SAdrian Knoth 	if (hdspm->tco) {
37400dca1793SAdrian Knoth 		switch (hdspm->io_type) {
37410dca1793SAdrian Knoth 		case MADI:
37420dca1793SAdrian Knoth 		case AES32:
37430dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_statusRegister);
37440dca1793SAdrian Knoth 			if (status & HDSPM_tcoLock) {
37450dca1793SAdrian Knoth 				if (status & HDSPM_tcoSync)
37460dca1793SAdrian Knoth 					return 2;
37470dca1793SAdrian Knoth 				else
37480dca1793SAdrian Knoth 					return 1;
37490dca1793SAdrian Knoth 			}
37500dca1793SAdrian Knoth 			return 0;
37510dca1793SAdrian Knoth 
37520dca1793SAdrian Knoth 			break;
37530dca1793SAdrian Knoth 
37540dca1793SAdrian Knoth 		case RayDAT:
37550dca1793SAdrian Knoth 		case AIO:
37560dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
37570dca1793SAdrian Knoth 
37580dca1793SAdrian Knoth 			if (status & 0x8000000)
37590dca1793SAdrian Knoth 				return 2; /* Sync */
37600dca1793SAdrian Knoth 			if (status & 0x4000000)
37610dca1793SAdrian Knoth 				return 1; /* Lock */
37620dca1793SAdrian Knoth 			return 0; /* No signal */
37630dca1793SAdrian Knoth 			break;
37640dca1793SAdrian Knoth 
37650dca1793SAdrian Knoth 		default:
37660dca1793SAdrian Knoth 			break;
37670dca1793SAdrian Knoth 		}
37680dca1793SAdrian Knoth 	}
37690dca1793SAdrian Knoth 
37700dca1793SAdrian Knoth 	return 3; /* N/A */
37710dca1793SAdrian Knoth }
37720dca1793SAdrian Knoth 
37730dca1793SAdrian Knoth 
37740dca1793SAdrian Knoth static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
37753cee5a60SRemy Bruno 				    struct snd_ctl_elem_value *ucontrol)
37763cee5a60SRemy Bruno {
37773cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
37780dca1793SAdrian Knoth 	int val = -1;
37793cee5a60SRemy Bruno 
37800dca1793SAdrian Knoth 	switch (hdspm->io_type) {
37810dca1793SAdrian Knoth 	case RayDAT:
37820dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
37830dca1793SAdrian Knoth 		case 0: /* WC */
37840dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
37850dca1793SAdrian Knoth 		case 7: /* TCO */
37860dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
37870dca1793SAdrian Knoth 		case 8: /* SYNC IN */
37880dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
37890dca1793SAdrian Knoth 		default:
37900dca1793SAdrian Knoth 			val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1);
37910dca1793SAdrian Knoth 		}
37923cee5a60SRemy Bruno 
37930dca1793SAdrian Knoth 	case AIO:
37940dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
37950dca1793SAdrian Knoth 		case 0: /* WC */
37960dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
37970dca1793SAdrian Knoth 		case 4: /* TCO */
37980dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
37990dca1793SAdrian Knoth 		case 5: /* SYNC IN */
38000dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
38010dca1793SAdrian Knoth 		default:
38020dca1793SAdrian Knoth 			val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1);
38030dca1793SAdrian Knoth 		}
38040dca1793SAdrian Knoth 
38050dca1793SAdrian Knoth 	case MADI:
38060dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
38070dca1793SAdrian Knoth 		case 0: /* WC */
38080dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
38090dca1793SAdrian Knoth 		case 1: /* MADI */
38100dca1793SAdrian Knoth 			val = hdspm_madi_sync_check(hdspm); break;
38110dca1793SAdrian Knoth 		case 2: /* TCO */
38120dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
38130dca1793SAdrian Knoth 		case 3: /* SYNC_IN */
38140dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
38150dca1793SAdrian Knoth 		}
38160dca1793SAdrian Knoth 
38170dca1793SAdrian Knoth 	case MADIface:
38180dca1793SAdrian Knoth 		val = hdspm_madi_sync_check(hdspm); /* MADI */
38190dca1793SAdrian Knoth 		break;
38200dca1793SAdrian Knoth 
38210dca1793SAdrian Knoth 	case AES32:
38220dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
38230dca1793SAdrian Knoth 		case 0: /* WC */
38240dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
38250dca1793SAdrian Knoth 		case 9: /* TCO */
38260dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
38270dca1793SAdrian Knoth 		case 10 /* SYNC IN */:
38280dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
38297c4a95b5SAdrian Knoth 		default: /* AES1 to AES8 */
38300dca1793SAdrian Knoth 			 val = hdspm_aes_sync_check(hdspm,
38317c4a95b5SAdrian Knoth 					 kcontrol->private_value-1);
38320dca1793SAdrian Knoth 		}
38330dca1793SAdrian Knoth 
38340dca1793SAdrian Knoth 	}
38350dca1793SAdrian Knoth 
38360dca1793SAdrian Knoth 	if (-1 == val)
38370dca1793SAdrian Knoth 		val = 3;
38380dca1793SAdrian Knoth 
38390dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = val;
38403cee5a60SRemy Bruno 	return 0;
38413cee5a60SRemy Bruno }
3842763f356cSTakashi Iwai 
3843763f356cSTakashi Iwai 
38440dca1793SAdrian Knoth 
38450dca1793SAdrian Knoth /**
38460dca1793SAdrian Knoth  * TCO controls
38470dca1793SAdrian Knoth  **/
38480dca1793SAdrian Knoth static void hdspm_tco_write(struct hdspm *hdspm)
38490dca1793SAdrian Knoth {
38500dca1793SAdrian Knoth 	unsigned int tc[4] = { 0, 0, 0, 0};
38510dca1793SAdrian Knoth 
38520dca1793SAdrian Knoth 	switch (hdspm->tco->input) {
38530dca1793SAdrian Knoth 	case 0:
38540dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_input_MSB;
38550dca1793SAdrian Knoth 		break;
38560dca1793SAdrian Knoth 	case 1:
38570dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_input_LSB;
38580dca1793SAdrian Knoth 		break;
38590dca1793SAdrian Knoth 	default:
38600dca1793SAdrian Knoth 		break;
38610dca1793SAdrian Knoth 	}
38620dca1793SAdrian Knoth 
38630dca1793SAdrian Knoth 	switch (hdspm->tco->framerate) {
38640dca1793SAdrian Knoth 	case 1:
38650dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB;
38660dca1793SAdrian Knoth 		break;
38670dca1793SAdrian Knoth 	case 2:
38680dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_MSB;
38690dca1793SAdrian Knoth 		break;
38700dca1793SAdrian Knoth 	case 3:
38710dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_MSB +
38720dca1793SAdrian Knoth 			HDSPM_TCO1_set_drop_frame_flag;
38730dca1793SAdrian Knoth 		break;
38740dca1793SAdrian Knoth 	case 4:
38750dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
38760dca1793SAdrian Knoth 			HDSPM_TCO1_LTC_Format_MSB;
38770dca1793SAdrian Knoth 		break;
38780dca1793SAdrian Knoth 	case 5:
38790dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
38800dca1793SAdrian Knoth 			HDSPM_TCO1_LTC_Format_MSB +
38810dca1793SAdrian Knoth 			HDSPM_TCO1_set_drop_frame_flag;
38820dca1793SAdrian Knoth 		break;
38830dca1793SAdrian Knoth 	default:
38840dca1793SAdrian Knoth 		break;
38850dca1793SAdrian Knoth 	}
38860dca1793SAdrian Knoth 
38870dca1793SAdrian Knoth 	switch (hdspm->tco->wordclock) {
38880dca1793SAdrian Knoth 	case 1:
38890dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB;
38900dca1793SAdrian Knoth 		break;
38910dca1793SAdrian Knoth 	case 2:
38920dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB;
38930dca1793SAdrian Knoth 		break;
38940dca1793SAdrian Knoth 	default:
38950dca1793SAdrian Knoth 		break;
38960dca1793SAdrian Knoth 	}
38970dca1793SAdrian Knoth 
38980dca1793SAdrian Knoth 	switch (hdspm->tco->samplerate) {
38990dca1793SAdrian Knoth 	case 1:
39000dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_freq;
39010dca1793SAdrian Knoth 		break;
39020dca1793SAdrian Knoth 	case 2:
39030dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_freq_from_app;
39040dca1793SAdrian Knoth 		break;
39050dca1793SAdrian Knoth 	default:
39060dca1793SAdrian Knoth 		break;
39070dca1793SAdrian Knoth 	}
39080dca1793SAdrian Knoth 
39090dca1793SAdrian Knoth 	switch (hdspm->tco->pull) {
39100dca1793SAdrian Knoth 	case 1:
39110dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_up;
39120dca1793SAdrian Knoth 		break;
39130dca1793SAdrian Knoth 	case 2:
39140dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_down;
39150dca1793SAdrian Knoth 		break;
39160dca1793SAdrian Knoth 	case 3:
39170dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4;
39180dca1793SAdrian Knoth 		break;
39190dca1793SAdrian Knoth 	case 4:
39200dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4;
39210dca1793SAdrian Knoth 		break;
39220dca1793SAdrian Knoth 	default:
39230dca1793SAdrian Knoth 		break;
39240dca1793SAdrian Knoth 	}
39250dca1793SAdrian Knoth 
39260dca1793SAdrian Knoth 	if (1 == hdspm->tco->term) {
39270dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_term_75R;
39280dca1793SAdrian Knoth 	}
39290dca1793SAdrian Knoth 
39300dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]);
39310dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]);
39320dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]);
39330dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]);
39340dca1793SAdrian Knoth }
39350dca1793SAdrian Knoth 
39360dca1793SAdrian Knoth 
39370dca1793SAdrian Knoth #define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \
39380dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
39390dca1793SAdrian Knoth 	.name = xname, \
39400dca1793SAdrian Knoth 	.index = xindex, \
39410dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
39420dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
39430dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_sample_rate, \
39440dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_sample_rate, \
39450dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_sample_rate \
39460dca1793SAdrian Knoth }
39470dca1793SAdrian Knoth 
39480dca1793SAdrian Knoth static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
39490dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
39500dca1793SAdrian Knoth {
39510dca1793SAdrian Knoth 	static char *texts[] = { "44.1 kHz", "48 kHz" };
39520dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
39530dca1793SAdrian Knoth 	uinfo->count = 1;
39540dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 2;
39550dca1793SAdrian Knoth 
39560dca1793SAdrian Knoth 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
39570dca1793SAdrian Knoth 		uinfo->value.enumerated.item =
39580dca1793SAdrian Knoth 			uinfo->value.enumerated.items - 1;
39590dca1793SAdrian Knoth 
39600dca1793SAdrian Knoth 	strcpy(uinfo->value.enumerated.name,
39610dca1793SAdrian Knoth 			texts[uinfo->value.enumerated.item]);
39620dca1793SAdrian Knoth 
39630dca1793SAdrian Knoth 	return 0;
39640dca1793SAdrian Knoth }
39650dca1793SAdrian Knoth 
39660dca1793SAdrian Knoth static int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol,
39670dca1793SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
39680dca1793SAdrian Knoth {
39690dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
39700dca1793SAdrian Knoth 
39710dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate;
39720dca1793SAdrian Knoth 
39730dca1793SAdrian Knoth 	return 0;
39740dca1793SAdrian Knoth }
39750dca1793SAdrian Knoth 
39760dca1793SAdrian Knoth static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
39770dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
39780dca1793SAdrian Knoth {
39790dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
39800dca1793SAdrian Knoth 
39810dca1793SAdrian Knoth 	if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) {
39820dca1793SAdrian Knoth 		hdspm->tco->samplerate = ucontrol->value.enumerated.item[0];
39830dca1793SAdrian Knoth 
39840dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
39850dca1793SAdrian Knoth 
39860dca1793SAdrian Knoth 		return 1;
39870dca1793SAdrian Knoth 	}
39880dca1793SAdrian Knoth 
39890dca1793SAdrian Knoth 	return 0;
39900dca1793SAdrian Knoth }
39910dca1793SAdrian Knoth 
39920dca1793SAdrian Knoth 
39930dca1793SAdrian Knoth #define HDSPM_TCO_PULL(xname, xindex) \
39940dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
39950dca1793SAdrian Knoth 	.name = xname, \
39960dca1793SAdrian Knoth 	.index = xindex, \
39970dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
39980dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
39990dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_pull, \
40000dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_pull, \
40010dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_pull \
40020dca1793SAdrian Knoth }
40030dca1793SAdrian Knoth 
40040dca1793SAdrian Knoth static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
40050dca1793SAdrian Knoth 				   struct snd_ctl_elem_info *uinfo)
40060dca1793SAdrian Knoth {
40070dca1793SAdrian Knoth 	static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" };
40080dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
40090dca1793SAdrian Knoth 	uinfo->count = 1;
40100dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 5;
40110dca1793SAdrian Knoth 
40120dca1793SAdrian Knoth 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
40130dca1793SAdrian Knoth 		uinfo->value.enumerated.item =
40140dca1793SAdrian Knoth 			uinfo->value.enumerated.items - 1;
40150dca1793SAdrian Knoth 
40160dca1793SAdrian Knoth 	strcpy(uinfo->value.enumerated.name,
40170dca1793SAdrian Knoth 			texts[uinfo->value.enumerated.item]);
40180dca1793SAdrian Knoth 
40190dca1793SAdrian Knoth 	return 0;
40200dca1793SAdrian Knoth }
40210dca1793SAdrian Knoth 
40220dca1793SAdrian Knoth static int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol,
40230dca1793SAdrian Knoth 				  struct snd_ctl_elem_value *ucontrol)
40240dca1793SAdrian Knoth {
40250dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
40260dca1793SAdrian Knoth 
40270dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->pull;
40280dca1793SAdrian Knoth 
40290dca1793SAdrian Knoth 	return 0;
40300dca1793SAdrian Knoth }
40310dca1793SAdrian Knoth 
40320dca1793SAdrian Knoth static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
40330dca1793SAdrian Knoth 				  struct snd_ctl_elem_value *ucontrol)
40340dca1793SAdrian Knoth {
40350dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
40360dca1793SAdrian Knoth 
40370dca1793SAdrian Knoth 	if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) {
40380dca1793SAdrian Knoth 		hdspm->tco->pull = ucontrol->value.enumerated.item[0];
40390dca1793SAdrian Knoth 
40400dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
40410dca1793SAdrian Knoth 
40420dca1793SAdrian Knoth 		return 1;
40430dca1793SAdrian Knoth 	}
40440dca1793SAdrian Knoth 
40450dca1793SAdrian Knoth 	return 0;
40460dca1793SAdrian Knoth }
40470dca1793SAdrian Knoth 
40480dca1793SAdrian Knoth #define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \
40490dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
40500dca1793SAdrian Knoth 	.name = xname, \
40510dca1793SAdrian Knoth 	.index = xindex, \
40520dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
40530dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
40540dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_wck_conversion, \
40550dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_wck_conversion, \
40560dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_wck_conversion \
40570dca1793SAdrian Knoth }
40580dca1793SAdrian Knoth 
40590dca1793SAdrian Knoth static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
40600dca1793SAdrian Knoth 					     struct snd_ctl_elem_info *uinfo)
40610dca1793SAdrian Knoth {
40620dca1793SAdrian Knoth 	static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
40630dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
40640dca1793SAdrian Knoth 	uinfo->count = 1;
40650dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 3;
40660dca1793SAdrian Knoth 
40670dca1793SAdrian Knoth 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
40680dca1793SAdrian Knoth 		uinfo->value.enumerated.item =
40690dca1793SAdrian Knoth 			uinfo->value.enumerated.items - 1;
40700dca1793SAdrian Knoth 
40710dca1793SAdrian Knoth 	strcpy(uinfo->value.enumerated.name,
40720dca1793SAdrian Knoth 			texts[uinfo->value.enumerated.item]);
40730dca1793SAdrian Knoth 
40740dca1793SAdrian Knoth 	return 0;
40750dca1793SAdrian Knoth }
40760dca1793SAdrian Knoth 
40770dca1793SAdrian Knoth static int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol,
40780dca1793SAdrian Knoth 					    struct snd_ctl_elem_value *ucontrol)
40790dca1793SAdrian Knoth {
40800dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
40810dca1793SAdrian Knoth 
40820dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock;
40830dca1793SAdrian Knoth 
40840dca1793SAdrian Knoth 	return 0;
40850dca1793SAdrian Knoth }
40860dca1793SAdrian Knoth 
40870dca1793SAdrian Knoth static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
40880dca1793SAdrian Knoth 					    struct snd_ctl_elem_value *ucontrol)
40890dca1793SAdrian Knoth {
40900dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
40910dca1793SAdrian Knoth 
40920dca1793SAdrian Knoth 	if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) {
40930dca1793SAdrian Knoth 		hdspm->tco->wordclock = ucontrol->value.enumerated.item[0];
40940dca1793SAdrian Knoth 
40950dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
40960dca1793SAdrian Knoth 
40970dca1793SAdrian Knoth 		return 1;
40980dca1793SAdrian Knoth 	}
40990dca1793SAdrian Knoth 
41000dca1793SAdrian Knoth 	return 0;
41010dca1793SAdrian Knoth }
41020dca1793SAdrian Knoth 
41030dca1793SAdrian Knoth 
41040dca1793SAdrian Knoth #define HDSPM_TCO_FRAME_RATE(xname, xindex) \
41050dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
41060dca1793SAdrian Knoth 	.name = xname, \
41070dca1793SAdrian Knoth 	.index = xindex, \
41080dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
41090dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
41100dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_frame_rate, \
41110dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_frame_rate, \
41120dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_frame_rate \
41130dca1793SAdrian Knoth }
41140dca1793SAdrian Knoth 
41150dca1793SAdrian Knoth static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
41160dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
41170dca1793SAdrian Knoth {
41180dca1793SAdrian Knoth 	static char *texts[] = { "24 fps", "25 fps", "29.97fps",
41190dca1793SAdrian Knoth 		"29.97 dfps", "30 fps", "30 dfps" };
41200dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
41210dca1793SAdrian Knoth 	uinfo->count = 1;
41220dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 6;
41230dca1793SAdrian Knoth 
41240dca1793SAdrian Knoth 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
41250dca1793SAdrian Knoth 		uinfo->value.enumerated.item =
41260dca1793SAdrian Knoth 			uinfo->value.enumerated.items - 1;
41270dca1793SAdrian Knoth 
41280dca1793SAdrian Knoth 	strcpy(uinfo->value.enumerated.name,
41290dca1793SAdrian Knoth 			texts[uinfo->value.enumerated.item]);
41300dca1793SAdrian Knoth 
41310dca1793SAdrian Knoth 	return 0;
41320dca1793SAdrian Knoth }
41330dca1793SAdrian Knoth 
41340dca1793SAdrian Knoth static int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol,
41350dca1793SAdrian Knoth 					struct snd_ctl_elem_value *ucontrol)
41360dca1793SAdrian Knoth {
41370dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
41380dca1793SAdrian Knoth 
41390dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->framerate;
41400dca1793SAdrian Knoth 
41410dca1793SAdrian Knoth 	return 0;
41420dca1793SAdrian Knoth }
41430dca1793SAdrian Knoth 
41440dca1793SAdrian Knoth static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
41450dca1793SAdrian Knoth 					struct snd_ctl_elem_value *ucontrol)
41460dca1793SAdrian Knoth {
41470dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
41480dca1793SAdrian Knoth 
41490dca1793SAdrian Knoth 	if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) {
41500dca1793SAdrian Knoth 		hdspm->tco->framerate = ucontrol->value.enumerated.item[0];
41510dca1793SAdrian Knoth 
41520dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
41530dca1793SAdrian Knoth 
41540dca1793SAdrian Knoth 		return 1;
41550dca1793SAdrian Knoth 	}
41560dca1793SAdrian Knoth 
41570dca1793SAdrian Knoth 	return 0;
41580dca1793SAdrian Knoth }
41590dca1793SAdrian Knoth 
41600dca1793SAdrian Knoth 
41610dca1793SAdrian Knoth #define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \
41620dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
41630dca1793SAdrian Knoth 	.name = xname, \
41640dca1793SAdrian Knoth 	.index = xindex, \
41650dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
41660dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
41670dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_sync_source, \
41680dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_sync_source, \
41690dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_sync_source \
41700dca1793SAdrian Knoth }
41710dca1793SAdrian Knoth 
41720dca1793SAdrian Knoth static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
41730dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
41740dca1793SAdrian Knoth {
41750dca1793SAdrian Knoth 	static char *texts[] = { "LTC", "Video", "WCK" };
41760dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
41770dca1793SAdrian Knoth 	uinfo->count = 1;
41780dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 3;
41790dca1793SAdrian Knoth 
41800dca1793SAdrian Knoth 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
41810dca1793SAdrian Knoth 		uinfo->value.enumerated.item =
41820dca1793SAdrian Knoth 			uinfo->value.enumerated.items - 1;
41830dca1793SAdrian Knoth 
41840dca1793SAdrian Knoth 	strcpy(uinfo->value.enumerated.name,
41850dca1793SAdrian Knoth 			texts[uinfo->value.enumerated.item]);
41860dca1793SAdrian Knoth 
41870dca1793SAdrian Knoth 	return 0;
41880dca1793SAdrian Knoth }
41890dca1793SAdrian Knoth 
41900dca1793SAdrian Knoth static int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol,
41910dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
41920dca1793SAdrian Knoth {
41930dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
41940dca1793SAdrian Knoth 
41950dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->input;
41960dca1793SAdrian Knoth 
41970dca1793SAdrian Knoth 	return 0;
41980dca1793SAdrian Knoth }
41990dca1793SAdrian Knoth 
42000dca1793SAdrian Knoth static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol,
42010dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
42020dca1793SAdrian Knoth {
42030dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42040dca1793SAdrian Knoth 
42050dca1793SAdrian Knoth 	if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) {
42060dca1793SAdrian Knoth 		hdspm->tco->input = ucontrol->value.enumerated.item[0];
42070dca1793SAdrian Knoth 
42080dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
42090dca1793SAdrian Knoth 
42100dca1793SAdrian Knoth 		return 1;
42110dca1793SAdrian Knoth 	}
42120dca1793SAdrian Knoth 
42130dca1793SAdrian Knoth 	return 0;
42140dca1793SAdrian Knoth }
42150dca1793SAdrian Knoth 
42160dca1793SAdrian Knoth 
42170dca1793SAdrian Knoth #define HDSPM_TCO_WORD_TERM(xname, xindex) \
42180dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
42190dca1793SAdrian Knoth 	.name = xname, \
42200dca1793SAdrian Knoth 	.index = xindex, \
42210dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
42220dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
42230dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_word_term, \
42240dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_word_term, \
42250dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_word_term \
42260dca1793SAdrian Knoth }
42270dca1793SAdrian Knoth 
42280dca1793SAdrian Knoth static int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol,
42290dca1793SAdrian Knoth 					struct snd_ctl_elem_info *uinfo)
42300dca1793SAdrian Knoth {
42310dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
42320dca1793SAdrian Knoth 	uinfo->count = 1;
42330dca1793SAdrian Knoth 	uinfo->value.integer.min = 0;
42340dca1793SAdrian Knoth 	uinfo->value.integer.max = 1;
42350dca1793SAdrian Knoth 
42360dca1793SAdrian Knoth 	return 0;
42370dca1793SAdrian Knoth }
42380dca1793SAdrian Knoth 
42390dca1793SAdrian Knoth 
42400dca1793SAdrian Knoth static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
42410dca1793SAdrian Knoth 				       struct snd_ctl_elem_value *ucontrol)
42420dca1793SAdrian Knoth {
42430dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42440dca1793SAdrian Knoth 
42450dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->term;
42460dca1793SAdrian Knoth 
42470dca1793SAdrian Knoth 	return 0;
42480dca1793SAdrian Knoth }
42490dca1793SAdrian Knoth 
42500dca1793SAdrian Knoth 
42510dca1793SAdrian Knoth static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
42520dca1793SAdrian Knoth 				       struct snd_ctl_elem_value *ucontrol)
42530dca1793SAdrian Knoth {
42540dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42550dca1793SAdrian Knoth 
42560dca1793SAdrian Knoth 	if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) {
42570dca1793SAdrian Knoth 		hdspm->tco->term = ucontrol->value.enumerated.item[0];
42580dca1793SAdrian Knoth 
42590dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
42600dca1793SAdrian Knoth 
42610dca1793SAdrian Knoth 		return 1;
42620dca1793SAdrian Knoth 	}
42630dca1793SAdrian Knoth 
42640dca1793SAdrian Knoth 	return 0;
42650dca1793SAdrian Knoth }
42660dca1793SAdrian Knoth 
42670dca1793SAdrian Knoth 
42680dca1793SAdrian Knoth 
42690dca1793SAdrian Knoth 
42703cee5a60SRemy Bruno static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
4271763f356cSTakashi Iwai 	HDSPM_MIXER("Mixer", 0),
42720dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
4273763f356cSTakashi Iwai 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
4274763f356cSTakashi Iwai 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
4275763f356cSTakashi Iwai 	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
4276763f356cSTakashi Iwai 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
42770dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
42780dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("MADI SyncCheck", 1),
42790dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCHeck", 2),
42800dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
4281763f356cSTakashi Iwai 	HDSPM_LINE_OUT("Line Out", 0),
4282763f356cSTakashi Iwai 	HDSPM_TX_64("TX 64 channels mode", 0),
4283763f356cSTakashi Iwai 	HDSPM_C_TMS("Clear Track Marker", 0),
4284763f356cSTakashi Iwai 	HDSPM_SAFE_MODE("Safe Mode", 0),
42850dca1793SAdrian Knoth 	HDSPM_INPUT_SELECT("Input Select", 0)
42860dca1793SAdrian Knoth };
42870dca1793SAdrian Knoth 
42880dca1793SAdrian Knoth 
42890dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
42900dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
42910dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
42920dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
42930dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
42940dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
42950dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
42960dca1793SAdrian Knoth 	HDSPM_TX_64("TX 64 channels mode", 0),
42970dca1793SAdrian Knoth 	HDSPM_C_TMS("Clear Track Marker", 0),
4298f6ea805fSAdrian Knoth 	HDSPM_SAFE_MODE("Safe Mode", 0)
4299763f356cSTakashi Iwai };
4300763f356cSTakashi Iwai 
43010dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
43023cee5a60SRemy Bruno 	HDSPM_MIXER("Mixer", 0),
43030dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
43043cee5a60SRemy Bruno 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
43053cee5a60SRemy Bruno 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
43063cee5a60SRemy Bruno 	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
43073cee5a60SRemy Bruno 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
43083cee5a60SRemy Bruno 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
43090dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
43100dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
43110dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
43120dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT SyncCheck", 3),
43130dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 4),
43140dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5),
43150dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
43160dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
43170dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
43180dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
43190dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
43200dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5)
43210dca1793SAdrian Knoth 
43220dca1793SAdrian Knoth 		/*
43230dca1793SAdrian Knoth 		   HDSPM_INPUT_SELECT("Input Select", 0),
43240dca1793SAdrian Knoth 		   HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0),
43250dca1793SAdrian Knoth 		   HDSPM_PROFESSIONAL("SPDIF Out Professional", 0);
43260dca1793SAdrian Knoth 		   HDSPM_SPDIF_IN("SPDIF In", 0);
43270dca1793SAdrian Knoth 		   HDSPM_BREAKOUT_CABLE("Breakout Cable", 0);
43280dca1793SAdrian Knoth 		   HDSPM_INPUT_LEVEL("Input Level", 0);
43290dca1793SAdrian Knoth 		   HDSPM_OUTPUT_LEVEL("Output Level", 0);
43300dca1793SAdrian Knoth 		   HDSPM_PHONES("Phones", 0);
43310dca1793SAdrian Knoth 		   */
43320dca1793SAdrian Knoth };
43330dca1793SAdrian Knoth 
43340dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
43350dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
43360dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
43370dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0),
43380dca1793SAdrian Knoth 	HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0),
43390dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
43400dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
43410dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
43420dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
43430dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3),
43440dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4),
43450dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5),
43460dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6),
43470dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 7),
43480dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8),
43490dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
43500dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
43510dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
43520dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3),
43530dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4),
43540dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
43550dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
43560dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
43570dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8)
43580dca1793SAdrian Knoth };
43590dca1793SAdrian Knoth 
43600dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
43610dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
43620dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
43630dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
43640dca1793SAdrian Knoth 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
43650dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
43660dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
43670dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
43680dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC Sync Check", 0),
43690dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
43700dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
43710dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES3 Sync Check", 3),
43720dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES4 Sync Check", 4),
43730dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES5 Sync Check", 5),
43740dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES6 Sync Check", 6),
43750dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES7 Sync Check", 7),
43760dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES8 Sync Check", 8),
43770dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO Sync Check", 9),
43780dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10),
43790dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
43800dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1),
43810dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2),
43820dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3),
43830dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4),
43840dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5),
43850dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6),
43860dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7),
43870dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8),
43880dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9),
43890dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10),
43903cee5a60SRemy Bruno 	HDSPM_LINE_OUT("Line Out", 0),
43913cee5a60SRemy Bruno 	HDSPM_EMPHASIS("Emphasis", 0),
43923cee5a60SRemy Bruno 	HDSPM_DOLBY("Non Audio", 0),
43933cee5a60SRemy Bruno 	HDSPM_PROFESSIONAL("Professional", 0),
43943cee5a60SRemy Bruno 	HDSPM_C_TMS("Clear Track Marker", 0),
43953cee5a60SRemy Bruno 	HDSPM_DS_WIRE("Double Speed Wire Mode", 0),
43963cee5a60SRemy Bruno 	HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
43973cee5a60SRemy Bruno };
43983cee5a60SRemy Bruno 
43990dca1793SAdrian Knoth 
44000dca1793SAdrian Knoth 
44010dca1793SAdrian Knoth /* Control elements for the optional TCO module */
44020dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
44030dca1793SAdrian Knoth 	HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0),
44040dca1793SAdrian Knoth 	HDSPM_TCO_PULL("TCO Pull", 0),
44050dca1793SAdrian Knoth 	HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
44060dca1793SAdrian Knoth 	HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
44070dca1793SAdrian Knoth 	HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
44080dca1793SAdrian Knoth 	HDSPM_TCO_WORD_TERM("TCO Word Term", 0)
44090dca1793SAdrian Knoth };
44100dca1793SAdrian Knoth 
44110dca1793SAdrian Knoth 
441298274f07STakashi Iwai static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
4413763f356cSTakashi Iwai 
4414763f356cSTakashi Iwai 
441598274f07STakashi Iwai static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm)
4416763f356cSTakashi Iwai {
4417763f356cSTakashi Iwai 	int i;
4418763f356cSTakashi Iwai 
44190dca1793SAdrian Knoth 	for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) {
4420763f356cSTakashi Iwai 		if (hdspm->system_sample_rate > 48000) {
4421763f356cSTakashi Iwai 			hdspm->playback_mixer_ctls[i]->vd[0].access =
4422763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_INACTIVE |
4423763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_READ |
4424763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
4425763f356cSTakashi Iwai 		} else {
4426763f356cSTakashi Iwai 			hdspm->playback_mixer_ctls[i]->vd[0].access =
4427763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_READWRITE |
4428763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
4429763f356cSTakashi Iwai 		}
4430763f356cSTakashi Iwai 		snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
4431763f356cSTakashi Iwai 				SNDRV_CTL_EVENT_MASK_INFO,
4432763f356cSTakashi Iwai 				&hdspm->playback_mixer_ctls[i]->id);
4433763f356cSTakashi Iwai 	}
4434763f356cSTakashi Iwai 
4435763f356cSTakashi Iwai 	return 0;
4436763f356cSTakashi Iwai }
4437763f356cSTakashi Iwai 
4438763f356cSTakashi Iwai 
44390dca1793SAdrian Knoth static int snd_hdspm_create_controls(struct snd_card *card,
44400dca1793SAdrian Knoth 					struct hdspm *hdspm)
4441763f356cSTakashi Iwai {
4442763f356cSTakashi Iwai 	unsigned int idx, limit;
4443763f356cSTakashi Iwai 	int err;
444498274f07STakashi Iwai 	struct snd_kcontrol *kctl;
44450dca1793SAdrian Knoth 	struct snd_kcontrol_new *list = NULL;
4446763f356cSTakashi Iwai 
44470dca1793SAdrian Knoth 	switch (hdspm->io_type) {
44480dca1793SAdrian Knoth 	case MADI:
44490dca1793SAdrian Knoth 		list = snd_hdspm_controls_madi;
44500dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_madi);
44510dca1793SAdrian Knoth 		break;
44520dca1793SAdrian Knoth 	case MADIface:
44530dca1793SAdrian Knoth 		list = snd_hdspm_controls_madiface;
44540dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_madiface);
44550dca1793SAdrian Knoth 		break;
44560dca1793SAdrian Knoth 	case AIO:
44570dca1793SAdrian Knoth 		list = snd_hdspm_controls_aio;
44580dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_aio);
44590dca1793SAdrian Knoth 		break;
44600dca1793SAdrian Knoth 	case RayDAT:
44610dca1793SAdrian Knoth 		list = snd_hdspm_controls_raydat;
44620dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_raydat);
44630dca1793SAdrian Knoth 		break;
44640dca1793SAdrian Knoth 	case AES32:
44650dca1793SAdrian Knoth 		list = snd_hdspm_controls_aes32;
44660dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_aes32);
44670dca1793SAdrian Knoth 		break;
44680dca1793SAdrian Knoth 	}
4469763f356cSTakashi Iwai 
44700dca1793SAdrian Knoth 	if (NULL != list) {
44710dca1793SAdrian Knoth 		for (idx = 0; idx < limit; idx++) {
44723cee5a60SRemy Bruno 			err = snd_ctl_add(card,
44730dca1793SAdrian Knoth 					snd_ctl_new1(&list[idx], hdspm));
44743cee5a60SRemy Bruno 			if (err < 0)
4475763f356cSTakashi Iwai 				return err;
4476763f356cSTakashi Iwai 		}
4477763f356cSTakashi Iwai 	}
4478763f356cSTakashi Iwai 
4479763f356cSTakashi Iwai 
44800dca1793SAdrian Knoth 	/* create simple 1:1 playback mixer controls */
4481763f356cSTakashi Iwai 	snd_hdspm_playback_mixer.name = "Chn";
44820dca1793SAdrian Knoth 	if (hdspm->system_sample_rate >= 128000) {
44830dca1793SAdrian Knoth 		limit = hdspm->qs_out_channels;
44840dca1793SAdrian Knoth 	} else if (hdspm->system_sample_rate >= 64000) {
44850dca1793SAdrian Knoth 		limit = hdspm->ds_out_channels;
44860dca1793SAdrian Knoth 	} else {
44870dca1793SAdrian Knoth 		limit = hdspm->ss_out_channels;
44880dca1793SAdrian Knoth 	}
4489763f356cSTakashi Iwai 	for (idx = 0; idx < limit; ++idx) {
4490763f356cSTakashi Iwai 		snd_hdspm_playback_mixer.index = idx + 1;
4491ef5fa1a4STakashi Iwai 		kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm);
4492ef5fa1a4STakashi Iwai 		err = snd_ctl_add(card, kctl);
4493ef5fa1a4STakashi Iwai 		if (err < 0)
4494763f356cSTakashi Iwai 			return err;
4495763f356cSTakashi Iwai 		hdspm->playback_mixer_ctls[idx] = kctl;
4496763f356cSTakashi Iwai 	}
4497763f356cSTakashi Iwai 
44980dca1793SAdrian Knoth 
44990dca1793SAdrian Knoth 	if (hdspm->tco) {
45000dca1793SAdrian Knoth 		/* add tco control elements */
45010dca1793SAdrian Knoth 		list = snd_hdspm_controls_tco;
45020dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_tco);
45030dca1793SAdrian Knoth 		for (idx = 0; idx < limit; idx++) {
45040dca1793SAdrian Knoth 			err = snd_ctl_add(card,
45050dca1793SAdrian Knoth 					snd_ctl_new1(&list[idx], hdspm));
45060dca1793SAdrian Knoth 			if (err < 0)
45070dca1793SAdrian Knoth 				return err;
45080dca1793SAdrian Knoth 		}
45090dca1793SAdrian Knoth 	}
45100dca1793SAdrian Knoth 
4511763f356cSTakashi Iwai 	return 0;
4512763f356cSTakashi Iwai }
4513763f356cSTakashi Iwai 
4514763f356cSTakashi Iwai /*------------------------------------------------------------
4515763f356cSTakashi Iwai    /proc interface
4516763f356cSTakashi Iwai  ------------------------------------------------------------*/
4517763f356cSTakashi Iwai 
4518763f356cSTakashi Iwai static void
45193cee5a60SRemy Bruno snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
45203cee5a60SRemy Bruno 			 struct snd_info_buffer *buffer)
4521763f356cSTakashi Iwai {
4522ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
45230dca1793SAdrian Knoth 	unsigned int status, status2, control, freq;
45240dca1793SAdrian Knoth 
4525763f356cSTakashi Iwai 	char *pref_sync_ref;
4526763f356cSTakashi Iwai 	char *autosync_ref;
4527763f356cSTakashi Iwai 	char *system_clock_mode;
4528763f356cSTakashi Iwai 	char *insel;
4529763f356cSTakashi Iwai 	int x, x2;
4530763f356cSTakashi Iwai 
45310dca1793SAdrian Knoth 	/* TCO stuff */
45320dca1793SAdrian Knoth 	int a, ltc, frames, seconds, minutes, hours;
45330dca1793SAdrian Knoth 	unsigned int period;
45340dca1793SAdrian Knoth 	u64 freq_const = 0;
45350dca1793SAdrian Knoth 	u32 rate;
45360dca1793SAdrian Knoth 
4537763f356cSTakashi Iwai 	status = hdspm_read(hdspm, HDSPM_statusRegister);
4538763f356cSTakashi Iwai 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
45390dca1793SAdrian Knoth 	control = hdspm->control_register;
45400dca1793SAdrian Knoth 	freq = hdspm_read(hdspm, HDSPM_timecodeRegister);
4541763f356cSTakashi Iwai 
4542763f356cSTakashi Iwai 	snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
4543763f356cSTakashi Iwai 			hdspm->card_name, hdspm->card->number + 1,
4544763f356cSTakashi Iwai 			hdspm->firmware_rev,
4545763f356cSTakashi Iwai 			(status2 & HDSPM_version0) |
4546763f356cSTakashi Iwai 			(status2 & HDSPM_version1) | (status2 &
4547763f356cSTakashi Iwai 				HDSPM_version2));
4548763f356cSTakashi Iwai 
45490dca1793SAdrian Knoth 	snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
45500dca1793SAdrian Knoth 			(hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
45510dca1793SAdrian Knoth 			(hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF);
45520dca1793SAdrian Knoth 
4553763f356cSTakashi Iwai 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
4554763f356cSTakashi Iwai 			hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
4555763f356cSTakashi Iwai 
4556763f356cSTakashi Iwai 	snd_iprintf(buffer, "--- System ---\n");
4557763f356cSTakashi Iwai 
4558763f356cSTakashi Iwai 	snd_iprintf(buffer,
4559763f356cSTakashi Iwai 		"IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
4560763f356cSTakashi Iwai 		status & HDSPM_audioIRQPending,
4561763f356cSTakashi Iwai 		(status & HDSPM_midi0IRQPending) ? 1 : 0,
4562763f356cSTakashi Iwai 		(status & HDSPM_midi1IRQPending) ? 1 : 0,
4563763f356cSTakashi Iwai 		hdspm->irq_count);
4564763f356cSTakashi Iwai 	snd_iprintf(buffer,
4565ef5fa1a4STakashi Iwai 		"HW pointer: id = %d, rawptr = %d (%d->%d) "
4566ef5fa1a4STakashi Iwai 		"estimated= %ld (bytes)\n",
4567763f356cSTakashi Iwai 		((status & HDSPM_BufferID) ? 1 : 0),
4568763f356cSTakashi Iwai 		(status & HDSPM_BufferPositionMask),
4569ef5fa1a4STakashi Iwai 		(status & HDSPM_BufferPositionMask) %
4570ef5fa1a4STakashi Iwai 		(2 * (int)hdspm->period_bytes),
4571ef5fa1a4STakashi Iwai 		((status & HDSPM_BufferPositionMask) - 64) %
4572ef5fa1a4STakashi Iwai 		(2 * (int)hdspm->period_bytes),
4573763f356cSTakashi Iwai 		(long) hdspm_hw_pointer(hdspm) * 4);
4574763f356cSTakashi Iwai 
4575763f356cSTakashi Iwai 	snd_iprintf(buffer,
4576763f356cSTakashi Iwai 		"MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
4577763f356cSTakashi Iwai 		hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
4578763f356cSTakashi Iwai 		hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
4579763f356cSTakashi Iwai 		hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
4580763f356cSTakashi Iwai 		hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
4581763f356cSTakashi Iwai 	snd_iprintf(buffer,
45820dca1793SAdrian Knoth 		"MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
45830dca1793SAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
45840dca1793SAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
45850dca1793SAdrian Knoth 	snd_iprintf(buffer,
4586ef5fa1a4STakashi Iwai 		"Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
4587ef5fa1a4STakashi Iwai 		"status2=0x%x\n",
4588763f356cSTakashi Iwai 		hdspm->control_register, hdspm->control2_register,
4589763f356cSTakashi Iwai 		status, status2);
45900dca1793SAdrian Knoth 	if (status & HDSPM_tco_detect) {
45910dca1793SAdrian Knoth 		snd_iprintf(buffer, "TCO module detected.\n");
45920dca1793SAdrian Knoth 		a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
45930dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_LTC_Input_valid) {
45940dca1793SAdrian Knoth 			snd_iprintf(buffer, "  LTC valid, ");
45950dca1793SAdrian Knoth 			switch (a & (HDSPM_TCO1_LTC_Format_LSB |
45960dca1793SAdrian Knoth 						HDSPM_TCO1_LTC_Format_MSB)) {
45970dca1793SAdrian Knoth 			case 0:
45980dca1793SAdrian Knoth 				snd_iprintf(buffer, "24 fps, ");
45990dca1793SAdrian Knoth 				break;
46000dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_LSB:
46010dca1793SAdrian Knoth 				snd_iprintf(buffer, "25 fps, ");
46020dca1793SAdrian Knoth 				break;
46030dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_MSB:
46040dca1793SAdrian Knoth 				snd_iprintf(buffer, "29.97 fps, ");
46050dca1793SAdrian Knoth 				break;
46060dca1793SAdrian Knoth 			default:
46070dca1793SAdrian Knoth 				snd_iprintf(buffer, "30 fps, ");
46080dca1793SAdrian Knoth 				break;
46090dca1793SAdrian Knoth 			}
46100dca1793SAdrian Knoth 			if (a & HDSPM_TCO1_set_drop_frame_flag) {
46110dca1793SAdrian Knoth 				snd_iprintf(buffer, "drop frame\n");
46120dca1793SAdrian Knoth 			} else {
46130dca1793SAdrian Knoth 				snd_iprintf(buffer, "full frame\n");
46140dca1793SAdrian Knoth 			}
46150dca1793SAdrian Knoth 		} else {
46160dca1793SAdrian Knoth 			snd_iprintf(buffer, "  no LTC\n");
46170dca1793SAdrian Knoth 		}
46180dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_Video_Input_Format_NTSC) {
46190dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Video: NTSC\n");
46200dca1793SAdrian Knoth 		} else if (a & HDSPM_TCO1_Video_Input_Format_PAL) {
46210dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Video: PAL\n");
46220dca1793SAdrian Knoth 		} else {
46230dca1793SAdrian Knoth 			snd_iprintf(buffer, "  No video\n");
46240dca1793SAdrian Knoth 		}
46250dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_TCO_lock) {
46260dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Sync: lock\n");
46270dca1793SAdrian Knoth 		} else {
46280dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Sync: no lock\n");
46290dca1793SAdrian Knoth 		}
46300dca1793SAdrian Knoth 
46310dca1793SAdrian Knoth 		switch (hdspm->io_type) {
46320dca1793SAdrian Knoth 		case MADI:
46330dca1793SAdrian Knoth 		case AES32:
46340dca1793SAdrian Knoth 			freq_const = 110069313433624ULL;
46350dca1793SAdrian Knoth 			break;
46360dca1793SAdrian Knoth 		case RayDAT:
46370dca1793SAdrian Knoth 		case AIO:
46380dca1793SAdrian Knoth 			freq_const = 104857600000000ULL;
46390dca1793SAdrian Knoth 			break;
46400dca1793SAdrian Knoth 		case MADIface:
46410dca1793SAdrian Knoth 			break; /* no TCO possible */
46420dca1793SAdrian Knoth 		}
46430dca1793SAdrian Knoth 
46440dca1793SAdrian Knoth 		period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
46450dca1793SAdrian Knoth 		snd_iprintf(buffer, "    period: %u\n", period);
46460dca1793SAdrian Knoth 
46470dca1793SAdrian Knoth 
46480dca1793SAdrian Knoth 		/* rate = freq_const/period; */
46490dca1793SAdrian Knoth 		rate = div_u64(freq_const, period);
46500dca1793SAdrian Knoth 
46510dca1793SAdrian Knoth 		if (control & HDSPM_QuadSpeed) {
46520dca1793SAdrian Knoth 			rate *= 4;
46530dca1793SAdrian Knoth 		} else if (control & HDSPM_DoubleSpeed) {
46540dca1793SAdrian Knoth 			rate *= 2;
46550dca1793SAdrian Knoth 		}
46560dca1793SAdrian Knoth 
46570dca1793SAdrian Knoth 		snd_iprintf(buffer, "  Frequency: %u Hz\n",
46580dca1793SAdrian Knoth 				(unsigned int) rate);
46590dca1793SAdrian Knoth 
46600dca1793SAdrian Knoth 		ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
46610dca1793SAdrian Knoth 		frames = ltc & 0xF;
46620dca1793SAdrian Knoth 		ltc >>= 4;
46630dca1793SAdrian Knoth 		frames += (ltc & 0x3) * 10;
46640dca1793SAdrian Knoth 		ltc >>= 4;
46650dca1793SAdrian Knoth 		seconds = ltc & 0xF;
46660dca1793SAdrian Knoth 		ltc >>= 4;
46670dca1793SAdrian Knoth 		seconds += (ltc & 0x7) * 10;
46680dca1793SAdrian Knoth 		ltc >>= 4;
46690dca1793SAdrian Knoth 		minutes = ltc & 0xF;
46700dca1793SAdrian Knoth 		ltc >>= 4;
46710dca1793SAdrian Knoth 		minutes += (ltc & 0x7) * 10;
46720dca1793SAdrian Knoth 		ltc >>= 4;
46730dca1793SAdrian Knoth 		hours = ltc & 0xF;
46740dca1793SAdrian Knoth 		ltc >>= 4;
46750dca1793SAdrian Knoth 		hours += (ltc & 0x3) * 10;
46760dca1793SAdrian Knoth 		snd_iprintf(buffer,
46770dca1793SAdrian Knoth 			"  LTC In: %02d:%02d:%02d:%02d\n",
46780dca1793SAdrian Knoth 			hours, minutes, seconds, frames);
46790dca1793SAdrian Knoth 
46800dca1793SAdrian Knoth 	} else {
46810dca1793SAdrian Knoth 		snd_iprintf(buffer, "No TCO module detected.\n");
46820dca1793SAdrian Knoth 	}
4683763f356cSTakashi Iwai 
4684763f356cSTakashi Iwai 	snd_iprintf(buffer, "--- Settings ---\n");
4685763f356cSTakashi Iwai 
4686ef5fa1a4STakashi Iwai 	x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
4687763f356cSTakashi Iwai 							HDSPM_LatencyMask));
4688763f356cSTakashi Iwai 
4689763f356cSTakashi Iwai 	snd_iprintf(buffer,
4690763f356cSTakashi Iwai 		"Size (Latency): %d samples (2 periods of %lu bytes)\n",
4691763f356cSTakashi Iwai 		x, (unsigned long) hdspm->period_bytes);
4692763f356cSTakashi Iwai 
46930dca1793SAdrian Knoth 	snd_iprintf(buffer, "Line out: %s\n",
46940dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
4695763f356cSTakashi Iwai 
4696763f356cSTakashi Iwai 	switch (hdspm->control_register & HDSPM_InputMask) {
4697763f356cSTakashi Iwai 	case HDSPM_InputOptical:
4698763f356cSTakashi Iwai 		insel = "Optical";
4699763f356cSTakashi Iwai 		break;
4700763f356cSTakashi Iwai 	case HDSPM_InputCoaxial:
4701763f356cSTakashi Iwai 		insel = "Coaxial";
4702763f356cSTakashi Iwai 		break;
4703763f356cSTakashi Iwai 	default:
47040dca1793SAdrian Knoth 		insel = "Unkown";
4705763f356cSTakashi Iwai 	}
4706763f356cSTakashi Iwai 
4707763f356cSTakashi Iwai 	snd_iprintf(buffer,
4708ef5fa1a4STakashi Iwai 		"ClearTrackMarker = %s, Transmit in %s Channel Mode, "
4709ef5fa1a4STakashi Iwai 		"Auto Input %s\n",
47100dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
47110dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
47120dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
4713763f356cSTakashi Iwai 
47140dca1793SAdrian Knoth 
47153cee5a60SRemy Bruno 	if (!(hdspm->control_register & HDSPM_ClockModeMaster))
47160dca1793SAdrian Knoth 		system_clock_mode = "AutoSync";
47173cee5a60SRemy Bruno 	else
4718763f356cSTakashi Iwai 		system_clock_mode = "Master";
47190dca1793SAdrian Knoth 	snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode);
4720763f356cSTakashi Iwai 
4721763f356cSTakashi Iwai 	switch (hdspm_pref_sync_ref(hdspm)) {
4722763f356cSTakashi Iwai 	case HDSPM_SYNC_FROM_WORD:
4723763f356cSTakashi Iwai 		pref_sync_ref = "Word Clock";
4724763f356cSTakashi Iwai 		break;
4725763f356cSTakashi Iwai 	case HDSPM_SYNC_FROM_MADI:
4726763f356cSTakashi Iwai 		pref_sync_ref = "MADI Sync";
4727763f356cSTakashi Iwai 		break;
47280dca1793SAdrian Knoth 	case HDSPM_SYNC_FROM_TCO:
47290dca1793SAdrian Knoth 		pref_sync_ref = "TCO";
47300dca1793SAdrian Knoth 		break;
47310dca1793SAdrian Knoth 	case HDSPM_SYNC_FROM_SYNC_IN:
47320dca1793SAdrian Knoth 		pref_sync_ref = "Sync In";
47330dca1793SAdrian Knoth 		break;
4734763f356cSTakashi Iwai 	default:
4735763f356cSTakashi Iwai 		pref_sync_ref = "XXXX Clock";
4736763f356cSTakashi Iwai 		break;
4737763f356cSTakashi Iwai 	}
4738763f356cSTakashi Iwai 	snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
4739763f356cSTakashi Iwai 			pref_sync_ref);
4740763f356cSTakashi Iwai 
4741763f356cSTakashi Iwai 	snd_iprintf(buffer, "System Clock Frequency: %d\n",
4742763f356cSTakashi Iwai 			hdspm->system_sample_rate);
4743763f356cSTakashi Iwai 
4744763f356cSTakashi Iwai 
4745763f356cSTakashi Iwai 	snd_iprintf(buffer, "--- Status:\n");
4746763f356cSTakashi Iwai 
4747763f356cSTakashi Iwai 	x = status & HDSPM_madiSync;
4748763f356cSTakashi Iwai 	x2 = status2 & HDSPM_wcSync;
4749763f356cSTakashi Iwai 
4750763f356cSTakashi Iwai 	snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
4751763f356cSTakashi Iwai 			(status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
4752763f356cSTakashi Iwai 			"NoLock",
4753763f356cSTakashi Iwai 			(status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
4754763f356cSTakashi Iwai 			"NoLock");
4755763f356cSTakashi Iwai 
4756763f356cSTakashi Iwai 	switch (hdspm_autosync_ref(hdspm)) {
47570dca1793SAdrian Knoth 	case HDSPM_AUTOSYNC_FROM_SYNC_IN:
47580dca1793SAdrian Knoth 		autosync_ref = "Sync In";
47590dca1793SAdrian Knoth 		break;
47600dca1793SAdrian Knoth 	case HDSPM_AUTOSYNC_FROM_TCO:
47610dca1793SAdrian Knoth 		autosync_ref = "TCO";
47620dca1793SAdrian Knoth 		break;
4763763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_WORD:
4764763f356cSTakashi Iwai 		autosync_ref = "Word Clock";
4765763f356cSTakashi Iwai 		break;
4766763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_MADI:
4767763f356cSTakashi Iwai 		autosync_ref = "MADI Sync";
4768763f356cSTakashi Iwai 		break;
4769763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_NONE:
4770763f356cSTakashi Iwai 		autosync_ref = "Input not valid";
4771763f356cSTakashi Iwai 		break;
4772763f356cSTakashi Iwai 	default:
4773763f356cSTakashi Iwai 		autosync_ref = "---";
4774763f356cSTakashi Iwai 		break;
4775763f356cSTakashi Iwai 	}
4776763f356cSTakashi Iwai 	snd_iprintf(buffer,
4777763f356cSTakashi Iwai 		"AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
4778763f356cSTakashi Iwai 		autosync_ref, hdspm_external_sample_rate(hdspm),
4779763f356cSTakashi Iwai 		(status & HDSPM_madiFreqMask) >> 22,
4780763f356cSTakashi Iwai 		(status2 & HDSPM_wcFreqMask) >> 5);
4781763f356cSTakashi Iwai 
4782763f356cSTakashi Iwai 	snd_iprintf(buffer, "Input: %s, Mode=%s\n",
4783763f356cSTakashi Iwai 		(status & HDSPM_AB_int) ? "Coax" : "Optical",
4784763f356cSTakashi Iwai 		(status & HDSPM_RX_64ch) ? "64 channels" :
4785763f356cSTakashi Iwai 		"56 channels");
4786763f356cSTakashi Iwai 
4787763f356cSTakashi Iwai 	snd_iprintf(buffer, "\n");
4788763f356cSTakashi Iwai }
4789763f356cSTakashi Iwai 
47903cee5a60SRemy Bruno static void
47913cee5a60SRemy Bruno snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
47923cee5a60SRemy Bruno 			  struct snd_info_buffer *buffer)
47933cee5a60SRemy Bruno {
4794ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
47953cee5a60SRemy Bruno 	unsigned int status;
47963cee5a60SRemy Bruno 	unsigned int status2;
47973cee5a60SRemy Bruno 	unsigned int timecode;
47983cee5a60SRemy Bruno 	int pref_syncref;
47993cee5a60SRemy Bruno 	char *autosync_ref;
48003cee5a60SRemy Bruno 	int x;
48013cee5a60SRemy Bruno 
48023cee5a60SRemy Bruno 	status = hdspm_read(hdspm, HDSPM_statusRegister);
48033cee5a60SRemy Bruno 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
48043cee5a60SRemy Bruno 	timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
48053cee5a60SRemy Bruno 
48063cee5a60SRemy Bruno 	snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n",
48073cee5a60SRemy Bruno 		    hdspm->card_name, hdspm->card->number + 1,
48083cee5a60SRemy Bruno 		    hdspm->firmware_rev);
48093cee5a60SRemy Bruno 
48103cee5a60SRemy Bruno 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
48113cee5a60SRemy Bruno 		    hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
48123cee5a60SRemy Bruno 
48133cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- System ---\n");
48143cee5a60SRemy Bruno 
48153cee5a60SRemy Bruno 	snd_iprintf(buffer,
48163cee5a60SRemy Bruno 		    "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
48173cee5a60SRemy Bruno 		    status & HDSPM_audioIRQPending,
48183cee5a60SRemy Bruno 		    (status & HDSPM_midi0IRQPending) ? 1 : 0,
48193cee5a60SRemy Bruno 		    (status & HDSPM_midi1IRQPending) ? 1 : 0,
48203cee5a60SRemy Bruno 		    hdspm->irq_count);
48213cee5a60SRemy Bruno 	snd_iprintf(buffer,
4822ef5fa1a4STakashi Iwai 		    "HW pointer: id = %d, rawptr = %d (%d->%d) "
4823ef5fa1a4STakashi Iwai 		    "estimated= %ld (bytes)\n",
48243cee5a60SRemy Bruno 		    ((status & HDSPM_BufferID) ? 1 : 0),
48253cee5a60SRemy Bruno 		    (status & HDSPM_BufferPositionMask),
4826ef5fa1a4STakashi Iwai 		    (status & HDSPM_BufferPositionMask) %
4827ef5fa1a4STakashi Iwai 		    (2 * (int)hdspm->period_bytes),
4828ef5fa1a4STakashi Iwai 		    ((status & HDSPM_BufferPositionMask) - 64) %
4829ef5fa1a4STakashi Iwai 		    (2 * (int)hdspm->period_bytes),
48303cee5a60SRemy Bruno 		    (long) hdspm_hw_pointer(hdspm) * 4);
48313cee5a60SRemy Bruno 
48323cee5a60SRemy Bruno 	snd_iprintf(buffer,
48333cee5a60SRemy Bruno 		    "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
48343cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
48353cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
48363cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
48373cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
48383cee5a60SRemy Bruno 	snd_iprintf(buffer,
48390dca1793SAdrian Knoth 		    "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
48400dca1793SAdrian Knoth 		    hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
48410dca1793SAdrian Knoth 		    hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
48420dca1793SAdrian Knoth 	snd_iprintf(buffer,
48430dca1793SAdrian Knoth 		    "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
48440dca1793SAdrian Knoth 		    "status2=0x%x\n",
48450dca1793SAdrian Knoth 		    hdspm->control_register, hdspm->control2_register,
48460dca1793SAdrian Knoth 		    status, status2);
48473cee5a60SRemy Bruno 
48483cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- Settings ---\n");
48493cee5a60SRemy Bruno 
4850ef5fa1a4STakashi Iwai 	x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
48513cee5a60SRemy Bruno 				HDSPM_LatencyMask));
48523cee5a60SRemy Bruno 
48533cee5a60SRemy Bruno 	snd_iprintf(buffer,
48543cee5a60SRemy Bruno 		    "Size (Latency): %d samples (2 periods of %lu bytes)\n",
48553cee5a60SRemy Bruno 		    x, (unsigned long) hdspm->period_bytes);
48563cee5a60SRemy Bruno 
48570dca1793SAdrian Knoth 	snd_iprintf(buffer, "Line out: %s\n",
48583cee5a60SRemy Bruno 		    (hdspm->
48590dca1793SAdrian Knoth 		     control_register & HDSPM_LineOut) ? "on " : "off");
48603cee5a60SRemy Bruno 
48613cee5a60SRemy Bruno 	snd_iprintf(buffer,
48623cee5a60SRemy Bruno 		    "ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
48633cee5a60SRemy Bruno 		    (hdspm->
48643cee5a60SRemy Bruno 		     control_register & HDSPM_clr_tms) ? "on" : "off",
48653cee5a60SRemy Bruno 		    (hdspm->
48663cee5a60SRemy Bruno 		     control_register & HDSPM_Emphasis) ? "on" : "off",
48673cee5a60SRemy Bruno 		    (hdspm->
48683cee5a60SRemy Bruno 		     control_register & HDSPM_Dolby) ? "on" : "off");
48693cee5a60SRemy Bruno 
48703cee5a60SRemy Bruno 
48713cee5a60SRemy Bruno 	pref_syncref = hdspm_pref_sync_ref(hdspm);
48723cee5a60SRemy Bruno 	if (pref_syncref == 0)
48733cee5a60SRemy Bruno 		snd_iprintf(buffer, "Preferred Sync Reference: Word Clock\n");
48743cee5a60SRemy Bruno 	else
48753cee5a60SRemy Bruno 		snd_iprintf(buffer, "Preferred Sync Reference: AES%d\n",
48763cee5a60SRemy Bruno 				pref_syncref);
48773cee5a60SRemy Bruno 
48783cee5a60SRemy Bruno 	snd_iprintf(buffer, "System Clock Frequency: %d\n",
48793cee5a60SRemy Bruno 		    hdspm->system_sample_rate);
48803cee5a60SRemy Bruno 
48813cee5a60SRemy Bruno 	snd_iprintf(buffer, "Double speed: %s\n",
48823cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_DS_DoubleWire?
48833cee5a60SRemy Bruno 			"Double wire" : "Single wire");
48843cee5a60SRemy Bruno 	snd_iprintf(buffer, "Quad speed: %s\n",
48853cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_QS_DoubleWire?
48863cee5a60SRemy Bruno 			"Double wire" :
48873cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_QS_QuadWire?
48883cee5a60SRemy Bruno 			"Quad wire" : "Single wire");
48893cee5a60SRemy Bruno 
48903cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- Status:\n");
48913cee5a60SRemy Bruno 
48923cee5a60SRemy Bruno 	snd_iprintf(buffer, "Word: %s  Frequency: %d\n",
48933cee5a60SRemy Bruno 		    (status & HDSPM_AES32_wcLock) ? "Sync   " : "No Lock",
48943cee5a60SRemy Bruno 		    HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
48953cee5a60SRemy Bruno 
48963cee5a60SRemy Bruno 	for (x = 0; x < 8; x++) {
48973cee5a60SRemy Bruno 		snd_iprintf(buffer, "AES%d: %s  Frequency: %d\n",
48983cee5a60SRemy Bruno 			    x+1,
4899ef5fa1a4STakashi Iwai 			    (status2 & (HDSPM_LockAES >> x)) ?
4900ef5fa1a4STakashi Iwai 			    "Sync   " : "No Lock",
49013cee5a60SRemy Bruno 			    HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
49023cee5a60SRemy Bruno 	}
49033cee5a60SRemy Bruno 
49043cee5a60SRemy Bruno 	switch (hdspm_autosync_ref(hdspm)) {
49050dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_NONE:
49060dca1793SAdrian Knoth 		autosync_ref = "None"; break;
49070dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_WORD:
49080dca1793SAdrian Knoth 		autosync_ref = "Word Clock"; break;
49090dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES1:
49100dca1793SAdrian Knoth 		autosync_ref = "AES1"; break;
49110dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES2:
49120dca1793SAdrian Knoth 		autosync_ref = "AES2"; break;
49130dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES3:
49140dca1793SAdrian Knoth 		autosync_ref = "AES3"; break;
49150dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES4:
49160dca1793SAdrian Knoth 		autosync_ref = "AES4"; break;
49170dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES5:
49180dca1793SAdrian Knoth 		autosync_ref = "AES5"; break;
49190dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES6:
49200dca1793SAdrian Knoth 		autosync_ref = "AES6"; break;
49210dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES7:
49220dca1793SAdrian Knoth 		autosync_ref = "AES7"; break;
49230dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES8:
49240dca1793SAdrian Knoth 		autosync_ref = "AES8"; break;
49250dca1793SAdrian Knoth 	default:
49260dca1793SAdrian Knoth 		autosync_ref = "---"; break;
49273cee5a60SRemy Bruno 	}
49283cee5a60SRemy Bruno 	snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
49293cee5a60SRemy Bruno 
49303cee5a60SRemy Bruno 	snd_iprintf(buffer, "\n");
49313cee5a60SRemy Bruno }
49323cee5a60SRemy Bruno 
49330dca1793SAdrian Knoth static void
49340dca1793SAdrian Knoth snd_hdspm_proc_read_raydat(struct snd_info_entry *entry,
49350dca1793SAdrian Knoth 			 struct snd_info_buffer *buffer)
49360dca1793SAdrian Knoth {
49370dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
49380dca1793SAdrian Knoth 	unsigned int status1, status2, status3, control, i;
49390dca1793SAdrian Knoth 	unsigned int lock, sync;
49400dca1793SAdrian Knoth 
49410dca1793SAdrian Knoth 	status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */
49420dca1793SAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */
49430dca1793SAdrian Knoth 	status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */
49440dca1793SAdrian Knoth 
49450dca1793SAdrian Knoth 	control = hdspm->control_register;
49460dca1793SAdrian Knoth 
49470dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1);
49480dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2);
49490dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3);
49500dca1793SAdrian Knoth 
49510dca1793SAdrian Knoth 
49520dca1793SAdrian Knoth 	snd_iprintf(buffer, "\n*** CLOCK MODE\n\n");
49530dca1793SAdrian Knoth 
49540dca1793SAdrian Knoth 	snd_iprintf(buffer, "Clock mode      : %s\n",
49550dca1793SAdrian Knoth 		(hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave");
49560dca1793SAdrian Knoth 	snd_iprintf(buffer, "System frequency: %d Hz\n",
49570dca1793SAdrian Knoth 		hdspm_get_system_sample_rate(hdspm));
49580dca1793SAdrian Knoth 
49590dca1793SAdrian Knoth 	snd_iprintf(buffer, "\n*** INPUT STATUS\n\n");
49600dca1793SAdrian Knoth 
49610dca1793SAdrian Knoth 	lock = 0x1;
49620dca1793SAdrian Knoth 	sync = 0x100;
49630dca1793SAdrian Knoth 
49640dca1793SAdrian Knoth 	for (i = 0; i < 8; i++) {
49650dca1793SAdrian Knoth 		snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n",
49660dca1793SAdrian Knoth 				i,
49670dca1793SAdrian Knoth 				(status1 & lock) ? 1 : 0,
49680dca1793SAdrian Knoth 				(status1 & sync) ? 1 : 0,
49690dca1793SAdrian Knoth 				texts_freq[(status2 >> (i * 4)) & 0xF]);
49700dca1793SAdrian Knoth 
49710dca1793SAdrian Knoth 		lock = lock<<1;
49720dca1793SAdrian Knoth 		sync = sync<<1;
49730dca1793SAdrian Knoth 	}
49740dca1793SAdrian Knoth 
49750dca1793SAdrian Knoth 	snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n",
49760dca1793SAdrian Knoth 			(status1 & 0x1000000) ? 1 : 0,
49770dca1793SAdrian Knoth 			(status1 & 0x2000000) ? 1 : 0,
49780dca1793SAdrian Knoth 			texts_freq[(status1 >> 16) & 0xF]);
49790dca1793SAdrian Knoth 
49800dca1793SAdrian Knoth 	snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n",
49810dca1793SAdrian Knoth 			(status1 & 0x4000000) ? 1 : 0,
49820dca1793SAdrian Knoth 			(status1 & 0x8000000) ? 1 : 0,
49830dca1793SAdrian Knoth 			texts_freq[(status1 >> 20) & 0xF]);
49840dca1793SAdrian Knoth 
49850dca1793SAdrian Knoth 	snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n",
49860dca1793SAdrian Knoth 			(status3 & 0x400) ? 1 : 0,
49870dca1793SAdrian Knoth 			(status3 & 0x800) ? 1 : 0,
49880dca1793SAdrian Knoth 			texts_freq[(status2 >> 12) & 0xF]);
49890dca1793SAdrian Knoth 
49900dca1793SAdrian Knoth }
49910dca1793SAdrian Knoth 
49923cee5a60SRemy Bruno #ifdef CONFIG_SND_DEBUG
49933cee5a60SRemy Bruno static void
49943cee5a60SRemy Bruno snd_hdspm_proc_read_debug(struct snd_info_entry *entry,
49953cee5a60SRemy Bruno 			  struct snd_info_buffer *buffer)
49963cee5a60SRemy Bruno {
4997ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
49983cee5a60SRemy Bruno 
49993cee5a60SRemy Bruno 	int j,i;
50003cee5a60SRemy Bruno 
5001ef5fa1a4STakashi Iwai 	for (i = 0; i < 256 /* 1024*64 */; i += j) {
50023cee5a60SRemy Bruno 		snd_iprintf(buffer, "0x%08X: ", i);
50033cee5a60SRemy Bruno 		for (j = 0; j < 16; j += 4)
50043cee5a60SRemy Bruno 			snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j));
50053cee5a60SRemy Bruno 		snd_iprintf(buffer, "\n");
50063cee5a60SRemy Bruno 	}
50073cee5a60SRemy Bruno }
50083cee5a60SRemy Bruno #endif
50093cee5a60SRemy Bruno 
50103cee5a60SRemy Bruno 
50110dca1793SAdrian Knoth static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry,
50120dca1793SAdrian Knoth 			  struct snd_info_buffer *buffer)
50130dca1793SAdrian Knoth {
50140dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
50150dca1793SAdrian Knoth 	int i;
50160dca1793SAdrian Knoth 
50170dca1793SAdrian Knoth 	snd_iprintf(buffer, "# generated by hdspm\n");
50180dca1793SAdrian Knoth 
50190dca1793SAdrian Knoth 	for (i = 0; i < hdspm->max_channels_in; i++) {
50200dca1793SAdrian Knoth 		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]);
50210dca1793SAdrian Knoth 	}
50220dca1793SAdrian Knoth }
50230dca1793SAdrian Knoth 
50240dca1793SAdrian Knoth static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry,
50250dca1793SAdrian Knoth 			  struct snd_info_buffer *buffer)
50260dca1793SAdrian Knoth {
50270dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
50280dca1793SAdrian Knoth 	int i;
50290dca1793SAdrian Knoth 
50300dca1793SAdrian Knoth 	snd_iprintf(buffer, "# generated by hdspm\n");
50310dca1793SAdrian Knoth 
50320dca1793SAdrian Knoth 	for (i = 0; i < hdspm->max_channels_out; i++) {
50330dca1793SAdrian Knoth 		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]);
50340dca1793SAdrian Knoth 	}
50350dca1793SAdrian Knoth }
50360dca1793SAdrian Knoth 
50373cee5a60SRemy Bruno 
503898274f07STakashi Iwai static void __devinit snd_hdspm_proc_init(struct hdspm *hdspm)
5039763f356cSTakashi Iwai {
504098274f07STakashi Iwai 	struct snd_info_entry *entry;
5041763f356cSTakashi Iwai 
50420dca1793SAdrian Knoth 	if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) {
50430dca1793SAdrian Knoth 		switch (hdspm->io_type) {
50440dca1793SAdrian Knoth 		case AES32:
5045bf850204STakashi Iwai 			snd_info_set_text_ops(entry, hdspm,
50460dca1793SAdrian Knoth 					snd_hdspm_proc_read_aes32);
50470dca1793SAdrian Knoth 			break;
50480dca1793SAdrian Knoth 		case MADI:
50490dca1793SAdrian Knoth 			snd_info_set_text_ops(entry, hdspm,
50503cee5a60SRemy Bruno 					snd_hdspm_proc_read_madi);
50510dca1793SAdrian Knoth 			break;
50520dca1793SAdrian Knoth 		case MADIface:
50530dca1793SAdrian Knoth 			/* snd_info_set_text_ops(entry, hdspm,
50540dca1793SAdrian Knoth 			 snd_hdspm_proc_read_madiface); */
50550dca1793SAdrian Knoth 			break;
50560dca1793SAdrian Knoth 		case RayDAT:
50570dca1793SAdrian Knoth 			snd_info_set_text_ops(entry, hdspm,
50580dca1793SAdrian Knoth 					snd_hdspm_proc_read_raydat);
50590dca1793SAdrian Knoth 			break;
50600dca1793SAdrian Knoth 		case AIO:
50610dca1793SAdrian Knoth 			break;
50620dca1793SAdrian Knoth 		}
50630dca1793SAdrian Knoth 	}
50640dca1793SAdrian Knoth 
50650dca1793SAdrian Knoth 	if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) {
50660dca1793SAdrian Knoth 		snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in);
50670dca1793SAdrian Knoth 	}
50680dca1793SAdrian Knoth 
50690dca1793SAdrian Knoth 	if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) {
50700dca1793SAdrian Knoth 		snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out);
50710dca1793SAdrian Knoth 	}
50720dca1793SAdrian Knoth 
50733cee5a60SRemy Bruno #ifdef CONFIG_SND_DEBUG
50743cee5a60SRemy Bruno 	/* debug file to read all hdspm registers */
50753cee5a60SRemy Bruno 	if (!snd_card_proc_new(hdspm->card, "debug", &entry))
50763cee5a60SRemy Bruno 		snd_info_set_text_ops(entry, hdspm,
50773cee5a60SRemy Bruno 				snd_hdspm_proc_read_debug);
50783cee5a60SRemy Bruno #endif
5079763f356cSTakashi Iwai }
5080763f356cSTakashi Iwai 
5081763f356cSTakashi Iwai /*------------------------------------------------------------
5082763f356cSTakashi Iwai    hdspm intitialize
5083763f356cSTakashi Iwai  ------------------------------------------------------------*/
5084763f356cSTakashi Iwai 
508598274f07STakashi Iwai static int snd_hdspm_set_defaults(struct hdspm * hdspm)
5086763f356cSTakashi Iwai {
5087763f356cSTakashi Iwai 	/* ASSUMPTION: hdspm->lock is either held, or there is no need to
5088561de31aSJoe Perches 	   hold it (e.g. during module initialization).
5089763f356cSTakashi Iwai 	   */
5090763f356cSTakashi Iwai 
5091763f356cSTakashi Iwai 	/* set defaults:       */
5092763f356cSTakashi Iwai 
50930dca1793SAdrian Knoth 	hdspm->settings_register = 0;
50940dca1793SAdrian Knoth 
50950dca1793SAdrian Knoth 	switch (hdspm->io_type) {
50960dca1793SAdrian Knoth 	case MADI:
50970dca1793SAdrian Knoth 	case MADIface:
50980dca1793SAdrian Knoth 		hdspm->control_register =
50990dca1793SAdrian Knoth 			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
51000dca1793SAdrian Knoth 		break;
51010dca1793SAdrian Knoth 
51020dca1793SAdrian Knoth 	case RayDAT:
51030dca1793SAdrian Knoth 	case AIO:
51040dca1793SAdrian Knoth 		hdspm->settings_register = 0x1 + 0x1000;
51050dca1793SAdrian Knoth 		/* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0,
51060dca1793SAdrian Knoth 		 * line_out */
51070dca1793SAdrian Knoth 		hdspm->control_register =
51080dca1793SAdrian Knoth 			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
51090dca1793SAdrian Knoth 		break;
51100dca1793SAdrian Knoth 
51110dca1793SAdrian Knoth 	case AES32:
5112ef5fa1a4STakashi Iwai 		hdspm->control_register =
5113ef5fa1a4STakashi Iwai 			HDSPM_ClockModeMaster |	/* Master Cloack Mode on */
51140dca1793SAdrian Knoth 			hdspm_encode_latency(7) | /* latency max=8192samples */
51153cee5a60SRemy Bruno 			HDSPM_SyncRef0 |	/* AES1 is syncclock */
51163cee5a60SRemy Bruno 			HDSPM_LineOut |	/* Analog output in */
51173cee5a60SRemy Bruno 			HDSPM_Professional;  /* Professional mode */
51180dca1793SAdrian Knoth 		break;
51190dca1793SAdrian Knoth 	}
5120763f356cSTakashi Iwai 
5121763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
5122763f356cSTakashi Iwai 
51230dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
5124ffb2c3c0SRemy Bruno 		/* No control2 register for AES32 */
5125763f356cSTakashi Iwai #ifdef SNDRV_BIG_ENDIAN
5126763f356cSTakashi Iwai 		hdspm->control2_register = HDSPM_BIGENDIAN_MODE;
5127763f356cSTakashi Iwai #else
5128763f356cSTakashi Iwai 		hdspm->control2_register = 0;
5129763f356cSTakashi Iwai #endif
5130763f356cSTakashi Iwai 
5131763f356cSTakashi Iwai 		hdspm_write(hdspm, HDSPM_control2Reg, hdspm->control2_register);
5132ffb2c3c0SRemy Bruno 	}
5133763f356cSTakashi Iwai 	hdspm_compute_period_size(hdspm);
5134763f356cSTakashi Iwai 
5135763f356cSTakashi Iwai 	/* silence everything */
5136763f356cSTakashi Iwai 
5137763f356cSTakashi Iwai 	all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
5138763f356cSTakashi Iwai 
51390dca1793SAdrian Knoth 	if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) {
51400dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
5141763f356cSTakashi Iwai 	}
5142763f356cSTakashi Iwai 
5143763f356cSTakashi Iwai 	/* set a default rate so that the channel map is set up. */
51440dca1793SAdrian Knoth 	hdspm_set_rate(hdspm, 48000, 1);
5145763f356cSTakashi Iwai 
5146763f356cSTakashi Iwai 	return 0;
5147763f356cSTakashi Iwai }
5148763f356cSTakashi Iwai 
5149763f356cSTakashi Iwai 
5150763f356cSTakashi Iwai /*------------------------------------------------------------
5151561de31aSJoe Perches    interrupt
5152763f356cSTakashi Iwai  ------------------------------------------------------------*/
5153763f356cSTakashi Iwai 
51547d12e780SDavid Howells static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
5155763f356cSTakashi Iwai {
515698274f07STakashi Iwai 	struct hdspm *hdspm = (struct hdspm *) dev_id;
5157763f356cSTakashi Iwai 	unsigned int status;
51580dca1793SAdrian Knoth 	int i, audio, midi, schedule = 0;
51590dca1793SAdrian Knoth 	/* cycles_t now; */
5160763f356cSTakashi Iwai 
5161763f356cSTakashi Iwai 	status = hdspm_read(hdspm, HDSPM_statusRegister);
5162763f356cSTakashi Iwai 
5163763f356cSTakashi Iwai 	audio = status & HDSPM_audioIRQPending;
51640dca1793SAdrian Knoth 	midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending |
51650dca1793SAdrian Knoth 			HDSPM_midi2IRQPending | HDSPM_midi3IRQPending);
5166763f356cSTakashi Iwai 
51670dca1793SAdrian Knoth 	/* now = get_cycles(); */
51680dca1793SAdrian Knoth 	/**
51690dca1793SAdrian Knoth 	 *   LAT_2..LAT_0 period  counter (win)  counter (mac)
51700dca1793SAdrian Knoth 	 *          6       4096   ~256053425     ~514672358
51710dca1793SAdrian Knoth 	 *          5       2048   ~128024983     ~257373821
51720dca1793SAdrian Knoth 	 *          4       1024    ~64023706     ~128718089
51730dca1793SAdrian Knoth 	 *          3        512    ~32005945      ~64385999
51740dca1793SAdrian Knoth 	 *          2        256    ~16003039      ~32260176
51750dca1793SAdrian Knoth 	 *          1        128     ~7998738      ~16194507
51760dca1793SAdrian Knoth 	 *          0         64     ~3998231       ~8191558
51770dca1793SAdrian Knoth 	 **/
51780dca1793SAdrian Knoth 	/*
51790dca1793SAdrian Knoth 	   snd_printk(KERN_INFO "snd_hdspm_interrupt %llu @ %llx\n",
51800dca1793SAdrian Knoth 	   now-hdspm->last_interrupt, status & 0xFFC0);
51810dca1793SAdrian Knoth 	   hdspm->last_interrupt = now;
51820dca1793SAdrian Knoth 	*/
51830dca1793SAdrian Knoth 
51840dca1793SAdrian Knoth 	if (!audio && !midi)
5185763f356cSTakashi Iwai 		return IRQ_NONE;
5186763f356cSTakashi Iwai 
5187763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_interruptConfirmation, 0);
5188763f356cSTakashi Iwai 	hdspm->irq_count++;
5189763f356cSTakashi Iwai 
5190763f356cSTakashi Iwai 
5191763f356cSTakashi Iwai 	if (audio) {
5192763f356cSTakashi Iwai 		if (hdspm->capture_substream)
5193ef5fa1a4STakashi Iwai 			snd_pcm_period_elapsed(hdspm->capture_substream);
5194763f356cSTakashi Iwai 
5195763f356cSTakashi Iwai 		if (hdspm->playback_substream)
5196ef5fa1a4STakashi Iwai 			snd_pcm_period_elapsed(hdspm->playback_substream);
5197763f356cSTakashi Iwai 	}
5198763f356cSTakashi Iwai 
51990dca1793SAdrian Knoth 	if (midi) {
52000dca1793SAdrian Knoth 		i = 0;
52010dca1793SAdrian Knoth 		while (i < hdspm->midiPorts) {
52020dca1793SAdrian Knoth 			if ((hdspm_read(hdspm,
52030dca1793SAdrian Knoth 				hdspm->midi[i].statusIn) & 0xff) &&
52040dca1793SAdrian Knoth 					(status & hdspm->midi[i].irq)) {
52050dca1793SAdrian Knoth 				/* we disable interrupts for this input until
52060dca1793SAdrian Knoth 				 * processing is done
5207ef5fa1a4STakashi Iwai 				 */
52080dca1793SAdrian Knoth 				hdspm->control_register &= ~hdspm->midi[i].ie;
5209763f356cSTakashi Iwai 				hdspm_write(hdspm, HDSPM_controlRegister,
5210763f356cSTakashi Iwai 						hdspm->control_register);
52110dca1793SAdrian Knoth 				hdspm->midi[i].pending = 1;
5212763f356cSTakashi Iwai 				schedule = 1;
5213763f356cSTakashi Iwai 			}
52140dca1793SAdrian Knoth 
52150dca1793SAdrian Knoth 			i++;
5216763f356cSTakashi Iwai 		}
52170dca1793SAdrian Knoth 
5218763f356cSTakashi Iwai 		if (schedule)
52190dca1793SAdrian Knoth 			tasklet_hi_schedule(&hdspm->midi_tasklet);
52200dca1793SAdrian Knoth 	}
52210dca1793SAdrian Knoth 
5222763f356cSTakashi Iwai 	return IRQ_HANDLED;
5223763f356cSTakashi Iwai }
5224763f356cSTakashi Iwai 
5225763f356cSTakashi Iwai /*------------------------------------------------------------
5226763f356cSTakashi Iwai    pcm interface
5227763f356cSTakashi Iwai   ------------------------------------------------------------*/
5228763f356cSTakashi Iwai 
5229763f356cSTakashi Iwai 
52300dca1793SAdrian Knoth static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream
52310dca1793SAdrian Knoth 					      *substream)
5232763f356cSTakashi Iwai {
523398274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5234763f356cSTakashi Iwai 	return hdspm_hw_pointer(hdspm);
5235763f356cSTakashi Iwai }
5236763f356cSTakashi Iwai 
5237763f356cSTakashi Iwai 
523898274f07STakashi Iwai static int snd_hdspm_reset(struct snd_pcm_substream *substream)
5239763f356cSTakashi Iwai {
524098274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
524198274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
524298274f07STakashi Iwai 	struct snd_pcm_substream *other;
5243763f356cSTakashi Iwai 
5244763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5245763f356cSTakashi Iwai 		other = hdspm->capture_substream;
5246763f356cSTakashi Iwai 	else
5247763f356cSTakashi Iwai 		other = hdspm->playback_substream;
5248763f356cSTakashi Iwai 
5249763f356cSTakashi Iwai 	if (hdspm->running)
5250763f356cSTakashi Iwai 		runtime->status->hw_ptr = hdspm_hw_pointer(hdspm);
5251763f356cSTakashi Iwai 	else
5252763f356cSTakashi Iwai 		runtime->status->hw_ptr = 0;
5253763f356cSTakashi Iwai 	if (other) {
525498274f07STakashi Iwai 		struct snd_pcm_substream *s;
525598274f07STakashi Iwai 		struct snd_pcm_runtime *oruntime = other->runtime;
5256ef991b95STakashi Iwai 		snd_pcm_group_for_each_entry(s, substream) {
5257763f356cSTakashi Iwai 			if (s == other) {
5258763f356cSTakashi Iwai 				oruntime->status->hw_ptr =
5259763f356cSTakashi Iwai 					runtime->status->hw_ptr;
5260763f356cSTakashi Iwai 				break;
5261763f356cSTakashi Iwai 			}
5262763f356cSTakashi Iwai 		}
5263763f356cSTakashi Iwai 	}
5264763f356cSTakashi Iwai 	return 0;
5265763f356cSTakashi Iwai }
5266763f356cSTakashi Iwai 
526798274f07STakashi Iwai static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
526898274f07STakashi Iwai 			       struct snd_pcm_hw_params *params)
5269763f356cSTakashi Iwai {
527098274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5271763f356cSTakashi Iwai 	int err;
5272763f356cSTakashi Iwai 	int i;
5273763f356cSTakashi Iwai 	pid_t this_pid;
5274763f356cSTakashi Iwai 	pid_t other_pid;
5275763f356cSTakashi Iwai 
5276763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5277763f356cSTakashi Iwai 
5278763f356cSTakashi Iwai 	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5279763f356cSTakashi Iwai 		this_pid = hdspm->playback_pid;
5280763f356cSTakashi Iwai 		other_pid = hdspm->capture_pid;
5281763f356cSTakashi Iwai 	} else {
5282763f356cSTakashi Iwai 		this_pid = hdspm->capture_pid;
5283763f356cSTakashi Iwai 		other_pid = hdspm->playback_pid;
5284763f356cSTakashi Iwai 	}
5285763f356cSTakashi Iwai 
5286ef5fa1a4STakashi Iwai 	if (other_pid > 0 && this_pid != other_pid) {
5287763f356cSTakashi Iwai 
5288763f356cSTakashi Iwai 		/* The other stream is open, and not by the same
5289763f356cSTakashi Iwai 		   task as this one. Make sure that the parameters
5290763f356cSTakashi Iwai 		   that matter are the same.
5291763f356cSTakashi Iwai 		   */
5292763f356cSTakashi Iwai 
5293763f356cSTakashi Iwai 		if (params_rate(params) != hdspm->system_sample_rate) {
5294763f356cSTakashi Iwai 			spin_unlock_irq(&hdspm->lock);
5295763f356cSTakashi Iwai 			_snd_pcm_hw_param_setempty(params,
5296763f356cSTakashi Iwai 					SNDRV_PCM_HW_PARAM_RATE);
5297763f356cSTakashi Iwai 			return -EBUSY;
5298763f356cSTakashi Iwai 		}
5299763f356cSTakashi Iwai 
5300763f356cSTakashi Iwai 		if (params_period_size(params) != hdspm->period_bytes / 4) {
5301763f356cSTakashi Iwai 			spin_unlock_irq(&hdspm->lock);
5302763f356cSTakashi Iwai 			_snd_pcm_hw_param_setempty(params,
5303763f356cSTakashi Iwai 					SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
5304763f356cSTakashi Iwai 			return -EBUSY;
5305763f356cSTakashi Iwai 		}
5306763f356cSTakashi Iwai 
5307763f356cSTakashi Iwai 	}
5308763f356cSTakashi Iwai 	/* We're fine. */
5309763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5310763f356cSTakashi Iwai 
5311763f356cSTakashi Iwai 	/* how to make sure that the rate matches an externally-set one ?   */
5312763f356cSTakashi Iwai 
5313763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5314ef5fa1a4STakashi Iwai 	err = hdspm_set_rate(hdspm, params_rate(params), 0);
5315ef5fa1a4STakashi Iwai 	if (err < 0) {
53160dca1793SAdrian Knoth 		snd_printk(KERN_INFO "err on hdspm_set_rate: %d\n", err);
5317763f356cSTakashi Iwai 		spin_unlock_irq(&hdspm->lock);
5318763f356cSTakashi Iwai 		_snd_pcm_hw_param_setempty(params,
5319763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_RATE);
5320763f356cSTakashi Iwai 		return err;
5321763f356cSTakashi Iwai 	}
5322763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5323763f356cSTakashi Iwai 
5324ef5fa1a4STakashi Iwai 	err = hdspm_set_interrupt_interval(hdspm,
5325ef5fa1a4STakashi Iwai 			params_period_size(params));
5326ef5fa1a4STakashi Iwai 	if (err < 0) {
53270dca1793SAdrian Knoth 		snd_printk(KERN_INFO "err on hdspm_set_interrupt_interval: %d\n", err);
5328763f356cSTakashi Iwai 		_snd_pcm_hw_param_setempty(params,
5329763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
5330763f356cSTakashi Iwai 		return err;
5331763f356cSTakashi Iwai 	}
5332763f356cSTakashi Iwai 
5333ef5fa1a4STakashi Iwai 	/* Memory allocation, takashi's method, dont know if we should
5334ef5fa1a4STakashi Iwai 	 * spinlock
5335ef5fa1a4STakashi Iwai 	 */
5336763f356cSTakashi Iwai 	/* malloc all buffer even if not enabled to get sure */
5337ffb2c3c0SRemy Bruno 	/* Update for MADI rev 204: we need to allocate for all channels,
5338ffb2c3c0SRemy Bruno 	 * otherwise it doesn't work at 96kHz */
53390dca1793SAdrian Knoth 
5340763f356cSTakashi Iwai 	err =
5341ffb2c3c0SRemy Bruno 		snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
53420dca1793SAdrian Knoth 	if (err < 0) {
53430dca1793SAdrian Knoth 		snd_printk(KERN_INFO "err on snd_pcm_lib_malloc_pages: %d\n", err);
5344763f356cSTakashi Iwai 		return err;
53450dca1793SAdrian Knoth 	}
5346763f356cSTakashi Iwai 
5347763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5348763f356cSTakashi Iwai 
534977a23f26STakashi Iwai 		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut,
5350763f356cSTakashi Iwai 				params_channels(params));
5351763f356cSTakashi Iwai 
5352763f356cSTakashi Iwai 		for (i = 0; i < params_channels(params); ++i)
5353763f356cSTakashi Iwai 			snd_hdspm_enable_out(hdspm, i, 1);
5354763f356cSTakashi Iwai 
5355763f356cSTakashi Iwai 		hdspm->playback_buffer =
5356763f356cSTakashi Iwai 			(unsigned char *) substream->runtime->dma_area;
535754bf5dd9STakashi Iwai 		snd_printdd("Allocated sample buffer for playback at %p\n",
53583cee5a60SRemy Bruno 				hdspm->playback_buffer);
5359763f356cSTakashi Iwai 	} else {
536077a23f26STakashi Iwai 		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn,
5361763f356cSTakashi Iwai 				params_channels(params));
5362763f356cSTakashi Iwai 
5363763f356cSTakashi Iwai 		for (i = 0; i < params_channels(params); ++i)
5364763f356cSTakashi Iwai 			snd_hdspm_enable_in(hdspm, i, 1);
5365763f356cSTakashi Iwai 
5366763f356cSTakashi Iwai 		hdspm->capture_buffer =
5367763f356cSTakashi Iwai 			(unsigned char *) substream->runtime->dma_area;
536854bf5dd9STakashi Iwai 		snd_printdd("Allocated sample buffer for capture at %p\n",
53693cee5a60SRemy Bruno 				hdspm->capture_buffer);
5370763f356cSTakashi Iwai 	}
53710dca1793SAdrian Knoth 
53723cee5a60SRemy Bruno 	/*
53733cee5a60SRemy Bruno 	   snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
53743cee5a60SRemy Bruno 	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
53753cee5a60SRemy Bruno 	   "playback" : "capture",
537677a23f26STakashi Iwai 	   snd_pcm_sgbuf_get_addr(substream, 0));
53773cee5a60SRemy Bruno 	   */
5378ffb2c3c0SRemy Bruno 	/*
5379ffb2c3c0SRemy Bruno 	   snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n",
5380ffb2c3c0SRemy Bruno 	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
5381ffb2c3c0SRemy Bruno 	   "playback" : "capture",
5382ffb2c3c0SRemy Bruno 	   params_rate(params), params_channels(params),
5383ffb2c3c0SRemy Bruno 	   params_buffer_size(params));
5384ffb2c3c0SRemy Bruno 	   */
53850dca1793SAdrian Knoth 
53860dca1793SAdrian Knoth 
53870dca1793SAdrian Knoth 	/* Switch to native float format if requested */
53880dca1793SAdrian Knoth 	if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
53890dca1793SAdrian Knoth 		if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
53900dca1793SAdrian Knoth 			snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE float format.\n");
53910dca1793SAdrian Knoth 
53920dca1793SAdrian Knoth 		hdspm->control_register |= HDSPe_FLOAT_FORMAT;
53930dca1793SAdrian Knoth 	} else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) {
53940dca1793SAdrian Knoth 		if (hdspm->control_register & HDSPe_FLOAT_FORMAT)
53950dca1793SAdrian Knoth 			snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE integer format.\n");
53960dca1793SAdrian Knoth 
53970dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPe_FLOAT_FORMAT;
53980dca1793SAdrian Knoth 	}
53990dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
54000dca1793SAdrian Knoth 
5401763f356cSTakashi Iwai 	return 0;
5402763f356cSTakashi Iwai }
5403763f356cSTakashi Iwai 
540498274f07STakashi Iwai static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
5405763f356cSTakashi Iwai {
5406763f356cSTakashi Iwai 	int i;
540798274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5408763f356cSTakashi Iwai 
5409763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5410763f356cSTakashi Iwai 
5411763f356cSTakashi Iwai 		/* params_channels(params) should be enough,
5412763f356cSTakashi Iwai 		   but to get sure in case of error */
54130dca1793SAdrian Knoth 		for (i = 0; i < hdspm->max_channels_out; ++i)
5414763f356cSTakashi Iwai 			snd_hdspm_enable_out(hdspm, i, 0);
5415763f356cSTakashi Iwai 
5416763f356cSTakashi Iwai 		hdspm->playback_buffer = NULL;
5417763f356cSTakashi Iwai 	} else {
54180dca1793SAdrian Knoth 		for (i = 0; i < hdspm->max_channels_in; ++i)
5419763f356cSTakashi Iwai 			snd_hdspm_enable_in(hdspm, i, 0);
5420763f356cSTakashi Iwai 
5421763f356cSTakashi Iwai 		hdspm->capture_buffer = NULL;
5422763f356cSTakashi Iwai 
5423763f356cSTakashi Iwai 	}
5424763f356cSTakashi Iwai 
5425763f356cSTakashi Iwai 	snd_pcm_lib_free_pages(substream);
5426763f356cSTakashi Iwai 
5427763f356cSTakashi Iwai 	return 0;
5428763f356cSTakashi Iwai }
5429763f356cSTakashi Iwai 
54300dca1793SAdrian Knoth 
543198274f07STakashi Iwai static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
543298274f07STakashi Iwai 		struct snd_pcm_channel_info *info)
5433763f356cSTakashi Iwai {
543498274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5435763f356cSTakashi Iwai 
54360dca1793SAdrian Knoth 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
54370dca1793SAdrian Knoth 		if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) {
54380dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel out of range (%d)\n", info->channel);
5439da3cec35STakashi Iwai 			return -EINVAL;
54400dca1793SAdrian Knoth 		}
5441763f356cSTakashi Iwai 
54420dca1793SAdrian Knoth 		if (hdspm->channel_map_out[info->channel] < 0) {
54430dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel %d mapped out\n", info->channel);
5444763f356cSTakashi Iwai 			return -EINVAL;
54450dca1793SAdrian Knoth 		}
5446763f356cSTakashi Iwai 
54470dca1793SAdrian Knoth 		info->offset = hdspm->channel_map_out[info->channel] *
54480dca1793SAdrian Knoth 			HDSPM_CHANNEL_BUFFER_BYTES;
54490dca1793SAdrian Knoth 	} else {
54500dca1793SAdrian Knoth 		if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) {
54510dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel out of range (%d)\n", info->channel);
54520dca1793SAdrian Knoth 			return -EINVAL;
54530dca1793SAdrian Knoth 		}
54540dca1793SAdrian Knoth 
54550dca1793SAdrian Knoth 		if (hdspm->channel_map_in[info->channel] < 0) {
54560dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel %d mapped out\n", info->channel);
54570dca1793SAdrian Knoth 			return -EINVAL;
54580dca1793SAdrian Knoth 		}
54590dca1793SAdrian Knoth 
54600dca1793SAdrian Knoth 		info->offset = hdspm->channel_map_in[info->channel] *
54610dca1793SAdrian Knoth 			HDSPM_CHANNEL_BUFFER_BYTES;
54620dca1793SAdrian Knoth 	}
54630dca1793SAdrian Knoth 
5464763f356cSTakashi Iwai 	info->first = 0;
5465763f356cSTakashi Iwai 	info->step = 32;
5466763f356cSTakashi Iwai 	return 0;
5467763f356cSTakashi Iwai }
5468763f356cSTakashi Iwai 
54690dca1793SAdrian Knoth 
547098274f07STakashi Iwai static int snd_hdspm_ioctl(struct snd_pcm_substream *substream,
5471763f356cSTakashi Iwai 		unsigned int cmd, void *arg)
5472763f356cSTakashi Iwai {
5473763f356cSTakashi Iwai 	switch (cmd) {
5474763f356cSTakashi Iwai 	case SNDRV_PCM_IOCTL1_RESET:
5475763f356cSTakashi Iwai 		return snd_hdspm_reset(substream);
5476763f356cSTakashi Iwai 
5477763f356cSTakashi Iwai 	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
5478763f356cSTakashi Iwai 		{
547998274f07STakashi Iwai 			struct snd_pcm_channel_info *info = arg;
5480763f356cSTakashi Iwai 			return snd_hdspm_channel_info(substream, info);
5481763f356cSTakashi Iwai 		}
5482763f356cSTakashi Iwai 	default:
5483763f356cSTakashi Iwai 		break;
5484763f356cSTakashi Iwai 	}
5485763f356cSTakashi Iwai 
5486763f356cSTakashi Iwai 	return snd_pcm_lib_ioctl(substream, cmd, arg);
5487763f356cSTakashi Iwai }
5488763f356cSTakashi Iwai 
548998274f07STakashi Iwai static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
5490763f356cSTakashi Iwai {
549198274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
549298274f07STakashi Iwai 	struct snd_pcm_substream *other;
5493763f356cSTakashi Iwai 	int running;
5494763f356cSTakashi Iwai 
5495763f356cSTakashi Iwai 	spin_lock(&hdspm->lock);
5496763f356cSTakashi Iwai 	running = hdspm->running;
5497763f356cSTakashi Iwai 	switch (cmd) {
5498763f356cSTakashi Iwai 	case SNDRV_PCM_TRIGGER_START:
5499763f356cSTakashi Iwai 		running |= 1 << substream->stream;
5500763f356cSTakashi Iwai 		break;
5501763f356cSTakashi Iwai 	case SNDRV_PCM_TRIGGER_STOP:
5502763f356cSTakashi Iwai 		running &= ~(1 << substream->stream);
5503763f356cSTakashi Iwai 		break;
5504763f356cSTakashi Iwai 	default:
5505763f356cSTakashi Iwai 		snd_BUG();
5506763f356cSTakashi Iwai 		spin_unlock(&hdspm->lock);
5507763f356cSTakashi Iwai 		return -EINVAL;
5508763f356cSTakashi Iwai 	}
5509763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5510763f356cSTakashi Iwai 		other = hdspm->capture_substream;
5511763f356cSTakashi Iwai 	else
5512763f356cSTakashi Iwai 		other = hdspm->playback_substream;
5513763f356cSTakashi Iwai 
5514763f356cSTakashi Iwai 	if (other) {
551598274f07STakashi Iwai 		struct snd_pcm_substream *s;
5516ef991b95STakashi Iwai 		snd_pcm_group_for_each_entry(s, substream) {
5517763f356cSTakashi Iwai 			if (s == other) {
5518763f356cSTakashi Iwai 				snd_pcm_trigger_done(s, substream);
5519763f356cSTakashi Iwai 				if (cmd == SNDRV_PCM_TRIGGER_START)
5520763f356cSTakashi Iwai 					running |= 1 << s->stream;
5521763f356cSTakashi Iwai 				else
5522763f356cSTakashi Iwai 					running &= ~(1 << s->stream);
5523763f356cSTakashi Iwai 				goto _ok;
5524763f356cSTakashi Iwai 			}
5525763f356cSTakashi Iwai 		}
5526763f356cSTakashi Iwai 		if (cmd == SNDRV_PCM_TRIGGER_START) {
5527763f356cSTakashi Iwai 			if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
5528763f356cSTakashi Iwai 					&& substream->stream ==
5529763f356cSTakashi Iwai 					SNDRV_PCM_STREAM_CAPTURE)
5530763f356cSTakashi Iwai 				hdspm_silence_playback(hdspm);
5531763f356cSTakashi Iwai 		} else {
5532763f356cSTakashi Iwai 			if (running &&
5533763f356cSTakashi Iwai 				substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5534763f356cSTakashi Iwai 				hdspm_silence_playback(hdspm);
5535763f356cSTakashi Iwai 		}
5536763f356cSTakashi Iwai 	} else {
5537763f356cSTakashi Iwai 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
5538763f356cSTakashi Iwai 			hdspm_silence_playback(hdspm);
5539763f356cSTakashi Iwai 	}
5540763f356cSTakashi Iwai _ok:
5541763f356cSTakashi Iwai 	snd_pcm_trigger_done(substream, substream);
5542763f356cSTakashi Iwai 	if (!hdspm->running && running)
5543763f356cSTakashi Iwai 		hdspm_start_audio(hdspm);
5544763f356cSTakashi Iwai 	else if (hdspm->running && !running)
5545763f356cSTakashi Iwai 		hdspm_stop_audio(hdspm);
5546763f356cSTakashi Iwai 	hdspm->running = running;
5547763f356cSTakashi Iwai 	spin_unlock(&hdspm->lock);
5548763f356cSTakashi Iwai 
5549763f356cSTakashi Iwai 	return 0;
5550763f356cSTakashi Iwai }
5551763f356cSTakashi Iwai 
555298274f07STakashi Iwai static int snd_hdspm_prepare(struct snd_pcm_substream *substream)
5553763f356cSTakashi Iwai {
5554763f356cSTakashi Iwai 	return 0;
5555763f356cSTakashi Iwai }
5556763f356cSTakashi Iwai 
55570dca1793SAdrian Knoth static unsigned int period_sizes_old[] = {
55580dca1793SAdrian Knoth 	64, 128, 256, 512, 1024, 2048, 4096
55590dca1793SAdrian Knoth };
55600dca1793SAdrian Knoth 
55610dca1793SAdrian Knoth static unsigned int period_sizes_new[] = {
55620dca1793SAdrian Knoth 	32, 64, 128, 256, 512, 1024, 2048, 4096
55630dca1793SAdrian Knoth };
55640dca1793SAdrian Knoth 
55650dca1793SAdrian Knoth /* RayDAT and AIO always have a buffer of 16384 samples per channel */
55660dca1793SAdrian Knoth static unsigned int raydat_aio_buffer_sizes[] = {
55670dca1793SAdrian Knoth 	16384
55680dca1793SAdrian Knoth };
5569763f356cSTakashi Iwai 
557098274f07STakashi Iwai static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
5571763f356cSTakashi Iwai 	.info = (SNDRV_PCM_INFO_MMAP |
5572763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_MMAP_VALID |
5573763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_NONINTERLEAVED |
5574763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_DOUBLE),
5575763f356cSTakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
5576763f356cSTakashi Iwai 	.rates = (SNDRV_PCM_RATE_32000 |
5577763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_44100 |
5578763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_48000 |
5579763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_64000 |
55803cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
55813cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 ),
5582763f356cSTakashi Iwai 	.rate_min = 32000,
55833cee5a60SRemy Bruno 	.rate_max = 192000,
5584763f356cSTakashi Iwai 	.channels_min = 1,
5585763f356cSTakashi Iwai 	.channels_max = HDSPM_MAX_CHANNELS,
5586763f356cSTakashi Iwai 	.buffer_bytes_max =
5587763f356cSTakashi Iwai 	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
5588763f356cSTakashi Iwai 	.period_bytes_min = (64 * 4),
55890dca1793SAdrian Knoth 	.period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS,
5590763f356cSTakashi Iwai 	.periods_min = 2,
55910dca1793SAdrian Knoth 	.periods_max = 512,
5592763f356cSTakashi Iwai 	.fifo_size = 0
5593763f356cSTakashi Iwai };
5594763f356cSTakashi Iwai 
559598274f07STakashi Iwai static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
5596763f356cSTakashi Iwai 	.info = (SNDRV_PCM_INFO_MMAP |
5597763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_MMAP_VALID |
5598763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_NONINTERLEAVED |
5599763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_SYNC_START),
5600763f356cSTakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
5601763f356cSTakashi Iwai 	.rates = (SNDRV_PCM_RATE_32000 |
5602763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_44100 |
5603763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_48000 |
5604763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_64000 |
56053cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
56063cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
5607763f356cSTakashi Iwai 	.rate_min = 32000,
56083cee5a60SRemy Bruno 	.rate_max = 192000,
5609763f356cSTakashi Iwai 	.channels_min = 1,
5610763f356cSTakashi Iwai 	.channels_max = HDSPM_MAX_CHANNELS,
5611763f356cSTakashi Iwai 	.buffer_bytes_max =
5612763f356cSTakashi Iwai 	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
5613763f356cSTakashi Iwai 	.period_bytes_min = (64 * 4),
56140dca1793SAdrian Knoth 	.period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS,
5615763f356cSTakashi Iwai 	.periods_min = 2,
56160dca1793SAdrian Knoth 	.periods_max = 512,
5617763f356cSTakashi Iwai 	.fifo_size = 0
5618763f356cSTakashi Iwai };
5619763f356cSTakashi Iwai 
56200dca1793SAdrian Knoth static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_old = {
56210dca1793SAdrian Knoth 	.count = ARRAY_SIZE(period_sizes_old),
56220dca1793SAdrian Knoth 	.list = period_sizes_old,
5623763f356cSTakashi Iwai 	.mask = 0
5624763f356cSTakashi Iwai };
5625763f356cSTakashi Iwai 
56260dca1793SAdrian Knoth static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_new = {
56270dca1793SAdrian Knoth 	.count = ARRAY_SIZE(period_sizes_new),
56280dca1793SAdrian Knoth 	.list = period_sizes_new,
56290dca1793SAdrian Knoth 	.mask = 0
56300dca1793SAdrian Knoth };
5631763f356cSTakashi Iwai 
56320dca1793SAdrian Knoth static struct snd_pcm_hw_constraint_list hw_constraints_raydat_io_buffer = {
56330dca1793SAdrian Knoth 	.count = ARRAY_SIZE(raydat_aio_buffer_sizes),
56340dca1793SAdrian Knoth 	.list = raydat_aio_buffer_sizes,
56350dca1793SAdrian Knoth 	.mask = 0
56360dca1793SAdrian Knoth };
56370dca1793SAdrian Knoth 
56380dca1793SAdrian Knoth static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params,
563998274f07STakashi Iwai 					   struct snd_pcm_hw_rule *rule)
5640763f356cSTakashi Iwai {
564198274f07STakashi Iwai 	struct hdspm *hdspm = rule->private;
564298274f07STakashi Iwai 	struct snd_interval *c =
5643763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
564498274f07STakashi Iwai 	struct snd_interval *r =
5645763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5646763f356cSTakashi Iwai 
56470dca1793SAdrian Knoth 	if (r->min > 96000 && r->max <= 192000) {
564898274f07STakashi Iwai 		struct snd_interval t = {
56490dca1793SAdrian Knoth 			.min = hdspm->qs_in_channels,
56500dca1793SAdrian Knoth 			.max = hdspm->qs_in_channels,
56510dca1793SAdrian Knoth 			.integer = 1,
56520dca1793SAdrian Knoth 		};
56530dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
56540dca1793SAdrian Knoth 	} else if (r->min > 48000 && r->max <= 96000) {
56550dca1793SAdrian Knoth 		struct snd_interval t = {
56560dca1793SAdrian Knoth 			.min = hdspm->ds_in_channels,
56570dca1793SAdrian Knoth 			.max = hdspm->ds_in_channels,
5658763f356cSTakashi Iwai 			.integer = 1,
5659763f356cSTakashi Iwai 		};
5660763f356cSTakashi Iwai 		return snd_interval_refine(c, &t);
5661763f356cSTakashi Iwai 	} else if (r->max < 64000) {
566298274f07STakashi Iwai 		struct snd_interval t = {
56630dca1793SAdrian Knoth 			.min = hdspm->ss_in_channels,
56640dca1793SAdrian Knoth 			.max = hdspm->ss_in_channels,
5665763f356cSTakashi Iwai 			.integer = 1,
5666763f356cSTakashi Iwai 		};
5667763f356cSTakashi Iwai 		return snd_interval_refine(c, &t);
5668763f356cSTakashi Iwai 	}
56690dca1793SAdrian Knoth 
5670763f356cSTakashi Iwai 	return 0;
5671763f356cSTakashi Iwai }
5672763f356cSTakashi Iwai 
56730dca1793SAdrian Knoth static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params,
567498274f07STakashi Iwai 					   struct snd_pcm_hw_rule * rule)
5675763f356cSTakashi Iwai {
567698274f07STakashi Iwai 	struct hdspm *hdspm = rule->private;
567798274f07STakashi Iwai 	struct snd_interval *c =
5678763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
567998274f07STakashi Iwai 	struct snd_interval *r =
5680763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5681763f356cSTakashi Iwai 
56820dca1793SAdrian Knoth 	if (r->min > 96000 && r->max <= 192000) {
56830dca1793SAdrian Knoth 		struct snd_interval t = {
56840dca1793SAdrian Knoth 			.min = hdspm->qs_out_channels,
56850dca1793SAdrian Knoth 			.max = hdspm->qs_out_channels,
56860dca1793SAdrian Knoth 			.integer = 1,
56870dca1793SAdrian Knoth 		};
56880dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
56890dca1793SAdrian Knoth 	} else if (r->min > 48000 && r->max <= 96000) {
56900dca1793SAdrian Knoth 		struct snd_interval t = {
56910dca1793SAdrian Knoth 			.min = hdspm->ds_out_channels,
56920dca1793SAdrian Knoth 			.max = hdspm->ds_out_channels,
56930dca1793SAdrian Knoth 			.integer = 1,
56940dca1793SAdrian Knoth 		};
56950dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
56960dca1793SAdrian Knoth 	} else if (r->max < 64000) {
56970dca1793SAdrian Knoth 		struct snd_interval t = {
56980dca1793SAdrian Knoth 			.min = hdspm->ss_out_channels,
56990dca1793SAdrian Knoth 			.max = hdspm->ss_out_channels,
57000dca1793SAdrian Knoth 			.integer = 1,
57010dca1793SAdrian Knoth 		};
57020dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
57030dca1793SAdrian Knoth 	} else {
57040dca1793SAdrian Knoth 	}
57050dca1793SAdrian Knoth 	return 0;
57060dca1793SAdrian Knoth }
57070dca1793SAdrian Knoth 
57080dca1793SAdrian Knoth static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params,
57090dca1793SAdrian Knoth 					   struct snd_pcm_hw_rule * rule)
57100dca1793SAdrian Knoth {
57110dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
57120dca1793SAdrian Knoth 	struct snd_interval *c =
57130dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
57140dca1793SAdrian Knoth 	struct snd_interval *r =
57150dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
57160dca1793SAdrian Knoth 
57170dca1793SAdrian Knoth 	if (c->min >= hdspm->ss_in_channels) {
571898274f07STakashi Iwai 		struct snd_interval t = {
5719763f356cSTakashi Iwai 			.min = 32000,
5720763f356cSTakashi Iwai 			.max = 48000,
5721763f356cSTakashi Iwai 			.integer = 1,
5722763f356cSTakashi Iwai 		};
5723763f356cSTakashi Iwai 		return snd_interval_refine(r, &t);
57240dca1793SAdrian Knoth 	} else if (c->max <= hdspm->qs_in_channels) {
57250dca1793SAdrian Knoth 		struct snd_interval t = {
57260dca1793SAdrian Knoth 			.min = 128000,
57270dca1793SAdrian Knoth 			.max = 192000,
57280dca1793SAdrian Knoth 			.integer = 1,
57290dca1793SAdrian Knoth 		};
57300dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
57310dca1793SAdrian Knoth 	} else if (c->max <= hdspm->ds_in_channels) {
573298274f07STakashi Iwai 		struct snd_interval t = {
5733763f356cSTakashi Iwai 			.min = 64000,
5734763f356cSTakashi Iwai 			.max = 96000,
5735763f356cSTakashi Iwai 			.integer = 1,
5736763f356cSTakashi Iwai 		};
5737763f356cSTakashi Iwai 		return snd_interval_refine(r, &t);
5738763f356cSTakashi Iwai 	}
57390dca1793SAdrian Knoth 
57400dca1793SAdrian Knoth 	return 0;
57410dca1793SAdrian Knoth }
57420dca1793SAdrian Knoth static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params,
57430dca1793SAdrian Knoth 					   struct snd_pcm_hw_rule *rule)
57440dca1793SAdrian Knoth {
57450dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
57460dca1793SAdrian Knoth 	struct snd_interval *c =
57470dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
57480dca1793SAdrian Knoth 	struct snd_interval *r =
57490dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
57500dca1793SAdrian Knoth 
57510dca1793SAdrian Knoth 	if (c->min >= hdspm->ss_out_channels) {
57520dca1793SAdrian Knoth 		struct snd_interval t = {
57530dca1793SAdrian Knoth 			.min = 32000,
57540dca1793SAdrian Knoth 			.max = 48000,
57550dca1793SAdrian Knoth 			.integer = 1,
57560dca1793SAdrian Knoth 		};
57570dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
57580dca1793SAdrian Knoth 	} else if (c->max <= hdspm->qs_out_channels) {
57590dca1793SAdrian Knoth 		struct snd_interval t = {
57600dca1793SAdrian Knoth 			.min = 128000,
57610dca1793SAdrian Knoth 			.max = 192000,
57620dca1793SAdrian Knoth 			.integer = 1,
57630dca1793SAdrian Knoth 		};
57640dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
57650dca1793SAdrian Knoth 	} else if (c->max <= hdspm->ds_out_channels) {
57660dca1793SAdrian Knoth 		struct snd_interval t = {
57670dca1793SAdrian Knoth 			.min = 64000,
57680dca1793SAdrian Knoth 			.max = 96000,
57690dca1793SAdrian Knoth 			.integer = 1,
57700dca1793SAdrian Knoth 		};
57710dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
57720dca1793SAdrian Knoth 	}
57730dca1793SAdrian Knoth 
5774763f356cSTakashi Iwai 	return 0;
5775763f356cSTakashi Iwai }
5776763f356cSTakashi Iwai 
57770dca1793SAdrian Knoth static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params,
5778ffb2c3c0SRemy Bruno 				      struct snd_pcm_hw_rule *rule)
5779ffb2c3c0SRemy Bruno {
5780ffb2c3c0SRemy Bruno 	unsigned int list[3];
5781ffb2c3c0SRemy Bruno 	struct hdspm *hdspm = rule->private;
5782ffb2c3c0SRemy Bruno 	struct snd_interval *c = hw_param_interval(params,
5783ffb2c3c0SRemy Bruno 			SNDRV_PCM_HW_PARAM_CHANNELS);
57840dca1793SAdrian Knoth 
57850dca1793SAdrian Knoth 	list[0] = hdspm->qs_in_channels;
57860dca1793SAdrian Knoth 	list[1] = hdspm->ds_in_channels;
57870dca1793SAdrian Knoth 	list[2] = hdspm->ss_in_channels;
5788ffb2c3c0SRemy Bruno 	return snd_interval_list(c, 3, list, 0);
5789ffb2c3c0SRemy Bruno }
57900dca1793SAdrian Knoth 
57910dca1793SAdrian Knoth static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params,
57920dca1793SAdrian Knoth 				      struct snd_pcm_hw_rule *rule)
57930dca1793SAdrian Knoth {
57940dca1793SAdrian Knoth 	unsigned int list[3];
57950dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
57960dca1793SAdrian Knoth 	struct snd_interval *c = hw_param_interval(params,
57970dca1793SAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS);
57980dca1793SAdrian Knoth 
57990dca1793SAdrian Knoth 	list[0] = hdspm->qs_out_channels;
58000dca1793SAdrian Knoth 	list[1] = hdspm->ds_out_channels;
58010dca1793SAdrian Knoth 	list[2] = hdspm->ss_out_channels;
58020dca1793SAdrian Knoth 	return snd_interval_list(c, 3, list, 0);
5803ffb2c3c0SRemy Bruno }
5804ffb2c3c0SRemy Bruno 
5805ffb2c3c0SRemy Bruno 
5806ef5fa1a4STakashi Iwai static unsigned int hdspm_aes32_sample_rates[] = {
5807ef5fa1a4STakashi Iwai 	32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
5808ef5fa1a4STakashi Iwai };
5809ffb2c3c0SRemy Bruno 
5810ef5fa1a4STakashi Iwai static struct snd_pcm_hw_constraint_list
5811ef5fa1a4STakashi Iwai hdspm_hw_constraints_aes32_sample_rates = {
5812ffb2c3c0SRemy Bruno 	.count = ARRAY_SIZE(hdspm_aes32_sample_rates),
5813ffb2c3c0SRemy Bruno 	.list = hdspm_aes32_sample_rates,
5814ffb2c3c0SRemy Bruno 	.mask = 0
5815ffb2c3c0SRemy Bruno };
5816ffb2c3c0SRemy Bruno 
581798274f07STakashi Iwai static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
5818763f356cSTakashi Iwai {
581998274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
582098274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
5821763f356cSTakashi Iwai 
5822763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5823763f356cSTakashi Iwai 
5824763f356cSTakashi Iwai 	snd_pcm_set_sync(substream);
5825763f356cSTakashi Iwai 
58260dca1793SAdrian Knoth 
5827763f356cSTakashi Iwai 	runtime->hw = snd_hdspm_playback_subinfo;
5828763f356cSTakashi Iwai 
5829763f356cSTakashi Iwai 	if (hdspm->capture_substream == NULL)
5830763f356cSTakashi Iwai 		hdspm_stop_audio(hdspm);
5831763f356cSTakashi Iwai 
5832763f356cSTakashi Iwai 	hdspm->playback_pid = current->pid;
5833763f356cSTakashi Iwai 	hdspm->playback_substream = substream;
5834763f356cSTakashi Iwai 
5835763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5836763f356cSTakashi Iwai 
5837763f356cSTakashi Iwai 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
5838763f356cSTakashi Iwai 
58390dca1793SAdrian Knoth 	switch (hdspm->io_type) {
58400dca1793SAdrian Knoth 	case AIO:
58410dca1793SAdrian Knoth 	case RayDAT:
5842763f356cSTakashi Iwai 		snd_pcm_hw_constraint_list(runtime, 0,
5843763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
58440dca1793SAdrian Knoth 				&hw_constraints_period_sizes_new);
58450dca1793SAdrian Knoth 		snd_pcm_hw_constraint_list(runtime, 0,
58460dca1793SAdrian Knoth 				SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
58470dca1793SAdrian Knoth 				&hw_constraints_raydat_io_buffer);
5848763f356cSTakashi Iwai 
58490dca1793SAdrian Knoth 		break;
58500dca1793SAdrian Knoth 
58510dca1793SAdrian Knoth 	default:
58520dca1793SAdrian Knoth 		snd_pcm_hw_constraint_list(runtime, 0,
58530dca1793SAdrian Knoth 				SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
58540dca1793SAdrian Knoth 				&hw_constraints_period_sizes_old);
58550dca1793SAdrian Knoth 	}
58560dca1793SAdrian Knoth 
58570dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
5858ffb2c3c0SRemy Bruno 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
5859ffb2c3c0SRemy Bruno 				&hdspm_hw_constraints_aes32_sample_rates);
5860ffb2c3c0SRemy Bruno 	} else {
5861763f356cSTakashi Iwai 		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
58620dca1793SAdrian Knoth 				snd_hdspm_hw_rule_rate_out_channels, hdspm,
5863763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_CHANNELS, -1);
5864ffb2c3c0SRemy Bruno 	}
586588fabbfcSAdrian Knoth 
586688fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
586788fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_out_channels, hdspm,
586888fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS, -1);
586988fabbfcSAdrian Knoth 
587088fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
587188fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_out_channels_rate, hdspm,
587288fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_RATE, -1);
587388fabbfcSAdrian Knoth 
5874763f356cSTakashi Iwai 	return 0;
5875763f356cSTakashi Iwai }
5876763f356cSTakashi Iwai 
587798274f07STakashi Iwai static int snd_hdspm_playback_release(struct snd_pcm_substream *substream)
5878763f356cSTakashi Iwai {
587998274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5880763f356cSTakashi Iwai 
5881763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5882763f356cSTakashi Iwai 
5883763f356cSTakashi Iwai 	hdspm->playback_pid = -1;
5884763f356cSTakashi Iwai 	hdspm->playback_substream = NULL;
5885763f356cSTakashi Iwai 
5886763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5887763f356cSTakashi Iwai 
5888763f356cSTakashi Iwai 	return 0;
5889763f356cSTakashi Iwai }
5890763f356cSTakashi Iwai 
5891763f356cSTakashi Iwai 
589298274f07STakashi Iwai static int snd_hdspm_capture_open(struct snd_pcm_substream *substream)
5893763f356cSTakashi Iwai {
589498274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
589598274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
5896763f356cSTakashi Iwai 
5897763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5898763f356cSTakashi Iwai 	snd_pcm_set_sync(substream);
5899763f356cSTakashi Iwai 	runtime->hw = snd_hdspm_capture_subinfo;
5900763f356cSTakashi Iwai 
5901763f356cSTakashi Iwai 	if (hdspm->playback_substream == NULL)
5902763f356cSTakashi Iwai 		hdspm_stop_audio(hdspm);
5903763f356cSTakashi Iwai 
5904763f356cSTakashi Iwai 	hdspm->capture_pid = current->pid;
5905763f356cSTakashi Iwai 	hdspm->capture_substream = substream;
5906763f356cSTakashi Iwai 
5907763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5908763f356cSTakashi Iwai 
5909763f356cSTakashi Iwai 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
59100dca1793SAdrian Knoth 	switch (hdspm->io_type) {
59110dca1793SAdrian Knoth 	case AIO:
59120dca1793SAdrian Knoth 	case RayDAT:
5913763f356cSTakashi Iwai 	  snd_pcm_hw_constraint_list(runtime, 0,
5914763f356cSTakashi Iwai 				     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
59150dca1793SAdrian Knoth 				     &hw_constraints_period_sizes_new);
59160dca1793SAdrian Knoth 	  snd_pcm_hw_constraint_list(runtime, 0,
59170dca1793SAdrian Knoth 				     SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
59180dca1793SAdrian Knoth 				     &hw_constraints_raydat_io_buffer);
59190dca1793SAdrian Knoth 	  break;
59200dca1793SAdrian Knoth 
59210dca1793SAdrian Knoth 	default:
59220dca1793SAdrian Knoth 	  snd_pcm_hw_constraint_list(runtime, 0,
59230dca1793SAdrian Knoth 				     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
59240dca1793SAdrian Knoth 				     &hw_constraints_period_sizes_old);
59250dca1793SAdrian Knoth 	}
59260dca1793SAdrian Knoth 
59270dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
5928ffb2c3c0SRemy Bruno 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
5929ffb2c3c0SRemy Bruno 				&hdspm_hw_constraints_aes32_sample_rates);
5930ffb2c3c0SRemy Bruno 	} else {
5931763f356cSTakashi Iwai 		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
59320dca1793SAdrian Knoth 				snd_hdspm_hw_rule_rate_in_channels, hdspm,
5933763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_CHANNELS, -1);
5934ffb2c3c0SRemy Bruno 	}
593588fabbfcSAdrian Knoth 
593688fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
593788fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_in_channels, hdspm,
593888fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS, -1);
593988fabbfcSAdrian Knoth 
594088fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
594188fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_in_channels_rate, hdspm,
594288fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_RATE, -1);
594388fabbfcSAdrian Knoth 
5944763f356cSTakashi Iwai 	return 0;
5945763f356cSTakashi Iwai }
5946763f356cSTakashi Iwai 
594798274f07STakashi Iwai static int snd_hdspm_capture_release(struct snd_pcm_substream *substream)
5948763f356cSTakashi Iwai {
594998274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5950763f356cSTakashi Iwai 
5951763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5952763f356cSTakashi Iwai 
5953763f356cSTakashi Iwai 	hdspm->capture_pid = -1;
5954763f356cSTakashi Iwai 	hdspm->capture_substream = NULL;
5955763f356cSTakashi Iwai 
5956763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5957763f356cSTakashi Iwai 	return 0;
5958763f356cSTakashi Iwai }
5959763f356cSTakashi Iwai 
59600dca1793SAdrian Knoth static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
5961763f356cSTakashi Iwai {
59620dca1793SAdrian Knoth 	/* we have nothing to initialize but the call is required */
59630dca1793SAdrian Knoth 	return 0;
59640dca1793SAdrian Knoth }
59650dca1793SAdrian Knoth 
59660dca1793SAdrian Knoth static inline int copy_u32_le(void __user *dest, void __iomem *src)
59670dca1793SAdrian Knoth {
59680dca1793SAdrian Knoth 	u32 val = readl(src);
59690dca1793SAdrian Knoth 	return copy_to_user(dest, &val, 4);
59700dca1793SAdrian Knoth }
59710dca1793SAdrian Knoth 
59720dca1793SAdrian Knoth static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
59730dca1793SAdrian Knoth 		unsigned int cmd, unsigned long __user arg)
59740dca1793SAdrian Knoth {
59750dca1793SAdrian Knoth 	void __user *argp = (void __user *)arg;
5976ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = hw->private_data;
597798274f07STakashi Iwai 	struct hdspm_mixer_ioctl mixer;
59780dca1793SAdrian Knoth 	struct hdspm_config info;
59790dca1793SAdrian Knoth 	struct hdspm_status status;
598098274f07STakashi Iwai 	struct hdspm_version hdspm_version;
5981730a5865SJaroslav Kysela 	struct hdspm_peak_rms *levels;
59820dca1793SAdrian Knoth 	struct hdspm_ltc ltc;
59830dca1793SAdrian Knoth 	unsigned int statusregister;
59840dca1793SAdrian Knoth 	long unsigned int s;
59850dca1793SAdrian Knoth 	int i = 0;
5986763f356cSTakashi Iwai 
5987763f356cSTakashi Iwai 	switch (cmd) {
5988763f356cSTakashi Iwai 
5989763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS:
5990730a5865SJaroslav Kysela 		levels = &hdspm->peak_rms;
59910dca1793SAdrian Knoth 		for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
5992730a5865SJaroslav Kysela 			levels->input_peaks[i] =
59930dca1793SAdrian Knoth 				readl(hdspm->iobase +
59940dca1793SAdrian Knoth 						HDSPM_MADI_INPUT_PEAK + i*4);
5995730a5865SJaroslav Kysela 			levels->playback_peaks[i] =
59960dca1793SAdrian Knoth 				readl(hdspm->iobase +
59970dca1793SAdrian Knoth 						HDSPM_MADI_PLAYBACK_PEAK + i*4);
5998730a5865SJaroslav Kysela 			levels->output_peaks[i] =
59990dca1793SAdrian Knoth 				readl(hdspm->iobase +
60000dca1793SAdrian Knoth 						HDSPM_MADI_OUTPUT_PEAK + i*4);
60010dca1793SAdrian Knoth 
6002730a5865SJaroslav Kysela 			levels->input_rms[i] =
60030dca1793SAdrian Knoth 				((uint64_t) readl(hdspm->iobase +
60040dca1793SAdrian Knoth 					HDSPM_MADI_INPUT_RMS_H + i*4) << 32) |
60050dca1793SAdrian Knoth 				(uint64_t) readl(hdspm->iobase +
60060dca1793SAdrian Knoth 						HDSPM_MADI_INPUT_RMS_L + i*4);
6007730a5865SJaroslav Kysela 			levels->playback_rms[i] =
60080dca1793SAdrian Knoth 				((uint64_t)readl(hdspm->iobase +
60090dca1793SAdrian Knoth 					HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) |
60100dca1793SAdrian Knoth 				(uint64_t)readl(hdspm->iobase +
60110dca1793SAdrian Knoth 					HDSPM_MADI_PLAYBACK_RMS_L + i*4);
6012730a5865SJaroslav Kysela 			levels->output_rms[i] =
60130dca1793SAdrian Knoth 				((uint64_t)readl(hdspm->iobase +
60140dca1793SAdrian Knoth 					HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) |
60150dca1793SAdrian Knoth 				(uint64_t)readl(hdspm->iobase +
60160dca1793SAdrian Knoth 						HDSPM_MADI_OUTPUT_RMS_L + i*4);
60170dca1793SAdrian Knoth 		}
60180dca1793SAdrian Knoth 
60190dca1793SAdrian Knoth 		if (hdspm->system_sample_rate > 96000) {
6020730a5865SJaroslav Kysela 			levels->speed = qs;
60210dca1793SAdrian Knoth 		} else if (hdspm->system_sample_rate > 48000) {
6022730a5865SJaroslav Kysela 			levels->speed = ds;
60230dca1793SAdrian Knoth 		} else {
6024730a5865SJaroslav Kysela 			levels->speed = ss;
60250dca1793SAdrian Knoth 		}
6026730a5865SJaroslav Kysela 		levels->status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
60270dca1793SAdrian Knoth 
6028730a5865SJaroslav Kysela 		s = copy_to_user(argp, levels, sizeof(struct hdspm_peak_rms));
60290dca1793SAdrian Knoth 		if (0 != s) {
60300dca1793SAdrian Knoth 			/* snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu
60310dca1793SAdrian Knoth 			 [Levels]\n", sizeof(struct hdspm_peak_rms), s);
6032ef5fa1a4STakashi Iwai 			 */
6033763f356cSTakashi Iwai 			return -EFAULT;
60340dca1793SAdrian Knoth 		}
60350dca1793SAdrian Knoth 		break;
60360dca1793SAdrian Knoth 
60370dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_LTC:
60380dca1793SAdrian Knoth 		ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
60390dca1793SAdrian Knoth 		i = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
60400dca1793SAdrian Knoth 		if (i & HDSPM_TCO1_LTC_Input_valid) {
60410dca1793SAdrian Knoth 			switch (i & (HDSPM_TCO1_LTC_Format_LSB |
60420dca1793SAdrian Knoth 				HDSPM_TCO1_LTC_Format_MSB)) {
60430dca1793SAdrian Knoth 			case 0:
60440dca1793SAdrian Knoth 				ltc.format = fps_24;
60450dca1793SAdrian Knoth 				break;
60460dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_LSB:
60470dca1793SAdrian Knoth 				ltc.format = fps_25;
60480dca1793SAdrian Knoth 				break;
60490dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_MSB:
60500dca1793SAdrian Knoth 				ltc.format = fps_2997;
60510dca1793SAdrian Knoth 				break;
60520dca1793SAdrian Knoth 			default:
60530dca1793SAdrian Knoth 				ltc.format = 30;
60540dca1793SAdrian Knoth 				break;
60550dca1793SAdrian Knoth 			}
60560dca1793SAdrian Knoth 			if (i & HDSPM_TCO1_set_drop_frame_flag) {
60570dca1793SAdrian Knoth 				ltc.frame = drop_frame;
60580dca1793SAdrian Knoth 			} else {
60590dca1793SAdrian Knoth 				ltc.frame = full_frame;
60600dca1793SAdrian Knoth 			}
60610dca1793SAdrian Knoth 		} else {
60620dca1793SAdrian Knoth 			ltc.format = format_invalid;
60630dca1793SAdrian Knoth 			ltc.frame = frame_invalid;
60640dca1793SAdrian Knoth 		}
60650dca1793SAdrian Knoth 		if (i & HDSPM_TCO1_Video_Input_Format_NTSC) {
60660dca1793SAdrian Knoth 			ltc.input_format = ntsc;
60670dca1793SAdrian Knoth 		} else if (i & HDSPM_TCO1_Video_Input_Format_PAL) {
60680dca1793SAdrian Knoth 			ltc.input_format = pal;
60690dca1793SAdrian Knoth 		} else {
60700dca1793SAdrian Knoth 			ltc.input_format = no_video;
60710dca1793SAdrian Knoth 		}
60720dca1793SAdrian Knoth 
60730dca1793SAdrian Knoth 		s = copy_to_user(argp, &ltc, sizeof(struct hdspm_ltc));
60740dca1793SAdrian Knoth 		if (0 != s) {
60750dca1793SAdrian Knoth 			/*
60760dca1793SAdrian Knoth 			 snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */
60770dca1793SAdrian Knoth 			return -EFAULT;
60780dca1793SAdrian Knoth 		}
6079763f356cSTakashi Iwai 
6080763f356cSTakashi Iwai 		break;
6081763f356cSTakashi Iwai 
60820dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_CONFIG:
6083763f356cSTakashi Iwai 
6084*4ab69a2bSAdrian Knoth 		memset(&info, 0, sizeof(info));
6085763f356cSTakashi Iwai 		spin_lock_irq(&hdspm->lock);
6086ef5fa1a4STakashi Iwai 		info.pref_sync_ref = hdspm_pref_sync_ref(hdspm);
6087ef5fa1a4STakashi Iwai 		info.wordclock_sync_check = hdspm_wc_sync_check(hdspm);
6088763f356cSTakashi Iwai 
6089763f356cSTakashi Iwai 		info.system_sample_rate = hdspm->system_sample_rate;
6090763f356cSTakashi Iwai 		info.autosync_sample_rate =
6091763f356cSTakashi Iwai 			hdspm_external_sample_rate(hdspm);
6092ef5fa1a4STakashi Iwai 		info.system_clock_mode = hdspm_system_clock_mode(hdspm);
6093ef5fa1a4STakashi Iwai 		info.clock_source = hdspm_clock_source(hdspm);
6094ef5fa1a4STakashi Iwai 		info.autosync_ref = hdspm_autosync_ref(hdspm);
6095ef5fa1a4STakashi Iwai 		info.line_out = hdspm_line_out(hdspm);
6096763f356cSTakashi Iwai 		info.passthru = 0;
6097763f356cSTakashi Iwai 		spin_unlock_irq(&hdspm->lock);
6098763f356cSTakashi Iwai 		if (copy_to_user((void __user *) arg, &info, sizeof(info)))
6099763f356cSTakashi Iwai 			return -EFAULT;
6100763f356cSTakashi Iwai 		break;
6101763f356cSTakashi Iwai 
61020dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_STATUS:
61030dca1793SAdrian Knoth 		status.card_type = hdspm->io_type;
61040dca1793SAdrian Knoth 
61050dca1793SAdrian Knoth 		status.autosync_source = hdspm_autosync_ref(hdspm);
61060dca1793SAdrian Knoth 
61070dca1793SAdrian Knoth 		status.card_clock = 110069313433624ULL;
61080dca1793SAdrian Knoth 		status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
61090dca1793SAdrian Knoth 
61100dca1793SAdrian Knoth 		switch (hdspm->io_type) {
61110dca1793SAdrian Knoth 		case MADI:
61120dca1793SAdrian Knoth 		case MADIface:
61130dca1793SAdrian Knoth 			status.card_specific.madi.sync_wc =
61140dca1793SAdrian Knoth 				hdspm_wc_sync_check(hdspm);
61150dca1793SAdrian Knoth 			status.card_specific.madi.sync_madi =
61160dca1793SAdrian Knoth 				hdspm_madi_sync_check(hdspm);
61170dca1793SAdrian Knoth 			status.card_specific.madi.sync_tco =
61180dca1793SAdrian Knoth 				hdspm_tco_sync_check(hdspm);
61190dca1793SAdrian Knoth 			status.card_specific.madi.sync_in =
61200dca1793SAdrian Knoth 				hdspm_sync_in_sync_check(hdspm);
61210dca1793SAdrian Knoth 
61220dca1793SAdrian Knoth 			statusregister =
61230dca1793SAdrian Knoth 				hdspm_read(hdspm, HDSPM_statusRegister);
61240dca1793SAdrian Knoth 			status.card_specific.madi.madi_input =
61250dca1793SAdrian Knoth 				(statusregister & HDSPM_AB_int) ? 1 : 0;
61260dca1793SAdrian Knoth 			status.card_specific.madi.channel_format =
61270dca1793SAdrian Knoth 				(statusregister & HDSPM_TX_64ch) ? 1 : 0;
61280dca1793SAdrian Knoth 			/* TODO: Mac driver sets it when f_s>48kHz */
61290dca1793SAdrian Knoth 			status.card_specific.madi.frame_format = 0;
61300dca1793SAdrian Knoth 
61310dca1793SAdrian Knoth 		default:
61320dca1793SAdrian Knoth 			break;
61330dca1793SAdrian Knoth 		}
61340dca1793SAdrian Knoth 
61350dca1793SAdrian Knoth 		if (copy_to_user((void __user *) arg, &status, sizeof(status)))
61360dca1793SAdrian Knoth 			return -EFAULT;
61370dca1793SAdrian Knoth 
61380dca1793SAdrian Knoth 
61390dca1793SAdrian Knoth 		break;
61400dca1793SAdrian Knoth 
6141763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_VERSION:
61420dca1793SAdrian Knoth 		hdspm_version.card_type = hdspm->io_type;
61430dca1793SAdrian Knoth 		strncpy(hdspm_version.cardname, hdspm->card_name,
61440dca1793SAdrian Knoth 				sizeof(hdspm_version.cardname));
61450dca1793SAdrian Knoth 		hdspm_version.serial = (hdspm_read(hdspm,
61460dca1793SAdrian Knoth 					HDSPM_midiStatusIn0)>>8) & 0xFFFFFF;
6147763f356cSTakashi Iwai 		hdspm_version.firmware_rev = hdspm->firmware_rev;
61480dca1793SAdrian Knoth 		hdspm_version.addons = 0;
61490dca1793SAdrian Knoth 		if (hdspm->tco)
61500dca1793SAdrian Knoth 			hdspm_version.addons |= HDSPM_ADDON_TCO;
61510dca1793SAdrian Knoth 
6152763f356cSTakashi Iwai 		if (copy_to_user((void __user *) arg, &hdspm_version,
6153763f356cSTakashi Iwai 					sizeof(hdspm_version)))
6154763f356cSTakashi Iwai 			return -EFAULT;
6155763f356cSTakashi Iwai 		break;
6156763f356cSTakashi Iwai 
6157763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_MIXER:
6158763f356cSTakashi Iwai 		if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer)))
6159763f356cSTakashi Iwai 			return -EFAULT;
6160ef5fa1a4STakashi Iwai 		if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer,
6161ef5fa1a4STakashi Iwai 					sizeof(struct hdspm_mixer)))
6162763f356cSTakashi Iwai 			return -EFAULT;
6163763f356cSTakashi Iwai 		break;
6164763f356cSTakashi Iwai 
6165763f356cSTakashi Iwai 	default:
6166763f356cSTakashi Iwai 		return -EINVAL;
6167763f356cSTakashi Iwai 	}
6168763f356cSTakashi Iwai 	return 0;
6169763f356cSTakashi Iwai }
6170763f356cSTakashi Iwai 
617198274f07STakashi Iwai static struct snd_pcm_ops snd_hdspm_playback_ops = {
6172763f356cSTakashi Iwai 	.open = snd_hdspm_playback_open,
6173763f356cSTakashi Iwai 	.close = snd_hdspm_playback_release,
6174763f356cSTakashi Iwai 	.ioctl = snd_hdspm_ioctl,
6175763f356cSTakashi Iwai 	.hw_params = snd_hdspm_hw_params,
6176763f356cSTakashi Iwai 	.hw_free = snd_hdspm_hw_free,
6177763f356cSTakashi Iwai 	.prepare = snd_hdspm_prepare,
6178763f356cSTakashi Iwai 	.trigger = snd_hdspm_trigger,
6179763f356cSTakashi Iwai 	.pointer = snd_hdspm_hw_pointer,
6180763f356cSTakashi Iwai 	.page = snd_pcm_sgbuf_ops_page,
6181763f356cSTakashi Iwai };
6182763f356cSTakashi Iwai 
618398274f07STakashi Iwai static struct snd_pcm_ops snd_hdspm_capture_ops = {
6184763f356cSTakashi Iwai 	.open = snd_hdspm_capture_open,
6185763f356cSTakashi Iwai 	.close = snd_hdspm_capture_release,
6186763f356cSTakashi Iwai 	.ioctl = snd_hdspm_ioctl,
6187763f356cSTakashi Iwai 	.hw_params = snd_hdspm_hw_params,
6188763f356cSTakashi Iwai 	.hw_free = snd_hdspm_hw_free,
6189763f356cSTakashi Iwai 	.prepare = snd_hdspm_prepare,
6190763f356cSTakashi Iwai 	.trigger = snd_hdspm_trigger,
6191763f356cSTakashi Iwai 	.pointer = snd_hdspm_hw_pointer,
6192763f356cSTakashi Iwai 	.page = snd_pcm_sgbuf_ops_page,
6193763f356cSTakashi Iwai };
6194763f356cSTakashi Iwai 
619598274f07STakashi Iwai static int __devinit snd_hdspm_create_hwdep(struct snd_card *card,
619698274f07STakashi Iwai 					    struct hdspm * hdspm)
6197763f356cSTakashi Iwai {
619898274f07STakashi Iwai 	struct snd_hwdep *hw;
6199763f356cSTakashi Iwai 	int err;
6200763f356cSTakashi Iwai 
6201ef5fa1a4STakashi Iwai 	err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw);
6202ef5fa1a4STakashi Iwai 	if (err < 0)
6203763f356cSTakashi Iwai 		return err;
6204763f356cSTakashi Iwai 
6205763f356cSTakashi Iwai 	hdspm->hwdep = hw;
6206763f356cSTakashi Iwai 	hw->private_data = hdspm;
6207763f356cSTakashi Iwai 	strcpy(hw->name, "HDSPM hwdep interface");
6208763f356cSTakashi Iwai 
62090dca1793SAdrian Knoth 	hw->ops.open = snd_hdspm_hwdep_dummy_op;
6210763f356cSTakashi Iwai 	hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
62110dca1793SAdrian Knoth 	hw->ops.release = snd_hdspm_hwdep_dummy_op;
6212763f356cSTakashi Iwai 
6213763f356cSTakashi Iwai 	return 0;
6214763f356cSTakashi Iwai }
6215763f356cSTakashi Iwai 
6216763f356cSTakashi Iwai 
6217763f356cSTakashi Iwai /*------------------------------------------------------------
6218763f356cSTakashi Iwai    memory interface
6219763f356cSTakashi Iwai  ------------------------------------------------------------*/
622098274f07STakashi Iwai static int __devinit snd_hdspm_preallocate_memory(struct hdspm *hdspm)
6221763f356cSTakashi Iwai {
6222763f356cSTakashi Iwai 	int err;
622398274f07STakashi Iwai 	struct snd_pcm *pcm;
6224763f356cSTakashi Iwai 	size_t wanted;
6225763f356cSTakashi Iwai 
6226763f356cSTakashi Iwai 	pcm = hdspm->pcm;
6227763f356cSTakashi Iwai 
62283cee5a60SRemy Bruno 	wanted = HDSPM_DMA_AREA_BYTES;
6229763f356cSTakashi Iwai 
6230ef5fa1a4STakashi Iwai 	err =
6231763f356cSTakashi Iwai 	     snd_pcm_lib_preallocate_pages_for_all(pcm,
6232763f356cSTakashi Iwai 						   SNDRV_DMA_TYPE_DEV_SG,
6233763f356cSTakashi Iwai 						   snd_dma_pci_data(hdspm->pci),
6234763f356cSTakashi Iwai 						   wanted,
6235ef5fa1a4STakashi Iwai 						   wanted);
6236ef5fa1a4STakashi Iwai 	if (err < 0) {
6237e2eba3e7SAndrew Morton 		snd_printdd("Could not preallocate %zd Bytes\n", wanted);
6238763f356cSTakashi Iwai 
6239763f356cSTakashi Iwai 		return err;
6240763f356cSTakashi Iwai 	} else
6241e2eba3e7SAndrew Morton 		snd_printdd(" Preallocated %zd Bytes\n", wanted);
6242763f356cSTakashi Iwai 
6243763f356cSTakashi Iwai 	return 0;
6244763f356cSTakashi Iwai }
6245763f356cSTakashi Iwai 
62460dca1793SAdrian Knoth 
624777a23f26STakashi Iwai static void hdspm_set_sgbuf(struct hdspm *hdspm,
624877a23f26STakashi Iwai 			    struct snd_pcm_substream *substream,
6249763f356cSTakashi Iwai 			     unsigned int reg, int channels)
6250763f356cSTakashi Iwai {
6251763f356cSTakashi Iwai 	int i;
62520dca1793SAdrian Knoth 
62530dca1793SAdrian Knoth 	/* continuous memory segment */
6254763f356cSTakashi Iwai 	for (i = 0; i < (channels * 16); i++)
6255763f356cSTakashi Iwai 		hdspm_write(hdspm, reg + 4 * i,
625677a23f26STakashi Iwai 				snd_pcm_sgbuf_get_addr(substream, 4096 * i));
6257763f356cSTakashi Iwai }
6258763f356cSTakashi Iwai 
62590dca1793SAdrian Knoth 
6260763f356cSTakashi Iwai /* ------------- ALSA Devices ---------------------------- */
626198274f07STakashi Iwai static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
626298274f07STakashi Iwai 					  struct hdspm *hdspm)
6263763f356cSTakashi Iwai {
626498274f07STakashi Iwai 	struct snd_pcm *pcm;
6265763f356cSTakashi Iwai 	int err;
6266763f356cSTakashi Iwai 
6267ef5fa1a4STakashi Iwai 	err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm);
6268ef5fa1a4STakashi Iwai 	if (err < 0)
6269763f356cSTakashi Iwai 		return err;
6270763f356cSTakashi Iwai 
6271763f356cSTakashi Iwai 	hdspm->pcm = pcm;
6272763f356cSTakashi Iwai 	pcm->private_data = hdspm;
6273763f356cSTakashi Iwai 	strcpy(pcm->name, hdspm->card_name);
6274763f356cSTakashi Iwai 
6275763f356cSTakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
6276763f356cSTakashi Iwai 			&snd_hdspm_playback_ops);
6277763f356cSTakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
6278763f356cSTakashi Iwai 			&snd_hdspm_capture_ops);
6279763f356cSTakashi Iwai 
6280763f356cSTakashi Iwai 	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
6281763f356cSTakashi Iwai 
6282ef5fa1a4STakashi Iwai 	err = snd_hdspm_preallocate_memory(hdspm);
6283ef5fa1a4STakashi Iwai 	if (err < 0)
6284763f356cSTakashi Iwai 		return err;
6285763f356cSTakashi Iwai 
6286763f356cSTakashi Iwai 	return 0;
6287763f356cSTakashi Iwai }
6288763f356cSTakashi Iwai 
628998274f07STakashi Iwai static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm)
6290763f356cSTakashi Iwai {
6291763f356cSTakashi Iwai 	snd_hdspm_flush_midi_input(hdspm, 0);
6292763f356cSTakashi Iwai 	snd_hdspm_flush_midi_input(hdspm, 1);
6293763f356cSTakashi Iwai }
6294763f356cSTakashi Iwai 
629598274f07STakashi Iwai static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
629698274f07STakashi Iwai 						   struct hdspm * hdspm)
6297763f356cSTakashi Iwai {
62980dca1793SAdrian Knoth 	int err, i;
6299763f356cSTakashi Iwai 
6300763f356cSTakashi Iwai 	snd_printdd("Create card...\n");
6301ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_pcm(card, hdspm);
6302ef5fa1a4STakashi Iwai 	if (err < 0)
6303763f356cSTakashi Iwai 		return err;
6304763f356cSTakashi Iwai 
63050dca1793SAdrian Knoth 	i = 0;
63060dca1793SAdrian Knoth 	while (i < hdspm->midiPorts) {
63070dca1793SAdrian Knoth 		err = snd_hdspm_create_midi(card, hdspm, i);
63080dca1793SAdrian Knoth 		if (err < 0) {
6309763f356cSTakashi Iwai 			return err;
63100dca1793SAdrian Knoth 		}
63110dca1793SAdrian Knoth 		i++;
63120dca1793SAdrian Knoth 	}
6313763f356cSTakashi Iwai 
6314ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_controls(card, hdspm);
6315ef5fa1a4STakashi Iwai 	if (err < 0)
6316763f356cSTakashi Iwai 		return err;
6317763f356cSTakashi Iwai 
6318ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_hwdep(card, hdspm);
6319ef5fa1a4STakashi Iwai 	if (err < 0)
6320763f356cSTakashi Iwai 		return err;
6321763f356cSTakashi Iwai 
6322763f356cSTakashi Iwai 	snd_printdd("proc init...\n");
6323763f356cSTakashi Iwai 	snd_hdspm_proc_init(hdspm);
6324763f356cSTakashi Iwai 
6325763f356cSTakashi Iwai 	hdspm->system_sample_rate = -1;
6326763f356cSTakashi Iwai 	hdspm->last_external_sample_rate = -1;
6327763f356cSTakashi Iwai 	hdspm->last_internal_sample_rate = -1;
6328763f356cSTakashi Iwai 	hdspm->playback_pid = -1;
6329763f356cSTakashi Iwai 	hdspm->capture_pid = -1;
6330763f356cSTakashi Iwai 	hdspm->capture_substream = NULL;
6331763f356cSTakashi Iwai 	hdspm->playback_substream = NULL;
6332763f356cSTakashi Iwai 
6333763f356cSTakashi Iwai 	snd_printdd("Set defaults...\n");
6334ef5fa1a4STakashi Iwai 	err = snd_hdspm_set_defaults(hdspm);
6335ef5fa1a4STakashi Iwai 	if (err < 0)
6336763f356cSTakashi Iwai 		return err;
6337763f356cSTakashi Iwai 
6338763f356cSTakashi Iwai 	snd_printdd("Update mixer controls...\n");
6339763f356cSTakashi Iwai 	hdspm_update_simple_mixer_controls(hdspm);
6340763f356cSTakashi Iwai 
6341763f356cSTakashi Iwai 	snd_printdd("Initializeing complete ???\n");
6342763f356cSTakashi Iwai 
6343ef5fa1a4STakashi Iwai 	err = snd_card_register(card);
6344ef5fa1a4STakashi Iwai 	if (err < 0) {
6345763f356cSTakashi Iwai 		snd_printk(KERN_ERR "HDSPM: error registering card\n");
6346763f356cSTakashi Iwai 		return err;
6347763f356cSTakashi Iwai 	}
6348763f356cSTakashi Iwai 
6349763f356cSTakashi Iwai 	snd_printdd("... yes now\n");
6350763f356cSTakashi Iwai 
6351763f356cSTakashi Iwai 	return 0;
6352763f356cSTakashi Iwai }
6353763f356cSTakashi Iwai 
6354ef5fa1a4STakashi Iwai static int __devinit snd_hdspm_create(struct snd_card *card,
63550dca1793SAdrian Knoth 		struct hdspm *hdspm) {
63560dca1793SAdrian Knoth 
6357763f356cSTakashi Iwai 	struct pci_dev *pci = hdspm->pci;
6358763f356cSTakashi Iwai 	int err;
6359763f356cSTakashi Iwai 	unsigned long io_extent;
6360763f356cSTakashi Iwai 
6361763f356cSTakashi Iwai 	hdspm->irq = -1;
6362763f356cSTakashi Iwai 	hdspm->card = card;
6363763f356cSTakashi Iwai 
6364763f356cSTakashi Iwai 	spin_lock_init(&hdspm->lock);
6365763f356cSTakashi Iwai 
6366763f356cSTakashi Iwai 	pci_read_config_word(hdspm->pci,
6367763f356cSTakashi Iwai 			PCI_CLASS_REVISION, &hdspm->firmware_rev);
6368763f356cSTakashi Iwai 
6369763f356cSTakashi Iwai 	strcpy(card->mixername, "Xilinx FPGA");
63703cee5a60SRemy Bruno 	strcpy(card->driver, "HDSPM");
63710dca1793SAdrian Knoth 
63720dca1793SAdrian Knoth 	switch (hdspm->firmware_rev) {
63730dca1793SAdrian Knoth 	case HDSPM_MADI_REV:
63740dca1793SAdrian Knoth 		hdspm->io_type = MADI;
63750dca1793SAdrian Knoth 		hdspm->card_name = "RME MADI";
63760dca1793SAdrian Knoth 		hdspm->midiPorts = 3;
63770dca1793SAdrian Knoth 		break;
63780dca1793SAdrian Knoth 	case HDSPM_RAYDAT_REV:
63790dca1793SAdrian Knoth 		hdspm->io_type = RayDAT;
63800dca1793SAdrian Knoth 		hdspm->card_name = "RME RayDAT";
63810dca1793SAdrian Knoth 		hdspm->midiPorts = 2;
63820dca1793SAdrian Knoth 		break;
63830dca1793SAdrian Knoth 	case HDSPM_AIO_REV:
63840dca1793SAdrian Knoth 		hdspm->io_type = AIO;
63850dca1793SAdrian Knoth 		hdspm->card_name = "RME AIO";
63860dca1793SAdrian Knoth 		hdspm->midiPorts = 1;
63870dca1793SAdrian Knoth 		break;
63880dca1793SAdrian Knoth 	case HDSPM_MADIFACE_REV:
63890dca1793SAdrian Knoth 		hdspm->io_type = MADIface;
63900dca1793SAdrian Knoth 		hdspm->card_name = "RME MADIface";
63910dca1793SAdrian Knoth 		hdspm->midiPorts = 1;
63920dca1793SAdrian Knoth 		break;
63930dca1793SAdrian Knoth 	case HDSPM_AES_REV:
63940dca1793SAdrian Knoth 		hdspm->io_type = AES32;
63950dca1793SAdrian Knoth 		hdspm->card_name = "RME AES32";
63960dca1793SAdrian Knoth 		hdspm->midiPorts = 2;
63970dca1793SAdrian Knoth 		break;
63983cee5a60SRemy Bruno 	}
6399763f356cSTakashi Iwai 
6400ef5fa1a4STakashi Iwai 	err = pci_enable_device(pci);
6401ef5fa1a4STakashi Iwai 	if (err < 0)
6402763f356cSTakashi Iwai 		return err;
6403763f356cSTakashi Iwai 
6404763f356cSTakashi Iwai 	pci_set_master(hdspm->pci);
6405763f356cSTakashi Iwai 
6406ef5fa1a4STakashi Iwai 	err = pci_request_regions(pci, "hdspm");
6407ef5fa1a4STakashi Iwai 	if (err < 0)
6408763f356cSTakashi Iwai 		return err;
6409763f356cSTakashi Iwai 
6410763f356cSTakashi Iwai 	hdspm->port = pci_resource_start(pci, 0);
6411763f356cSTakashi Iwai 	io_extent = pci_resource_len(pci, 0);
6412763f356cSTakashi Iwai 
6413763f356cSTakashi Iwai 	snd_printdd("grabbed memory region 0x%lx-0x%lx\n",
6414763f356cSTakashi Iwai 			hdspm->port, hdspm->port + io_extent - 1);
6415763f356cSTakashi Iwai 
6416ef5fa1a4STakashi Iwai 	hdspm->iobase = ioremap_nocache(hdspm->port, io_extent);
6417ef5fa1a4STakashi Iwai 	if (!hdspm->iobase) {
6418ef5fa1a4STakashi Iwai 		snd_printk(KERN_ERR "HDSPM: "
6419ef5fa1a4STakashi Iwai 				"unable to remap region 0x%lx-0x%lx\n",
6420763f356cSTakashi Iwai 				hdspm->port, hdspm->port + io_extent - 1);
6421763f356cSTakashi Iwai 		return -EBUSY;
6422763f356cSTakashi Iwai 	}
6423763f356cSTakashi Iwai 	snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n",
6424763f356cSTakashi Iwai 			(unsigned long)hdspm->iobase, hdspm->port,
6425763f356cSTakashi Iwai 			hdspm->port + io_extent - 1);
6426763f356cSTakashi Iwai 
6427763f356cSTakashi Iwai 	if (request_irq(pci->irq, snd_hdspm_interrupt,
6428437a5a46STakashi Iwai 				IRQF_SHARED, "hdspm", hdspm)) {
6429763f356cSTakashi Iwai 		snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
6430763f356cSTakashi Iwai 		return -EBUSY;
6431763f356cSTakashi Iwai 	}
6432763f356cSTakashi Iwai 
6433763f356cSTakashi Iwai 	snd_printdd("use IRQ %d\n", pci->irq);
6434763f356cSTakashi Iwai 
6435763f356cSTakashi Iwai 	hdspm->irq = pci->irq;
6436763f356cSTakashi Iwai 
6437e2eba3e7SAndrew Morton 	snd_printdd("kmalloc Mixer memory of %zd Bytes\n",
643898274f07STakashi Iwai 			sizeof(struct hdspm_mixer));
6439ef5fa1a4STakashi Iwai 	hdspm->mixer = kzalloc(sizeof(struct hdspm_mixer), GFP_KERNEL);
6440ef5fa1a4STakashi Iwai 	if (!hdspm->mixer) {
6441ef5fa1a4STakashi Iwai 		snd_printk(KERN_ERR "HDSPM: "
6442ef5fa1a4STakashi Iwai 				"unable to kmalloc Mixer memory of %d Bytes\n",
644398274f07STakashi Iwai 				(int)sizeof(struct hdspm_mixer));
6444763f356cSTakashi Iwai 		return err;
6445763f356cSTakashi Iwai 	}
6446763f356cSTakashi Iwai 
64470dca1793SAdrian Knoth 	hdspm->port_names_in = NULL;
64480dca1793SAdrian Knoth 	hdspm->port_names_out = NULL;
64490dca1793SAdrian Knoth 
64500dca1793SAdrian Knoth 	switch (hdspm->io_type) {
64510dca1793SAdrian Knoth 	case AES32:
6452432d2500SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels = 16;
6453432d2500SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels = 16;
6454432d2500SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels = 16;
6455432d2500SAdrian Knoth 
6456432d2500SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
6457432d2500SAdrian Knoth 			channel_map_aes32;
6458432d2500SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
6459432d2500SAdrian Knoth 			channel_map_aes32;
6460432d2500SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
6461432d2500SAdrian Knoth 			channel_map_aes32;
6462432d2500SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
6463432d2500SAdrian Knoth 			texts_ports_aes32;
6464432d2500SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
6465432d2500SAdrian Knoth 			texts_ports_aes32;
6466432d2500SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
6467432d2500SAdrian Knoth 			texts_ports_aes32;
6468432d2500SAdrian Knoth 
6469432d2500SAdrian Knoth 		hdspm->max_channels_out = hdspm->max_channels_in = 16;
6470432d2500SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_out =
6471432d2500SAdrian Knoth 			texts_ports_aes32;
6472432d2500SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_out =
6473432d2500SAdrian Knoth 			channel_map_aes32;
6474432d2500SAdrian Knoth 
64750dca1793SAdrian Knoth 		break;
64760dca1793SAdrian Knoth 
64770dca1793SAdrian Knoth 	case MADI:
64780dca1793SAdrian Knoth 	case MADIface:
64790dca1793SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels =
64800dca1793SAdrian Knoth 			MADI_SS_CHANNELS;
64810dca1793SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels =
64820dca1793SAdrian Knoth 			MADI_DS_CHANNELS;
64830dca1793SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels =
64840dca1793SAdrian Knoth 			MADI_QS_CHANNELS;
64850dca1793SAdrian Knoth 
64860dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
64870dca1793SAdrian Knoth 			channel_map_unity_ss;
648801e96078SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
64890dca1793SAdrian Knoth 			channel_map_unity_ss;
649001e96078SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
64910dca1793SAdrian Knoth 			channel_map_unity_ss;
64920dca1793SAdrian Knoth 
64930dca1793SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
64940dca1793SAdrian Knoth 			texts_ports_madi;
64950dca1793SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
64960dca1793SAdrian Knoth 			texts_ports_madi;
64970dca1793SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
64980dca1793SAdrian Knoth 			texts_ports_madi;
64990dca1793SAdrian Knoth 		break;
65000dca1793SAdrian Knoth 
65010dca1793SAdrian Knoth 	case AIO:
65020dca1793SAdrian Knoth 		if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
65030dca1793SAdrian Knoth 			snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n");
65040dca1793SAdrian Knoth 		}
65050dca1793SAdrian Knoth 
65060dca1793SAdrian Knoth 		hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
65070dca1793SAdrian Knoth 		hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
65080dca1793SAdrian Knoth 		hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
65090dca1793SAdrian Knoth 		hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS;
65100dca1793SAdrian Knoth 		hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
65110dca1793SAdrian Knoth 		hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
65120dca1793SAdrian Knoth 
65130dca1793SAdrian Knoth 		hdspm->channel_map_out_ss = channel_map_aio_out_ss;
65140dca1793SAdrian Knoth 		hdspm->channel_map_out_ds = channel_map_aio_out_ds;
65150dca1793SAdrian Knoth 		hdspm->channel_map_out_qs = channel_map_aio_out_qs;
65160dca1793SAdrian Knoth 
65170dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = channel_map_aio_in_ss;
65180dca1793SAdrian Knoth 		hdspm->channel_map_in_ds = channel_map_aio_in_ds;
65190dca1793SAdrian Knoth 		hdspm->channel_map_in_qs = channel_map_aio_in_qs;
65200dca1793SAdrian Knoth 
65210dca1793SAdrian Knoth 		hdspm->port_names_in_ss = texts_ports_aio_in_ss;
65220dca1793SAdrian Knoth 		hdspm->port_names_out_ss = texts_ports_aio_out_ss;
65230dca1793SAdrian Knoth 		hdspm->port_names_in_ds = texts_ports_aio_in_ds;
65240dca1793SAdrian Knoth 		hdspm->port_names_out_ds = texts_ports_aio_out_ds;
65250dca1793SAdrian Knoth 		hdspm->port_names_in_qs = texts_ports_aio_in_qs;
65260dca1793SAdrian Knoth 		hdspm->port_names_out_qs = texts_ports_aio_out_qs;
65270dca1793SAdrian Knoth 
65280dca1793SAdrian Knoth 		break;
65290dca1793SAdrian Knoth 
65300dca1793SAdrian Knoth 	case RayDAT:
65310dca1793SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels =
65320dca1793SAdrian Knoth 			RAYDAT_SS_CHANNELS;
65330dca1793SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels =
65340dca1793SAdrian Knoth 			RAYDAT_DS_CHANNELS;
65350dca1793SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels =
65360dca1793SAdrian Knoth 			RAYDAT_QS_CHANNELS;
65370dca1793SAdrian Knoth 
65380dca1793SAdrian Knoth 		hdspm->max_channels_in = RAYDAT_SS_CHANNELS;
65390dca1793SAdrian Knoth 		hdspm->max_channels_out = RAYDAT_SS_CHANNELS;
65400dca1793SAdrian Knoth 
65410dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
65420dca1793SAdrian Knoth 			channel_map_raydat_ss;
65430dca1793SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
65440dca1793SAdrian Knoth 			channel_map_raydat_ds;
65450dca1793SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
65460dca1793SAdrian Knoth 			channel_map_raydat_qs;
65470dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_out =
65480dca1793SAdrian Knoth 			channel_map_raydat_ss;
65490dca1793SAdrian Knoth 
65500dca1793SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
65510dca1793SAdrian Knoth 			texts_ports_raydat_ss;
65520dca1793SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
65530dca1793SAdrian Knoth 			texts_ports_raydat_ds;
65540dca1793SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
65550dca1793SAdrian Knoth 			texts_ports_raydat_qs;
65560dca1793SAdrian Knoth 
65570dca1793SAdrian Knoth 
65580dca1793SAdrian Knoth 		break;
65590dca1793SAdrian Knoth 
65600dca1793SAdrian Knoth 	}
65610dca1793SAdrian Knoth 
65620dca1793SAdrian Knoth 	/* TCO detection */
65630dca1793SAdrian Knoth 	switch (hdspm->io_type) {
65640dca1793SAdrian Knoth 	case AIO:
65650dca1793SAdrian Knoth 	case RayDAT:
65660dca1793SAdrian Knoth 		if (hdspm_read(hdspm, HDSPM_statusRegister2) &
65670dca1793SAdrian Knoth 				HDSPM_s2_tco_detect) {
65680dca1793SAdrian Knoth 			hdspm->midiPorts++;
65690dca1793SAdrian Knoth 			hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
65700dca1793SAdrian Knoth 					GFP_KERNEL);
65710dca1793SAdrian Knoth 			if (NULL != hdspm->tco) {
65720dca1793SAdrian Knoth 				hdspm_tco_write(hdspm);
65730dca1793SAdrian Knoth 			}
65740dca1793SAdrian Knoth 			snd_printk(KERN_INFO "HDSPM: AIO/RayDAT TCO module found\n");
65750dca1793SAdrian Knoth 		} else {
65760dca1793SAdrian Knoth 			hdspm->tco = NULL;
65770dca1793SAdrian Knoth 		}
65780dca1793SAdrian Knoth 		break;
65790dca1793SAdrian Knoth 
65800dca1793SAdrian Knoth 	case MADI:
65810dca1793SAdrian Knoth 		if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
65820dca1793SAdrian Knoth 			hdspm->midiPorts++;
65830dca1793SAdrian Knoth 			hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
65840dca1793SAdrian Knoth 					GFP_KERNEL);
65850dca1793SAdrian Knoth 			if (NULL != hdspm->tco) {
65860dca1793SAdrian Knoth 				hdspm_tco_write(hdspm);
65870dca1793SAdrian Knoth 			}
65880dca1793SAdrian Knoth 			snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n");
65890dca1793SAdrian Knoth 		} else {
65900dca1793SAdrian Knoth 			hdspm->tco = NULL;
65910dca1793SAdrian Knoth 		}
65920dca1793SAdrian Knoth 		break;
65930dca1793SAdrian Knoth 
65940dca1793SAdrian Knoth 	default:
65950dca1793SAdrian Knoth 		hdspm->tco = NULL;
65960dca1793SAdrian Knoth 	}
65970dca1793SAdrian Knoth 
65980dca1793SAdrian Knoth 	/* texts */
65990dca1793SAdrian Knoth 	switch (hdspm->io_type) {
66000dca1793SAdrian Knoth 	case AES32:
66010dca1793SAdrian Knoth 		if (hdspm->tco) {
66020dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aes_tco;
66030dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 10;
66040dca1793SAdrian Knoth 		} else {
66050dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aes;
66060dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 9;
66070dca1793SAdrian Knoth 		}
66080dca1793SAdrian Knoth 		break;
66090dca1793SAdrian Knoth 
66100dca1793SAdrian Knoth 	case MADI:
66110dca1793SAdrian Knoth 		if (hdspm->tco) {
66120dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_madi_tco;
66130dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 4;
66140dca1793SAdrian Knoth 		} else {
66150dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_madi;
66160dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 3;
66170dca1793SAdrian Knoth 		}
66180dca1793SAdrian Knoth 		break;
66190dca1793SAdrian Knoth 
66200dca1793SAdrian Knoth 	case MADIface:
66210dca1793SAdrian Knoth 
66220dca1793SAdrian Knoth 		break;
66230dca1793SAdrian Knoth 
66240dca1793SAdrian Knoth 	case RayDAT:
66250dca1793SAdrian Knoth 		if (hdspm->tco) {
66260dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_raydat_tco;
66270dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 9;
66280dca1793SAdrian Knoth 		} else {
66290dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_raydat;
66300dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 8;
66310dca1793SAdrian Knoth 		}
66320dca1793SAdrian Knoth 		break;
66330dca1793SAdrian Knoth 
66340dca1793SAdrian Knoth 	case AIO:
66350dca1793SAdrian Knoth 		if (hdspm->tco) {
66360dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aio_tco;
66370dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 6;
66380dca1793SAdrian Knoth 		} else {
66390dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aio;
66400dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 5;
66410dca1793SAdrian Knoth 		}
66420dca1793SAdrian Knoth 		break;
66430dca1793SAdrian Knoth 
66440dca1793SAdrian Knoth 	}
66450dca1793SAdrian Knoth 
66460dca1793SAdrian Knoth 	tasklet_init(&hdspm->midi_tasklet,
66470dca1793SAdrian Knoth 			hdspm_midi_tasklet, (unsigned long) hdspm);
6648763f356cSTakashi Iwai 
6649763f356cSTakashi Iwai 	snd_printdd("create alsa devices.\n");
6650ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_alsa_devices(card, hdspm);
6651ef5fa1a4STakashi Iwai 	if (err < 0)
6652763f356cSTakashi Iwai 		return err;
6653763f356cSTakashi Iwai 
6654763f356cSTakashi Iwai 	snd_hdspm_initialize_midi_flush(hdspm);
6655763f356cSTakashi Iwai 
6656763f356cSTakashi Iwai 	return 0;
6657763f356cSTakashi Iwai }
6658763f356cSTakashi Iwai 
66590dca1793SAdrian Knoth 
666098274f07STakashi Iwai static int snd_hdspm_free(struct hdspm * hdspm)
6661763f356cSTakashi Iwai {
6662763f356cSTakashi Iwai 
6663763f356cSTakashi Iwai 	if (hdspm->port) {
6664763f356cSTakashi Iwai 
6665763f356cSTakashi Iwai 		/* stop th audio, and cancel all interrupts */
6666763f356cSTakashi Iwai 		hdspm->control_register &=
6667ef5fa1a4STakashi Iwai 		    ~(HDSPM_Start | HDSPM_AudioInterruptEnable |
66680dca1793SAdrian Knoth 		      HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable |
66690dca1793SAdrian Knoth 		      HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable);
6670763f356cSTakashi Iwai 		hdspm_write(hdspm, HDSPM_controlRegister,
6671763f356cSTakashi Iwai 			    hdspm->control_register);
6672763f356cSTakashi Iwai 	}
6673763f356cSTakashi Iwai 
6674763f356cSTakashi Iwai 	if (hdspm->irq >= 0)
6675763f356cSTakashi Iwai 		free_irq(hdspm->irq, (void *) hdspm);
6676763f356cSTakashi Iwai 
6677763f356cSTakashi Iwai 	kfree(hdspm->mixer);
6678763f356cSTakashi Iwai 
6679763f356cSTakashi Iwai 	if (hdspm->iobase)
6680763f356cSTakashi Iwai 		iounmap(hdspm->iobase);
6681763f356cSTakashi Iwai 
6682763f356cSTakashi Iwai 	if (hdspm->port)
6683763f356cSTakashi Iwai 		pci_release_regions(hdspm->pci);
6684763f356cSTakashi Iwai 
6685763f356cSTakashi Iwai 	pci_disable_device(hdspm->pci);
6686763f356cSTakashi Iwai 	return 0;
6687763f356cSTakashi Iwai }
6688763f356cSTakashi Iwai 
66890dca1793SAdrian Knoth 
669098274f07STakashi Iwai static void snd_hdspm_card_free(struct snd_card *card)
6691763f356cSTakashi Iwai {
6692ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = card->private_data;
6693763f356cSTakashi Iwai 
6694763f356cSTakashi Iwai 	if (hdspm)
6695763f356cSTakashi Iwai 		snd_hdspm_free(hdspm);
6696763f356cSTakashi Iwai }
6697763f356cSTakashi Iwai 
66980dca1793SAdrian Knoth 
6699763f356cSTakashi Iwai static int __devinit snd_hdspm_probe(struct pci_dev *pci,
6700763f356cSTakashi Iwai 				     const struct pci_device_id *pci_id)
6701763f356cSTakashi Iwai {
6702763f356cSTakashi Iwai 	static int dev;
670398274f07STakashi Iwai 	struct hdspm *hdspm;
670498274f07STakashi Iwai 	struct snd_card *card;
6705763f356cSTakashi Iwai 	int err;
6706763f356cSTakashi Iwai 
6707763f356cSTakashi Iwai 	if (dev >= SNDRV_CARDS)
6708763f356cSTakashi Iwai 		return -ENODEV;
6709763f356cSTakashi Iwai 	if (!enable[dev]) {
6710763f356cSTakashi Iwai 		dev++;
6711763f356cSTakashi Iwai 		return -ENOENT;
6712763f356cSTakashi Iwai 	}
6713763f356cSTakashi Iwai 
6714e58de7baSTakashi Iwai 	err = snd_card_create(index[dev], id[dev],
6715e58de7baSTakashi Iwai 			THIS_MODULE, sizeof(struct hdspm), &card);
6716e58de7baSTakashi Iwai 	if (err < 0)
6717e58de7baSTakashi Iwai 		return err;
6718763f356cSTakashi Iwai 
6719ef5fa1a4STakashi Iwai 	hdspm = card->private_data;
6720763f356cSTakashi Iwai 	card->private_free = snd_hdspm_card_free;
6721763f356cSTakashi Iwai 	hdspm->dev = dev;
6722763f356cSTakashi Iwai 	hdspm->pci = pci;
6723763f356cSTakashi Iwai 
6724c187c041STakashi Iwai 	snd_card_set_dev(card, &pci->dev);
6725c187c041STakashi Iwai 
67260dca1793SAdrian Knoth 	err = snd_hdspm_create(card, hdspm);
6727ef5fa1a4STakashi Iwai 	if (err < 0) {
6728763f356cSTakashi Iwai 		snd_card_free(card);
6729763f356cSTakashi Iwai 		return err;
6730763f356cSTakashi Iwai 	}
6731763f356cSTakashi Iwai 
67320dca1793SAdrian Knoth 	if (hdspm->io_type != MADIface) {
67330dca1793SAdrian Knoth 		sprintf(card->shortname, "%s_%x",
67340dca1793SAdrian Knoth 			hdspm->card_name,
67350dca1793SAdrian Knoth 			(hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF);
67360dca1793SAdrian Knoth 		sprintf(card->longname, "%s S/N 0x%x at 0x%lx, irq %d",
67370dca1793SAdrian Knoth 			hdspm->card_name,
67380dca1793SAdrian Knoth 			(hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF,
6739763f356cSTakashi Iwai 			hdspm->port, hdspm->irq);
67400dca1793SAdrian Knoth 	} else {
67410dca1793SAdrian Knoth 		sprintf(card->shortname, "%s", hdspm->card_name);
67420dca1793SAdrian Knoth 		sprintf(card->longname, "%s at 0x%lx, irq %d",
67430dca1793SAdrian Knoth 				hdspm->card_name, hdspm->port, hdspm->irq);
67440dca1793SAdrian Knoth 	}
6745763f356cSTakashi Iwai 
6746ef5fa1a4STakashi Iwai 	err = snd_card_register(card);
6747ef5fa1a4STakashi Iwai 	if (err < 0) {
6748763f356cSTakashi Iwai 		snd_card_free(card);
6749763f356cSTakashi Iwai 		return err;
6750763f356cSTakashi Iwai 	}
6751763f356cSTakashi Iwai 
6752763f356cSTakashi Iwai 	pci_set_drvdata(pci, card);
6753763f356cSTakashi Iwai 
6754763f356cSTakashi Iwai 	dev++;
6755763f356cSTakashi Iwai 	return 0;
6756763f356cSTakashi Iwai }
6757763f356cSTakashi Iwai 
6758763f356cSTakashi Iwai static void __devexit snd_hdspm_remove(struct pci_dev *pci)
6759763f356cSTakashi Iwai {
6760763f356cSTakashi Iwai 	snd_card_free(pci_get_drvdata(pci));
6761763f356cSTakashi Iwai 	pci_set_drvdata(pci, NULL);
6762763f356cSTakashi Iwai }
6763763f356cSTakashi Iwai 
6764763f356cSTakashi Iwai static struct pci_driver driver = {
6765763f356cSTakashi Iwai 	.name = "RME Hammerfall DSP MADI",
6766763f356cSTakashi Iwai 	.id_table = snd_hdspm_ids,
6767763f356cSTakashi Iwai 	.probe = snd_hdspm_probe,
6768763f356cSTakashi Iwai 	.remove = __devexit_p(snd_hdspm_remove),
6769763f356cSTakashi Iwai };
6770763f356cSTakashi Iwai 
6771763f356cSTakashi Iwai 
6772763f356cSTakashi Iwai static int __init alsa_card_hdspm_init(void)
6773763f356cSTakashi Iwai {
6774763f356cSTakashi Iwai 	return pci_register_driver(&driver);
6775763f356cSTakashi Iwai }
6776763f356cSTakashi Iwai 
6777763f356cSTakashi Iwai static void __exit alsa_card_hdspm_exit(void)
6778763f356cSTakashi Iwai {
6779763f356cSTakashi Iwai 	pci_unregister_driver(&driver);
6780763f356cSTakashi Iwai }
6781763f356cSTakashi Iwai 
6782763f356cSTakashi Iwai module_init(alsa_card_hdspm_init)
6783763f356cSTakashi Iwai module_exit(alsa_card_hdspm_exit)
6784