xref: /openbmc/linux/sound/pci/rme9652/hdspm.c (revision 69358fca4203eda93e008f234fabf603d9dba15e)
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  */
41*69358fcaSMartin Dausel 
42*69358fcaSMartin Dausel /* *************    Register Documentation   *******************************************************
43*69358fcaSMartin Dausel  *
44*69358fcaSMartin Dausel  * Work in progress! Documentation is based on the code in this file.
45*69358fcaSMartin Dausel  *
46*69358fcaSMartin Dausel  * --------- HDSPM_controlRegister ---------
47*69358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
48*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
49*69358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
50*69358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
51*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
52*69358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
53*69358fcaSMartin Dausel  * :    .    :    .    :    .    :  x .    :  HDSPM_AudioInterruptEnable \_ setting both bits
54*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .   x:  HDSPM_Start                /  enables audio IO
55*69358fcaSMartin Dausel  * :    .    :    .    :    .    :   x.    :  HDSPM_ClockModeMaster - 1: Master, 0: Slave
56*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .210 :  HDSPM_LatencyMask - 3 Bit value for latency
57*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :      0:64, 1:128, 2:256, 3:512,
58*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :      4:1024, 5:2048, 6:4096, 7:8192
59*69358fcaSMartin Dausel  * :x   .    :    .    :    .   x:xx  .    :  HDSPM_FrequencyMask
60*69358fcaSMartin Dausel  * :    .    :    .    :    .    :10  .    :  HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=??
61*69358fcaSMartin Dausel  * :    .    :    .    :    .   x:    .    :  <MADI> HDSPM_DoubleSpeed
62*69358fcaSMartin Dausel  * :x   .    :    .    :    .    :    .    :  <MADI> HDSPM_QuadSpeed
63*69358fcaSMartin Dausel  * :    .  3 :    .  10:  2 .    :    .    :  HDSPM_SyncRefMask :
64*69358fcaSMartin Dausel  * :    .    :    .   x:    .    :    .    :  HDSPM_SyncRef0
65*69358fcaSMartin Dausel  * :    .    :    .  x :    .    :    .    :  HDSPM_SyncRef1
66*69358fcaSMartin Dausel  * :    .    :    .    :  x .    :    .    :  <AES32> HDSPM_SyncRef2
67*69358fcaSMartin Dausel  * :    .  x :    .    :    .    :    .    :  <AES32> HDSPM_SyncRef3
68*69358fcaSMartin Dausel  * :    .    :    .  10:    .    :    .    :  <MADI> sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn
69*69358fcaSMartin Dausel  * :    .  3 :    .  10:  2 .    :    .    :  <AES32>  0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn?
70*69358fcaSMartin Dausel  * :    .  x :    .    :    .    :    .    :  <MADIe> HDSPe_FLOAT_FORMAT
71*69358fcaSMartin Dausel  * :    .    :    .    : x  .    :    .    :  <MADI> HDSPM_InputSelect0 : 0=optical,1=coax
72*69358fcaSMartin Dausel  * :    .    :    .    :x   .    :    .    :  <MADI> HDSPM_InputSelect1
73*69358fcaSMartin Dausel  * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
74*69358fcaSMartin Dausel  * :    .    :    .    :    . x  :    .    :  <MADI> HDSPM_TX_64ch
75*69358fcaSMartin Dausel  * :    .    :    .    :    . x  :    .    :  <AES32> HDSPM_Emphasis
76*69358fcaSMartin Dausel  * :    .    :    .    :    .x   :    .    :  <MADI> HDSPM_AutoInp
77*69358fcaSMartin Dausel  * :    .    :    . x  :    .    :    .    :  <MADI> HDSPM_SMUX
78*69358fcaSMartin Dausel  * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
79*69358fcaSMartin Dausel  * :    .    :   x.    :    .    :    .    :  <MADI> HDSPM_taxi_reset
80*69358fcaSMartin Dausel  * :    .   x:    .    :    .    :    .    :  <MADI> HDSPM_LineOut
81*69358fcaSMartin Dausel  * :    .   x:    .    :    .    :    .    :  <AES32> ??????????????????
82*69358fcaSMartin Dausel  * :    .    :   x.    :    .    :    .    :  <AES32> HDSPM_WCK48
83*69358fcaSMartin Dausel  * :    .    :    .    :    .x   :    .    :  <AES32> HDSPM_Dolby
84*69358fcaSMartin Dausel  * :    .    : x  .    :    .    :    .    :  HDSPM_Midi0InterruptEnable
85*69358fcaSMartin Dausel  * :    .    :x   .    :    .    :    .    :  HDSPM_Midi1InterruptEnable
86*69358fcaSMartin Dausel  * :    .    :  x .    :    .    :    .    :  HDSPM_Midi2InterruptEnable
87*69358fcaSMartin Dausel  * :    . x  :    .    :    .    :    .    :  <MADI> HDSPM_Midi3InterruptEnable
88*69358fcaSMartin Dausel  * :    . x  :    .    :    .    :    .    :  <AES32> HDSPM_DS_DoubleWire
89*69358fcaSMartin Dausel  * :    .x   :    .    :    .    :    .    :  <AES32> HDSPM_QS_DoubleWire
90*69358fcaSMartin Dausel  * :   x.    :    .    :    .    :    .    :  <AES32> HDSPM_QS_QuadWire
91*69358fcaSMartin Dausel  * :    .    :    .    :    .  x :    .    :  <AES32> HDSPM_Professional
92*69358fcaSMartin Dausel  * : x  .    :    .    :    .    :    .    :  HDSPM_wclk_sel
93*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :
94*69358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
95*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
96*69358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
97*69358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
98*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
99*69358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit
100*69358fcaSMartin Dausel  *
101*69358fcaSMartin Dausel  *
102*69358fcaSMartin Dausel  *
103*69358fcaSMartin Dausel  * AIO / RayDAT only
104*69358fcaSMartin Dausel  *
105*69358fcaSMartin Dausel  * ------------ HDSPM_WR_SETTINGS ----------
106*69358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
107*69358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210:
108*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
109*69358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
110*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
111*69358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
112*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .   x: HDSPM_c0Master 1: Master, 0: Slave
113*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .  x : HDSPM_c0_SyncRef0
114*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    . x  : HDSPM_c0_SyncRef1
115*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .x   : HDSPM_c0_SyncRef2
116*69358fcaSMartin Dausel  * :    .    :    .    :    .    :   x.    : HDSPM_c0_SyncRef3
117*69358fcaSMartin Dausel  * :    .    :    .    :    .    :   3.210 : HDSPM_c0_SyncRefMask:
118*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :  RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4,
119*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
120*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :  AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT,
121*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
122*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :
123*69358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :
124*69358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
125*69358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210:
126*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
127*69358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
128*69358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
129*69358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
130*69358fcaSMartin Dausel  *
131*69358fcaSMartin Dausel  */
132763f356cSTakashi Iwai #include <linux/init.h>
133763f356cSTakashi Iwai #include <linux/delay.h>
134763f356cSTakashi Iwai #include <linux/interrupt.h>
13565a77217SPaul Gortmaker #include <linux/module.h>
136763f356cSTakashi Iwai #include <linux/slab.h>
137763f356cSTakashi Iwai #include <linux/pci.h>
1383f7440a6STakashi Iwai #include <linux/math64.h>
139763f356cSTakashi Iwai #include <asm/io.h>
140763f356cSTakashi Iwai 
141763f356cSTakashi Iwai #include <sound/core.h>
142763f356cSTakashi Iwai #include <sound/control.h>
143763f356cSTakashi Iwai #include <sound/pcm.h>
1440dca1793SAdrian Knoth #include <sound/pcm_params.h>
145763f356cSTakashi Iwai #include <sound/info.h>
146763f356cSTakashi Iwai #include <sound/asoundef.h>
147763f356cSTakashi Iwai #include <sound/rawmidi.h>
148763f356cSTakashi Iwai #include <sound/hwdep.h>
149763f356cSTakashi Iwai #include <sound/initval.h>
150763f356cSTakashi Iwai 
151763f356cSTakashi Iwai #include <sound/hdspm.h>
152763f356cSTakashi Iwai 
153763f356cSTakashi Iwai static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	  /* Index 0-MAX */
154763f356cSTakashi Iwai static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	  /* ID for this card */
155a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
156763f356cSTakashi Iwai 
157763f356cSTakashi Iwai module_param_array(index, int, NULL, 0444);
158763f356cSTakashi Iwai MODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
159763f356cSTakashi Iwai 
160763f356cSTakashi Iwai module_param_array(id, charp, NULL, 0444);
161763f356cSTakashi Iwai MODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
162763f356cSTakashi Iwai 
163763f356cSTakashi Iwai module_param_array(enable, bool, NULL, 0444);
164763f356cSTakashi Iwai MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
165763f356cSTakashi Iwai 
166763f356cSTakashi Iwai 
167763f356cSTakashi Iwai MODULE_AUTHOR
1680dca1793SAdrian Knoth (
1690dca1793SAdrian Knoth 	"Winfried Ritsch <ritsch_AT_iem.at>, "
170ef5fa1a4STakashi Iwai 	"Paul Davis <paul@linuxaudiosystems.com>, "
1713cee5a60SRemy Bruno 	"Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
1720dca1793SAdrian Knoth 	"Remy Bruno <remy.bruno@trinnov.com>, "
1730dca1793SAdrian Knoth 	"Florian Faber <faberman@linuxproaudio.org>, "
1740dca1793SAdrian Knoth 	"Adrian Knoth <adi@drcomp.erfurt.thur.de>"
1750dca1793SAdrian Knoth );
176763f356cSTakashi Iwai MODULE_DESCRIPTION("RME HDSPM");
177763f356cSTakashi Iwai MODULE_LICENSE("GPL");
178763f356cSTakashi Iwai MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
179763f356cSTakashi Iwai 
180763f356cSTakashi Iwai /* --- Write registers. ---
181763f356cSTakashi Iwai   These are defined as byte-offsets from the iobase value.  */
182763f356cSTakashi Iwai 
1830dca1793SAdrian Knoth #define HDSPM_WR_SETTINGS             0
1840dca1793SAdrian Knoth #define HDSPM_outputBufferAddress    32
1850dca1793SAdrian Knoth #define HDSPM_inputBufferAddress     36
186763f356cSTakashi Iwai #define HDSPM_controlRegister	     64
187763f356cSTakashi Iwai #define HDSPM_interruptConfirmation  96
188763f356cSTakashi Iwai #define HDSPM_control2Reg	     256  /* not in specs ???????? */
189*69358fcaSMartin Dausel #define HDSPM_freqReg                256  /* for setting arbitrary clock values (DDS feature) */
190763f356cSTakashi Iwai #define HDSPM_midiDataOut0	     352  /* just believe in old code */
191763f356cSTakashi Iwai #define HDSPM_midiDataOut1	     356
192ffb2c3c0SRemy Bruno #define HDSPM_eeprom_wr		     384  /* for AES32 */
193763f356cSTakashi Iwai 
194763f356cSTakashi Iwai /* DMA enable for 64 channels, only Bit 0 is relevant */
195763f356cSTakashi Iwai #define HDSPM_outputEnableBase       512  /* 512-767  input  DMA */
196763f356cSTakashi Iwai #define HDSPM_inputEnableBase        768  /* 768-1023 output DMA */
197763f356cSTakashi Iwai 
198763f356cSTakashi Iwai /* 16 page addresses for each of the 64 channels DMA buffer in and out
199763f356cSTakashi Iwai    (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
200763f356cSTakashi Iwai #define HDSPM_pageAddressBufferOut       8192
201763f356cSTakashi Iwai #define HDSPM_pageAddressBufferIn        (HDSPM_pageAddressBufferOut+64*16*4)
202763f356cSTakashi Iwai 
203763f356cSTakashi Iwai #define HDSPM_MADI_mixerBase    32768	/* 32768-65535 for 2x64x64 Fader */
204763f356cSTakashi Iwai 
205763f356cSTakashi Iwai #define HDSPM_MATRIX_MIXER_SIZE  8192	/* = 2*64*64 * 4 Byte => 32kB */
206763f356cSTakashi Iwai 
207763f356cSTakashi Iwai /* --- Read registers. ---
208763f356cSTakashi Iwai    These are defined as byte-offsets from the iobase value */
209763f356cSTakashi Iwai #define HDSPM_statusRegister    0
2103cee5a60SRemy Bruno /*#define HDSPM_statusRegister2  96 */
2113cee5a60SRemy Bruno /* after RME Windows driver sources, status2 is 4-byte word # 48 = word at
2123cee5a60SRemy Bruno  * offset 192, for AES32 *and* MADI
2133cee5a60SRemy Bruno  * => need to check that offset 192 is working on MADI */
2143cee5a60SRemy Bruno #define HDSPM_statusRegister2  192
2153cee5a60SRemy Bruno #define HDSPM_timecodeRegister 128
216763f356cSTakashi Iwai 
2170dca1793SAdrian Knoth /* AIO, RayDAT */
2180dca1793SAdrian Knoth #define HDSPM_RD_STATUS_0 0
2190dca1793SAdrian Knoth #define HDSPM_RD_STATUS_1 64
2200dca1793SAdrian Knoth #define HDSPM_RD_STATUS_2 128
2210dca1793SAdrian Knoth #define HDSPM_RD_STATUS_3 192
2220dca1793SAdrian Knoth 
2230dca1793SAdrian Knoth #define HDSPM_RD_TCO           256
2240dca1793SAdrian Knoth #define HDSPM_RD_PLL_FREQ      512
2250dca1793SAdrian Knoth #define HDSPM_WR_TCO           128
2260dca1793SAdrian Knoth 
2270dca1793SAdrian Knoth #define HDSPM_TCO1_TCO_lock			0x00000001
2280dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_Range_LSB		0x00000002
2290dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_Range_MSB		0x00000004
2300dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Input_valid		0x00000008
2310dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_valid		0x00000010
2320dca1793SAdrian Knoth #define HDSPM_TCO1_Video_Input_Format_NTSC	0x00000020
2330dca1793SAdrian Knoth #define HDSPM_TCO1_Video_Input_Format_PAL	0x00000040
2340dca1793SAdrian Knoth 
2350dca1793SAdrian Knoth #define HDSPM_TCO1_set_TC			0x00000100
2360dca1793SAdrian Knoth #define HDSPM_TCO1_set_drop_frame_flag		0x00000200
2370dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Format_LSB		0x00000400
2380dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Format_MSB		0x00000800
2390dca1793SAdrian Knoth 
2400dca1793SAdrian Knoth #define HDSPM_TCO2_TC_run			0x00010000
2410dca1793SAdrian Knoth #define HDSPM_TCO2_WCK_IO_ratio_LSB		0x00020000
2420dca1793SAdrian Knoth #define HDSPM_TCO2_WCK_IO_ratio_MSB		0x00040000
2430dca1793SAdrian Knoth #define HDSPM_TCO2_set_num_drop_frames_LSB	0x00080000
2440dca1793SAdrian Knoth #define HDSPM_TCO2_set_num_drop_frames_MSB	0x00100000
2450dca1793SAdrian Knoth #define HDSPM_TCO2_set_jam_sync			0x00200000
2460dca1793SAdrian Knoth #define HDSPM_TCO2_set_flywheel			0x00400000
2470dca1793SAdrian Knoth 
2480dca1793SAdrian Knoth #define HDSPM_TCO2_set_01_4			0x01000000
2490dca1793SAdrian Knoth #define HDSPM_TCO2_set_pull_down		0x02000000
2500dca1793SAdrian Knoth #define HDSPM_TCO2_set_pull_up			0x04000000
2510dca1793SAdrian Knoth #define HDSPM_TCO2_set_freq			0x08000000
2520dca1793SAdrian Knoth #define HDSPM_TCO2_set_term_75R			0x10000000
2530dca1793SAdrian Knoth #define HDSPM_TCO2_set_input_LSB		0x20000000
2540dca1793SAdrian Knoth #define HDSPM_TCO2_set_input_MSB		0x40000000
2550dca1793SAdrian Knoth #define HDSPM_TCO2_set_freq_from_app		0x80000000
2560dca1793SAdrian Knoth 
2570dca1793SAdrian Knoth 
2580dca1793SAdrian Knoth #define HDSPM_midiDataOut0    352
2590dca1793SAdrian Knoth #define HDSPM_midiDataOut1    356
2600dca1793SAdrian Knoth #define HDSPM_midiDataOut2    368
2610dca1793SAdrian Knoth 
262763f356cSTakashi Iwai #define HDSPM_midiDataIn0     360
263763f356cSTakashi Iwai #define HDSPM_midiDataIn1     364
2640dca1793SAdrian Knoth #define HDSPM_midiDataIn2     372
2650dca1793SAdrian Knoth #define HDSPM_midiDataIn3     376
266763f356cSTakashi Iwai 
267763f356cSTakashi Iwai /* status is data bytes in MIDI-FIFO (0-128) */
268763f356cSTakashi Iwai #define HDSPM_midiStatusOut0  384
269763f356cSTakashi Iwai #define HDSPM_midiStatusOut1  388
2700dca1793SAdrian Knoth #define HDSPM_midiStatusOut2  400
2710dca1793SAdrian Knoth 
272763f356cSTakashi Iwai #define HDSPM_midiStatusIn0   392
273763f356cSTakashi Iwai #define HDSPM_midiStatusIn1   396
2740dca1793SAdrian Knoth #define HDSPM_midiStatusIn2   404
2750dca1793SAdrian Knoth #define HDSPM_midiStatusIn3   408
276763f356cSTakashi Iwai 
277763f356cSTakashi Iwai 
278763f356cSTakashi Iwai /* the meters are regular i/o-mapped registers, but offset
279763f356cSTakashi Iwai    considerably from the rest. the peak registers are reset
280763f356cSTakashi Iwai    when read; the least-significant 4 bits are full-scale counters;
281763f356cSTakashi Iwai    the actual peak value is in the most-significant 24 bits.
282763f356cSTakashi Iwai */
2830dca1793SAdrian Knoth 
2840dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_PEAK		4096
2850dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_PEAK	4352
2860dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_PEAK		4608
2870dca1793SAdrian Knoth 
2880dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_RMS_L		6144
2890dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_RMS_L	6400
2900dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_RMS_L		6656
2910dca1793SAdrian Knoth 
2920dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_RMS_H		7168
2930dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_RMS_H	7424
2940dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_RMS_H		7680
295763f356cSTakashi Iwai 
296763f356cSTakashi Iwai /* --- Control Register bits --------- */
297763f356cSTakashi Iwai #define HDSPM_Start                (1<<0) /* start engine */
298763f356cSTakashi Iwai 
299763f356cSTakashi Iwai #define HDSPM_Latency0             (1<<1) /* buffer size = 2^n */
300763f356cSTakashi Iwai #define HDSPM_Latency1             (1<<2) /* where n is defined */
301763f356cSTakashi Iwai #define HDSPM_Latency2             (1<<3) /* by Latency{2,1,0} */
302763f356cSTakashi Iwai 
3030dca1793SAdrian Knoth #define HDSPM_ClockModeMaster      (1<<4) /* 1=Master, 0=Autosync */
3040dca1793SAdrian Knoth #define HDSPM_c0Master		0x1    /* Master clock bit in settings
3050dca1793SAdrian Knoth 					  register [RayDAT, AIO] */
306763f356cSTakashi Iwai 
307763f356cSTakashi Iwai #define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
308763f356cSTakashi Iwai 
309763f356cSTakashi Iwai #define HDSPM_Frequency0  (1<<6)  /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */
310763f356cSTakashi Iwai #define HDSPM_Frequency1  (1<<7)  /* 0=32kHz/64kHz */
311763f356cSTakashi Iwai #define HDSPM_DoubleSpeed (1<<8)  /* 0=normal speed, 1=double speed */
3123cee5a60SRemy Bruno #define HDSPM_QuadSpeed   (1<<31) /* quad speed bit */
313763f356cSTakashi Iwai 
3143cee5a60SRemy Bruno #define HDSPM_Professional (1<<9) /* Professional */ /* AES32 ONLY */
315763f356cSTakashi Iwai #define HDSPM_TX_64ch     (1<<10) /* Output 64channel MODE=1,
3163cee5a60SRemy Bruno 				     56channelMODE=0 */ /* MADI ONLY*/
3173cee5a60SRemy Bruno #define HDSPM_Emphasis    (1<<10) /* Emphasis */ /* AES32 ONLY */
318763f356cSTakashi Iwai 
319763f356cSTakashi Iwai #define HDSPM_AutoInp     (1<<11) /* Auto Input (takeover) == Safe Mode,
3203cee5a60SRemy Bruno                                      0=off, 1=on  */ /* MADI ONLY */
3213cee5a60SRemy Bruno #define HDSPM_Dolby       (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
322763f356cSTakashi Iwai 
323ef5fa1a4STakashi Iwai #define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax
324ef5fa1a4STakashi Iwai 				    * -- MADI ONLY
325ef5fa1a4STakashi Iwai 				    */
326763f356cSTakashi Iwai #define HDSPM_InputSelect1 (1<<15) /* should be 0 */
327763f356cSTakashi Iwai 
3283cee5a60SRemy Bruno #define HDSPM_SyncRef2     (1<<13)
3293cee5a60SRemy Bruno #define HDSPM_SyncRef3     (1<<25)
330763f356cSTakashi Iwai 
3313cee5a60SRemy Bruno #define HDSPM_SMUX         (1<<18) /* Frame ??? */ /* MADI ONY */
332763f356cSTakashi Iwai #define HDSPM_clr_tms      (1<<19) /* clear track marker, do not use
333763f356cSTakashi Iwai                                       AES additional bits in
334763f356cSTakashi Iwai 				      lower 5 Audiodatabits ??? */
3353cee5a60SRemy Bruno #define HDSPM_taxi_reset   (1<<20) /* ??? */ /* MADI ONLY ? */
3363cee5a60SRemy Bruno #define HDSPM_WCK48        (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
337763f356cSTakashi Iwai 
3380dca1793SAdrian Knoth #define HDSPM_Midi0InterruptEnable 0x0400000
3390dca1793SAdrian Knoth #define HDSPM_Midi1InterruptEnable 0x0800000
3400dca1793SAdrian Knoth #define HDSPM_Midi2InterruptEnable 0x0200000
3410dca1793SAdrian Knoth #define HDSPM_Midi3InterruptEnable 0x4000000
342763f356cSTakashi Iwai 
343763f356cSTakashi Iwai #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
3440dca1793SAdrian Knoth #define HDSPe_FLOAT_FORMAT         0x2000000
345763f356cSTakashi Iwai 
3463cee5a60SRemy Bruno #define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
3473cee5a60SRemy Bruno #define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
3483cee5a60SRemy Bruno #define HDSPM_QS_QuadWire   (1<<28) /* AES32 ONLY */
3493cee5a60SRemy Bruno 
3503cee5a60SRemy Bruno #define HDSPM_wclk_sel (1<<30)
351763f356cSTakashi Iwai 
352384f778fSAdrian Knoth /* additional control register bits for AIO*/
353384f778fSAdrian Knoth #define HDSPM_c0_Wck48				0x20 /* also RayDAT */
354384f778fSAdrian Knoth #define HDSPM_c0_Input0				0x1000
355384f778fSAdrian Knoth #define HDSPM_c0_Input1				0x2000
356384f778fSAdrian Knoth #define HDSPM_c0_Spdif_Opt			0x4000
357384f778fSAdrian Knoth #define HDSPM_c0_Pro				0x8000
358384f778fSAdrian Knoth #define HDSPM_c0_clr_tms			0x10000
359384f778fSAdrian Knoth #define HDSPM_c0_AEB1				0x20000
360384f778fSAdrian Knoth #define HDSPM_c0_AEB2				0x40000
361384f778fSAdrian Knoth #define HDSPM_c0_LineOut			0x80000
362384f778fSAdrian Knoth #define HDSPM_c0_AD_GAIN0			0x100000
363384f778fSAdrian Knoth #define HDSPM_c0_AD_GAIN1			0x200000
364384f778fSAdrian Knoth #define HDSPM_c0_DA_GAIN0			0x400000
365384f778fSAdrian Knoth #define HDSPM_c0_DA_GAIN1			0x800000
366384f778fSAdrian Knoth #define HDSPM_c0_PH_GAIN0			0x1000000
367384f778fSAdrian Knoth #define HDSPM_c0_PH_GAIN1			0x2000000
368384f778fSAdrian Knoth #define HDSPM_c0_Sym6db				0x4000000
369384f778fSAdrian Knoth 
370384f778fSAdrian Knoth 
371763f356cSTakashi Iwai /* --- bit helper defines */
372763f356cSTakashi Iwai #define HDSPM_LatencyMask    (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
373ef5fa1a4STakashi Iwai #define HDSPM_FrequencyMask  (HDSPM_Frequency0|HDSPM_Frequency1|\
374ef5fa1a4STakashi Iwai 			      HDSPM_DoubleSpeed|HDSPM_QuadSpeed)
375763f356cSTakashi Iwai #define HDSPM_InputMask      (HDSPM_InputSelect0|HDSPM_InputSelect1)
376763f356cSTakashi Iwai #define HDSPM_InputOptical   0
377763f356cSTakashi Iwai #define HDSPM_InputCoaxial   (HDSPM_InputSelect0)
378ef5fa1a4STakashi Iwai #define HDSPM_SyncRefMask    (HDSPM_SyncRef0|HDSPM_SyncRef1|\
379ef5fa1a4STakashi Iwai 			      HDSPM_SyncRef2|HDSPM_SyncRef3)
3800dca1793SAdrian Knoth 
3810dca1793SAdrian Knoth #define HDSPM_c0_SyncRef0      0x2
3820dca1793SAdrian Knoth #define HDSPM_c0_SyncRef1      0x4
3830dca1793SAdrian Knoth #define HDSPM_c0_SyncRef2      0x8
3840dca1793SAdrian Knoth #define HDSPM_c0_SyncRef3      0x10
3850dca1793SAdrian Knoth #define HDSPM_c0_SyncRefMask   (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\
3860dca1793SAdrian Knoth 				HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3)
387763f356cSTakashi Iwai 
388763f356cSTakashi Iwai #define HDSPM_SYNC_FROM_WORD    0	/* Preferred sync reference */
389763f356cSTakashi Iwai #define HDSPM_SYNC_FROM_MADI    1	/* choices - used by "pref_sync_ref" */
3900dca1793SAdrian Knoth #define HDSPM_SYNC_FROM_TCO     2
3910dca1793SAdrian Knoth #define HDSPM_SYNC_FROM_SYNC_IN 3
392763f356cSTakashi Iwai 
393763f356cSTakashi Iwai #define HDSPM_Frequency32KHz    HDSPM_Frequency0
394763f356cSTakashi Iwai #define HDSPM_Frequency44_1KHz  HDSPM_Frequency1
395763f356cSTakashi Iwai #define HDSPM_Frequency48KHz   (HDSPM_Frequency1|HDSPM_Frequency0)
396763f356cSTakashi Iwai #define HDSPM_Frequency64KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency0)
397763f356cSTakashi Iwai #define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
398ef5fa1a4STakashi Iwai #define HDSPM_Frequency96KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency1|\
399ef5fa1a4STakashi Iwai 				HDSPM_Frequency0)
4003cee5a60SRemy Bruno #define HDSPM_Frequency128KHz   (HDSPM_QuadSpeed|HDSPM_Frequency0)
4013cee5a60SRemy Bruno #define HDSPM_Frequency176_4KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1)
402ef5fa1a4STakashi Iwai #define HDSPM_Frequency192KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1|\
403ef5fa1a4STakashi Iwai 				 HDSPM_Frequency0)
404763f356cSTakashi Iwai 
405763f356cSTakashi Iwai 
406763f356cSTakashi Iwai /* Synccheck Status */
407763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_NO_LOCK 0
408763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_LOCK    1
409763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_SYNC	 2
410763f356cSTakashi Iwai 
411763f356cSTakashi Iwai /* AutoSync References - used by "autosync_ref" control switch */
412763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_FROM_WORD      0
413763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_FROM_MADI      1
4140dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_TCO       2
4150dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_SYNC_IN   3
4160dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_NONE      4
417763f356cSTakashi Iwai 
418763f356cSTakashi Iwai /* Possible sources of MADI input */
419763f356cSTakashi Iwai #define HDSPM_OPTICAL 0		/* optical   */
420763f356cSTakashi Iwai #define HDSPM_COAXIAL 1		/* BNC */
421763f356cSTakashi Iwai 
422763f356cSTakashi Iwai #define hdspm_encode_latency(x)       (((x)<<1) & HDSPM_LatencyMask)
4230dca1793SAdrian Knoth #define hdspm_decode_latency(x)       ((((x) & HDSPM_LatencyMask)>>1))
424763f356cSTakashi Iwai 
425763f356cSTakashi Iwai #define hdspm_encode_in(x) (((x)&0x3)<<14)
426763f356cSTakashi Iwai #define hdspm_decode_in(x) (((x)>>14)&0x3)
427763f356cSTakashi Iwai 
428763f356cSTakashi Iwai /* --- control2 register bits --- */
429763f356cSTakashi Iwai #define HDSPM_TMS             (1<<0)
430763f356cSTakashi Iwai #define HDSPM_TCK             (1<<1)
431763f356cSTakashi Iwai #define HDSPM_TDI             (1<<2)
432763f356cSTakashi Iwai #define HDSPM_JTAG            (1<<3)
433763f356cSTakashi Iwai #define HDSPM_PWDN            (1<<4)
434763f356cSTakashi Iwai #define HDSPM_PROGRAM	      (1<<5)
435763f356cSTakashi Iwai #define HDSPM_CONFIG_MODE_0   (1<<6)
436763f356cSTakashi Iwai #define HDSPM_CONFIG_MODE_1   (1<<7)
437763f356cSTakashi Iwai /*#define HDSPM_VERSION_BIT     (1<<8) not defined any more*/
438763f356cSTakashi Iwai #define HDSPM_BIGENDIAN_MODE  (1<<9)
439763f356cSTakashi Iwai #define HDSPM_RD_MULTIPLE     (1<<10)
440763f356cSTakashi Iwai 
4413cee5a60SRemy Bruno /* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and
442ef5fa1a4STakashi Iwai      that do not conflict with specific bits for AES32 seem to be valid also
443ef5fa1a4STakashi Iwai      for the AES32
444ef5fa1a4STakashi Iwai  */
445763f356cSTakashi Iwai #define HDSPM_audioIRQPending    (1<<0)	/* IRQ is high and pending */
446ef5fa1a4STakashi Iwai #define HDSPM_RX_64ch            (1<<1)	/* Input 64chan. MODE=1, 56chn MODE=0 */
447ef5fa1a4STakashi Iwai #define HDSPM_AB_int             (1<<2)	/* InputChannel Opt=0, Coax=1
448ef5fa1a4STakashi Iwai 					 * (like inp0)
449ef5fa1a4STakashi Iwai 					 */
4500dca1793SAdrian Knoth 
451763f356cSTakashi Iwai #define HDSPM_madiLock           (1<<3)	/* MADI Locked =1, no=0 */
4520dca1793SAdrian Knoth #define HDSPM_madiSync          (1<<18) /* MADI is in sync */
4530dca1793SAdrian Knoth 
454b0bf5504SAdrian Knoth #define HDSPM_tcoLockMadi    0x00000020 /* Optional TCO locked status for HDSPe MADI*/
455b0bf5504SAdrian Knoth #define HDSPM_tcoSync    0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/
4560dca1793SAdrian Knoth 
457b0bf5504SAdrian Knoth #define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */
458b0bf5504SAdrian Knoth #define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */
459763f356cSTakashi Iwai 
460763f356cSTakashi Iwai #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
4610dca1793SAdrian Knoth 			/* since 64byte accurate, last 6 bits are not used */
462763f356cSTakashi Iwai 
4630dca1793SAdrian Knoth 
4640dca1793SAdrian Knoth 
465763f356cSTakashi Iwai #define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
466763f356cSTakashi Iwai 
467763f356cSTakashi Iwai #define HDSPM_madiFreq0         (1<<22)	/* system freq 0=error */
468763f356cSTakashi Iwai #define HDSPM_madiFreq1         (1<<23)	/* 1=32, 2=44.1 3=48 */
469763f356cSTakashi Iwai #define HDSPM_madiFreq2         (1<<24)	/* 4=64, 5=88.2 6=96 */
470763f356cSTakashi Iwai #define HDSPM_madiFreq3         (1<<25)	/* 7=128, 8=176.4 9=192 */
471763f356cSTakashi Iwai 
472ef5fa1a4STakashi Iwai #define HDSPM_BufferID          (1<<26)	/* (Double)Buffer ID toggles with
473ef5fa1a4STakashi Iwai 					 * Interrupt
474ef5fa1a4STakashi Iwai 					 */
4750dca1793SAdrian Knoth #define HDSPM_tco_detect         0x08000000
476b0bf5504SAdrian Knoth #define HDSPM_tcoLockAes         0x20000000 /* Optional TCO locked status for HDSPe AES */
4770dca1793SAdrian Knoth 
4780dca1793SAdrian Knoth #define HDSPM_s2_tco_detect      0x00000040
4790dca1793SAdrian Knoth #define HDSPM_s2_AEBO_D          0x00000080
4800dca1793SAdrian Knoth #define HDSPM_s2_AEBI_D          0x00000100
4810dca1793SAdrian Knoth 
4820dca1793SAdrian Knoth 
4830dca1793SAdrian Knoth #define HDSPM_midi0IRQPending    0x40000000
4840dca1793SAdrian Knoth #define HDSPM_midi1IRQPending    0x80000000
4850dca1793SAdrian Knoth #define HDSPM_midi2IRQPending    0x20000000
4860dca1793SAdrian Knoth #define HDSPM_midi2IRQPendingAES 0x00000020
4870dca1793SAdrian Knoth #define HDSPM_midi3IRQPending    0x00200000
488763f356cSTakashi Iwai 
489763f356cSTakashi Iwai /* --- status bit helpers */
490ef5fa1a4STakashi Iwai #define HDSPM_madiFreqMask  (HDSPM_madiFreq0|HDSPM_madiFreq1|\
491ef5fa1a4STakashi Iwai 			     HDSPM_madiFreq2|HDSPM_madiFreq3)
492763f356cSTakashi Iwai #define HDSPM_madiFreq32    (HDSPM_madiFreq0)
493763f356cSTakashi Iwai #define HDSPM_madiFreq44_1  (HDSPM_madiFreq1)
494763f356cSTakashi Iwai #define HDSPM_madiFreq48    (HDSPM_madiFreq0|HDSPM_madiFreq1)
495763f356cSTakashi Iwai #define HDSPM_madiFreq64    (HDSPM_madiFreq2)
496763f356cSTakashi Iwai #define HDSPM_madiFreq88_2  (HDSPM_madiFreq0|HDSPM_madiFreq2)
497763f356cSTakashi Iwai #define HDSPM_madiFreq96    (HDSPM_madiFreq1|HDSPM_madiFreq2)
498763f356cSTakashi Iwai #define HDSPM_madiFreq128   (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2)
499763f356cSTakashi Iwai #define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
500763f356cSTakashi Iwai #define HDSPM_madiFreq192   (HDSPM_madiFreq3|HDSPM_madiFreq0)
501763f356cSTakashi Iwai 
5023cee5a60SRemy Bruno /* Status2 Register bits */ /* MADI ONLY */
503763f356cSTakashi Iwai 
50425985edcSLucas De Marchi #define HDSPM_version0 (1<<0)	/* not really defined but I guess */
505763f356cSTakashi Iwai #define HDSPM_version1 (1<<1)	/* in former cards it was ??? */
506763f356cSTakashi Iwai #define HDSPM_version2 (1<<2)
507763f356cSTakashi Iwai 
508763f356cSTakashi Iwai #define HDSPM_wcLock (1<<3)	/* Wordclock is detected and locked */
509763f356cSTakashi Iwai #define HDSPM_wcSync (1<<4)	/* Wordclock is in sync with systemclock */
510763f356cSTakashi Iwai 
511763f356cSTakashi Iwai #define HDSPM_wc_freq0 (1<<5)	/* input freq detected via autosync  */
512763f356cSTakashi Iwai #define HDSPM_wc_freq1 (1<<6)	/* 001=32, 010==44.1, 011=48, */
513a8cd7148SAdrian Knoth #define HDSPM_wc_freq2 (1<<7)	/* 100=64, 101=88.2, 110=96, 111=128 */
514a8cd7148SAdrian Knoth #define HDSPM_wc_freq3 0x800	/* 1000=176.4, 1001=192 */
515763f356cSTakashi Iwai 
5160dca1793SAdrian Knoth #define HDSPM_SyncRef0 0x10000  /* Sync Reference */
5170dca1793SAdrian Knoth #define HDSPM_SyncRef1 0x20000
5180dca1793SAdrian Knoth 
5190dca1793SAdrian Knoth #define HDSPM_SelSyncRef0 (1<<8)	/* AutoSync Source */
520763f356cSTakashi Iwai #define HDSPM_SelSyncRef1 (1<<9)	/* 000=word, 001=MADI, */
521763f356cSTakashi Iwai #define HDSPM_SelSyncRef2 (1<<10)	/* 111=no valid signal */
522763f356cSTakashi Iwai 
523763f356cSTakashi Iwai #define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
524763f356cSTakashi Iwai 
525a8cd7148SAdrian Knoth #define HDSPM_wcFreqMask  (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2|\
526a8cd7148SAdrian Knoth 			    HDSPM_wc_freq3)
527763f356cSTakashi Iwai #define HDSPM_wcFreq32    (HDSPM_wc_freq0)
528763f356cSTakashi Iwai #define HDSPM_wcFreq44_1  (HDSPM_wc_freq1)
529763f356cSTakashi Iwai #define HDSPM_wcFreq48    (HDSPM_wc_freq0|HDSPM_wc_freq1)
530763f356cSTakashi Iwai #define HDSPM_wcFreq64    (HDSPM_wc_freq2)
531763f356cSTakashi Iwai #define HDSPM_wcFreq88_2  (HDSPM_wc_freq0|HDSPM_wc_freq2)
532763f356cSTakashi Iwai #define HDSPM_wcFreq96    (HDSPM_wc_freq1|HDSPM_wc_freq2)
533a8cd7148SAdrian Knoth #define HDSPM_wcFreq128   (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
534a8cd7148SAdrian Knoth #define HDSPM_wcFreq176_4 (HDSPM_wc_freq3)
535a8cd7148SAdrian Knoth #define HDSPM_wcFreq192   (HDSPM_wc_freq0|HDSPM_wc_freq3)
536763f356cSTakashi Iwai 
5370dca1793SAdrian Knoth #define HDSPM_status1_F_0 0x0400000
5380dca1793SAdrian Knoth #define HDSPM_status1_F_1 0x0800000
5390dca1793SAdrian Knoth #define HDSPM_status1_F_2 0x1000000
5400dca1793SAdrian Knoth #define HDSPM_status1_F_3 0x2000000
5410dca1793SAdrian Knoth #define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3)
5420dca1793SAdrian Knoth 
543763f356cSTakashi Iwai 
544ef5fa1a4STakashi Iwai #define HDSPM_SelSyncRefMask       (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
545ef5fa1a4STakashi Iwai 				    HDSPM_SelSyncRef2)
546763f356cSTakashi Iwai #define HDSPM_SelSyncRef_WORD      0
547763f356cSTakashi Iwai #define HDSPM_SelSyncRef_MADI      (HDSPM_SelSyncRef0)
5480dca1793SAdrian Knoth #define HDSPM_SelSyncRef_TCO       (HDSPM_SelSyncRef1)
5490dca1793SAdrian Knoth #define HDSPM_SelSyncRef_SyncIn    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1)
550ef5fa1a4STakashi Iwai #define HDSPM_SelSyncRef_NVALID    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
551ef5fa1a4STakashi Iwai 				    HDSPM_SelSyncRef2)
552763f356cSTakashi Iwai 
5533cee5a60SRemy Bruno /*
5543cee5a60SRemy Bruno    For AES32, bits for status, status2 and timecode are different
5553cee5a60SRemy Bruno */
5563cee5a60SRemy Bruno /* status */
5573cee5a60SRemy Bruno #define HDSPM_AES32_wcLock	0x0200000
55856bde0f3SAndre Schramm #define HDSPM_AES32_wcSync	0x0100000
5593cee5a60SRemy Bruno #define HDSPM_AES32_wcFreq_bit  22
5603cee5a60SRemy Bruno /* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
5613cee5a60SRemy Bruno   HDSPM_bit2freq */
5623cee5a60SRemy Bruno #define HDSPM_AES32_syncref_bit  16
5633cee5a60SRemy Bruno /* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
5643cee5a60SRemy Bruno 
5653cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_WORD 0
5663cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES1 1
5673cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES2 2
5683cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES3 3
5693cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES4 4
5703cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES5 5
5713cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
5723cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
5733cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
574b0bf5504SAdrian Knoth #define HDSPM_AES32_AUTOSYNC_FROM_TCO 9
575b0bf5504SAdrian Knoth #define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10
576b0bf5504SAdrian Knoth #define HDSPM_AES32_AUTOSYNC_FROM_NONE 11
5773cee5a60SRemy Bruno 
5783cee5a60SRemy Bruno /*  status2 */
5793cee5a60SRemy Bruno /* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
5803cee5a60SRemy Bruno #define HDSPM_LockAES   0x80
5813cee5a60SRemy Bruno #define HDSPM_LockAES1  0x80
5823cee5a60SRemy Bruno #define HDSPM_LockAES2  0x40
5833cee5a60SRemy Bruno #define HDSPM_LockAES3  0x20
5843cee5a60SRemy Bruno #define HDSPM_LockAES4  0x10
5853cee5a60SRemy Bruno #define HDSPM_LockAES5  0x8
5863cee5a60SRemy Bruno #define HDSPM_LockAES6  0x4
5873cee5a60SRemy Bruno #define HDSPM_LockAES7  0x2
5883cee5a60SRemy Bruno #define HDSPM_LockAES8  0x1
5893cee5a60SRemy Bruno /*
5903cee5a60SRemy Bruno    Timecode
5913cee5a60SRemy Bruno    After windows driver sources, bits 4*i to 4*i+3 give the input frequency on
5923cee5a60SRemy Bruno    AES i+1
5933cee5a60SRemy Bruno  bits 3210
5943cee5a60SRemy Bruno       0001  32kHz
5953cee5a60SRemy Bruno       0010  44.1kHz
5963cee5a60SRemy Bruno       0011  48kHz
5973cee5a60SRemy Bruno       0100  64kHz
5983cee5a60SRemy Bruno       0101  88.2kHz
5993cee5a60SRemy Bruno       0110  96kHz
6003cee5a60SRemy Bruno       0111  128kHz
6013cee5a60SRemy Bruno       1000  176.4kHz
6023cee5a60SRemy Bruno       1001  192kHz
6033cee5a60SRemy Bruno   NB: Timecode register doesn't seem to work on AES32 card revision 230
6043cee5a60SRemy Bruno */
6053cee5a60SRemy Bruno 
606763f356cSTakashi Iwai /* Mixer Values */
607763f356cSTakashi Iwai #define UNITY_GAIN          32768	/* = 65536/2 */
608763f356cSTakashi Iwai #define MINUS_INFINITY_GAIN 0
609763f356cSTakashi Iwai 
610763f356cSTakashi Iwai /* Number of channels for different Speed Modes */
611763f356cSTakashi Iwai #define MADI_SS_CHANNELS       64
612763f356cSTakashi Iwai #define MADI_DS_CHANNELS       32
613763f356cSTakashi Iwai #define MADI_QS_CHANNELS       16
614763f356cSTakashi Iwai 
6150dca1793SAdrian Knoth #define RAYDAT_SS_CHANNELS     36
6160dca1793SAdrian Knoth #define RAYDAT_DS_CHANNELS     20
6170dca1793SAdrian Knoth #define RAYDAT_QS_CHANNELS     12
6180dca1793SAdrian Knoth 
6190dca1793SAdrian Knoth #define AIO_IN_SS_CHANNELS        14
6200dca1793SAdrian Knoth #define AIO_IN_DS_CHANNELS        10
6210dca1793SAdrian Knoth #define AIO_IN_QS_CHANNELS        8
6220dca1793SAdrian Knoth #define AIO_OUT_SS_CHANNELS        16
6230dca1793SAdrian Knoth #define AIO_OUT_DS_CHANNELS        12
6240dca1793SAdrian Knoth #define AIO_OUT_QS_CHANNELS        10
6250dca1793SAdrian Knoth 
626d2d10a21SAdrian Knoth #define AES32_CHANNELS		16
627d2d10a21SAdrian Knoth 
628763f356cSTakashi Iwai /* the size of a substream (1 mono data stream) */
629763f356cSTakashi Iwai #define HDSPM_CHANNEL_BUFFER_SAMPLES  (16*1024)
630763f356cSTakashi Iwai #define HDSPM_CHANNEL_BUFFER_BYTES    (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
631763f356cSTakashi Iwai 
632763f356cSTakashi Iwai /* the size of the area we need to allocate for DMA transfers. the
633763f356cSTakashi Iwai    size is the same regardless of the number of channels, and
634763f356cSTakashi Iwai    also the latency to use.
635763f356cSTakashi Iwai    for one direction !!!
636763f356cSTakashi Iwai */
637ffb2c3c0SRemy Bruno #define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
638763f356cSTakashi Iwai #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
639763f356cSTakashi Iwai 
6400dca1793SAdrian Knoth #define HDSPM_RAYDAT_REV	211
6410dca1793SAdrian Knoth #define HDSPM_AIO_REV		212
6420dca1793SAdrian Knoth #define HDSPM_MADIFACE_REV	213
6433cee5a60SRemy Bruno 
6446534599dSRemy Bruno /* speed factor modes */
6456534599dSRemy Bruno #define HDSPM_SPEED_SINGLE 0
6466534599dSRemy Bruno #define HDSPM_SPEED_DOUBLE 1
6476534599dSRemy Bruno #define HDSPM_SPEED_QUAD   2
6480dca1793SAdrian Knoth 
6496534599dSRemy Bruno /* names for speed modes */
6506534599dSRemy Bruno static char *hdspm_speed_names[] = { "single", "double", "quad" };
6516534599dSRemy Bruno 
652eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aes_tco[] = { "Word Clock",
6530dca1793SAdrian Knoth 					  "AES1", "AES2", "AES3", "AES4",
6540dca1793SAdrian Knoth 					  "AES5", "AES6", "AES7", "AES8",
655db2d1a91SAdrian Knoth 					  "TCO", "Sync In"
656db2d1a91SAdrian Knoth };
657eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aes[] = { "Word Clock",
6580dca1793SAdrian Knoth 				      "AES1", "AES2", "AES3", "AES4",
659db2d1a91SAdrian Knoth 				      "AES5", "AES6", "AES7", "AES8",
660db2d1a91SAdrian Knoth 				      "Sync In"
661db2d1a91SAdrian Knoth };
662eb0d4dbfSAdrian Knoth static const char *const texts_autosync_madi_tco[] = { "Word Clock",
6630dca1793SAdrian Knoth 					   "MADI", "TCO", "Sync In" };
664eb0d4dbfSAdrian Knoth static const char *const texts_autosync_madi[] = { "Word Clock",
6650dca1793SAdrian Knoth 				       "MADI", "Sync In" };
6660dca1793SAdrian Knoth 
667eb0d4dbfSAdrian Knoth static const char *const texts_autosync_raydat_tco[] = {
6680dca1793SAdrian Knoth 	"Word Clock",
6690dca1793SAdrian Knoth 	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
6700dca1793SAdrian Knoth 	"AES", "SPDIF", "TCO", "Sync In"
6710dca1793SAdrian Knoth };
672eb0d4dbfSAdrian Knoth static const char *const texts_autosync_raydat[] = {
6730dca1793SAdrian Knoth 	"Word Clock",
6740dca1793SAdrian Knoth 	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
6750dca1793SAdrian Knoth 	"AES", "SPDIF", "Sync In"
6760dca1793SAdrian Knoth };
677eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aio_tco[] = {
6780dca1793SAdrian Knoth 	"Word Clock",
6790dca1793SAdrian Knoth 	"ADAT", "AES", "SPDIF", "TCO", "Sync In"
6800dca1793SAdrian Knoth };
681eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aio[] = { "Word Clock",
6820dca1793SAdrian Knoth 				      "ADAT", "AES", "SPDIF", "Sync In" };
6830dca1793SAdrian Knoth 
68438816545SAdrian Knoth static const char *const texts_freq[] = {
6850dca1793SAdrian Knoth 	"No Lock",
6860dca1793SAdrian Knoth 	"32 kHz",
6870dca1793SAdrian Knoth 	"44.1 kHz",
6880dca1793SAdrian Knoth 	"48 kHz",
6890dca1793SAdrian Knoth 	"64 kHz",
6900dca1793SAdrian Knoth 	"88.2 kHz",
6910dca1793SAdrian Knoth 	"96 kHz",
6920dca1793SAdrian Knoth 	"128 kHz",
6930dca1793SAdrian Knoth 	"176.4 kHz",
6940dca1793SAdrian Knoth 	"192 kHz"
6950dca1793SAdrian Knoth };
6960dca1793SAdrian Knoth 
6970dca1793SAdrian Knoth static char *texts_ports_madi[] = {
6980dca1793SAdrian Knoth 	"MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6",
6990dca1793SAdrian Knoth 	"MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12",
7000dca1793SAdrian Knoth 	"MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18",
7010dca1793SAdrian Knoth 	"MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24",
7020dca1793SAdrian Knoth 	"MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30",
7030dca1793SAdrian Knoth 	"MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36",
7040dca1793SAdrian Knoth 	"MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42",
7050dca1793SAdrian Knoth 	"MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48",
7060dca1793SAdrian Knoth 	"MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54",
7070dca1793SAdrian Knoth 	"MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60",
7080dca1793SAdrian Knoth 	"MADI.61", "MADI.62", "MADI.63", "MADI.64",
7090dca1793SAdrian Knoth };
7100dca1793SAdrian Knoth 
7110dca1793SAdrian Knoth 
7120dca1793SAdrian Knoth static char *texts_ports_raydat_ss[] = {
7130dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6",
7140dca1793SAdrian Knoth 	"ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
7150dca1793SAdrian Knoth 	"ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2",
7160dca1793SAdrian Knoth 	"ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8",
7170dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6",
7180dca1793SAdrian Knoth 	"ADAT4.7", "ADAT4.8",
7190dca1793SAdrian Knoth 	"AES.L", "AES.R",
7200dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
7210dca1793SAdrian Knoth };
7220dca1793SAdrian Knoth 
7230dca1793SAdrian Knoth static char *texts_ports_raydat_ds[] = {
7240dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4",
7250dca1793SAdrian Knoth 	"ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
7260dca1793SAdrian Knoth 	"ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4",
7270dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4",
7280dca1793SAdrian Knoth 	"AES.L", "AES.R",
7290dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
7300dca1793SAdrian Knoth };
7310dca1793SAdrian Knoth 
7320dca1793SAdrian Knoth static char *texts_ports_raydat_qs[] = {
7330dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2",
7340dca1793SAdrian Knoth 	"ADAT2.1", "ADAT2.2",
7350dca1793SAdrian Knoth 	"ADAT3.1", "ADAT3.2",
7360dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2",
7370dca1793SAdrian Knoth 	"AES.L", "AES.R",
7380dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
7390dca1793SAdrian Knoth };
7400dca1793SAdrian Knoth 
7410dca1793SAdrian Knoth 
7420dca1793SAdrian Knoth static char *texts_ports_aio_in_ss[] = {
7430dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7440dca1793SAdrian Knoth 	"AES.L", "AES.R",
7450dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7460dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
7473de9db26SAdrian Knoth 	"ADAT.7", "ADAT.8",
7483de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7490dca1793SAdrian Knoth };
7500dca1793SAdrian Knoth 
7510dca1793SAdrian Knoth static char *texts_ports_aio_out_ss[] = {
7520dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7530dca1793SAdrian Knoth 	"AES.L", "AES.R",
7540dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7550dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
7560dca1793SAdrian Knoth 	"ADAT.7", "ADAT.8",
7573de9db26SAdrian Knoth 	"Phone.L", "Phone.R",
7583de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7590dca1793SAdrian Knoth };
7600dca1793SAdrian Knoth 
7610dca1793SAdrian Knoth static char *texts_ports_aio_in_ds[] = {
7620dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7630dca1793SAdrian Knoth 	"AES.L", "AES.R",
7640dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7653de9db26SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7663de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7670dca1793SAdrian Knoth };
7680dca1793SAdrian Knoth 
7690dca1793SAdrian Knoth static char *texts_ports_aio_out_ds[] = {
7700dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7710dca1793SAdrian Knoth 	"AES.L", "AES.R",
7720dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7730dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7743de9db26SAdrian Knoth 	"Phone.L", "Phone.R",
7753de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7760dca1793SAdrian Knoth };
7770dca1793SAdrian Knoth 
7780dca1793SAdrian Knoth static char *texts_ports_aio_in_qs[] = {
7790dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7800dca1793SAdrian Knoth 	"AES.L", "AES.R",
7810dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7823de9db26SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7833de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7840dca1793SAdrian Knoth };
7850dca1793SAdrian Knoth 
7860dca1793SAdrian Knoth static char *texts_ports_aio_out_qs[] = {
7870dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7880dca1793SAdrian Knoth 	"AES.L", "AES.R",
7890dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7900dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7913de9db26SAdrian Knoth 	"Phone.L", "Phone.R",
7923de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7930dca1793SAdrian Knoth };
7940dca1793SAdrian Knoth 
795432d2500SAdrian Knoth static char *texts_ports_aes32[] = {
796432d2500SAdrian Knoth 	"AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7",
797432d2500SAdrian Knoth 	"AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14",
798432d2500SAdrian Knoth 	"AES.15", "AES.16"
799432d2500SAdrian Knoth };
800432d2500SAdrian Knoth 
80155a57606SAdrian Knoth /* These tables map the ALSA channels 1..N to the channels that we
80255a57606SAdrian Knoth    need to use in order to find the relevant channel buffer. RME
80355a57606SAdrian Knoth    refers to this kind of mapping as between "the ADAT channel and
80455a57606SAdrian Knoth    the DMA channel." We index it using the logical audio channel,
80555a57606SAdrian Knoth    and the value is the DMA channel (i.e. channel buffer number)
80655a57606SAdrian Knoth    where the data for that channel can be read/written from/to.
80755a57606SAdrian Knoth */
80855a57606SAdrian Knoth 
80955a57606SAdrian Knoth static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
81055a57606SAdrian Knoth 	0, 1, 2, 3, 4, 5, 6, 7,
81155a57606SAdrian Knoth 	8, 9, 10, 11, 12, 13, 14, 15,
81255a57606SAdrian Knoth 	16, 17, 18, 19, 20, 21, 22, 23,
81355a57606SAdrian Knoth 	24, 25, 26, 27, 28, 29, 30, 31,
81455a57606SAdrian Knoth 	32, 33, 34, 35, 36, 37, 38, 39,
81555a57606SAdrian Knoth 	40, 41, 42, 43, 44, 45, 46, 47,
81655a57606SAdrian Knoth 	48, 49, 50, 51, 52, 53, 54, 55,
81755a57606SAdrian Knoth 	56, 57, 58, 59, 60, 61, 62, 63
81855a57606SAdrian Knoth };
81955a57606SAdrian Knoth 
82055a57606SAdrian Knoth static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
82155a57606SAdrian Knoth 	4, 5, 6, 7, 8, 9, 10, 11,	/* ADAT 1 */
82255a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT 2 */
82355a57606SAdrian Knoth 	20, 21, 22, 23, 24, 25, 26, 27,	/* ADAT 3 */
82455a57606SAdrian Knoth 	28, 29, 30, 31, 32, 33, 34, 35,	/* ADAT 4 */
82555a57606SAdrian Knoth 	0, 1,			/* AES */
82655a57606SAdrian Knoth 	2, 3,			/* SPDIF */
82755a57606SAdrian Knoth 	-1, -1, -1, -1,
82855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
82955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
83055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
83155a57606SAdrian Knoth };
83255a57606SAdrian Knoth 
83355a57606SAdrian Knoth static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
83455a57606SAdrian Knoth 	4, 5, 6, 7,		/* ADAT 1 */
83555a57606SAdrian Knoth 	8, 9, 10, 11,		/* ADAT 2 */
83655a57606SAdrian Knoth 	12, 13, 14, 15,		/* ADAT 3 */
83755a57606SAdrian Knoth 	16, 17, 18, 19,		/* ADAT 4 */
83855a57606SAdrian Knoth 	0, 1,			/* AES */
83955a57606SAdrian Knoth 	2, 3,			/* SPDIF */
84055a57606SAdrian Knoth 	-1, -1, -1, -1,
84155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84655a57606SAdrian Knoth };
84755a57606SAdrian Knoth 
84855a57606SAdrian Knoth static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
84955a57606SAdrian Knoth 	4, 5,			/* ADAT 1 */
85055a57606SAdrian Knoth 	6, 7,			/* ADAT 2 */
85155a57606SAdrian Knoth 	8, 9,			/* ADAT 3 */
85255a57606SAdrian Knoth 	10, 11,			/* ADAT 4 */
85355a57606SAdrian Knoth 	0, 1,			/* AES */
85455a57606SAdrian Knoth 	2, 3,			/* SPDIF */
85555a57606SAdrian Knoth 	-1, -1, -1, -1,
85655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
85755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
85855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
85955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
86055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
86155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
86255a57606SAdrian Knoth };
86355a57606SAdrian Knoth 
86455a57606SAdrian Knoth static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
86555a57606SAdrian Knoth 	0, 1,			/* line in */
86655a57606SAdrian Knoth 	8, 9,			/* aes in, */
86755a57606SAdrian Knoth 	10, 11,			/* spdif in */
86855a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT in */
8693de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
8703de9db26SAdrian Knoth 	-1, -1, -1, -1, -1, -1,
87155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87655a57606SAdrian Knoth };
87755a57606SAdrian Knoth 
87855a57606SAdrian Knoth static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
87955a57606SAdrian Knoth 	0, 1,			/* line out */
88055a57606SAdrian Knoth 	8, 9,			/* aes out */
88155a57606SAdrian Knoth 	10, 11,			/* spdif out */
88255a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT out */
88355a57606SAdrian Knoth 	6, 7,			/* phone out */
8843de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
8853de9db26SAdrian Knoth 	-1, -1, -1, -1,
88655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
88755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
88855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
88955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
89055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
89155a57606SAdrian Knoth };
89255a57606SAdrian Knoth 
89355a57606SAdrian Knoth static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
89455a57606SAdrian Knoth 	0, 1,			/* line in */
89555a57606SAdrian Knoth 	8, 9,			/* aes in */
89655a57606SAdrian Knoth 	10, 11,			/* spdif in */
89755a57606SAdrian Knoth 	12, 14, 16, 18,		/* adat in */
8983de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
8993de9db26SAdrian Knoth 	-1, -1,
90055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
90655a57606SAdrian Knoth };
90755a57606SAdrian Knoth 
90855a57606SAdrian Knoth static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
90955a57606SAdrian Knoth 	0, 1,			/* line out */
91055a57606SAdrian Knoth 	8, 9,			/* aes out */
91155a57606SAdrian Knoth 	10, 11,			/* spdif out */
91255a57606SAdrian Knoth 	12, 14, 16, 18,		/* adat out */
91355a57606SAdrian Knoth 	6, 7,			/* phone out */
9143de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
91555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
91655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
91755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
91855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
91955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
92055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
92155a57606SAdrian Knoth };
92255a57606SAdrian Knoth 
92355a57606SAdrian Knoth static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
92455a57606SAdrian Knoth 	0, 1,			/* line in */
92555a57606SAdrian Knoth 	8, 9,			/* aes in */
92655a57606SAdrian Knoth 	10, 11,			/* spdif in */
92755a57606SAdrian Knoth 	12, 16,			/* adat in */
9283de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
9293de9db26SAdrian Knoth 	-1, -1, -1, -1,
93055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
93655a57606SAdrian Knoth };
93755a57606SAdrian Knoth 
93855a57606SAdrian Knoth static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
93955a57606SAdrian Knoth 	0, 1,			/* line out */
94055a57606SAdrian Knoth 	8, 9,			/* aes out */
94155a57606SAdrian Knoth 	10, 11,			/* spdif out */
94255a57606SAdrian Knoth 	12, 16,			/* adat out */
94355a57606SAdrian Knoth 	6, 7,			/* phone out */
9443de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
9453de9db26SAdrian Knoth 	-1, -1,
94655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
94755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
94855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
94955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
95055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
95155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
95255a57606SAdrian Knoth };
95355a57606SAdrian Knoth 
954432d2500SAdrian Knoth static char channel_map_aes32[HDSPM_MAX_CHANNELS] = {
955432d2500SAdrian Knoth 	0, 1, 2, 3, 4, 5, 6, 7,
956432d2500SAdrian Knoth 	8, 9, 10, 11, 12, 13, 14, 15,
957432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
958432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
959432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
960432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
961432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
962432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
963432d2500SAdrian Knoth };
964432d2500SAdrian Knoth 
96598274f07STakashi Iwai struct hdspm_midi {
96698274f07STakashi Iwai 	struct hdspm *hdspm;
967763f356cSTakashi Iwai 	int id;
96898274f07STakashi Iwai 	struct snd_rawmidi *rmidi;
96998274f07STakashi Iwai 	struct snd_rawmidi_substream *input;
97098274f07STakashi Iwai 	struct snd_rawmidi_substream *output;
971763f356cSTakashi Iwai 	char istimer;		/* timer in use */
972763f356cSTakashi Iwai 	struct timer_list timer;
973763f356cSTakashi Iwai 	spinlock_t lock;
974763f356cSTakashi Iwai 	int pending;
9750dca1793SAdrian Knoth 	int dataIn;
9760dca1793SAdrian Knoth 	int statusIn;
9770dca1793SAdrian Knoth 	int dataOut;
9780dca1793SAdrian Knoth 	int statusOut;
9790dca1793SAdrian Knoth 	int ie;
9800dca1793SAdrian Knoth 	int irq;
9810dca1793SAdrian Knoth };
9820dca1793SAdrian Knoth 
9830dca1793SAdrian Knoth struct hdspm_tco {
984*69358fcaSMartin Dausel 	int input; /* 0: LTC, 1:Video, 2: WC*/
985*69358fcaSMartin Dausel 	int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */
986*69358fcaSMartin Dausel 	int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */
987*69358fcaSMartin Dausel 	int samplerate; /* 0=44.1, 1=48, 2= freq from app */
988*69358fcaSMartin Dausel 	int pull; /*   0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/
9890dca1793SAdrian Knoth 	int term; /* 0 = off, 1 = on */
990763f356cSTakashi Iwai };
991763f356cSTakashi Iwai 
99298274f07STakashi Iwai struct hdspm {
993763f356cSTakashi Iwai         spinlock_t lock;
994ef5fa1a4STakashi Iwai 	/* only one playback and/or capture stream */
995ef5fa1a4STakashi Iwai         struct snd_pcm_substream *capture_substream;
996ef5fa1a4STakashi Iwai         struct snd_pcm_substream *playback_substream;
997763f356cSTakashi Iwai 
998763f356cSTakashi Iwai 	char *card_name;	     /* for procinfo */
9993cee5a60SRemy Bruno 	unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
10003cee5a60SRemy Bruno 
10010dca1793SAdrian Knoth 	uint8_t io_type;
1002763f356cSTakashi Iwai 
1003763f356cSTakashi Iwai 	int monitor_outs;	/* set up monitoring outs init flag */
1004763f356cSTakashi Iwai 
1005763f356cSTakashi Iwai 	u32 control_register;	/* cached value */
1006763f356cSTakashi Iwai 	u32 control2_register;	/* cached value */
1007*69358fcaSMartin Dausel 	u32 settings_register;  /* cached value for AIO / RayDat (sync reference, master/slave) */
1008763f356cSTakashi Iwai 
10090dca1793SAdrian Knoth 	struct hdspm_midi midi[4];
1010763f356cSTakashi Iwai 	struct tasklet_struct midi_tasklet;
1011763f356cSTakashi Iwai 
1012763f356cSTakashi Iwai 	size_t period_bytes;
10130dca1793SAdrian Knoth 	unsigned char ss_in_channels;
10140dca1793SAdrian Knoth 	unsigned char ds_in_channels;
10150dca1793SAdrian Knoth 	unsigned char qs_in_channels;
10160dca1793SAdrian Knoth 	unsigned char ss_out_channels;
10170dca1793SAdrian Knoth 	unsigned char ds_out_channels;
10180dca1793SAdrian Knoth 	unsigned char qs_out_channels;
10190dca1793SAdrian Knoth 
10200dca1793SAdrian Knoth 	unsigned char max_channels_in;
10210dca1793SAdrian Knoth 	unsigned char max_channels_out;
10220dca1793SAdrian Knoth 
1023286bed0fSTakashi Iwai 	signed char *channel_map_in;
1024286bed0fSTakashi Iwai 	signed char *channel_map_out;
10250dca1793SAdrian Knoth 
1026286bed0fSTakashi Iwai 	signed char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
1027286bed0fSTakashi Iwai 	signed char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
10280dca1793SAdrian Knoth 
10290dca1793SAdrian Knoth 	char **port_names_in;
10300dca1793SAdrian Knoth 	char **port_names_out;
10310dca1793SAdrian Knoth 
10320dca1793SAdrian Knoth 	char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs;
10330dca1793SAdrian Knoth 	char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs;
1034763f356cSTakashi Iwai 
1035763f356cSTakashi Iwai 	unsigned char *playback_buffer;	/* suitably aligned address */
1036763f356cSTakashi Iwai 	unsigned char *capture_buffer;	/* suitably aligned address */
1037763f356cSTakashi Iwai 
1038763f356cSTakashi Iwai 	pid_t capture_pid;	/* process id which uses capture */
1039763f356cSTakashi Iwai 	pid_t playback_pid;	/* process id which uses capture */
1040763f356cSTakashi Iwai 	int running;		/* running status */
1041763f356cSTakashi Iwai 
1042763f356cSTakashi Iwai 	int last_external_sample_rate;	/* samplerate mystic ... */
1043763f356cSTakashi Iwai 	int last_internal_sample_rate;
1044763f356cSTakashi Iwai 	int system_sample_rate;
1045763f356cSTakashi Iwai 
1046763f356cSTakashi Iwai 	int dev;		/* Hardware vars... */
1047763f356cSTakashi Iwai 	int irq;
1048763f356cSTakashi Iwai 	unsigned long port;
1049763f356cSTakashi Iwai 	void __iomem *iobase;
1050763f356cSTakashi Iwai 
1051763f356cSTakashi Iwai 	int irq_count;		/* for debug */
10520dca1793SAdrian Knoth 	int midiPorts;
1053763f356cSTakashi Iwai 
105498274f07STakashi Iwai 	struct snd_card *card;	/* one card */
105598274f07STakashi Iwai 	struct snd_pcm *pcm;		/* has one pcm */
105698274f07STakashi Iwai 	struct snd_hwdep *hwdep;	/* and a hwdep for additional ioctl */
1057763f356cSTakashi Iwai 	struct pci_dev *pci;	/* and an pci info */
1058763f356cSTakashi Iwai 
1059763f356cSTakashi Iwai 	/* Mixer vars */
1060ef5fa1a4STakashi Iwai 	/* fast alsa mixer */
1061ef5fa1a4STakashi Iwai 	struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS];
1062ef5fa1a4STakashi Iwai 	/* but input to much, so not used */
1063ef5fa1a4STakashi Iwai 	struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS];
106425985edcSLucas De Marchi 	/* full mixer accessible over mixer ioctl or hwdep-device */
1065ef5fa1a4STakashi Iwai 	struct hdspm_mixer *mixer;
1066763f356cSTakashi Iwai 
10670dca1793SAdrian Knoth 	struct hdspm_tco *tco;  /* NULL if no TCO detected */
1068763f356cSTakashi Iwai 
1069eb0d4dbfSAdrian Knoth 	const char *const *texts_autosync;
10700dca1793SAdrian Knoth 	int texts_autosync_items;
1071763f356cSTakashi Iwai 
10720dca1793SAdrian Knoth 	cycles_t last_interrupt;
1073730a5865SJaroslav Kysela 
10747d53a631SAdrian Knoth 	unsigned int serial;
10757d53a631SAdrian Knoth 
1076730a5865SJaroslav Kysela 	struct hdspm_peak_rms peak_rms;
1077763f356cSTakashi Iwai };
1078763f356cSTakashi Iwai 
1079763f356cSTakashi Iwai 
1080cebe41d4SAlexey Dobriyan static DEFINE_PCI_DEVICE_TABLE(snd_hdspm_ids) = {
1081763f356cSTakashi Iwai 	{
1082763f356cSTakashi Iwai 	 .vendor = PCI_VENDOR_ID_XILINX,
1083763f356cSTakashi Iwai 	 .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
1084763f356cSTakashi Iwai 	 .subvendor = PCI_ANY_ID,
1085763f356cSTakashi Iwai 	 .subdevice = PCI_ANY_ID,
1086763f356cSTakashi Iwai 	 .class = 0,
1087763f356cSTakashi Iwai 	 .class_mask = 0,
1088763f356cSTakashi Iwai 	 .driver_data = 0},
1089763f356cSTakashi Iwai 	{0,}
1090763f356cSTakashi Iwai };
1091763f356cSTakashi Iwai 
1092763f356cSTakashi Iwai MODULE_DEVICE_TABLE(pci, snd_hdspm_ids);
1093763f356cSTakashi Iwai 
1094763f356cSTakashi Iwai /* prototypes */
1095e23e7a14SBill Pemberton static int snd_hdspm_create_alsa_devices(struct snd_card *card,
109698274f07STakashi Iwai 					 struct hdspm *hdspm);
1097e23e7a14SBill Pemberton static int snd_hdspm_create_pcm(struct snd_card *card,
109898274f07STakashi Iwai 				struct hdspm *hdspm);
1099763f356cSTakashi Iwai 
110098274f07STakashi Iwai static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
11013f7bf918SAdrian Knoth static inline int hdspm_get_pll_freq(struct hdspm *hdspm);
110298274f07STakashi Iwai static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
110398274f07STakashi Iwai static int hdspm_autosync_ref(struct hdspm *hdspm);
110434be7ebbSAdrian Knoth static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out);
110598274f07STakashi Iwai static int snd_hdspm_set_defaults(struct hdspm *hdspm);
110621a164dfSAdrian Knoth static int hdspm_system_clock_mode(struct hdspm *hdspm);
110777a23f26STakashi Iwai static void hdspm_set_sgbuf(struct hdspm *hdspm,
110877a23f26STakashi Iwai 			    struct snd_pcm_substream *substream,
1109763f356cSTakashi Iwai 			     unsigned int reg, int channels);
1110763f356cSTakashi Iwai 
11115b266354SAdrian Knoth static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx);
11125b266354SAdrian Knoth static int hdspm_wc_sync_check(struct hdspm *hdspm);
11135b266354SAdrian Knoth static int hdspm_tco_sync_check(struct hdspm *hdspm);
11145b266354SAdrian Knoth static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
11155b266354SAdrian Knoth 
11165b266354SAdrian Knoth static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index);
11175b266354SAdrian Knoth static int hdspm_get_tco_sample_rate(struct hdspm *hdspm);
11185b266354SAdrian Knoth static int hdspm_get_wc_sample_rate(struct hdspm *hdspm);
11195b266354SAdrian Knoth 
11205b266354SAdrian Knoth 
11215b266354SAdrian Knoth 
11223cee5a60SRemy Bruno static inline int HDSPM_bit2freq(int n)
11233cee5a60SRemy Bruno {
112462cef821SDenys Vlasenko 	static const int bit2freq_tab[] = {
112562cef821SDenys Vlasenko 		0, 32000, 44100, 48000, 64000, 88200,
11263cee5a60SRemy Bruno 		96000, 128000, 176400, 192000 };
11273cee5a60SRemy Bruno 	if (n < 1 || n > 9)
11283cee5a60SRemy Bruno 		return 0;
11293cee5a60SRemy Bruno 	return bit2freq_tab[n];
11303cee5a60SRemy Bruno }
11313cee5a60SRemy Bruno 
1132b2ed6326SAdrian Knoth static bool hdspm_is_raydat_or_aio(struct hdspm *hdspm)
1133b2ed6326SAdrian Knoth {
1134b2ed6326SAdrian Knoth 	return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type));
1135b2ed6326SAdrian Knoth }
1136b2ed6326SAdrian Knoth 
1137b2ed6326SAdrian Knoth 
11380dca1793SAdrian Knoth /* Write/read to/from HDSPM with Adresses in Bytes
1139763f356cSTakashi Iwai    not words but only 32Bit writes are allowed */
1140763f356cSTakashi Iwai 
114198274f07STakashi Iwai static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg,
1142763f356cSTakashi Iwai 			       unsigned int val)
1143763f356cSTakashi Iwai {
1144763f356cSTakashi Iwai 	writel(val, hdspm->iobase + reg);
1145763f356cSTakashi Iwai }
1146763f356cSTakashi Iwai 
114798274f07STakashi Iwai static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg)
1148763f356cSTakashi Iwai {
1149763f356cSTakashi Iwai 	return readl(hdspm->iobase + reg);
1150763f356cSTakashi Iwai }
1151763f356cSTakashi Iwai 
1152763f356cSTakashi Iwai /* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
1153763f356cSTakashi Iwai    mixer is write only on hardware so we have to cache him for read
1154763f356cSTakashi Iwai    each fader is a u32, but uses only the first 16 bit */
1155763f356cSTakashi Iwai 
115698274f07STakashi Iwai static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan,
1157763f356cSTakashi Iwai 				     unsigned int in)
1158763f356cSTakashi Iwai {
11595bab2482SAdrian Bunk 	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
1160763f356cSTakashi Iwai 		return 0;
1161763f356cSTakashi Iwai 
1162763f356cSTakashi Iwai 	return hdspm->mixer->ch[chan].in[in];
1163763f356cSTakashi Iwai }
1164763f356cSTakashi Iwai 
116598274f07STakashi Iwai static inline int hdspm_read_pb_gain(struct hdspm * hdspm, unsigned int chan,
1166763f356cSTakashi Iwai 				     unsigned int pb)
1167763f356cSTakashi Iwai {
11685bab2482SAdrian Bunk 	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
1169763f356cSTakashi Iwai 		return 0;
1170763f356cSTakashi Iwai 	return hdspm->mixer->ch[chan].pb[pb];
1171763f356cSTakashi Iwai }
1172763f356cSTakashi Iwai 
117362cef821SDenys Vlasenko static int hdspm_write_in_gain(struct hdspm *hdspm, unsigned int chan,
1174763f356cSTakashi Iwai 				      unsigned int in, unsigned short data)
1175763f356cSTakashi Iwai {
1176763f356cSTakashi Iwai 	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
1177763f356cSTakashi Iwai 		return -1;
1178763f356cSTakashi Iwai 
1179763f356cSTakashi Iwai 	hdspm_write(hdspm,
1180763f356cSTakashi Iwai 		    HDSPM_MADI_mixerBase +
1181763f356cSTakashi Iwai 		    ((in + 128 * chan) * sizeof(u32)),
1182763f356cSTakashi Iwai 		    (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF));
1183763f356cSTakashi Iwai 	return 0;
1184763f356cSTakashi Iwai }
1185763f356cSTakashi Iwai 
118662cef821SDenys Vlasenko static int hdspm_write_pb_gain(struct hdspm *hdspm, unsigned int chan,
1187763f356cSTakashi Iwai 				      unsigned int pb, unsigned short data)
1188763f356cSTakashi Iwai {
1189763f356cSTakashi Iwai 	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
1190763f356cSTakashi Iwai 		return -1;
1191763f356cSTakashi Iwai 
1192763f356cSTakashi Iwai 	hdspm_write(hdspm,
1193763f356cSTakashi Iwai 		    HDSPM_MADI_mixerBase +
1194763f356cSTakashi Iwai 		    ((64 + pb + 128 * chan) * sizeof(u32)),
1195763f356cSTakashi Iwai 		    (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF));
1196763f356cSTakashi Iwai 	return 0;
1197763f356cSTakashi Iwai }
1198763f356cSTakashi Iwai 
1199763f356cSTakashi Iwai 
1200763f356cSTakashi Iwai /* enable DMA for specific channels, now available for DSP-MADI */
120198274f07STakashi Iwai static inline void snd_hdspm_enable_in(struct hdspm * hdspm, int i, int v)
1202763f356cSTakashi Iwai {
1203763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_inputEnableBase + (4 * i), v);
1204763f356cSTakashi Iwai }
1205763f356cSTakashi Iwai 
120698274f07STakashi Iwai static inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v)
1207763f356cSTakashi Iwai {
1208763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_outputEnableBase + (4 * i), v);
1209763f356cSTakashi Iwai }
1210763f356cSTakashi Iwai 
1211763f356cSTakashi Iwai /* check if same process is writing and reading */
121262cef821SDenys Vlasenko static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
1213763f356cSTakashi Iwai {
1214763f356cSTakashi Iwai 	unsigned long flags;
1215763f356cSTakashi Iwai 	int ret = 1;
1216763f356cSTakashi Iwai 
1217763f356cSTakashi Iwai 	spin_lock_irqsave(&hdspm->lock, flags);
1218763f356cSTakashi Iwai 	if ((hdspm->playback_pid != hdspm->capture_pid) &&
1219763f356cSTakashi Iwai 	    (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) {
1220763f356cSTakashi Iwai 		ret = 0;
1221763f356cSTakashi Iwai 	}
1222763f356cSTakashi Iwai 	spin_unlock_irqrestore(&hdspm->lock, flags);
1223763f356cSTakashi Iwai 	return ret;
1224763f356cSTakashi Iwai }
1225763f356cSTakashi Iwai 
1226fcdc4ba1SAdrian Knoth /* round arbitary sample rates to commonly known rates */
1227fcdc4ba1SAdrian Knoth static int hdspm_round_frequency(int rate)
1228fcdc4ba1SAdrian Knoth {
1229fcdc4ba1SAdrian Knoth 	if (rate < 38050)
1230fcdc4ba1SAdrian Knoth 		return 32000;
1231fcdc4ba1SAdrian Knoth 	if (rate < 46008)
1232fcdc4ba1SAdrian Knoth 		return 44100;
1233fcdc4ba1SAdrian Knoth 	else
1234fcdc4ba1SAdrian Knoth 		return 48000;
1235fcdc4ba1SAdrian Knoth }
1236fcdc4ba1SAdrian Knoth 
1237a8a729faSAdrian Knoth /* QS and DS rates normally can not be detected
1238a8a729faSAdrian Knoth  * automatically by the card. Only exception is MADI
1239a8a729faSAdrian Knoth  * in 96k frame mode.
1240a8a729faSAdrian Knoth  *
1241a8a729faSAdrian Knoth  * So if we read SS values (32 .. 48k), check for
1242a8a729faSAdrian Knoth  * user-provided DS/QS bits in the control register
1243a8a729faSAdrian Knoth  * and multiply the base frequency accordingly.
1244a8a729faSAdrian Knoth  */
1245a8a729faSAdrian Knoth static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate)
1246a8a729faSAdrian Knoth {
1247a8a729faSAdrian Knoth 	if (rate <= 48000) {
1248a8a729faSAdrian Knoth 		if (hdspm->control_register & HDSPM_QuadSpeed)
1249a8a729faSAdrian Knoth 			return rate * 4;
1250a8a729faSAdrian Knoth 		else if (hdspm->control_register &
1251a8a729faSAdrian Knoth 				HDSPM_DoubleSpeed)
1252a8a729faSAdrian Knoth 			return rate * 2;
1253a8a729faSAdrian Knoth 	};
1254a8a729faSAdrian Knoth 	return rate;
1255a8a729faSAdrian Knoth }
1256a8a729faSAdrian Knoth 
12575b266354SAdrian Knoth /* check for external sample rate, returns the sample rate in Hz*/
125862cef821SDenys Vlasenko static int hdspm_external_sample_rate(struct hdspm *hdspm)
1259763f356cSTakashi Iwai {
12600dca1793SAdrian Knoth 	unsigned int status, status2, timecode;
12610dca1793SAdrian Knoth 	int syncref, rate = 0, rate_bits;
12623cee5a60SRemy Bruno 
12630dca1793SAdrian Knoth 	switch (hdspm->io_type) {
12640dca1793SAdrian Knoth 	case AES32:
12650dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
12660dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
12670dca1793SAdrian Knoth 		timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
12680dca1793SAdrian Knoth 
12690dca1793SAdrian Knoth 		syncref = hdspm_autosync_ref(hdspm);
1270dbae4a0cSAdrian Knoth 		switch (syncref) {
1271dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_WORD:
1272dbae4a0cSAdrian Knoth 		/* Check WC sync and get sample rate */
1273dbae4a0cSAdrian Knoth 			if (hdspm_wc_sync_check(hdspm))
1274dbae4a0cSAdrian Knoth 				return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm));
1275dbae4a0cSAdrian Knoth 			break;
12763cee5a60SRemy Bruno 
1277dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES1:
1278dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES2:
1279dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES3:
1280dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES4:
1281dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES5:
1282dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES6:
1283dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES7:
1284dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES8:
1285dbae4a0cSAdrian Knoth 		/* Check AES sync and get sample rate */
1286dbae4a0cSAdrian Knoth 			if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))
1287dbae4a0cSAdrian Knoth 				return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm,
1288dbae4a0cSAdrian Knoth 							syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1));
1289dbae4a0cSAdrian Knoth 			break;
12900dca1793SAdrian Knoth 
1291dbae4a0cSAdrian Knoth 
1292dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_TCO:
1293dbae4a0cSAdrian Knoth 		/* Check TCO sync and get sample rate */
1294dbae4a0cSAdrian Knoth 			if (hdspm_tco_sync_check(hdspm))
1295dbae4a0cSAdrian Knoth 				return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm));
1296dbae4a0cSAdrian Knoth 			break;
1297dbae4a0cSAdrian Knoth 		default:
12983cee5a60SRemy Bruno 			return 0;
1299dbae4a0cSAdrian Knoth 		} /* end switch(syncref) */
13000dca1793SAdrian Knoth 		break;
13010dca1793SAdrian Knoth 
13020dca1793SAdrian Knoth 	case MADIface:
13030dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
13040dca1793SAdrian Knoth 
13050dca1793SAdrian Knoth 		if (!(status & HDSPM_madiLock)) {
13060dca1793SAdrian Knoth 			rate = 0;  /* no lock */
13073cee5a60SRemy Bruno 		} else {
13080dca1793SAdrian Knoth 			switch (status & (HDSPM_status1_freqMask)) {
13090dca1793SAdrian Knoth 			case HDSPM_status1_F_0*1:
13100dca1793SAdrian Knoth 				rate = 32000; break;
13110dca1793SAdrian Knoth 			case HDSPM_status1_F_0*2:
13120dca1793SAdrian Knoth 				rate = 44100; break;
13130dca1793SAdrian Knoth 			case HDSPM_status1_F_0*3:
13140dca1793SAdrian Knoth 				rate = 48000; break;
13150dca1793SAdrian Knoth 			case HDSPM_status1_F_0*4:
13160dca1793SAdrian Knoth 				rate = 64000; break;
13170dca1793SAdrian Knoth 			case HDSPM_status1_F_0*5:
13180dca1793SAdrian Knoth 				rate = 88200; break;
13190dca1793SAdrian Knoth 			case HDSPM_status1_F_0*6:
13200dca1793SAdrian Knoth 				rate = 96000; break;
13210dca1793SAdrian Knoth 			case HDSPM_status1_F_0*7:
13220dca1793SAdrian Knoth 				rate = 128000; break;
13230dca1793SAdrian Knoth 			case HDSPM_status1_F_0*8:
13240dca1793SAdrian Knoth 				rate = 176400; break;
13250dca1793SAdrian Knoth 			case HDSPM_status1_F_0*9:
13260dca1793SAdrian Knoth 				rate = 192000; break;
13270dca1793SAdrian Knoth 			default:
13280dca1793SAdrian Knoth 				rate = 0; break;
13290dca1793SAdrian Knoth 			}
13300dca1793SAdrian Knoth 		}
13310dca1793SAdrian Knoth 
13320dca1793SAdrian Knoth 		break;
13330dca1793SAdrian Knoth 
13340dca1793SAdrian Knoth 	case MADI:
13350dca1793SAdrian Knoth 	case AIO:
13360dca1793SAdrian Knoth 	case RayDAT:
13370dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
13380dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
13390dca1793SAdrian Knoth 		rate = 0;
1340763f356cSTakashi Iwai 
1341763f356cSTakashi Iwai 		/* if wordclock has synced freq and wordclock is valid */
1342763f356cSTakashi Iwai 		if ((status2 & HDSPM_wcLock) != 0 &&
1343fedf1535SAdrian Knoth 				(status2 & HDSPM_SelSyncRef0) == 0) {
1344763f356cSTakashi Iwai 
1345763f356cSTakashi Iwai 			rate_bits = status2 & HDSPM_wcFreqMask;
1346763f356cSTakashi Iwai 
13470dca1793SAdrian Knoth 
1348763f356cSTakashi Iwai 			switch (rate_bits) {
1349763f356cSTakashi Iwai 			case HDSPM_wcFreq32:
1350763f356cSTakashi Iwai 				rate = 32000;
1351763f356cSTakashi Iwai 				break;
1352763f356cSTakashi Iwai 			case HDSPM_wcFreq44_1:
1353763f356cSTakashi Iwai 				rate = 44100;
1354763f356cSTakashi Iwai 				break;
1355763f356cSTakashi Iwai 			case HDSPM_wcFreq48:
1356763f356cSTakashi Iwai 				rate = 48000;
1357763f356cSTakashi Iwai 				break;
1358763f356cSTakashi Iwai 			case HDSPM_wcFreq64:
1359763f356cSTakashi Iwai 				rate = 64000;
1360763f356cSTakashi Iwai 				break;
1361763f356cSTakashi Iwai 			case HDSPM_wcFreq88_2:
1362763f356cSTakashi Iwai 				rate = 88200;
1363763f356cSTakashi Iwai 				break;
1364763f356cSTakashi Iwai 			case HDSPM_wcFreq96:
1365763f356cSTakashi Iwai 				rate = 96000;
1366763f356cSTakashi Iwai 				break;
1367a8cd7148SAdrian Knoth 			case HDSPM_wcFreq128:
1368a8cd7148SAdrian Knoth 				rate = 128000;
1369a8cd7148SAdrian Knoth 				break;
1370a8cd7148SAdrian Knoth 			case HDSPM_wcFreq176_4:
1371a8cd7148SAdrian Knoth 				rate = 176400;
1372a8cd7148SAdrian Knoth 				break;
1373a8cd7148SAdrian Knoth 			case HDSPM_wcFreq192:
1374a8cd7148SAdrian Knoth 				rate = 192000;
1375a8cd7148SAdrian Knoth 				break;
1376763f356cSTakashi Iwai 			default:
1377763f356cSTakashi Iwai 				rate = 0;
1378763f356cSTakashi Iwai 				break;
1379763f356cSTakashi Iwai 			}
1380763f356cSTakashi Iwai 		}
1381763f356cSTakashi Iwai 
1382ef5fa1a4STakashi Iwai 		/* if rate detected and Syncref is Word than have it,
1383ef5fa1a4STakashi Iwai 		 * word has priority to MADI
1384ef5fa1a4STakashi Iwai 		 */
13853cee5a60SRemy Bruno 		if (rate != 0 &&
13863cee5a60SRemy Bruno 		(status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
13877b559397SAdrian Knoth 			return hdspm_rate_multiplier(hdspm, rate);
1388763f356cSTakashi Iwai 
13890dca1793SAdrian Knoth 		/* maybe a madi input (which is taken if sel sync is madi) */
1390763f356cSTakashi Iwai 		if (status & HDSPM_madiLock) {
1391763f356cSTakashi Iwai 			rate_bits = status & HDSPM_madiFreqMask;
1392763f356cSTakashi Iwai 
1393763f356cSTakashi Iwai 			switch (rate_bits) {
1394763f356cSTakashi Iwai 			case HDSPM_madiFreq32:
1395763f356cSTakashi Iwai 				rate = 32000;
1396763f356cSTakashi Iwai 				break;
1397763f356cSTakashi Iwai 			case HDSPM_madiFreq44_1:
1398763f356cSTakashi Iwai 				rate = 44100;
1399763f356cSTakashi Iwai 				break;
1400763f356cSTakashi Iwai 			case HDSPM_madiFreq48:
1401763f356cSTakashi Iwai 				rate = 48000;
1402763f356cSTakashi Iwai 				break;
1403763f356cSTakashi Iwai 			case HDSPM_madiFreq64:
1404763f356cSTakashi Iwai 				rate = 64000;
1405763f356cSTakashi Iwai 				break;
1406763f356cSTakashi Iwai 			case HDSPM_madiFreq88_2:
1407763f356cSTakashi Iwai 				rate = 88200;
1408763f356cSTakashi Iwai 				break;
1409763f356cSTakashi Iwai 			case HDSPM_madiFreq96:
1410763f356cSTakashi Iwai 				rate = 96000;
1411763f356cSTakashi Iwai 				break;
1412763f356cSTakashi Iwai 			case HDSPM_madiFreq128:
1413763f356cSTakashi Iwai 				rate = 128000;
1414763f356cSTakashi Iwai 				break;
1415763f356cSTakashi Iwai 			case HDSPM_madiFreq176_4:
1416763f356cSTakashi Iwai 				rate = 176400;
1417763f356cSTakashi Iwai 				break;
1418763f356cSTakashi Iwai 			case HDSPM_madiFreq192:
1419763f356cSTakashi Iwai 				rate = 192000;
1420763f356cSTakashi Iwai 				break;
1421763f356cSTakashi Iwai 			default:
1422763f356cSTakashi Iwai 				rate = 0;
1423763f356cSTakashi Iwai 				break;
1424763f356cSTakashi Iwai 			}
1425d12c51d8SAdrian Knoth 
1426fcdc4ba1SAdrian Knoth 		} /* endif HDSPM_madiLock */
1427fcdc4ba1SAdrian Knoth 
1428fcdc4ba1SAdrian Knoth 		/* check sample rate from TCO or SYNC_IN */
1429fcdc4ba1SAdrian Knoth 		{
1430fcdc4ba1SAdrian Knoth 			bool is_valid_input = 0;
1431fcdc4ba1SAdrian Knoth 			bool has_sync = 0;
1432fcdc4ba1SAdrian Knoth 
1433fcdc4ba1SAdrian Knoth 			syncref = hdspm_autosync_ref(hdspm);
1434fcdc4ba1SAdrian Knoth 			if (HDSPM_AUTOSYNC_FROM_TCO == syncref) {
1435fcdc4ba1SAdrian Knoth 				is_valid_input = 1;
1436fcdc4ba1SAdrian Knoth 				has_sync = (HDSPM_SYNC_CHECK_SYNC ==
1437fcdc4ba1SAdrian Knoth 					hdspm_tco_sync_check(hdspm));
1438fcdc4ba1SAdrian Knoth 			} else if (HDSPM_AUTOSYNC_FROM_SYNC_IN == syncref) {
1439fcdc4ba1SAdrian Knoth 				is_valid_input = 1;
1440fcdc4ba1SAdrian Knoth 				has_sync = (HDSPM_SYNC_CHECK_SYNC ==
1441fcdc4ba1SAdrian Knoth 					hdspm_sync_in_sync_check(hdspm));
1442fcdc4ba1SAdrian Knoth 			}
1443fcdc4ba1SAdrian Knoth 
1444fcdc4ba1SAdrian Knoth 			if (is_valid_input && has_sync) {
1445fcdc4ba1SAdrian Knoth 				rate = hdspm_round_frequency(
1446fcdc4ba1SAdrian Knoth 					hdspm_get_pll_freq(hdspm));
1447fcdc4ba1SAdrian Knoth 			}
1448fcdc4ba1SAdrian Knoth 		}
1449fcdc4ba1SAdrian Knoth 
1450a8a729faSAdrian Knoth 		rate = hdspm_rate_multiplier(hdspm, rate);
1451a8a729faSAdrian Knoth 
14520dca1793SAdrian Knoth 		break;
1453763f356cSTakashi Iwai 	}
14540dca1793SAdrian Knoth 
14550dca1793SAdrian Knoth 	return rate;
14563cee5a60SRemy Bruno }
1457763f356cSTakashi Iwai 
14587cb155ffSAdrian Knoth /* return latency in samples per period */
14597cb155ffSAdrian Knoth static int hdspm_get_latency(struct hdspm *hdspm)
14607cb155ffSAdrian Knoth {
14617cb155ffSAdrian Knoth 	int n;
14627cb155ffSAdrian Knoth 
14637cb155ffSAdrian Knoth 	n = hdspm_decode_latency(hdspm->control_register);
14647cb155ffSAdrian Knoth 
14657cb155ffSAdrian Knoth 	/* Special case for new RME cards with 32 samples period size.
14667cb155ffSAdrian Knoth 	 * The three latency bits in the control register
14677cb155ffSAdrian Knoth 	 * (HDSP_LatencyMask) encode latency values of 64 samples as
14687cb155ffSAdrian Knoth 	 * 0, 128 samples as 1 ... 4096 samples as 6. For old cards, 7
14697cb155ffSAdrian Knoth 	 * denotes 8192 samples, but on new cards like RayDAT or AIO,
14707cb155ffSAdrian Knoth 	 * it corresponds to 32 samples.
14717cb155ffSAdrian Knoth 	 */
14727cb155ffSAdrian Knoth 	if ((7 == n) && (RayDAT == hdspm->io_type || AIO == hdspm->io_type))
14737cb155ffSAdrian Knoth 		n = -1;
14747cb155ffSAdrian Knoth 
14757cb155ffSAdrian Knoth 	return 1 << (n + 6);
14767cb155ffSAdrian Knoth }
14777cb155ffSAdrian Knoth 
1478763f356cSTakashi Iwai /* Latency function */
147998274f07STakashi Iwai static inline void hdspm_compute_period_size(struct hdspm *hdspm)
1480763f356cSTakashi Iwai {
14817cb155ffSAdrian Knoth 	hdspm->period_bytes = 4 * hdspm_get_latency(hdspm);
1482763f356cSTakashi Iwai }
1483763f356cSTakashi Iwai 
14840dca1793SAdrian Knoth 
148598274f07STakashi Iwai static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm)
1486763f356cSTakashi Iwai {
1487763f356cSTakashi Iwai 	int position;
1488763f356cSTakashi Iwai 
1489763f356cSTakashi Iwai 	position = hdspm_read(hdspm, HDSPM_statusRegister);
1490483cee77SAdrian Knoth 
1491483cee77SAdrian Knoth 	switch (hdspm->io_type) {
1492483cee77SAdrian Knoth 	case RayDAT:
1493483cee77SAdrian Knoth 	case AIO:
1494763f356cSTakashi Iwai 		position &= HDSPM_BufferPositionMask;
14950dca1793SAdrian Knoth 		position /= 4; /* Bytes per sample */
1496483cee77SAdrian Knoth 		break;
1497483cee77SAdrian Knoth 	default:
1498483cee77SAdrian Knoth 		position = (position & HDSPM_BufferID) ?
1499483cee77SAdrian Knoth 			(hdspm->period_bytes / 4) : 0;
1500483cee77SAdrian Knoth 	}
1501763f356cSTakashi Iwai 
1502763f356cSTakashi Iwai 	return position;
1503763f356cSTakashi Iwai }
1504763f356cSTakashi Iwai 
1505763f356cSTakashi Iwai 
150698274f07STakashi Iwai static inline void hdspm_start_audio(struct hdspm * s)
1507763f356cSTakashi Iwai {
1508763f356cSTakashi Iwai 	s->control_register |= (HDSPM_AudioInterruptEnable | HDSPM_Start);
1509763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1510763f356cSTakashi Iwai }
1511763f356cSTakashi Iwai 
151298274f07STakashi Iwai static inline void hdspm_stop_audio(struct hdspm * s)
1513763f356cSTakashi Iwai {
1514763f356cSTakashi Iwai 	s->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable);
1515763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1516763f356cSTakashi Iwai }
1517763f356cSTakashi Iwai 
1518763f356cSTakashi Iwai /* should I silence all or only opened ones ? doit all for first even is 4MB*/
151962cef821SDenys Vlasenko static void hdspm_silence_playback(struct hdspm *hdspm)
1520763f356cSTakashi Iwai {
1521763f356cSTakashi Iwai 	int i;
1522763f356cSTakashi Iwai 	int n = hdspm->period_bytes;
1523763f356cSTakashi Iwai 	void *buf = hdspm->playback_buffer;
1524763f356cSTakashi Iwai 
15253cee5a60SRemy Bruno 	if (buf == NULL)
15263cee5a60SRemy Bruno 		return;
1527763f356cSTakashi Iwai 
1528763f356cSTakashi Iwai 	for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
1529763f356cSTakashi Iwai 		memset(buf, 0, n);
1530763f356cSTakashi Iwai 		buf += HDSPM_CHANNEL_BUFFER_BYTES;
1531763f356cSTakashi Iwai 	}
1532763f356cSTakashi Iwai }
1533763f356cSTakashi Iwai 
153498274f07STakashi Iwai static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
1535763f356cSTakashi Iwai {
1536763f356cSTakashi Iwai 	int n;
1537763f356cSTakashi Iwai 
1538763f356cSTakashi Iwai 	spin_lock_irq(&s->lock);
1539763f356cSTakashi Iwai 
15402e610270SAdrian Knoth 	if (32 == frames) {
15412e610270SAdrian Knoth 		/* Special case for new RME cards like RayDAT/AIO which
15422e610270SAdrian Knoth 		 * support period sizes of 32 samples. Since latency is
15432e610270SAdrian Knoth 		 * encoded in the three bits of HDSP_LatencyMask, we can only
15442e610270SAdrian Knoth 		 * have values from 0 .. 7. While 0 still means 64 samples and
15452e610270SAdrian Knoth 		 * 6 represents 4096 samples on all cards, 7 represents 8192
15462e610270SAdrian Knoth 		 * on older cards and 32 samples on new cards.
15472e610270SAdrian Knoth 		 *
15482e610270SAdrian Knoth 		 * In other words, period size in samples is calculated by
15492e610270SAdrian Knoth 		 * 2^(n+6) with n ranging from 0 .. 7.
15502e610270SAdrian Knoth 		 */
15512e610270SAdrian Knoth 		n = 7;
15522e610270SAdrian Knoth 	} else {
1553763f356cSTakashi Iwai 		frames >>= 7;
1554763f356cSTakashi Iwai 		n = 0;
1555763f356cSTakashi Iwai 		while (frames) {
1556763f356cSTakashi Iwai 			n++;
1557763f356cSTakashi Iwai 			frames >>= 1;
1558763f356cSTakashi Iwai 		}
15592e610270SAdrian Knoth 	}
15602e610270SAdrian Knoth 
1561763f356cSTakashi Iwai 	s->control_register &= ~HDSPM_LatencyMask;
1562763f356cSTakashi Iwai 	s->control_register |= hdspm_encode_latency(n);
1563763f356cSTakashi Iwai 
1564763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1565763f356cSTakashi Iwai 
1566763f356cSTakashi Iwai 	hdspm_compute_period_size(s);
1567763f356cSTakashi Iwai 
1568763f356cSTakashi Iwai 	spin_unlock_irq(&s->lock);
1569763f356cSTakashi Iwai 
1570763f356cSTakashi Iwai 	return 0;
1571763f356cSTakashi Iwai }
1572763f356cSTakashi Iwai 
15730dca1793SAdrian Knoth static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period)
15740dca1793SAdrian Knoth {
15750dca1793SAdrian Knoth 	u64 freq_const;
15760dca1793SAdrian Knoth 
15770dca1793SAdrian Knoth 	if (period == 0)
15780dca1793SAdrian Knoth 		return 0;
15790dca1793SAdrian Knoth 
15800dca1793SAdrian Knoth 	switch (hdspm->io_type) {
15810dca1793SAdrian Knoth 	case MADI:
15820dca1793SAdrian Knoth 	case AES32:
15830dca1793SAdrian Knoth 		freq_const = 110069313433624ULL;
15840dca1793SAdrian Knoth 		break;
15850dca1793SAdrian Knoth 	case RayDAT:
15860dca1793SAdrian Knoth 	case AIO:
15870dca1793SAdrian Knoth 		freq_const = 104857600000000ULL;
15880dca1793SAdrian Knoth 		break;
15890dca1793SAdrian Knoth 	case MADIface:
15900dca1793SAdrian Knoth 		freq_const = 131072000000000ULL;
15913d56c8e6STakashi Iwai 		break;
15923d56c8e6STakashi Iwai 	default:
15933d56c8e6STakashi Iwai 		snd_BUG();
15943d56c8e6STakashi Iwai 		return 0;
15950dca1793SAdrian Knoth 	}
15960dca1793SAdrian Knoth 
15970dca1793SAdrian Knoth 	return div_u64(freq_const, period);
15980dca1793SAdrian Knoth }
15990dca1793SAdrian Knoth 
16000dca1793SAdrian Knoth 
1601ffb2c3c0SRemy Bruno static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
1602ffb2c3c0SRemy Bruno {
1603ffb2c3c0SRemy Bruno 	u64 n;
1604ffb2c3c0SRemy Bruno 
1605ffb2c3c0SRemy Bruno 	if (rate >= 112000)
1606ffb2c3c0SRemy Bruno 		rate /= 4;
1607ffb2c3c0SRemy Bruno 	else if (rate >= 56000)
1608ffb2c3c0SRemy Bruno 		rate /= 2;
1609ffb2c3c0SRemy Bruno 
16100dca1793SAdrian Knoth 	switch (hdspm->io_type) {
16110dca1793SAdrian Knoth 	case MADIface:
16120dca1793SAdrian Knoth 		n = 131072000000000ULL;  /* 125 MHz */
16130dca1793SAdrian Knoth 		break;
16140dca1793SAdrian Knoth 	case MADI:
16150dca1793SAdrian Knoth 	case AES32:
16160dca1793SAdrian Knoth 		n = 110069313433624ULL;  /* 105 MHz */
16170dca1793SAdrian Knoth 		break;
16180dca1793SAdrian Knoth 	case RayDAT:
16190dca1793SAdrian Knoth 	case AIO:
16200dca1793SAdrian Knoth 		n = 104857600000000ULL;  /* 100 MHz */
16210dca1793SAdrian Knoth 		break;
16223d56c8e6STakashi Iwai 	default:
16233d56c8e6STakashi Iwai 		snd_BUG();
16243d56c8e6STakashi Iwai 		return;
16250dca1793SAdrian Knoth 	}
16260dca1793SAdrian Knoth 
16273f7440a6STakashi Iwai 	n = div_u64(n, rate);
1628ffb2c3c0SRemy Bruno 	/* n should be less than 2^32 for being written to FREQ register */
1629da3cec35STakashi Iwai 	snd_BUG_ON(n >> 32);
1630ffb2c3c0SRemy Bruno 	hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
1631ffb2c3c0SRemy Bruno }
1632763f356cSTakashi Iwai 
1633763f356cSTakashi Iwai /* dummy set rate lets see what happens */
163498274f07STakashi Iwai static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
1635763f356cSTakashi Iwai {
1636763f356cSTakashi Iwai 	int current_rate;
1637763f356cSTakashi Iwai 	int rate_bits;
1638763f356cSTakashi Iwai 	int not_set = 0;
16396534599dSRemy Bruno 	int current_speed, target_speed;
1640763f356cSTakashi Iwai 
1641763f356cSTakashi Iwai 	/* ASSUMPTION: hdspm->lock is either set, or there is no need for
1642763f356cSTakashi Iwai 	   it (e.g. during module initialization).
1643763f356cSTakashi Iwai 	 */
1644763f356cSTakashi Iwai 
1645763f356cSTakashi Iwai 	if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
1646763f356cSTakashi Iwai 
1647763f356cSTakashi Iwai 		/* SLAVE --- */
1648763f356cSTakashi Iwai 		if (called_internally) {
1649763f356cSTakashi Iwai 
1650763f356cSTakashi Iwai 			/* request from ctl or card initialization
1651763f356cSTakashi Iwai 			   just make a warning an remember setting
1652763f356cSTakashi Iwai 			   for future master mode switching */
1653763f356cSTakashi Iwai 
1654ef5fa1a4STakashi Iwai 			snd_printk(KERN_WARNING "HDSPM: "
1655ef5fa1a4STakashi Iwai 				   "Warning: device is not running "
1656ef5fa1a4STakashi Iwai 				   "as a clock master.\n");
1657763f356cSTakashi Iwai 			not_set = 1;
1658763f356cSTakashi Iwai 		} else {
1659763f356cSTakashi Iwai 
1660763f356cSTakashi Iwai 			/* hw_param request while in AutoSync mode */
1661763f356cSTakashi Iwai 			int external_freq =
1662763f356cSTakashi Iwai 			    hdspm_external_sample_rate(hdspm);
1663763f356cSTakashi Iwai 
1664ef5fa1a4STakashi Iwai 			if (hdspm_autosync_ref(hdspm) ==
1665ef5fa1a4STakashi Iwai 			    HDSPM_AUTOSYNC_FROM_NONE) {
1666763f356cSTakashi Iwai 
1667ef5fa1a4STakashi Iwai 				snd_printk(KERN_WARNING "HDSPM: "
1668ef5fa1a4STakashi Iwai 					   "Detected no Externel Sync \n");
1669763f356cSTakashi Iwai 				not_set = 1;
1670763f356cSTakashi Iwai 
1671763f356cSTakashi Iwai 			} else if (rate != external_freq) {
1672763f356cSTakashi Iwai 
1673ef5fa1a4STakashi Iwai 				snd_printk(KERN_WARNING "HDSPM: "
1674ef5fa1a4STakashi Iwai 					   "Warning: No AutoSync source for "
1675ef5fa1a4STakashi Iwai 					   "requested rate\n");
1676763f356cSTakashi Iwai 				not_set = 1;
1677763f356cSTakashi Iwai 			}
1678763f356cSTakashi Iwai 		}
1679763f356cSTakashi Iwai 	}
1680763f356cSTakashi Iwai 
1681763f356cSTakashi Iwai 	current_rate = hdspm->system_sample_rate;
1682763f356cSTakashi Iwai 
1683763f356cSTakashi Iwai 	/* Changing between Singe, Double and Quad speed is not
1684763f356cSTakashi Iwai 	   allowed if any substreams are open. This is because such a change
1685763f356cSTakashi Iwai 	   causes a shift in the location of the DMA buffers and a reduction
1686763f356cSTakashi Iwai 	   in the number of available buffers.
1687763f356cSTakashi Iwai 
1688763f356cSTakashi Iwai 	   Note that a similar but essentially insoluble problem exists for
1689763f356cSTakashi Iwai 	   externally-driven rate changes. All we can do is to flag rate
1690763f356cSTakashi Iwai 	   changes in the read/write routines.
1691763f356cSTakashi Iwai 	 */
1692763f356cSTakashi Iwai 
16936534599dSRemy Bruno 	if (current_rate <= 48000)
16946534599dSRemy Bruno 		current_speed = HDSPM_SPEED_SINGLE;
16956534599dSRemy Bruno 	else if (current_rate <= 96000)
16966534599dSRemy Bruno 		current_speed = HDSPM_SPEED_DOUBLE;
16976534599dSRemy Bruno 	else
16986534599dSRemy Bruno 		current_speed = HDSPM_SPEED_QUAD;
16996534599dSRemy Bruno 
17006534599dSRemy Bruno 	if (rate <= 48000)
17016534599dSRemy Bruno 		target_speed = HDSPM_SPEED_SINGLE;
17026534599dSRemy Bruno 	else if (rate <= 96000)
17036534599dSRemy Bruno 		target_speed = HDSPM_SPEED_DOUBLE;
17046534599dSRemy Bruno 	else
17056534599dSRemy Bruno 		target_speed = HDSPM_SPEED_QUAD;
17063cee5a60SRemy Bruno 
1707763f356cSTakashi Iwai 	switch (rate) {
1708763f356cSTakashi Iwai 	case 32000:
1709763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency32KHz;
1710763f356cSTakashi Iwai 		break;
1711763f356cSTakashi Iwai 	case 44100:
1712763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency44_1KHz;
1713763f356cSTakashi Iwai 		break;
1714763f356cSTakashi Iwai 	case 48000:
1715763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency48KHz;
1716763f356cSTakashi Iwai 		break;
1717763f356cSTakashi Iwai 	case 64000:
1718763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency64KHz;
1719763f356cSTakashi Iwai 		break;
1720763f356cSTakashi Iwai 	case 88200:
1721763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency88_2KHz;
1722763f356cSTakashi Iwai 		break;
1723763f356cSTakashi Iwai 	case 96000:
1724763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency96KHz;
1725763f356cSTakashi Iwai 		break;
17263cee5a60SRemy Bruno 	case 128000:
17273cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency128KHz;
17283cee5a60SRemy Bruno 		break;
17293cee5a60SRemy Bruno 	case 176400:
17303cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency176_4KHz;
17313cee5a60SRemy Bruno 		break;
17323cee5a60SRemy Bruno 	case 192000:
17333cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency192KHz;
17343cee5a60SRemy Bruno 		break;
1735763f356cSTakashi Iwai 	default:
1736763f356cSTakashi Iwai 		return -EINVAL;
1737763f356cSTakashi Iwai 	}
1738763f356cSTakashi Iwai 
17396534599dSRemy Bruno 	if (current_speed != target_speed
1740763f356cSTakashi Iwai 	    && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
1741763f356cSTakashi Iwai 		snd_printk
1742ef5fa1a4STakashi Iwai 		    (KERN_ERR "HDSPM: "
17436534599dSRemy Bruno 		     "cannot change from %s speed to %s speed mode "
1744ef5fa1a4STakashi Iwai 		     "(capture PID = %d, playback PID = %d)\n",
17456534599dSRemy Bruno 		     hdspm_speed_names[current_speed],
17466534599dSRemy Bruno 		     hdspm_speed_names[target_speed],
1747763f356cSTakashi Iwai 		     hdspm->capture_pid, hdspm->playback_pid);
1748763f356cSTakashi Iwai 		return -EBUSY;
1749763f356cSTakashi Iwai 	}
1750763f356cSTakashi Iwai 
1751763f356cSTakashi Iwai 	hdspm->control_register &= ~HDSPM_FrequencyMask;
1752763f356cSTakashi Iwai 	hdspm->control_register |= rate_bits;
1753763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
1754763f356cSTakashi Iwai 
1755ffb2c3c0SRemy Bruno 	/* For AES32, need to set DDS value in FREQ register
1756ffb2c3c0SRemy Bruno 	   For MADI, also apparently */
1757ffb2c3c0SRemy Bruno 	hdspm_set_dds_value(hdspm, rate);
1758ffb2c3c0SRemy Bruno 
17590dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type && rate != current_rate)
1760ffb2c3c0SRemy Bruno 		hdspm_write(hdspm, HDSPM_eeprom_wr, 0);
1761ffb2c3c0SRemy Bruno 
1762763f356cSTakashi Iwai 	hdspm->system_sample_rate = rate;
1763763f356cSTakashi Iwai 
17640dca1793SAdrian Knoth 	if (rate <= 48000) {
17650dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_ss;
17660dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_ss;
17670dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->ss_in_channels;
17680dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->ss_out_channels;
17690dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_ss;
17700dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_ss;
17710dca1793SAdrian Knoth 	} else if (rate <= 96000) {
17720dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_ds;
17730dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_ds;
17740dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->ds_in_channels;
17750dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->ds_out_channels;
17760dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_ds;
17770dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_ds;
17780dca1793SAdrian Knoth 	} else {
17790dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_qs;
17800dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_qs;
17810dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->qs_in_channels;
17820dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->qs_out_channels;
17830dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_qs;
17840dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_qs;
17850dca1793SAdrian Knoth 	}
17860dca1793SAdrian Knoth 
1787763f356cSTakashi Iwai 	if (not_set != 0)
1788763f356cSTakashi Iwai 		return -1;
1789763f356cSTakashi Iwai 
1790763f356cSTakashi Iwai 	return 0;
1791763f356cSTakashi Iwai }
1792763f356cSTakashi Iwai 
1793763f356cSTakashi Iwai /* mainly for init to 0 on load */
179498274f07STakashi Iwai static void all_in_all_mixer(struct hdspm * hdspm, int sgain)
1795763f356cSTakashi Iwai {
1796763f356cSTakashi Iwai 	int i, j;
1797ef5fa1a4STakashi Iwai 	unsigned int gain;
1798ef5fa1a4STakashi Iwai 
1799ef5fa1a4STakashi Iwai 	if (sgain > UNITY_GAIN)
1800ef5fa1a4STakashi Iwai 		gain = UNITY_GAIN;
1801ef5fa1a4STakashi Iwai 	else if (sgain < 0)
1802ef5fa1a4STakashi Iwai 		gain = 0;
1803ef5fa1a4STakashi Iwai 	else
1804ef5fa1a4STakashi Iwai 		gain = sgain;
1805763f356cSTakashi Iwai 
1806763f356cSTakashi Iwai 	for (i = 0; i < HDSPM_MIXER_CHANNELS; i++)
1807763f356cSTakashi Iwai 		for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) {
1808763f356cSTakashi Iwai 			hdspm_write_in_gain(hdspm, i, j, gain);
1809763f356cSTakashi Iwai 			hdspm_write_pb_gain(hdspm, i, j, gain);
1810763f356cSTakashi Iwai 		}
1811763f356cSTakashi Iwai }
1812763f356cSTakashi Iwai 
1813763f356cSTakashi Iwai /*----------------------------------------------------------------------------
1814763f356cSTakashi Iwai    MIDI
1815763f356cSTakashi Iwai   ----------------------------------------------------------------------------*/
1816763f356cSTakashi Iwai 
1817ef5fa1a4STakashi Iwai static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm,
1818ef5fa1a4STakashi Iwai 						      int id)
1819763f356cSTakashi Iwai {
1820763f356cSTakashi Iwai 	/* the hardware already does the relevant bit-mask with 0xff */
18210dca1793SAdrian Knoth 	return hdspm_read(hdspm, hdspm->midi[id].dataIn);
1822763f356cSTakashi Iwai }
1823763f356cSTakashi Iwai 
1824ef5fa1a4STakashi Iwai static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id,
1825ef5fa1a4STakashi Iwai 					      int val)
1826763f356cSTakashi Iwai {
1827763f356cSTakashi Iwai 	/* the hardware already does the relevant bit-mask with 0xff */
18280dca1793SAdrian Knoth 	return hdspm_write(hdspm, hdspm->midi[id].dataOut, val);
1829763f356cSTakashi Iwai }
1830763f356cSTakashi Iwai 
183198274f07STakashi Iwai static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id)
1832763f356cSTakashi Iwai {
18330dca1793SAdrian Knoth 	return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF;
1834763f356cSTakashi Iwai }
1835763f356cSTakashi Iwai 
183698274f07STakashi Iwai static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id)
1837763f356cSTakashi Iwai {
1838763f356cSTakashi Iwai 	int fifo_bytes_used;
1839763f356cSTakashi Iwai 
18400dca1793SAdrian Knoth 	fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF;
1841763f356cSTakashi Iwai 
1842763f356cSTakashi Iwai 	if (fifo_bytes_used < 128)
1843763f356cSTakashi Iwai 		return  128 - fifo_bytes_used;
1844763f356cSTakashi Iwai 	else
1845763f356cSTakashi Iwai 		return 0;
1846763f356cSTakashi Iwai }
1847763f356cSTakashi Iwai 
184862cef821SDenys Vlasenko static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id)
1849763f356cSTakashi Iwai {
1850763f356cSTakashi Iwai 	while (snd_hdspm_midi_input_available (hdspm, id))
1851763f356cSTakashi Iwai 		snd_hdspm_midi_read_byte (hdspm, id);
1852763f356cSTakashi Iwai }
1853763f356cSTakashi Iwai 
185498274f07STakashi Iwai static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
1855763f356cSTakashi Iwai {
1856763f356cSTakashi Iwai 	unsigned long flags;
1857763f356cSTakashi Iwai 	int n_pending;
1858763f356cSTakashi Iwai 	int to_write;
1859763f356cSTakashi Iwai 	int i;
1860763f356cSTakashi Iwai 	unsigned char buf[128];
1861763f356cSTakashi Iwai 
1862763f356cSTakashi Iwai 	/* Output is not interrupt driven */
1863763f356cSTakashi Iwai 
1864763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1865ef5fa1a4STakashi Iwai 	if (hmidi->output &&
1866ef5fa1a4STakashi Iwai 	    !snd_rawmidi_transmit_empty (hmidi->output)) {
1867ef5fa1a4STakashi Iwai 		n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm,
1868ef5fa1a4STakashi Iwai 							    hmidi->id);
1869ef5fa1a4STakashi Iwai 		if (n_pending > 0) {
1870763f356cSTakashi Iwai 			if (n_pending > (int)sizeof (buf))
1871763f356cSTakashi Iwai 				n_pending = sizeof (buf);
1872763f356cSTakashi Iwai 
1873ef5fa1a4STakashi Iwai 			to_write = snd_rawmidi_transmit (hmidi->output, buf,
1874ef5fa1a4STakashi Iwai 							 n_pending);
1875ef5fa1a4STakashi Iwai 			if (to_write > 0) {
1876763f356cSTakashi Iwai 				for (i = 0; i < to_write; ++i)
1877ef5fa1a4STakashi Iwai 					snd_hdspm_midi_write_byte (hmidi->hdspm,
1878ef5fa1a4STakashi Iwai 								   hmidi->id,
1879ef5fa1a4STakashi Iwai 								   buf[i]);
1880763f356cSTakashi Iwai 			}
1881763f356cSTakashi Iwai 		}
1882763f356cSTakashi Iwai 	}
1883763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1884763f356cSTakashi Iwai 	return 0;
1885763f356cSTakashi Iwai }
1886763f356cSTakashi Iwai 
188798274f07STakashi Iwai static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi)
1888763f356cSTakashi Iwai {
1889ef5fa1a4STakashi Iwai 	unsigned char buf[128]; /* this buffer is designed to match the MIDI
1890ef5fa1a4STakashi Iwai 				 * input FIFO size
1891ef5fa1a4STakashi Iwai 				 */
1892763f356cSTakashi Iwai 	unsigned long flags;
1893763f356cSTakashi Iwai 	int n_pending;
1894763f356cSTakashi Iwai 	int i;
1895763f356cSTakashi Iwai 
1896763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1897ef5fa1a4STakashi Iwai 	n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id);
1898ef5fa1a4STakashi Iwai 	if (n_pending > 0) {
1899763f356cSTakashi Iwai 		if (hmidi->input) {
1900ef5fa1a4STakashi Iwai 			if (n_pending > (int)sizeof (buf))
1901763f356cSTakashi Iwai 				n_pending = sizeof (buf);
1902ef5fa1a4STakashi Iwai 			for (i = 0; i < n_pending; ++i)
1903ef5fa1a4STakashi Iwai 				buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm,
1904ef5fa1a4STakashi Iwai 								   hmidi->id);
1905ef5fa1a4STakashi Iwai 			if (n_pending)
1906ef5fa1a4STakashi Iwai 				snd_rawmidi_receive (hmidi->input, buf,
1907ef5fa1a4STakashi Iwai 						     n_pending);
1908763f356cSTakashi Iwai 		} else {
1909763f356cSTakashi Iwai 			/* flush the MIDI input FIFO */
1910ef5fa1a4STakashi Iwai 			while (n_pending--)
1911ef5fa1a4STakashi Iwai 				snd_hdspm_midi_read_byte (hmidi->hdspm,
1912ef5fa1a4STakashi Iwai 							  hmidi->id);
1913763f356cSTakashi Iwai 		}
1914763f356cSTakashi Iwai 	}
1915763f356cSTakashi Iwai 	hmidi->pending = 0;
1916c0da0014SAdrian Knoth 	spin_unlock_irqrestore(&hmidi->lock, flags);
19170dca1793SAdrian Knoth 
1918c0da0014SAdrian Knoth 	spin_lock_irqsave(&hmidi->hdspm->lock, flags);
19190dca1793SAdrian Knoth 	hmidi->hdspm->control_register |= hmidi->ie;
1920ef5fa1a4STakashi Iwai 	hdspm_write(hmidi->hdspm, HDSPM_controlRegister,
1921ef5fa1a4STakashi Iwai 		    hmidi->hdspm->control_register);
1922c0da0014SAdrian Knoth 	spin_unlock_irqrestore(&hmidi->hdspm->lock, flags);
19230dca1793SAdrian Knoth 
1924763f356cSTakashi Iwai 	return snd_hdspm_midi_output_write (hmidi);
1925763f356cSTakashi Iwai }
1926763f356cSTakashi Iwai 
1927ef5fa1a4STakashi Iwai static void
1928ef5fa1a4STakashi Iwai snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
1929763f356cSTakashi Iwai {
193098274f07STakashi Iwai 	struct hdspm *hdspm;
193198274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1932763f356cSTakashi Iwai 	unsigned long flags;
1933763f356cSTakashi Iwai 
1934ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1935763f356cSTakashi Iwai 	hdspm = hmidi->hdspm;
19360dca1793SAdrian Knoth 
1937763f356cSTakashi Iwai 	spin_lock_irqsave (&hdspm->lock, flags);
1938763f356cSTakashi Iwai 	if (up) {
19390dca1793SAdrian Knoth 		if (!(hdspm->control_register & hmidi->ie)) {
1940763f356cSTakashi Iwai 			snd_hdspm_flush_midi_input (hdspm, hmidi->id);
19410dca1793SAdrian Knoth 			hdspm->control_register |= hmidi->ie;
1942763f356cSTakashi Iwai 		}
1943763f356cSTakashi Iwai 	} else {
19440dca1793SAdrian Knoth 		hdspm->control_register &= ~hmidi->ie;
1945763f356cSTakashi Iwai 	}
1946763f356cSTakashi Iwai 
1947763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
1948763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hdspm->lock, flags);
1949763f356cSTakashi Iwai }
1950763f356cSTakashi Iwai 
1951763f356cSTakashi Iwai static void snd_hdspm_midi_output_timer(unsigned long data)
1952763f356cSTakashi Iwai {
195398274f07STakashi Iwai 	struct hdspm_midi *hmidi = (struct hdspm_midi *) data;
1954763f356cSTakashi Iwai 	unsigned long flags;
1955763f356cSTakashi Iwai 
1956763f356cSTakashi Iwai 	snd_hdspm_midi_output_write(hmidi);
1957763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1958763f356cSTakashi Iwai 
1959763f356cSTakashi Iwai 	/* this does not bump hmidi->istimer, because the
1960763f356cSTakashi Iwai 	   kernel automatically removed the timer when it
1961763f356cSTakashi Iwai 	   expired, and we are now adding it back, thus
1962763f356cSTakashi Iwai 	   leaving istimer wherever it was set before.
1963763f356cSTakashi Iwai 	*/
1964763f356cSTakashi Iwai 
1965763f356cSTakashi Iwai 	if (hmidi->istimer) {
1966763f356cSTakashi Iwai 		hmidi->timer.expires = 1 + jiffies;
1967763f356cSTakashi Iwai 		add_timer(&hmidi->timer);
1968763f356cSTakashi Iwai 	}
1969763f356cSTakashi Iwai 
1970763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1971763f356cSTakashi Iwai }
1972763f356cSTakashi Iwai 
1973ef5fa1a4STakashi Iwai static void
1974ef5fa1a4STakashi Iwai snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
1975763f356cSTakashi Iwai {
197698274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1977763f356cSTakashi Iwai 	unsigned long flags;
1978763f356cSTakashi Iwai 
1979ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1980763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1981763f356cSTakashi Iwai 	if (up) {
1982763f356cSTakashi Iwai 		if (!hmidi->istimer) {
1983763f356cSTakashi Iwai 			init_timer(&hmidi->timer);
1984763f356cSTakashi Iwai 			hmidi->timer.function = snd_hdspm_midi_output_timer;
1985763f356cSTakashi Iwai 			hmidi->timer.data = (unsigned long) hmidi;
1986763f356cSTakashi Iwai 			hmidi->timer.expires = 1 + jiffies;
1987763f356cSTakashi Iwai 			add_timer(&hmidi->timer);
1988763f356cSTakashi Iwai 			hmidi->istimer++;
1989763f356cSTakashi Iwai 		}
1990763f356cSTakashi Iwai 	} else {
1991ef5fa1a4STakashi Iwai 		if (hmidi->istimer && --hmidi->istimer <= 0)
1992763f356cSTakashi Iwai 			del_timer (&hmidi->timer);
1993763f356cSTakashi Iwai 	}
1994763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1995763f356cSTakashi Iwai 	if (up)
1996763f356cSTakashi Iwai 		snd_hdspm_midi_output_write(hmidi);
1997763f356cSTakashi Iwai }
1998763f356cSTakashi Iwai 
199998274f07STakashi Iwai static int snd_hdspm_midi_input_open(struct snd_rawmidi_substream *substream)
2000763f356cSTakashi Iwai {
200198274f07STakashi Iwai 	struct hdspm_midi *hmidi;
2002763f356cSTakashi Iwai 
2003ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
2004763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
2005763f356cSTakashi Iwai 	snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id);
2006763f356cSTakashi Iwai 	hmidi->input = substream;
2007763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
2008763f356cSTakashi Iwai 
2009763f356cSTakashi Iwai 	return 0;
2010763f356cSTakashi Iwai }
2011763f356cSTakashi Iwai 
201298274f07STakashi Iwai static int snd_hdspm_midi_output_open(struct snd_rawmidi_substream *substream)
2013763f356cSTakashi Iwai {
201498274f07STakashi Iwai 	struct hdspm_midi *hmidi;
2015763f356cSTakashi Iwai 
2016ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
2017763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
2018763f356cSTakashi Iwai 	hmidi->output = substream;
2019763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
2020763f356cSTakashi Iwai 
2021763f356cSTakashi Iwai 	return 0;
2022763f356cSTakashi Iwai }
2023763f356cSTakashi Iwai 
202498274f07STakashi Iwai static int snd_hdspm_midi_input_close(struct snd_rawmidi_substream *substream)
2025763f356cSTakashi Iwai {
202698274f07STakashi Iwai 	struct hdspm_midi *hmidi;
2027763f356cSTakashi Iwai 
2028763f356cSTakashi Iwai 	snd_hdspm_midi_input_trigger (substream, 0);
2029763f356cSTakashi Iwai 
2030ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
2031763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
2032763f356cSTakashi Iwai 	hmidi->input = NULL;
2033763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
2034763f356cSTakashi Iwai 
2035763f356cSTakashi Iwai 	return 0;
2036763f356cSTakashi Iwai }
2037763f356cSTakashi Iwai 
203898274f07STakashi Iwai static int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream)
2039763f356cSTakashi Iwai {
204098274f07STakashi Iwai 	struct hdspm_midi *hmidi;
2041763f356cSTakashi Iwai 
2042763f356cSTakashi Iwai 	snd_hdspm_midi_output_trigger (substream, 0);
2043763f356cSTakashi Iwai 
2044ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
2045763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
2046763f356cSTakashi Iwai 	hmidi->output = NULL;
2047763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
2048763f356cSTakashi Iwai 
2049763f356cSTakashi Iwai 	return 0;
2050763f356cSTakashi Iwai }
2051763f356cSTakashi Iwai 
205298274f07STakashi Iwai static struct snd_rawmidi_ops snd_hdspm_midi_output =
2053763f356cSTakashi Iwai {
2054763f356cSTakashi Iwai 	.open =		snd_hdspm_midi_output_open,
2055763f356cSTakashi Iwai 	.close =	snd_hdspm_midi_output_close,
2056763f356cSTakashi Iwai 	.trigger =	snd_hdspm_midi_output_trigger,
2057763f356cSTakashi Iwai };
2058763f356cSTakashi Iwai 
205998274f07STakashi Iwai static struct snd_rawmidi_ops snd_hdspm_midi_input =
2060763f356cSTakashi Iwai {
2061763f356cSTakashi Iwai 	.open =		snd_hdspm_midi_input_open,
2062763f356cSTakashi Iwai 	.close =	snd_hdspm_midi_input_close,
2063763f356cSTakashi Iwai 	.trigger =	snd_hdspm_midi_input_trigger,
2064763f356cSTakashi Iwai };
2065763f356cSTakashi Iwai 
2066e23e7a14SBill Pemberton static int snd_hdspm_create_midi(struct snd_card *card,
2067ef5fa1a4STakashi Iwai 				 struct hdspm *hdspm, int id)
2068763f356cSTakashi Iwai {
2069763f356cSTakashi Iwai 	int err;
2070763f356cSTakashi Iwai 	char buf[32];
2071763f356cSTakashi Iwai 
2072763f356cSTakashi Iwai 	hdspm->midi[id].id = id;
2073763f356cSTakashi Iwai 	hdspm->midi[id].hdspm = hdspm;
2074763f356cSTakashi Iwai 	spin_lock_init (&hdspm->midi[id].lock);
2075763f356cSTakashi Iwai 
20760dca1793SAdrian Knoth 	if (0 == id) {
20770dca1793SAdrian Knoth 		if (MADIface == hdspm->io_type) {
20780dca1793SAdrian Knoth 			/* MIDI-over-MADI on HDSPe MADIface */
20790dca1793SAdrian Knoth 			hdspm->midi[0].dataIn = HDSPM_midiDataIn2;
20800dca1793SAdrian Knoth 			hdspm->midi[0].statusIn = HDSPM_midiStatusIn2;
20810dca1793SAdrian Knoth 			hdspm->midi[0].dataOut = HDSPM_midiDataOut2;
20820dca1793SAdrian Knoth 			hdspm->midi[0].statusOut = HDSPM_midiStatusOut2;
20830dca1793SAdrian Knoth 			hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable;
20840dca1793SAdrian Knoth 			hdspm->midi[0].irq = HDSPM_midi2IRQPending;
20850dca1793SAdrian Knoth 		} else {
20860dca1793SAdrian Knoth 			hdspm->midi[0].dataIn = HDSPM_midiDataIn0;
20870dca1793SAdrian Knoth 			hdspm->midi[0].statusIn = HDSPM_midiStatusIn0;
20880dca1793SAdrian Knoth 			hdspm->midi[0].dataOut = HDSPM_midiDataOut0;
20890dca1793SAdrian Knoth 			hdspm->midi[0].statusOut = HDSPM_midiStatusOut0;
20900dca1793SAdrian Knoth 			hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable;
20910dca1793SAdrian Knoth 			hdspm->midi[0].irq = HDSPM_midi0IRQPending;
20920dca1793SAdrian Knoth 		}
20930dca1793SAdrian Knoth 	} else if (1 == id) {
20940dca1793SAdrian Knoth 		hdspm->midi[1].dataIn = HDSPM_midiDataIn1;
20950dca1793SAdrian Knoth 		hdspm->midi[1].statusIn = HDSPM_midiStatusIn1;
20960dca1793SAdrian Knoth 		hdspm->midi[1].dataOut = HDSPM_midiDataOut1;
20970dca1793SAdrian Knoth 		hdspm->midi[1].statusOut = HDSPM_midiStatusOut1;
20980dca1793SAdrian Knoth 		hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable;
20990dca1793SAdrian Knoth 		hdspm->midi[1].irq = HDSPM_midi1IRQPending;
21000dca1793SAdrian Knoth 	} else if ((2 == id) && (MADI == hdspm->io_type)) {
21010dca1793SAdrian Knoth 		/* MIDI-over-MADI on HDSPe MADI */
21020dca1793SAdrian Knoth 		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
21030dca1793SAdrian Knoth 		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
21040dca1793SAdrian Knoth 		hdspm->midi[2].dataOut = HDSPM_midiDataOut2;
21050dca1793SAdrian Knoth 		hdspm->midi[2].statusOut = HDSPM_midiStatusOut2;
21060dca1793SAdrian Knoth 		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
21070dca1793SAdrian Knoth 		hdspm->midi[2].irq = HDSPM_midi2IRQPending;
21080dca1793SAdrian Knoth 	} else if (2 == id) {
21090dca1793SAdrian Knoth 		/* TCO MTC, read only */
21100dca1793SAdrian Knoth 		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
21110dca1793SAdrian Knoth 		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
21120dca1793SAdrian Knoth 		hdspm->midi[2].dataOut = -1;
21130dca1793SAdrian Knoth 		hdspm->midi[2].statusOut = -1;
21140dca1793SAdrian Knoth 		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
21150dca1793SAdrian Knoth 		hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES;
21160dca1793SAdrian Knoth 	} else if (3 == id) {
21170dca1793SAdrian Knoth 		/* TCO MTC on HDSPe MADI */
21180dca1793SAdrian Knoth 		hdspm->midi[3].dataIn = HDSPM_midiDataIn3;
21190dca1793SAdrian Knoth 		hdspm->midi[3].statusIn = HDSPM_midiStatusIn3;
21200dca1793SAdrian Knoth 		hdspm->midi[3].dataOut = -1;
21210dca1793SAdrian Knoth 		hdspm->midi[3].statusOut = -1;
21220dca1793SAdrian Knoth 		hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable;
21230dca1793SAdrian Knoth 		hdspm->midi[3].irq = HDSPM_midi3IRQPending;
21240dca1793SAdrian Knoth 	}
21250dca1793SAdrian Knoth 
21260dca1793SAdrian Knoth 	if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) ||
21270dca1793SAdrian Knoth 					(MADIface == hdspm->io_type)))) {
21280dca1793SAdrian Knoth 		if ((id == 0) && (MADIface == hdspm->io_type)) {
21290dca1793SAdrian Knoth 			sprintf(buf, "%s MIDIoverMADI", card->shortname);
21300dca1793SAdrian Knoth 		} else if ((id == 2) && (MADI == hdspm->io_type)) {
21310dca1793SAdrian Knoth 			sprintf(buf, "%s MIDIoverMADI", card->shortname);
21320dca1793SAdrian Knoth 		} else {
2133763f356cSTakashi Iwai 			sprintf(buf, "%s MIDI %d", card->shortname, id+1);
21340dca1793SAdrian Knoth 		}
21350dca1793SAdrian Knoth 		err = snd_rawmidi_new(card, buf, id, 1, 1,
21360dca1793SAdrian Knoth 				&hdspm->midi[id].rmidi);
2137ef5fa1a4STakashi Iwai 		if (err < 0)
2138763f356cSTakashi Iwai 			return err;
2139763f356cSTakashi Iwai 
21400dca1793SAdrian Knoth 		sprintf(hdspm->midi[id].rmidi->name, "%s MIDI %d",
21410dca1793SAdrian Knoth 				card->id, id+1);
2142763f356cSTakashi Iwai 		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
2143763f356cSTakashi Iwai 
21440dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
21450dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_OUTPUT,
2146ef5fa1a4STakashi Iwai 				&snd_hdspm_midi_output);
21470dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
21480dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_INPUT,
2149ef5fa1a4STakashi Iwai 				&snd_hdspm_midi_input);
2150763f356cSTakashi Iwai 
21510dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->info_flags |=
21520dca1793SAdrian Knoth 			SNDRV_RAWMIDI_INFO_OUTPUT |
2153763f356cSTakashi Iwai 			SNDRV_RAWMIDI_INFO_INPUT |
2154763f356cSTakashi Iwai 			SNDRV_RAWMIDI_INFO_DUPLEX;
21550dca1793SAdrian Knoth 	} else {
21560dca1793SAdrian Knoth 		/* TCO MTC, read only */
21570dca1793SAdrian Knoth 		sprintf(buf, "%s MTC %d", card->shortname, id+1);
21580dca1793SAdrian Knoth 		err = snd_rawmidi_new(card, buf, id, 1, 1,
21590dca1793SAdrian Knoth 				&hdspm->midi[id].rmidi);
21600dca1793SAdrian Knoth 		if (err < 0)
21610dca1793SAdrian Knoth 			return err;
21620dca1793SAdrian Knoth 
21630dca1793SAdrian Knoth 		sprintf(hdspm->midi[id].rmidi->name,
21640dca1793SAdrian Knoth 				"%s MTC %d", card->id, id+1);
21650dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
21660dca1793SAdrian Knoth 
21670dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
21680dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_INPUT,
21690dca1793SAdrian Knoth 				&snd_hdspm_midi_input);
21700dca1793SAdrian Knoth 
21710dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
21720dca1793SAdrian Knoth 	}
2173763f356cSTakashi Iwai 
2174763f356cSTakashi Iwai 	return 0;
2175763f356cSTakashi Iwai }
2176763f356cSTakashi Iwai 
2177763f356cSTakashi Iwai 
2178763f356cSTakashi Iwai static void hdspm_midi_tasklet(unsigned long arg)
2179763f356cSTakashi Iwai {
218098274f07STakashi Iwai 	struct hdspm *hdspm = (struct hdspm *)arg;
21810dca1793SAdrian Knoth 	int i = 0;
2182763f356cSTakashi Iwai 
21830dca1793SAdrian Knoth 	while (i < hdspm->midiPorts) {
21840dca1793SAdrian Knoth 		if (hdspm->midi[i].pending)
21850dca1793SAdrian Knoth 			snd_hdspm_midi_input_read(&hdspm->midi[i]);
21860dca1793SAdrian Knoth 
21870dca1793SAdrian Knoth 		i++;
21880dca1793SAdrian Knoth 	}
2189763f356cSTakashi Iwai }
2190763f356cSTakashi Iwai 
2191763f356cSTakashi Iwai 
2192763f356cSTakashi Iwai /*-----------------------------------------------------------------------------
2193763f356cSTakashi Iwai   Status Interface
2194763f356cSTakashi Iwai   ----------------------------------------------------------------------------*/
2195763f356cSTakashi Iwai 
2196763f356cSTakashi Iwai /* get the system sample rate which is set */
2197763f356cSTakashi Iwai 
21980dca1793SAdrian Knoth 
21993f7bf918SAdrian Knoth static inline int hdspm_get_pll_freq(struct hdspm *hdspm)
22003f7bf918SAdrian Knoth {
22013f7bf918SAdrian Knoth 	unsigned int period, rate;
22023f7bf918SAdrian Knoth 
22033f7bf918SAdrian Knoth 	period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
22043f7bf918SAdrian Knoth 	rate = hdspm_calc_dds_value(hdspm, period);
22053f7bf918SAdrian Knoth 
22063f7bf918SAdrian Knoth 	return rate;
22073f7bf918SAdrian Knoth }
22083f7bf918SAdrian Knoth 
22090dca1793SAdrian Knoth /**
22100dca1793SAdrian Knoth  * Calculate the real sample rate from the
22110dca1793SAdrian Knoth  * current DDS value.
22120dca1793SAdrian Knoth  **/
22130dca1793SAdrian Knoth static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
22140dca1793SAdrian Knoth {
22153f7bf918SAdrian Knoth 	unsigned int rate;
22160dca1793SAdrian Knoth 
22173f7bf918SAdrian Knoth 	rate = hdspm_get_pll_freq(hdspm);
22180dca1793SAdrian Knoth 
2219a97bda7dSAdrian Knoth 	if (rate > 207000) {
222021a164dfSAdrian Knoth 		/* Unreasonable high sample rate as seen on PCI MADI cards. */
222121a164dfSAdrian Knoth 		if (0 == hdspm_system_clock_mode(hdspm)) {
222221a164dfSAdrian Knoth 			/* master mode, return internal sample rate */
2223a97bda7dSAdrian Knoth 			rate = hdspm->system_sample_rate;
222421a164dfSAdrian Knoth 		} else {
222521a164dfSAdrian Knoth 			/* slave mode, return external sample rate */
222621a164dfSAdrian Knoth 			rate = hdspm_external_sample_rate(hdspm);
222721a164dfSAdrian Knoth 		}
2228a97bda7dSAdrian Knoth 	}
2229a97bda7dSAdrian Knoth 
22300dca1793SAdrian Knoth 	return rate;
22310dca1793SAdrian Knoth }
22320dca1793SAdrian Knoth 
22330dca1793SAdrian Knoth 
2234763f356cSTakashi Iwai #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
223567ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2236763f356cSTakashi Iwai 	.name = xname, \
2237763f356cSTakashi Iwai 	.index = xindex, \
223841285a98SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
223941285a98SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2240763f356cSTakashi Iwai 	.info = snd_hdspm_info_system_sample_rate, \
224141285a98SAdrian Knoth 	.put = snd_hdspm_put_system_sample_rate, \
2242763f356cSTakashi Iwai 	.get = snd_hdspm_get_system_sample_rate \
2243763f356cSTakashi Iwai }
2244763f356cSTakashi Iwai 
224598274f07STakashi Iwai static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol,
224698274f07STakashi Iwai 					     struct snd_ctl_elem_info *uinfo)
2247763f356cSTakashi Iwai {
2248763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2249763f356cSTakashi Iwai 	uinfo->count = 1;
22500dca1793SAdrian Knoth 	uinfo->value.integer.min = 27000;
22510dca1793SAdrian Knoth 	uinfo->value.integer.max = 207000;
22520dca1793SAdrian Knoth 	uinfo->value.integer.step = 1;
2253763f356cSTakashi Iwai 	return 0;
2254763f356cSTakashi Iwai }
2255763f356cSTakashi Iwai 
22560dca1793SAdrian Knoth 
225798274f07STakashi Iwai static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol,
225898274f07STakashi Iwai 					    struct snd_ctl_elem_value *
2259763f356cSTakashi Iwai 					    ucontrol)
2260763f356cSTakashi Iwai {
226198274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2262763f356cSTakashi Iwai 
22630dca1793SAdrian Knoth 	ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm);
2264763f356cSTakashi Iwai 	return 0;
2265763f356cSTakashi Iwai }
2266763f356cSTakashi Iwai 
226741285a98SAdrian Knoth static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol,
226841285a98SAdrian Knoth 					    struct snd_ctl_elem_value *
226941285a98SAdrian Knoth 					    ucontrol)
227041285a98SAdrian Knoth {
227141285a98SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
227241285a98SAdrian Knoth 
227341285a98SAdrian Knoth 	hdspm_set_dds_value(hdspm, ucontrol->value.enumerated.item[0]);
227441285a98SAdrian Knoth 	return 0;
227541285a98SAdrian Knoth }
227641285a98SAdrian Knoth 
22770dca1793SAdrian Knoth 
22780dca1793SAdrian Knoth /**
22790dca1793SAdrian Knoth  * Returns the WordClock sample rate class for the given card.
22800dca1793SAdrian Knoth  **/
22810dca1793SAdrian Knoth static int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
22820dca1793SAdrian Knoth {
22830dca1793SAdrian Knoth 	int status;
22840dca1793SAdrian Knoth 
22850dca1793SAdrian Knoth 	switch (hdspm->io_type) {
22860dca1793SAdrian Knoth 	case RayDAT:
22870dca1793SAdrian Knoth 	case AIO:
22880dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
22890dca1793SAdrian Knoth 		return (status >> 16) & 0xF;
22900dca1793SAdrian Knoth 		break;
2291a57fea8eSAdrian Knoth 	case AES32:
2292a57fea8eSAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
2293a57fea8eSAdrian Knoth 		return (status >> HDSPM_AES32_wcFreq_bit) & 0xF;
22940dca1793SAdrian Knoth 	default:
22950dca1793SAdrian Knoth 		break;
22960dca1793SAdrian Knoth 	}
22970dca1793SAdrian Knoth 
22980dca1793SAdrian Knoth 
22990dca1793SAdrian Knoth 	return 0;
23000dca1793SAdrian Knoth }
23010dca1793SAdrian Knoth 
23020dca1793SAdrian Knoth 
23030dca1793SAdrian Knoth /**
23040dca1793SAdrian Knoth  * Returns the TCO sample rate class for the given card.
23050dca1793SAdrian Knoth  **/
23060dca1793SAdrian Knoth static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
23070dca1793SAdrian Knoth {
23080dca1793SAdrian Knoth 	int status;
23090dca1793SAdrian Knoth 
23100dca1793SAdrian Knoth 	if (hdspm->tco) {
23110dca1793SAdrian Knoth 		switch (hdspm->io_type) {
23120dca1793SAdrian Knoth 		case RayDAT:
23130dca1793SAdrian Knoth 		case AIO:
23140dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
23150dca1793SAdrian Knoth 			return (status >> 20) & 0xF;
23160dca1793SAdrian Knoth 			break;
2317051c44feSAdrian Knoth 		case AES32:
2318051c44feSAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_statusRegister);
2319051c44feSAdrian Knoth 			return (status >> 1) & 0xF;
23200dca1793SAdrian Knoth 		default:
23210dca1793SAdrian Knoth 			break;
23220dca1793SAdrian Knoth 		}
23230dca1793SAdrian Knoth 	}
23240dca1793SAdrian Knoth 
23250dca1793SAdrian Knoth 	return 0;
23260dca1793SAdrian Knoth }
23270dca1793SAdrian Knoth 
23280dca1793SAdrian Knoth 
23290dca1793SAdrian Knoth /**
23300dca1793SAdrian Knoth  * Returns the SYNC_IN sample rate class for the given card.
23310dca1793SAdrian Knoth  **/
23320dca1793SAdrian Knoth static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
23330dca1793SAdrian Knoth {
23340dca1793SAdrian Knoth 	int status;
23350dca1793SAdrian Knoth 
23360dca1793SAdrian Knoth 	if (hdspm->tco) {
23370dca1793SAdrian Knoth 		switch (hdspm->io_type) {
23380dca1793SAdrian Knoth 		case RayDAT:
23390dca1793SAdrian Knoth 		case AIO:
23400dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
23410dca1793SAdrian Knoth 			return (status >> 12) & 0xF;
23420dca1793SAdrian Knoth 			break;
23430dca1793SAdrian Knoth 		default:
23440dca1793SAdrian Knoth 			break;
23450dca1793SAdrian Knoth 		}
23460dca1793SAdrian Knoth 	}
23470dca1793SAdrian Knoth 
23480dca1793SAdrian Knoth 	return 0;
23490dca1793SAdrian Knoth }
23500dca1793SAdrian Knoth 
2351d3c36ed8SAdrian Knoth /**
2352d3c36ed8SAdrian Knoth  * Returns the AES sample rate class for the given card.
2353d3c36ed8SAdrian Knoth  **/
2354d3c36ed8SAdrian Knoth static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index)
2355d3c36ed8SAdrian Knoth {
2356d3c36ed8SAdrian Knoth 	int timecode;
2357d3c36ed8SAdrian Knoth 
2358d3c36ed8SAdrian Knoth 	switch (hdspm->io_type) {
2359d3c36ed8SAdrian Knoth 	case AES32:
2360d3c36ed8SAdrian Knoth 		timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
2361d3c36ed8SAdrian Knoth 		return (timecode >> (4*index)) & 0xF;
2362d3c36ed8SAdrian Knoth 		break;
2363d3c36ed8SAdrian Knoth 	default:
2364d3c36ed8SAdrian Knoth 		break;
2365d3c36ed8SAdrian Knoth 	}
2366d3c36ed8SAdrian Knoth 	return 0;
2367d3c36ed8SAdrian Knoth }
23680dca1793SAdrian Knoth 
23690dca1793SAdrian Knoth /**
23700dca1793SAdrian Knoth  * Returns the sample rate class for input source <idx> for
23710dca1793SAdrian Knoth  * 'new style' cards like the AIO and RayDAT.
23720dca1793SAdrian Knoth  **/
23730dca1793SAdrian Knoth static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
23740dca1793SAdrian Knoth {
23750dca1793SAdrian Knoth 	int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
23760dca1793SAdrian Knoth 
23770dca1793SAdrian Knoth 	return (status >> (idx*4)) & 0xF;
23780dca1793SAdrian Knoth }
23790dca1793SAdrian Knoth 
23808cea5710SAdrian Knoth #define ENUMERATED_CTL_INFO(info, texts) \
238138816545SAdrian Knoth 	snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts)
23828cea5710SAdrian Knoth 
23830dca1793SAdrian Knoth 
23842336142fSAdrian Knoth /* Helper function to query the external sample rate and return the
23852336142fSAdrian Knoth  * corresponding enum to be returned to userspace.
23862336142fSAdrian Knoth  */
23872336142fSAdrian Knoth static int hdspm_external_rate_to_enum(struct hdspm *hdspm)
23882336142fSAdrian Knoth {
23892336142fSAdrian Knoth 	int rate = hdspm_external_sample_rate(hdspm);
23902336142fSAdrian Knoth 	int i, selected_rate = 0;
23912336142fSAdrian Knoth 	for (i = 1; i < 10; i++)
23922336142fSAdrian Knoth 		if (HDSPM_bit2freq(i) == rate) {
23932336142fSAdrian Knoth 			selected_rate = i;
23942336142fSAdrian Knoth 			break;
23952336142fSAdrian Knoth 		}
23962336142fSAdrian Knoth 	return selected_rate;
23972336142fSAdrian Knoth }
23982336142fSAdrian Knoth 
23990dca1793SAdrian Knoth 
2400763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
240167ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2402763f356cSTakashi Iwai 	.name = xname, \
24030dca1793SAdrian Knoth 	.private_value = xindex, \
2404763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ, \
2405763f356cSTakashi Iwai 	.info = snd_hdspm_info_autosync_sample_rate, \
2406763f356cSTakashi Iwai 	.get = snd_hdspm_get_autosync_sample_rate \
2407763f356cSTakashi Iwai }
2408763f356cSTakashi Iwai 
24090dca1793SAdrian Knoth 
241098274f07STakashi Iwai static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
241198274f07STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
2412763f356cSTakashi Iwai {
2413e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts_freq);
2414763f356cSTakashi Iwai 	return 0;
2415763f356cSTakashi Iwai }
2416763f356cSTakashi Iwai 
24170dca1793SAdrian Knoth 
241898274f07STakashi Iwai static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
241998274f07STakashi Iwai 					      struct snd_ctl_elem_value *
2420763f356cSTakashi Iwai 					      ucontrol)
2421763f356cSTakashi Iwai {
242298274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2423763f356cSTakashi Iwai 
24240dca1793SAdrian Knoth 	switch (hdspm->io_type) {
24250dca1793SAdrian Knoth 	case RayDAT:
24260dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
24270dca1793SAdrian Knoth 		case 0:
24280dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24290dca1793SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
2430763f356cSTakashi Iwai 			break;
24310dca1793SAdrian Knoth 		case 7:
24320dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24330dca1793SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
2434763f356cSTakashi Iwai 			break;
24350dca1793SAdrian Knoth 		case 8:
24360dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24370dca1793SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
2438763f356cSTakashi Iwai 			break;
2439763f356cSTakashi Iwai 		default:
24400dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24410dca1793SAdrian Knoth 				hdspm_get_s1_sample_rate(hdspm,
24420dca1793SAdrian Knoth 						kcontrol->private_value-1);
2443763f356cSTakashi Iwai 		}
2444d681deaaSAdrian Knoth 		break;
24450dca1793SAdrian Knoth 
24460dca1793SAdrian Knoth 	case AIO:
24470dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
24480dca1793SAdrian Knoth 		case 0: /* WC */
24490dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24500dca1793SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
24510dca1793SAdrian Knoth 			break;
24520dca1793SAdrian Knoth 		case 4: /* TCO */
24530dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24540dca1793SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
24550dca1793SAdrian Knoth 			break;
24560dca1793SAdrian Knoth 		case 5: /* SYNC_IN */
24570dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24580dca1793SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
24590dca1793SAdrian Knoth 			break;
24600dca1793SAdrian Knoth 		default:
24610dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24620dca1793SAdrian Knoth 				hdspm_get_s1_sample_rate(hdspm,
24631cb7dbf4SAdrian Knoth 						kcontrol->private_value-1);
24640dca1793SAdrian Knoth 		}
2465d681deaaSAdrian Knoth 		break;
24667c4a95b5SAdrian Knoth 
24677c4a95b5SAdrian Knoth 	case AES32:
24687c4a95b5SAdrian Knoth 
24697c4a95b5SAdrian Knoth 		switch (kcontrol->private_value) {
24707c4a95b5SAdrian Knoth 		case 0: /* WC */
24717c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24727c4a95b5SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
24737c4a95b5SAdrian Knoth 			break;
24747c4a95b5SAdrian Knoth 		case 9: /* TCO */
24757c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24767c4a95b5SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
24777c4a95b5SAdrian Knoth 			break;
24787c4a95b5SAdrian Knoth 		case 10: /* SYNC_IN */
24797c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24807c4a95b5SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
24817c4a95b5SAdrian Knoth 			break;
24822d63ec38SAdrian Knoth 		case 11: /* External Rate */
24832d63ec38SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24842d63ec38SAdrian Knoth 				hdspm_external_rate_to_enum(hdspm);
24852d63ec38SAdrian Knoth 			break;
24867c4a95b5SAdrian Knoth 		default: /* AES1 to AES8 */
24877c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24882d63ec38SAdrian Knoth 				hdspm_get_aes_sample_rate(hdspm,
24892d63ec38SAdrian Knoth 						kcontrol->private_value -
24902d63ec38SAdrian Knoth 						HDSPM_AES32_AUTOSYNC_FROM_AES1);
24917c4a95b5SAdrian Knoth 			break;
24927c4a95b5SAdrian Knoth 		}
2493d681deaaSAdrian Knoth 		break;
2494b8812c55SAdrian Knoth 
2495b8812c55SAdrian Knoth 	case MADI:
2496b8812c55SAdrian Knoth 	case MADIface:
24972336142fSAdrian Knoth 		ucontrol->value.enumerated.item[0] =
24982336142fSAdrian Knoth 			hdspm_external_rate_to_enum(hdspm);
2499b8812c55SAdrian Knoth 		break;
25000dca1793SAdrian Knoth 	default:
25010dca1793SAdrian Knoth 		break;
25020dca1793SAdrian Knoth 	}
25030dca1793SAdrian Knoth 
2504763f356cSTakashi Iwai 	return 0;
2505763f356cSTakashi Iwai }
2506763f356cSTakashi Iwai 
25070dca1793SAdrian Knoth 
2508763f356cSTakashi Iwai #define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \
250967ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2510763f356cSTakashi Iwai 	.name = xname, \
2511763f356cSTakashi Iwai 	.index = xindex, \
25120dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
25130dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2514763f356cSTakashi Iwai 	.info = snd_hdspm_info_system_clock_mode, \
2515763f356cSTakashi Iwai 	.get = snd_hdspm_get_system_clock_mode, \
25160dca1793SAdrian Knoth 	.put = snd_hdspm_put_system_clock_mode, \
2517763f356cSTakashi Iwai }
2518763f356cSTakashi Iwai 
2519763f356cSTakashi Iwai 
25200dca1793SAdrian Knoth /**
25210dca1793SAdrian Knoth  * Returns the system clock mode for the given card.
25220dca1793SAdrian Knoth  * @returns 0 - master, 1 - slave
25230dca1793SAdrian Knoth  **/
252498274f07STakashi Iwai static int hdspm_system_clock_mode(struct hdspm *hdspm)
2525763f356cSTakashi Iwai {
25260dca1793SAdrian Knoth 	switch (hdspm->io_type) {
25270dca1793SAdrian Knoth 	case AIO:
25280dca1793SAdrian Knoth 	case RayDAT:
25290dca1793SAdrian Knoth 		if (hdspm->settings_register & HDSPM_c0Master)
25300dca1793SAdrian Knoth 			return 0;
25310dca1793SAdrian Knoth 		break;
2532763f356cSTakashi Iwai 
25330dca1793SAdrian Knoth 	default:
2534763f356cSTakashi Iwai 		if (hdspm->control_register & HDSPM_ClockModeMaster)
2535763f356cSTakashi Iwai 			return 0;
25360dca1793SAdrian Knoth 	}
25370dca1793SAdrian Knoth 
2538763f356cSTakashi Iwai 	return 1;
2539763f356cSTakashi Iwai }
2540763f356cSTakashi Iwai 
25410dca1793SAdrian Knoth 
25420dca1793SAdrian Knoth /**
25430dca1793SAdrian Knoth  * Sets the system clock mode.
25440dca1793SAdrian Knoth  * @param mode 0 - master, 1 - slave
25450dca1793SAdrian Knoth  **/
25460dca1793SAdrian Knoth static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
25470dca1793SAdrian Knoth {
254834be7ebbSAdrian Knoth 	hdspm_set_toggle_setting(hdspm,
254934be7ebbSAdrian Knoth 			(hdspm_is_raydat_or_aio(hdspm)) ?
255034be7ebbSAdrian Knoth 			HDSPM_c0Master : HDSPM_ClockModeMaster,
255134be7ebbSAdrian Knoth 			(0 == mode));
25520dca1793SAdrian Knoth }
25530dca1793SAdrian Knoth 
25540dca1793SAdrian Knoth 
255598274f07STakashi Iwai static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
255698274f07STakashi Iwai 					    struct snd_ctl_elem_info *uinfo)
2557763f356cSTakashi Iwai {
255838816545SAdrian Knoth 	static const char *const texts[] = { "Master", "AutoSync" };
2559e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
2560763f356cSTakashi Iwai 	return 0;
2561763f356cSTakashi Iwai }
2562763f356cSTakashi Iwai 
256398274f07STakashi Iwai static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol,
256498274f07STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
2565763f356cSTakashi Iwai {
256698274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2567763f356cSTakashi Iwai 
25680dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm);
2569763f356cSTakashi Iwai 	return 0;
2570763f356cSTakashi Iwai }
2571763f356cSTakashi Iwai 
25720dca1793SAdrian Knoth static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol,
25730dca1793SAdrian Knoth 					   struct snd_ctl_elem_value *ucontrol)
25740dca1793SAdrian Knoth {
25750dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
25760dca1793SAdrian Knoth 	int val;
25770dca1793SAdrian Knoth 
25780dca1793SAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
25790dca1793SAdrian Knoth 		return -EBUSY;
25800dca1793SAdrian Knoth 
25810dca1793SAdrian Knoth 	val = ucontrol->value.enumerated.item[0];
25820dca1793SAdrian Knoth 	if (val < 0)
25830dca1793SAdrian Knoth 		val = 0;
25840dca1793SAdrian Knoth 	else if (val > 1)
25850dca1793SAdrian Knoth 		val = 1;
25860dca1793SAdrian Knoth 
25870dca1793SAdrian Knoth 	hdspm_set_system_clock_mode(hdspm, val);
25880dca1793SAdrian Knoth 
25890dca1793SAdrian Knoth 	return 0;
25900dca1793SAdrian Knoth }
25910dca1793SAdrian Knoth 
25920dca1793SAdrian Knoth 
25930dca1793SAdrian Knoth #define HDSPM_INTERNAL_CLOCK(xname, xindex) \
259467ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2595763f356cSTakashi Iwai 	.name = xname, \
2596763f356cSTakashi Iwai 	.index = xindex, \
2597763f356cSTakashi Iwai 	.info = snd_hdspm_info_clock_source, \
2598763f356cSTakashi Iwai 	.get = snd_hdspm_get_clock_source, \
2599763f356cSTakashi Iwai 	.put = snd_hdspm_put_clock_source \
2600763f356cSTakashi Iwai }
2601763f356cSTakashi Iwai 
26020dca1793SAdrian Knoth 
260398274f07STakashi Iwai static int hdspm_clock_source(struct hdspm * hdspm)
2604763f356cSTakashi Iwai {
2605763f356cSTakashi Iwai 	switch (hdspm->system_sample_rate) {
26060dca1793SAdrian Knoth 	case 32000: return 0;
26070dca1793SAdrian Knoth 	case 44100: return 1;
26080dca1793SAdrian Knoth 	case 48000: return 2;
26090dca1793SAdrian Knoth 	case 64000: return 3;
26100dca1793SAdrian Knoth 	case 88200: return 4;
26110dca1793SAdrian Knoth 	case 96000: return 5;
26120dca1793SAdrian Knoth 	case 128000: return 6;
26130dca1793SAdrian Knoth 	case 176400: return 7;
26140dca1793SAdrian Knoth 	case 192000: return 8;
2615763f356cSTakashi Iwai 	}
26160dca1793SAdrian Knoth 
26170dca1793SAdrian Knoth 	return -1;
2618763f356cSTakashi Iwai }
2619763f356cSTakashi Iwai 
262098274f07STakashi Iwai static int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
2621763f356cSTakashi Iwai {
2622763f356cSTakashi Iwai 	int rate;
2623763f356cSTakashi Iwai 	switch (mode) {
26240dca1793SAdrian Knoth 	case 0:
26250dca1793SAdrian Knoth 		rate = 32000; break;
26260dca1793SAdrian Knoth 	case 1:
26270dca1793SAdrian Knoth 		rate = 44100; break;
26280dca1793SAdrian Knoth 	case 2:
26290dca1793SAdrian Knoth 		rate = 48000; break;
26300dca1793SAdrian Knoth 	case 3:
26310dca1793SAdrian Knoth 		rate = 64000; break;
26320dca1793SAdrian Knoth 	case 4:
26330dca1793SAdrian Knoth 		rate = 88200; break;
26340dca1793SAdrian Knoth 	case 5:
26350dca1793SAdrian Knoth 		rate = 96000; break;
26360dca1793SAdrian Knoth 	case 6:
26370dca1793SAdrian Knoth 		rate = 128000; break;
26380dca1793SAdrian Knoth 	case 7:
26390dca1793SAdrian Knoth 		rate = 176400; break;
26400dca1793SAdrian Knoth 	case 8:
26410dca1793SAdrian Knoth 		rate = 192000; break;
2642763f356cSTakashi Iwai 	default:
26430dca1793SAdrian Knoth 		rate = 48000;
2644763f356cSTakashi Iwai 	}
2645763f356cSTakashi Iwai 	hdspm_set_rate(hdspm, rate, 1);
2646763f356cSTakashi Iwai 	return 0;
2647763f356cSTakashi Iwai }
2648763f356cSTakashi Iwai 
264998274f07STakashi Iwai static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol,
265098274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
2651763f356cSTakashi Iwai {
2652763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2653763f356cSTakashi Iwai 	uinfo->count = 1;
26540dca1793SAdrian Knoth 	uinfo->value.enumerated.items = 9;
2655763f356cSTakashi Iwai 
2656763f356cSTakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
2657763f356cSTakashi Iwai 		uinfo->value.enumerated.item =
2658763f356cSTakashi Iwai 		    uinfo->value.enumerated.items - 1;
2659763f356cSTakashi Iwai 
2660763f356cSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
26610dca1793SAdrian Knoth 	       texts_freq[uinfo->value.enumerated.item+1]);
2662763f356cSTakashi Iwai 
2663763f356cSTakashi Iwai 	return 0;
2664763f356cSTakashi Iwai }
2665763f356cSTakashi Iwai 
266698274f07STakashi Iwai static int snd_hdspm_get_clock_source(struct snd_kcontrol *kcontrol,
266798274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
2668763f356cSTakashi Iwai {
266998274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2670763f356cSTakashi Iwai 
2671763f356cSTakashi Iwai 	ucontrol->value.enumerated.item[0] = hdspm_clock_source(hdspm);
2672763f356cSTakashi Iwai 	return 0;
2673763f356cSTakashi Iwai }
2674763f356cSTakashi Iwai 
267598274f07STakashi Iwai static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol,
267698274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
2677763f356cSTakashi Iwai {
267898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2679763f356cSTakashi Iwai 	int change;
2680763f356cSTakashi Iwai 	int val;
2681763f356cSTakashi Iwai 
2682763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2683763f356cSTakashi Iwai 		return -EBUSY;
2684763f356cSTakashi Iwai 	val = ucontrol->value.enumerated.item[0];
2685763f356cSTakashi Iwai 	if (val < 0)
2686763f356cSTakashi Iwai 		val = 0;
26876534599dSRemy Bruno 	if (val > 9)
26886534599dSRemy Bruno 		val = 9;
2689763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2690763f356cSTakashi Iwai 	if (val != hdspm_clock_source(hdspm))
2691763f356cSTakashi Iwai 		change = (hdspm_set_clock_source(hdspm, val) == 0) ? 1 : 0;
2692763f356cSTakashi Iwai 	else
2693763f356cSTakashi Iwai 		change = 0;
2694763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2695763f356cSTakashi Iwai 	return change;
2696763f356cSTakashi Iwai }
2697763f356cSTakashi Iwai 
26980dca1793SAdrian Knoth 
2699763f356cSTakashi Iwai #define HDSPM_PREF_SYNC_REF(xname, xindex) \
270067ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2701763f356cSTakashi Iwai 	.name = xname, \
2702763f356cSTakashi Iwai 	.index = xindex, \
27030dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
27040dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2705763f356cSTakashi Iwai 	.info = snd_hdspm_info_pref_sync_ref, \
2706763f356cSTakashi Iwai 	.get = snd_hdspm_get_pref_sync_ref, \
2707763f356cSTakashi Iwai 	.put = snd_hdspm_put_pref_sync_ref \
2708763f356cSTakashi Iwai }
2709763f356cSTakashi Iwai 
27100dca1793SAdrian Knoth 
27110dca1793SAdrian Knoth /**
27120dca1793SAdrian Knoth  * Returns the current preferred sync reference setting.
27130dca1793SAdrian Knoth  * The semantics of the return value are depending on the
27140dca1793SAdrian Knoth  * card, please see the comments for clarification.
27150dca1793SAdrian Knoth  **/
271698274f07STakashi Iwai static int hdspm_pref_sync_ref(struct hdspm * hdspm)
2717763f356cSTakashi Iwai {
27180dca1793SAdrian Knoth 	switch (hdspm->io_type) {
27190dca1793SAdrian Knoth 	case AES32:
27203cee5a60SRemy Bruno 		switch (hdspm->control_register & HDSPM_SyncRefMask) {
27210dca1793SAdrian Knoth 		case 0: return 0;  /* WC */
27220dca1793SAdrian Knoth 		case HDSPM_SyncRef0: return 1; /* AES 1 */
27230dca1793SAdrian Knoth 		case HDSPM_SyncRef1: return 2; /* AES 2 */
27240dca1793SAdrian Knoth 		case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */
27250dca1793SAdrian Knoth 		case HDSPM_SyncRef2: return 4; /* AES 4 */
27260dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */
27270dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */
27280dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0:
27290dca1793SAdrian Knoth 						    return 7; /* AES 7 */
27300dca1793SAdrian Knoth 		case HDSPM_SyncRef3: return 8; /* AES 8 */
27310dca1793SAdrian Knoth 		case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */
27320dca1793SAdrian Knoth 		}
27330dca1793SAdrian Knoth 		break;
27340dca1793SAdrian Knoth 
27350dca1793SAdrian Knoth 	case MADI:
27360dca1793SAdrian Knoth 	case MADIface:
27370dca1793SAdrian Knoth 		if (hdspm->tco) {
27380dca1793SAdrian Knoth 			switch (hdspm->control_register & HDSPM_SyncRefMask) {
27390dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27400dca1793SAdrian Knoth 			case HDSPM_SyncRef0: return 1;  /* MADI */
27410dca1793SAdrian Knoth 			case HDSPM_SyncRef1: return 2;  /* TCO */
27420dca1793SAdrian Knoth 			case HDSPM_SyncRef1+HDSPM_SyncRef0:
27430dca1793SAdrian Knoth 					     return 3;  /* SYNC_IN */
27443cee5a60SRemy Bruno 			}
27453cee5a60SRemy Bruno 		} else {
2746763f356cSTakashi Iwai 			switch (hdspm->control_register & HDSPM_SyncRefMask) {
27470dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27480dca1793SAdrian Knoth 			case HDSPM_SyncRef0: return 1;  /* MADI */
27490dca1793SAdrian Knoth 			case HDSPM_SyncRef1+HDSPM_SyncRef0:
27500dca1793SAdrian Knoth 					     return 2;  /* SYNC_IN */
27510dca1793SAdrian Knoth 			}
27520dca1793SAdrian Knoth 		}
27530dca1793SAdrian Knoth 		break;
27540dca1793SAdrian Knoth 
27550dca1793SAdrian Knoth 	case RayDAT:
27560dca1793SAdrian Knoth 		if (hdspm->tco) {
27570dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27580dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27590dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27600dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT 1 */
27610dca1793SAdrian Knoth 			case 4: return 2;  /* ADAT 2 */
27620dca1793SAdrian Knoth 			case 5: return 3;  /* ADAT 3 */
27630dca1793SAdrian Knoth 			case 6: return 4;  /* ADAT 4 */
27640dca1793SAdrian Knoth 			case 1: return 5;  /* AES */
27650dca1793SAdrian Knoth 			case 2: return 6;  /* SPDIF */
27660dca1793SAdrian Knoth 			case 9: return 7;  /* TCO */
27670dca1793SAdrian Knoth 			case 10: return 8; /* SYNC_IN */
27680dca1793SAdrian Knoth 			}
27690dca1793SAdrian Knoth 		} else {
27700dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27710dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27720dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27730dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT 1 */
27740dca1793SAdrian Knoth 			case 4: return 2;  /* ADAT 2 */
27750dca1793SAdrian Knoth 			case 5: return 3;  /* ADAT 3 */
27760dca1793SAdrian Knoth 			case 6: return 4;  /* ADAT 4 */
27770dca1793SAdrian Knoth 			case 1: return 5;  /* AES */
27780dca1793SAdrian Knoth 			case 2: return 6;  /* SPDIF */
27790dca1793SAdrian Knoth 			case 10: return 7; /* SYNC_IN */
2780763f356cSTakashi Iwai 			}
27813cee5a60SRemy Bruno 		}
2782763f356cSTakashi Iwai 
27830dca1793SAdrian Knoth 		break;
27840dca1793SAdrian Knoth 
27850dca1793SAdrian Knoth 	case AIO:
27860dca1793SAdrian Knoth 		if (hdspm->tco) {
27870dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27880dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27890dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27900dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT */
27910dca1793SAdrian Knoth 			case 1: return 2;  /* AES */
27920dca1793SAdrian Knoth 			case 2: return 3;  /* SPDIF */
27930dca1793SAdrian Knoth 			case 9: return 4;  /* TCO */
27940dca1793SAdrian Knoth 			case 10: return 5; /* SYNC_IN */
27950dca1793SAdrian Knoth 			}
27960dca1793SAdrian Knoth 		} else {
27970dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27980dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27990dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
28000dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT */
28010dca1793SAdrian Knoth 			case 1: return 2;  /* AES */
28020dca1793SAdrian Knoth 			case 2: return 3;  /* SPDIF */
28030dca1793SAdrian Knoth 			case 10: return 4; /* SYNC_IN */
28040dca1793SAdrian Knoth 			}
2805763f356cSTakashi Iwai 		}
2806763f356cSTakashi Iwai 
28070dca1793SAdrian Knoth 		break;
28080dca1793SAdrian Knoth 	}
28090dca1793SAdrian Knoth 
28100dca1793SAdrian Knoth 	return -1;
28110dca1793SAdrian Knoth }
28120dca1793SAdrian Knoth 
28130dca1793SAdrian Knoth 
28140dca1793SAdrian Knoth /**
28150dca1793SAdrian Knoth  * Set the preferred sync reference to <pref>. The semantics
28160dca1793SAdrian Knoth  * of <pref> are depending on the card type, see the comments
28170dca1793SAdrian Knoth  * for clarification.
28180dca1793SAdrian Knoth  **/
281998274f07STakashi Iwai static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
2820763f356cSTakashi Iwai {
28210dca1793SAdrian Knoth 	int p = 0;
2822763f356cSTakashi Iwai 
28230dca1793SAdrian Knoth 	switch (hdspm->io_type) {
28240dca1793SAdrian Knoth 	case AES32:
28250dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPM_SyncRefMask;
28263cee5a60SRemy Bruno 		switch (pref) {
28270dca1793SAdrian Knoth 		case 0: /* WC  */
28283cee5a60SRemy Bruno 			break;
28290dca1793SAdrian Knoth 		case 1: /* AES 1 */
28303cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef0;
28313cee5a60SRemy Bruno 			break;
28320dca1793SAdrian Knoth 		case 2: /* AES 2 */
28333cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef1;
28343cee5a60SRemy Bruno 			break;
28350dca1793SAdrian Knoth 		case 3: /* AES 3 */
28360dca1793SAdrian Knoth 			hdspm->control_register |=
28370dca1793SAdrian Knoth 				HDSPM_SyncRef1+HDSPM_SyncRef0;
28383cee5a60SRemy Bruno 			break;
28390dca1793SAdrian Knoth 		case 4: /* AES 4 */
28403cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef2;
28413cee5a60SRemy Bruno 			break;
28420dca1793SAdrian Knoth 		case 5: /* AES 5 */
28430dca1793SAdrian Knoth 			hdspm->control_register |=
28440dca1793SAdrian Knoth 				HDSPM_SyncRef2+HDSPM_SyncRef0;
28453cee5a60SRemy Bruno 			break;
28460dca1793SAdrian Knoth 		case 6: /* AES 6 */
28470dca1793SAdrian Knoth 			hdspm->control_register |=
28480dca1793SAdrian Knoth 				HDSPM_SyncRef2+HDSPM_SyncRef1;
28493cee5a60SRemy Bruno 			break;
28500dca1793SAdrian Knoth 		case 7: /* AES 7 */
2851ef5fa1a4STakashi Iwai 			hdspm->control_register |=
2852ef5fa1a4STakashi Iwai 				HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
28533cee5a60SRemy Bruno 			break;
28540dca1793SAdrian Knoth 		case 8: /* AES 8 */
28553cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef3;
28563cee5a60SRemy Bruno 			break;
28570dca1793SAdrian Knoth 		case 9: /* TCO */
28580dca1793SAdrian Knoth 			hdspm->control_register |=
28590dca1793SAdrian Knoth 				HDSPM_SyncRef3+HDSPM_SyncRef0;
28600dca1793SAdrian Knoth 			break;
28610dca1793SAdrian Knoth 		default:
28620dca1793SAdrian Knoth 			return -1;
28630dca1793SAdrian Knoth 		}
28640dca1793SAdrian Knoth 
28650dca1793SAdrian Knoth 		break;
28660dca1793SAdrian Knoth 
28670dca1793SAdrian Knoth 	case MADI:
28680dca1793SAdrian Knoth 	case MADIface:
28690dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPM_SyncRefMask;
28700dca1793SAdrian Knoth 		if (hdspm->tco) {
28710dca1793SAdrian Knoth 			switch (pref) {
28720dca1793SAdrian Knoth 			case 0: /* WC */
28730dca1793SAdrian Knoth 				break;
28740dca1793SAdrian Knoth 			case 1: /* MADI */
28750dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef0;
28760dca1793SAdrian Knoth 				break;
28770dca1793SAdrian Knoth 			case 2: /* TCO */
28780dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef1;
28790dca1793SAdrian Knoth 				break;
28800dca1793SAdrian Knoth 			case 3: /* SYNC_IN */
28810dca1793SAdrian Knoth 				hdspm->control_register |=
28820dca1793SAdrian Knoth 					HDSPM_SyncRef0+HDSPM_SyncRef1;
28830dca1793SAdrian Knoth 				break;
28843cee5a60SRemy Bruno 			default:
28853cee5a60SRemy Bruno 				return -1;
28863cee5a60SRemy Bruno 			}
28873cee5a60SRemy Bruno 		} else {
2888763f356cSTakashi Iwai 			switch (pref) {
28890dca1793SAdrian Knoth 			case 0: /* WC */
2890763f356cSTakashi Iwai 				break;
28910dca1793SAdrian Knoth 			case 1: /* MADI */
28920dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef0;
28930dca1793SAdrian Knoth 				break;
28940dca1793SAdrian Knoth 			case 2: /* SYNC_IN */
28950dca1793SAdrian Knoth 				hdspm->control_register |=
28960dca1793SAdrian Knoth 					HDSPM_SyncRef0+HDSPM_SyncRef1;
2897763f356cSTakashi Iwai 				break;
2898763f356cSTakashi Iwai 			default:
2899763f356cSTakashi Iwai 				return -1;
2900763f356cSTakashi Iwai 			}
29013cee5a60SRemy Bruno 		}
29020dca1793SAdrian Knoth 
29030dca1793SAdrian Knoth 		break;
29040dca1793SAdrian Knoth 
29050dca1793SAdrian Knoth 	case RayDAT:
29060dca1793SAdrian Knoth 		if (hdspm->tco) {
29070dca1793SAdrian Knoth 			switch (pref) {
29080dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
29090dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT 1 */
29100dca1793SAdrian Knoth 			case 2: p = 4; break;  /* ADAT 2 */
29110dca1793SAdrian Knoth 			case 3: p = 5; break;  /* ADAT 3 */
29120dca1793SAdrian Knoth 			case 4: p = 6; break;  /* ADAT 4 */
29130dca1793SAdrian Knoth 			case 5: p = 1; break;  /* AES */
29140dca1793SAdrian Knoth 			case 6: p = 2; break;  /* SPDIF */
29150dca1793SAdrian Knoth 			case 7: p = 9; break;  /* TCO */
29160dca1793SAdrian Knoth 			case 8: p = 10; break; /* SYNC_IN */
29170dca1793SAdrian Knoth 			default: return -1;
29180dca1793SAdrian Knoth 			}
29190dca1793SAdrian Knoth 		} else {
29200dca1793SAdrian Knoth 			switch (pref) {
29210dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
29220dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT 1 */
29230dca1793SAdrian Knoth 			case 2: p = 4; break;  /* ADAT 2 */
29240dca1793SAdrian Knoth 			case 3: p = 5; break;  /* ADAT 3 */
29250dca1793SAdrian Knoth 			case 4: p = 6; break;  /* ADAT 4 */
29260dca1793SAdrian Knoth 			case 5: p = 1; break;  /* AES */
29270dca1793SAdrian Knoth 			case 6: p = 2; break;  /* SPDIF */
29280dca1793SAdrian Knoth 			case 7: p = 10; break; /* SYNC_IN */
29290dca1793SAdrian Knoth 			default: return -1;
29300dca1793SAdrian Knoth 			}
29310dca1793SAdrian Knoth 		}
29320dca1793SAdrian Knoth 		break;
29330dca1793SAdrian Knoth 
29340dca1793SAdrian Knoth 	case AIO:
29350dca1793SAdrian Knoth 		if (hdspm->tco) {
29360dca1793SAdrian Knoth 			switch (pref) {
29370dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
29380dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT */
29390dca1793SAdrian Knoth 			case 2: p = 1; break;  /* AES */
29400dca1793SAdrian Knoth 			case 3: p = 2; break;  /* SPDIF */
29410dca1793SAdrian Knoth 			case 4: p = 9; break;  /* TCO */
29420dca1793SAdrian Knoth 			case 5: p = 10; break; /* SYNC_IN */
29430dca1793SAdrian Knoth 			default: return -1;
29440dca1793SAdrian Knoth 			}
29450dca1793SAdrian Knoth 		} else {
29460dca1793SAdrian Knoth 			switch (pref) {
29470dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
29480dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT */
29490dca1793SAdrian Knoth 			case 2: p = 1; break;  /* AES */
29500dca1793SAdrian Knoth 			case 3: p = 2; break;  /* SPDIF */
29510dca1793SAdrian Knoth 			case 4: p = 10; break; /* SYNC_IN */
29520dca1793SAdrian Knoth 			default: return -1;
29530dca1793SAdrian Knoth 			}
29540dca1793SAdrian Knoth 		}
29550dca1793SAdrian Knoth 		break;
29560dca1793SAdrian Knoth 	}
29570dca1793SAdrian Knoth 
29580dca1793SAdrian Knoth 	switch (hdspm->io_type) {
29590dca1793SAdrian Knoth 	case RayDAT:
29600dca1793SAdrian Knoth 	case AIO:
29610dca1793SAdrian Knoth 		hdspm->settings_register &= ~HDSPM_c0_SyncRefMask;
29620dca1793SAdrian Knoth 		hdspm->settings_register |= HDSPM_c0_SyncRef0 * p;
29630dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
29640dca1793SAdrian Knoth 		break;
29650dca1793SAdrian Knoth 
29660dca1793SAdrian Knoth 	case MADI:
29670dca1793SAdrian Knoth 	case MADIface:
29680dca1793SAdrian Knoth 	case AES32:
29690dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_controlRegister,
29700dca1793SAdrian Knoth 				hdspm->control_register);
29710dca1793SAdrian Knoth 	}
29720dca1793SAdrian Knoth 
2973763f356cSTakashi Iwai 	return 0;
2974763f356cSTakashi Iwai }
2975763f356cSTakashi Iwai 
29760dca1793SAdrian Knoth 
297798274f07STakashi Iwai static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
297898274f07STakashi Iwai 					struct snd_ctl_elem_info *uinfo)
2979763f356cSTakashi Iwai {
29803cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
29813cee5a60SRemy Bruno 
2982eb0d4dbfSAdrian Knoth 	snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync);
2983763f356cSTakashi Iwai 
2984763f356cSTakashi Iwai 	return 0;
2985763f356cSTakashi Iwai }
2986763f356cSTakashi Iwai 
298798274f07STakashi Iwai static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol,
298898274f07STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
2989763f356cSTakashi Iwai {
299098274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
29910dca1793SAdrian Knoth 	int psf = hdspm_pref_sync_ref(hdspm);
2992763f356cSTakashi Iwai 
29930dca1793SAdrian Knoth 	if (psf >= 0) {
29940dca1793SAdrian Knoth 		ucontrol->value.enumerated.item[0] = psf;
2995763f356cSTakashi Iwai 		return 0;
2996763f356cSTakashi Iwai 	}
2997763f356cSTakashi Iwai 
29980dca1793SAdrian Knoth 	return -1;
29990dca1793SAdrian Knoth }
30000dca1793SAdrian Knoth 
300198274f07STakashi Iwai static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
300298274f07STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
3003763f356cSTakashi Iwai {
300498274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
30050dca1793SAdrian Knoth 	int val, change = 0;
3006763f356cSTakashi Iwai 
3007763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3008763f356cSTakashi Iwai 		return -EBUSY;
3009763f356cSTakashi Iwai 
30100dca1793SAdrian Knoth 	val = ucontrol->value.enumerated.item[0];
30110dca1793SAdrian Knoth 
30120dca1793SAdrian Knoth 	if (val < 0)
30130dca1793SAdrian Knoth 		val = 0;
30140dca1793SAdrian Knoth 	else if (val >= hdspm->texts_autosync_items)
30150dca1793SAdrian Knoth 		val = hdspm->texts_autosync_items-1;
3016763f356cSTakashi Iwai 
3017763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
30180dca1793SAdrian Knoth 	if (val != hdspm_pref_sync_ref(hdspm))
30190dca1793SAdrian Knoth 		change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0;
30200dca1793SAdrian Knoth 
3021763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3022763f356cSTakashi Iwai 	return change;
3023763f356cSTakashi Iwai }
3024763f356cSTakashi Iwai 
30250dca1793SAdrian Knoth 
3026763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_REF(xname, xindex) \
302767ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3028763f356cSTakashi Iwai 	.name = xname, \
3029763f356cSTakashi Iwai 	.index = xindex, \
3030763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ, \
3031763f356cSTakashi Iwai 	.info = snd_hdspm_info_autosync_ref, \
3032763f356cSTakashi Iwai 	.get = snd_hdspm_get_autosync_ref, \
3033763f356cSTakashi Iwai }
3034763f356cSTakashi Iwai 
303598274f07STakashi Iwai static int hdspm_autosync_ref(struct hdspm *hdspm)
3036763f356cSTakashi Iwai {
3037763f356cSTakashi Iwai 	/* This looks at the autosync selected sync reference */
30382d60fc7fSAdrian Knoth 	if (AES32 == hdspm->io_type) {
3039763f356cSTakashi Iwai 
30402d60fc7fSAdrian Knoth 		unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
30412d60fc7fSAdrian Knoth 		unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
30422d60fc7fSAdrian Knoth 		if ((syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD) &&
30432d60fc7fSAdrian Knoth 				(syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN)) {
30442d60fc7fSAdrian Knoth 			return syncref;
30452d60fc7fSAdrian Knoth 		}
30462d60fc7fSAdrian Knoth 		return HDSPM_AES32_AUTOSYNC_FROM_NONE;
30472d60fc7fSAdrian Knoth 
30482d60fc7fSAdrian Knoth 	} else if (MADI == hdspm->io_type) {
30492d60fc7fSAdrian Knoth 
30502d60fc7fSAdrian Knoth 		unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
3051763f356cSTakashi Iwai 		switch (status2 & HDSPM_SelSyncRefMask) {
3052763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_WORD:
3053763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_WORD;
3054763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_MADI:
3055763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_MADI;
30560dca1793SAdrian Knoth 		case HDSPM_SelSyncRef_TCO:
30570dca1793SAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_TCO;
30580dca1793SAdrian Knoth 		case HDSPM_SelSyncRef_SyncIn:
30590dca1793SAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_SYNC_IN;
3060763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_NVALID:
3061763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_NONE;
3062763f356cSTakashi Iwai 		default:
3063e71b95adSAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_NONE;
3064763f356cSTakashi Iwai 		}
3065763f356cSTakashi Iwai 
30660dca1793SAdrian Knoth 	}
3067763f356cSTakashi Iwai 	return 0;
3068763f356cSTakashi Iwai }
30690dca1793SAdrian Knoth 
3070763f356cSTakashi Iwai 
307198274f07STakashi Iwai static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
307298274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
3073763f356cSTakashi Iwai {
30743cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
30753cee5a60SRemy Bruno 
30760dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
307704659f9eSAdrian Knoth 		static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3",
3078db2d1a91SAdrian Knoth 			"AES4",	"AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"};
30793cee5a60SRemy Bruno 
308004659f9eSAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts);
30810dca1793SAdrian Knoth 	} else if (MADI == hdspm->io_type) {
308204659f9eSAdrian Knoth 		static const char *const texts[] = {"Word Clock", "MADI", "TCO",
30830dca1793SAdrian Knoth 			"Sync In", "None" };
3084763f356cSTakashi Iwai 
308504659f9eSAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts);
30863cee5a60SRemy Bruno 	}
3087763f356cSTakashi Iwai 	return 0;
3088763f356cSTakashi Iwai }
3089763f356cSTakashi Iwai 
309098274f07STakashi Iwai static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
309198274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3092763f356cSTakashi Iwai {
309398274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3094763f356cSTakashi Iwai 
30956534599dSRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_autosync_ref(hdspm);
3096763f356cSTakashi Iwai 	return 0;
3097763f356cSTakashi Iwai }
3098763f356cSTakashi Iwai 
3099f99c7881SAdrian Knoth 
3100f99c7881SAdrian Knoth 
3101f99c7881SAdrian Knoth #define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \
3102f99c7881SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3103f99c7881SAdrian Knoth 	.name = xname, \
3104f99c7881SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READ |\
3105f99c7881SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3106f99c7881SAdrian Knoth 	.info = snd_hdspm_info_tco_video_input_format, \
3107f99c7881SAdrian Knoth 	.get = snd_hdspm_get_tco_video_input_format, \
3108f99c7881SAdrian Knoth }
3109f99c7881SAdrian Knoth 
3110f99c7881SAdrian Knoth static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
3111f99c7881SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3112f99c7881SAdrian Knoth {
311338816545SAdrian Knoth 	static const char *const texts[] = {"No video", "NTSC", "PAL"};
3114f99c7881SAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3115f99c7881SAdrian Knoth 	return 0;
3116f99c7881SAdrian Knoth }
3117f99c7881SAdrian Knoth 
3118f99c7881SAdrian Knoth static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
3119f99c7881SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3120f99c7881SAdrian Knoth {
3121f99c7881SAdrian Knoth 	u32 status;
3122f99c7881SAdrian Knoth 	int ret = 0;
3123f99c7881SAdrian Knoth 
3124f99c7881SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3125f99c7881SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
3126f99c7881SAdrian Knoth 	switch (status & (HDSPM_TCO1_Video_Input_Format_NTSC |
3127f99c7881SAdrian Knoth 			HDSPM_TCO1_Video_Input_Format_PAL)) {
3128f99c7881SAdrian Knoth 	case HDSPM_TCO1_Video_Input_Format_NTSC:
3129f99c7881SAdrian Knoth 		/* ntsc */
3130f99c7881SAdrian Knoth 		ret = 1;
3131f99c7881SAdrian Knoth 		break;
3132f99c7881SAdrian Knoth 	case HDSPM_TCO1_Video_Input_Format_PAL:
3133f99c7881SAdrian Knoth 		/* pal */
3134f99c7881SAdrian Knoth 		ret = 2;
3135f99c7881SAdrian Knoth 		break;
3136f99c7881SAdrian Knoth 	default:
3137f99c7881SAdrian Knoth 		/* no video */
3138f99c7881SAdrian Knoth 		ret = 0;
3139f99c7881SAdrian Knoth 		break;
3140f99c7881SAdrian Knoth 	}
3141f99c7881SAdrian Knoth 	ucontrol->value.enumerated.item[0] = ret;
3142f99c7881SAdrian Knoth 	return 0;
3143f99c7881SAdrian Knoth }
3144f99c7881SAdrian Knoth 
3145f99c7881SAdrian Knoth 
3146f99c7881SAdrian Knoth 
3147f99c7881SAdrian Knoth #define HDSPM_TCO_LTC_FRAMES(xname, xindex) \
3148f99c7881SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3149f99c7881SAdrian Knoth 	.name = xname, \
3150f99c7881SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READ |\
3151f99c7881SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3152f99c7881SAdrian Knoth 	.info = snd_hdspm_info_tco_ltc_frames, \
3153f99c7881SAdrian Knoth 	.get = snd_hdspm_get_tco_ltc_frames, \
3154f99c7881SAdrian Knoth }
3155f99c7881SAdrian Knoth 
3156f99c7881SAdrian Knoth static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
3157f99c7881SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3158f99c7881SAdrian Knoth {
315938816545SAdrian Knoth 	static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
3160f99c7881SAdrian Knoth 				"30 fps"};
3161f99c7881SAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3162f99c7881SAdrian Knoth 	return 0;
3163f99c7881SAdrian Knoth }
3164f99c7881SAdrian Knoth 
3165f99c7881SAdrian Knoth static int hdspm_tco_ltc_frames(struct hdspm *hdspm)
3166f99c7881SAdrian Knoth {
3167f99c7881SAdrian Knoth 	u32 status;
3168f99c7881SAdrian Knoth 	int ret = 0;
3169f99c7881SAdrian Knoth 
3170f99c7881SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
3171f99c7881SAdrian Knoth 	if (status & HDSPM_TCO1_LTC_Input_valid) {
3172f99c7881SAdrian Knoth 		switch (status & (HDSPM_TCO1_LTC_Format_LSB |
3173f99c7881SAdrian Knoth 					HDSPM_TCO1_LTC_Format_MSB)) {
3174f99c7881SAdrian Knoth 		case 0:
3175f99c7881SAdrian Knoth 			/* 24 fps */
3176f99c7881SAdrian Knoth 			ret = 1;
3177f99c7881SAdrian Knoth 			break;
3178f99c7881SAdrian Knoth 		case HDSPM_TCO1_LTC_Format_LSB:
3179f99c7881SAdrian Knoth 			/* 25 fps */
3180f99c7881SAdrian Knoth 			ret = 2;
3181f99c7881SAdrian Knoth 			break;
3182f99c7881SAdrian Knoth 		case HDSPM_TCO1_LTC_Format_MSB:
3183f99c7881SAdrian Knoth 			/* 25 fps */
3184f99c7881SAdrian Knoth 			ret = 3;
3185f99c7881SAdrian Knoth 			break;
3186f99c7881SAdrian Knoth 		default:
3187f99c7881SAdrian Knoth 			/* 30 fps */
3188f99c7881SAdrian Knoth 			ret = 4;
3189f99c7881SAdrian Knoth 			break;
3190f99c7881SAdrian Knoth 		}
3191f99c7881SAdrian Knoth 	}
3192f99c7881SAdrian Knoth 
3193f99c7881SAdrian Knoth 	return ret;
3194f99c7881SAdrian Knoth }
3195f99c7881SAdrian Knoth 
3196f99c7881SAdrian Knoth static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
3197f99c7881SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3198f99c7881SAdrian Knoth {
3199f99c7881SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3200f99c7881SAdrian Knoth 
3201f99c7881SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_tco_ltc_frames(hdspm);
3202f99c7881SAdrian Knoth 	return 0;
3203f99c7881SAdrian Knoth }
3204f99c7881SAdrian Knoth 
3205bf0ff87bSAdrian Knoth #define HDSPM_TOGGLE_SETTING(xname, xindex) \
3206bf0ff87bSAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3207bf0ff87bSAdrian Knoth 	.name = xname, \
3208bf0ff87bSAdrian Knoth 	.private_value = xindex, \
3209bf0ff87bSAdrian Knoth 	.info = snd_hdspm_info_toggle_setting, \
3210bf0ff87bSAdrian Knoth 	.get = snd_hdspm_get_toggle_setting, \
3211bf0ff87bSAdrian Knoth 	.put = snd_hdspm_put_toggle_setting \
3212bf0ff87bSAdrian Knoth }
3213bf0ff87bSAdrian Knoth 
3214bf0ff87bSAdrian Knoth static int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask)
3215bf0ff87bSAdrian Knoth {
3216ce13f3f3SAdrian Knoth 	u32 reg;
3217ce13f3f3SAdrian Knoth 
3218ce13f3f3SAdrian Knoth 	if (hdspm_is_raydat_or_aio(hdspm))
3219ce13f3f3SAdrian Knoth 		reg = hdspm->settings_register;
3220ce13f3f3SAdrian Knoth 	else
3221ce13f3f3SAdrian Knoth 		reg = hdspm->control_register;
3222ce13f3f3SAdrian Knoth 
3223ce13f3f3SAdrian Knoth 	return (reg & regmask) ? 1 : 0;
3224bf0ff87bSAdrian Knoth }
3225bf0ff87bSAdrian Knoth 
3226bf0ff87bSAdrian Knoth static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out)
3227bf0ff87bSAdrian Knoth {
3228ce13f3f3SAdrian Knoth 	u32 *reg;
3229ce13f3f3SAdrian Knoth 	u32 target_reg;
3230ce13f3f3SAdrian Knoth 
3231ce13f3f3SAdrian Knoth 	if (hdspm_is_raydat_or_aio(hdspm)) {
3232ce13f3f3SAdrian Knoth 		reg = &(hdspm->settings_register);
3233ce13f3f3SAdrian Knoth 		target_reg = HDSPM_WR_SETTINGS;
3234ce13f3f3SAdrian Knoth 	} else {
3235ce13f3f3SAdrian Knoth 		reg = &(hdspm->control_register);
3236ce13f3f3SAdrian Knoth 		target_reg = HDSPM_controlRegister;
3237ce13f3f3SAdrian Knoth 	}
3238ce13f3f3SAdrian Knoth 
3239bf0ff87bSAdrian Knoth 	if (out)
3240ce13f3f3SAdrian Knoth 		*reg |= regmask;
3241bf0ff87bSAdrian Knoth 	else
3242ce13f3f3SAdrian Knoth 		*reg &= ~regmask;
3243ce13f3f3SAdrian Knoth 
3244ce13f3f3SAdrian Knoth 	hdspm_write(hdspm, target_reg, *reg);
3245bf0ff87bSAdrian Knoth 
3246bf0ff87bSAdrian Knoth 	return 0;
3247bf0ff87bSAdrian Knoth }
3248bf0ff87bSAdrian Knoth 
3249bf0ff87bSAdrian Knoth #define snd_hdspm_info_toggle_setting		snd_ctl_boolean_mono_info
3250bf0ff87bSAdrian Knoth 
3251bf0ff87bSAdrian Knoth static int snd_hdspm_get_toggle_setting(struct snd_kcontrol *kcontrol,
3252bf0ff87bSAdrian Knoth 			       struct snd_ctl_elem_value *ucontrol)
3253bf0ff87bSAdrian Knoth {
3254bf0ff87bSAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3255bf0ff87bSAdrian Knoth 	u32 regmask = kcontrol->private_value;
3256bf0ff87bSAdrian Knoth 
3257bf0ff87bSAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3258bf0ff87bSAdrian Knoth 	ucontrol->value.integer.value[0] = hdspm_toggle_setting(hdspm, regmask);
3259bf0ff87bSAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3260bf0ff87bSAdrian Knoth 	return 0;
3261bf0ff87bSAdrian Knoth }
3262bf0ff87bSAdrian Knoth 
3263bf0ff87bSAdrian Knoth static int snd_hdspm_put_toggle_setting(struct snd_kcontrol *kcontrol,
3264bf0ff87bSAdrian Knoth 			       struct snd_ctl_elem_value *ucontrol)
3265bf0ff87bSAdrian Knoth {
3266bf0ff87bSAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3267bf0ff87bSAdrian Knoth 	u32 regmask = kcontrol->private_value;
3268bf0ff87bSAdrian Knoth 	int change;
3269bf0ff87bSAdrian Knoth 	unsigned int val;
3270bf0ff87bSAdrian Knoth 
3271bf0ff87bSAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
3272bf0ff87bSAdrian Knoth 		return -EBUSY;
3273bf0ff87bSAdrian Knoth 	val = ucontrol->value.integer.value[0] & 1;
3274bf0ff87bSAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3275bf0ff87bSAdrian Knoth 	change = (int) val != hdspm_toggle_setting(hdspm, regmask);
3276bf0ff87bSAdrian Knoth 	hdspm_set_toggle_setting(hdspm, regmask, val);
3277bf0ff87bSAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3278bf0ff87bSAdrian Knoth 	return change;
3279bf0ff87bSAdrian Knoth }
3280bf0ff87bSAdrian Knoth 
3281763f356cSTakashi Iwai #define HDSPM_INPUT_SELECT(xname, xindex) \
328267ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3283763f356cSTakashi Iwai 	.name = xname, \
3284763f356cSTakashi Iwai 	.index = xindex, \
3285763f356cSTakashi Iwai 	.info = snd_hdspm_info_input_select, \
3286763f356cSTakashi Iwai 	.get = snd_hdspm_get_input_select, \
3287763f356cSTakashi Iwai 	.put = snd_hdspm_put_input_select \
3288763f356cSTakashi Iwai }
3289763f356cSTakashi Iwai 
329098274f07STakashi Iwai static int hdspm_input_select(struct hdspm * hdspm)
3291763f356cSTakashi Iwai {
3292763f356cSTakashi Iwai 	return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
3293763f356cSTakashi Iwai }
3294763f356cSTakashi Iwai 
329598274f07STakashi Iwai static int hdspm_set_input_select(struct hdspm * hdspm, int out)
3296763f356cSTakashi Iwai {
3297763f356cSTakashi Iwai 	if (out)
3298763f356cSTakashi Iwai 		hdspm->control_register |= HDSPM_InputSelect0;
3299763f356cSTakashi Iwai 	else
3300763f356cSTakashi Iwai 		hdspm->control_register &= ~HDSPM_InputSelect0;
3301763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
3302763f356cSTakashi Iwai 
3303763f356cSTakashi Iwai 	return 0;
3304763f356cSTakashi Iwai }
3305763f356cSTakashi Iwai 
330698274f07STakashi Iwai static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
330798274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
3308763f356cSTakashi Iwai {
330938816545SAdrian Knoth 	static const char *const texts[] = { "optical", "coaxial" };
3310e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3311763f356cSTakashi Iwai 	return 0;
3312763f356cSTakashi Iwai }
3313763f356cSTakashi Iwai 
331498274f07STakashi Iwai static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
331598274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3316763f356cSTakashi Iwai {
331798274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3318763f356cSTakashi Iwai 
3319763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3320763f356cSTakashi Iwai 	ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
3321763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3322763f356cSTakashi Iwai 	return 0;
3323763f356cSTakashi Iwai }
3324763f356cSTakashi Iwai 
332598274f07STakashi Iwai static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
332698274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3327763f356cSTakashi Iwai {
332898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3329763f356cSTakashi Iwai 	int change;
3330763f356cSTakashi Iwai 	unsigned int val;
3331763f356cSTakashi Iwai 
3332763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3333763f356cSTakashi Iwai 		return -EBUSY;
3334763f356cSTakashi Iwai 	val = ucontrol->value.integer.value[0] & 1;
3335763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3336763f356cSTakashi Iwai 	change = (int) val != hdspm_input_select(hdspm);
3337763f356cSTakashi Iwai 	hdspm_set_input_select(hdspm, val);
3338763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3339763f356cSTakashi Iwai 	return change;
3340763f356cSTakashi Iwai }
3341763f356cSTakashi Iwai 
33420dca1793SAdrian Knoth 
33433cee5a60SRemy Bruno #define HDSPM_DS_WIRE(xname, xindex) \
33443cee5a60SRemy Bruno {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
33453cee5a60SRemy Bruno 	.name = xname, \
33463cee5a60SRemy Bruno 	.index = xindex, \
33473cee5a60SRemy Bruno 	.info = snd_hdspm_info_ds_wire, \
33483cee5a60SRemy Bruno 	.get = snd_hdspm_get_ds_wire, \
33493cee5a60SRemy Bruno 	.put = snd_hdspm_put_ds_wire \
33503cee5a60SRemy Bruno }
33513cee5a60SRemy Bruno 
33523cee5a60SRemy Bruno static int hdspm_ds_wire(struct hdspm * hdspm)
33533cee5a60SRemy Bruno {
33543cee5a60SRemy Bruno 	return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
33553cee5a60SRemy Bruno }
33563cee5a60SRemy Bruno 
33573cee5a60SRemy Bruno static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
33583cee5a60SRemy Bruno {
33593cee5a60SRemy Bruno 	if (ds)
33603cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_DS_DoubleWire;
33613cee5a60SRemy Bruno 	else
33623cee5a60SRemy Bruno 		hdspm->control_register &= ~HDSPM_DS_DoubleWire;
33633cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
33643cee5a60SRemy Bruno 
33653cee5a60SRemy Bruno 	return 0;
33663cee5a60SRemy Bruno }
33673cee5a60SRemy Bruno 
33683cee5a60SRemy Bruno static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
33693cee5a60SRemy Bruno 				  struct snd_ctl_elem_info *uinfo)
33703cee5a60SRemy Bruno {
337138816545SAdrian Knoth 	static const char *const texts[] = { "Single", "Double" };
3372e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
33733cee5a60SRemy Bruno 	return 0;
33743cee5a60SRemy Bruno }
33753cee5a60SRemy Bruno 
33763cee5a60SRemy Bruno static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
33773cee5a60SRemy Bruno 				 struct snd_ctl_elem_value *ucontrol)
33783cee5a60SRemy Bruno {
33793cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33803cee5a60SRemy Bruno 
33813cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
33823cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
33833cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
33843cee5a60SRemy Bruno 	return 0;
33853cee5a60SRemy Bruno }
33863cee5a60SRemy Bruno 
33873cee5a60SRemy Bruno static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
33883cee5a60SRemy Bruno 				 struct snd_ctl_elem_value *ucontrol)
33893cee5a60SRemy Bruno {
33903cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33913cee5a60SRemy Bruno 	int change;
33923cee5a60SRemy Bruno 	unsigned int val;
33933cee5a60SRemy Bruno 
33943cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
33953cee5a60SRemy Bruno 		return -EBUSY;
33963cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0] & 1;
33973cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
33983cee5a60SRemy Bruno 	change = (int) val != hdspm_ds_wire(hdspm);
33993cee5a60SRemy Bruno 	hdspm_set_ds_wire(hdspm, val);
34003cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
34013cee5a60SRemy Bruno 	return change;
34023cee5a60SRemy Bruno }
34033cee5a60SRemy Bruno 
34040dca1793SAdrian Knoth 
34053cee5a60SRemy Bruno #define HDSPM_QS_WIRE(xname, xindex) \
34063cee5a60SRemy Bruno {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
34073cee5a60SRemy Bruno 	.name = xname, \
34083cee5a60SRemy Bruno 	.index = xindex, \
34093cee5a60SRemy Bruno 	.info = snd_hdspm_info_qs_wire, \
34103cee5a60SRemy Bruno 	.get = snd_hdspm_get_qs_wire, \
34113cee5a60SRemy Bruno 	.put = snd_hdspm_put_qs_wire \
34123cee5a60SRemy Bruno }
34133cee5a60SRemy Bruno 
34143cee5a60SRemy Bruno static int hdspm_qs_wire(struct hdspm * hdspm)
34153cee5a60SRemy Bruno {
34163cee5a60SRemy Bruno 	if (hdspm->control_register & HDSPM_QS_DoubleWire)
34173cee5a60SRemy Bruno 		return 1;
34183cee5a60SRemy Bruno 	if (hdspm->control_register & HDSPM_QS_QuadWire)
34193cee5a60SRemy Bruno 		return 2;
34203cee5a60SRemy Bruno 	return 0;
34213cee5a60SRemy Bruno }
34223cee5a60SRemy Bruno 
34233cee5a60SRemy Bruno static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
34243cee5a60SRemy Bruno {
34253cee5a60SRemy Bruno 	hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
34263cee5a60SRemy Bruno 	switch (mode) {
34273cee5a60SRemy Bruno 	case 0:
34283cee5a60SRemy Bruno 		break;
34293cee5a60SRemy Bruno 	case 1:
34303cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_QS_DoubleWire;
34313cee5a60SRemy Bruno 		break;
34323cee5a60SRemy Bruno 	case 2:
34333cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_QS_QuadWire;
34343cee5a60SRemy Bruno 		break;
34353cee5a60SRemy Bruno 	}
34363cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
34373cee5a60SRemy Bruno 
34383cee5a60SRemy Bruno 	return 0;
34393cee5a60SRemy Bruno }
34403cee5a60SRemy Bruno 
34413cee5a60SRemy Bruno static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
34423cee5a60SRemy Bruno 				       struct snd_ctl_elem_info *uinfo)
34433cee5a60SRemy Bruno {
344438816545SAdrian Knoth 	static const char *const texts[] = { "Single", "Double", "Quad" };
3445e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
34463cee5a60SRemy Bruno 	return 0;
34473cee5a60SRemy Bruno }
34483cee5a60SRemy Bruno 
34493cee5a60SRemy Bruno static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
34503cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
34513cee5a60SRemy Bruno {
34523cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
34533cee5a60SRemy Bruno 
34543cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
34553cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
34563cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
34573cee5a60SRemy Bruno 	return 0;
34583cee5a60SRemy Bruno }
34593cee5a60SRemy Bruno 
34603cee5a60SRemy Bruno static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
34613cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
34623cee5a60SRemy Bruno {
34633cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
34643cee5a60SRemy Bruno 	int change;
34653cee5a60SRemy Bruno 	int val;
34663cee5a60SRemy Bruno 
34673cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
34683cee5a60SRemy Bruno 		return -EBUSY;
34693cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0];
34703cee5a60SRemy Bruno 	if (val < 0)
34713cee5a60SRemy Bruno 		val = 0;
34723cee5a60SRemy Bruno 	if (val > 2)
34733cee5a60SRemy Bruno 		val = 2;
34743cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
3475ef5fa1a4STakashi Iwai 	change = val != hdspm_qs_wire(hdspm);
34763cee5a60SRemy Bruno 	hdspm_set_qs_wire(hdspm, val);
34773cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
34783cee5a60SRemy Bruno 	return change;
34793cee5a60SRemy Bruno }
34803cee5a60SRemy Bruno 
3481acf14767SAdrian Knoth #define HDSPM_CONTROL_TRISTATE(xname, xindex) \
3482acf14767SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3483acf14767SAdrian Knoth 	.name = xname, \
3484acf14767SAdrian Knoth 	.private_value = xindex, \
3485acf14767SAdrian Knoth 	.info = snd_hdspm_info_tristate, \
3486acf14767SAdrian Knoth 	.get = snd_hdspm_get_tristate, \
3487acf14767SAdrian Knoth 	.put = snd_hdspm_put_tristate \
3488acf14767SAdrian Knoth }
3489acf14767SAdrian Knoth 
3490acf14767SAdrian Knoth static int hdspm_tristate(struct hdspm *hdspm, u32 regmask)
3491acf14767SAdrian Knoth {
3492acf14767SAdrian Knoth 	u32 reg = hdspm->settings_register & (regmask * 3);
3493acf14767SAdrian Knoth 	return reg / regmask;
3494acf14767SAdrian Knoth }
3495acf14767SAdrian Knoth 
3496acf14767SAdrian Knoth static int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask)
3497acf14767SAdrian Knoth {
3498acf14767SAdrian Knoth 	hdspm->settings_register &= ~(regmask * 3);
3499acf14767SAdrian Knoth 	hdspm->settings_register |= (regmask * mode);
3500acf14767SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
3501acf14767SAdrian Knoth 
3502acf14767SAdrian Knoth 	return 0;
3503acf14767SAdrian Knoth }
3504acf14767SAdrian Knoth 
3505acf14767SAdrian Knoth static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol,
3506acf14767SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3507acf14767SAdrian Knoth {
3508acf14767SAdrian Knoth 	u32 regmask = kcontrol->private_value;
3509acf14767SAdrian Knoth 
351038816545SAdrian Knoth 	static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" };
351138816545SAdrian Knoth 	static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" };
3512acf14767SAdrian Knoth 
3513acf14767SAdrian Knoth 	switch (regmask) {
3514acf14767SAdrian Knoth 	case HDSPM_c0_Input0:
3515acf14767SAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts_spdif);
3516acf14767SAdrian Knoth 		break;
3517acf14767SAdrian Knoth 	default:
3518acf14767SAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts_levels);
3519acf14767SAdrian Knoth 		break;
3520acf14767SAdrian Knoth 	}
3521acf14767SAdrian Knoth 	return 0;
3522acf14767SAdrian Knoth }
3523acf14767SAdrian Knoth 
3524acf14767SAdrian Knoth static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol,
3525acf14767SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3526acf14767SAdrian Knoth {
3527acf14767SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3528acf14767SAdrian Knoth 	u32 regmask = kcontrol->private_value;
3529acf14767SAdrian Knoth 
3530acf14767SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3531acf14767SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask);
3532acf14767SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3533acf14767SAdrian Knoth 	return 0;
3534acf14767SAdrian Knoth }
3535acf14767SAdrian Knoth 
3536acf14767SAdrian Knoth static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol,
3537acf14767SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3538acf14767SAdrian Knoth {
3539acf14767SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3540acf14767SAdrian Knoth 	u32 regmask = kcontrol->private_value;
3541acf14767SAdrian Knoth 	int change;
3542acf14767SAdrian Knoth 	int val;
3543acf14767SAdrian Knoth 
3544acf14767SAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
3545acf14767SAdrian Knoth 		return -EBUSY;
3546acf14767SAdrian Knoth 	val = ucontrol->value.integer.value[0];
3547acf14767SAdrian Knoth 	if (val < 0)
3548acf14767SAdrian Knoth 		val = 0;
3549acf14767SAdrian Knoth 	if (val > 2)
3550acf14767SAdrian Knoth 		val = 2;
3551acf14767SAdrian Knoth 
3552acf14767SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3553acf14767SAdrian Knoth 	change = val != hdspm_tristate(hdspm, regmask);
3554acf14767SAdrian Knoth 	hdspm_set_tristate(hdspm, val, regmask);
3555acf14767SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3556acf14767SAdrian Knoth 	return change;
3557acf14767SAdrian Knoth }
3558acf14767SAdrian Knoth 
3559700d1ef3SAdrian Knoth #define HDSPM_MADI_SPEEDMODE(xname, xindex) \
3560700d1ef3SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3561700d1ef3SAdrian Knoth 	.name = xname, \
3562700d1ef3SAdrian Knoth 	.index = xindex, \
3563700d1ef3SAdrian Knoth 	.info = snd_hdspm_info_madi_speedmode, \
3564700d1ef3SAdrian Knoth 	.get = snd_hdspm_get_madi_speedmode, \
3565700d1ef3SAdrian Knoth 	.put = snd_hdspm_put_madi_speedmode \
3566700d1ef3SAdrian Knoth }
3567700d1ef3SAdrian Knoth 
3568700d1ef3SAdrian Knoth static int hdspm_madi_speedmode(struct hdspm *hdspm)
3569700d1ef3SAdrian Knoth {
3570700d1ef3SAdrian Knoth 	if (hdspm->control_register & HDSPM_QuadSpeed)
3571700d1ef3SAdrian Knoth 		return 2;
3572700d1ef3SAdrian Knoth 	if (hdspm->control_register & HDSPM_DoubleSpeed)
3573700d1ef3SAdrian Knoth 		return 1;
3574700d1ef3SAdrian Knoth 	return 0;
3575700d1ef3SAdrian Knoth }
3576700d1ef3SAdrian Knoth 
3577700d1ef3SAdrian Knoth static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
3578700d1ef3SAdrian Knoth {
3579700d1ef3SAdrian Knoth 	hdspm->control_register &= ~(HDSPM_DoubleSpeed | HDSPM_QuadSpeed);
3580700d1ef3SAdrian Knoth 	switch (mode) {
3581700d1ef3SAdrian Knoth 	case 0:
3582700d1ef3SAdrian Knoth 		break;
3583700d1ef3SAdrian Knoth 	case 1:
3584700d1ef3SAdrian Knoth 		hdspm->control_register |= HDSPM_DoubleSpeed;
3585700d1ef3SAdrian Knoth 		break;
3586700d1ef3SAdrian Knoth 	case 2:
3587700d1ef3SAdrian Knoth 		hdspm->control_register |= HDSPM_QuadSpeed;
3588700d1ef3SAdrian Knoth 		break;
3589700d1ef3SAdrian Knoth 	}
3590700d1ef3SAdrian Knoth 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
3591700d1ef3SAdrian Knoth 
3592700d1ef3SAdrian Knoth 	return 0;
3593700d1ef3SAdrian Knoth }
3594700d1ef3SAdrian Knoth 
3595700d1ef3SAdrian Knoth static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
3596700d1ef3SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3597700d1ef3SAdrian Knoth {
359838816545SAdrian Knoth 	static const char *const texts[] = { "Single", "Double", "Quad" };
3599e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3600700d1ef3SAdrian Knoth 	return 0;
3601700d1ef3SAdrian Knoth }
3602700d1ef3SAdrian Knoth 
3603700d1ef3SAdrian Knoth static int snd_hdspm_get_madi_speedmode(struct snd_kcontrol *kcontrol,
3604700d1ef3SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3605700d1ef3SAdrian Knoth {
3606700d1ef3SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3607700d1ef3SAdrian Knoth 
3608700d1ef3SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3609700d1ef3SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_madi_speedmode(hdspm);
3610700d1ef3SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3611700d1ef3SAdrian Knoth 	return 0;
3612700d1ef3SAdrian Knoth }
3613700d1ef3SAdrian Knoth 
3614700d1ef3SAdrian Knoth static int snd_hdspm_put_madi_speedmode(struct snd_kcontrol *kcontrol,
3615700d1ef3SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3616700d1ef3SAdrian Knoth {
3617700d1ef3SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3618700d1ef3SAdrian Knoth 	int change;
3619700d1ef3SAdrian Knoth 	int val;
3620700d1ef3SAdrian Knoth 
3621700d1ef3SAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
3622700d1ef3SAdrian Knoth 		return -EBUSY;
3623700d1ef3SAdrian Knoth 	val = ucontrol->value.integer.value[0];
3624700d1ef3SAdrian Knoth 	if (val < 0)
3625700d1ef3SAdrian Knoth 		val = 0;
3626700d1ef3SAdrian Knoth 	if (val > 2)
3627700d1ef3SAdrian Knoth 		val = 2;
3628700d1ef3SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3629700d1ef3SAdrian Knoth 	change = val != hdspm_madi_speedmode(hdspm);
3630700d1ef3SAdrian Knoth 	hdspm_set_madi_speedmode(hdspm, val);
3631700d1ef3SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3632700d1ef3SAdrian Knoth 	return change;
3633700d1ef3SAdrian Knoth }
3634763f356cSTakashi Iwai 
3635763f356cSTakashi Iwai #define HDSPM_MIXER(xname, xindex) \
3636763f356cSTakashi Iwai {	.iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
3637763f356cSTakashi Iwai 	.name = xname, \
3638763f356cSTakashi Iwai 	.index = xindex, \
363967ed4161SClemens Ladisch 	.device = 0, \
3640763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
3641763f356cSTakashi Iwai 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3642763f356cSTakashi Iwai 	.info = snd_hdspm_info_mixer, \
3643763f356cSTakashi Iwai 	.get = snd_hdspm_get_mixer, \
3644763f356cSTakashi Iwai 	.put = snd_hdspm_put_mixer \
3645763f356cSTakashi Iwai }
3646763f356cSTakashi Iwai 
364798274f07STakashi Iwai static int snd_hdspm_info_mixer(struct snd_kcontrol *kcontrol,
364898274f07STakashi Iwai 				struct snd_ctl_elem_info *uinfo)
3649763f356cSTakashi Iwai {
3650763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3651763f356cSTakashi Iwai 	uinfo->count = 3;
3652763f356cSTakashi Iwai 	uinfo->value.integer.min = 0;
3653763f356cSTakashi Iwai 	uinfo->value.integer.max = 65535;
3654763f356cSTakashi Iwai 	uinfo->value.integer.step = 1;
3655763f356cSTakashi Iwai 	return 0;
3656763f356cSTakashi Iwai }
3657763f356cSTakashi Iwai 
365898274f07STakashi Iwai static int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
365998274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
3660763f356cSTakashi Iwai {
366198274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3662763f356cSTakashi Iwai 	int source;
3663763f356cSTakashi Iwai 	int destination;
3664763f356cSTakashi Iwai 
3665763f356cSTakashi Iwai 	source = ucontrol->value.integer.value[0];
3666763f356cSTakashi Iwai 	if (source < 0)
3667763f356cSTakashi Iwai 		source = 0;
3668763f356cSTakashi Iwai 	else if (source >= 2 * HDSPM_MAX_CHANNELS)
3669763f356cSTakashi Iwai 		source = 2 * HDSPM_MAX_CHANNELS - 1;
3670763f356cSTakashi Iwai 
3671763f356cSTakashi Iwai 	destination = ucontrol->value.integer.value[1];
3672763f356cSTakashi Iwai 	if (destination < 0)
3673763f356cSTakashi Iwai 		destination = 0;
3674763f356cSTakashi Iwai 	else if (destination >= HDSPM_MAX_CHANNELS)
3675763f356cSTakashi Iwai 		destination = HDSPM_MAX_CHANNELS - 1;
3676763f356cSTakashi Iwai 
3677763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3678763f356cSTakashi Iwai 	if (source >= HDSPM_MAX_CHANNELS)
3679763f356cSTakashi Iwai 		ucontrol->value.integer.value[2] =
3680763f356cSTakashi Iwai 		    hdspm_read_pb_gain(hdspm, destination,
3681763f356cSTakashi Iwai 				       source - HDSPM_MAX_CHANNELS);
3682763f356cSTakashi Iwai 	else
3683763f356cSTakashi Iwai 		ucontrol->value.integer.value[2] =
3684763f356cSTakashi Iwai 		    hdspm_read_in_gain(hdspm, destination, source);
3685763f356cSTakashi Iwai 
3686763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3687763f356cSTakashi Iwai 
3688763f356cSTakashi Iwai 	return 0;
3689763f356cSTakashi Iwai }
3690763f356cSTakashi Iwai 
369198274f07STakashi Iwai static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
369298274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
3693763f356cSTakashi Iwai {
369498274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3695763f356cSTakashi Iwai 	int change;
3696763f356cSTakashi Iwai 	int source;
3697763f356cSTakashi Iwai 	int destination;
3698763f356cSTakashi Iwai 	int gain;
3699763f356cSTakashi Iwai 
3700763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3701763f356cSTakashi Iwai 		return -EBUSY;
3702763f356cSTakashi Iwai 
3703763f356cSTakashi Iwai 	source = ucontrol->value.integer.value[0];
3704763f356cSTakashi Iwai 	destination = ucontrol->value.integer.value[1];
3705763f356cSTakashi Iwai 
3706763f356cSTakashi Iwai 	if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
3707763f356cSTakashi Iwai 		return -1;
3708763f356cSTakashi Iwai 	if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
3709763f356cSTakashi Iwai 		return -1;
3710763f356cSTakashi Iwai 
3711763f356cSTakashi Iwai 	gain = ucontrol->value.integer.value[2];
3712763f356cSTakashi Iwai 
3713763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3714763f356cSTakashi Iwai 
3715763f356cSTakashi Iwai 	if (source >= HDSPM_MAX_CHANNELS)
3716763f356cSTakashi Iwai 		change = gain != hdspm_read_pb_gain(hdspm, destination,
3717763f356cSTakashi Iwai 						    source -
3718763f356cSTakashi Iwai 						    HDSPM_MAX_CHANNELS);
3719763f356cSTakashi Iwai 	else
3720ef5fa1a4STakashi Iwai 		change = gain != hdspm_read_in_gain(hdspm, destination,
3721ef5fa1a4STakashi Iwai 						    source);
3722763f356cSTakashi Iwai 
3723763f356cSTakashi Iwai 	if (change) {
3724763f356cSTakashi Iwai 		if (source >= HDSPM_MAX_CHANNELS)
3725763f356cSTakashi Iwai 			hdspm_write_pb_gain(hdspm, destination,
3726763f356cSTakashi Iwai 					    source - HDSPM_MAX_CHANNELS,
3727763f356cSTakashi Iwai 					    gain);
3728763f356cSTakashi Iwai 		else
3729763f356cSTakashi Iwai 			hdspm_write_in_gain(hdspm, destination, source,
3730763f356cSTakashi Iwai 					    gain);
3731763f356cSTakashi Iwai 	}
3732763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3733763f356cSTakashi Iwai 
3734763f356cSTakashi Iwai 	return change;
3735763f356cSTakashi Iwai }
3736763f356cSTakashi Iwai 
3737763f356cSTakashi Iwai /* The simple mixer control(s) provide gain control for the
3738763f356cSTakashi Iwai    basic 1:1 mappings of playback streams to output
3739763f356cSTakashi Iwai    streams.
3740763f356cSTakashi Iwai */
3741763f356cSTakashi Iwai 
3742763f356cSTakashi Iwai #define HDSPM_PLAYBACK_MIXER \
3743763f356cSTakashi Iwai {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3744763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
3745763f356cSTakashi Iwai 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3746763f356cSTakashi Iwai 	.info = snd_hdspm_info_playback_mixer, \
3747763f356cSTakashi Iwai 	.get = snd_hdspm_get_playback_mixer, \
3748763f356cSTakashi Iwai 	.put = snd_hdspm_put_playback_mixer \
3749763f356cSTakashi Iwai }
3750763f356cSTakashi Iwai 
375198274f07STakashi Iwai static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
375298274f07STakashi Iwai 					 struct snd_ctl_elem_info *uinfo)
3753763f356cSTakashi Iwai {
3754763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3755763f356cSTakashi Iwai 	uinfo->count = 1;
3756763f356cSTakashi Iwai 	uinfo->value.integer.min = 0;
37570dca1793SAdrian Knoth 	uinfo->value.integer.max = 64;
3758763f356cSTakashi Iwai 	uinfo->value.integer.step = 1;
3759763f356cSTakashi Iwai 	return 0;
3760763f356cSTakashi Iwai }
3761763f356cSTakashi Iwai 
376298274f07STakashi Iwai static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
376398274f07STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
3764763f356cSTakashi Iwai {
376598274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3766763f356cSTakashi Iwai 	int channel;
3767763f356cSTakashi Iwai 
3768763f356cSTakashi Iwai 	channel = ucontrol->id.index - 1;
3769763f356cSTakashi Iwai 
3770da3cec35STakashi Iwai 	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
3771da3cec35STakashi Iwai 		return -EINVAL;
3772763f356cSTakashi Iwai 
3773763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3774763f356cSTakashi Iwai 	ucontrol->value.integer.value[0] =
37750dca1793SAdrian Knoth 	  (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
3776763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3777763f356cSTakashi Iwai 
3778763f356cSTakashi Iwai 	return 0;
3779763f356cSTakashi Iwai }
3780763f356cSTakashi Iwai 
378198274f07STakashi Iwai static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
378298274f07STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
3783763f356cSTakashi Iwai {
378498274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3785763f356cSTakashi Iwai 	int change;
3786763f356cSTakashi Iwai 	int channel;
3787763f356cSTakashi Iwai 	int gain;
3788763f356cSTakashi Iwai 
3789763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3790763f356cSTakashi Iwai 		return -EBUSY;
3791763f356cSTakashi Iwai 
3792763f356cSTakashi Iwai 	channel = ucontrol->id.index - 1;
3793763f356cSTakashi Iwai 
3794da3cec35STakashi Iwai 	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
3795da3cec35STakashi Iwai 		return -EINVAL;
3796763f356cSTakashi Iwai 
37970dca1793SAdrian Knoth 	gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
3798763f356cSTakashi Iwai 
3799763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3800763f356cSTakashi Iwai 	change =
38010dca1793SAdrian Knoth 	    gain != hdspm_read_pb_gain(hdspm, channel,
38020dca1793SAdrian Knoth 				       channel);
3803763f356cSTakashi Iwai 	if (change)
38040dca1793SAdrian Knoth 		hdspm_write_pb_gain(hdspm, channel, channel,
3805763f356cSTakashi Iwai 				    gain);
3806763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3807763f356cSTakashi Iwai 	return change;
3808763f356cSTakashi Iwai }
3809763f356cSTakashi Iwai 
38100dca1793SAdrian Knoth #define HDSPM_SYNC_CHECK(xname, xindex) \
381167ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3812763f356cSTakashi Iwai 	.name = xname, \
38130dca1793SAdrian Knoth 	.private_value = xindex, \
3814763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3815763f356cSTakashi Iwai 	.info = snd_hdspm_info_sync_check, \
38160dca1793SAdrian Knoth 	.get = snd_hdspm_get_sync_check \
3817763f356cSTakashi Iwai }
3818763f356cSTakashi Iwai 
381934542213SAdrian Knoth #define HDSPM_TCO_LOCK_CHECK(xname, xindex) \
382034542213SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
382134542213SAdrian Knoth 	.name = xname, \
382234542213SAdrian Knoth 	.private_value = xindex, \
382334542213SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
382434542213SAdrian Knoth 	.info = snd_hdspm_tco_info_lock_check, \
382534542213SAdrian Knoth 	.get = snd_hdspm_get_sync_check \
382634542213SAdrian Knoth }
382734542213SAdrian Knoth 
382834542213SAdrian Knoth 
38290dca1793SAdrian Knoth 
383098274f07STakashi Iwai static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
383198274f07STakashi Iwai 				     struct snd_ctl_elem_info *uinfo)
3832763f356cSTakashi Iwai {
383338816545SAdrian Knoth 	static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" };
3834e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3835763f356cSTakashi Iwai 	return 0;
3836763f356cSTakashi Iwai }
3837763f356cSTakashi Iwai 
383834542213SAdrian Knoth static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
383934542213SAdrian Knoth 				     struct snd_ctl_elem_info *uinfo)
384034542213SAdrian Knoth {
384138816545SAdrian Knoth 	static const char *const texts[] = { "No Lock", "Lock" };
384234542213SAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
384334542213SAdrian Knoth 	return 0;
384434542213SAdrian Knoth }
384534542213SAdrian Knoth 
384698274f07STakashi Iwai static int hdspm_wc_sync_check(struct hdspm *hdspm)
3847763f356cSTakashi Iwai {
38480dca1793SAdrian Knoth 	int status, status2;
38490dca1793SAdrian Knoth 
38500dca1793SAdrian Knoth 	switch (hdspm->io_type) {
38510dca1793SAdrian Knoth 	case AES32:
38520dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
385356bde0f3SAndre Schramm 		if (status & HDSPM_AES32_wcLock) {
385456bde0f3SAndre Schramm 			if (status & HDSPM_AES32_wcSync)
38553cee5a60SRemy Bruno 				return 2;
385656bde0f3SAndre Schramm 			else
38570dca1793SAdrian Knoth 				return 1;
385856bde0f3SAndre Schramm 		}
38593cee5a60SRemy Bruno 		return 0;
38600dca1793SAdrian Knoth 		break;
38610dca1793SAdrian Knoth 
38620dca1793SAdrian Knoth 	case MADI:
38630dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
3864763f356cSTakashi Iwai 		if (status2 & HDSPM_wcLock) {
3865763f356cSTakashi Iwai 			if (status2 & HDSPM_wcSync)
3866763f356cSTakashi Iwai 				return 2;
3867763f356cSTakashi Iwai 			else
3868763f356cSTakashi Iwai 				return 1;
3869763f356cSTakashi Iwai 		}
3870763f356cSTakashi Iwai 		return 0;
38710dca1793SAdrian Knoth 		break;
3872763f356cSTakashi Iwai 
38730dca1793SAdrian Knoth 	case RayDAT:
38740dca1793SAdrian Knoth 	case AIO:
38750dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
3876763f356cSTakashi Iwai 
38770dca1793SAdrian Knoth 		if (status & 0x2000000)
38780dca1793SAdrian Knoth 			return 2;
38790dca1793SAdrian Knoth 		else if (status & 0x1000000)
38800dca1793SAdrian Knoth 			return 1;
3881763f356cSTakashi Iwai 		return 0;
38820dca1793SAdrian Knoth 
38830dca1793SAdrian Knoth 		break;
38840dca1793SAdrian Knoth 
38850dca1793SAdrian Knoth 	case MADIface:
38860dca1793SAdrian Knoth 		break;
3887763f356cSTakashi Iwai 	}
3888763f356cSTakashi Iwai 
3889763f356cSTakashi Iwai 
38900dca1793SAdrian Knoth 	return 3;
3891763f356cSTakashi Iwai }
3892763f356cSTakashi Iwai 
38930dca1793SAdrian Knoth 
38940dca1793SAdrian Knoth static int hdspm_madi_sync_check(struct hdspm *hdspm)
3895763f356cSTakashi Iwai {
3896763f356cSTakashi Iwai 	int status = hdspm_read(hdspm, HDSPM_statusRegister);
3897763f356cSTakashi Iwai 	if (status & HDSPM_madiLock) {
3898763f356cSTakashi Iwai 		if (status & HDSPM_madiSync)
3899763f356cSTakashi Iwai 			return 2;
3900763f356cSTakashi Iwai 		else
3901763f356cSTakashi Iwai 			return 1;
3902763f356cSTakashi Iwai 	}
3903763f356cSTakashi Iwai 	return 0;
3904763f356cSTakashi Iwai }
3905763f356cSTakashi Iwai 
3906763f356cSTakashi Iwai 
39070dca1793SAdrian Knoth static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx)
39080dca1793SAdrian Knoth {
39090dca1793SAdrian Knoth 	int status, lock, sync;
39100dca1793SAdrian Knoth 
39110dca1793SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
39120dca1793SAdrian Knoth 
39130dca1793SAdrian Knoth 	lock = (status & (0x1<<idx)) ? 1 : 0;
39140dca1793SAdrian Knoth 	sync = (status & (0x100<<idx)) ? 1 : 0;
39150dca1793SAdrian Knoth 
39160dca1793SAdrian Knoth 	if (lock && sync)
39170dca1793SAdrian Knoth 		return 2;
39180dca1793SAdrian Knoth 	else if (lock)
39190dca1793SAdrian Knoth 		return 1;
3920763f356cSTakashi Iwai 	return 0;
3921763f356cSTakashi Iwai }
3922763f356cSTakashi Iwai 
3923763f356cSTakashi Iwai 
39240dca1793SAdrian Knoth static int hdspm_sync_in_sync_check(struct hdspm *hdspm)
39250dca1793SAdrian Knoth {
39260dca1793SAdrian Knoth 	int status, lock = 0, sync = 0;
39270dca1793SAdrian Knoth 
39280dca1793SAdrian Knoth 	switch (hdspm->io_type) {
39290dca1793SAdrian Knoth 	case RayDAT:
39300dca1793SAdrian Knoth 	case AIO:
39310dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_RD_STATUS_3);
39320dca1793SAdrian Knoth 		lock = (status & 0x400) ? 1 : 0;
39330dca1793SAdrian Knoth 		sync = (status & 0x800) ? 1 : 0;
39340dca1793SAdrian Knoth 		break;
39350dca1793SAdrian Knoth 
39360dca1793SAdrian Knoth 	case MADI:
39372e0452f5SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
39382e0452f5SAdrian Knoth 		lock = (status & HDSPM_syncInLock) ? 1 : 0;
39392e0452f5SAdrian Knoth 		sync = (status & HDSPM_syncInSync) ? 1 : 0;
39402e0452f5SAdrian Knoth 		break;
39412e0452f5SAdrian Knoth 
39420dca1793SAdrian Knoth 	case AES32:
39430dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister2);
39449a215f47SAdrian Knoth 		lock = (status & 0x100000) ? 1 : 0;
39459a215f47SAdrian Knoth 		sync = (status & 0x200000) ? 1 : 0;
39460dca1793SAdrian Knoth 		break;
39470dca1793SAdrian Knoth 
39480dca1793SAdrian Knoth 	case MADIface:
39490dca1793SAdrian Knoth 		break;
39500dca1793SAdrian Knoth 	}
39510dca1793SAdrian Knoth 
39520dca1793SAdrian Knoth 	if (lock && sync)
39530dca1793SAdrian Knoth 		return 2;
39540dca1793SAdrian Knoth 	else if (lock)
39550dca1793SAdrian Knoth 		return 1;
39560dca1793SAdrian Knoth 
39570dca1793SAdrian Knoth 	return 0;
39583cee5a60SRemy Bruno }
39593cee5a60SRemy Bruno 
39603cee5a60SRemy Bruno static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx)
39613cee5a60SRemy Bruno {
39620dca1793SAdrian Knoth 	int status2, lock, sync;
39630dca1793SAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
39640dca1793SAdrian Knoth 
39650dca1793SAdrian Knoth 	lock = (status2 & (0x0080 >> idx)) ? 1 : 0;
39660dca1793SAdrian Knoth 	sync = (status2 & (0x8000 >> idx)) ? 1 : 0;
39670dca1793SAdrian Knoth 
39680dca1793SAdrian Knoth 	if (sync)
39693cee5a60SRemy Bruno 		return 2;
39700dca1793SAdrian Knoth 	else if (lock)
39710dca1793SAdrian Knoth 		return 1;
39723cee5a60SRemy Bruno 	return 0;
39733cee5a60SRemy Bruno }
39743cee5a60SRemy Bruno 
397534542213SAdrian Knoth static int hdspm_tco_input_check(struct hdspm *hdspm, u32 mask)
397634542213SAdrian Knoth {
397734542213SAdrian Knoth 	u32 status;
397834542213SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
397934542213SAdrian Knoth 
398034542213SAdrian Knoth 	return (status & mask) ? 1 : 0;
398134542213SAdrian Knoth }
398234542213SAdrian Knoth 
39830dca1793SAdrian Knoth 
39840dca1793SAdrian Knoth static int hdspm_tco_sync_check(struct hdspm *hdspm)
39850dca1793SAdrian Knoth {
39860dca1793SAdrian Knoth 	int status;
39870dca1793SAdrian Knoth 
39880dca1793SAdrian Knoth 	if (hdspm->tco) {
39890dca1793SAdrian Knoth 		switch (hdspm->io_type) {
39900dca1793SAdrian Knoth 		case MADI:
3991b0bf5504SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_statusRegister);
3992b0bf5504SAdrian Knoth 			if (status & HDSPM_tcoLockMadi) {
3993b0bf5504SAdrian Knoth 				if (status & HDSPM_tcoSync)
3994b0bf5504SAdrian Knoth 					return 2;
3995b0bf5504SAdrian Knoth 				else
3996b0bf5504SAdrian Knoth 					return 1;
3997b0bf5504SAdrian Knoth 			}
3998b0bf5504SAdrian Knoth 			return 0;
3999b0bf5504SAdrian Knoth 			break;
40000dca1793SAdrian Knoth 		case AES32:
40010dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_statusRegister);
4002b0bf5504SAdrian Knoth 			if (status & HDSPM_tcoLockAes) {
40030dca1793SAdrian Knoth 				if (status & HDSPM_tcoSync)
40040dca1793SAdrian Knoth 					return 2;
40050dca1793SAdrian Knoth 				else
40060dca1793SAdrian Knoth 					return 1;
40070dca1793SAdrian Knoth 			}
40080dca1793SAdrian Knoth 			return 0;
40090dca1793SAdrian Knoth 
40100dca1793SAdrian Knoth 			break;
40110dca1793SAdrian Knoth 
40120dca1793SAdrian Knoth 		case RayDAT:
40130dca1793SAdrian Knoth 		case AIO:
40140dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
40150dca1793SAdrian Knoth 
40160dca1793SAdrian Knoth 			if (status & 0x8000000)
40170dca1793SAdrian Knoth 				return 2; /* Sync */
40180dca1793SAdrian Knoth 			if (status & 0x4000000)
40190dca1793SAdrian Knoth 				return 1; /* Lock */
40200dca1793SAdrian Knoth 			return 0; /* No signal */
40210dca1793SAdrian Knoth 			break;
40220dca1793SAdrian Knoth 
40230dca1793SAdrian Knoth 		default:
40240dca1793SAdrian Knoth 			break;
40250dca1793SAdrian Knoth 		}
40260dca1793SAdrian Knoth 	}
40270dca1793SAdrian Knoth 
40280dca1793SAdrian Knoth 	return 3; /* N/A */
40290dca1793SAdrian Knoth }
40300dca1793SAdrian Knoth 
40310dca1793SAdrian Knoth 
40320dca1793SAdrian Knoth static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
40333cee5a60SRemy Bruno 				    struct snd_ctl_elem_value *ucontrol)
40343cee5a60SRemy Bruno {
40353cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
40360dca1793SAdrian Knoth 	int val = -1;
40373cee5a60SRemy Bruno 
40380dca1793SAdrian Knoth 	switch (hdspm->io_type) {
40390dca1793SAdrian Knoth 	case RayDAT:
40400dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40410dca1793SAdrian Knoth 		case 0: /* WC */
40420dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40430dca1793SAdrian Knoth 		case 7: /* TCO */
40440dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40450dca1793SAdrian Knoth 		case 8: /* SYNC IN */
40460dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40470dca1793SAdrian Knoth 		default:
4048d1a3c98dSAdrian Knoth 			val = hdspm_s1_sync_check(hdspm,
4049d1a3c98dSAdrian Knoth 					kcontrol->private_value-1);
40500dca1793SAdrian Knoth 		}
4051fba30fd3SAdrian Knoth 		break;
40523cee5a60SRemy Bruno 
40530dca1793SAdrian Knoth 	case AIO:
40540dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40550dca1793SAdrian Knoth 		case 0: /* WC */
40560dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40570dca1793SAdrian Knoth 		case 4: /* TCO */
40580dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40590dca1793SAdrian Knoth 		case 5: /* SYNC IN */
40600dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40610dca1793SAdrian Knoth 		default:
40621cb7dbf4SAdrian Knoth 			val = hdspm_s1_sync_check(hdspm,
40631cb7dbf4SAdrian Knoth 					kcontrol->private_value-1);
40640dca1793SAdrian Knoth 		}
4065fba30fd3SAdrian Knoth 		break;
40660dca1793SAdrian Knoth 
40670dca1793SAdrian Knoth 	case MADI:
40680dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40690dca1793SAdrian Knoth 		case 0: /* WC */
40700dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40710dca1793SAdrian Knoth 		case 1: /* MADI */
40720dca1793SAdrian Knoth 			val = hdspm_madi_sync_check(hdspm); break;
40730dca1793SAdrian Knoth 		case 2: /* TCO */
40740dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40750dca1793SAdrian Knoth 		case 3: /* SYNC_IN */
40760dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40770dca1793SAdrian Knoth 		}
4078fba30fd3SAdrian Knoth 		break;
40790dca1793SAdrian Knoth 
40800dca1793SAdrian Knoth 	case MADIface:
40810dca1793SAdrian Knoth 		val = hdspm_madi_sync_check(hdspm); /* MADI */
40820dca1793SAdrian Knoth 		break;
40830dca1793SAdrian Knoth 
40840dca1793SAdrian Knoth 	case AES32:
40850dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40860dca1793SAdrian Knoth 		case 0: /* WC */
40870dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40880dca1793SAdrian Knoth 		case 9: /* TCO */
40890dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40900dca1793SAdrian Knoth 		case 10 /* SYNC IN */:
40910dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40927c4a95b5SAdrian Knoth 		default: /* AES1 to AES8 */
40930dca1793SAdrian Knoth 			 val = hdspm_aes_sync_check(hdspm,
40947c4a95b5SAdrian Knoth 					 kcontrol->private_value-1);
40950dca1793SAdrian Knoth 		}
4096fba30fd3SAdrian Knoth 		break;
40970dca1793SAdrian Knoth 
40980dca1793SAdrian Knoth 	}
40990dca1793SAdrian Knoth 
410034542213SAdrian Knoth 	if (hdspm->tco) {
410134542213SAdrian Knoth 		switch (kcontrol->private_value) {
410234542213SAdrian Knoth 		case 11:
410334542213SAdrian Knoth 			/* Check TCO for lock state of its current input */
410434542213SAdrian Knoth 			val = hdspm_tco_input_check(hdspm, HDSPM_TCO1_TCO_lock);
410534542213SAdrian Knoth 			break;
410634542213SAdrian Knoth 		case 12:
410734542213SAdrian Knoth 			/* Check TCO for valid time code on LTC input. */
410834542213SAdrian Knoth 			val = hdspm_tco_input_check(hdspm,
410934542213SAdrian Knoth 				HDSPM_TCO1_LTC_Input_valid);
411034542213SAdrian Knoth 			break;
411134542213SAdrian Knoth 		default:
411234542213SAdrian Knoth 			break;
411334542213SAdrian Knoth 		}
411434542213SAdrian Knoth 	}
411534542213SAdrian Knoth 
41160dca1793SAdrian Knoth 	if (-1 == val)
41170dca1793SAdrian Knoth 		val = 3;
41180dca1793SAdrian Knoth 
41190dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = val;
41203cee5a60SRemy Bruno 	return 0;
41213cee5a60SRemy Bruno }
4122763f356cSTakashi Iwai 
4123763f356cSTakashi Iwai 
41240dca1793SAdrian Knoth 
41250dca1793SAdrian Knoth /**
41260dca1793SAdrian Knoth  * TCO controls
41270dca1793SAdrian Knoth  **/
41280dca1793SAdrian Knoth static void hdspm_tco_write(struct hdspm *hdspm)
41290dca1793SAdrian Knoth {
41300dca1793SAdrian Knoth 	unsigned int tc[4] = { 0, 0, 0, 0};
41310dca1793SAdrian Knoth 
41320dca1793SAdrian Knoth 	switch (hdspm->tco->input) {
41330dca1793SAdrian Knoth 	case 0:
41340dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_input_MSB;
41350dca1793SAdrian Knoth 		break;
41360dca1793SAdrian Knoth 	case 1:
41370dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_input_LSB;
41380dca1793SAdrian Knoth 		break;
41390dca1793SAdrian Knoth 	default:
41400dca1793SAdrian Knoth 		break;
41410dca1793SAdrian Knoth 	}
41420dca1793SAdrian Knoth 
41430dca1793SAdrian Knoth 	switch (hdspm->tco->framerate) {
41440dca1793SAdrian Knoth 	case 1:
41450dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB;
41460dca1793SAdrian Knoth 		break;
41470dca1793SAdrian Knoth 	case 2:
41480dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_MSB;
41490dca1793SAdrian Knoth 		break;
41500dca1793SAdrian Knoth 	case 3:
41510dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_MSB +
41520dca1793SAdrian Knoth 			HDSPM_TCO1_set_drop_frame_flag;
41530dca1793SAdrian Knoth 		break;
41540dca1793SAdrian Knoth 	case 4:
41550dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
41560dca1793SAdrian Knoth 			HDSPM_TCO1_LTC_Format_MSB;
41570dca1793SAdrian Knoth 		break;
41580dca1793SAdrian Knoth 	case 5:
41590dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
41600dca1793SAdrian Knoth 			HDSPM_TCO1_LTC_Format_MSB +
41610dca1793SAdrian Knoth 			HDSPM_TCO1_set_drop_frame_flag;
41620dca1793SAdrian Knoth 		break;
41630dca1793SAdrian Knoth 	default:
41640dca1793SAdrian Knoth 		break;
41650dca1793SAdrian Knoth 	}
41660dca1793SAdrian Knoth 
41670dca1793SAdrian Knoth 	switch (hdspm->tco->wordclock) {
41680dca1793SAdrian Knoth 	case 1:
41690dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB;
41700dca1793SAdrian Knoth 		break;
41710dca1793SAdrian Knoth 	case 2:
41720dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB;
41730dca1793SAdrian Knoth 		break;
41740dca1793SAdrian Knoth 	default:
41750dca1793SAdrian Knoth 		break;
41760dca1793SAdrian Knoth 	}
41770dca1793SAdrian Knoth 
41780dca1793SAdrian Knoth 	switch (hdspm->tco->samplerate) {
41790dca1793SAdrian Knoth 	case 1:
41800dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_freq;
41810dca1793SAdrian Knoth 		break;
41820dca1793SAdrian Knoth 	case 2:
41830dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_freq_from_app;
41840dca1793SAdrian Knoth 		break;
41850dca1793SAdrian Knoth 	default:
41860dca1793SAdrian Knoth 		break;
41870dca1793SAdrian Knoth 	}
41880dca1793SAdrian Knoth 
41890dca1793SAdrian Knoth 	switch (hdspm->tco->pull) {
41900dca1793SAdrian Knoth 	case 1:
41910dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_up;
41920dca1793SAdrian Knoth 		break;
41930dca1793SAdrian Knoth 	case 2:
41940dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_down;
41950dca1793SAdrian Knoth 		break;
41960dca1793SAdrian Knoth 	case 3:
41970dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4;
41980dca1793SAdrian Knoth 		break;
41990dca1793SAdrian Knoth 	case 4:
42000dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4;
42010dca1793SAdrian Knoth 		break;
42020dca1793SAdrian Knoth 	default:
42030dca1793SAdrian Knoth 		break;
42040dca1793SAdrian Knoth 	}
42050dca1793SAdrian Knoth 
42060dca1793SAdrian Knoth 	if (1 == hdspm->tco->term) {
42070dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_term_75R;
42080dca1793SAdrian Knoth 	}
42090dca1793SAdrian Knoth 
42100dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]);
42110dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]);
42120dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]);
42130dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]);
42140dca1793SAdrian Knoth }
42150dca1793SAdrian Knoth 
42160dca1793SAdrian Knoth 
42170dca1793SAdrian Knoth #define HDSPM_TCO_SAMPLE_RATE(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_sample_rate, \
42240dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_sample_rate, \
42250dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_sample_rate \
42260dca1793SAdrian Knoth }
42270dca1793SAdrian Knoth 
42280dca1793SAdrian Knoth static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
42290dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
42300dca1793SAdrian Knoth {
4231*69358fcaSMartin Dausel 	/* TODO freq from app could be supported here, see tco->samplerate */
423238816545SAdrian Knoth 	static const char *const texts[] = { "44.1 kHz", "48 kHz" };
4233e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
42340dca1793SAdrian Knoth 	return 0;
42350dca1793SAdrian Knoth }
42360dca1793SAdrian Knoth 
42370dca1793SAdrian Knoth static int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol,
42380dca1793SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
42390dca1793SAdrian Knoth {
42400dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42410dca1793SAdrian Knoth 
42420dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate;
42430dca1793SAdrian Knoth 
42440dca1793SAdrian Knoth 	return 0;
42450dca1793SAdrian Knoth }
42460dca1793SAdrian Knoth 
42470dca1793SAdrian Knoth static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
42480dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
42490dca1793SAdrian Knoth {
42500dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42510dca1793SAdrian Knoth 
42520dca1793SAdrian Knoth 	if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) {
42530dca1793SAdrian Knoth 		hdspm->tco->samplerate = ucontrol->value.enumerated.item[0];
42540dca1793SAdrian Knoth 
42550dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
42560dca1793SAdrian Knoth 
42570dca1793SAdrian Knoth 		return 1;
42580dca1793SAdrian Knoth 	}
42590dca1793SAdrian Knoth 
42600dca1793SAdrian Knoth 	return 0;
42610dca1793SAdrian Knoth }
42620dca1793SAdrian Knoth 
42630dca1793SAdrian Knoth 
42640dca1793SAdrian Knoth #define HDSPM_TCO_PULL(xname, xindex) \
42650dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
42660dca1793SAdrian Knoth 	.name = xname, \
42670dca1793SAdrian Knoth 	.index = xindex, \
42680dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
42690dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
42700dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_pull, \
42710dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_pull, \
42720dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_pull \
42730dca1793SAdrian Knoth }
42740dca1793SAdrian Knoth 
42750dca1793SAdrian Knoth static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
42760dca1793SAdrian Knoth 				   struct snd_ctl_elem_info *uinfo)
42770dca1793SAdrian Knoth {
427838816545SAdrian Knoth 	static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %",
427938816545SAdrian Knoth 		"+ 4 %", "- 4 %" };
4280e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
42810dca1793SAdrian Knoth 	return 0;
42820dca1793SAdrian Knoth }
42830dca1793SAdrian Knoth 
42840dca1793SAdrian Knoth static int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol,
42850dca1793SAdrian Knoth 				  struct snd_ctl_elem_value *ucontrol)
42860dca1793SAdrian Knoth {
42870dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42880dca1793SAdrian Knoth 
42890dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->pull;
42900dca1793SAdrian Knoth 
42910dca1793SAdrian Knoth 	return 0;
42920dca1793SAdrian Knoth }
42930dca1793SAdrian Knoth 
42940dca1793SAdrian Knoth static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
42950dca1793SAdrian Knoth 				  struct snd_ctl_elem_value *ucontrol)
42960dca1793SAdrian Knoth {
42970dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42980dca1793SAdrian Knoth 
42990dca1793SAdrian Knoth 	if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) {
43000dca1793SAdrian Knoth 		hdspm->tco->pull = ucontrol->value.enumerated.item[0];
43010dca1793SAdrian Knoth 
43020dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
43030dca1793SAdrian Knoth 
43040dca1793SAdrian Knoth 		return 1;
43050dca1793SAdrian Knoth 	}
43060dca1793SAdrian Knoth 
43070dca1793SAdrian Knoth 	return 0;
43080dca1793SAdrian Knoth }
43090dca1793SAdrian Knoth 
43100dca1793SAdrian Knoth #define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \
43110dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
43120dca1793SAdrian Knoth 	.name = xname, \
43130dca1793SAdrian Knoth 	.index = xindex, \
43140dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
43150dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
43160dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_wck_conversion, \
43170dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_wck_conversion, \
43180dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_wck_conversion \
43190dca1793SAdrian Knoth }
43200dca1793SAdrian Knoth 
43210dca1793SAdrian Knoth static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
43220dca1793SAdrian Knoth 					     struct snd_ctl_elem_info *uinfo)
43230dca1793SAdrian Knoth {
432438816545SAdrian Knoth 	static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
4325e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
43260dca1793SAdrian Knoth 	return 0;
43270dca1793SAdrian Knoth }
43280dca1793SAdrian Knoth 
43290dca1793SAdrian Knoth static int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol,
43300dca1793SAdrian Knoth 					    struct snd_ctl_elem_value *ucontrol)
43310dca1793SAdrian Knoth {
43320dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43330dca1793SAdrian Knoth 
43340dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock;
43350dca1793SAdrian Knoth 
43360dca1793SAdrian Knoth 	return 0;
43370dca1793SAdrian Knoth }
43380dca1793SAdrian Knoth 
43390dca1793SAdrian Knoth static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
43400dca1793SAdrian Knoth 					    struct snd_ctl_elem_value *ucontrol)
43410dca1793SAdrian Knoth {
43420dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43430dca1793SAdrian Knoth 
43440dca1793SAdrian Knoth 	if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) {
43450dca1793SAdrian Knoth 		hdspm->tco->wordclock = ucontrol->value.enumerated.item[0];
43460dca1793SAdrian Knoth 
43470dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
43480dca1793SAdrian Knoth 
43490dca1793SAdrian Knoth 		return 1;
43500dca1793SAdrian Knoth 	}
43510dca1793SAdrian Knoth 
43520dca1793SAdrian Knoth 	return 0;
43530dca1793SAdrian Knoth }
43540dca1793SAdrian Knoth 
43550dca1793SAdrian Knoth 
43560dca1793SAdrian Knoth #define HDSPM_TCO_FRAME_RATE(xname, xindex) \
43570dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
43580dca1793SAdrian Knoth 	.name = xname, \
43590dca1793SAdrian Knoth 	.index = xindex, \
43600dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
43610dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
43620dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_frame_rate, \
43630dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_frame_rate, \
43640dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_frame_rate \
43650dca1793SAdrian Knoth }
43660dca1793SAdrian Knoth 
43670dca1793SAdrian Knoth static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
43680dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
43690dca1793SAdrian Knoth {
437038816545SAdrian Knoth 	static const char *const texts[] = { "24 fps", "25 fps", "29.97fps",
43710dca1793SAdrian Knoth 		"29.97 dfps", "30 fps", "30 dfps" };
4372e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
43730dca1793SAdrian Knoth 	return 0;
43740dca1793SAdrian Knoth }
43750dca1793SAdrian Knoth 
43760dca1793SAdrian Knoth static int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol,
43770dca1793SAdrian Knoth 					struct snd_ctl_elem_value *ucontrol)
43780dca1793SAdrian Knoth {
43790dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43800dca1793SAdrian Knoth 
43810dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->framerate;
43820dca1793SAdrian Knoth 
43830dca1793SAdrian Knoth 	return 0;
43840dca1793SAdrian Knoth }
43850dca1793SAdrian Knoth 
43860dca1793SAdrian Knoth static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
43870dca1793SAdrian Knoth 					struct snd_ctl_elem_value *ucontrol)
43880dca1793SAdrian Knoth {
43890dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43900dca1793SAdrian Knoth 
43910dca1793SAdrian Knoth 	if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) {
43920dca1793SAdrian Knoth 		hdspm->tco->framerate = ucontrol->value.enumerated.item[0];
43930dca1793SAdrian Knoth 
43940dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
43950dca1793SAdrian Knoth 
43960dca1793SAdrian Knoth 		return 1;
43970dca1793SAdrian Knoth 	}
43980dca1793SAdrian Knoth 
43990dca1793SAdrian Knoth 	return 0;
44000dca1793SAdrian Knoth }
44010dca1793SAdrian Knoth 
44020dca1793SAdrian Knoth 
44030dca1793SAdrian Knoth #define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \
44040dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
44050dca1793SAdrian Knoth 	.name = xname, \
44060dca1793SAdrian Knoth 	.index = xindex, \
44070dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
44080dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
44090dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_sync_source, \
44100dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_sync_source, \
44110dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_sync_source \
44120dca1793SAdrian Knoth }
44130dca1793SAdrian Knoth 
44140dca1793SAdrian Knoth static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
44150dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
44160dca1793SAdrian Knoth {
441738816545SAdrian Knoth 	static const char *const texts[] = { "LTC", "Video", "WCK" };
4418e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
44190dca1793SAdrian Knoth 	return 0;
44200dca1793SAdrian Knoth }
44210dca1793SAdrian Knoth 
44220dca1793SAdrian Knoth static int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol,
44230dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
44240dca1793SAdrian Knoth {
44250dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44260dca1793SAdrian Knoth 
44270dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->input;
44280dca1793SAdrian Knoth 
44290dca1793SAdrian Knoth 	return 0;
44300dca1793SAdrian Knoth }
44310dca1793SAdrian Knoth 
44320dca1793SAdrian Knoth static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol,
44330dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
44340dca1793SAdrian Knoth {
44350dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44360dca1793SAdrian Knoth 
44370dca1793SAdrian Knoth 	if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) {
44380dca1793SAdrian Knoth 		hdspm->tco->input = ucontrol->value.enumerated.item[0];
44390dca1793SAdrian Knoth 
44400dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
44410dca1793SAdrian Knoth 
44420dca1793SAdrian Knoth 		return 1;
44430dca1793SAdrian Knoth 	}
44440dca1793SAdrian Knoth 
44450dca1793SAdrian Knoth 	return 0;
44460dca1793SAdrian Knoth }
44470dca1793SAdrian Knoth 
44480dca1793SAdrian Knoth 
44490dca1793SAdrian Knoth #define HDSPM_TCO_WORD_TERM(xname, xindex) \
44500dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
44510dca1793SAdrian Knoth 	.name = xname, \
44520dca1793SAdrian Knoth 	.index = xindex, \
44530dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
44540dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
44550dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_word_term, \
44560dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_word_term, \
44570dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_word_term \
44580dca1793SAdrian Knoth }
44590dca1793SAdrian Knoth 
44600dca1793SAdrian Knoth static int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol,
44610dca1793SAdrian Knoth 					struct snd_ctl_elem_info *uinfo)
44620dca1793SAdrian Knoth {
44630dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
44640dca1793SAdrian Knoth 	uinfo->count = 1;
44650dca1793SAdrian Knoth 	uinfo->value.integer.min = 0;
44660dca1793SAdrian Knoth 	uinfo->value.integer.max = 1;
44670dca1793SAdrian Knoth 
44680dca1793SAdrian Knoth 	return 0;
44690dca1793SAdrian Knoth }
44700dca1793SAdrian Knoth 
44710dca1793SAdrian Knoth 
44720dca1793SAdrian Knoth static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
44730dca1793SAdrian Knoth 				       struct snd_ctl_elem_value *ucontrol)
44740dca1793SAdrian Knoth {
44750dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44760dca1793SAdrian Knoth 
44770dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->term;
44780dca1793SAdrian Knoth 
44790dca1793SAdrian Knoth 	return 0;
44800dca1793SAdrian Knoth }
44810dca1793SAdrian Knoth 
44820dca1793SAdrian Knoth 
44830dca1793SAdrian Knoth static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
44840dca1793SAdrian Knoth 				       struct snd_ctl_elem_value *ucontrol)
44850dca1793SAdrian Knoth {
44860dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44870dca1793SAdrian Knoth 
44880dca1793SAdrian Knoth 	if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) {
44890dca1793SAdrian Knoth 		hdspm->tco->term = ucontrol->value.enumerated.item[0];
44900dca1793SAdrian Knoth 
44910dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
44920dca1793SAdrian Knoth 
44930dca1793SAdrian Knoth 		return 1;
44940dca1793SAdrian Knoth 	}
44950dca1793SAdrian Knoth 
44960dca1793SAdrian Knoth 	return 0;
44970dca1793SAdrian Knoth }
44980dca1793SAdrian Knoth 
44990dca1793SAdrian Knoth 
45000dca1793SAdrian Knoth 
45010dca1793SAdrian Knoth 
45023cee5a60SRemy Bruno static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
4503763f356cSTakashi Iwai 	HDSPM_MIXER("Mixer", 0),
45040dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
4505763f356cSTakashi Iwai 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
4506763f356cSTakashi Iwai 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
4507763f356cSTakashi Iwai 	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
4508763f356cSTakashi Iwai 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
4509b8812c55SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
45100dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
45110dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("MADI SyncCheck", 1),
4512930f4ff0SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 2),
45130dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
4514c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
4515c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
4516696be0fbSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Disable 96K frames", HDSPM_SMUX),
4517c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
4518c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
4519700d1ef3SAdrian Knoth 	HDSPM_INPUT_SELECT("Input Select", 0),
4520700d1ef3SAdrian Knoth 	HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
45210dca1793SAdrian Knoth };
45220dca1793SAdrian Knoth 
45230dca1793SAdrian Knoth 
45240dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
45250dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
45260dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
45270dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
45280dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
45290dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
45300dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
4531c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
4532c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
4533c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
4534700d1ef3SAdrian Knoth 	HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
4535763f356cSTakashi Iwai };
4536763f356cSTakashi Iwai 
45370dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
45383cee5a60SRemy Bruno 	HDSPM_MIXER("Mixer", 0),
45390dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
45403cee5a60SRemy Bruno 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
45413cee5a60SRemy Bruno 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
45423cee5a60SRemy Bruno 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
45433cee5a60SRemy Bruno 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
45440dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
45450dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
45460dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
45470dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT SyncCheck", 3),
45480dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 4),
45490dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5),
45500dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
45510dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
45520dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
45530dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
45540dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
4555fb0f121eSAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5),
455642f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0),
4557fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt),
4558fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
4559fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1),
4560fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db),
456142f4c12dSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48),
456242f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0),
456342f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0),
456442f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0)
45650dca1793SAdrian Knoth 
45660dca1793SAdrian Knoth 		/*
45670dca1793SAdrian Knoth 		   HDSPM_INPUT_SELECT("Input Select", 0),
45680dca1793SAdrian Knoth 		   HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0),
45690dca1793SAdrian Knoth 		   HDSPM_PROFESSIONAL("SPDIF Out Professional", 0);
45700dca1793SAdrian Knoth 		   HDSPM_SPDIF_IN("SPDIF In", 0);
45710dca1793SAdrian Knoth 		   HDSPM_BREAKOUT_CABLE("Breakout Cable", 0);
45720dca1793SAdrian Knoth 		   HDSPM_INPUT_LEVEL("Input Level", 0);
45730dca1793SAdrian Knoth 		   HDSPM_OUTPUT_LEVEL("Output Level", 0);
45740dca1793SAdrian Knoth 		   HDSPM_PHONES("Phones", 0);
45750dca1793SAdrian Knoth 		   */
45760dca1793SAdrian Knoth };
45770dca1793SAdrian Knoth 
45780dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
45790dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
45800dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
45810dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0),
45820dca1793SAdrian Knoth 	HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0),
45830dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
45840dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
45850dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
45860dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
45870dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3),
45880dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4),
45890dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5),
45900dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6),
45910dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 7),
45920dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8),
45930dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
45940dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
45950dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
45960dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3),
45970dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4),
45980dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
45990dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
46000dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
460111a5cd3cSAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8),
460211a5cd3cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
460311a5cd3cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48)
46040dca1793SAdrian Knoth };
46050dca1793SAdrian Knoth 
46060dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
46070dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
46080dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
46090dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
46100dca1793SAdrian Knoth 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
46110dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
46120dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
46132d63ec38SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11),
46140dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC Sync Check", 0),
46150dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
46160dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
46170dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES3 Sync Check", 3),
46180dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES4 Sync Check", 4),
46190dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES5 Sync Check", 5),
46200dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES6 Sync Check", 6),
46210dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES7 Sync Check", 7),
46220dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES8 Sync Check", 8),
46230dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO Sync Check", 9),
46240dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10),
46250dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
46260dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1),
46270dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2),
46280dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3),
46290dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4),
46300dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5),
46310dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6),
46320dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7),
46330dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8),
46340dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9),
46350dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10),
4636c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
4637c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Emphasis", HDSPM_Emphasis),
4638c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Non Audio", HDSPM_Dolby),
4639c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Professional", HDSPM_Professional),
4640c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
46413cee5a60SRemy Bruno 	HDSPM_DS_WIRE("Double Speed Wire Mode", 0),
46423cee5a60SRemy Bruno 	HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
46433cee5a60SRemy Bruno };
46443cee5a60SRemy Bruno 
46450dca1793SAdrian Knoth 
46460dca1793SAdrian Knoth 
46470dca1793SAdrian Knoth /* Control elements for the optional TCO module */
46480dca1793SAdrian Knoth static struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
46490dca1793SAdrian Knoth 	HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0),
46500dca1793SAdrian Knoth 	HDSPM_TCO_PULL("TCO Pull", 0),
46510dca1793SAdrian Knoth 	HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
46520dca1793SAdrian Knoth 	HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
46530dca1793SAdrian Knoth 	HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
4654a817650eSAdrian Knoth 	HDSPM_TCO_WORD_TERM("TCO Word Term", 0),
4655a817650eSAdrian Knoth 	HDSPM_TCO_LOCK_CHECK("TCO Input Check", 11),
4656a817650eSAdrian Knoth 	HDSPM_TCO_LOCK_CHECK("TCO LTC Valid", 12),
4657a817650eSAdrian Knoth 	HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate", 0),
4658a817650eSAdrian Knoth 	HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format", 0)
46590dca1793SAdrian Knoth };
46600dca1793SAdrian Knoth 
46610dca1793SAdrian Knoth 
466298274f07STakashi Iwai static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
4663763f356cSTakashi Iwai 
4664763f356cSTakashi Iwai 
466598274f07STakashi Iwai static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm)
4666763f356cSTakashi Iwai {
4667763f356cSTakashi Iwai 	int i;
4668763f356cSTakashi Iwai 
46690dca1793SAdrian Knoth 	for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) {
4670763f356cSTakashi Iwai 		if (hdspm->system_sample_rate > 48000) {
4671763f356cSTakashi Iwai 			hdspm->playback_mixer_ctls[i]->vd[0].access =
4672763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_INACTIVE |
4673763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_READ |
4674763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
4675763f356cSTakashi Iwai 		} else {
4676763f356cSTakashi Iwai 			hdspm->playback_mixer_ctls[i]->vd[0].access =
4677763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_READWRITE |
4678763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
4679763f356cSTakashi Iwai 		}
4680763f356cSTakashi Iwai 		snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
4681763f356cSTakashi Iwai 				SNDRV_CTL_EVENT_MASK_INFO,
4682763f356cSTakashi Iwai 				&hdspm->playback_mixer_ctls[i]->id);
4683763f356cSTakashi Iwai 	}
4684763f356cSTakashi Iwai 
4685763f356cSTakashi Iwai 	return 0;
4686763f356cSTakashi Iwai }
4687763f356cSTakashi Iwai 
4688763f356cSTakashi Iwai 
46890dca1793SAdrian Knoth static int snd_hdspm_create_controls(struct snd_card *card,
46900dca1793SAdrian Knoth 					struct hdspm *hdspm)
4691763f356cSTakashi Iwai {
4692763f356cSTakashi Iwai 	unsigned int idx, limit;
4693763f356cSTakashi Iwai 	int err;
469498274f07STakashi Iwai 	struct snd_kcontrol *kctl;
46950dca1793SAdrian Knoth 	struct snd_kcontrol_new *list = NULL;
4696763f356cSTakashi Iwai 
46970dca1793SAdrian Knoth 	switch (hdspm->io_type) {
46980dca1793SAdrian Knoth 	case MADI:
46990dca1793SAdrian Knoth 		list = snd_hdspm_controls_madi;
47000dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_madi);
47010dca1793SAdrian Knoth 		break;
47020dca1793SAdrian Knoth 	case MADIface:
47030dca1793SAdrian Knoth 		list = snd_hdspm_controls_madiface;
47040dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_madiface);
47050dca1793SAdrian Knoth 		break;
47060dca1793SAdrian Knoth 	case AIO:
47070dca1793SAdrian Knoth 		list = snd_hdspm_controls_aio;
47080dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_aio);
47090dca1793SAdrian Knoth 		break;
47100dca1793SAdrian Knoth 	case RayDAT:
47110dca1793SAdrian Knoth 		list = snd_hdspm_controls_raydat;
47120dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_raydat);
47130dca1793SAdrian Knoth 		break;
47140dca1793SAdrian Knoth 	case AES32:
47150dca1793SAdrian Knoth 		list = snd_hdspm_controls_aes32;
47160dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_aes32);
47170dca1793SAdrian Knoth 		break;
47180dca1793SAdrian Knoth 	}
4719763f356cSTakashi Iwai 
47200dca1793SAdrian Knoth 	if (NULL != list) {
47210dca1793SAdrian Knoth 		for (idx = 0; idx < limit; idx++) {
47223cee5a60SRemy Bruno 			err = snd_ctl_add(card,
47230dca1793SAdrian Knoth 					snd_ctl_new1(&list[idx], hdspm));
47243cee5a60SRemy Bruno 			if (err < 0)
4725763f356cSTakashi Iwai 				return err;
4726763f356cSTakashi Iwai 		}
4727763f356cSTakashi Iwai 	}
4728763f356cSTakashi Iwai 
4729763f356cSTakashi Iwai 
47300dca1793SAdrian Knoth 	/* create simple 1:1 playback mixer controls */
4731763f356cSTakashi Iwai 	snd_hdspm_playback_mixer.name = "Chn";
47320dca1793SAdrian Knoth 	if (hdspm->system_sample_rate >= 128000) {
47330dca1793SAdrian Knoth 		limit = hdspm->qs_out_channels;
47340dca1793SAdrian Knoth 	} else if (hdspm->system_sample_rate >= 64000) {
47350dca1793SAdrian Knoth 		limit = hdspm->ds_out_channels;
47360dca1793SAdrian Knoth 	} else {
47370dca1793SAdrian Knoth 		limit = hdspm->ss_out_channels;
47380dca1793SAdrian Knoth 	}
4739763f356cSTakashi Iwai 	for (idx = 0; idx < limit; ++idx) {
4740763f356cSTakashi Iwai 		snd_hdspm_playback_mixer.index = idx + 1;
4741ef5fa1a4STakashi Iwai 		kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm);
4742ef5fa1a4STakashi Iwai 		err = snd_ctl_add(card, kctl);
4743ef5fa1a4STakashi Iwai 		if (err < 0)
4744763f356cSTakashi Iwai 			return err;
4745763f356cSTakashi Iwai 		hdspm->playback_mixer_ctls[idx] = kctl;
4746763f356cSTakashi Iwai 	}
4747763f356cSTakashi Iwai 
47480dca1793SAdrian Knoth 
47490dca1793SAdrian Knoth 	if (hdspm->tco) {
47500dca1793SAdrian Knoth 		/* add tco control elements */
47510dca1793SAdrian Knoth 		list = snd_hdspm_controls_tco;
47520dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_tco);
47530dca1793SAdrian Knoth 		for (idx = 0; idx < limit; idx++) {
47540dca1793SAdrian Knoth 			err = snd_ctl_add(card,
47550dca1793SAdrian Knoth 					snd_ctl_new1(&list[idx], hdspm));
47560dca1793SAdrian Knoth 			if (err < 0)
47570dca1793SAdrian Knoth 				return err;
47580dca1793SAdrian Knoth 		}
47590dca1793SAdrian Knoth 	}
47600dca1793SAdrian Knoth 
4761763f356cSTakashi Iwai 	return 0;
4762763f356cSTakashi Iwai }
4763763f356cSTakashi Iwai 
4764763f356cSTakashi Iwai /*------------------------------------------------------------
4765763f356cSTakashi Iwai    /proc interface
4766763f356cSTakashi Iwai  ------------------------------------------------------------*/
4767763f356cSTakashi Iwai 
4768763f356cSTakashi Iwai static void
47695760107cSAdrian Knoth snd_hdspm_proc_read_tco(struct snd_info_entry *entry,
47703cee5a60SRemy Bruno 					struct snd_info_buffer *buffer)
4771763f356cSTakashi Iwai {
4772ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
47735760107cSAdrian Knoth 	unsigned int status, control;
47740dca1793SAdrian Knoth 	int a, ltc, frames, seconds, minutes, hours;
47750dca1793SAdrian Knoth 	unsigned int period;
47760dca1793SAdrian Knoth 	u64 freq_const = 0;
47770dca1793SAdrian Knoth 	u32 rate;
47780dca1793SAdrian Knoth 
47795760107cSAdrian Knoth 	snd_iprintf(buffer, "--- TCO ---\n");
47805760107cSAdrian Knoth 
4781763f356cSTakashi Iwai 	status = hdspm_read(hdspm, HDSPM_statusRegister);
47820dca1793SAdrian Knoth 	control = hdspm->control_register;
4783763f356cSTakashi Iwai 
4784763f356cSTakashi Iwai 
47850dca1793SAdrian Knoth 	if (status & HDSPM_tco_detect) {
47860dca1793SAdrian Knoth 		snd_iprintf(buffer, "TCO module detected.\n");
47870dca1793SAdrian Knoth 		a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
47880dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_LTC_Input_valid) {
47890dca1793SAdrian Knoth 			snd_iprintf(buffer, "  LTC valid, ");
47900dca1793SAdrian Knoth 			switch (a & (HDSPM_TCO1_LTC_Format_LSB |
47910dca1793SAdrian Knoth 						HDSPM_TCO1_LTC_Format_MSB)) {
47920dca1793SAdrian Knoth 			case 0:
47930dca1793SAdrian Knoth 				snd_iprintf(buffer, "24 fps, ");
47940dca1793SAdrian Knoth 				break;
47950dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_LSB:
47960dca1793SAdrian Knoth 				snd_iprintf(buffer, "25 fps, ");
47970dca1793SAdrian Knoth 				break;
47980dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_MSB:
47990dca1793SAdrian Knoth 				snd_iprintf(buffer, "29.97 fps, ");
48000dca1793SAdrian Knoth 				break;
48010dca1793SAdrian Knoth 			default:
48020dca1793SAdrian Knoth 				snd_iprintf(buffer, "30 fps, ");
48030dca1793SAdrian Knoth 				break;
48040dca1793SAdrian Knoth 			}
48050dca1793SAdrian Knoth 			if (a & HDSPM_TCO1_set_drop_frame_flag) {
48060dca1793SAdrian Knoth 				snd_iprintf(buffer, "drop frame\n");
48070dca1793SAdrian Knoth 			} else {
48080dca1793SAdrian Knoth 				snd_iprintf(buffer, "full frame\n");
48090dca1793SAdrian Knoth 			}
48100dca1793SAdrian Knoth 		} else {
48110dca1793SAdrian Knoth 			snd_iprintf(buffer, "  no LTC\n");
48120dca1793SAdrian Knoth 		}
48130dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_Video_Input_Format_NTSC) {
48140dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Video: NTSC\n");
48150dca1793SAdrian Knoth 		} else if (a & HDSPM_TCO1_Video_Input_Format_PAL) {
48160dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Video: PAL\n");
48170dca1793SAdrian Knoth 		} else {
48180dca1793SAdrian Knoth 			snd_iprintf(buffer, "  No video\n");
48190dca1793SAdrian Knoth 		}
48200dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_TCO_lock) {
48210dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Sync: lock\n");
48220dca1793SAdrian Knoth 		} else {
48230dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Sync: no lock\n");
48240dca1793SAdrian Knoth 		}
48250dca1793SAdrian Knoth 
48260dca1793SAdrian Knoth 		switch (hdspm->io_type) {
48270dca1793SAdrian Knoth 		case MADI:
48280dca1793SAdrian Knoth 		case AES32:
48290dca1793SAdrian Knoth 			freq_const = 110069313433624ULL;
48300dca1793SAdrian Knoth 			break;
48310dca1793SAdrian Knoth 		case RayDAT:
48320dca1793SAdrian Knoth 		case AIO:
48330dca1793SAdrian Knoth 			freq_const = 104857600000000ULL;
48340dca1793SAdrian Knoth 			break;
48350dca1793SAdrian Knoth 		case MADIface:
48360dca1793SAdrian Knoth 			break; /* no TCO possible */
48370dca1793SAdrian Knoth 		}
48380dca1793SAdrian Knoth 
48390dca1793SAdrian Knoth 		period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
48400dca1793SAdrian Knoth 		snd_iprintf(buffer, "    period: %u\n", period);
48410dca1793SAdrian Knoth 
48420dca1793SAdrian Knoth 
48430dca1793SAdrian Knoth 		/* rate = freq_const/period; */
48440dca1793SAdrian Knoth 		rate = div_u64(freq_const, period);
48450dca1793SAdrian Knoth 
48460dca1793SAdrian Knoth 		if (control & HDSPM_QuadSpeed) {
48470dca1793SAdrian Knoth 			rate *= 4;
48480dca1793SAdrian Knoth 		} else if (control & HDSPM_DoubleSpeed) {
48490dca1793SAdrian Knoth 			rate *= 2;
48500dca1793SAdrian Knoth 		}
48510dca1793SAdrian Knoth 
48520dca1793SAdrian Knoth 		snd_iprintf(buffer, "  Frequency: %u Hz\n",
48530dca1793SAdrian Knoth 				(unsigned int) rate);
48540dca1793SAdrian Knoth 
48550dca1793SAdrian Knoth 		ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
48560dca1793SAdrian Knoth 		frames = ltc & 0xF;
48570dca1793SAdrian Knoth 		ltc >>= 4;
48580dca1793SAdrian Knoth 		frames += (ltc & 0x3) * 10;
48590dca1793SAdrian Knoth 		ltc >>= 4;
48600dca1793SAdrian Knoth 		seconds = ltc & 0xF;
48610dca1793SAdrian Knoth 		ltc >>= 4;
48620dca1793SAdrian Knoth 		seconds += (ltc & 0x7) * 10;
48630dca1793SAdrian Knoth 		ltc >>= 4;
48640dca1793SAdrian Knoth 		minutes = ltc & 0xF;
48650dca1793SAdrian Knoth 		ltc >>= 4;
48660dca1793SAdrian Knoth 		minutes += (ltc & 0x7) * 10;
48670dca1793SAdrian Knoth 		ltc >>= 4;
48680dca1793SAdrian Knoth 		hours = ltc & 0xF;
48690dca1793SAdrian Knoth 		ltc >>= 4;
48700dca1793SAdrian Knoth 		hours += (ltc & 0x3) * 10;
48710dca1793SAdrian Knoth 		snd_iprintf(buffer,
48720dca1793SAdrian Knoth 			"  LTC In: %02d:%02d:%02d:%02d\n",
48730dca1793SAdrian Knoth 			hours, minutes, seconds, frames);
48740dca1793SAdrian Knoth 
48750dca1793SAdrian Knoth 	} else {
48760dca1793SAdrian Knoth 		snd_iprintf(buffer, "No TCO module detected.\n");
48770dca1793SAdrian Knoth 	}
48785760107cSAdrian Knoth }
48795760107cSAdrian Knoth 
48805760107cSAdrian Knoth static void
48815760107cSAdrian Knoth snd_hdspm_proc_read_madi(struct snd_info_entry *entry,
48825760107cSAdrian Knoth 			 struct snd_info_buffer *buffer)
48835760107cSAdrian Knoth {
48845760107cSAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
48855760107cSAdrian Knoth 	unsigned int status, status2, control, freq;
48865760107cSAdrian Knoth 
48875760107cSAdrian Knoth 	char *pref_sync_ref;
48885760107cSAdrian Knoth 	char *autosync_ref;
48895760107cSAdrian Knoth 	char *system_clock_mode;
48905760107cSAdrian Knoth 	char *insel;
48915760107cSAdrian Knoth 	int x, x2;
48925760107cSAdrian Knoth 
48935760107cSAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_statusRegister);
48945760107cSAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
48955760107cSAdrian Knoth 	control = hdspm->control_register;
48965760107cSAdrian Knoth 	freq = hdspm_read(hdspm, HDSPM_timecodeRegister);
48975760107cSAdrian Knoth 
48985760107cSAdrian Knoth 	snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
48995760107cSAdrian Knoth 			hdspm->card_name, hdspm->card->number + 1,
49005760107cSAdrian Knoth 			hdspm->firmware_rev,
49015760107cSAdrian Knoth 			(status2 & HDSPM_version0) |
49025760107cSAdrian Knoth 			(status2 & HDSPM_version1) | (status2 &
49035760107cSAdrian Knoth 				HDSPM_version2));
49045760107cSAdrian Knoth 
49055760107cSAdrian Knoth 	snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
49065760107cSAdrian Knoth 			(hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
49075760107cSAdrian Knoth 			hdspm->serial);
49085760107cSAdrian Knoth 
49095760107cSAdrian Knoth 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
49105760107cSAdrian Knoth 			hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
49115760107cSAdrian Knoth 
49125760107cSAdrian Knoth 	snd_iprintf(buffer, "--- System ---\n");
49135760107cSAdrian Knoth 
49145760107cSAdrian Knoth 	snd_iprintf(buffer,
49155760107cSAdrian Knoth 		"IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
49165760107cSAdrian Knoth 		status & HDSPM_audioIRQPending,
49175760107cSAdrian Knoth 		(status & HDSPM_midi0IRQPending) ? 1 : 0,
49185760107cSAdrian Knoth 		(status & HDSPM_midi1IRQPending) ? 1 : 0,
49195760107cSAdrian Knoth 		hdspm->irq_count);
49205760107cSAdrian Knoth 	snd_iprintf(buffer,
49215760107cSAdrian Knoth 		"HW pointer: id = %d, rawptr = %d (%d->%d) "
49225760107cSAdrian Knoth 		"estimated= %ld (bytes)\n",
49235760107cSAdrian Knoth 		((status & HDSPM_BufferID) ? 1 : 0),
49245760107cSAdrian Knoth 		(status & HDSPM_BufferPositionMask),
49255760107cSAdrian Knoth 		(status & HDSPM_BufferPositionMask) %
49265760107cSAdrian Knoth 		(2 * (int)hdspm->period_bytes),
49275760107cSAdrian Knoth 		((status & HDSPM_BufferPositionMask) - 64) %
49285760107cSAdrian Knoth 		(2 * (int)hdspm->period_bytes),
49295760107cSAdrian Knoth 		(long) hdspm_hw_pointer(hdspm) * 4);
49305760107cSAdrian Knoth 
49315760107cSAdrian Knoth 	snd_iprintf(buffer,
49325760107cSAdrian Knoth 		"MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
49335760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
49345760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
49355760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
49365760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
49375760107cSAdrian Knoth 	snd_iprintf(buffer,
49385760107cSAdrian Knoth 		"MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
49395760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
49405760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
49415760107cSAdrian Knoth 	snd_iprintf(buffer,
49425760107cSAdrian Knoth 		"Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
49435760107cSAdrian Knoth 		"status2=0x%x\n",
49445760107cSAdrian Knoth 		hdspm->control_register, hdspm->control2_register,
49455760107cSAdrian Knoth 		status, status2);
49465760107cSAdrian Knoth 
4947763f356cSTakashi Iwai 
4948763f356cSTakashi Iwai 	snd_iprintf(buffer, "--- Settings ---\n");
4949763f356cSTakashi Iwai 
49507cb155ffSAdrian Knoth 	x = hdspm_get_latency(hdspm);
4951763f356cSTakashi Iwai 
4952763f356cSTakashi Iwai 	snd_iprintf(buffer,
4953763f356cSTakashi Iwai 		"Size (Latency): %d samples (2 periods of %lu bytes)\n",
4954763f356cSTakashi Iwai 		x, (unsigned long) hdspm->period_bytes);
4955763f356cSTakashi Iwai 
49560dca1793SAdrian Knoth 	snd_iprintf(buffer, "Line out: %s\n",
49570dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
4958763f356cSTakashi Iwai 
4959763f356cSTakashi Iwai 	switch (hdspm->control_register & HDSPM_InputMask) {
4960763f356cSTakashi Iwai 	case HDSPM_InputOptical:
4961763f356cSTakashi Iwai 		insel = "Optical";
4962763f356cSTakashi Iwai 		break;
4963763f356cSTakashi Iwai 	case HDSPM_InputCoaxial:
4964763f356cSTakashi Iwai 		insel = "Coaxial";
4965763f356cSTakashi Iwai 		break;
4966763f356cSTakashi Iwai 	default:
4967ec8f53fbSMasanari Iida 		insel = "Unknown";
4968763f356cSTakashi Iwai 	}
4969763f356cSTakashi Iwai 
4970763f356cSTakashi Iwai 	snd_iprintf(buffer,
4971ef5fa1a4STakashi Iwai 		"ClearTrackMarker = %s, Transmit in %s Channel Mode, "
4972ef5fa1a4STakashi Iwai 		"Auto Input %s\n",
49730dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
49740dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
49750dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
4976763f356cSTakashi Iwai 
49770dca1793SAdrian Knoth 
49783cee5a60SRemy Bruno 	if (!(hdspm->control_register & HDSPM_ClockModeMaster))
49790dca1793SAdrian Knoth 		system_clock_mode = "AutoSync";
49803cee5a60SRemy Bruno 	else
4981763f356cSTakashi Iwai 		system_clock_mode = "Master";
49820dca1793SAdrian Knoth 	snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode);
4983763f356cSTakashi Iwai 
4984763f356cSTakashi Iwai 	switch (hdspm_pref_sync_ref(hdspm)) {
4985763f356cSTakashi Iwai 	case HDSPM_SYNC_FROM_WORD:
4986763f356cSTakashi Iwai 		pref_sync_ref = "Word Clock";
4987763f356cSTakashi Iwai 		break;
4988763f356cSTakashi Iwai 	case HDSPM_SYNC_FROM_MADI:
4989763f356cSTakashi Iwai 		pref_sync_ref = "MADI Sync";
4990763f356cSTakashi Iwai 		break;
49910dca1793SAdrian Knoth 	case HDSPM_SYNC_FROM_TCO:
49920dca1793SAdrian Knoth 		pref_sync_ref = "TCO";
49930dca1793SAdrian Knoth 		break;
49940dca1793SAdrian Knoth 	case HDSPM_SYNC_FROM_SYNC_IN:
49950dca1793SAdrian Knoth 		pref_sync_ref = "Sync In";
49960dca1793SAdrian Knoth 		break;
4997763f356cSTakashi Iwai 	default:
4998763f356cSTakashi Iwai 		pref_sync_ref = "XXXX Clock";
4999763f356cSTakashi Iwai 		break;
5000763f356cSTakashi Iwai 	}
5001763f356cSTakashi Iwai 	snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
5002763f356cSTakashi Iwai 			pref_sync_ref);
5003763f356cSTakashi Iwai 
5004763f356cSTakashi Iwai 	snd_iprintf(buffer, "System Clock Frequency: %d\n",
5005763f356cSTakashi Iwai 			hdspm->system_sample_rate);
5006763f356cSTakashi Iwai 
5007763f356cSTakashi Iwai 
5008763f356cSTakashi Iwai 	snd_iprintf(buffer, "--- Status:\n");
5009763f356cSTakashi Iwai 
5010763f356cSTakashi Iwai 	x = status & HDSPM_madiSync;
5011763f356cSTakashi Iwai 	x2 = status2 & HDSPM_wcSync;
5012763f356cSTakashi Iwai 
5013763f356cSTakashi Iwai 	snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
5014763f356cSTakashi Iwai 			(status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
5015763f356cSTakashi Iwai 			"NoLock",
5016763f356cSTakashi Iwai 			(status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
5017763f356cSTakashi Iwai 			"NoLock");
5018763f356cSTakashi Iwai 
5019763f356cSTakashi Iwai 	switch (hdspm_autosync_ref(hdspm)) {
50200dca1793SAdrian Knoth 	case HDSPM_AUTOSYNC_FROM_SYNC_IN:
50210dca1793SAdrian Knoth 		autosync_ref = "Sync In";
50220dca1793SAdrian Knoth 		break;
50230dca1793SAdrian Knoth 	case HDSPM_AUTOSYNC_FROM_TCO:
50240dca1793SAdrian Knoth 		autosync_ref = "TCO";
50250dca1793SAdrian Knoth 		break;
5026763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_WORD:
5027763f356cSTakashi Iwai 		autosync_ref = "Word Clock";
5028763f356cSTakashi Iwai 		break;
5029763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_MADI:
5030763f356cSTakashi Iwai 		autosync_ref = "MADI Sync";
5031763f356cSTakashi Iwai 		break;
5032763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_NONE:
5033763f356cSTakashi Iwai 		autosync_ref = "Input not valid";
5034763f356cSTakashi Iwai 		break;
5035763f356cSTakashi Iwai 	default:
5036763f356cSTakashi Iwai 		autosync_ref = "---";
5037763f356cSTakashi Iwai 		break;
5038763f356cSTakashi Iwai 	}
5039763f356cSTakashi Iwai 	snd_iprintf(buffer,
5040763f356cSTakashi Iwai 		"AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
5041763f356cSTakashi Iwai 		autosync_ref, hdspm_external_sample_rate(hdspm),
5042763f356cSTakashi Iwai 		(status & HDSPM_madiFreqMask) >> 22,
5043763f356cSTakashi Iwai 		(status2 & HDSPM_wcFreqMask) >> 5);
5044763f356cSTakashi Iwai 
5045763f356cSTakashi Iwai 	snd_iprintf(buffer, "Input: %s, Mode=%s\n",
5046763f356cSTakashi Iwai 		(status & HDSPM_AB_int) ? "Coax" : "Optical",
5047763f356cSTakashi Iwai 		(status & HDSPM_RX_64ch) ? "64 channels" :
5048763f356cSTakashi Iwai 		"56 channels");
5049763f356cSTakashi Iwai 
50505760107cSAdrian Knoth 	/* call readout function for TCO specific status */
50515760107cSAdrian Knoth 	snd_hdspm_proc_read_tco(entry, buffer);
50525760107cSAdrian Knoth 
5053763f356cSTakashi Iwai 	snd_iprintf(buffer, "\n");
5054763f356cSTakashi Iwai }
5055763f356cSTakashi Iwai 
50563cee5a60SRemy Bruno static void
50573cee5a60SRemy Bruno snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
50583cee5a60SRemy Bruno 			  struct snd_info_buffer *buffer)
50593cee5a60SRemy Bruno {
5060ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
50613cee5a60SRemy Bruno 	unsigned int status;
50623cee5a60SRemy Bruno 	unsigned int status2;
50633cee5a60SRemy Bruno 	unsigned int timecode;
506456bde0f3SAndre Schramm 	unsigned int wcLock, wcSync;
50653cee5a60SRemy Bruno 	int pref_syncref;
50663cee5a60SRemy Bruno 	char *autosync_ref;
50673cee5a60SRemy Bruno 	int x;
50683cee5a60SRemy Bruno 
50693cee5a60SRemy Bruno 	status = hdspm_read(hdspm, HDSPM_statusRegister);
50703cee5a60SRemy Bruno 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
50713cee5a60SRemy Bruno 	timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
50723cee5a60SRemy Bruno 
50733cee5a60SRemy Bruno 	snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n",
50743cee5a60SRemy Bruno 		    hdspm->card_name, hdspm->card->number + 1,
50753cee5a60SRemy Bruno 		    hdspm->firmware_rev);
50763cee5a60SRemy Bruno 
50773cee5a60SRemy Bruno 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
50783cee5a60SRemy Bruno 		    hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
50793cee5a60SRemy Bruno 
50803cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- System ---\n");
50813cee5a60SRemy Bruno 
50823cee5a60SRemy Bruno 	snd_iprintf(buffer,
50833cee5a60SRemy Bruno 		    "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
50843cee5a60SRemy Bruno 		    status & HDSPM_audioIRQPending,
50853cee5a60SRemy Bruno 		    (status & HDSPM_midi0IRQPending) ? 1 : 0,
50863cee5a60SRemy Bruno 		    (status & HDSPM_midi1IRQPending) ? 1 : 0,
50873cee5a60SRemy Bruno 		    hdspm->irq_count);
50883cee5a60SRemy Bruno 	snd_iprintf(buffer,
5089ef5fa1a4STakashi Iwai 		    "HW pointer: id = %d, rawptr = %d (%d->%d) "
5090ef5fa1a4STakashi Iwai 		    "estimated= %ld (bytes)\n",
50913cee5a60SRemy Bruno 		    ((status & HDSPM_BufferID) ? 1 : 0),
50923cee5a60SRemy Bruno 		    (status & HDSPM_BufferPositionMask),
5093ef5fa1a4STakashi Iwai 		    (status & HDSPM_BufferPositionMask) %
5094ef5fa1a4STakashi Iwai 		    (2 * (int)hdspm->period_bytes),
5095ef5fa1a4STakashi Iwai 		    ((status & HDSPM_BufferPositionMask) - 64) %
5096ef5fa1a4STakashi Iwai 		    (2 * (int)hdspm->period_bytes),
50973cee5a60SRemy Bruno 		    (long) hdspm_hw_pointer(hdspm) * 4);
50983cee5a60SRemy Bruno 
50993cee5a60SRemy Bruno 	snd_iprintf(buffer,
51003cee5a60SRemy Bruno 		    "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
51013cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
51023cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
51033cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
51043cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
51053cee5a60SRemy Bruno 	snd_iprintf(buffer,
51060dca1793SAdrian Knoth 		    "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
51070dca1793SAdrian Knoth 		    hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
51080dca1793SAdrian Knoth 		    hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
51090dca1793SAdrian Knoth 	snd_iprintf(buffer,
51100dca1793SAdrian Knoth 		    "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
51110dca1793SAdrian Knoth 		    "status2=0x%x\n",
51120dca1793SAdrian Knoth 		    hdspm->control_register, hdspm->control2_register,
51130dca1793SAdrian Knoth 		    status, status2);
51143cee5a60SRemy Bruno 
51153cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- Settings ---\n");
51163cee5a60SRemy Bruno 
51177cb155ffSAdrian Knoth 	x = hdspm_get_latency(hdspm);
51183cee5a60SRemy Bruno 
51193cee5a60SRemy Bruno 	snd_iprintf(buffer,
51203cee5a60SRemy Bruno 		    "Size (Latency): %d samples (2 periods of %lu bytes)\n",
51213cee5a60SRemy Bruno 		    x, (unsigned long) hdspm->period_bytes);
51223cee5a60SRemy Bruno 
51230dca1793SAdrian Knoth 	snd_iprintf(buffer, "Line out: %s\n",
51243cee5a60SRemy Bruno 		    (hdspm->
51250dca1793SAdrian Knoth 		     control_register & HDSPM_LineOut) ? "on " : "off");
51263cee5a60SRemy Bruno 
51273cee5a60SRemy Bruno 	snd_iprintf(buffer,
51283cee5a60SRemy Bruno 		    "ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
51293cee5a60SRemy Bruno 		    (hdspm->
51303cee5a60SRemy Bruno 		     control_register & HDSPM_clr_tms) ? "on" : "off",
51313cee5a60SRemy Bruno 		    (hdspm->
51323cee5a60SRemy Bruno 		     control_register & HDSPM_Emphasis) ? "on" : "off",
51333cee5a60SRemy Bruno 		    (hdspm->
51343cee5a60SRemy Bruno 		     control_register & HDSPM_Dolby) ? "on" : "off");
51353cee5a60SRemy Bruno 
51363cee5a60SRemy Bruno 
51373cee5a60SRemy Bruno 	pref_syncref = hdspm_pref_sync_ref(hdspm);
51383cee5a60SRemy Bruno 	if (pref_syncref == 0)
51393cee5a60SRemy Bruno 		snd_iprintf(buffer, "Preferred Sync Reference: Word Clock\n");
51403cee5a60SRemy Bruno 	else
51413cee5a60SRemy Bruno 		snd_iprintf(buffer, "Preferred Sync Reference: AES%d\n",
51423cee5a60SRemy Bruno 				pref_syncref);
51433cee5a60SRemy Bruno 
51443cee5a60SRemy Bruno 	snd_iprintf(buffer, "System Clock Frequency: %d\n",
51453cee5a60SRemy Bruno 		    hdspm->system_sample_rate);
51463cee5a60SRemy Bruno 
51473cee5a60SRemy Bruno 	snd_iprintf(buffer, "Double speed: %s\n",
51483cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_DS_DoubleWire?
51493cee5a60SRemy Bruno 			"Double wire" : "Single wire");
51503cee5a60SRemy Bruno 	snd_iprintf(buffer, "Quad speed: %s\n",
51513cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_QS_DoubleWire?
51523cee5a60SRemy Bruno 			"Double wire" :
51533cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_QS_QuadWire?
51543cee5a60SRemy Bruno 			"Quad wire" : "Single wire");
51553cee5a60SRemy Bruno 
51563cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- Status:\n");
51573cee5a60SRemy Bruno 
515856bde0f3SAndre Schramm 	wcLock = status & HDSPM_AES32_wcLock;
515956bde0f3SAndre Schramm 	wcSync = wcLock && (status & HDSPM_AES32_wcSync);
516056bde0f3SAndre Schramm 
51613cee5a60SRemy Bruno 	snd_iprintf(buffer, "Word: %s  Frequency: %d\n",
516256bde0f3SAndre Schramm 		    (wcLock) ? (wcSync ? "Sync   " : "Lock   ") : "No Lock",
51633cee5a60SRemy Bruno 		    HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
51643cee5a60SRemy Bruno 
51653cee5a60SRemy Bruno 	for (x = 0; x < 8; x++) {
51663cee5a60SRemy Bruno 		snd_iprintf(buffer, "AES%d: %s  Frequency: %d\n",
51673cee5a60SRemy Bruno 			    x+1,
5168ef5fa1a4STakashi Iwai 			    (status2 & (HDSPM_LockAES >> x)) ?
5169ef5fa1a4STakashi Iwai 			    "Sync   " : "No Lock",
51703cee5a60SRemy Bruno 			    HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
51713cee5a60SRemy Bruno 	}
51723cee5a60SRemy Bruno 
51733cee5a60SRemy Bruno 	switch (hdspm_autosync_ref(hdspm)) {
51740dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_NONE:
51750dca1793SAdrian Knoth 		autosync_ref = "None"; break;
51760dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_WORD:
51770dca1793SAdrian Knoth 		autosync_ref = "Word Clock"; break;
51780dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES1:
51790dca1793SAdrian Knoth 		autosync_ref = "AES1"; break;
51800dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES2:
51810dca1793SAdrian Knoth 		autosync_ref = "AES2"; break;
51820dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES3:
51830dca1793SAdrian Knoth 		autosync_ref = "AES3"; break;
51840dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES4:
51850dca1793SAdrian Knoth 		autosync_ref = "AES4"; break;
51860dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES5:
51870dca1793SAdrian Knoth 		autosync_ref = "AES5"; break;
51880dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES6:
51890dca1793SAdrian Knoth 		autosync_ref = "AES6"; break;
51900dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES7:
51910dca1793SAdrian Knoth 		autosync_ref = "AES7"; break;
51920dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES8:
51930dca1793SAdrian Knoth 		autosync_ref = "AES8"; break;
5194194062daSAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_TCO:
5195194062daSAdrian Knoth 		autosync_ref = "TCO"; break;
5196194062daSAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN:
5197194062daSAdrian Knoth 		autosync_ref = "Sync In"; break;
51980dca1793SAdrian Knoth 	default:
51990dca1793SAdrian Knoth 		autosync_ref = "---"; break;
52003cee5a60SRemy Bruno 	}
52013cee5a60SRemy Bruno 	snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
52023cee5a60SRemy Bruno 
5203194062daSAdrian Knoth 	/* call readout function for TCO specific status */
5204194062daSAdrian Knoth 	snd_hdspm_proc_read_tco(entry, buffer);
5205194062daSAdrian Knoth 
52063cee5a60SRemy Bruno 	snd_iprintf(buffer, "\n");
52073cee5a60SRemy Bruno }
52083cee5a60SRemy Bruno 
52090dca1793SAdrian Knoth static void
52100dca1793SAdrian Knoth snd_hdspm_proc_read_raydat(struct snd_info_entry *entry,
52110dca1793SAdrian Knoth 			 struct snd_info_buffer *buffer)
52120dca1793SAdrian Knoth {
52130dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
52140dca1793SAdrian Knoth 	unsigned int status1, status2, status3, control, i;
52150dca1793SAdrian Knoth 	unsigned int lock, sync;
52160dca1793SAdrian Knoth 
52170dca1793SAdrian Knoth 	status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */
52180dca1793SAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */
52190dca1793SAdrian Knoth 	status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */
52200dca1793SAdrian Knoth 
52210dca1793SAdrian Knoth 	control = hdspm->control_register;
52220dca1793SAdrian Knoth 
52230dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1);
52240dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2);
52250dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3);
52260dca1793SAdrian Knoth 
52270dca1793SAdrian Knoth 
52280dca1793SAdrian Knoth 	snd_iprintf(buffer, "\n*** CLOCK MODE\n\n");
52290dca1793SAdrian Knoth 
52300dca1793SAdrian Knoth 	snd_iprintf(buffer, "Clock mode      : %s\n",
52310dca1793SAdrian Knoth 		(hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave");
52320dca1793SAdrian Knoth 	snd_iprintf(buffer, "System frequency: %d Hz\n",
52330dca1793SAdrian Knoth 		hdspm_get_system_sample_rate(hdspm));
52340dca1793SAdrian Knoth 
52350dca1793SAdrian Knoth 	snd_iprintf(buffer, "\n*** INPUT STATUS\n\n");
52360dca1793SAdrian Knoth 
52370dca1793SAdrian Knoth 	lock = 0x1;
52380dca1793SAdrian Knoth 	sync = 0x100;
52390dca1793SAdrian Knoth 
52400dca1793SAdrian Knoth 	for (i = 0; i < 8; i++) {
52410dca1793SAdrian Knoth 		snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n",
52420dca1793SAdrian Knoth 				i,
52430dca1793SAdrian Knoth 				(status1 & lock) ? 1 : 0,
52440dca1793SAdrian Knoth 				(status1 & sync) ? 1 : 0,
52450dca1793SAdrian Knoth 				texts_freq[(status2 >> (i * 4)) & 0xF]);
52460dca1793SAdrian Knoth 
52470dca1793SAdrian Knoth 		lock = lock<<1;
52480dca1793SAdrian Knoth 		sync = sync<<1;
52490dca1793SAdrian Knoth 	}
52500dca1793SAdrian Knoth 
52510dca1793SAdrian Knoth 	snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n",
52520dca1793SAdrian Knoth 			(status1 & 0x1000000) ? 1 : 0,
52530dca1793SAdrian Knoth 			(status1 & 0x2000000) ? 1 : 0,
52540dca1793SAdrian Knoth 			texts_freq[(status1 >> 16) & 0xF]);
52550dca1793SAdrian Knoth 
52560dca1793SAdrian Knoth 	snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n",
52570dca1793SAdrian Knoth 			(status1 & 0x4000000) ? 1 : 0,
52580dca1793SAdrian Knoth 			(status1 & 0x8000000) ? 1 : 0,
52590dca1793SAdrian Knoth 			texts_freq[(status1 >> 20) & 0xF]);
52600dca1793SAdrian Knoth 
52610dca1793SAdrian Knoth 	snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n",
52620dca1793SAdrian Knoth 			(status3 & 0x400) ? 1 : 0,
52630dca1793SAdrian Knoth 			(status3 & 0x800) ? 1 : 0,
52640dca1793SAdrian Knoth 			texts_freq[(status2 >> 12) & 0xF]);
52650dca1793SAdrian Knoth 
52660dca1793SAdrian Knoth }
52670dca1793SAdrian Knoth 
52683cee5a60SRemy Bruno #ifdef CONFIG_SND_DEBUG
52693cee5a60SRemy Bruno static void
52703cee5a60SRemy Bruno snd_hdspm_proc_read_debug(struct snd_info_entry *entry,
52713cee5a60SRemy Bruno 			  struct snd_info_buffer *buffer)
52723cee5a60SRemy Bruno {
5273ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
52743cee5a60SRemy Bruno 
52753cee5a60SRemy Bruno 	int j,i;
52763cee5a60SRemy Bruno 
5277ef5fa1a4STakashi Iwai 	for (i = 0; i < 256 /* 1024*64 */; i += j) {
52783cee5a60SRemy Bruno 		snd_iprintf(buffer, "0x%08X: ", i);
52793cee5a60SRemy Bruno 		for (j = 0; j < 16; j += 4)
52803cee5a60SRemy Bruno 			snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j));
52813cee5a60SRemy Bruno 		snd_iprintf(buffer, "\n");
52823cee5a60SRemy Bruno 	}
52833cee5a60SRemy Bruno }
52843cee5a60SRemy Bruno #endif
52853cee5a60SRemy Bruno 
52863cee5a60SRemy Bruno 
52870dca1793SAdrian Knoth static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry,
52880dca1793SAdrian Knoth 			  struct snd_info_buffer *buffer)
52890dca1793SAdrian Knoth {
52900dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
52910dca1793SAdrian Knoth 	int i;
52920dca1793SAdrian Knoth 
52930dca1793SAdrian Knoth 	snd_iprintf(buffer, "# generated by hdspm\n");
52940dca1793SAdrian Knoth 
52950dca1793SAdrian Knoth 	for (i = 0; i < hdspm->max_channels_in; i++) {
52960dca1793SAdrian Knoth 		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]);
52970dca1793SAdrian Knoth 	}
52980dca1793SAdrian Knoth }
52990dca1793SAdrian Knoth 
53000dca1793SAdrian Knoth static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry,
53010dca1793SAdrian Knoth 			  struct snd_info_buffer *buffer)
53020dca1793SAdrian Knoth {
53030dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
53040dca1793SAdrian Knoth 	int i;
53050dca1793SAdrian Knoth 
53060dca1793SAdrian Knoth 	snd_iprintf(buffer, "# generated by hdspm\n");
53070dca1793SAdrian Knoth 
53080dca1793SAdrian Knoth 	for (i = 0; i < hdspm->max_channels_out; i++) {
53090dca1793SAdrian Knoth 		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]);
53100dca1793SAdrian Knoth 	}
53110dca1793SAdrian Knoth }
53120dca1793SAdrian Knoth 
53133cee5a60SRemy Bruno 
5314e23e7a14SBill Pemberton static void snd_hdspm_proc_init(struct hdspm *hdspm)
5315763f356cSTakashi Iwai {
531698274f07STakashi Iwai 	struct snd_info_entry *entry;
5317763f356cSTakashi Iwai 
53180dca1793SAdrian Knoth 	if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) {
53190dca1793SAdrian Knoth 		switch (hdspm->io_type) {
53200dca1793SAdrian Knoth 		case AES32:
5321bf850204STakashi Iwai 			snd_info_set_text_ops(entry, hdspm,
53220dca1793SAdrian Knoth 					snd_hdspm_proc_read_aes32);
53230dca1793SAdrian Knoth 			break;
53240dca1793SAdrian Knoth 		case MADI:
53250dca1793SAdrian Knoth 			snd_info_set_text_ops(entry, hdspm,
53263cee5a60SRemy Bruno 					snd_hdspm_proc_read_madi);
53270dca1793SAdrian Knoth 			break;
53280dca1793SAdrian Knoth 		case MADIface:
53290dca1793SAdrian Knoth 			/* snd_info_set_text_ops(entry, hdspm,
53300dca1793SAdrian Knoth 			 snd_hdspm_proc_read_madiface); */
53310dca1793SAdrian Knoth 			break;
53320dca1793SAdrian Knoth 		case RayDAT:
53330dca1793SAdrian Knoth 			snd_info_set_text_ops(entry, hdspm,
53340dca1793SAdrian Knoth 					snd_hdspm_proc_read_raydat);
53350dca1793SAdrian Knoth 			break;
53360dca1793SAdrian Knoth 		case AIO:
53370dca1793SAdrian Knoth 			break;
53380dca1793SAdrian Knoth 		}
53390dca1793SAdrian Knoth 	}
53400dca1793SAdrian Knoth 
53410dca1793SAdrian Knoth 	if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) {
53420dca1793SAdrian Knoth 		snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in);
53430dca1793SAdrian Knoth 	}
53440dca1793SAdrian Knoth 
53450dca1793SAdrian Knoth 	if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) {
53460dca1793SAdrian Knoth 		snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out);
53470dca1793SAdrian Knoth 	}
53480dca1793SAdrian Knoth 
53493cee5a60SRemy Bruno #ifdef CONFIG_SND_DEBUG
53503cee5a60SRemy Bruno 	/* debug file to read all hdspm registers */
53513cee5a60SRemy Bruno 	if (!snd_card_proc_new(hdspm->card, "debug", &entry))
53523cee5a60SRemy Bruno 		snd_info_set_text_ops(entry, hdspm,
53533cee5a60SRemy Bruno 				snd_hdspm_proc_read_debug);
53543cee5a60SRemy Bruno #endif
5355763f356cSTakashi Iwai }
5356763f356cSTakashi Iwai 
5357763f356cSTakashi Iwai /*------------------------------------------------------------
5358763f356cSTakashi Iwai    hdspm intitialize
5359763f356cSTakashi Iwai  ------------------------------------------------------------*/
5360763f356cSTakashi Iwai 
536198274f07STakashi Iwai static int snd_hdspm_set_defaults(struct hdspm * hdspm)
5362763f356cSTakashi Iwai {
5363763f356cSTakashi Iwai 	/* ASSUMPTION: hdspm->lock is either held, or there is no need to
5364561de31aSJoe Perches 	   hold it (e.g. during module initialization).
5365763f356cSTakashi Iwai 	   */
5366763f356cSTakashi Iwai 
5367763f356cSTakashi Iwai 	/* set defaults:       */
5368763f356cSTakashi Iwai 
53690dca1793SAdrian Knoth 	hdspm->settings_register = 0;
53700dca1793SAdrian Knoth 
53710dca1793SAdrian Knoth 	switch (hdspm->io_type) {
53720dca1793SAdrian Knoth 	case MADI:
53730dca1793SAdrian Knoth 	case MADIface:
53740dca1793SAdrian Knoth 		hdspm->control_register =
53750dca1793SAdrian Knoth 			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
53760dca1793SAdrian Knoth 		break;
53770dca1793SAdrian Knoth 
53780dca1793SAdrian Knoth 	case RayDAT:
53790dca1793SAdrian Knoth 	case AIO:
53800dca1793SAdrian Knoth 		hdspm->settings_register = 0x1 + 0x1000;
53810dca1793SAdrian Knoth 		/* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0,
53820dca1793SAdrian Knoth 		 * line_out */
53830dca1793SAdrian Knoth 		hdspm->control_register =
53840dca1793SAdrian Knoth 			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
53850dca1793SAdrian Knoth 		break;
53860dca1793SAdrian Knoth 
53870dca1793SAdrian Knoth 	case AES32:
5388ef5fa1a4STakashi Iwai 		hdspm->control_register =
5389e71b95adSAdrian Knoth 			HDSPM_ClockModeMaster |	/* Master Clock Mode on */
53900dca1793SAdrian Knoth 			hdspm_encode_latency(7) | /* latency max=8192samples */
53913cee5a60SRemy Bruno 			HDSPM_SyncRef0 |	/* AES1 is syncclock */
53923cee5a60SRemy Bruno 			HDSPM_LineOut |	/* Analog output in */
53933cee5a60SRemy Bruno 			HDSPM_Professional;  /* Professional mode */
53940dca1793SAdrian Knoth 		break;
53950dca1793SAdrian Knoth 	}
5396763f356cSTakashi Iwai 
5397763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
5398763f356cSTakashi Iwai 
53990dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
5400ffb2c3c0SRemy Bruno 		/* No control2 register for AES32 */
5401763f356cSTakashi Iwai #ifdef SNDRV_BIG_ENDIAN
5402763f356cSTakashi Iwai 		hdspm->control2_register = HDSPM_BIGENDIAN_MODE;
5403763f356cSTakashi Iwai #else
5404763f356cSTakashi Iwai 		hdspm->control2_register = 0;
5405763f356cSTakashi Iwai #endif
5406763f356cSTakashi Iwai 
5407763f356cSTakashi Iwai 		hdspm_write(hdspm, HDSPM_control2Reg, hdspm->control2_register);
5408ffb2c3c0SRemy Bruno 	}
5409763f356cSTakashi Iwai 	hdspm_compute_period_size(hdspm);
5410763f356cSTakashi Iwai 
5411763f356cSTakashi Iwai 	/* silence everything */
5412763f356cSTakashi Iwai 
5413763f356cSTakashi Iwai 	all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
5414763f356cSTakashi Iwai 
5415b2ed6326SAdrian Knoth 	if (hdspm_is_raydat_or_aio(hdspm))
54160dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
5417763f356cSTakashi Iwai 
5418763f356cSTakashi Iwai 	/* set a default rate so that the channel map is set up. */
54190dca1793SAdrian Knoth 	hdspm_set_rate(hdspm, 48000, 1);
5420763f356cSTakashi Iwai 
5421763f356cSTakashi Iwai 	return 0;
5422763f356cSTakashi Iwai }
5423763f356cSTakashi Iwai 
5424763f356cSTakashi Iwai 
5425763f356cSTakashi Iwai /*------------------------------------------------------------
5426561de31aSJoe Perches    interrupt
5427763f356cSTakashi Iwai  ------------------------------------------------------------*/
5428763f356cSTakashi Iwai 
54297d12e780SDavid Howells static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
5430763f356cSTakashi Iwai {
543198274f07STakashi Iwai 	struct hdspm *hdspm = (struct hdspm *) dev_id;
5432763f356cSTakashi Iwai 	unsigned int status;
54330dca1793SAdrian Knoth 	int i, audio, midi, schedule = 0;
54340dca1793SAdrian Knoth 	/* cycles_t now; */
5435763f356cSTakashi Iwai 
5436763f356cSTakashi Iwai 	status = hdspm_read(hdspm, HDSPM_statusRegister);
5437763f356cSTakashi Iwai 
5438763f356cSTakashi Iwai 	audio = status & HDSPM_audioIRQPending;
54390dca1793SAdrian Knoth 	midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending |
54400dca1793SAdrian Knoth 			HDSPM_midi2IRQPending | HDSPM_midi3IRQPending);
5441763f356cSTakashi Iwai 
54420dca1793SAdrian Knoth 	/* now = get_cycles(); */
54430dca1793SAdrian Knoth 	/**
54440dca1793SAdrian Knoth 	 *   LAT_2..LAT_0 period  counter (win)  counter (mac)
54450dca1793SAdrian Knoth 	 *          6       4096   ~256053425     ~514672358
54460dca1793SAdrian Knoth 	 *          5       2048   ~128024983     ~257373821
54470dca1793SAdrian Knoth 	 *          4       1024    ~64023706     ~128718089
54480dca1793SAdrian Knoth 	 *          3        512    ~32005945      ~64385999
54490dca1793SAdrian Knoth 	 *          2        256    ~16003039      ~32260176
54500dca1793SAdrian Knoth 	 *          1        128     ~7998738      ~16194507
54510dca1793SAdrian Knoth 	 *          0         64     ~3998231       ~8191558
54520dca1793SAdrian Knoth 	 **/
54530dca1793SAdrian Knoth 	/*
54540dca1793SAdrian Knoth 	   snd_printk(KERN_INFO "snd_hdspm_interrupt %llu @ %llx\n",
54550dca1793SAdrian Knoth 	   now-hdspm->last_interrupt, status & 0xFFC0);
54560dca1793SAdrian Knoth 	   hdspm->last_interrupt = now;
54570dca1793SAdrian Knoth 	*/
54580dca1793SAdrian Knoth 
54590dca1793SAdrian Knoth 	if (!audio && !midi)
5460763f356cSTakashi Iwai 		return IRQ_NONE;
5461763f356cSTakashi Iwai 
5462763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_interruptConfirmation, 0);
5463763f356cSTakashi Iwai 	hdspm->irq_count++;
5464763f356cSTakashi Iwai 
5465763f356cSTakashi Iwai 
5466763f356cSTakashi Iwai 	if (audio) {
5467763f356cSTakashi Iwai 		if (hdspm->capture_substream)
5468ef5fa1a4STakashi Iwai 			snd_pcm_period_elapsed(hdspm->capture_substream);
5469763f356cSTakashi Iwai 
5470763f356cSTakashi Iwai 		if (hdspm->playback_substream)
5471ef5fa1a4STakashi Iwai 			snd_pcm_period_elapsed(hdspm->playback_substream);
5472763f356cSTakashi Iwai 	}
5473763f356cSTakashi Iwai 
54740dca1793SAdrian Knoth 	if (midi) {
54750dca1793SAdrian Knoth 		i = 0;
54760dca1793SAdrian Knoth 		while (i < hdspm->midiPorts) {
54770dca1793SAdrian Knoth 			if ((hdspm_read(hdspm,
54780dca1793SAdrian Knoth 				hdspm->midi[i].statusIn) & 0xff) &&
54790dca1793SAdrian Knoth 					(status & hdspm->midi[i].irq)) {
54800dca1793SAdrian Knoth 				/* we disable interrupts for this input until
54810dca1793SAdrian Knoth 				 * processing is done
5482ef5fa1a4STakashi Iwai 				 */
54830dca1793SAdrian Knoth 				hdspm->control_register &= ~hdspm->midi[i].ie;
5484763f356cSTakashi Iwai 				hdspm_write(hdspm, HDSPM_controlRegister,
5485763f356cSTakashi Iwai 						hdspm->control_register);
54860dca1793SAdrian Knoth 				hdspm->midi[i].pending = 1;
5487763f356cSTakashi Iwai 				schedule = 1;
5488763f356cSTakashi Iwai 			}
54890dca1793SAdrian Knoth 
54900dca1793SAdrian Knoth 			i++;
5491763f356cSTakashi Iwai 		}
54920dca1793SAdrian Knoth 
5493763f356cSTakashi Iwai 		if (schedule)
54940dca1793SAdrian Knoth 			tasklet_hi_schedule(&hdspm->midi_tasklet);
54950dca1793SAdrian Knoth 	}
54960dca1793SAdrian Knoth 
5497763f356cSTakashi Iwai 	return IRQ_HANDLED;
5498763f356cSTakashi Iwai }
5499763f356cSTakashi Iwai 
5500763f356cSTakashi Iwai /*------------------------------------------------------------
5501763f356cSTakashi Iwai    pcm interface
5502763f356cSTakashi Iwai   ------------------------------------------------------------*/
5503763f356cSTakashi Iwai 
5504763f356cSTakashi Iwai 
55050dca1793SAdrian Knoth static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream
55060dca1793SAdrian Knoth 					      *substream)
5507763f356cSTakashi Iwai {
550898274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5509763f356cSTakashi Iwai 	return hdspm_hw_pointer(hdspm);
5510763f356cSTakashi Iwai }
5511763f356cSTakashi Iwai 
5512763f356cSTakashi Iwai 
551398274f07STakashi Iwai static int snd_hdspm_reset(struct snd_pcm_substream *substream)
5514763f356cSTakashi Iwai {
551598274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
551698274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
551798274f07STakashi Iwai 	struct snd_pcm_substream *other;
5518763f356cSTakashi Iwai 
5519763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5520763f356cSTakashi Iwai 		other = hdspm->capture_substream;
5521763f356cSTakashi Iwai 	else
5522763f356cSTakashi Iwai 		other = hdspm->playback_substream;
5523763f356cSTakashi Iwai 
5524763f356cSTakashi Iwai 	if (hdspm->running)
5525763f356cSTakashi Iwai 		runtime->status->hw_ptr = hdspm_hw_pointer(hdspm);
5526763f356cSTakashi Iwai 	else
5527763f356cSTakashi Iwai 		runtime->status->hw_ptr = 0;
5528763f356cSTakashi Iwai 	if (other) {
552998274f07STakashi Iwai 		struct snd_pcm_substream *s;
553098274f07STakashi Iwai 		struct snd_pcm_runtime *oruntime = other->runtime;
5531ef991b95STakashi Iwai 		snd_pcm_group_for_each_entry(s, substream) {
5532763f356cSTakashi Iwai 			if (s == other) {
5533763f356cSTakashi Iwai 				oruntime->status->hw_ptr =
5534763f356cSTakashi Iwai 					runtime->status->hw_ptr;
5535763f356cSTakashi Iwai 				break;
5536763f356cSTakashi Iwai 			}
5537763f356cSTakashi Iwai 		}
5538763f356cSTakashi Iwai 	}
5539763f356cSTakashi Iwai 	return 0;
5540763f356cSTakashi Iwai }
5541763f356cSTakashi Iwai 
554298274f07STakashi Iwai static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
554398274f07STakashi Iwai 			       struct snd_pcm_hw_params *params)
5544763f356cSTakashi Iwai {
554598274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5546763f356cSTakashi Iwai 	int err;
5547763f356cSTakashi Iwai 	int i;
5548763f356cSTakashi Iwai 	pid_t this_pid;
5549763f356cSTakashi Iwai 	pid_t other_pid;
5550763f356cSTakashi Iwai 
5551763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5552763f356cSTakashi Iwai 
5553763f356cSTakashi Iwai 	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5554763f356cSTakashi Iwai 		this_pid = hdspm->playback_pid;
5555763f356cSTakashi Iwai 		other_pid = hdspm->capture_pid;
5556763f356cSTakashi Iwai 	} else {
5557763f356cSTakashi Iwai 		this_pid = hdspm->capture_pid;
5558763f356cSTakashi Iwai 		other_pid = hdspm->playback_pid;
5559763f356cSTakashi Iwai 	}
5560763f356cSTakashi Iwai 
5561ef5fa1a4STakashi Iwai 	if (other_pid > 0 && this_pid != other_pid) {
5562763f356cSTakashi Iwai 
5563763f356cSTakashi Iwai 		/* The other stream is open, and not by the same
5564763f356cSTakashi Iwai 		   task as this one. Make sure that the parameters
5565763f356cSTakashi Iwai 		   that matter are the same.
5566763f356cSTakashi Iwai 		   */
5567763f356cSTakashi Iwai 
5568763f356cSTakashi Iwai 		if (params_rate(params) != hdspm->system_sample_rate) {
5569763f356cSTakashi Iwai 			spin_unlock_irq(&hdspm->lock);
5570763f356cSTakashi Iwai 			_snd_pcm_hw_param_setempty(params,
5571763f356cSTakashi Iwai 					SNDRV_PCM_HW_PARAM_RATE);
5572763f356cSTakashi Iwai 			return -EBUSY;
5573763f356cSTakashi Iwai 		}
5574763f356cSTakashi Iwai 
5575763f356cSTakashi Iwai 		if (params_period_size(params) != hdspm->period_bytes / 4) {
5576763f356cSTakashi Iwai 			spin_unlock_irq(&hdspm->lock);
5577763f356cSTakashi Iwai 			_snd_pcm_hw_param_setempty(params,
5578763f356cSTakashi Iwai 					SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
5579763f356cSTakashi Iwai 			return -EBUSY;
5580763f356cSTakashi Iwai 		}
5581763f356cSTakashi Iwai 
5582763f356cSTakashi Iwai 	}
5583763f356cSTakashi Iwai 	/* We're fine. */
5584763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5585763f356cSTakashi Iwai 
5586763f356cSTakashi Iwai 	/* how to make sure that the rate matches an externally-set one ?   */
5587763f356cSTakashi Iwai 
5588763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5589ef5fa1a4STakashi Iwai 	err = hdspm_set_rate(hdspm, params_rate(params), 0);
5590ef5fa1a4STakashi Iwai 	if (err < 0) {
55910dca1793SAdrian Knoth 		snd_printk(KERN_INFO "err on hdspm_set_rate: %d\n", err);
5592763f356cSTakashi Iwai 		spin_unlock_irq(&hdspm->lock);
5593763f356cSTakashi Iwai 		_snd_pcm_hw_param_setempty(params,
5594763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_RATE);
5595763f356cSTakashi Iwai 		return err;
5596763f356cSTakashi Iwai 	}
5597763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5598763f356cSTakashi Iwai 
5599ef5fa1a4STakashi Iwai 	err = hdspm_set_interrupt_interval(hdspm,
5600ef5fa1a4STakashi Iwai 			params_period_size(params));
5601ef5fa1a4STakashi Iwai 	if (err < 0) {
56020dca1793SAdrian Knoth 		snd_printk(KERN_INFO "err on hdspm_set_interrupt_interval: %d\n", err);
5603763f356cSTakashi Iwai 		_snd_pcm_hw_param_setempty(params,
5604763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
5605763f356cSTakashi Iwai 		return err;
5606763f356cSTakashi Iwai 	}
5607763f356cSTakashi Iwai 
5608ef5fa1a4STakashi Iwai 	/* Memory allocation, takashi's method, dont know if we should
5609ef5fa1a4STakashi Iwai 	 * spinlock
5610ef5fa1a4STakashi Iwai 	 */
5611763f356cSTakashi Iwai 	/* malloc all buffer even if not enabled to get sure */
5612ffb2c3c0SRemy Bruno 	/* Update for MADI rev 204: we need to allocate for all channels,
5613ffb2c3c0SRemy Bruno 	 * otherwise it doesn't work at 96kHz */
56140dca1793SAdrian Knoth 
5615763f356cSTakashi Iwai 	err =
5616ffb2c3c0SRemy Bruno 		snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
56170dca1793SAdrian Knoth 	if (err < 0) {
56180dca1793SAdrian Knoth 		snd_printk(KERN_INFO "err on snd_pcm_lib_malloc_pages: %d\n", err);
5619763f356cSTakashi Iwai 		return err;
56200dca1793SAdrian Knoth 	}
5621763f356cSTakashi Iwai 
5622763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5623763f356cSTakashi Iwai 
562477a23f26STakashi Iwai 		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut,
5625763f356cSTakashi Iwai 				params_channels(params));
5626763f356cSTakashi Iwai 
5627763f356cSTakashi Iwai 		for (i = 0; i < params_channels(params); ++i)
5628763f356cSTakashi Iwai 			snd_hdspm_enable_out(hdspm, i, 1);
5629763f356cSTakashi Iwai 
5630763f356cSTakashi Iwai 		hdspm->playback_buffer =
5631763f356cSTakashi Iwai 			(unsigned char *) substream->runtime->dma_area;
563254bf5dd9STakashi Iwai 		snd_printdd("Allocated sample buffer for playback at %p\n",
56333cee5a60SRemy Bruno 				hdspm->playback_buffer);
5634763f356cSTakashi Iwai 	} else {
563577a23f26STakashi Iwai 		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn,
5636763f356cSTakashi Iwai 				params_channels(params));
5637763f356cSTakashi Iwai 
5638763f356cSTakashi Iwai 		for (i = 0; i < params_channels(params); ++i)
5639763f356cSTakashi Iwai 			snd_hdspm_enable_in(hdspm, i, 1);
5640763f356cSTakashi Iwai 
5641763f356cSTakashi Iwai 		hdspm->capture_buffer =
5642763f356cSTakashi Iwai 			(unsigned char *) substream->runtime->dma_area;
564354bf5dd9STakashi Iwai 		snd_printdd("Allocated sample buffer for capture at %p\n",
56443cee5a60SRemy Bruno 				hdspm->capture_buffer);
5645763f356cSTakashi Iwai 	}
56460dca1793SAdrian Knoth 
56473cee5a60SRemy Bruno 	/*
56483cee5a60SRemy Bruno 	   snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
56493cee5a60SRemy Bruno 	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
56503cee5a60SRemy Bruno 	   "playback" : "capture",
565177a23f26STakashi Iwai 	   snd_pcm_sgbuf_get_addr(substream, 0));
56523cee5a60SRemy Bruno 	   */
5653ffb2c3c0SRemy Bruno 	/*
5654ffb2c3c0SRemy Bruno 	   snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n",
5655ffb2c3c0SRemy Bruno 	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
5656ffb2c3c0SRemy Bruno 	   "playback" : "capture",
5657ffb2c3c0SRemy Bruno 	   params_rate(params), params_channels(params),
5658ffb2c3c0SRemy Bruno 	   params_buffer_size(params));
5659ffb2c3c0SRemy Bruno 	   */
56600dca1793SAdrian Knoth 
56610dca1793SAdrian Knoth 
56623ac9b0acSAdrian Knoth 	/*  For AES cards, the float format bit is the same as the
56633ac9b0acSAdrian Knoth 	 *  preferred sync reference. Since we don't want to break
56643ac9b0acSAdrian Knoth 	 *  sync settings, we have to skip the remaining part of this
56653ac9b0acSAdrian Knoth 	 *  function.
56663ac9b0acSAdrian Knoth 	 */
56673ac9b0acSAdrian Knoth 	if (hdspm->io_type == AES32) {
56683ac9b0acSAdrian Knoth 		return 0;
56693ac9b0acSAdrian Knoth 	}
56703ac9b0acSAdrian Knoth 
56713ac9b0acSAdrian Knoth 
56720dca1793SAdrian Knoth 	/* Switch to native float format if requested */
56730dca1793SAdrian Knoth 	if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
56740dca1793SAdrian Knoth 		if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
56750dca1793SAdrian Knoth 			snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE float format.\n");
56760dca1793SAdrian Knoth 
56770dca1793SAdrian Knoth 		hdspm->control_register |= HDSPe_FLOAT_FORMAT;
56780dca1793SAdrian Knoth 	} else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) {
56790dca1793SAdrian Knoth 		if (hdspm->control_register & HDSPe_FLOAT_FORMAT)
56800dca1793SAdrian Knoth 			snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE integer format.\n");
56810dca1793SAdrian Knoth 
56820dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPe_FLOAT_FORMAT;
56830dca1793SAdrian Knoth 	}
56840dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
56850dca1793SAdrian Knoth 
5686763f356cSTakashi Iwai 	return 0;
5687763f356cSTakashi Iwai }
5688763f356cSTakashi Iwai 
568998274f07STakashi Iwai static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
5690763f356cSTakashi Iwai {
5691763f356cSTakashi Iwai 	int i;
569298274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5693763f356cSTakashi Iwai 
5694763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5695763f356cSTakashi Iwai 
5696763f356cSTakashi Iwai 		/* params_channels(params) should be enough,
5697763f356cSTakashi Iwai 		   but to get sure in case of error */
56980dca1793SAdrian Knoth 		for (i = 0; i < hdspm->max_channels_out; ++i)
5699763f356cSTakashi Iwai 			snd_hdspm_enable_out(hdspm, i, 0);
5700763f356cSTakashi Iwai 
5701763f356cSTakashi Iwai 		hdspm->playback_buffer = NULL;
5702763f356cSTakashi Iwai 	} else {
57030dca1793SAdrian Knoth 		for (i = 0; i < hdspm->max_channels_in; ++i)
5704763f356cSTakashi Iwai 			snd_hdspm_enable_in(hdspm, i, 0);
5705763f356cSTakashi Iwai 
5706763f356cSTakashi Iwai 		hdspm->capture_buffer = NULL;
5707763f356cSTakashi Iwai 
5708763f356cSTakashi Iwai 	}
5709763f356cSTakashi Iwai 
5710763f356cSTakashi Iwai 	snd_pcm_lib_free_pages(substream);
5711763f356cSTakashi Iwai 
5712763f356cSTakashi Iwai 	return 0;
5713763f356cSTakashi Iwai }
5714763f356cSTakashi Iwai 
57150dca1793SAdrian Knoth 
571698274f07STakashi Iwai static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
571798274f07STakashi Iwai 		struct snd_pcm_channel_info *info)
5718763f356cSTakashi Iwai {
571998274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5720763f356cSTakashi Iwai 
57210dca1793SAdrian Knoth 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
57220dca1793SAdrian Knoth 		if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) {
57230dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel out of range (%d)\n", info->channel);
5724da3cec35STakashi Iwai 			return -EINVAL;
57250dca1793SAdrian Knoth 		}
5726763f356cSTakashi Iwai 
57270dca1793SAdrian Knoth 		if (hdspm->channel_map_out[info->channel] < 0) {
57280dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel %d mapped out\n", info->channel);
5729763f356cSTakashi Iwai 			return -EINVAL;
57300dca1793SAdrian Knoth 		}
5731763f356cSTakashi Iwai 
57320dca1793SAdrian Knoth 		info->offset = hdspm->channel_map_out[info->channel] *
57330dca1793SAdrian Knoth 			HDSPM_CHANNEL_BUFFER_BYTES;
57340dca1793SAdrian Knoth 	} else {
57350dca1793SAdrian Knoth 		if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) {
57360dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel out of range (%d)\n", info->channel);
57370dca1793SAdrian Knoth 			return -EINVAL;
57380dca1793SAdrian Knoth 		}
57390dca1793SAdrian Knoth 
57400dca1793SAdrian Knoth 		if (hdspm->channel_map_in[info->channel] < 0) {
57410dca1793SAdrian Knoth 			snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel %d mapped out\n", info->channel);
57420dca1793SAdrian Knoth 			return -EINVAL;
57430dca1793SAdrian Knoth 		}
57440dca1793SAdrian Knoth 
57450dca1793SAdrian Knoth 		info->offset = hdspm->channel_map_in[info->channel] *
57460dca1793SAdrian Knoth 			HDSPM_CHANNEL_BUFFER_BYTES;
57470dca1793SAdrian Knoth 	}
57480dca1793SAdrian Knoth 
5749763f356cSTakashi Iwai 	info->first = 0;
5750763f356cSTakashi Iwai 	info->step = 32;
5751763f356cSTakashi Iwai 	return 0;
5752763f356cSTakashi Iwai }
5753763f356cSTakashi Iwai 
57540dca1793SAdrian Knoth 
575598274f07STakashi Iwai static int snd_hdspm_ioctl(struct snd_pcm_substream *substream,
5756763f356cSTakashi Iwai 		unsigned int cmd, void *arg)
5757763f356cSTakashi Iwai {
5758763f356cSTakashi Iwai 	switch (cmd) {
5759763f356cSTakashi Iwai 	case SNDRV_PCM_IOCTL1_RESET:
5760763f356cSTakashi Iwai 		return snd_hdspm_reset(substream);
5761763f356cSTakashi Iwai 
5762763f356cSTakashi Iwai 	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
5763763f356cSTakashi Iwai 		{
576498274f07STakashi Iwai 			struct snd_pcm_channel_info *info = arg;
5765763f356cSTakashi Iwai 			return snd_hdspm_channel_info(substream, info);
5766763f356cSTakashi Iwai 		}
5767763f356cSTakashi Iwai 	default:
5768763f356cSTakashi Iwai 		break;
5769763f356cSTakashi Iwai 	}
5770763f356cSTakashi Iwai 
5771763f356cSTakashi Iwai 	return snd_pcm_lib_ioctl(substream, cmd, arg);
5772763f356cSTakashi Iwai }
5773763f356cSTakashi Iwai 
577498274f07STakashi Iwai static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
5775763f356cSTakashi Iwai {
577698274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
577798274f07STakashi Iwai 	struct snd_pcm_substream *other;
5778763f356cSTakashi Iwai 	int running;
5779763f356cSTakashi Iwai 
5780763f356cSTakashi Iwai 	spin_lock(&hdspm->lock);
5781763f356cSTakashi Iwai 	running = hdspm->running;
5782763f356cSTakashi Iwai 	switch (cmd) {
5783763f356cSTakashi Iwai 	case SNDRV_PCM_TRIGGER_START:
5784763f356cSTakashi Iwai 		running |= 1 << substream->stream;
5785763f356cSTakashi Iwai 		break;
5786763f356cSTakashi Iwai 	case SNDRV_PCM_TRIGGER_STOP:
5787763f356cSTakashi Iwai 		running &= ~(1 << substream->stream);
5788763f356cSTakashi Iwai 		break;
5789763f356cSTakashi Iwai 	default:
5790763f356cSTakashi Iwai 		snd_BUG();
5791763f356cSTakashi Iwai 		spin_unlock(&hdspm->lock);
5792763f356cSTakashi Iwai 		return -EINVAL;
5793763f356cSTakashi Iwai 	}
5794763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5795763f356cSTakashi Iwai 		other = hdspm->capture_substream;
5796763f356cSTakashi Iwai 	else
5797763f356cSTakashi Iwai 		other = hdspm->playback_substream;
5798763f356cSTakashi Iwai 
5799763f356cSTakashi Iwai 	if (other) {
580098274f07STakashi Iwai 		struct snd_pcm_substream *s;
5801ef991b95STakashi Iwai 		snd_pcm_group_for_each_entry(s, substream) {
5802763f356cSTakashi Iwai 			if (s == other) {
5803763f356cSTakashi Iwai 				snd_pcm_trigger_done(s, substream);
5804763f356cSTakashi Iwai 				if (cmd == SNDRV_PCM_TRIGGER_START)
5805763f356cSTakashi Iwai 					running |= 1 << s->stream;
5806763f356cSTakashi Iwai 				else
5807763f356cSTakashi Iwai 					running &= ~(1 << s->stream);
5808763f356cSTakashi Iwai 				goto _ok;
5809763f356cSTakashi Iwai 			}
5810763f356cSTakashi Iwai 		}
5811763f356cSTakashi Iwai 		if (cmd == SNDRV_PCM_TRIGGER_START) {
5812763f356cSTakashi Iwai 			if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
5813763f356cSTakashi Iwai 					&& substream->stream ==
5814763f356cSTakashi Iwai 					SNDRV_PCM_STREAM_CAPTURE)
5815763f356cSTakashi Iwai 				hdspm_silence_playback(hdspm);
5816763f356cSTakashi Iwai 		} else {
5817763f356cSTakashi Iwai 			if (running &&
5818763f356cSTakashi Iwai 				substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5819763f356cSTakashi Iwai 				hdspm_silence_playback(hdspm);
5820763f356cSTakashi Iwai 		}
5821763f356cSTakashi Iwai 	} else {
5822763f356cSTakashi Iwai 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
5823763f356cSTakashi Iwai 			hdspm_silence_playback(hdspm);
5824763f356cSTakashi Iwai 	}
5825763f356cSTakashi Iwai _ok:
5826763f356cSTakashi Iwai 	snd_pcm_trigger_done(substream, substream);
5827763f356cSTakashi Iwai 	if (!hdspm->running && running)
5828763f356cSTakashi Iwai 		hdspm_start_audio(hdspm);
5829763f356cSTakashi Iwai 	else if (hdspm->running && !running)
5830763f356cSTakashi Iwai 		hdspm_stop_audio(hdspm);
5831763f356cSTakashi Iwai 	hdspm->running = running;
5832763f356cSTakashi Iwai 	spin_unlock(&hdspm->lock);
5833763f356cSTakashi Iwai 
5834763f356cSTakashi Iwai 	return 0;
5835763f356cSTakashi Iwai }
5836763f356cSTakashi Iwai 
583798274f07STakashi Iwai static int snd_hdspm_prepare(struct snd_pcm_substream *substream)
5838763f356cSTakashi Iwai {
5839763f356cSTakashi Iwai 	return 0;
5840763f356cSTakashi Iwai }
5841763f356cSTakashi Iwai 
584298274f07STakashi Iwai static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
5843763f356cSTakashi Iwai 	.info = (SNDRV_PCM_INFO_MMAP |
5844763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_MMAP_VALID |
5845763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_NONINTERLEAVED |
5846763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_DOUBLE),
5847763f356cSTakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
5848763f356cSTakashi Iwai 	.rates = (SNDRV_PCM_RATE_32000 |
5849763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_44100 |
5850763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_48000 |
5851763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_64000 |
58523cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
58533cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 ),
5854763f356cSTakashi Iwai 	.rate_min = 32000,
58553cee5a60SRemy Bruno 	.rate_max = 192000,
5856763f356cSTakashi Iwai 	.channels_min = 1,
5857763f356cSTakashi Iwai 	.channels_max = HDSPM_MAX_CHANNELS,
5858763f356cSTakashi Iwai 	.buffer_bytes_max =
5859763f356cSTakashi Iwai 	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
58601b6fa108SAdrian Knoth 	.period_bytes_min = (32 * 4),
586152e6fb48STakashi Iwai 	.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
5862763f356cSTakashi Iwai 	.periods_min = 2,
58630dca1793SAdrian Knoth 	.periods_max = 512,
5864763f356cSTakashi Iwai 	.fifo_size = 0
5865763f356cSTakashi Iwai };
5866763f356cSTakashi Iwai 
586798274f07STakashi Iwai static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
5868763f356cSTakashi Iwai 	.info = (SNDRV_PCM_INFO_MMAP |
5869763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_MMAP_VALID |
5870763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_NONINTERLEAVED |
5871763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_SYNC_START),
5872763f356cSTakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
5873763f356cSTakashi Iwai 	.rates = (SNDRV_PCM_RATE_32000 |
5874763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_44100 |
5875763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_48000 |
5876763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_64000 |
58773cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
58783cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
5879763f356cSTakashi Iwai 	.rate_min = 32000,
58803cee5a60SRemy Bruno 	.rate_max = 192000,
5881763f356cSTakashi Iwai 	.channels_min = 1,
5882763f356cSTakashi Iwai 	.channels_max = HDSPM_MAX_CHANNELS,
5883763f356cSTakashi Iwai 	.buffer_bytes_max =
5884763f356cSTakashi Iwai 	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
58851b6fa108SAdrian Knoth 	.period_bytes_min = (32 * 4),
588652e6fb48STakashi Iwai 	.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
5887763f356cSTakashi Iwai 	.periods_min = 2,
58880dca1793SAdrian Knoth 	.periods_max = 512,
5889763f356cSTakashi Iwai 	.fifo_size = 0
5890763f356cSTakashi Iwai };
5891763f356cSTakashi Iwai 
58920dca1793SAdrian Knoth static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params,
589398274f07STakashi Iwai 					   struct snd_pcm_hw_rule *rule)
5894763f356cSTakashi Iwai {
589598274f07STakashi Iwai 	struct hdspm *hdspm = rule->private;
589698274f07STakashi Iwai 	struct snd_interval *c =
5897763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
589898274f07STakashi Iwai 	struct snd_interval *r =
5899763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5900763f356cSTakashi Iwai 
59010dca1793SAdrian Knoth 	if (r->min > 96000 && r->max <= 192000) {
590298274f07STakashi Iwai 		struct snd_interval t = {
59030dca1793SAdrian Knoth 			.min = hdspm->qs_in_channels,
59040dca1793SAdrian Knoth 			.max = hdspm->qs_in_channels,
59050dca1793SAdrian Knoth 			.integer = 1,
59060dca1793SAdrian Knoth 		};
59070dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
59080dca1793SAdrian Knoth 	} else if (r->min > 48000 && r->max <= 96000) {
59090dca1793SAdrian Knoth 		struct snd_interval t = {
59100dca1793SAdrian Knoth 			.min = hdspm->ds_in_channels,
59110dca1793SAdrian Knoth 			.max = hdspm->ds_in_channels,
5912763f356cSTakashi Iwai 			.integer = 1,
5913763f356cSTakashi Iwai 		};
5914763f356cSTakashi Iwai 		return snd_interval_refine(c, &t);
5915763f356cSTakashi Iwai 	} else if (r->max < 64000) {
591698274f07STakashi Iwai 		struct snd_interval t = {
59170dca1793SAdrian Knoth 			.min = hdspm->ss_in_channels,
59180dca1793SAdrian Knoth 			.max = hdspm->ss_in_channels,
5919763f356cSTakashi Iwai 			.integer = 1,
5920763f356cSTakashi Iwai 		};
5921763f356cSTakashi Iwai 		return snd_interval_refine(c, &t);
5922763f356cSTakashi Iwai 	}
59230dca1793SAdrian Knoth 
5924763f356cSTakashi Iwai 	return 0;
5925763f356cSTakashi Iwai }
5926763f356cSTakashi Iwai 
59270dca1793SAdrian Knoth static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params,
592898274f07STakashi Iwai 					   struct snd_pcm_hw_rule * rule)
5929763f356cSTakashi Iwai {
593098274f07STakashi Iwai 	struct hdspm *hdspm = rule->private;
593198274f07STakashi Iwai 	struct snd_interval *c =
5932763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
593398274f07STakashi Iwai 	struct snd_interval *r =
5934763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5935763f356cSTakashi Iwai 
59360dca1793SAdrian Knoth 	if (r->min > 96000 && r->max <= 192000) {
59370dca1793SAdrian Knoth 		struct snd_interval t = {
59380dca1793SAdrian Knoth 			.min = hdspm->qs_out_channels,
59390dca1793SAdrian Knoth 			.max = hdspm->qs_out_channels,
59400dca1793SAdrian Knoth 			.integer = 1,
59410dca1793SAdrian Knoth 		};
59420dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
59430dca1793SAdrian Knoth 	} else if (r->min > 48000 && r->max <= 96000) {
59440dca1793SAdrian Knoth 		struct snd_interval t = {
59450dca1793SAdrian Knoth 			.min = hdspm->ds_out_channels,
59460dca1793SAdrian Knoth 			.max = hdspm->ds_out_channels,
59470dca1793SAdrian Knoth 			.integer = 1,
59480dca1793SAdrian Knoth 		};
59490dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
59500dca1793SAdrian Knoth 	} else if (r->max < 64000) {
59510dca1793SAdrian Knoth 		struct snd_interval t = {
59520dca1793SAdrian Knoth 			.min = hdspm->ss_out_channels,
59530dca1793SAdrian Knoth 			.max = hdspm->ss_out_channels,
59540dca1793SAdrian Knoth 			.integer = 1,
59550dca1793SAdrian Knoth 		};
59560dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
59570dca1793SAdrian Knoth 	} else {
59580dca1793SAdrian Knoth 	}
59590dca1793SAdrian Knoth 	return 0;
59600dca1793SAdrian Knoth }
59610dca1793SAdrian Knoth 
59620dca1793SAdrian Knoth static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params,
59630dca1793SAdrian Knoth 					   struct snd_pcm_hw_rule * rule)
59640dca1793SAdrian Knoth {
59650dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
59660dca1793SAdrian Knoth 	struct snd_interval *c =
59670dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
59680dca1793SAdrian Knoth 	struct snd_interval *r =
59690dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
59700dca1793SAdrian Knoth 
59710dca1793SAdrian Knoth 	if (c->min >= hdspm->ss_in_channels) {
597298274f07STakashi Iwai 		struct snd_interval t = {
5973763f356cSTakashi Iwai 			.min = 32000,
5974763f356cSTakashi Iwai 			.max = 48000,
5975763f356cSTakashi Iwai 			.integer = 1,
5976763f356cSTakashi Iwai 		};
5977763f356cSTakashi Iwai 		return snd_interval_refine(r, &t);
59780dca1793SAdrian Knoth 	} else if (c->max <= hdspm->qs_in_channels) {
59790dca1793SAdrian Knoth 		struct snd_interval t = {
59800dca1793SAdrian Knoth 			.min = 128000,
59810dca1793SAdrian Knoth 			.max = 192000,
59820dca1793SAdrian Knoth 			.integer = 1,
59830dca1793SAdrian Knoth 		};
59840dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
59850dca1793SAdrian Knoth 	} else if (c->max <= hdspm->ds_in_channels) {
598698274f07STakashi Iwai 		struct snd_interval t = {
5987763f356cSTakashi Iwai 			.min = 64000,
5988763f356cSTakashi Iwai 			.max = 96000,
5989763f356cSTakashi Iwai 			.integer = 1,
5990763f356cSTakashi Iwai 		};
5991763f356cSTakashi Iwai 		return snd_interval_refine(r, &t);
5992763f356cSTakashi Iwai 	}
59930dca1793SAdrian Knoth 
59940dca1793SAdrian Knoth 	return 0;
59950dca1793SAdrian Knoth }
59960dca1793SAdrian Knoth static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params,
59970dca1793SAdrian Knoth 					   struct snd_pcm_hw_rule *rule)
59980dca1793SAdrian Knoth {
59990dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
60000dca1793SAdrian Knoth 	struct snd_interval *c =
60010dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
60020dca1793SAdrian Knoth 	struct snd_interval *r =
60030dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
60040dca1793SAdrian Knoth 
60050dca1793SAdrian Knoth 	if (c->min >= hdspm->ss_out_channels) {
60060dca1793SAdrian Knoth 		struct snd_interval t = {
60070dca1793SAdrian Knoth 			.min = 32000,
60080dca1793SAdrian Knoth 			.max = 48000,
60090dca1793SAdrian Knoth 			.integer = 1,
60100dca1793SAdrian Knoth 		};
60110dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
60120dca1793SAdrian Knoth 	} else if (c->max <= hdspm->qs_out_channels) {
60130dca1793SAdrian Knoth 		struct snd_interval t = {
60140dca1793SAdrian Knoth 			.min = 128000,
60150dca1793SAdrian Knoth 			.max = 192000,
60160dca1793SAdrian Knoth 			.integer = 1,
60170dca1793SAdrian Knoth 		};
60180dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
60190dca1793SAdrian Knoth 	} else if (c->max <= hdspm->ds_out_channels) {
60200dca1793SAdrian Knoth 		struct snd_interval t = {
60210dca1793SAdrian Knoth 			.min = 64000,
60220dca1793SAdrian Knoth 			.max = 96000,
60230dca1793SAdrian Knoth 			.integer = 1,
60240dca1793SAdrian Knoth 		};
60250dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
60260dca1793SAdrian Knoth 	}
60270dca1793SAdrian Knoth 
6028763f356cSTakashi Iwai 	return 0;
6029763f356cSTakashi Iwai }
6030763f356cSTakashi Iwai 
60310dca1793SAdrian Knoth static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params,
6032ffb2c3c0SRemy Bruno 				      struct snd_pcm_hw_rule *rule)
6033ffb2c3c0SRemy Bruno {
6034ffb2c3c0SRemy Bruno 	unsigned int list[3];
6035ffb2c3c0SRemy Bruno 	struct hdspm *hdspm = rule->private;
6036ffb2c3c0SRemy Bruno 	struct snd_interval *c = hw_param_interval(params,
6037ffb2c3c0SRemy Bruno 			SNDRV_PCM_HW_PARAM_CHANNELS);
60380dca1793SAdrian Knoth 
60390dca1793SAdrian Knoth 	list[0] = hdspm->qs_in_channels;
60400dca1793SAdrian Knoth 	list[1] = hdspm->ds_in_channels;
60410dca1793SAdrian Knoth 	list[2] = hdspm->ss_in_channels;
6042ffb2c3c0SRemy Bruno 	return snd_interval_list(c, 3, list, 0);
6043ffb2c3c0SRemy Bruno }
60440dca1793SAdrian Knoth 
60450dca1793SAdrian Knoth static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params,
60460dca1793SAdrian Knoth 				      struct snd_pcm_hw_rule *rule)
60470dca1793SAdrian Knoth {
60480dca1793SAdrian Knoth 	unsigned int list[3];
60490dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
60500dca1793SAdrian Knoth 	struct snd_interval *c = hw_param_interval(params,
60510dca1793SAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS);
60520dca1793SAdrian Knoth 
60530dca1793SAdrian Knoth 	list[0] = hdspm->qs_out_channels;
60540dca1793SAdrian Knoth 	list[1] = hdspm->ds_out_channels;
60550dca1793SAdrian Knoth 	list[2] = hdspm->ss_out_channels;
60560dca1793SAdrian Knoth 	return snd_interval_list(c, 3, list, 0);
6057ffb2c3c0SRemy Bruno }
6058ffb2c3c0SRemy Bruno 
6059ffb2c3c0SRemy Bruno 
6060ef5fa1a4STakashi Iwai static unsigned int hdspm_aes32_sample_rates[] = {
6061ef5fa1a4STakashi Iwai 	32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
6062ef5fa1a4STakashi Iwai };
6063ffb2c3c0SRemy Bruno 
6064ef5fa1a4STakashi Iwai static struct snd_pcm_hw_constraint_list
6065ef5fa1a4STakashi Iwai hdspm_hw_constraints_aes32_sample_rates = {
6066ffb2c3c0SRemy Bruno 	.count = ARRAY_SIZE(hdspm_aes32_sample_rates),
6067ffb2c3c0SRemy Bruno 	.list = hdspm_aes32_sample_rates,
6068ffb2c3c0SRemy Bruno 	.mask = 0
6069ffb2c3c0SRemy Bruno };
6070ffb2c3c0SRemy Bruno 
607198274f07STakashi Iwai static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
6072763f356cSTakashi Iwai {
607398274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
607498274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
6075763f356cSTakashi Iwai 
6076763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
6077763f356cSTakashi Iwai 
6078763f356cSTakashi Iwai 	snd_pcm_set_sync(substream);
6079763f356cSTakashi Iwai 
60800dca1793SAdrian Knoth 
6081763f356cSTakashi Iwai 	runtime->hw = snd_hdspm_playback_subinfo;
6082763f356cSTakashi Iwai 
6083763f356cSTakashi Iwai 	if (hdspm->capture_substream == NULL)
6084763f356cSTakashi Iwai 		hdspm_stop_audio(hdspm);
6085763f356cSTakashi Iwai 
6086763f356cSTakashi Iwai 	hdspm->playback_pid = current->pid;
6087763f356cSTakashi Iwai 	hdspm->playback_substream = substream;
6088763f356cSTakashi Iwai 
6089763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
6090763f356cSTakashi Iwai 
6091763f356cSTakashi Iwai 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
6092d877681dSTakashi Iwai 	snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
6093763f356cSTakashi Iwai 
60940dca1793SAdrian Knoth 	switch (hdspm->io_type) {
60950dca1793SAdrian Knoth 	case AIO:
60960dca1793SAdrian Knoth 	case RayDAT:
6097d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
6098763f356cSTakashi Iwai 					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
6099d877681dSTakashi Iwai 					     32, 4096);
6100d877681dSTakashi Iwai 		/* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
6101d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
61020dca1793SAdrian Knoth 					     SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
6103d877681dSTakashi Iwai 					     16384, 16384);
61040dca1793SAdrian Knoth 		break;
61050dca1793SAdrian Knoth 
61060dca1793SAdrian Knoth 	default:
6107d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
61080dca1793SAdrian Knoth 					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
6109d877681dSTakashi Iwai 					     64, 8192);
6110d877681dSTakashi Iwai 		break;
61110dca1793SAdrian Knoth 	}
61120dca1793SAdrian Knoth 
61130dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
61143fa9e3d2STakashi Iwai 		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
6115ffb2c3c0SRemy Bruno 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
6116ffb2c3c0SRemy Bruno 				&hdspm_hw_constraints_aes32_sample_rates);
6117ffb2c3c0SRemy Bruno 	} else {
6118763f356cSTakashi Iwai 		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
61190dca1793SAdrian Knoth 				snd_hdspm_hw_rule_rate_out_channels, hdspm,
6120763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_CHANNELS, -1);
6121ffb2c3c0SRemy Bruno 	}
612288fabbfcSAdrian Knoth 
612388fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
612488fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_out_channels, hdspm,
612588fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS, -1);
612688fabbfcSAdrian Knoth 
612788fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
612888fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_out_channels_rate, hdspm,
612988fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_RATE, -1);
613088fabbfcSAdrian Knoth 
6131763f356cSTakashi Iwai 	return 0;
6132763f356cSTakashi Iwai }
6133763f356cSTakashi Iwai 
613498274f07STakashi Iwai static int snd_hdspm_playback_release(struct snd_pcm_substream *substream)
6135763f356cSTakashi Iwai {
613698274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
6137763f356cSTakashi Iwai 
6138763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
6139763f356cSTakashi Iwai 
6140763f356cSTakashi Iwai 	hdspm->playback_pid = -1;
6141763f356cSTakashi Iwai 	hdspm->playback_substream = NULL;
6142763f356cSTakashi Iwai 
6143763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
6144763f356cSTakashi Iwai 
6145763f356cSTakashi Iwai 	return 0;
6146763f356cSTakashi Iwai }
6147763f356cSTakashi Iwai 
6148763f356cSTakashi Iwai 
614998274f07STakashi Iwai static int snd_hdspm_capture_open(struct snd_pcm_substream *substream)
6150763f356cSTakashi Iwai {
615198274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
615298274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
6153763f356cSTakashi Iwai 
6154763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
6155763f356cSTakashi Iwai 	snd_pcm_set_sync(substream);
6156763f356cSTakashi Iwai 	runtime->hw = snd_hdspm_capture_subinfo;
6157763f356cSTakashi Iwai 
6158763f356cSTakashi Iwai 	if (hdspm->playback_substream == NULL)
6159763f356cSTakashi Iwai 		hdspm_stop_audio(hdspm);
6160763f356cSTakashi Iwai 
6161763f356cSTakashi Iwai 	hdspm->capture_pid = current->pid;
6162763f356cSTakashi Iwai 	hdspm->capture_substream = substream;
6163763f356cSTakashi Iwai 
6164763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
6165763f356cSTakashi Iwai 
6166763f356cSTakashi Iwai 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
6167d877681dSTakashi Iwai 	snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
6168d877681dSTakashi Iwai 
61690dca1793SAdrian Knoth 	switch (hdspm->io_type) {
61700dca1793SAdrian Knoth 	case AIO:
61710dca1793SAdrian Knoth 	case RayDAT:
6172d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
6173763f356cSTakashi Iwai 					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
6174d877681dSTakashi Iwai 					     32, 4096);
6175d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
61760dca1793SAdrian Knoth 					     SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
6177d877681dSTakashi Iwai 					     16384, 16384);
61780dca1793SAdrian Knoth 		break;
61790dca1793SAdrian Knoth 
61800dca1793SAdrian Knoth 	default:
6181d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
61820dca1793SAdrian Knoth 					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
6183d877681dSTakashi Iwai 					     64, 8192);
6184d877681dSTakashi Iwai 		break;
61850dca1793SAdrian Knoth 	}
61860dca1793SAdrian Knoth 
61870dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
61883fa9e3d2STakashi Iwai 		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
6189ffb2c3c0SRemy Bruno 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
6190ffb2c3c0SRemy Bruno 				&hdspm_hw_constraints_aes32_sample_rates);
6191ffb2c3c0SRemy Bruno 	} else {
6192763f356cSTakashi Iwai 		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
61930dca1793SAdrian Knoth 				snd_hdspm_hw_rule_rate_in_channels, hdspm,
6194763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_CHANNELS, -1);
6195ffb2c3c0SRemy Bruno 	}
619688fabbfcSAdrian Knoth 
619788fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
619888fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_in_channels, hdspm,
619988fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS, -1);
620088fabbfcSAdrian Knoth 
620188fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
620288fabbfcSAdrian Knoth 			snd_hdspm_hw_rule_in_channels_rate, hdspm,
620388fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_RATE, -1);
620488fabbfcSAdrian Knoth 
6205763f356cSTakashi Iwai 	return 0;
6206763f356cSTakashi Iwai }
6207763f356cSTakashi Iwai 
620898274f07STakashi Iwai static int snd_hdspm_capture_release(struct snd_pcm_substream *substream)
6209763f356cSTakashi Iwai {
621098274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
6211763f356cSTakashi Iwai 
6212763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
6213763f356cSTakashi Iwai 
6214763f356cSTakashi Iwai 	hdspm->capture_pid = -1;
6215763f356cSTakashi Iwai 	hdspm->capture_substream = NULL;
6216763f356cSTakashi Iwai 
6217763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
6218763f356cSTakashi Iwai 	return 0;
6219763f356cSTakashi Iwai }
6220763f356cSTakashi Iwai 
62210dca1793SAdrian Knoth static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
6222763f356cSTakashi Iwai {
62230dca1793SAdrian Knoth 	/* we have nothing to initialize but the call is required */
62240dca1793SAdrian Knoth 	return 0;
62250dca1793SAdrian Knoth }
62260dca1793SAdrian Knoth 
62270dca1793SAdrian Knoth static inline int copy_u32_le(void __user *dest, void __iomem *src)
62280dca1793SAdrian Knoth {
62290dca1793SAdrian Knoth 	u32 val = readl(src);
62300dca1793SAdrian Knoth 	return copy_to_user(dest, &val, 4);
62310dca1793SAdrian Knoth }
62320dca1793SAdrian Knoth 
62330dca1793SAdrian Knoth static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
62342ca595abSDan Carpenter 		unsigned int cmd, unsigned long arg)
62350dca1793SAdrian Knoth {
62360dca1793SAdrian Knoth 	void __user *argp = (void __user *)arg;
6237ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = hw->private_data;
623898274f07STakashi Iwai 	struct hdspm_mixer_ioctl mixer;
62390dca1793SAdrian Knoth 	struct hdspm_config info;
62400dca1793SAdrian Knoth 	struct hdspm_status status;
624198274f07STakashi Iwai 	struct hdspm_version hdspm_version;
6242730a5865SJaroslav Kysela 	struct hdspm_peak_rms *levels;
62430dca1793SAdrian Knoth 	struct hdspm_ltc ltc;
62440dca1793SAdrian Knoth 	unsigned int statusregister;
62450dca1793SAdrian Knoth 	long unsigned int s;
62460dca1793SAdrian Knoth 	int i = 0;
6247763f356cSTakashi Iwai 
6248763f356cSTakashi Iwai 	switch (cmd) {
6249763f356cSTakashi Iwai 
6250763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS:
6251730a5865SJaroslav Kysela 		levels = &hdspm->peak_rms;
62520dca1793SAdrian Knoth 		for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
6253730a5865SJaroslav Kysela 			levels->input_peaks[i] =
62540dca1793SAdrian Knoth 				readl(hdspm->iobase +
62550dca1793SAdrian Knoth 						HDSPM_MADI_INPUT_PEAK + i*4);
6256730a5865SJaroslav Kysela 			levels->playback_peaks[i] =
62570dca1793SAdrian Knoth 				readl(hdspm->iobase +
62580dca1793SAdrian Knoth 						HDSPM_MADI_PLAYBACK_PEAK + i*4);
6259730a5865SJaroslav Kysela 			levels->output_peaks[i] =
62600dca1793SAdrian Knoth 				readl(hdspm->iobase +
62610dca1793SAdrian Knoth 						HDSPM_MADI_OUTPUT_PEAK + i*4);
62620dca1793SAdrian Knoth 
6263730a5865SJaroslav Kysela 			levels->input_rms[i] =
62640dca1793SAdrian Knoth 				((uint64_t) readl(hdspm->iobase +
62650dca1793SAdrian Knoth 					HDSPM_MADI_INPUT_RMS_H + i*4) << 32) |
62660dca1793SAdrian Knoth 				(uint64_t) readl(hdspm->iobase +
62670dca1793SAdrian Knoth 						HDSPM_MADI_INPUT_RMS_L + i*4);
6268730a5865SJaroslav Kysela 			levels->playback_rms[i] =
62690dca1793SAdrian Knoth 				((uint64_t)readl(hdspm->iobase +
62700dca1793SAdrian Knoth 					HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) |
62710dca1793SAdrian Knoth 				(uint64_t)readl(hdspm->iobase +
62720dca1793SAdrian Knoth 					HDSPM_MADI_PLAYBACK_RMS_L + i*4);
6273730a5865SJaroslav Kysela 			levels->output_rms[i] =
62740dca1793SAdrian Knoth 				((uint64_t)readl(hdspm->iobase +
62750dca1793SAdrian Knoth 					HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) |
62760dca1793SAdrian Knoth 				(uint64_t)readl(hdspm->iobase +
62770dca1793SAdrian Knoth 						HDSPM_MADI_OUTPUT_RMS_L + i*4);
62780dca1793SAdrian Knoth 		}
62790dca1793SAdrian Knoth 
62800dca1793SAdrian Knoth 		if (hdspm->system_sample_rate > 96000) {
6281730a5865SJaroslav Kysela 			levels->speed = qs;
62820dca1793SAdrian Knoth 		} else if (hdspm->system_sample_rate > 48000) {
6283730a5865SJaroslav Kysela 			levels->speed = ds;
62840dca1793SAdrian Knoth 		} else {
6285730a5865SJaroslav Kysela 			levels->speed = ss;
62860dca1793SAdrian Knoth 		}
6287730a5865SJaroslav Kysela 		levels->status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
62880dca1793SAdrian Knoth 
6289730a5865SJaroslav Kysela 		s = copy_to_user(argp, levels, sizeof(struct hdspm_peak_rms));
62900dca1793SAdrian Knoth 		if (0 != s) {
62910dca1793SAdrian Knoth 			/* snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu
62920dca1793SAdrian Knoth 			 [Levels]\n", sizeof(struct hdspm_peak_rms), s);
6293ef5fa1a4STakashi Iwai 			 */
6294763f356cSTakashi Iwai 			return -EFAULT;
62950dca1793SAdrian Knoth 		}
62960dca1793SAdrian Knoth 		break;
62970dca1793SAdrian Knoth 
62980dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_LTC:
62990dca1793SAdrian Knoth 		ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
63000dca1793SAdrian Knoth 		i = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
63010dca1793SAdrian Knoth 		if (i & HDSPM_TCO1_LTC_Input_valid) {
63020dca1793SAdrian Knoth 			switch (i & (HDSPM_TCO1_LTC_Format_LSB |
63030dca1793SAdrian Knoth 				HDSPM_TCO1_LTC_Format_MSB)) {
63040dca1793SAdrian Knoth 			case 0:
63050dca1793SAdrian Knoth 				ltc.format = fps_24;
63060dca1793SAdrian Knoth 				break;
63070dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_LSB:
63080dca1793SAdrian Knoth 				ltc.format = fps_25;
63090dca1793SAdrian Knoth 				break;
63100dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_MSB:
63110dca1793SAdrian Knoth 				ltc.format = fps_2997;
63120dca1793SAdrian Knoth 				break;
63130dca1793SAdrian Knoth 			default:
63140dca1793SAdrian Knoth 				ltc.format = 30;
63150dca1793SAdrian Knoth 				break;
63160dca1793SAdrian Knoth 			}
63170dca1793SAdrian Knoth 			if (i & HDSPM_TCO1_set_drop_frame_flag) {
63180dca1793SAdrian Knoth 				ltc.frame = drop_frame;
63190dca1793SAdrian Knoth 			} else {
63200dca1793SAdrian Knoth 				ltc.frame = full_frame;
63210dca1793SAdrian Knoth 			}
63220dca1793SAdrian Knoth 		} else {
63230dca1793SAdrian Knoth 			ltc.format = format_invalid;
63240dca1793SAdrian Knoth 			ltc.frame = frame_invalid;
63250dca1793SAdrian Knoth 		}
63260dca1793SAdrian Knoth 		if (i & HDSPM_TCO1_Video_Input_Format_NTSC) {
63270dca1793SAdrian Knoth 			ltc.input_format = ntsc;
63280dca1793SAdrian Knoth 		} else if (i & HDSPM_TCO1_Video_Input_Format_PAL) {
63290dca1793SAdrian Knoth 			ltc.input_format = pal;
63300dca1793SAdrian Knoth 		} else {
63310dca1793SAdrian Knoth 			ltc.input_format = no_video;
63320dca1793SAdrian Knoth 		}
63330dca1793SAdrian Knoth 
63340dca1793SAdrian Knoth 		s = copy_to_user(argp, &ltc, sizeof(struct hdspm_ltc));
63350dca1793SAdrian Knoth 		if (0 != s) {
63360dca1793SAdrian Knoth 			/*
63370dca1793SAdrian Knoth 			 snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */
63380dca1793SAdrian Knoth 			return -EFAULT;
63390dca1793SAdrian Knoth 		}
6340763f356cSTakashi Iwai 
6341763f356cSTakashi Iwai 		break;
6342763f356cSTakashi Iwai 
63430dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_CONFIG:
6344763f356cSTakashi Iwai 
63454ab69a2bSAdrian Knoth 		memset(&info, 0, sizeof(info));
6346763f356cSTakashi Iwai 		spin_lock_irq(&hdspm->lock);
6347ef5fa1a4STakashi Iwai 		info.pref_sync_ref = hdspm_pref_sync_ref(hdspm);
6348ef5fa1a4STakashi Iwai 		info.wordclock_sync_check = hdspm_wc_sync_check(hdspm);
6349763f356cSTakashi Iwai 
6350763f356cSTakashi Iwai 		info.system_sample_rate = hdspm->system_sample_rate;
6351763f356cSTakashi Iwai 		info.autosync_sample_rate =
6352763f356cSTakashi Iwai 			hdspm_external_sample_rate(hdspm);
6353ef5fa1a4STakashi Iwai 		info.system_clock_mode = hdspm_system_clock_mode(hdspm);
6354ef5fa1a4STakashi Iwai 		info.clock_source = hdspm_clock_source(hdspm);
6355ef5fa1a4STakashi Iwai 		info.autosync_ref = hdspm_autosync_ref(hdspm);
6356c9e1668cSAdrian Knoth 		info.line_out = hdspm_toggle_setting(hdspm, HDSPM_LineOut);
6357763f356cSTakashi Iwai 		info.passthru = 0;
6358763f356cSTakashi Iwai 		spin_unlock_irq(&hdspm->lock);
63592ca595abSDan Carpenter 		if (copy_to_user(argp, &info, sizeof(info)))
6360763f356cSTakashi Iwai 			return -EFAULT;
6361763f356cSTakashi Iwai 		break;
6362763f356cSTakashi Iwai 
63630dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_STATUS:
6364643d6bbbSDan Carpenter 		memset(&status, 0, sizeof(status));
6365643d6bbbSDan Carpenter 
63660dca1793SAdrian Knoth 		status.card_type = hdspm->io_type;
63670dca1793SAdrian Knoth 
63680dca1793SAdrian Knoth 		status.autosync_source = hdspm_autosync_ref(hdspm);
63690dca1793SAdrian Knoth 
63700dca1793SAdrian Knoth 		status.card_clock = 110069313433624ULL;
63710dca1793SAdrian Knoth 		status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
63720dca1793SAdrian Knoth 
63730dca1793SAdrian Knoth 		switch (hdspm->io_type) {
63740dca1793SAdrian Knoth 		case MADI:
63750dca1793SAdrian Knoth 		case MADIface:
63760dca1793SAdrian Knoth 			status.card_specific.madi.sync_wc =
63770dca1793SAdrian Knoth 				hdspm_wc_sync_check(hdspm);
63780dca1793SAdrian Knoth 			status.card_specific.madi.sync_madi =
63790dca1793SAdrian Knoth 				hdspm_madi_sync_check(hdspm);
63800dca1793SAdrian Knoth 			status.card_specific.madi.sync_tco =
63810dca1793SAdrian Knoth 				hdspm_tco_sync_check(hdspm);
63820dca1793SAdrian Knoth 			status.card_specific.madi.sync_in =
63830dca1793SAdrian Knoth 				hdspm_sync_in_sync_check(hdspm);
63840dca1793SAdrian Knoth 
63850dca1793SAdrian Knoth 			statusregister =
63860dca1793SAdrian Knoth 				hdspm_read(hdspm, HDSPM_statusRegister);
63870dca1793SAdrian Knoth 			status.card_specific.madi.madi_input =
63880dca1793SAdrian Knoth 				(statusregister & HDSPM_AB_int) ? 1 : 0;
63890dca1793SAdrian Knoth 			status.card_specific.madi.channel_format =
63909e6ff520SAdrian Knoth 				(statusregister & HDSPM_RX_64ch) ? 1 : 0;
63910dca1793SAdrian Knoth 			/* TODO: Mac driver sets it when f_s>48kHz */
63920dca1793SAdrian Knoth 			status.card_specific.madi.frame_format = 0;
63930dca1793SAdrian Knoth 
63940dca1793SAdrian Knoth 		default:
63950dca1793SAdrian Knoth 			break;
63960dca1793SAdrian Knoth 		}
63970dca1793SAdrian Knoth 
63982ca595abSDan Carpenter 		if (copy_to_user(argp, &status, sizeof(status)))
63990dca1793SAdrian Knoth 			return -EFAULT;
64000dca1793SAdrian Knoth 
64010dca1793SAdrian Knoth 
64020dca1793SAdrian Knoth 		break;
64030dca1793SAdrian Knoth 
6404763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_VERSION:
6405643d6bbbSDan Carpenter 		memset(&hdspm_version, 0, sizeof(hdspm_version));
6406643d6bbbSDan Carpenter 
64070dca1793SAdrian Knoth 		hdspm_version.card_type = hdspm->io_type;
64080dca1793SAdrian Knoth 		strncpy(hdspm_version.cardname, hdspm->card_name,
64090dca1793SAdrian Knoth 				sizeof(hdspm_version.cardname));
64107d53a631SAdrian Knoth 		hdspm_version.serial = hdspm->serial;
6411763f356cSTakashi Iwai 		hdspm_version.firmware_rev = hdspm->firmware_rev;
64120dca1793SAdrian Knoth 		hdspm_version.addons = 0;
64130dca1793SAdrian Knoth 		if (hdspm->tco)
64140dca1793SAdrian Knoth 			hdspm_version.addons |= HDSPM_ADDON_TCO;
64150dca1793SAdrian Knoth 
64162ca595abSDan Carpenter 		if (copy_to_user(argp, &hdspm_version,
6417763f356cSTakashi Iwai 					sizeof(hdspm_version)))
6418763f356cSTakashi Iwai 			return -EFAULT;
6419763f356cSTakashi Iwai 		break;
6420763f356cSTakashi Iwai 
6421763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_MIXER:
64222ca595abSDan Carpenter 		if (copy_from_user(&mixer, argp, sizeof(mixer)))
6423763f356cSTakashi Iwai 			return -EFAULT;
6424ef5fa1a4STakashi Iwai 		if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer,
6425ef5fa1a4STakashi Iwai 					sizeof(struct hdspm_mixer)))
6426763f356cSTakashi Iwai 			return -EFAULT;
6427763f356cSTakashi Iwai 		break;
6428763f356cSTakashi Iwai 
6429763f356cSTakashi Iwai 	default:
6430763f356cSTakashi Iwai 		return -EINVAL;
6431763f356cSTakashi Iwai 	}
6432763f356cSTakashi Iwai 	return 0;
6433763f356cSTakashi Iwai }
6434763f356cSTakashi Iwai 
643598274f07STakashi Iwai static struct snd_pcm_ops snd_hdspm_playback_ops = {
6436763f356cSTakashi Iwai 	.open = snd_hdspm_playback_open,
6437763f356cSTakashi Iwai 	.close = snd_hdspm_playback_release,
6438763f356cSTakashi Iwai 	.ioctl = snd_hdspm_ioctl,
6439763f356cSTakashi Iwai 	.hw_params = snd_hdspm_hw_params,
6440763f356cSTakashi Iwai 	.hw_free = snd_hdspm_hw_free,
6441763f356cSTakashi Iwai 	.prepare = snd_hdspm_prepare,
6442763f356cSTakashi Iwai 	.trigger = snd_hdspm_trigger,
6443763f356cSTakashi Iwai 	.pointer = snd_hdspm_hw_pointer,
6444763f356cSTakashi Iwai 	.page = snd_pcm_sgbuf_ops_page,
6445763f356cSTakashi Iwai };
6446763f356cSTakashi Iwai 
644798274f07STakashi Iwai static struct snd_pcm_ops snd_hdspm_capture_ops = {
6448763f356cSTakashi Iwai 	.open = snd_hdspm_capture_open,
6449763f356cSTakashi Iwai 	.close = snd_hdspm_capture_release,
6450763f356cSTakashi Iwai 	.ioctl = snd_hdspm_ioctl,
6451763f356cSTakashi Iwai 	.hw_params = snd_hdspm_hw_params,
6452763f356cSTakashi Iwai 	.hw_free = snd_hdspm_hw_free,
6453763f356cSTakashi Iwai 	.prepare = snd_hdspm_prepare,
6454763f356cSTakashi Iwai 	.trigger = snd_hdspm_trigger,
6455763f356cSTakashi Iwai 	.pointer = snd_hdspm_hw_pointer,
6456763f356cSTakashi Iwai 	.page = snd_pcm_sgbuf_ops_page,
6457763f356cSTakashi Iwai };
6458763f356cSTakashi Iwai 
6459e23e7a14SBill Pemberton static int snd_hdspm_create_hwdep(struct snd_card *card,
646098274f07STakashi Iwai 				  struct hdspm *hdspm)
6461763f356cSTakashi Iwai {
646298274f07STakashi Iwai 	struct snd_hwdep *hw;
6463763f356cSTakashi Iwai 	int err;
6464763f356cSTakashi Iwai 
6465ef5fa1a4STakashi Iwai 	err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw);
6466ef5fa1a4STakashi Iwai 	if (err < 0)
6467763f356cSTakashi Iwai 		return err;
6468763f356cSTakashi Iwai 
6469763f356cSTakashi Iwai 	hdspm->hwdep = hw;
6470763f356cSTakashi Iwai 	hw->private_data = hdspm;
6471763f356cSTakashi Iwai 	strcpy(hw->name, "HDSPM hwdep interface");
6472763f356cSTakashi Iwai 
64730dca1793SAdrian Knoth 	hw->ops.open = snd_hdspm_hwdep_dummy_op;
6474763f356cSTakashi Iwai 	hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
64758de5d6f1SAdrian Knoth 	hw->ops.ioctl_compat = snd_hdspm_hwdep_ioctl;
64760dca1793SAdrian Knoth 	hw->ops.release = snd_hdspm_hwdep_dummy_op;
6477763f356cSTakashi Iwai 
6478763f356cSTakashi Iwai 	return 0;
6479763f356cSTakashi Iwai }
6480763f356cSTakashi Iwai 
6481763f356cSTakashi Iwai 
6482763f356cSTakashi Iwai /*------------------------------------------------------------
6483763f356cSTakashi Iwai    memory interface
6484763f356cSTakashi Iwai  ------------------------------------------------------------*/
6485e23e7a14SBill Pemberton static int snd_hdspm_preallocate_memory(struct hdspm *hdspm)
6486763f356cSTakashi Iwai {
6487763f356cSTakashi Iwai 	int err;
648898274f07STakashi Iwai 	struct snd_pcm *pcm;
6489763f356cSTakashi Iwai 	size_t wanted;
6490763f356cSTakashi Iwai 
6491763f356cSTakashi Iwai 	pcm = hdspm->pcm;
6492763f356cSTakashi Iwai 
64933cee5a60SRemy Bruno 	wanted = HDSPM_DMA_AREA_BYTES;
6494763f356cSTakashi Iwai 
6495ef5fa1a4STakashi Iwai 	err =
6496763f356cSTakashi Iwai 	     snd_pcm_lib_preallocate_pages_for_all(pcm,
6497763f356cSTakashi Iwai 						   SNDRV_DMA_TYPE_DEV_SG,
6498763f356cSTakashi Iwai 						   snd_dma_pci_data(hdspm->pci),
6499763f356cSTakashi Iwai 						   wanted,
6500ef5fa1a4STakashi Iwai 						   wanted);
6501ef5fa1a4STakashi Iwai 	if (err < 0) {
6502e2eba3e7SAndrew Morton 		snd_printdd("Could not preallocate %zd Bytes\n", wanted);
6503763f356cSTakashi Iwai 
6504763f356cSTakashi Iwai 		return err;
6505763f356cSTakashi Iwai 	} else
6506e2eba3e7SAndrew Morton 		snd_printdd(" Preallocated %zd Bytes\n", wanted);
6507763f356cSTakashi Iwai 
6508763f356cSTakashi Iwai 	return 0;
6509763f356cSTakashi Iwai }
6510763f356cSTakashi Iwai 
65110dca1793SAdrian Knoth 
651277a23f26STakashi Iwai static void hdspm_set_sgbuf(struct hdspm *hdspm,
651377a23f26STakashi Iwai 			    struct snd_pcm_substream *substream,
6514763f356cSTakashi Iwai 			     unsigned int reg, int channels)
6515763f356cSTakashi Iwai {
6516763f356cSTakashi Iwai 	int i;
65170dca1793SAdrian Knoth 
65180dca1793SAdrian Knoth 	/* continuous memory segment */
6519763f356cSTakashi Iwai 	for (i = 0; i < (channels * 16); i++)
6520763f356cSTakashi Iwai 		hdspm_write(hdspm, reg + 4 * i,
652177a23f26STakashi Iwai 				snd_pcm_sgbuf_get_addr(substream, 4096 * i));
6522763f356cSTakashi Iwai }
6523763f356cSTakashi Iwai 
65240dca1793SAdrian Knoth 
6525763f356cSTakashi Iwai /* ------------- ALSA Devices ---------------------------- */
6526e23e7a14SBill Pemberton static int snd_hdspm_create_pcm(struct snd_card *card,
652798274f07STakashi Iwai 				struct hdspm *hdspm)
6528763f356cSTakashi Iwai {
652998274f07STakashi Iwai 	struct snd_pcm *pcm;
6530763f356cSTakashi Iwai 	int err;
6531763f356cSTakashi Iwai 
6532ef5fa1a4STakashi Iwai 	err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm);
6533ef5fa1a4STakashi Iwai 	if (err < 0)
6534763f356cSTakashi Iwai 		return err;
6535763f356cSTakashi Iwai 
6536763f356cSTakashi Iwai 	hdspm->pcm = pcm;
6537763f356cSTakashi Iwai 	pcm->private_data = hdspm;
6538763f356cSTakashi Iwai 	strcpy(pcm->name, hdspm->card_name);
6539763f356cSTakashi Iwai 
6540763f356cSTakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
6541763f356cSTakashi Iwai 			&snd_hdspm_playback_ops);
6542763f356cSTakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
6543763f356cSTakashi Iwai 			&snd_hdspm_capture_ops);
6544763f356cSTakashi Iwai 
6545763f356cSTakashi Iwai 	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
6546763f356cSTakashi Iwai 
6547ef5fa1a4STakashi Iwai 	err = snd_hdspm_preallocate_memory(hdspm);
6548ef5fa1a4STakashi Iwai 	if (err < 0)
6549763f356cSTakashi Iwai 		return err;
6550763f356cSTakashi Iwai 
6551763f356cSTakashi Iwai 	return 0;
6552763f356cSTakashi Iwai }
6553763f356cSTakashi Iwai 
655498274f07STakashi Iwai static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm)
6555763f356cSTakashi Iwai {
65567c7102b7SAdrian Knoth 	int i;
65577c7102b7SAdrian Knoth 
65587c7102b7SAdrian Knoth 	for (i = 0; i < hdspm->midiPorts; i++)
65597c7102b7SAdrian Knoth 		snd_hdspm_flush_midi_input(hdspm, i);
6560763f356cSTakashi Iwai }
6561763f356cSTakashi Iwai 
6562e23e7a14SBill Pemberton static int snd_hdspm_create_alsa_devices(struct snd_card *card,
656398274f07STakashi Iwai 					 struct hdspm *hdspm)
6564763f356cSTakashi Iwai {
65650dca1793SAdrian Knoth 	int err, i;
6566763f356cSTakashi Iwai 
6567763f356cSTakashi Iwai 	snd_printdd("Create card...\n");
6568ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_pcm(card, hdspm);
6569ef5fa1a4STakashi Iwai 	if (err < 0)
6570763f356cSTakashi Iwai 		return err;
6571763f356cSTakashi Iwai 
65720dca1793SAdrian Knoth 	i = 0;
65730dca1793SAdrian Knoth 	while (i < hdspm->midiPorts) {
65740dca1793SAdrian Knoth 		err = snd_hdspm_create_midi(card, hdspm, i);
65750dca1793SAdrian Knoth 		if (err < 0) {
6576763f356cSTakashi Iwai 			return err;
65770dca1793SAdrian Knoth 		}
65780dca1793SAdrian Knoth 		i++;
65790dca1793SAdrian Knoth 	}
6580763f356cSTakashi Iwai 
6581ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_controls(card, hdspm);
6582ef5fa1a4STakashi Iwai 	if (err < 0)
6583763f356cSTakashi Iwai 		return err;
6584763f356cSTakashi Iwai 
6585ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_hwdep(card, hdspm);
6586ef5fa1a4STakashi Iwai 	if (err < 0)
6587763f356cSTakashi Iwai 		return err;
6588763f356cSTakashi Iwai 
6589763f356cSTakashi Iwai 	snd_printdd("proc init...\n");
6590763f356cSTakashi Iwai 	snd_hdspm_proc_init(hdspm);
6591763f356cSTakashi Iwai 
6592763f356cSTakashi Iwai 	hdspm->system_sample_rate = -1;
6593763f356cSTakashi Iwai 	hdspm->last_external_sample_rate = -1;
6594763f356cSTakashi Iwai 	hdspm->last_internal_sample_rate = -1;
6595763f356cSTakashi Iwai 	hdspm->playback_pid = -1;
6596763f356cSTakashi Iwai 	hdspm->capture_pid = -1;
6597763f356cSTakashi Iwai 	hdspm->capture_substream = NULL;
6598763f356cSTakashi Iwai 	hdspm->playback_substream = NULL;
6599763f356cSTakashi Iwai 
6600763f356cSTakashi Iwai 	snd_printdd("Set defaults...\n");
6601ef5fa1a4STakashi Iwai 	err = snd_hdspm_set_defaults(hdspm);
6602ef5fa1a4STakashi Iwai 	if (err < 0)
6603763f356cSTakashi Iwai 		return err;
6604763f356cSTakashi Iwai 
6605763f356cSTakashi Iwai 	snd_printdd("Update mixer controls...\n");
6606763f356cSTakashi Iwai 	hdspm_update_simple_mixer_controls(hdspm);
6607763f356cSTakashi Iwai 
6608763f356cSTakashi Iwai 	snd_printdd("Initializeing complete ???\n");
6609763f356cSTakashi Iwai 
6610ef5fa1a4STakashi Iwai 	err = snd_card_register(card);
6611ef5fa1a4STakashi Iwai 	if (err < 0) {
6612763f356cSTakashi Iwai 		snd_printk(KERN_ERR "HDSPM: error registering card\n");
6613763f356cSTakashi Iwai 		return err;
6614763f356cSTakashi Iwai 	}
6615763f356cSTakashi Iwai 
6616763f356cSTakashi Iwai 	snd_printdd("... yes now\n");
6617763f356cSTakashi Iwai 
6618763f356cSTakashi Iwai 	return 0;
6619763f356cSTakashi Iwai }
6620763f356cSTakashi Iwai 
6621e23e7a14SBill Pemberton static int snd_hdspm_create(struct snd_card *card,
6622e23e7a14SBill Pemberton 			    struct hdspm *hdspm)
6623e23e7a14SBill Pemberton {
66240dca1793SAdrian Knoth 
6625763f356cSTakashi Iwai 	struct pci_dev *pci = hdspm->pci;
6626763f356cSTakashi Iwai 	int err;
6627763f356cSTakashi Iwai 	unsigned long io_extent;
6628763f356cSTakashi Iwai 
6629763f356cSTakashi Iwai 	hdspm->irq = -1;
6630763f356cSTakashi Iwai 	hdspm->card = card;
6631763f356cSTakashi Iwai 
6632763f356cSTakashi Iwai 	spin_lock_init(&hdspm->lock);
6633763f356cSTakashi Iwai 
6634763f356cSTakashi Iwai 	pci_read_config_word(hdspm->pci,
6635763f356cSTakashi Iwai 			PCI_CLASS_REVISION, &hdspm->firmware_rev);
6636763f356cSTakashi Iwai 
6637763f356cSTakashi Iwai 	strcpy(card->mixername, "Xilinx FPGA");
66383cee5a60SRemy Bruno 	strcpy(card->driver, "HDSPM");
66390dca1793SAdrian Knoth 
66400dca1793SAdrian Knoth 	switch (hdspm->firmware_rev) {
66410dca1793SAdrian Knoth 	case HDSPM_RAYDAT_REV:
66420dca1793SAdrian Knoth 		hdspm->io_type = RayDAT;
66430dca1793SAdrian Knoth 		hdspm->card_name = "RME RayDAT";
66440dca1793SAdrian Knoth 		hdspm->midiPorts = 2;
66450dca1793SAdrian Knoth 		break;
66460dca1793SAdrian Knoth 	case HDSPM_AIO_REV:
66470dca1793SAdrian Knoth 		hdspm->io_type = AIO;
66480dca1793SAdrian Knoth 		hdspm->card_name = "RME AIO";
66490dca1793SAdrian Knoth 		hdspm->midiPorts = 1;
66500dca1793SAdrian Knoth 		break;
66510dca1793SAdrian Knoth 	case HDSPM_MADIFACE_REV:
66520dca1793SAdrian Knoth 		hdspm->io_type = MADIface;
66530dca1793SAdrian Knoth 		hdspm->card_name = "RME MADIface";
66540dca1793SAdrian Knoth 		hdspm->midiPorts = 1;
66550dca1793SAdrian Knoth 		break;
6656c09403dcSAdrian Knoth 	default:
6657c09403dcSAdrian Knoth 		if ((hdspm->firmware_rev == 0xf0) ||
6658c09403dcSAdrian Knoth 			((hdspm->firmware_rev >= 0xe6) &&
6659c09403dcSAdrian Knoth 					(hdspm->firmware_rev <= 0xea))) {
66600dca1793SAdrian Knoth 			hdspm->io_type = AES32;
66610dca1793SAdrian Knoth 			hdspm->card_name = "RME AES32";
66620dca1793SAdrian Knoth 			hdspm->midiPorts = 2;
666305c7cc9cSAdrian Knoth 		} else if ((hdspm->firmware_rev == 0xd2) ||
6664c09403dcSAdrian Knoth 			((hdspm->firmware_rev >= 0xc8)  &&
6665c09403dcSAdrian Knoth 				(hdspm->firmware_rev <= 0xcf))) {
6666c09403dcSAdrian Knoth 			hdspm->io_type = MADI;
6667c09403dcSAdrian Knoth 			hdspm->card_name = "RME MADI";
6668c09403dcSAdrian Knoth 			hdspm->midiPorts = 3;
6669c09403dcSAdrian Knoth 		} else {
6670c09403dcSAdrian Knoth 			snd_printk(KERN_ERR
6671c09403dcSAdrian Knoth 				"HDSPM: unknown firmware revision %x\n",
66725027f347SAdrian Knoth 				hdspm->firmware_rev);
66735027f347SAdrian Knoth 			return -ENODEV;
66743cee5a60SRemy Bruno 		}
6675c09403dcSAdrian Knoth 	}
6676763f356cSTakashi Iwai 
6677ef5fa1a4STakashi Iwai 	err = pci_enable_device(pci);
6678ef5fa1a4STakashi Iwai 	if (err < 0)
6679763f356cSTakashi Iwai 		return err;
6680763f356cSTakashi Iwai 
6681763f356cSTakashi Iwai 	pci_set_master(hdspm->pci);
6682763f356cSTakashi Iwai 
6683ef5fa1a4STakashi Iwai 	err = pci_request_regions(pci, "hdspm");
6684ef5fa1a4STakashi Iwai 	if (err < 0)
6685763f356cSTakashi Iwai 		return err;
6686763f356cSTakashi Iwai 
6687763f356cSTakashi Iwai 	hdspm->port = pci_resource_start(pci, 0);
6688763f356cSTakashi Iwai 	io_extent = pci_resource_len(pci, 0);
6689763f356cSTakashi Iwai 
6690763f356cSTakashi Iwai 	snd_printdd("grabbed memory region 0x%lx-0x%lx\n",
6691763f356cSTakashi Iwai 			hdspm->port, hdspm->port + io_extent - 1);
6692763f356cSTakashi Iwai 
6693ef5fa1a4STakashi Iwai 	hdspm->iobase = ioremap_nocache(hdspm->port, io_extent);
6694ef5fa1a4STakashi Iwai 	if (!hdspm->iobase) {
6695ef5fa1a4STakashi Iwai 		snd_printk(KERN_ERR "HDSPM: "
6696ef5fa1a4STakashi Iwai 				"unable to remap region 0x%lx-0x%lx\n",
6697763f356cSTakashi Iwai 				hdspm->port, hdspm->port + io_extent - 1);
6698763f356cSTakashi Iwai 		return -EBUSY;
6699763f356cSTakashi Iwai 	}
6700763f356cSTakashi Iwai 	snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n",
6701763f356cSTakashi Iwai 			(unsigned long)hdspm->iobase, hdspm->port,
6702763f356cSTakashi Iwai 			hdspm->port + io_extent - 1);
6703763f356cSTakashi Iwai 
6704763f356cSTakashi Iwai 	if (request_irq(pci->irq, snd_hdspm_interrupt,
6705934c2b6dSTakashi Iwai 			IRQF_SHARED, KBUILD_MODNAME, hdspm)) {
6706763f356cSTakashi Iwai 		snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
6707763f356cSTakashi Iwai 		return -EBUSY;
6708763f356cSTakashi Iwai 	}
6709763f356cSTakashi Iwai 
6710763f356cSTakashi Iwai 	snd_printdd("use IRQ %d\n", pci->irq);
6711763f356cSTakashi Iwai 
6712763f356cSTakashi Iwai 	hdspm->irq = pci->irq;
6713763f356cSTakashi Iwai 
6714e2eba3e7SAndrew Morton 	snd_printdd("kmalloc Mixer memory of %zd Bytes\n",
671598274f07STakashi Iwai 			sizeof(struct hdspm_mixer));
6716ef5fa1a4STakashi Iwai 	hdspm->mixer = kzalloc(sizeof(struct hdspm_mixer), GFP_KERNEL);
6717ef5fa1a4STakashi Iwai 	if (!hdspm->mixer) {
6718ef5fa1a4STakashi Iwai 		snd_printk(KERN_ERR "HDSPM: "
6719ef5fa1a4STakashi Iwai 				"unable to kmalloc Mixer memory of %d Bytes\n",
672098274f07STakashi Iwai 				(int)sizeof(struct hdspm_mixer));
6721b17cbdd8SJulia Lawall 		return -ENOMEM;
6722763f356cSTakashi Iwai 	}
6723763f356cSTakashi Iwai 
67240dca1793SAdrian Knoth 	hdspm->port_names_in = NULL;
67250dca1793SAdrian Knoth 	hdspm->port_names_out = NULL;
67260dca1793SAdrian Knoth 
67270dca1793SAdrian Knoth 	switch (hdspm->io_type) {
67280dca1793SAdrian Knoth 	case AES32:
6729d2d10a21SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels = AES32_CHANNELS;
6730d2d10a21SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels = AES32_CHANNELS;
6731d2d10a21SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels = AES32_CHANNELS;
6732432d2500SAdrian Knoth 
6733432d2500SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
6734432d2500SAdrian Knoth 			channel_map_aes32;
6735432d2500SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
6736432d2500SAdrian Knoth 			channel_map_aes32;
6737432d2500SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
6738432d2500SAdrian Knoth 			channel_map_aes32;
6739432d2500SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
6740432d2500SAdrian Knoth 			texts_ports_aes32;
6741432d2500SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
6742432d2500SAdrian Knoth 			texts_ports_aes32;
6743432d2500SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
6744432d2500SAdrian Knoth 			texts_ports_aes32;
6745432d2500SAdrian Knoth 
6746d2d10a21SAdrian Knoth 		hdspm->max_channels_out = hdspm->max_channels_in =
6747d2d10a21SAdrian Knoth 			AES32_CHANNELS;
6748432d2500SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_out =
6749432d2500SAdrian Knoth 			texts_ports_aes32;
6750432d2500SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_out =
6751432d2500SAdrian Knoth 			channel_map_aes32;
6752432d2500SAdrian Knoth 
67530dca1793SAdrian Knoth 		break;
67540dca1793SAdrian Knoth 
67550dca1793SAdrian Knoth 	case MADI:
67560dca1793SAdrian Knoth 	case MADIface:
67570dca1793SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels =
67580dca1793SAdrian Knoth 			MADI_SS_CHANNELS;
67590dca1793SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels =
67600dca1793SAdrian Knoth 			MADI_DS_CHANNELS;
67610dca1793SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels =
67620dca1793SAdrian Knoth 			MADI_QS_CHANNELS;
67630dca1793SAdrian Knoth 
67640dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
67650dca1793SAdrian Knoth 			channel_map_unity_ss;
676601e96078SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
67670dca1793SAdrian Knoth 			channel_map_unity_ss;
676801e96078SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
67690dca1793SAdrian Knoth 			channel_map_unity_ss;
67700dca1793SAdrian Knoth 
67710dca1793SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
67720dca1793SAdrian Knoth 			texts_ports_madi;
67730dca1793SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
67740dca1793SAdrian Knoth 			texts_ports_madi;
67750dca1793SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
67760dca1793SAdrian Knoth 			texts_ports_madi;
67770dca1793SAdrian Knoth 		break;
67780dca1793SAdrian Knoth 
67790dca1793SAdrian Knoth 	case AIO:
67800dca1793SAdrian Knoth 		hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
67810dca1793SAdrian Knoth 		hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
67820dca1793SAdrian Knoth 		hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
67830dca1793SAdrian Knoth 		hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS;
67840dca1793SAdrian Knoth 		hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
67850dca1793SAdrian Knoth 		hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
67860dca1793SAdrian Knoth 
67873de9db26SAdrian Knoth 		if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
67883de9db26SAdrian Knoth 			snd_printk(KERN_INFO "HDSPM: AEB input board found\n");
67893de9db26SAdrian Knoth 			hdspm->ss_in_channels += 4;
67903de9db26SAdrian Knoth 			hdspm->ds_in_channels += 4;
67913de9db26SAdrian Knoth 			hdspm->qs_in_channels += 4;
67923de9db26SAdrian Knoth 		}
67933de9db26SAdrian Knoth 
67943de9db26SAdrian Knoth 		if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) {
67953de9db26SAdrian Knoth 			snd_printk(KERN_INFO "HDSPM: AEB output board found\n");
67963de9db26SAdrian Knoth 			hdspm->ss_out_channels += 4;
67973de9db26SAdrian Knoth 			hdspm->ds_out_channels += 4;
67983de9db26SAdrian Knoth 			hdspm->qs_out_channels += 4;
67993de9db26SAdrian Knoth 		}
68003de9db26SAdrian Knoth 
68010dca1793SAdrian Knoth 		hdspm->channel_map_out_ss = channel_map_aio_out_ss;
68020dca1793SAdrian Knoth 		hdspm->channel_map_out_ds = channel_map_aio_out_ds;
68030dca1793SAdrian Knoth 		hdspm->channel_map_out_qs = channel_map_aio_out_qs;
68040dca1793SAdrian Knoth 
68050dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = channel_map_aio_in_ss;
68060dca1793SAdrian Knoth 		hdspm->channel_map_in_ds = channel_map_aio_in_ds;
68070dca1793SAdrian Knoth 		hdspm->channel_map_in_qs = channel_map_aio_in_qs;
68080dca1793SAdrian Knoth 
68090dca1793SAdrian Knoth 		hdspm->port_names_in_ss = texts_ports_aio_in_ss;
68100dca1793SAdrian Knoth 		hdspm->port_names_out_ss = texts_ports_aio_out_ss;
68110dca1793SAdrian Knoth 		hdspm->port_names_in_ds = texts_ports_aio_in_ds;
68120dca1793SAdrian Knoth 		hdspm->port_names_out_ds = texts_ports_aio_out_ds;
68130dca1793SAdrian Knoth 		hdspm->port_names_in_qs = texts_ports_aio_in_qs;
68140dca1793SAdrian Knoth 		hdspm->port_names_out_qs = texts_ports_aio_out_qs;
68150dca1793SAdrian Knoth 
68160dca1793SAdrian Knoth 		break;
68170dca1793SAdrian Knoth 
68180dca1793SAdrian Knoth 	case RayDAT:
68190dca1793SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels =
68200dca1793SAdrian Knoth 			RAYDAT_SS_CHANNELS;
68210dca1793SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels =
68220dca1793SAdrian Knoth 			RAYDAT_DS_CHANNELS;
68230dca1793SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels =
68240dca1793SAdrian Knoth 			RAYDAT_QS_CHANNELS;
68250dca1793SAdrian Knoth 
68260dca1793SAdrian Knoth 		hdspm->max_channels_in = RAYDAT_SS_CHANNELS;
68270dca1793SAdrian Knoth 		hdspm->max_channels_out = RAYDAT_SS_CHANNELS;
68280dca1793SAdrian Knoth 
68290dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
68300dca1793SAdrian Knoth 			channel_map_raydat_ss;
68310dca1793SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
68320dca1793SAdrian Knoth 			channel_map_raydat_ds;
68330dca1793SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
68340dca1793SAdrian Knoth 			channel_map_raydat_qs;
68350dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_out =
68360dca1793SAdrian Knoth 			channel_map_raydat_ss;
68370dca1793SAdrian Knoth 
68380dca1793SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
68390dca1793SAdrian Knoth 			texts_ports_raydat_ss;
68400dca1793SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
68410dca1793SAdrian Knoth 			texts_ports_raydat_ds;
68420dca1793SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
68430dca1793SAdrian Knoth 			texts_ports_raydat_qs;
68440dca1793SAdrian Knoth 
68450dca1793SAdrian Knoth 
68460dca1793SAdrian Knoth 		break;
68470dca1793SAdrian Knoth 
68480dca1793SAdrian Knoth 	}
68490dca1793SAdrian Knoth 
68500dca1793SAdrian Knoth 	/* TCO detection */
68510dca1793SAdrian Knoth 	switch (hdspm->io_type) {
68520dca1793SAdrian Knoth 	case AIO:
68530dca1793SAdrian Knoth 	case RayDAT:
68540dca1793SAdrian Knoth 		if (hdspm_read(hdspm, HDSPM_statusRegister2) &
68550dca1793SAdrian Knoth 				HDSPM_s2_tco_detect) {
68560dca1793SAdrian Knoth 			hdspm->midiPorts++;
68570dca1793SAdrian Knoth 			hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
68580dca1793SAdrian Knoth 					GFP_KERNEL);
68590dca1793SAdrian Knoth 			if (NULL != hdspm->tco) {
68600dca1793SAdrian Knoth 				hdspm_tco_write(hdspm);
68610dca1793SAdrian Knoth 			}
68620dca1793SAdrian Knoth 			snd_printk(KERN_INFO "HDSPM: AIO/RayDAT TCO module found\n");
68630dca1793SAdrian Knoth 		} else {
68640dca1793SAdrian Knoth 			hdspm->tco = NULL;
68650dca1793SAdrian Knoth 		}
68660dca1793SAdrian Knoth 		break;
68670dca1793SAdrian Knoth 
68680dca1793SAdrian Knoth 	case MADI:
68690dc831b9SAdrian Knoth 	case AES32:
68700dca1793SAdrian Knoth 		if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
68710dca1793SAdrian Knoth 			hdspm->midiPorts++;
68720dca1793SAdrian Knoth 			hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
68730dca1793SAdrian Knoth 					GFP_KERNEL);
68740dca1793SAdrian Knoth 			if (NULL != hdspm->tco) {
68750dca1793SAdrian Knoth 				hdspm_tco_write(hdspm);
68760dca1793SAdrian Knoth 			}
6877e71b95adSAdrian Knoth 			snd_printk(KERN_INFO "HDSPM: MADI/AES TCO module found\n");
68780dca1793SAdrian Knoth 		} else {
68790dca1793SAdrian Knoth 			hdspm->tco = NULL;
68800dca1793SAdrian Knoth 		}
68810dca1793SAdrian Knoth 		break;
68820dca1793SAdrian Knoth 
68830dca1793SAdrian Knoth 	default:
68840dca1793SAdrian Knoth 		hdspm->tco = NULL;
68850dca1793SAdrian Knoth 	}
68860dca1793SAdrian Knoth 
68870dca1793SAdrian Knoth 	/* texts */
68880dca1793SAdrian Knoth 	switch (hdspm->io_type) {
68890dca1793SAdrian Knoth 	case AES32:
68900dca1793SAdrian Knoth 		if (hdspm->tco) {
68910dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aes_tco;
6892e71b95adSAdrian Knoth 			hdspm->texts_autosync_items =
6893e71b95adSAdrian Knoth 				ARRAY_SIZE(texts_autosync_aes_tco);
68940dca1793SAdrian Knoth 		} else {
68950dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aes;
6896e71b95adSAdrian Knoth 			hdspm->texts_autosync_items =
6897e71b95adSAdrian Knoth 				ARRAY_SIZE(texts_autosync_aes);
68980dca1793SAdrian Knoth 		}
68990dca1793SAdrian Knoth 		break;
69000dca1793SAdrian Knoth 
69010dca1793SAdrian Knoth 	case MADI:
69020dca1793SAdrian Knoth 		if (hdspm->tco) {
69030dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_madi_tco;
69040dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 4;
69050dca1793SAdrian Knoth 		} else {
69060dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_madi;
69070dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 3;
69080dca1793SAdrian Knoth 		}
69090dca1793SAdrian Knoth 		break;
69100dca1793SAdrian Knoth 
69110dca1793SAdrian Knoth 	case MADIface:
69120dca1793SAdrian Knoth 
69130dca1793SAdrian Knoth 		break;
69140dca1793SAdrian Knoth 
69150dca1793SAdrian Knoth 	case RayDAT:
69160dca1793SAdrian Knoth 		if (hdspm->tco) {
69170dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_raydat_tco;
69180dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 9;
69190dca1793SAdrian Knoth 		} else {
69200dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_raydat;
69210dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 8;
69220dca1793SAdrian Knoth 		}
69230dca1793SAdrian Knoth 		break;
69240dca1793SAdrian Knoth 
69250dca1793SAdrian Knoth 	case AIO:
69260dca1793SAdrian Knoth 		if (hdspm->tco) {
69270dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aio_tco;
69280dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 6;
69290dca1793SAdrian Knoth 		} else {
69300dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aio;
69310dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 5;
69320dca1793SAdrian Knoth 		}
69330dca1793SAdrian Knoth 		break;
69340dca1793SAdrian Knoth 
69350dca1793SAdrian Knoth 	}
69360dca1793SAdrian Knoth 
69370dca1793SAdrian Knoth 	tasklet_init(&hdspm->midi_tasklet,
69380dca1793SAdrian Knoth 			hdspm_midi_tasklet, (unsigned long) hdspm);
6939763f356cSTakashi Iwai 
6940f7de8ba3SAdrian Knoth 
6941f7de8ba3SAdrian Knoth 	if (hdspm->io_type != MADIface) {
6942f7de8ba3SAdrian Knoth 		hdspm->serial = (hdspm_read(hdspm,
6943f7de8ba3SAdrian Knoth 				HDSPM_midiStatusIn0)>>8) & 0xFFFFFF;
6944f7de8ba3SAdrian Knoth 		/* id contains either a user-provided value or the default
6945f7de8ba3SAdrian Knoth 		 * NULL. If it's the default, we're safe to
6946f7de8ba3SAdrian Knoth 		 * fill card->id with the serial number.
6947f7de8ba3SAdrian Knoth 		 *
6948f7de8ba3SAdrian Knoth 		 * If the serial number is 0xFFFFFF, then we're dealing with
6949f7de8ba3SAdrian Knoth 		 * an old PCI revision that comes without a sane number. In
6950f7de8ba3SAdrian Knoth 		 * this case, we don't set card->id to avoid collisions
6951f7de8ba3SAdrian Knoth 		 * when running with multiple cards.
6952f7de8ba3SAdrian Knoth 		 */
6953f7de8ba3SAdrian Knoth 		if (NULL == id[hdspm->dev] && hdspm->serial != 0xFFFFFF) {
6954f7de8ba3SAdrian Knoth 			sprintf(card->id, "HDSPMx%06x", hdspm->serial);
6955f7de8ba3SAdrian Knoth 			snd_card_set_id(card, card->id);
6956f7de8ba3SAdrian Knoth 		}
6957f7de8ba3SAdrian Knoth 	}
6958f7de8ba3SAdrian Knoth 
6959763f356cSTakashi Iwai 	snd_printdd("create alsa devices.\n");
6960ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_alsa_devices(card, hdspm);
6961ef5fa1a4STakashi Iwai 	if (err < 0)
6962763f356cSTakashi Iwai 		return err;
6963763f356cSTakashi Iwai 
6964763f356cSTakashi Iwai 	snd_hdspm_initialize_midi_flush(hdspm);
6965763f356cSTakashi Iwai 
6966763f356cSTakashi Iwai 	return 0;
6967763f356cSTakashi Iwai }
6968763f356cSTakashi Iwai 
69690dca1793SAdrian Knoth 
697098274f07STakashi Iwai static int snd_hdspm_free(struct hdspm * hdspm)
6971763f356cSTakashi Iwai {
6972763f356cSTakashi Iwai 
6973763f356cSTakashi Iwai 	if (hdspm->port) {
6974763f356cSTakashi Iwai 
6975763f356cSTakashi Iwai 		/* stop th audio, and cancel all interrupts */
6976763f356cSTakashi Iwai 		hdspm->control_register &=
6977ef5fa1a4STakashi Iwai 		    ~(HDSPM_Start | HDSPM_AudioInterruptEnable |
69780dca1793SAdrian Knoth 		      HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable |
69790dca1793SAdrian Knoth 		      HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable);
6980763f356cSTakashi Iwai 		hdspm_write(hdspm, HDSPM_controlRegister,
6981763f356cSTakashi Iwai 			    hdspm->control_register);
6982763f356cSTakashi Iwai 	}
6983763f356cSTakashi Iwai 
6984763f356cSTakashi Iwai 	if (hdspm->irq >= 0)
6985763f356cSTakashi Iwai 		free_irq(hdspm->irq, (void *) hdspm);
6986763f356cSTakashi Iwai 
6987763f356cSTakashi Iwai 	kfree(hdspm->mixer);
6988763f356cSTakashi Iwai 
6989763f356cSTakashi Iwai 	if (hdspm->iobase)
6990763f356cSTakashi Iwai 		iounmap(hdspm->iobase);
6991763f356cSTakashi Iwai 
6992763f356cSTakashi Iwai 	if (hdspm->port)
6993763f356cSTakashi Iwai 		pci_release_regions(hdspm->pci);
6994763f356cSTakashi Iwai 
6995763f356cSTakashi Iwai 	pci_disable_device(hdspm->pci);
6996763f356cSTakashi Iwai 	return 0;
6997763f356cSTakashi Iwai }
6998763f356cSTakashi Iwai 
69990dca1793SAdrian Knoth 
700098274f07STakashi Iwai static void snd_hdspm_card_free(struct snd_card *card)
7001763f356cSTakashi Iwai {
7002ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = card->private_data;
7003763f356cSTakashi Iwai 
7004763f356cSTakashi Iwai 	if (hdspm)
7005763f356cSTakashi Iwai 		snd_hdspm_free(hdspm);
7006763f356cSTakashi Iwai }
7007763f356cSTakashi Iwai 
70080dca1793SAdrian Knoth 
7009e23e7a14SBill Pemberton static int snd_hdspm_probe(struct pci_dev *pci,
7010763f356cSTakashi Iwai 			   const struct pci_device_id *pci_id)
7011763f356cSTakashi Iwai {
7012763f356cSTakashi Iwai 	static int dev;
701398274f07STakashi Iwai 	struct hdspm *hdspm;
701498274f07STakashi Iwai 	struct snd_card *card;
7015763f356cSTakashi Iwai 	int err;
7016763f356cSTakashi Iwai 
7017763f356cSTakashi Iwai 	if (dev >= SNDRV_CARDS)
7018763f356cSTakashi Iwai 		return -ENODEV;
7019763f356cSTakashi Iwai 	if (!enable[dev]) {
7020763f356cSTakashi Iwai 		dev++;
7021763f356cSTakashi Iwai 		return -ENOENT;
7022763f356cSTakashi Iwai 	}
7023763f356cSTakashi Iwai 
7024e58de7baSTakashi Iwai 	err = snd_card_create(index[dev], id[dev],
7025e58de7baSTakashi Iwai 			THIS_MODULE, sizeof(struct hdspm), &card);
7026e58de7baSTakashi Iwai 	if (err < 0)
7027e58de7baSTakashi Iwai 		return err;
7028763f356cSTakashi Iwai 
7029ef5fa1a4STakashi Iwai 	hdspm = card->private_data;
7030763f356cSTakashi Iwai 	card->private_free = snd_hdspm_card_free;
7031763f356cSTakashi Iwai 	hdspm->dev = dev;
7032763f356cSTakashi Iwai 	hdspm->pci = pci;
7033763f356cSTakashi Iwai 
7034c187c041STakashi Iwai 	snd_card_set_dev(card, &pci->dev);
7035c187c041STakashi Iwai 
70360dca1793SAdrian Knoth 	err = snd_hdspm_create(card, hdspm);
7037ef5fa1a4STakashi Iwai 	if (err < 0) {
7038763f356cSTakashi Iwai 		snd_card_free(card);
7039763f356cSTakashi Iwai 		return err;
7040763f356cSTakashi Iwai 	}
7041763f356cSTakashi Iwai 
70420dca1793SAdrian Knoth 	if (hdspm->io_type != MADIface) {
70430dca1793SAdrian Knoth 		sprintf(card->shortname, "%s_%x",
70440dca1793SAdrian Knoth 			hdspm->card_name,
70457d53a631SAdrian Knoth 			hdspm->serial);
70460dca1793SAdrian Knoth 		sprintf(card->longname, "%s S/N 0x%x at 0x%lx, irq %d",
70470dca1793SAdrian Knoth 			hdspm->card_name,
70487d53a631SAdrian Knoth 			hdspm->serial,
7049763f356cSTakashi Iwai 			hdspm->port, hdspm->irq);
70500dca1793SAdrian Knoth 	} else {
70510dca1793SAdrian Knoth 		sprintf(card->shortname, "%s", hdspm->card_name);
70520dca1793SAdrian Knoth 		sprintf(card->longname, "%s at 0x%lx, irq %d",
70530dca1793SAdrian Knoth 				hdspm->card_name, hdspm->port, hdspm->irq);
70540dca1793SAdrian Knoth 	}
7055763f356cSTakashi Iwai 
7056ef5fa1a4STakashi Iwai 	err = snd_card_register(card);
7057ef5fa1a4STakashi Iwai 	if (err < 0) {
7058763f356cSTakashi Iwai 		snd_card_free(card);
7059763f356cSTakashi Iwai 		return err;
7060763f356cSTakashi Iwai 	}
7061763f356cSTakashi Iwai 
7062763f356cSTakashi Iwai 	pci_set_drvdata(pci, card);
7063763f356cSTakashi Iwai 
7064763f356cSTakashi Iwai 	dev++;
7065763f356cSTakashi Iwai 	return 0;
7066763f356cSTakashi Iwai }
7067763f356cSTakashi Iwai 
7068e23e7a14SBill Pemberton static void snd_hdspm_remove(struct pci_dev *pci)
7069763f356cSTakashi Iwai {
7070763f356cSTakashi Iwai 	snd_card_free(pci_get_drvdata(pci));
7071763f356cSTakashi Iwai }
7072763f356cSTakashi Iwai 
7073e9f66d9bSTakashi Iwai static struct pci_driver hdspm_driver = {
70743733e424STakashi Iwai 	.name = KBUILD_MODNAME,
7075763f356cSTakashi Iwai 	.id_table = snd_hdspm_ids,
7076763f356cSTakashi Iwai 	.probe = snd_hdspm_probe,
7077e23e7a14SBill Pemberton 	.remove = snd_hdspm_remove,
7078763f356cSTakashi Iwai };
7079763f356cSTakashi Iwai 
7080e9f66d9bSTakashi Iwai module_pci_driver(hdspm_driver);
7081