xref: /openbmc/linux/sound/pci/rme9652/hdspm.c (revision fac59652993f075d57860769c99045b3ca18780d)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ef5fa1a4STakashi Iwai /*
3763f356cSTakashi Iwai  *   ALSA driver for RME Hammerfall DSP MADI audio interface(s)
4763f356cSTakashi Iwai  *
5763f356cSTakashi Iwai  *      Copyright (c) 2003 Winfried Ritsch (IEM)
6763f356cSTakashi Iwai  *      code based on hdsp.c   Paul Davis
7763f356cSTakashi Iwai  *                             Marcus Andersson
8763f356cSTakashi Iwai  *                             Thomas Charbonnel
93cee5a60SRemy Bruno  *      Modified 2006-06-01 for AES32 support by Remy Bruno
103cee5a60SRemy Bruno  *                                               <remy.bruno@trinnov.com>
11763f356cSTakashi Iwai  *
120dca1793SAdrian Knoth  *      Modified 2009-04-13 for proper metering by Florian Faber
130dca1793SAdrian Knoth  *                                               <faber@faberman.de>
140dca1793SAdrian Knoth  *
150dca1793SAdrian Knoth  *      Modified 2009-04-14 for native float support by Florian Faber
160dca1793SAdrian Knoth  *                                               <faber@faberman.de>
170dca1793SAdrian Knoth  *
180dca1793SAdrian Knoth  *      Modified 2009-04-26 fixed bug in rms metering by Florian Faber
190dca1793SAdrian Knoth  *                                               <faber@faberman.de>
200dca1793SAdrian Knoth  *
210dca1793SAdrian Knoth  *      Modified 2009-04-30 added hw serial number support by Florian Faber
220dca1793SAdrian Knoth  *
230dca1793SAdrian Knoth  *      Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth
240dca1793SAdrian Knoth  *
250dca1793SAdrian Knoth  *	Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
260dca1793SAdrian Knoth  *
27e4e07c6cSPhilippe Bekaert  *      Modified 2019-05-23 fix AIO single speed ADAT capture and playback
28e4e07c6cSPhilippe Bekaert  *      by Philippe.Bekaert@uhasselt.be
29763f356cSTakashi Iwai  */
3069358fcaSMartin Dausel 
3169358fcaSMartin Dausel /* *************    Register Documentation   *******************************************************
3269358fcaSMartin Dausel  *
3369358fcaSMartin Dausel  * Work in progress! Documentation is based on the code in this file.
3469358fcaSMartin Dausel  *
3569358fcaSMartin Dausel  * --------- HDSPM_controlRegister ---------
3669358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
3769358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
3869358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
3969358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
4069358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
4169358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
4269358fcaSMartin Dausel  * :    .    :    .    :    .    :  x .    :  HDSPM_AudioInterruptEnable \_ setting both bits
4369358fcaSMartin Dausel  * :    .    :    .    :    .    :    .   x:  HDSPM_Start                /  enables audio IO
4469358fcaSMartin Dausel  * :    .    :    .    :    .    :   x.    :  HDSPM_ClockModeMaster - 1: Master, 0: Slave
4569358fcaSMartin Dausel  * :    .    :    .    :    .    :    .210 :  HDSPM_LatencyMask - 3 Bit value for latency
4669358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :      0:64, 1:128, 2:256, 3:512,
4769358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :      4:1024, 5:2048, 6:4096, 7:8192
4869358fcaSMartin Dausel  * :x   .    :    .    :    .   x:xx  .    :  HDSPM_FrequencyMask
4969358fcaSMartin Dausel  * :    .    :    .    :    .    :10  .    :  HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=??
5069358fcaSMartin Dausel  * :    .    :    .    :    .   x:    .    :  <MADI> HDSPM_DoubleSpeed
5169358fcaSMartin Dausel  * :x   .    :    .    :    .    :    .    :  <MADI> HDSPM_QuadSpeed
5269358fcaSMartin Dausel  * :    .  3 :    .  10:  2 .    :    .    :  HDSPM_SyncRefMask :
5369358fcaSMartin Dausel  * :    .    :    .   x:    .    :    .    :  HDSPM_SyncRef0
5469358fcaSMartin Dausel  * :    .    :    .  x :    .    :    .    :  HDSPM_SyncRef1
5569358fcaSMartin Dausel  * :    .    :    .    :  x .    :    .    :  <AES32> HDSPM_SyncRef2
5669358fcaSMartin Dausel  * :    .  x :    .    :    .    :    .    :  <AES32> HDSPM_SyncRef3
5769358fcaSMartin Dausel  * :    .    :    .  10:    .    :    .    :  <MADI> sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn
5869358fcaSMartin Dausel  * :    .  3 :    .  10:  2 .    :    .    :  <AES32>  0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn?
5969358fcaSMartin Dausel  * :    .  x :    .    :    .    :    .    :  <MADIe> HDSPe_FLOAT_FORMAT
6069358fcaSMartin Dausel  * :    .    :    .    : x  .    :    .    :  <MADI> HDSPM_InputSelect0 : 0=optical,1=coax
6169358fcaSMartin Dausel  * :    .    :    .    :x   .    :    .    :  <MADI> HDSPM_InputSelect1
6269358fcaSMartin Dausel  * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
6369358fcaSMartin Dausel  * :    .    :    .    :    . x  :    .    :  <MADI> HDSPM_TX_64ch
6469358fcaSMartin Dausel  * :    .    :    .    :    . x  :    .    :  <AES32> HDSPM_Emphasis
6569358fcaSMartin Dausel  * :    .    :    .    :    .x   :    .    :  <MADI> HDSPM_AutoInp
6669358fcaSMartin Dausel  * :    .    :    . x  :    .    :    .    :  <MADI> HDSPM_SMUX
6769358fcaSMartin Dausel  * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
6869358fcaSMartin Dausel  * :    .    :   x.    :    .    :    .    :  <MADI> HDSPM_taxi_reset
6969358fcaSMartin Dausel  * :    .   x:    .    :    .    :    .    :  <MADI> HDSPM_LineOut
7069358fcaSMartin Dausel  * :    .   x:    .    :    .    :    .    :  <AES32> ??????????????????
7169358fcaSMartin Dausel  * :    .    :   x.    :    .    :    .    :  <AES32> HDSPM_WCK48
7269358fcaSMartin Dausel  * :    .    :    .    :    .x   :    .    :  <AES32> HDSPM_Dolby
7369358fcaSMartin Dausel  * :    .    : x  .    :    .    :    .    :  HDSPM_Midi0InterruptEnable
7469358fcaSMartin Dausel  * :    .    :x   .    :    .    :    .    :  HDSPM_Midi1InterruptEnable
7569358fcaSMartin Dausel  * :    .    :  x .    :    .    :    .    :  HDSPM_Midi2InterruptEnable
7669358fcaSMartin Dausel  * :    . x  :    .    :    .    :    .    :  <MADI> HDSPM_Midi3InterruptEnable
7769358fcaSMartin Dausel  * :    . x  :    .    :    .    :    .    :  <AES32> HDSPM_DS_DoubleWire
7869358fcaSMartin Dausel  * :    .x   :    .    :    .    :    .    :  <AES32> HDSPM_QS_DoubleWire
7969358fcaSMartin Dausel  * :   x.    :    .    :    .    :    .    :  <AES32> HDSPM_QS_QuadWire
8069358fcaSMartin Dausel  * :    .    :    .    :    .  x :    .    :  <AES32> HDSPM_Professional
8169358fcaSMartin Dausel  * : x  .    :    .    :    .    :    .    :  HDSPM_wclk_sel
8269358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :
8369358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
8469358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
8569358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
8669358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
8769358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
8869358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit
8969358fcaSMartin Dausel  *
9069358fcaSMartin Dausel  *
9169358fcaSMartin Dausel  *
9269358fcaSMartin Dausel  * AIO / RayDAT only
9369358fcaSMartin Dausel  *
9469358fcaSMartin Dausel  * ------------ HDSPM_WR_SETTINGS ----------
9569358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
9669358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210:
9769358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
9869358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
9969358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
10069358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
10169358fcaSMartin Dausel  * :    .    :    .    :    .    :    .   x: HDSPM_c0Master 1: Master, 0: Slave
10269358fcaSMartin Dausel  * :    .    :    .    :    .    :    .  x : HDSPM_c0_SyncRef0
10369358fcaSMartin Dausel  * :    .    :    .    :    .    :    . x  : HDSPM_c0_SyncRef1
10469358fcaSMartin Dausel  * :    .    :    .    :    .    :    .x   : HDSPM_c0_SyncRef2
10569358fcaSMartin Dausel  * :    .    :    .    :    .    :   x.    : HDSPM_c0_SyncRef3
10669358fcaSMartin Dausel  * :    .    :    .    :    .    :   3.210 : HDSPM_c0_SyncRefMask:
10769358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :  RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4,
10869358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
10969358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :  AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT,
11069358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
11169358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :
11269358fcaSMartin Dausel  * :    .    :    .    :    .    :    .    :
11369358fcaSMartin Dausel  * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
11469358fcaSMartin Dausel  * :1098.7654:3210.9876:5432.1098:7654.3210:
11569358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
11669358fcaSMartin Dausel  * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
11769358fcaSMartin Dausel  * :||||.||||:||||.||||:||||.||||:||||.||||:
11869358fcaSMartin Dausel  * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
11969358fcaSMartin Dausel  *
12069358fcaSMartin Dausel  */
121763f356cSTakashi Iwai #include <linux/init.h>
122763f356cSTakashi Iwai #include <linux/delay.h>
123763f356cSTakashi Iwai #include <linux/interrupt.h>
12465a77217SPaul Gortmaker #include <linux/module.h>
125763f356cSTakashi Iwai #include <linux/slab.h>
126763f356cSTakashi Iwai #include <linux/pci.h>
1273f7440a6STakashi Iwai #include <linux/math64.h>
1286cbbfe1cSTakashi Iwai #include <linux/io.h>
12910513142STakashi Iwai #include <linux/nospec.h>
130763f356cSTakashi Iwai 
131763f356cSTakashi Iwai #include <sound/core.h>
132763f356cSTakashi Iwai #include <sound/control.h>
133763f356cSTakashi Iwai #include <sound/pcm.h>
1340dca1793SAdrian Knoth #include <sound/pcm_params.h>
135763f356cSTakashi Iwai #include <sound/info.h>
136763f356cSTakashi Iwai #include <sound/asoundef.h>
137763f356cSTakashi Iwai #include <sound/rawmidi.h>
138763f356cSTakashi Iwai #include <sound/hwdep.h>
139763f356cSTakashi Iwai #include <sound/initval.h>
140763f356cSTakashi Iwai 
141763f356cSTakashi Iwai #include <sound/hdspm.h>
142763f356cSTakashi Iwai 
143763f356cSTakashi Iwai static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	  /* Index 0-MAX */
144763f356cSTakashi Iwai static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	  /* ID for this card */
145a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
146763f356cSTakashi Iwai 
147763f356cSTakashi Iwai module_param_array(index, int, NULL, 0444);
148763f356cSTakashi Iwai MODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
149763f356cSTakashi Iwai 
150763f356cSTakashi Iwai module_param_array(id, charp, NULL, 0444);
151763f356cSTakashi Iwai MODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
152763f356cSTakashi Iwai 
153763f356cSTakashi Iwai module_param_array(enable, bool, NULL, 0444);
154763f356cSTakashi Iwai MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
155763f356cSTakashi Iwai 
156763f356cSTakashi Iwai 
157763f356cSTakashi Iwai MODULE_AUTHOR
1580dca1793SAdrian Knoth (
1590dca1793SAdrian Knoth 	"Winfried Ritsch <ritsch_AT_iem.at>, "
160ef5fa1a4STakashi Iwai 	"Paul Davis <paul@linuxaudiosystems.com>, "
1613cee5a60SRemy Bruno 	"Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
1620dca1793SAdrian Knoth 	"Remy Bruno <remy.bruno@trinnov.com>, "
1630dca1793SAdrian Knoth 	"Florian Faber <faberman@linuxproaudio.org>, "
1640dca1793SAdrian Knoth 	"Adrian Knoth <adi@drcomp.erfurt.thur.de>"
1650dca1793SAdrian Knoth );
166763f356cSTakashi Iwai MODULE_DESCRIPTION("RME HDSPM");
167763f356cSTakashi Iwai MODULE_LICENSE("GPL");
168763f356cSTakashi Iwai 
169763f356cSTakashi Iwai /* --- Write registers. ---
170763f356cSTakashi Iwai   These are defined as byte-offsets from the iobase value.  */
171763f356cSTakashi Iwai 
1720dca1793SAdrian Knoth #define HDSPM_WR_SETTINGS             0
1730dca1793SAdrian Knoth #define HDSPM_outputBufferAddress    32
1740dca1793SAdrian Knoth #define HDSPM_inputBufferAddress     36
175763f356cSTakashi Iwai #define HDSPM_controlRegister	     64
176763f356cSTakashi Iwai #define HDSPM_interruptConfirmation  96
177763f356cSTakashi Iwai #define HDSPM_control2Reg	     256  /* not in specs ???????? */
17869358fcaSMartin Dausel #define HDSPM_freqReg                256  /* for setting arbitrary clock values (DDS feature) */
179763f356cSTakashi Iwai #define HDSPM_midiDataOut0	     352  /* just believe in old code */
180763f356cSTakashi Iwai #define HDSPM_midiDataOut1	     356
181ffb2c3c0SRemy Bruno #define HDSPM_eeprom_wr		     384  /* for AES32 */
182763f356cSTakashi Iwai 
183763f356cSTakashi Iwai /* DMA enable for 64 channels, only Bit 0 is relevant */
184763f356cSTakashi Iwai #define HDSPM_outputEnableBase       512  /* 512-767  input  DMA */
185763f356cSTakashi Iwai #define HDSPM_inputEnableBase        768  /* 768-1023 output DMA */
186763f356cSTakashi Iwai 
187763f356cSTakashi Iwai /* 16 page addresses for each of the 64 channels DMA buffer in and out
188763f356cSTakashi Iwai    (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
189763f356cSTakashi Iwai #define HDSPM_pageAddressBufferOut       8192
190763f356cSTakashi Iwai #define HDSPM_pageAddressBufferIn        (HDSPM_pageAddressBufferOut+64*16*4)
191763f356cSTakashi Iwai 
192763f356cSTakashi Iwai #define HDSPM_MADI_mixerBase    32768	/* 32768-65535 for 2x64x64 Fader */
193763f356cSTakashi Iwai 
194763f356cSTakashi Iwai #define HDSPM_MATRIX_MIXER_SIZE  8192	/* = 2*64*64 * 4 Byte => 32kB */
195763f356cSTakashi Iwai 
196763f356cSTakashi Iwai /* --- Read registers. ---
197763f356cSTakashi Iwai    These are defined as byte-offsets from the iobase value */
198763f356cSTakashi Iwai #define HDSPM_statusRegister    0
1993cee5a60SRemy Bruno /*#define HDSPM_statusRegister2  96 */
2003cee5a60SRemy Bruno /* after RME Windows driver sources, status2 is 4-byte word # 48 = word at
2013cee5a60SRemy Bruno  * offset 192, for AES32 *and* MADI
2023cee5a60SRemy Bruno  * => need to check that offset 192 is working on MADI */
2033cee5a60SRemy Bruno #define HDSPM_statusRegister2  192
2043cee5a60SRemy Bruno #define HDSPM_timecodeRegister 128
205763f356cSTakashi Iwai 
2060dca1793SAdrian Knoth /* AIO, RayDAT */
2070dca1793SAdrian Knoth #define HDSPM_RD_STATUS_0 0
2080dca1793SAdrian Knoth #define HDSPM_RD_STATUS_1 64
2090dca1793SAdrian Knoth #define HDSPM_RD_STATUS_2 128
2100dca1793SAdrian Knoth #define HDSPM_RD_STATUS_3 192
2110dca1793SAdrian Knoth 
2120dca1793SAdrian Knoth #define HDSPM_RD_TCO           256
2130dca1793SAdrian Knoth #define HDSPM_RD_PLL_FREQ      512
2140dca1793SAdrian Knoth #define HDSPM_WR_TCO           128
2150dca1793SAdrian Knoth 
2160dca1793SAdrian Knoth #define HDSPM_TCO1_TCO_lock			0x00000001
2170dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_Range_LSB		0x00000002
2180dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_Range_MSB		0x00000004
2190dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Input_valid		0x00000008
2200dca1793SAdrian Knoth #define HDSPM_TCO1_WCK_Input_valid		0x00000010
2210dca1793SAdrian Knoth #define HDSPM_TCO1_Video_Input_Format_NTSC	0x00000020
2220dca1793SAdrian Knoth #define HDSPM_TCO1_Video_Input_Format_PAL	0x00000040
2230dca1793SAdrian Knoth 
2240dca1793SAdrian Knoth #define HDSPM_TCO1_set_TC			0x00000100
2250dca1793SAdrian Knoth #define HDSPM_TCO1_set_drop_frame_flag		0x00000200
2260dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Format_LSB		0x00000400
2270dca1793SAdrian Knoth #define HDSPM_TCO1_LTC_Format_MSB		0x00000800
2280dca1793SAdrian Knoth 
2290dca1793SAdrian Knoth #define HDSPM_TCO2_TC_run			0x00010000
2300dca1793SAdrian Knoth #define HDSPM_TCO2_WCK_IO_ratio_LSB		0x00020000
2310dca1793SAdrian Knoth #define HDSPM_TCO2_WCK_IO_ratio_MSB		0x00040000
2320dca1793SAdrian Knoth #define HDSPM_TCO2_set_num_drop_frames_LSB	0x00080000
2330dca1793SAdrian Knoth #define HDSPM_TCO2_set_num_drop_frames_MSB	0x00100000
2340dca1793SAdrian Knoth #define HDSPM_TCO2_set_jam_sync			0x00200000
2350dca1793SAdrian Knoth #define HDSPM_TCO2_set_flywheel			0x00400000
2360dca1793SAdrian Knoth 
2370dca1793SAdrian Knoth #define HDSPM_TCO2_set_01_4			0x01000000
2380dca1793SAdrian Knoth #define HDSPM_TCO2_set_pull_down		0x02000000
2390dca1793SAdrian Knoth #define HDSPM_TCO2_set_pull_up			0x04000000
2400dca1793SAdrian Knoth #define HDSPM_TCO2_set_freq			0x08000000
2410dca1793SAdrian Knoth #define HDSPM_TCO2_set_term_75R			0x10000000
2420dca1793SAdrian Knoth #define HDSPM_TCO2_set_input_LSB		0x20000000
2430dca1793SAdrian Knoth #define HDSPM_TCO2_set_input_MSB		0x40000000
2440dca1793SAdrian Knoth #define HDSPM_TCO2_set_freq_from_app		0x80000000
2450dca1793SAdrian Knoth 
2460dca1793SAdrian Knoth 
2470dca1793SAdrian Knoth #define HDSPM_midiDataOut0    352
2480dca1793SAdrian Knoth #define HDSPM_midiDataOut1    356
2490dca1793SAdrian Knoth #define HDSPM_midiDataOut2    368
2500dca1793SAdrian Knoth 
251763f356cSTakashi Iwai #define HDSPM_midiDataIn0     360
252763f356cSTakashi Iwai #define HDSPM_midiDataIn1     364
2530dca1793SAdrian Knoth #define HDSPM_midiDataIn2     372
2540dca1793SAdrian Knoth #define HDSPM_midiDataIn3     376
255763f356cSTakashi Iwai 
256763f356cSTakashi Iwai /* status is data bytes in MIDI-FIFO (0-128) */
257763f356cSTakashi Iwai #define HDSPM_midiStatusOut0  384
258763f356cSTakashi Iwai #define HDSPM_midiStatusOut1  388
2590dca1793SAdrian Knoth #define HDSPM_midiStatusOut2  400
2600dca1793SAdrian Knoth 
261763f356cSTakashi Iwai #define HDSPM_midiStatusIn0   392
262763f356cSTakashi Iwai #define HDSPM_midiStatusIn1   396
2630dca1793SAdrian Knoth #define HDSPM_midiStatusIn2   404
2640dca1793SAdrian Knoth #define HDSPM_midiStatusIn3   408
265763f356cSTakashi Iwai 
266763f356cSTakashi Iwai 
267763f356cSTakashi Iwai /* the meters are regular i/o-mapped registers, but offset
268763f356cSTakashi Iwai    considerably from the rest. the peak registers are reset
269763f356cSTakashi Iwai    when read; the least-significant 4 bits are full-scale counters;
270763f356cSTakashi Iwai    the actual peak value is in the most-significant 24 bits.
271763f356cSTakashi Iwai */
2720dca1793SAdrian Knoth 
2730dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_PEAK		4096
2740dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_PEAK	4352
2750dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_PEAK		4608
2760dca1793SAdrian Knoth 
2770dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_RMS_L		6144
2780dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_RMS_L	6400
2790dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_RMS_L		6656
2800dca1793SAdrian Knoth 
2810dca1793SAdrian Knoth #define HDSPM_MADI_INPUT_RMS_H		7168
2820dca1793SAdrian Knoth #define HDSPM_MADI_PLAYBACK_RMS_H	7424
2830dca1793SAdrian Knoth #define HDSPM_MADI_OUTPUT_RMS_H		7680
284763f356cSTakashi Iwai 
285763f356cSTakashi Iwai /* --- Control Register bits --------- */
286763f356cSTakashi Iwai #define HDSPM_Start                (1<<0) /* start engine */
287763f356cSTakashi Iwai 
288763f356cSTakashi Iwai #define HDSPM_Latency0             (1<<1) /* buffer size = 2^n */
289763f356cSTakashi Iwai #define HDSPM_Latency1             (1<<2) /* where n is defined */
290763f356cSTakashi Iwai #define HDSPM_Latency2             (1<<3) /* by Latency{2,1,0} */
291763f356cSTakashi Iwai 
2920dca1793SAdrian Knoth #define HDSPM_ClockModeMaster      (1<<4) /* 1=Master, 0=Autosync */
2930dca1793SAdrian Knoth #define HDSPM_c0Master		0x1    /* Master clock bit in settings
2940dca1793SAdrian Knoth 					  register [RayDAT, AIO] */
295763f356cSTakashi Iwai 
296763f356cSTakashi Iwai #define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
297763f356cSTakashi Iwai 
298763f356cSTakashi Iwai #define HDSPM_Frequency0  (1<<6)  /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */
299763f356cSTakashi Iwai #define HDSPM_Frequency1  (1<<7)  /* 0=32kHz/64kHz */
300763f356cSTakashi Iwai #define HDSPM_DoubleSpeed (1<<8)  /* 0=normal speed, 1=double speed */
3013cee5a60SRemy Bruno #define HDSPM_QuadSpeed   (1<<31) /* quad speed bit */
302763f356cSTakashi Iwai 
3033cee5a60SRemy Bruno #define HDSPM_Professional (1<<9) /* Professional */ /* AES32 ONLY */
304763f356cSTakashi Iwai #define HDSPM_TX_64ch     (1<<10) /* Output 64channel MODE=1,
3053cee5a60SRemy Bruno 				     56channelMODE=0 */ /* MADI ONLY*/
3063cee5a60SRemy Bruno #define HDSPM_Emphasis    (1<<10) /* Emphasis */ /* AES32 ONLY */
307763f356cSTakashi Iwai 
308763f356cSTakashi Iwai #define HDSPM_AutoInp     (1<<11) /* Auto Input (takeover) == Safe Mode,
3093cee5a60SRemy Bruno                                      0=off, 1=on  */ /* MADI ONLY */
3103cee5a60SRemy Bruno #define HDSPM_Dolby       (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
311763f356cSTakashi Iwai 
312ef5fa1a4STakashi Iwai #define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax
313ef5fa1a4STakashi Iwai 				    * -- MADI ONLY
314ef5fa1a4STakashi Iwai 				    */
315763f356cSTakashi Iwai #define HDSPM_InputSelect1 (1<<15) /* should be 0 */
316763f356cSTakashi Iwai 
3173cee5a60SRemy Bruno #define HDSPM_SyncRef2     (1<<13)
3183cee5a60SRemy Bruno #define HDSPM_SyncRef3     (1<<25)
319763f356cSTakashi Iwai 
3203cee5a60SRemy Bruno #define HDSPM_SMUX         (1<<18) /* Frame ??? */ /* MADI ONY */
321763f356cSTakashi Iwai #define HDSPM_clr_tms      (1<<19) /* clear track marker, do not use
322763f356cSTakashi Iwai                                       AES additional bits in
323763f356cSTakashi Iwai 				      lower 5 Audiodatabits ??? */
3243cee5a60SRemy Bruno #define HDSPM_taxi_reset   (1<<20) /* ??? */ /* MADI ONLY ? */
3253cee5a60SRemy Bruno #define HDSPM_WCK48        (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
326763f356cSTakashi Iwai 
3270dca1793SAdrian Knoth #define HDSPM_Midi0InterruptEnable 0x0400000
3280dca1793SAdrian Knoth #define HDSPM_Midi1InterruptEnable 0x0800000
3290dca1793SAdrian Knoth #define HDSPM_Midi2InterruptEnable 0x0200000
3300dca1793SAdrian Knoth #define HDSPM_Midi3InterruptEnable 0x4000000
331763f356cSTakashi Iwai 
332763f356cSTakashi Iwai #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
3330dca1793SAdrian Knoth #define HDSPe_FLOAT_FORMAT         0x2000000
334763f356cSTakashi Iwai 
3353cee5a60SRemy Bruno #define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
3363cee5a60SRemy Bruno #define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
3373cee5a60SRemy Bruno #define HDSPM_QS_QuadWire   (1<<28) /* AES32 ONLY */
3383cee5a60SRemy Bruno 
3393cee5a60SRemy Bruno #define HDSPM_wclk_sel (1<<30)
340763f356cSTakashi Iwai 
341384f778fSAdrian Knoth /* additional control register bits for AIO*/
342384f778fSAdrian Knoth #define HDSPM_c0_Wck48				0x20 /* also RayDAT */
343384f778fSAdrian Knoth #define HDSPM_c0_Input0				0x1000
344384f778fSAdrian Knoth #define HDSPM_c0_Input1				0x2000
345384f778fSAdrian Knoth #define HDSPM_c0_Spdif_Opt			0x4000
346384f778fSAdrian Knoth #define HDSPM_c0_Pro				0x8000
347384f778fSAdrian Knoth #define HDSPM_c0_clr_tms			0x10000
348384f778fSAdrian Knoth #define HDSPM_c0_AEB1				0x20000
349384f778fSAdrian Knoth #define HDSPM_c0_AEB2				0x40000
350384f778fSAdrian Knoth #define HDSPM_c0_LineOut			0x80000
351384f778fSAdrian Knoth #define HDSPM_c0_AD_GAIN0			0x100000
352384f778fSAdrian Knoth #define HDSPM_c0_AD_GAIN1			0x200000
353384f778fSAdrian Knoth #define HDSPM_c0_DA_GAIN0			0x400000
354384f778fSAdrian Knoth #define HDSPM_c0_DA_GAIN1			0x800000
355384f778fSAdrian Knoth #define HDSPM_c0_PH_GAIN0			0x1000000
356384f778fSAdrian Knoth #define HDSPM_c0_PH_GAIN1			0x2000000
357384f778fSAdrian Knoth #define HDSPM_c0_Sym6db				0x4000000
358384f778fSAdrian Knoth 
359384f778fSAdrian Knoth 
360763f356cSTakashi Iwai /* --- bit helper defines */
361763f356cSTakashi Iwai #define HDSPM_LatencyMask    (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
362ef5fa1a4STakashi Iwai #define HDSPM_FrequencyMask  (HDSPM_Frequency0|HDSPM_Frequency1|\
363ef5fa1a4STakashi Iwai 			      HDSPM_DoubleSpeed|HDSPM_QuadSpeed)
364763f356cSTakashi Iwai #define HDSPM_InputMask      (HDSPM_InputSelect0|HDSPM_InputSelect1)
365763f356cSTakashi Iwai #define HDSPM_InputOptical   0
366763f356cSTakashi Iwai #define HDSPM_InputCoaxial   (HDSPM_InputSelect0)
367ef5fa1a4STakashi Iwai #define HDSPM_SyncRefMask    (HDSPM_SyncRef0|HDSPM_SyncRef1|\
368ef5fa1a4STakashi Iwai 			      HDSPM_SyncRef2|HDSPM_SyncRef3)
3690dca1793SAdrian Knoth 
3700dca1793SAdrian Knoth #define HDSPM_c0_SyncRef0      0x2
3710dca1793SAdrian Knoth #define HDSPM_c0_SyncRef1      0x4
3720dca1793SAdrian Knoth #define HDSPM_c0_SyncRef2      0x8
3730dca1793SAdrian Knoth #define HDSPM_c0_SyncRef3      0x10
3740dca1793SAdrian Knoth #define HDSPM_c0_SyncRefMask   (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\
3750dca1793SAdrian Knoth 				HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3)
376763f356cSTakashi Iwai 
377763f356cSTakashi Iwai #define HDSPM_SYNC_FROM_WORD    0	/* Preferred sync reference */
378763f356cSTakashi Iwai #define HDSPM_SYNC_FROM_MADI    1	/* choices - used by "pref_sync_ref" */
3790dca1793SAdrian Knoth #define HDSPM_SYNC_FROM_TCO     2
3800dca1793SAdrian Knoth #define HDSPM_SYNC_FROM_SYNC_IN 3
381763f356cSTakashi Iwai 
382763f356cSTakashi Iwai #define HDSPM_Frequency32KHz    HDSPM_Frequency0
383763f356cSTakashi Iwai #define HDSPM_Frequency44_1KHz  HDSPM_Frequency1
384763f356cSTakashi Iwai #define HDSPM_Frequency48KHz   (HDSPM_Frequency1|HDSPM_Frequency0)
385763f356cSTakashi Iwai #define HDSPM_Frequency64KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency0)
386763f356cSTakashi Iwai #define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
387ef5fa1a4STakashi Iwai #define HDSPM_Frequency96KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency1|\
388ef5fa1a4STakashi Iwai 				HDSPM_Frequency0)
3893cee5a60SRemy Bruno #define HDSPM_Frequency128KHz   (HDSPM_QuadSpeed|HDSPM_Frequency0)
3903cee5a60SRemy Bruno #define HDSPM_Frequency176_4KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1)
391ef5fa1a4STakashi Iwai #define HDSPM_Frequency192KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1|\
392ef5fa1a4STakashi Iwai 				 HDSPM_Frequency0)
393763f356cSTakashi Iwai 
394763f356cSTakashi Iwai 
395763f356cSTakashi Iwai /* Synccheck Status */
396763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_NO_LOCK 0
397763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_LOCK    1
398763f356cSTakashi Iwai #define HDSPM_SYNC_CHECK_SYNC	 2
399763f356cSTakashi Iwai 
400763f356cSTakashi Iwai /* AutoSync References - used by "autosync_ref" control switch */
401763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_FROM_WORD      0
402763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_FROM_MADI      1
4030dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_TCO       2
4040dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_SYNC_IN   3
4050dca1793SAdrian Knoth #define HDSPM_AUTOSYNC_FROM_NONE      4
406763f356cSTakashi Iwai 
407763f356cSTakashi Iwai /* Possible sources of MADI input */
408763f356cSTakashi Iwai #define HDSPM_OPTICAL 0		/* optical   */
409763f356cSTakashi Iwai #define HDSPM_COAXIAL 1		/* BNC */
410763f356cSTakashi Iwai 
411763f356cSTakashi Iwai #define hdspm_encode_latency(x)       (((x)<<1) & HDSPM_LatencyMask)
4120dca1793SAdrian Knoth #define hdspm_decode_latency(x)       ((((x) & HDSPM_LatencyMask)>>1))
413763f356cSTakashi Iwai 
414763f356cSTakashi Iwai #define hdspm_encode_in(x) (((x)&0x3)<<14)
415763f356cSTakashi Iwai #define hdspm_decode_in(x) (((x)>>14)&0x3)
416763f356cSTakashi Iwai 
417763f356cSTakashi Iwai /* --- control2 register bits --- */
418763f356cSTakashi Iwai #define HDSPM_TMS             (1<<0)
419763f356cSTakashi Iwai #define HDSPM_TCK             (1<<1)
420763f356cSTakashi Iwai #define HDSPM_TDI             (1<<2)
421763f356cSTakashi Iwai #define HDSPM_JTAG            (1<<3)
422763f356cSTakashi Iwai #define HDSPM_PWDN            (1<<4)
423763f356cSTakashi Iwai #define HDSPM_PROGRAM	      (1<<5)
424763f356cSTakashi Iwai #define HDSPM_CONFIG_MODE_0   (1<<6)
425763f356cSTakashi Iwai #define HDSPM_CONFIG_MODE_1   (1<<7)
426763f356cSTakashi Iwai /*#define HDSPM_VERSION_BIT     (1<<8) not defined any more*/
427763f356cSTakashi Iwai #define HDSPM_BIGENDIAN_MODE  (1<<9)
428763f356cSTakashi Iwai #define HDSPM_RD_MULTIPLE     (1<<10)
429763f356cSTakashi Iwai 
4303cee5a60SRemy Bruno /* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and
431ef5fa1a4STakashi Iwai      that do not conflict with specific bits for AES32 seem to be valid also
432ef5fa1a4STakashi Iwai      for the AES32
433ef5fa1a4STakashi Iwai  */
434763f356cSTakashi Iwai #define HDSPM_audioIRQPending    (1<<0)	/* IRQ is high and pending */
435ef5fa1a4STakashi Iwai #define HDSPM_RX_64ch            (1<<1)	/* Input 64chan. MODE=1, 56chn MODE=0 */
436ef5fa1a4STakashi Iwai #define HDSPM_AB_int             (1<<2)	/* InputChannel Opt=0, Coax=1
437ef5fa1a4STakashi Iwai 					 * (like inp0)
438ef5fa1a4STakashi Iwai 					 */
4390dca1793SAdrian Knoth 
440763f356cSTakashi Iwai #define HDSPM_madiLock           (1<<3)	/* MADI Locked =1, no=0 */
4410dca1793SAdrian Knoth #define HDSPM_madiSync          (1<<18) /* MADI is in sync */
4420dca1793SAdrian Knoth 
443b0bf5504SAdrian Knoth #define HDSPM_tcoLockMadi    0x00000020 /* Optional TCO locked status for HDSPe MADI*/
444b0bf5504SAdrian Knoth #define HDSPM_tcoSync    0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/
4450dca1793SAdrian Knoth 
446b0bf5504SAdrian Knoth #define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */
447b0bf5504SAdrian Knoth #define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */
448763f356cSTakashi Iwai 
449763f356cSTakashi Iwai #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
4500dca1793SAdrian Knoth 			/* since 64byte accurate, last 6 bits are not used */
451763f356cSTakashi Iwai 
4520dca1793SAdrian Knoth 
4530dca1793SAdrian Knoth 
454763f356cSTakashi Iwai #define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
455763f356cSTakashi Iwai 
456763f356cSTakashi Iwai #define HDSPM_madiFreq0         (1<<22)	/* system freq 0=error */
457763f356cSTakashi Iwai #define HDSPM_madiFreq1         (1<<23)	/* 1=32, 2=44.1 3=48 */
458763f356cSTakashi Iwai #define HDSPM_madiFreq2         (1<<24)	/* 4=64, 5=88.2 6=96 */
459763f356cSTakashi Iwai #define HDSPM_madiFreq3         (1<<25)	/* 7=128, 8=176.4 9=192 */
460763f356cSTakashi Iwai 
461ef5fa1a4STakashi Iwai #define HDSPM_BufferID          (1<<26)	/* (Double)Buffer ID toggles with
462ef5fa1a4STakashi Iwai 					 * Interrupt
463ef5fa1a4STakashi Iwai 					 */
4640dca1793SAdrian Knoth #define HDSPM_tco_detect         0x08000000
465b0bf5504SAdrian Knoth #define HDSPM_tcoLockAes         0x20000000 /* Optional TCO locked status for HDSPe AES */
4660dca1793SAdrian Knoth 
4670dca1793SAdrian Knoth #define HDSPM_s2_tco_detect      0x00000040
4680dca1793SAdrian Knoth #define HDSPM_s2_AEBO_D          0x00000080
4690dca1793SAdrian Knoth #define HDSPM_s2_AEBI_D          0x00000100
4700dca1793SAdrian Knoth 
4710dca1793SAdrian Knoth 
4720dca1793SAdrian Knoth #define HDSPM_midi0IRQPending    0x40000000
4730dca1793SAdrian Knoth #define HDSPM_midi1IRQPending    0x80000000
4740dca1793SAdrian Knoth #define HDSPM_midi2IRQPending    0x20000000
4750dca1793SAdrian Knoth #define HDSPM_midi2IRQPendingAES 0x00000020
4760dca1793SAdrian Knoth #define HDSPM_midi3IRQPending    0x00200000
477763f356cSTakashi Iwai 
478763f356cSTakashi Iwai /* --- status bit helpers */
479ef5fa1a4STakashi Iwai #define HDSPM_madiFreqMask  (HDSPM_madiFreq0|HDSPM_madiFreq1|\
480ef5fa1a4STakashi Iwai 			     HDSPM_madiFreq2|HDSPM_madiFreq3)
481763f356cSTakashi Iwai #define HDSPM_madiFreq32    (HDSPM_madiFreq0)
482763f356cSTakashi Iwai #define HDSPM_madiFreq44_1  (HDSPM_madiFreq1)
483763f356cSTakashi Iwai #define HDSPM_madiFreq48    (HDSPM_madiFreq0|HDSPM_madiFreq1)
484763f356cSTakashi Iwai #define HDSPM_madiFreq64    (HDSPM_madiFreq2)
485763f356cSTakashi Iwai #define HDSPM_madiFreq88_2  (HDSPM_madiFreq0|HDSPM_madiFreq2)
486763f356cSTakashi Iwai #define HDSPM_madiFreq96    (HDSPM_madiFreq1|HDSPM_madiFreq2)
487763f356cSTakashi Iwai #define HDSPM_madiFreq128   (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2)
488763f356cSTakashi Iwai #define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
489763f356cSTakashi Iwai #define HDSPM_madiFreq192   (HDSPM_madiFreq3|HDSPM_madiFreq0)
490763f356cSTakashi Iwai 
4913cee5a60SRemy Bruno /* Status2 Register bits */ /* MADI ONLY */
492763f356cSTakashi Iwai 
49325985edcSLucas De Marchi #define HDSPM_version0 (1<<0)	/* not really defined but I guess */
494763f356cSTakashi Iwai #define HDSPM_version1 (1<<1)	/* in former cards it was ??? */
495763f356cSTakashi Iwai #define HDSPM_version2 (1<<2)
496763f356cSTakashi Iwai 
497763f356cSTakashi Iwai #define HDSPM_wcLock (1<<3)	/* Wordclock is detected and locked */
498763f356cSTakashi Iwai #define HDSPM_wcSync (1<<4)	/* Wordclock is in sync with systemclock */
499763f356cSTakashi Iwai 
500763f356cSTakashi Iwai #define HDSPM_wc_freq0 (1<<5)	/* input freq detected via autosync  */
501763f356cSTakashi Iwai #define HDSPM_wc_freq1 (1<<6)	/* 001=32, 010==44.1, 011=48, */
502a8cd7148SAdrian Knoth #define HDSPM_wc_freq2 (1<<7)	/* 100=64, 101=88.2, 110=96, 111=128 */
503a8cd7148SAdrian Knoth #define HDSPM_wc_freq3 0x800	/* 1000=176.4, 1001=192 */
504763f356cSTakashi Iwai 
5050dca1793SAdrian Knoth #define HDSPM_SyncRef0 0x10000  /* Sync Reference */
5060dca1793SAdrian Knoth #define HDSPM_SyncRef1 0x20000
5070dca1793SAdrian Knoth 
5080dca1793SAdrian Knoth #define HDSPM_SelSyncRef0 (1<<8)	/* AutoSync Source */
509763f356cSTakashi Iwai #define HDSPM_SelSyncRef1 (1<<9)	/* 000=word, 001=MADI, */
510763f356cSTakashi Iwai #define HDSPM_SelSyncRef2 (1<<10)	/* 111=no valid signal */
511763f356cSTakashi Iwai 
512763f356cSTakashi Iwai #define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
513763f356cSTakashi Iwai 
514a8cd7148SAdrian Knoth #define HDSPM_wcFreqMask  (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2|\
515a8cd7148SAdrian Knoth 			    HDSPM_wc_freq3)
516763f356cSTakashi Iwai #define HDSPM_wcFreq32    (HDSPM_wc_freq0)
517763f356cSTakashi Iwai #define HDSPM_wcFreq44_1  (HDSPM_wc_freq1)
518763f356cSTakashi Iwai #define HDSPM_wcFreq48    (HDSPM_wc_freq0|HDSPM_wc_freq1)
519763f356cSTakashi Iwai #define HDSPM_wcFreq64    (HDSPM_wc_freq2)
520763f356cSTakashi Iwai #define HDSPM_wcFreq88_2  (HDSPM_wc_freq0|HDSPM_wc_freq2)
521763f356cSTakashi Iwai #define HDSPM_wcFreq96    (HDSPM_wc_freq1|HDSPM_wc_freq2)
522a8cd7148SAdrian Knoth #define HDSPM_wcFreq128   (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
523a8cd7148SAdrian Knoth #define HDSPM_wcFreq176_4 (HDSPM_wc_freq3)
524a8cd7148SAdrian Knoth #define HDSPM_wcFreq192   (HDSPM_wc_freq0|HDSPM_wc_freq3)
525763f356cSTakashi Iwai 
5260dca1793SAdrian Knoth #define HDSPM_status1_F_0 0x0400000
5270dca1793SAdrian Knoth #define HDSPM_status1_F_1 0x0800000
5280dca1793SAdrian Knoth #define HDSPM_status1_F_2 0x1000000
5290dca1793SAdrian Knoth #define HDSPM_status1_F_3 0x2000000
5300dca1793SAdrian Knoth #define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3)
5310dca1793SAdrian Knoth 
532763f356cSTakashi Iwai 
533ef5fa1a4STakashi Iwai #define HDSPM_SelSyncRefMask       (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
534ef5fa1a4STakashi Iwai 				    HDSPM_SelSyncRef2)
535763f356cSTakashi Iwai #define HDSPM_SelSyncRef_WORD      0
536763f356cSTakashi Iwai #define HDSPM_SelSyncRef_MADI      (HDSPM_SelSyncRef0)
5370dca1793SAdrian Knoth #define HDSPM_SelSyncRef_TCO       (HDSPM_SelSyncRef1)
5380dca1793SAdrian Knoth #define HDSPM_SelSyncRef_SyncIn    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1)
539ef5fa1a4STakashi Iwai #define HDSPM_SelSyncRef_NVALID    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
540ef5fa1a4STakashi Iwai 				    HDSPM_SelSyncRef2)
541763f356cSTakashi Iwai 
5423cee5a60SRemy Bruno /*
5433cee5a60SRemy Bruno    For AES32, bits for status, status2 and timecode are different
5443cee5a60SRemy Bruno */
5453cee5a60SRemy Bruno /* status */
5463cee5a60SRemy Bruno #define HDSPM_AES32_wcLock	0x0200000
54756bde0f3SAndre Schramm #define HDSPM_AES32_wcSync	0x0100000
5483cee5a60SRemy Bruno #define HDSPM_AES32_wcFreq_bit  22
5493cee5a60SRemy Bruno /* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
5503cee5a60SRemy Bruno   HDSPM_bit2freq */
5513cee5a60SRemy Bruno #define HDSPM_AES32_syncref_bit  16
5523cee5a60SRemy Bruno /* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
5533cee5a60SRemy Bruno 
5543cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_WORD 0
5553cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES1 1
5563cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES2 2
5573cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES3 3
5583cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES4 4
5593cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES5 5
5603cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
5613cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
5623cee5a60SRemy Bruno #define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
563b0bf5504SAdrian Knoth #define HDSPM_AES32_AUTOSYNC_FROM_TCO 9
564b0bf5504SAdrian Knoth #define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10
565b0bf5504SAdrian Knoth #define HDSPM_AES32_AUTOSYNC_FROM_NONE 11
5663cee5a60SRemy Bruno 
5673cee5a60SRemy Bruno /*  status2 */
5683cee5a60SRemy Bruno /* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
5693cee5a60SRemy Bruno #define HDSPM_LockAES   0x80
5703cee5a60SRemy Bruno #define HDSPM_LockAES1  0x80
5713cee5a60SRemy Bruno #define HDSPM_LockAES2  0x40
5723cee5a60SRemy Bruno #define HDSPM_LockAES3  0x20
5733cee5a60SRemy Bruno #define HDSPM_LockAES4  0x10
5743cee5a60SRemy Bruno #define HDSPM_LockAES5  0x8
5753cee5a60SRemy Bruno #define HDSPM_LockAES6  0x4
5763cee5a60SRemy Bruno #define HDSPM_LockAES7  0x2
5773cee5a60SRemy Bruno #define HDSPM_LockAES8  0x1
5783cee5a60SRemy Bruno /*
5793cee5a60SRemy Bruno    Timecode
5803cee5a60SRemy Bruno    After windows driver sources, bits 4*i to 4*i+3 give the input frequency on
5813cee5a60SRemy Bruno    AES i+1
5823cee5a60SRemy Bruno  bits 3210
5833cee5a60SRemy Bruno       0001  32kHz
5843cee5a60SRemy Bruno       0010  44.1kHz
5853cee5a60SRemy Bruno       0011  48kHz
5863cee5a60SRemy Bruno       0100  64kHz
5873cee5a60SRemy Bruno       0101  88.2kHz
5883cee5a60SRemy Bruno       0110  96kHz
5893cee5a60SRemy Bruno       0111  128kHz
5903cee5a60SRemy Bruno       1000  176.4kHz
5913cee5a60SRemy Bruno       1001  192kHz
5923cee5a60SRemy Bruno   NB: Timecode register doesn't seem to work on AES32 card revision 230
5933cee5a60SRemy Bruno */
5943cee5a60SRemy Bruno 
595763f356cSTakashi Iwai /* Mixer Values */
596763f356cSTakashi Iwai #define UNITY_GAIN          32768	/* = 65536/2 */
597763f356cSTakashi Iwai #define MINUS_INFINITY_GAIN 0
598763f356cSTakashi Iwai 
599763f356cSTakashi Iwai /* Number of channels for different Speed Modes */
600763f356cSTakashi Iwai #define MADI_SS_CHANNELS       64
601763f356cSTakashi Iwai #define MADI_DS_CHANNELS       32
602763f356cSTakashi Iwai #define MADI_QS_CHANNELS       16
603763f356cSTakashi Iwai 
6040dca1793SAdrian Knoth #define RAYDAT_SS_CHANNELS     36
6050dca1793SAdrian Knoth #define RAYDAT_DS_CHANNELS     20
6060dca1793SAdrian Knoth #define RAYDAT_QS_CHANNELS     12
6070dca1793SAdrian Knoth 
6080dca1793SAdrian Knoth #define AIO_IN_SS_CHANNELS        14
6090dca1793SAdrian Knoth #define AIO_IN_DS_CHANNELS        10
6100dca1793SAdrian Knoth #define AIO_IN_QS_CHANNELS        8
6110dca1793SAdrian Knoth #define AIO_OUT_SS_CHANNELS        16
6120dca1793SAdrian Knoth #define AIO_OUT_DS_CHANNELS        12
6130dca1793SAdrian Knoth #define AIO_OUT_QS_CHANNELS        10
6140dca1793SAdrian Knoth 
615d2d10a21SAdrian Knoth #define AES32_CHANNELS		16
616d2d10a21SAdrian Knoth 
617763f356cSTakashi Iwai /* the size of a substream (1 mono data stream) */
618763f356cSTakashi Iwai #define HDSPM_CHANNEL_BUFFER_SAMPLES  (16*1024)
619763f356cSTakashi Iwai #define HDSPM_CHANNEL_BUFFER_BYTES    (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
620763f356cSTakashi Iwai 
621763f356cSTakashi Iwai /* the size of the area we need to allocate for DMA transfers. the
622763f356cSTakashi Iwai    size is the same regardless of the number of channels, and
623763f356cSTakashi Iwai    also the latency to use.
624763f356cSTakashi Iwai    for one direction !!!
625763f356cSTakashi Iwai */
626ffb2c3c0SRemy Bruno #define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
627763f356cSTakashi Iwai #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
628763f356cSTakashi Iwai 
6290dca1793SAdrian Knoth #define HDSPM_RAYDAT_REV	211
6300dca1793SAdrian Knoth #define HDSPM_AIO_REV		212
6310dca1793SAdrian Knoth #define HDSPM_MADIFACE_REV	213
6323cee5a60SRemy Bruno 
6336534599dSRemy Bruno /* speed factor modes */
6346534599dSRemy Bruno #define HDSPM_SPEED_SINGLE 0
6356534599dSRemy Bruno #define HDSPM_SPEED_DOUBLE 1
6366534599dSRemy Bruno #define HDSPM_SPEED_QUAD   2
6370dca1793SAdrian Knoth 
6386534599dSRemy Bruno /* names for speed modes */
639e315cc3fSTakashi Iwai static const char * const hdspm_speed_names[] = { "single", "double", "quad" };
6406534599dSRemy Bruno 
641eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aes_tco[] = { "Word Clock",
6420dca1793SAdrian Knoth 					  "AES1", "AES2", "AES3", "AES4",
6430dca1793SAdrian Knoth 					  "AES5", "AES6", "AES7", "AES8",
644db2d1a91SAdrian Knoth 					  "TCO", "Sync In"
645db2d1a91SAdrian Knoth };
646eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aes[] = { "Word Clock",
6470dca1793SAdrian Knoth 				      "AES1", "AES2", "AES3", "AES4",
648db2d1a91SAdrian Knoth 				      "AES5", "AES6", "AES7", "AES8",
649db2d1a91SAdrian Knoth 				      "Sync In"
650db2d1a91SAdrian Knoth };
651eb0d4dbfSAdrian Knoth static const char *const texts_autosync_madi_tco[] = { "Word Clock",
6520dca1793SAdrian Knoth 					   "MADI", "TCO", "Sync In" };
653eb0d4dbfSAdrian Knoth static const char *const texts_autosync_madi[] = { "Word Clock",
6540dca1793SAdrian Knoth 				       "MADI", "Sync In" };
6550dca1793SAdrian Knoth 
656eb0d4dbfSAdrian Knoth static const char *const texts_autosync_raydat_tco[] = {
6570dca1793SAdrian Knoth 	"Word Clock",
6580dca1793SAdrian Knoth 	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
6590dca1793SAdrian Knoth 	"AES", "SPDIF", "TCO", "Sync In"
6600dca1793SAdrian Knoth };
661eb0d4dbfSAdrian Knoth static const char *const texts_autosync_raydat[] = {
6620dca1793SAdrian Knoth 	"Word Clock",
6630dca1793SAdrian Knoth 	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
6640dca1793SAdrian Knoth 	"AES", "SPDIF", "Sync In"
6650dca1793SAdrian Knoth };
666eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aio_tco[] = {
6670dca1793SAdrian Knoth 	"Word Clock",
6680dca1793SAdrian Knoth 	"ADAT", "AES", "SPDIF", "TCO", "Sync In"
6690dca1793SAdrian Knoth };
670eb0d4dbfSAdrian Knoth static const char *const texts_autosync_aio[] = { "Word Clock",
6710dca1793SAdrian Knoth 				      "ADAT", "AES", "SPDIF", "Sync In" };
6720dca1793SAdrian Knoth 
67338816545SAdrian Knoth static const char *const texts_freq[] = {
6740dca1793SAdrian Knoth 	"No Lock",
6750dca1793SAdrian Knoth 	"32 kHz",
6760dca1793SAdrian Knoth 	"44.1 kHz",
6770dca1793SAdrian Knoth 	"48 kHz",
6780dca1793SAdrian Knoth 	"64 kHz",
6790dca1793SAdrian Knoth 	"88.2 kHz",
6800dca1793SAdrian Knoth 	"96 kHz",
6810dca1793SAdrian Knoth 	"128 kHz",
6820dca1793SAdrian Knoth 	"176.4 kHz",
6830dca1793SAdrian Knoth 	"192 kHz"
6840dca1793SAdrian Knoth };
6850dca1793SAdrian Knoth 
686e315cc3fSTakashi Iwai static const char * const texts_ports_madi[] = {
6870dca1793SAdrian Knoth 	"MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6",
6880dca1793SAdrian Knoth 	"MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12",
6890dca1793SAdrian Knoth 	"MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18",
6900dca1793SAdrian Knoth 	"MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24",
6910dca1793SAdrian Knoth 	"MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30",
6920dca1793SAdrian Knoth 	"MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36",
6930dca1793SAdrian Knoth 	"MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42",
6940dca1793SAdrian Knoth 	"MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48",
6950dca1793SAdrian Knoth 	"MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54",
6960dca1793SAdrian Knoth 	"MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60",
6970dca1793SAdrian Knoth 	"MADI.61", "MADI.62", "MADI.63", "MADI.64",
6980dca1793SAdrian Knoth };
6990dca1793SAdrian Knoth 
7000dca1793SAdrian Knoth 
701e315cc3fSTakashi Iwai static const char * const texts_ports_raydat_ss[] = {
7020dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6",
7030dca1793SAdrian Knoth 	"ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
7040dca1793SAdrian Knoth 	"ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2",
7050dca1793SAdrian Knoth 	"ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8",
7060dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6",
7070dca1793SAdrian Knoth 	"ADAT4.7", "ADAT4.8",
7080dca1793SAdrian Knoth 	"AES.L", "AES.R",
7090dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
7100dca1793SAdrian Knoth };
7110dca1793SAdrian Knoth 
712e315cc3fSTakashi Iwai static const char * const texts_ports_raydat_ds[] = {
7130dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4",
7140dca1793SAdrian Knoth 	"ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
7150dca1793SAdrian Knoth 	"ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4",
7160dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4",
7170dca1793SAdrian Knoth 	"AES.L", "AES.R",
7180dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
7190dca1793SAdrian Knoth };
7200dca1793SAdrian Knoth 
721e315cc3fSTakashi Iwai static const char * const texts_ports_raydat_qs[] = {
7220dca1793SAdrian Knoth 	"ADAT1.1", "ADAT1.2",
7230dca1793SAdrian Knoth 	"ADAT2.1", "ADAT2.2",
7240dca1793SAdrian Knoth 	"ADAT3.1", "ADAT3.2",
7250dca1793SAdrian Knoth 	"ADAT4.1", "ADAT4.2",
7260dca1793SAdrian Knoth 	"AES.L", "AES.R",
7270dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R"
7280dca1793SAdrian Knoth };
7290dca1793SAdrian Knoth 
7300dca1793SAdrian Knoth 
731e315cc3fSTakashi Iwai static const char * const texts_ports_aio_in_ss[] = {
7320dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7330dca1793SAdrian Knoth 	"AES.L", "AES.R",
7340dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7350dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
7363de9db26SAdrian Knoth 	"ADAT.7", "ADAT.8",
7373de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7380dca1793SAdrian Knoth };
7390dca1793SAdrian Knoth 
740e315cc3fSTakashi Iwai static const char * const texts_ports_aio_out_ss[] = {
7410dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7420dca1793SAdrian Knoth 	"AES.L", "AES.R",
7430dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7440dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
7450dca1793SAdrian Knoth 	"ADAT.7", "ADAT.8",
7463de9db26SAdrian Knoth 	"Phone.L", "Phone.R",
7473de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7480dca1793SAdrian Knoth };
7490dca1793SAdrian Knoth 
750e315cc3fSTakashi Iwai static const char * const texts_ports_aio_in_ds[] = {
7510dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7520dca1793SAdrian Knoth 	"AES.L", "AES.R",
7530dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7543de9db26SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7553de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7560dca1793SAdrian Knoth };
7570dca1793SAdrian Knoth 
758e315cc3fSTakashi Iwai static const char * const texts_ports_aio_out_ds[] = {
7590dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7600dca1793SAdrian Knoth 	"AES.L", "AES.R",
7610dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7620dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7633de9db26SAdrian Knoth 	"Phone.L", "Phone.R",
7643de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7650dca1793SAdrian Knoth };
7660dca1793SAdrian Knoth 
767e315cc3fSTakashi Iwai static const char * const texts_ports_aio_in_qs[] = {
7680dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7690dca1793SAdrian Knoth 	"AES.L", "AES.R",
7700dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7713de9db26SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7723de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7730dca1793SAdrian Knoth };
7740dca1793SAdrian Knoth 
775e315cc3fSTakashi Iwai static const char * const texts_ports_aio_out_qs[] = {
7760dca1793SAdrian Knoth 	"Analogue.L", "Analogue.R",
7770dca1793SAdrian Knoth 	"AES.L", "AES.R",
7780dca1793SAdrian Knoth 	"SPDIF.L", "SPDIF.R",
7790dca1793SAdrian Knoth 	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
7803de9db26SAdrian Knoth 	"Phone.L", "Phone.R",
7813de9db26SAdrian Knoth 	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
7820dca1793SAdrian Knoth };
7830dca1793SAdrian Knoth 
784e315cc3fSTakashi Iwai static const char * const texts_ports_aes32[] = {
785432d2500SAdrian Knoth 	"AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7",
786432d2500SAdrian Knoth 	"AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14",
787432d2500SAdrian Knoth 	"AES.15", "AES.16"
788432d2500SAdrian Knoth };
789432d2500SAdrian Knoth 
79055a57606SAdrian Knoth /* These tables map the ALSA channels 1..N to the channels that we
79155a57606SAdrian Knoth    need to use in order to find the relevant channel buffer. RME
79255a57606SAdrian Knoth    refers to this kind of mapping as between "the ADAT channel and
79355a57606SAdrian Knoth    the DMA channel." We index it using the logical audio channel,
79455a57606SAdrian Knoth    and the value is the DMA channel (i.e. channel buffer number)
79555a57606SAdrian Knoth    where the data for that channel can be read/written from/to.
79655a57606SAdrian Knoth */
79755a57606SAdrian Knoth 
798e315cc3fSTakashi Iwai static const char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
79955a57606SAdrian Knoth 	0, 1, 2, 3, 4, 5, 6, 7,
80055a57606SAdrian Knoth 	8, 9, 10, 11, 12, 13, 14, 15,
80155a57606SAdrian Knoth 	16, 17, 18, 19, 20, 21, 22, 23,
80255a57606SAdrian Knoth 	24, 25, 26, 27, 28, 29, 30, 31,
80355a57606SAdrian Knoth 	32, 33, 34, 35, 36, 37, 38, 39,
80455a57606SAdrian Knoth 	40, 41, 42, 43, 44, 45, 46, 47,
80555a57606SAdrian Knoth 	48, 49, 50, 51, 52, 53, 54, 55,
80655a57606SAdrian Knoth 	56, 57, 58, 59, 60, 61, 62, 63
80755a57606SAdrian Knoth };
80855a57606SAdrian Knoth 
809e315cc3fSTakashi Iwai static const char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
81055a57606SAdrian Knoth 	4, 5, 6, 7, 8, 9, 10, 11,	/* ADAT 1 */
81155a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT 2 */
81255a57606SAdrian Knoth 	20, 21, 22, 23, 24, 25, 26, 27,	/* ADAT 3 */
81355a57606SAdrian Knoth 	28, 29, 30, 31, 32, 33, 34, 35,	/* ADAT 4 */
81455a57606SAdrian Knoth 	0, 1,			/* AES */
81555a57606SAdrian Knoth 	2, 3,			/* SPDIF */
81655a57606SAdrian Knoth 	-1, -1, -1, -1,
81755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
81855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
81955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
82055a57606SAdrian Knoth };
82155a57606SAdrian Knoth 
822e315cc3fSTakashi Iwai static const char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
82355a57606SAdrian Knoth 	4, 5, 6, 7,		/* ADAT 1 */
82455a57606SAdrian Knoth 	8, 9, 10, 11,		/* ADAT 2 */
82555a57606SAdrian Knoth 	12, 13, 14, 15,		/* ADAT 3 */
82655a57606SAdrian Knoth 	16, 17, 18, 19,		/* ADAT 4 */
82755a57606SAdrian Knoth 	0, 1,			/* AES */
82855a57606SAdrian Knoth 	2, 3,			/* SPDIF */
82955a57606SAdrian Knoth 	-1, -1, -1, -1,
83055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
83155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
83255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
83355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
83455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
83555a57606SAdrian Knoth };
83655a57606SAdrian Knoth 
837e315cc3fSTakashi Iwai static const char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
83855a57606SAdrian Knoth 	4, 5,			/* ADAT 1 */
83955a57606SAdrian Knoth 	6, 7,			/* ADAT 2 */
84055a57606SAdrian Knoth 	8, 9,			/* ADAT 3 */
84155a57606SAdrian Knoth 	10, 11,			/* ADAT 4 */
84255a57606SAdrian Knoth 	0, 1,			/* AES */
84355a57606SAdrian Knoth 	2, 3,			/* SPDIF */
84455a57606SAdrian Knoth 	-1, -1, -1, -1,
84555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
84955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
85055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
85155a57606SAdrian Knoth };
85255a57606SAdrian Knoth 
853e315cc3fSTakashi Iwai static const char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
85455a57606SAdrian Knoth 	0, 1,			/* line in */
85555a57606SAdrian Knoth 	8, 9,			/* aes in, */
85655a57606SAdrian Knoth 	10, 11,			/* spdif in */
85755a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT in */
8583de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
8593de9db26SAdrian Knoth 	-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 	-1, -1, -1, -1, -1, -1, -1, -1,
86355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
86455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
86555a57606SAdrian Knoth };
86655a57606SAdrian Knoth 
867e315cc3fSTakashi Iwai static const char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
86855a57606SAdrian Knoth 	0, 1,			/* line out */
86955a57606SAdrian Knoth 	8, 9,			/* aes out */
87055a57606SAdrian Knoth 	10, 11,			/* spdif out */
87155a57606SAdrian Knoth 	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT out */
87255a57606SAdrian Knoth 	6, 7,			/* phone out */
8733de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
8743de9db26SAdrian Knoth 	-1, -1, -1, -1,
87555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
87955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
88055a57606SAdrian Knoth };
88155a57606SAdrian Knoth 
882e315cc3fSTakashi Iwai static const char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
88355a57606SAdrian Knoth 	0, 1,			/* line in */
88455a57606SAdrian Knoth 	8, 9,			/* aes in */
88555a57606SAdrian Knoth 	10, 11,			/* spdif in */
88655a57606SAdrian Knoth 	12, 14, 16, 18,		/* adat in */
8873de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
8883de9db26SAdrian Knoth 	-1, -1,
88955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
89055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
89155a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
89255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
89355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
89455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
89555a57606SAdrian Knoth };
89655a57606SAdrian Knoth 
897e315cc3fSTakashi Iwai static const char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
89855a57606SAdrian Knoth 	0, 1,			/* line out */
89955a57606SAdrian Knoth 	8, 9,			/* aes out */
90055a57606SAdrian Knoth 	10, 11,			/* spdif out */
90155a57606SAdrian Knoth 	12, 14, 16, 18,		/* adat out */
90255a57606SAdrian Knoth 	6, 7,			/* phone out */
9033de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
90455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
90955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
91055a57606SAdrian Knoth };
91155a57606SAdrian Knoth 
912e315cc3fSTakashi Iwai static const char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
91355a57606SAdrian Knoth 	0, 1,			/* line in */
91455a57606SAdrian Knoth 	8, 9,			/* aes in */
91555a57606SAdrian Knoth 	10, 11,			/* spdif in */
91655a57606SAdrian Knoth 	12, 16,			/* adat in */
9173de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
9183de9db26SAdrian Knoth 	-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 	-1, -1, -1, -1, -1, -1, -1, -1,
92255a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
92355a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
92455a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
92555a57606SAdrian Knoth };
92655a57606SAdrian Knoth 
927e315cc3fSTakashi Iwai static const char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
92855a57606SAdrian Knoth 	0, 1,			/* line out */
92955a57606SAdrian Knoth 	8, 9,			/* aes out */
93055a57606SAdrian Knoth 	10, 11,			/* spdif out */
93155a57606SAdrian Knoth 	12, 16,			/* adat out */
93255a57606SAdrian Knoth 	6, 7,			/* phone out */
9333de9db26SAdrian Knoth 	2, 3, 4, 5,		/* AEB */
9343de9db26SAdrian Knoth 	-1, -1,
93555a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93655a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93755a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93855a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
93955a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
94055a57606SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
94155a57606SAdrian Knoth };
94255a57606SAdrian Knoth 
943e315cc3fSTakashi Iwai static const char channel_map_aes32[HDSPM_MAX_CHANNELS] = {
944432d2500SAdrian Knoth 	0, 1, 2, 3, 4, 5, 6, 7,
945432d2500SAdrian Knoth 	8, 9, 10, 11, 12, 13, 14, 15,
946432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
947432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
948432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
949432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
950432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1,
951432d2500SAdrian Knoth 	-1, -1, -1, -1, -1, -1, -1, -1
952432d2500SAdrian Knoth };
953432d2500SAdrian Knoth 
95498274f07STakashi Iwai struct hdspm_midi {
95598274f07STakashi Iwai 	struct hdspm *hdspm;
956763f356cSTakashi Iwai 	int id;
95798274f07STakashi Iwai 	struct snd_rawmidi *rmidi;
95898274f07STakashi Iwai 	struct snd_rawmidi_substream *input;
95998274f07STakashi Iwai 	struct snd_rawmidi_substream *output;
960763f356cSTakashi Iwai 	char istimer;		/* timer in use */
961763f356cSTakashi Iwai 	struct timer_list timer;
962763f356cSTakashi Iwai 	spinlock_t lock;
963763f356cSTakashi Iwai 	int pending;
9640dca1793SAdrian Knoth 	int dataIn;
9650dca1793SAdrian Knoth 	int statusIn;
9660dca1793SAdrian Knoth 	int dataOut;
9670dca1793SAdrian Knoth 	int statusOut;
9680dca1793SAdrian Knoth 	int ie;
9690dca1793SAdrian Knoth 	int irq;
9700dca1793SAdrian Knoth };
9710dca1793SAdrian Knoth 
9720dca1793SAdrian Knoth struct hdspm_tco {
97369358fcaSMartin Dausel 	int input; /* 0: LTC, 1:Video, 2: WC*/
97469358fcaSMartin Dausel 	int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */
97569358fcaSMartin Dausel 	int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */
97669358fcaSMartin Dausel 	int samplerate; /* 0=44.1, 1=48, 2= freq from app */
97769358fcaSMartin Dausel 	int pull; /*   0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/
9780dca1793SAdrian Knoth 	int term; /* 0 = off, 1 = on */
979763f356cSTakashi Iwai };
980763f356cSTakashi Iwai 
98198274f07STakashi Iwai struct hdspm {
982763f356cSTakashi Iwai         spinlock_t lock;
983ef5fa1a4STakashi Iwai 	/* only one playback and/or capture stream */
984ef5fa1a4STakashi Iwai         struct snd_pcm_substream *capture_substream;
985ef5fa1a4STakashi Iwai         struct snd_pcm_substream *playback_substream;
986763f356cSTakashi Iwai 
987763f356cSTakashi Iwai 	char *card_name;	     /* for procinfo */
9883cee5a60SRemy Bruno 	unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
9893cee5a60SRemy Bruno 
9900dca1793SAdrian Knoth 	uint8_t io_type;
991763f356cSTakashi Iwai 
992763f356cSTakashi Iwai 	int monitor_outs;	/* set up monitoring outs init flag */
993763f356cSTakashi Iwai 
994763f356cSTakashi Iwai 	u32 control_register;	/* cached value */
995763f356cSTakashi Iwai 	u32 control2_register;	/* cached value */
99669358fcaSMartin Dausel 	u32 settings_register;  /* cached value for AIO / RayDat (sync reference, master/slave) */
997763f356cSTakashi Iwai 
9980dca1793SAdrian Knoth 	struct hdspm_midi midi[4];
999a2e527c5STakashi Iwai 	struct work_struct midi_work;
1000763f356cSTakashi Iwai 
1001763f356cSTakashi Iwai 	size_t period_bytes;
10020dca1793SAdrian Knoth 	unsigned char ss_in_channels;
10030dca1793SAdrian Knoth 	unsigned char ds_in_channels;
10040dca1793SAdrian Knoth 	unsigned char qs_in_channels;
10050dca1793SAdrian Knoth 	unsigned char ss_out_channels;
10060dca1793SAdrian Knoth 	unsigned char ds_out_channels;
10070dca1793SAdrian Knoth 	unsigned char qs_out_channels;
10080dca1793SAdrian Knoth 
10090dca1793SAdrian Knoth 	unsigned char max_channels_in;
10100dca1793SAdrian Knoth 	unsigned char max_channels_out;
10110dca1793SAdrian Knoth 
1012e315cc3fSTakashi Iwai 	const signed char *channel_map_in;
1013e315cc3fSTakashi Iwai 	const signed char *channel_map_out;
10140dca1793SAdrian Knoth 
1015e315cc3fSTakashi Iwai 	const signed char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
1016e315cc3fSTakashi Iwai 	const signed char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
10170dca1793SAdrian Knoth 
1018e315cc3fSTakashi Iwai 	const char * const *port_names_in;
1019e315cc3fSTakashi Iwai 	const char * const *port_names_out;
10200dca1793SAdrian Knoth 
1021e315cc3fSTakashi Iwai 	const char * const *port_names_in_ss;
1022e315cc3fSTakashi Iwai 	const char * const *port_names_in_ds;
1023e315cc3fSTakashi Iwai 	const char * const *port_names_in_qs;
1024e315cc3fSTakashi Iwai 	const char * const *port_names_out_ss;
1025e315cc3fSTakashi Iwai 	const char * const *port_names_out_ds;
1026e315cc3fSTakashi Iwai 	const char * const *port_names_out_qs;
1027763f356cSTakashi Iwai 
1028763f356cSTakashi Iwai 	unsigned char *playback_buffer;	/* suitably aligned address */
1029763f356cSTakashi Iwai 	unsigned char *capture_buffer;	/* suitably aligned address */
1030763f356cSTakashi Iwai 
1031763f356cSTakashi Iwai 	pid_t capture_pid;	/* process id which uses capture */
1032763f356cSTakashi Iwai 	pid_t playback_pid;	/* process id which uses capture */
1033763f356cSTakashi Iwai 	int running;		/* running status */
1034763f356cSTakashi Iwai 
1035763f356cSTakashi Iwai 	int last_external_sample_rate;	/* samplerate mystic ... */
1036763f356cSTakashi Iwai 	int last_internal_sample_rate;
1037763f356cSTakashi Iwai 	int system_sample_rate;
1038763f356cSTakashi Iwai 
1039763f356cSTakashi Iwai 	int dev;		/* Hardware vars... */
1040763f356cSTakashi Iwai 	int irq;
1041763f356cSTakashi Iwai 	unsigned long port;
1042763f356cSTakashi Iwai 	void __iomem *iobase;
1043763f356cSTakashi Iwai 
1044763f356cSTakashi Iwai 	int irq_count;		/* for debug */
10450dca1793SAdrian Knoth 	int midiPorts;
1046763f356cSTakashi Iwai 
104798274f07STakashi Iwai 	struct snd_card *card;	/* one card */
104898274f07STakashi Iwai 	struct snd_pcm *pcm;		/* has one pcm */
104998274f07STakashi Iwai 	struct snd_hwdep *hwdep;	/* and a hwdep for additional ioctl */
1050763f356cSTakashi Iwai 	struct pci_dev *pci;	/* and an pci info */
1051763f356cSTakashi Iwai 
1052763f356cSTakashi Iwai 	/* Mixer vars */
1053ef5fa1a4STakashi Iwai 	/* fast alsa mixer */
1054ef5fa1a4STakashi Iwai 	struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS];
1055ef5fa1a4STakashi Iwai 	/* but input to much, so not used */
1056ef5fa1a4STakashi Iwai 	struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS];
105725985edcSLucas De Marchi 	/* full mixer accessible over mixer ioctl or hwdep-device */
1058ef5fa1a4STakashi Iwai 	struct hdspm_mixer *mixer;
1059763f356cSTakashi Iwai 
10600dca1793SAdrian Knoth 	struct hdspm_tco *tco;  /* NULL if no TCO detected */
1061763f356cSTakashi Iwai 
1062eb0d4dbfSAdrian Knoth 	const char *const *texts_autosync;
10630dca1793SAdrian Knoth 	int texts_autosync_items;
1064763f356cSTakashi Iwai 
10650dca1793SAdrian Knoth 	cycles_t last_interrupt;
1066730a5865SJaroslav Kysela 
10677d53a631SAdrian Knoth 	unsigned int serial;
10687d53a631SAdrian Knoth 
1069730a5865SJaroslav Kysela 	struct hdspm_peak_rms peak_rms;
1070763f356cSTakashi Iwai };
1071763f356cSTakashi Iwai 
1072763f356cSTakashi Iwai 
10739baa3c34SBenoit Taine static const struct pci_device_id snd_hdspm_ids[] = {
1074763f356cSTakashi Iwai 	{
1075763f356cSTakashi Iwai 	 .vendor = PCI_VENDOR_ID_XILINX,
1076763f356cSTakashi Iwai 	 .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
1077763f356cSTakashi Iwai 	 .subvendor = PCI_ANY_ID,
1078763f356cSTakashi Iwai 	 .subdevice = PCI_ANY_ID,
1079763f356cSTakashi Iwai 	 .class = 0,
1080763f356cSTakashi Iwai 	 .class_mask = 0,
1081763f356cSTakashi Iwai 	 .driver_data = 0},
1082763f356cSTakashi Iwai 	{0,}
1083763f356cSTakashi Iwai };
1084763f356cSTakashi Iwai 
1085763f356cSTakashi Iwai MODULE_DEVICE_TABLE(pci, snd_hdspm_ids);
1086763f356cSTakashi Iwai 
1087763f356cSTakashi Iwai /* prototypes */
1088e23e7a14SBill Pemberton static int snd_hdspm_create_alsa_devices(struct snd_card *card,
108998274f07STakashi Iwai 					 struct hdspm *hdspm);
1090e23e7a14SBill Pemberton static int snd_hdspm_create_pcm(struct snd_card *card,
109198274f07STakashi Iwai 				struct hdspm *hdspm);
1092763f356cSTakashi Iwai 
109398274f07STakashi Iwai static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
10943f7bf918SAdrian Knoth static inline int hdspm_get_pll_freq(struct hdspm *hdspm);
109598274f07STakashi Iwai static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
109698274f07STakashi Iwai static int hdspm_autosync_ref(struct hdspm *hdspm);
109734be7ebbSAdrian Knoth static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out);
109898274f07STakashi Iwai static int snd_hdspm_set_defaults(struct hdspm *hdspm);
109921a164dfSAdrian Knoth static int hdspm_system_clock_mode(struct hdspm *hdspm);
1100e4e07c6cSPhilippe Bekaert static void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
110177a23f26STakashi Iwai 				       struct snd_pcm_substream *substream,
1102763f356cSTakashi Iwai 				       unsigned int reg, int channels);
1103763f356cSTakashi Iwai 
11045b266354SAdrian Knoth static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx);
11055b266354SAdrian Knoth static int hdspm_wc_sync_check(struct hdspm *hdspm);
11065b266354SAdrian Knoth static int hdspm_tco_sync_check(struct hdspm *hdspm);
11075b266354SAdrian Knoth static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
11085b266354SAdrian Knoth 
11095b266354SAdrian Knoth static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index);
11105b266354SAdrian Knoth static int hdspm_get_tco_sample_rate(struct hdspm *hdspm);
11115b266354SAdrian Knoth static int hdspm_get_wc_sample_rate(struct hdspm *hdspm);
11125b266354SAdrian Knoth 
11135b266354SAdrian Knoth 
11145b266354SAdrian Knoth 
HDSPM_bit2freq(int n)11153cee5a60SRemy Bruno static inline int HDSPM_bit2freq(int n)
11163cee5a60SRemy Bruno {
111762cef821SDenys Vlasenko 	static const int bit2freq_tab[] = {
111862cef821SDenys Vlasenko 		0, 32000, 44100, 48000, 64000, 88200,
11193cee5a60SRemy Bruno 		96000, 128000, 176400, 192000 };
11203cee5a60SRemy Bruno 	if (n < 1 || n > 9)
11213cee5a60SRemy Bruno 		return 0;
11223cee5a60SRemy Bruno 	return bit2freq_tab[n];
11233cee5a60SRemy Bruno }
11243cee5a60SRemy Bruno 
hdspm_is_raydat_or_aio(struct hdspm * hdspm)1125b2ed6326SAdrian Knoth static bool hdspm_is_raydat_or_aio(struct hdspm *hdspm)
1126b2ed6326SAdrian Knoth {
1127b2ed6326SAdrian Knoth 	return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type));
1128b2ed6326SAdrian Knoth }
1129b2ed6326SAdrian Knoth 
1130b2ed6326SAdrian Knoth 
11310dca1793SAdrian Knoth /* Write/read to/from HDSPM with Adresses in Bytes
1132763f356cSTakashi Iwai    not words but only 32Bit writes are allowed */
1133763f356cSTakashi Iwai 
hdspm_write(struct hdspm * hdspm,unsigned int reg,unsigned int val)113498274f07STakashi Iwai static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg,
1135763f356cSTakashi Iwai 			       unsigned int val)
1136763f356cSTakashi Iwai {
1137763f356cSTakashi Iwai 	writel(val, hdspm->iobase + reg);
1138763f356cSTakashi Iwai }
1139763f356cSTakashi Iwai 
hdspm_read(struct hdspm * hdspm,unsigned int reg)114098274f07STakashi Iwai static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg)
1141763f356cSTakashi Iwai {
1142763f356cSTakashi Iwai 	return readl(hdspm->iobase + reg);
1143763f356cSTakashi Iwai }
1144763f356cSTakashi Iwai 
1145763f356cSTakashi Iwai /* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
1146763f356cSTakashi Iwai    mixer is write only on hardware so we have to cache him for read
1147763f356cSTakashi Iwai    each fader is a u32, but uses only the first 16 bit */
1148763f356cSTakashi Iwai 
hdspm_read_in_gain(struct hdspm * hdspm,unsigned int chan,unsigned int in)114998274f07STakashi Iwai static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan,
1150763f356cSTakashi Iwai 				     unsigned int in)
1151763f356cSTakashi Iwai {
11525bab2482SAdrian Bunk 	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
1153763f356cSTakashi Iwai 		return 0;
1154763f356cSTakashi Iwai 
1155763f356cSTakashi Iwai 	return hdspm->mixer->ch[chan].in[in];
1156763f356cSTakashi Iwai }
1157763f356cSTakashi Iwai 
hdspm_read_pb_gain(struct hdspm * hdspm,unsigned int chan,unsigned int pb)115898274f07STakashi Iwai static inline int hdspm_read_pb_gain(struct hdspm * hdspm, unsigned int chan,
1159763f356cSTakashi Iwai 				     unsigned int pb)
1160763f356cSTakashi Iwai {
11615bab2482SAdrian Bunk 	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
1162763f356cSTakashi Iwai 		return 0;
1163763f356cSTakashi Iwai 	return hdspm->mixer->ch[chan].pb[pb];
1164763f356cSTakashi Iwai }
1165763f356cSTakashi Iwai 
hdspm_write_in_gain(struct hdspm * hdspm,unsigned int chan,unsigned int in,unsigned short data)116662cef821SDenys Vlasenko static int hdspm_write_in_gain(struct hdspm *hdspm, unsigned int chan,
1167763f356cSTakashi Iwai 				      unsigned int in, unsigned short data)
1168763f356cSTakashi Iwai {
1169763f356cSTakashi Iwai 	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
1170763f356cSTakashi Iwai 		return -1;
1171763f356cSTakashi Iwai 
1172763f356cSTakashi Iwai 	hdspm_write(hdspm,
1173763f356cSTakashi Iwai 		    HDSPM_MADI_mixerBase +
1174763f356cSTakashi Iwai 		    ((in + 128 * chan) * sizeof(u32)),
1175763f356cSTakashi Iwai 		    (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF));
1176763f356cSTakashi Iwai 	return 0;
1177763f356cSTakashi Iwai }
1178763f356cSTakashi Iwai 
hdspm_write_pb_gain(struct hdspm * hdspm,unsigned int chan,unsigned int pb,unsigned short data)117962cef821SDenys Vlasenko static int hdspm_write_pb_gain(struct hdspm *hdspm, unsigned int chan,
1180763f356cSTakashi Iwai 				      unsigned int pb, unsigned short data)
1181763f356cSTakashi Iwai {
1182763f356cSTakashi Iwai 	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
1183763f356cSTakashi Iwai 		return -1;
1184763f356cSTakashi Iwai 
1185763f356cSTakashi Iwai 	hdspm_write(hdspm,
1186763f356cSTakashi Iwai 		    HDSPM_MADI_mixerBase +
1187763f356cSTakashi Iwai 		    ((64 + pb + 128 * chan) * sizeof(u32)),
1188763f356cSTakashi Iwai 		    (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF));
1189763f356cSTakashi Iwai 	return 0;
1190763f356cSTakashi Iwai }
1191763f356cSTakashi Iwai 
1192763f356cSTakashi Iwai 
1193763f356cSTakashi Iwai /* enable DMA for specific channels, now available for DSP-MADI */
snd_hdspm_enable_in(struct hdspm * hdspm,int i,int v)119498274f07STakashi Iwai static inline void snd_hdspm_enable_in(struct hdspm * hdspm, int i, int v)
1195763f356cSTakashi Iwai {
1196763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_inputEnableBase + (4 * i), v);
1197763f356cSTakashi Iwai }
1198763f356cSTakashi Iwai 
snd_hdspm_enable_out(struct hdspm * hdspm,int i,int v)119998274f07STakashi Iwai static inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v)
1200763f356cSTakashi Iwai {
1201763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_outputEnableBase + (4 * i), v);
1202763f356cSTakashi Iwai }
1203763f356cSTakashi Iwai 
1204763f356cSTakashi Iwai /* check if same process is writing and reading */
snd_hdspm_use_is_exclusive(struct hdspm * hdspm)120562cef821SDenys Vlasenko static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
1206763f356cSTakashi Iwai {
1207763f356cSTakashi Iwai 	unsigned long flags;
1208763f356cSTakashi Iwai 	int ret = 1;
1209763f356cSTakashi Iwai 
1210763f356cSTakashi Iwai 	spin_lock_irqsave(&hdspm->lock, flags);
1211763f356cSTakashi Iwai 	if ((hdspm->playback_pid != hdspm->capture_pid) &&
1212763f356cSTakashi Iwai 	    (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) {
1213763f356cSTakashi Iwai 		ret = 0;
1214763f356cSTakashi Iwai 	}
1215763f356cSTakashi Iwai 	spin_unlock_irqrestore(&hdspm->lock, flags);
1216763f356cSTakashi Iwai 	return ret;
1217763f356cSTakashi Iwai }
1218763f356cSTakashi Iwai 
12197dcd5612SNaoki Hayama /* round arbitrary sample rates to commonly known rates */
hdspm_round_frequency(int rate)1220fcdc4ba1SAdrian Knoth static int hdspm_round_frequency(int rate)
1221fcdc4ba1SAdrian Knoth {
1222fcdc4ba1SAdrian Knoth 	if (rate < 38050)
1223fcdc4ba1SAdrian Knoth 		return 32000;
1224fcdc4ba1SAdrian Knoth 	if (rate < 46008)
1225fcdc4ba1SAdrian Knoth 		return 44100;
1226fcdc4ba1SAdrian Knoth 	else
1227fcdc4ba1SAdrian Knoth 		return 48000;
1228fcdc4ba1SAdrian Knoth }
1229fcdc4ba1SAdrian Knoth 
1230a8a729faSAdrian Knoth /* QS and DS rates normally can not be detected
1231a8a729faSAdrian Knoth  * automatically by the card. Only exception is MADI
1232a8a729faSAdrian Knoth  * in 96k frame mode.
1233a8a729faSAdrian Knoth  *
1234a8a729faSAdrian Knoth  * So if we read SS values (32 .. 48k), check for
1235a8a729faSAdrian Knoth  * user-provided DS/QS bits in the control register
1236a8a729faSAdrian Knoth  * and multiply the base frequency accordingly.
1237a8a729faSAdrian Knoth  */
hdspm_rate_multiplier(struct hdspm * hdspm,int rate)1238a8a729faSAdrian Knoth static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate)
1239a8a729faSAdrian Knoth {
1240a8a729faSAdrian Knoth 	if (rate <= 48000) {
1241a8a729faSAdrian Knoth 		if (hdspm->control_register & HDSPM_QuadSpeed)
1242a8a729faSAdrian Knoth 			return rate * 4;
1243a8a729faSAdrian Knoth 		else if (hdspm->control_register &
1244a8a729faSAdrian Knoth 				HDSPM_DoubleSpeed)
1245a8a729faSAdrian Knoth 			return rate * 2;
124668593c93SFengguang Wu 	}
1247a8a729faSAdrian Knoth 	return rate;
1248a8a729faSAdrian Knoth }
1249a8a729faSAdrian Knoth 
12505b266354SAdrian Knoth /* check for external sample rate, returns the sample rate in Hz*/
hdspm_external_sample_rate(struct hdspm * hdspm)125162cef821SDenys Vlasenko static int hdspm_external_sample_rate(struct hdspm *hdspm)
1252763f356cSTakashi Iwai {
1253df57de17SSudip Mukherjee 	unsigned int status, status2;
12540dca1793SAdrian Knoth 	int syncref, rate = 0, rate_bits;
12553cee5a60SRemy Bruno 
12560dca1793SAdrian Knoth 	switch (hdspm->io_type) {
12570dca1793SAdrian Knoth 	case AES32:
12580dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
12590dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
12600dca1793SAdrian Knoth 
12610dca1793SAdrian Knoth 		syncref = hdspm_autosync_ref(hdspm);
1262dbae4a0cSAdrian Knoth 		switch (syncref) {
1263dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_WORD:
1264dbae4a0cSAdrian Knoth 		/* Check WC sync and get sample rate */
1265dbae4a0cSAdrian Knoth 			if (hdspm_wc_sync_check(hdspm))
1266dbae4a0cSAdrian Knoth 				return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm));
1267dbae4a0cSAdrian Knoth 			break;
12683cee5a60SRemy Bruno 
1269dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES1:
1270dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES2:
1271dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES3:
1272dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES4:
1273dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES5:
1274dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES6:
1275dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES7:
1276dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_AES8:
1277dbae4a0cSAdrian Knoth 		/* Check AES sync and get sample rate */
1278dbae4a0cSAdrian Knoth 			if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))
1279dbae4a0cSAdrian Knoth 				return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm,
1280dbae4a0cSAdrian Knoth 							syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1));
1281dbae4a0cSAdrian Knoth 			break;
12820dca1793SAdrian Knoth 
1283dbae4a0cSAdrian Knoth 
1284dbae4a0cSAdrian Knoth 		case HDSPM_AES32_AUTOSYNC_FROM_TCO:
1285dbae4a0cSAdrian Knoth 		/* Check TCO sync and get sample rate */
1286dbae4a0cSAdrian Knoth 			if (hdspm_tco_sync_check(hdspm))
1287dbae4a0cSAdrian Knoth 				return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm));
1288dbae4a0cSAdrian Knoth 			break;
1289dbae4a0cSAdrian Knoth 		default:
12903cee5a60SRemy Bruno 			return 0;
1291dbae4a0cSAdrian Knoth 		} /* end switch(syncref) */
12920dca1793SAdrian Knoth 		break;
12930dca1793SAdrian Knoth 
12940dca1793SAdrian Knoth 	case MADIface:
12950dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
12960dca1793SAdrian Knoth 
12970dca1793SAdrian Knoth 		if (!(status & HDSPM_madiLock)) {
12980dca1793SAdrian Knoth 			rate = 0;  /* no lock */
12993cee5a60SRemy Bruno 		} else {
13000dca1793SAdrian Knoth 			switch (status & (HDSPM_status1_freqMask)) {
13010dca1793SAdrian Knoth 			case HDSPM_status1_F_0*1:
13020dca1793SAdrian Knoth 				rate = 32000; break;
13030dca1793SAdrian Knoth 			case HDSPM_status1_F_0*2:
13040dca1793SAdrian Knoth 				rate = 44100; break;
13050dca1793SAdrian Knoth 			case HDSPM_status1_F_0*3:
13060dca1793SAdrian Knoth 				rate = 48000; break;
13070dca1793SAdrian Knoth 			case HDSPM_status1_F_0*4:
13080dca1793SAdrian Knoth 				rate = 64000; break;
13090dca1793SAdrian Knoth 			case HDSPM_status1_F_0*5:
13100dca1793SAdrian Knoth 				rate = 88200; break;
13110dca1793SAdrian Knoth 			case HDSPM_status1_F_0*6:
13120dca1793SAdrian Knoth 				rate = 96000; break;
13130dca1793SAdrian Knoth 			case HDSPM_status1_F_0*7:
13140dca1793SAdrian Knoth 				rate = 128000; break;
13150dca1793SAdrian Knoth 			case HDSPM_status1_F_0*8:
13160dca1793SAdrian Knoth 				rate = 176400; break;
13170dca1793SAdrian Knoth 			case HDSPM_status1_F_0*9:
13180dca1793SAdrian Knoth 				rate = 192000; break;
13190dca1793SAdrian Knoth 			default:
13200dca1793SAdrian Knoth 				rate = 0; break;
13210dca1793SAdrian Knoth 			}
13220dca1793SAdrian Knoth 		}
13230dca1793SAdrian Knoth 
13240dca1793SAdrian Knoth 		break;
13250dca1793SAdrian Knoth 
13260dca1793SAdrian Knoth 	case MADI:
13270dca1793SAdrian Knoth 	case AIO:
13280dca1793SAdrian Knoth 	case RayDAT:
13290dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
13300dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
13310dca1793SAdrian Knoth 		rate = 0;
1332763f356cSTakashi Iwai 
1333763f356cSTakashi Iwai 		/* if wordclock has synced freq and wordclock is valid */
1334763f356cSTakashi Iwai 		if ((status2 & HDSPM_wcLock) != 0 &&
1335fedf1535SAdrian Knoth 				(status2 & HDSPM_SelSyncRef0) == 0) {
1336763f356cSTakashi Iwai 
1337763f356cSTakashi Iwai 			rate_bits = status2 & HDSPM_wcFreqMask;
1338763f356cSTakashi Iwai 
13390dca1793SAdrian Knoth 
1340763f356cSTakashi Iwai 			switch (rate_bits) {
1341763f356cSTakashi Iwai 			case HDSPM_wcFreq32:
1342763f356cSTakashi Iwai 				rate = 32000;
1343763f356cSTakashi Iwai 				break;
1344763f356cSTakashi Iwai 			case HDSPM_wcFreq44_1:
1345763f356cSTakashi Iwai 				rate = 44100;
1346763f356cSTakashi Iwai 				break;
1347763f356cSTakashi Iwai 			case HDSPM_wcFreq48:
1348763f356cSTakashi Iwai 				rate = 48000;
1349763f356cSTakashi Iwai 				break;
1350763f356cSTakashi Iwai 			case HDSPM_wcFreq64:
1351763f356cSTakashi Iwai 				rate = 64000;
1352763f356cSTakashi Iwai 				break;
1353763f356cSTakashi Iwai 			case HDSPM_wcFreq88_2:
1354763f356cSTakashi Iwai 				rate = 88200;
1355763f356cSTakashi Iwai 				break;
1356763f356cSTakashi Iwai 			case HDSPM_wcFreq96:
1357763f356cSTakashi Iwai 				rate = 96000;
1358763f356cSTakashi Iwai 				break;
1359a8cd7148SAdrian Knoth 			case HDSPM_wcFreq128:
1360a8cd7148SAdrian Knoth 				rate = 128000;
1361a8cd7148SAdrian Knoth 				break;
1362a8cd7148SAdrian Knoth 			case HDSPM_wcFreq176_4:
1363a8cd7148SAdrian Knoth 				rate = 176400;
1364a8cd7148SAdrian Knoth 				break;
1365a8cd7148SAdrian Knoth 			case HDSPM_wcFreq192:
1366a8cd7148SAdrian Knoth 				rate = 192000;
1367a8cd7148SAdrian Knoth 				break;
1368763f356cSTakashi Iwai 			default:
1369763f356cSTakashi Iwai 				rate = 0;
1370763f356cSTakashi Iwai 				break;
1371763f356cSTakashi Iwai 			}
1372763f356cSTakashi Iwai 		}
1373763f356cSTakashi Iwai 
1374ef5fa1a4STakashi Iwai 		/* if rate detected and Syncref is Word than have it,
1375ef5fa1a4STakashi Iwai 		 * word has priority to MADI
1376ef5fa1a4STakashi Iwai 		 */
13773cee5a60SRemy Bruno 		if (rate != 0 &&
13783cee5a60SRemy Bruno 		(status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
13797b559397SAdrian Knoth 			return hdspm_rate_multiplier(hdspm, rate);
1380763f356cSTakashi Iwai 
13810dca1793SAdrian Knoth 		/* maybe a madi input (which is taken if sel sync is madi) */
1382763f356cSTakashi Iwai 		if (status & HDSPM_madiLock) {
1383763f356cSTakashi Iwai 			rate_bits = status & HDSPM_madiFreqMask;
1384763f356cSTakashi Iwai 
1385763f356cSTakashi Iwai 			switch (rate_bits) {
1386763f356cSTakashi Iwai 			case HDSPM_madiFreq32:
1387763f356cSTakashi Iwai 				rate = 32000;
1388763f356cSTakashi Iwai 				break;
1389763f356cSTakashi Iwai 			case HDSPM_madiFreq44_1:
1390763f356cSTakashi Iwai 				rate = 44100;
1391763f356cSTakashi Iwai 				break;
1392763f356cSTakashi Iwai 			case HDSPM_madiFreq48:
1393763f356cSTakashi Iwai 				rate = 48000;
1394763f356cSTakashi Iwai 				break;
1395763f356cSTakashi Iwai 			case HDSPM_madiFreq64:
1396763f356cSTakashi Iwai 				rate = 64000;
1397763f356cSTakashi Iwai 				break;
1398763f356cSTakashi Iwai 			case HDSPM_madiFreq88_2:
1399763f356cSTakashi Iwai 				rate = 88200;
1400763f356cSTakashi Iwai 				break;
1401763f356cSTakashi Iwai 			case HDSPM_madiFreq96:
1402763f356cSTakashi Iwai 				rate = 96000;
1403763f356cSTakashi Iwai 				break;
1404763f356cSTakashi Iwai 			case HDSPM_madiFreq128:
1405763f356cSTakashi Iwai 				rate = 128000;
1406763f356cSTakashi Iwai 				break;
1407763f356cSTakashi Iwai 			case HDSPM_madiFreq176_4:
1408763f356cSTakashi Iwai 				rate = 176400;
1409763f356cSTakashi Iwai 				break;
1410763f356cSTakashi Iwai 			case HDSPM_madiFreq192:
1411763f356cSTakashi Iwai 				rate = 192000;
1412763f356cSTakashi Iwai 				break;
1413763f356cSTakashi Iwai 			default:
1414763f356cSTakashi Iwai 				rate = 0;
1415763f356cSTakashi Iwai 				break;
1416763f356cSTakashi Iwai 			}
1417d12c51d8SAdrian Knoth 
1418fcdc4ba1SAdrian Knoth 		} /* endif HDSPM_madiLock */
1419fcdc4ba1SAdrian Knoth 
1420fcdc4ba1SAdrian Knoth 		/* check sample rate from TCO or SYNC_IN */
1421fcdc4ba1SAdrian Knoth 		{
1422fcdc4ba1SAdrian Knoth 			bool is_valid_input = 0;
1423fcdc4ba1SAdrian Knoth 			bool has_sync = 0;
1424fcdc4ba1SAdrian Knoth 
1425fcdc4ba1SAdrian Knoth 			syncref = hdspm_autosync_ref(hdspm);
1426fcdc4ba1SAdrian Knoth 			if (HDSPM_AUTOSYNC_FROM_TCO == syncref) {
1427fcdc4ba1SAdrian Knoth 				is_valid_input = 1;
1428fcdc4ba1SAdrian Knoth 				has_sync = (HDSPM_SYNC_CHECK_SYNC ==
1429fcdc4ba1SAdrian Knoth 					hdspm_tco_sync_check(hdspm));
1430fcdc4ba1SAdrian Knoth 			} else if (HDSPM_AUTOSYNC_FROM_SYNC_IN == syncref) {
1431fcdc4ba1SAdrian Knoth 				is_valid_input = 1;
1432fcdc4ba1SAdrian Knoth 				has_sync = (HDSPM_SYNC_CHECK_SYNC ==
1433fcdc4ba1SAdrian Knoth 					hdspm_sync_in_sync_check(hdspm));
1434fcdc4ba1SAdrian Knoth 			}
1435fcdc4ba1SAdrian Knoth 
1436fcdc4ba1SAdrian Knoth 			if (is_valid_input && has_sync) {
1437fcdc4ba1SAdrian Knoth 				rate = hdspm_round_frequency(
1438fcdc4ba1SAdrian Knoth 					hdspm_get_pll_freq(hdspm));
1439fcdc4ba1SAdrian Knoth 			}
1440fcdc4ba1SAdrian Knoth 		}
1441fcdc4ba1SAdrian Knoth 
1442a8a729faSAdrian Knoth 		rate = hdspm_rate_multiplier(hdspm, rate);
1443a8a729faSAdrian Knoth 
14440dca1793SAdrian Knoth 		break;
1445763f356cSTakashi Iwai 	}
14460dca1793SAdrian Knoth 
14470dca1793SAdrian Knoth 	return rate;
14483cee5a60SRemy Bruno }
1449763f356cSTakashi Iwai 
14507cb155ffSAdrian Knoth /* return latency in samples per period */
hdspm_get_latency(struct hdspm * hdspm)14517cb155ffSAdrian Knoth static int hdspm_get_latency(struct hdspm *hdspm)
14527cb155ffSAdrian Knoth {
14537cb155ffSAdrian Knoth 	int n;
14547cb155ffSAdrian Knoth 
14557cb155ffSAdrian Knoth 	n = hdspm_decode_latency(hdspm->control_register);
14567cb155ffSAdrian Knoth 
14577cb155ffSAdrian Knoth 	/* Special case for new RME cards with 32 samples period size.
14587cb155ffSAdrian Knoth 	 * The three latency bits in the control register
14597cb155ffSAdrian Knoth 	 * (HDSP_LatencyMask) encode latency values of 64 samples as
14607cb155ffSAdrian Knoth 	 * 0, 128 samples as 1 ... 4096 samples as 6. For old cards, 7
14617cb155ffSAdrian Knoth 	 * denotes 8192 samples, but on new cards like RayDAT or AIO,
14627cb155ffSAdrian Knoth 	 * it corresponds to 32 samples.
14637cb155ffSAdrian Knoth 	 */
14647cb155ffSAdrian Knoth 	if ((7 == n) && (RayDAT == hdspm->io_type || AIO == hdspm->io_type))
14657cb155ffSAdrian Knoth 		n = -1;
14667cb155ffSAdrian Knoth 
14677cb155ffSAdrian Knoth 	return 1 << (n + 6);
14687cb155ffSAdrian Knoth }
14697cb155ffSAdrian Knoth 
1470763f356cSTakashi Iwai /* Latency function */
hdspm_compute_period_size(struct hdspm * hdspm)147198274f07STakashi Iwai static inline void hdspm_compute_period_size(struct hdspm *hdspm)
1472763f356cSTakashi Iwai {
14737cb155ffSAdrian Knoth 	hdspm->period_bytes = 4 * hdspm_get_latency(hdspm);
1474763f356cSTakashi Iwai }
1475763f356cSTakashi Iwai 
14760dca1793SAdrian Knoth 
hdspm_hw_pointer(struct hdspm * hdspm)147798274f07STakashi Iwai static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm)
1478763f356cSTakashi Iwai {
1479763f356cSTakashi Iwai 	int position;
1480763f356cSTakashi Iwai 
1481763f356cSTakashi Iwai 	position = hdspm_read(hdspm, HDSPM_statusRegister);
1482483cee77SAdrian Knoth 
1483483cee77SAdrian Knoth 	switch (hdspm->io_type) {
1484483cee77SAdrian Knoth 	case RayDAT:
1485483cee77SAdrian Knoth 	case AIO:
1486763f356cSTakashi Iwai 		position &= HDSPM_BufferPositionMask;
14870dca1793SAdrian Knoth 		position /= 4; /* Bytes per sample */
1488483cee77SAdrian Knoth 		break;
1489483cee77SAdrian Knoth 	default:
1490483cee77SAdrian Knoth 		position = (position & HDSPM_BufferID) ?
1491483cee77SAdrian Knoth 			(hdspm->period_bytes / 4) : 0;
1492483cee77SAdrian Knoth 	}
1493763f356cSTakashi Iwai 
1494763f356cSTakashi Iwai 	return position;
1495763f356cSTakashi Iwai }
1496763f356cSTakashi Iwai 
1497763f356cSTakashi Iwai 
hdspm_start_audio(struct hdspm * s)149898274f07STakashi Iwai static inline void hdspm_start_audio(struct hdspm * s)
1499763f356cSTakashi Iwai {
1500763f356cSTakashi Iwai 	s->control_register |= (HDSPM_AudioInterruptEnable | HDSPM_Start);
1501763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1502763f356cSTakashi Iwai }
1503763f356cSTakashi Iwai 
hdspm_stop_audio(struct hdspm * s)150498274f07STakashi Iwai static inline void hdspm_stop_audio(struct hdspm * s)
1505763f356cSTakashi Iwai {
1506763f356cSTakashi Iwai 	s->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable);
1507763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1508763f356cSTakashi Iwai }
1509763f356cSTakashi Iwai 
1510763f356cSTakashi Iwai /* should I silence all or only opened ones ? doit all for first even is 4MB*/
hdspm_silence_playback(struct hdspm * hdspm)151162cef821SDenys Vlasenko static void hdspm_silence_playback(struct hdspm *hdspm)
1512763f356cSTakashi Iwai {
1513763f356cSTakashi Iwai 	int i;
1514763f356cSTakashi Iwai 	int n = hdspm->period_bytes;
1515763f356cSTakashi Iwai 	void *buf = hdspm->playback_buffer;
1516763f356cSTakashi Iwai 
1517da2ea374SMarkus Elfring 	if (!buf)
15183cee5a60SRemy Bruno 		return;
1519763f356cSTakashi Iwai 
1520763f356cSTakashi Iwai 	for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
1521763f356cSTakashi Iwai 		memset(buf, 0, n);
1522763f356cSTakashi Iwai 		buf += HDSPM_CHANNEL_BUFFER_BYTES;
1523763f356cSTakashi Iwai 	}
1524763f356cSTakashi Iwai }
1525763f356cSTakashi Iwai 
hdspm_set_interrupt_interval(struct hdspm * s,unsigned int frames)152698274f07STakashi Iwai static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
1527763f356cSTakashi Iwai {
1528763f356cSTakashi Iwai 	int n;
1529763f356cSTakashi Iwai 
1530763f356cSTakashi Iwai 	spin_lock_irq(&s->lock);
1531763f356cSTakashi Iwai 
15322e610270SAdrian Knoth 	if (32 == frames) {
15332e610270SAdrian Knoth 		/* Special case for new RME cards like RayDAT/AIO which
15342e610270SAdrian Knoth 		 * support period sizes of 32 samples. Since latency is
15352e610270SAdrian Knoth 		 * encoded in the three bits of HDSP_LatencyMask, we can only
15362e610270SAdrian Knoth 		 * have values from 0 .. 7. While 0 still means 64 samples and
15372e610270SAdrian Knoth 		 * 6 represents 4096 samples on all cards, 7 represents 8192
15382e610270SAdrian Knoth 		 * on older cards and 32 samples on new cards.
15392e610270SAdrian Knoth 		 *
15402e610270SAdrian Knoth 		 * In other words, period size in samples is calculated by
15412e610270SAdrian Knoth 		 * 2^(n+6) with n ranging from 0 .. 7.
15422e610270SAdrian Knoth 		 */
15432e610270SAdrian Knoth 		n = 7;
15442e610270SAdrian Knoth 	} else {
1545763f356cSTakashi Iwai 		frames >>= 7;
1546763f356cSTakashi Iwai 		n = 0;
1547763f356cSTakashi Iwai 		while (frames) {
1548763f356cSTakashi Iwai 			n++;
1549763f356cSTakashi Iwai 			frames >>= 1;
1550763f356cSTakashi Iwai 		}
15512e610270SAdrian Knoth 	}
15522e610270SAdrian Knoth 
1553763f356cSTakashi Iwai 	s->control_register &= ~HDSPM_LatencyMask;
1554763f356cSTakashi Iwai 	s->control_register |= hdspm_encode_latency(n);
1555763f356cSTakashi Iwai 
1556763f356cSTakashi Iwai 	hdspm_write(s, HDSPM_controlRegister, s->control_register);
1557763f356cSTakashi Iwai 
1558763f356cSTakashi Iwai 	hdspm_compute_period_size(s);
1559763f356cSTakashi Iwai 
1560763f356cSTakashi Iwai 	spin_unlock_irq(&s->lock);
1561763f356cSTakashi Iwai 
1562763f356cSTakashi Iwai 	return 0;
1563763f356cSTakashi Iwai }
1564763f356cSTakashi Iwai 
hdspm_calc_dds_value(struct hdspm * hdspm,u64 period)15650dca1793SAdrian Knoth static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period)
15660dca1793SAdrian Knoth {
15670dca1793SAdrian Knoth 	u64 freq_const;
15680dca1793SAdrian Knoth 
15690dca1793SAdrian Knoth 	if (period == 0)
15700dca1793SAdrian Knoth 		return 0;
15710dca1793SAdrian Knoth 
15720dca1793SAdrian Knoth 	switch (hdspm->io_type) {
15730dca1793SAdrian Knoth 	case MADI:
15740dca1793SAdrian Knoth 	case AES32:
15750dca1793SAdrian Knoth 		freq_const = 110069313433624ULL;
15760dca1793SAdrian Knoth 		break;
15770dca1793SAdrian Knoth 	case RayDAT:
15780dca1793SAdrian Knoth 	case AIO:
15790dca1793SAdrian Knoth 		freq_const = 104857600000000ULL;
15800dca1793SAdrian Knoth 		break;
15810dca1793SAdrian Knoth 	case MADIface:
15820dca1793SAdrian Knoth 		freq_const = 131072000000000ULL;
15833d56c8e6STakashi Iwai 		break;
15843d56c8e6STakashi Iwai 	default:
15853d56c8e6STakashi Iwai 		snd_BUG();
15863d56c8e6STakashi Iwai 		return 0;
15870dca1793SAdrian Knoth 	}
15880dca1793SAdrian Knoth 
15890dca1793SAdrian Knoth 	return div_u64(freq_const, period);
15900dca1793SAdrian Knoth }
15910dca1793SAdrian Knoth 
15920dca1793SAdrian Knoth 
hdspm_set_dds_value(struct hdspm * hdspm,int rate)1593ffb2c3c0SRemy Bruno static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
1594ffb2c3c0SRemy Bruno {
1595ffb2c3c0SRemy Bruno 	u64 n;
1596ffb2c3c0SRemy Bruno 
1597c1099c32STakashi Iwai 	if (snd_BUG_ON(rate <= 0))
1598c1099c32STakashi Iwai 		return;
1599c1099c32STakashi Iwai 
1600ffb2c3c0SRemy Bruno 	if (rate >= 112000)
1601ffb2c3c0SRemy Bruno 		rate /= 4;
1602ffb2c3c0SRemy Bruno 	else if (rate >= 56000)
1603ffb2c3c0SRemy Bruno 		rate /= 2;
1604ffb2c3c0SRemy Bruno 
16050dca1793SAdrian Knoth 	switch (hdspm->io_type) {
16060dca1793SAdrian Knoth 	case MADIface:
16070dca1793SAdrian Knoth 		n = 131072000000000ULL;  /* 125 MHz */
16080dca1793SAdrian Knoth 		break;
16090dca1793SAdrian Knoth 	case MADI:
16100dca1793SAdrian Knoth 	case AES32:
16110dca1793SAdrian Knoth 		n = 110069313433624ULL;  /* 105 MHz */
16120dca1793SAdrian Knoth 		break;
16130dca1793SAdrian Knoth 	case RayDAT:
16140dca1793SAdrian Knoth 	case AIO:
16150dca1793SAdrian Knoth 		n = 104857600000000ULL;  /* 100 MHz */
16160dca1793SAdrian Knoth 		break;
16173d56c8e6STakashi Iwai 	default:
16183d56c8e6STakashi Iwai 		snd_BUG();
16193d56c8e6STakashi Iwai 		return;
16200dca1793SAdrian Knoth 	}
16210dca1793SAdrian Knoth 
16223f7440a6STakashi Iwai 	n = div_u64(n, rate);
1623ffb2c3c0SRemy Bruno 	/* n should be less than 2^32 for being written to FREQ register */
1624da3cec35STakashi Iwai 	snd_BUG_ON(n >> 32);
1625ffb2c3c0SRemy Bruno 	hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
1626ffb2c3c0SRemy Bruno }
1627763f356cSTakashi Iwai 
1628763f356cSTakashi Iwai /* dummy set rate lets see what happens */
hdspm_set_rate(struct hdspm * hdspm,int rate,int called_internally)162998274f07STakashi Iwai static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
1630763f356cSTakashi Iwai {
1631763f356cSTakashi Iwai 	int current_rate;
1632763f356cSTakashi Iwai 	int rate_bits;
1633763f356cSTakashi Iwai 	int not_set = 0;
16346534599dSRemy Bruno 	int current_speed, target_speed;
1635763f356cSTakashi Iwai 
1636763f356cSTakashi Iwai 	/* ASSUMPTION: hdspm->lock is either set, or there is no need for
1637763f356cSTakashi Iwai 	   it (e.g. during module initialization).
1638763f356cSTakashi Iwai 	 */
1639763f356cSTakashi Iwai 
1640763f356cSTakashi Iwai 	if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
1641763f356cSTakashi Iwai 
1642763f356cSTakashi Iwai 		/* SLAVE --- */
1643763f356cSTakashi Iwai 		if (called_internally) {
1644763f356cSTakashi Iwai 
1645763f356cSTakashi Iwai 			/* request from ctl or card initialization
1646763f356cSTakashi Iwai 			   just make a warning an remember setting
1647763f356cSTakashi Iwai 			   for future master mode switching */
1648763f356cSTakashi Iwai 
1649e3a471d6STakashi Iwai 			dev_warn(hdspm->card->dev,
1650e3a471d6STakashi Iwai 				 "Warning: device is not running as a clock master.\n");
1651763f356cSTakashi Iwai 			not_set = 1;
1652763f356cSTakashi Iwai 		} else {
1653763f356cSTakashi Iwai 
1654763f356cSTakashi Iwai 			/* hw_param request while in AutoSync mode */
1655763f356cSTakashi Iwai 			int external_freq =
1656763f356cSTakashi Iwai 			    hdspm_external_sample_rate(hdspm);
1657763f356cSTakashi Iwai 
1658ef5fa1a4STakashi Iwai 			if (hdspm_autosync_ref(hdspm) ==
1659ef5fa1a4STakashi Iwai 			    HDSPM_AUTOSYNC_FROM_NONE) {
1660763f356cSTakashi Iwai 
1661e3a471d6STakashi Iwai 				dev_warn(hdspm->card->dev,
166207cb3272SColin Ian King 					 "Detected no External Sync\n");
1663763f356cSTakashi Iwai 				not_set = 1;
1664763f356cSTakashi Iwai 
1665763f356cSTakashi Iwai 			} else if (rate != external_freq) {
1666763f356cSTakashi Iwai 
1667e3a471d6STakashi Iwai 				dev_warn(hdspm->card->dev,
1668e3a471d6STakashi Iwai 					 "Warning: No AutoSync source for requested rate\n");
1669763f356cSTakashi Iwai 				not_set = 1;
1670763f356cSTakashi Iwai 			}
1671763f356cSTakashi Iwai 		}
1672763f356cSTakashi Iwai 	}
1673763f356cSTakashi Iwai 
1674763f356cSTakashi Iwai 	current_rate = hdspm->system_sample_rate;
1675763f356cSTakashi Iwai 
1676763f356cSTakashi Iwai 	/* Changing between Singe, Double and Quad speed is not
1677763f356cSTakashi Iwai 	   allowed if any substreams are open. This is because such a change
1678763f356cSTakashi Iwai 	   causes a shift in the location of the DMA buffers and a reduction
1679763f356cSTakashi Iwai 	   in the number of available buffers.
1680763f356cSTakashi Iwai 
1681763f356cSTakashi Iwai 	   Note that a similar but essentially insoluble problem exists for
1682763f356cSTakashi Iwai 	   externally-driven rate changes. All we can do is to flag rate
1683763f356cSTakashi Iwai 	   changes in the read/write routines.
1684763f356cSTakashi Iwai 	 */
1685763f356cSTakashi Iwai 
16866534599dSRemy Bruno 	if (current_rate <= 48000)
16876534599dSRemy Bruno 		current_speed = HDSPM_SPEED_SINGLE;
16886534599dSRemy Bruno 	else if (current_rate <= 96000)
16896534599dSRemy Bruno 		current_speed = HDSPM_SPEED_DOUBLE;
16906534599dSRemy Bruno 	else
16916534599dSRemy Bruno 		current_speed = HDSPM_SPEED_QUAD;
16926534599dSRemy Bruno 
16936534599dSRemy Bruno 	if (rate <= 48000)
16946534599dSRemy Bruno 		target_speed = HDSPM_SPEED_SINGLE;
16956534599dSRemy Bruno 	else if (rate <= 96000)
16966534599dSRemy Bruno 		target_speed = HDSPM_SPEED_DOUBLE;
16976534599dSRemy Bruno 	else
16986534599dSRemy Bruno 		target_speed = HDSPM_SPEED_QUAD;
16993cee5a60SRemy Bruno 
1700763f356cSTakashi Iwai 	switch (rate) {
1701763f356cSTakashi Iwai 	case 32000:
1702763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency32KHz;
1703763f356cSTakashi Iwai 		break;
1704763f356cSTakashi Iwai 	case 44100:
1705763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency44_1KHz;
1706763f356cSTakashi Iwai 		break;
1707763f356cSTakashi Iwai 	case 48000:
1708763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency48KHz;
1709763f356cSTakashi Iwai 		break;
1710763f356cSTakashi Iwai 	case 64000:
1711763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency64KHz;
1712763f356cSTakashi Iwai 		break;
1713763f356cSTakashi Iwai 	case 88200:
1714763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency88_2KHz;
1715763f356cSTakashi Iwai 		break;
1716763f356cSTakashi Iwai 	case 96000:
1717763f356cSTakashi Iwai 		rate_bits = HDSPM_Frequency96KHz;
1718763f356cSTakashi Iwai 		break;
17193cee5a60SRemy Bruno 	case 128000:
17203cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency128KHz;
17213cee5a60SRemy Bruno 		break;
17223cee5a60SRemy Bruno 	case 176400:
17233cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency176_4KHz;
17243cee5a60SRemy Bruno 		break;
17253cee5a60SRemy Bruno 	case 192000:
17263cee5a60SRemy Bruno 		rate_bits = HDSPM_Frequency192KHz;
17273cee5a60SRemy Bruno 		break;
1728763f356cSTakashi Iwai 	default:
1729763f356cSTakashi Iwai 		return -EINVAL;
1730763f356cSTakashi Iwai 	}
1731763f356cSTakashi Iwai 
17326534599dSRemy Bruno 	if (current_speed != target_speed
1733763f356cSTakashi Iwai 	    && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
1734e3a471d6STakashi Iwai 		dev_err(hdspm->card->dev,
1735e3a471d6STakashi Iwai 			"cannot change from %s speed to %s speed mode (capture PID = %d, playback PID = %d)\n",
17366534599dSRemy Bruno 			hdspm_speed_names[current_speed],
17376534599dSRemy Bruno 			hdspm_speed_names[target_speed],
1738763f356cSTakashi Iwai 			hdspm->capture_pid, hdspm->playback_pid);
1739763f356cSTakashi Iwai 		return -EBUSY;
1740763f356cSTakashi Iwai 	}
1741763f356cSTakashi Iwai 
1742763f356cSTakashi Iwai 	hdspm->control_register &= ~HDSPM_FrequencyMask;
1743763f356cSTakashi Iwai 	hdspm->control_register |= rate_bits;
1744763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
1745763f356cSTakashi Iwai 
1746ffb2c3c0SRemy Bruno 	/* For AES32, need to set DDS value in FREQ register
1747ffb2c3c0SRemy Bruno 	   For MADI, also apparently */
1748ffb2c3c0SRemy Bruno 	hdspm_set_dds_value(hdspm, rate);
1749ffb2c3c0SRemy Bruno 
17500dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type && rate != current_rate)
1751ffb2c3c0SRemy Bruno 		hdspm_write(hdspm, HDSPM_eeprom_wr, 0);
1752ffb2c3c0SRemy Bruno 
1753763f356cSTakashi Iwai 	hdspm->system_sample_rate = rate;
1754763f356cSTakashi Iwai 
17550dca1793SAdrian Knoth 	if (rate <= 48000) {
17560dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_ss;
17570dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_ss;
17580dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->ss_in_channels;
17590dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->ss_out_channels;
17600dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_ss;
17610dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_ss;
17620dca1793SAdrian Knoth 	} else if (rate <= 96000) {
17630dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_ds;
17640dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_ds;
17650dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->ds_in_channels;
17660dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->ds_out_channels;
17670dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_ds;
17680dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_ds;
17690dca1793SAdrian Knoth 	} else {
17700dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_in_qs;
17710dca1793SAdrian Knoth 		hdspm->channel_map_out = hdspm->channel_map_out_qs;
17720dca1793SAdrian Knoth 		hdspm->max_channels_in = hdspm->qs_in_channels;
17730dca1793SAdrian Knoth 		hdspm->max_channels_out = hdspm->qs_out_channels;
17740dca1793SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_in_qs;
17750dca1793SAdrian Knoth 		hdspm->port_names_out = hdspm->port_names_out_qs;
17760dca1793SAdrian Knoth 	}
17770dca1793SAdrian Knoth 
1778763f356cSTakashi Iwai 	if (not_set != 0)
1779763f356cSTakashi Iwai 		return -1;
1780763f356cSTakashi Iwai 
1781763f356cSTakashi Iwai 	return 0;
1782763f356cSTakashi Iwai }
1783763f356cSTakashi Iwai 
1784763f356cSTakashi Iwai /* mainly for init to 0 on load */
all_in_all_mixer(struct hdspm * hdspm,int sgain)178598274f07STakashi Iwai static void all_in_all_mixer(struct hdspm * hdspm, int sgain)
1786763f356cSTakashi Iwai {
1787763f356cSTakashi Iwai 	int i, j;
1788ef5fa1a4STakashi Iwai 	unsigned int gain;
1789ef5fa1a4STakashi Iwai 
1790ef5fa1a4STakashi Iwai 	if (sgain > UNITY_GAIN)
1791ef5fa1a4STakashi Iwai 		gain = UNITY_GAIN;
1792ef5fa1a4STakashi Iwai 	else if (sgain < 0)
1793ef5fa1a4STakashi Iwai 		gain = 0;
1794ef5fa1a4STakashi Iwai 	else
1795ef5fa1a4STakashi Iwai 		gain = sgain;
1796763f356cSTakashi Iwai 
1797763f356cSTakashi Iwai 	for (i = 0; i < HDSPM_MIXER_CHANNELS; i++)
1798763f356cSTakashi Iwai 		for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) {
1799763f356cSTakashi Iwai 			hdspm_write_in_gain(hdspm, i, j, gain);
1800763f356cSTakashi Iwai 			hdspm_write_pb_gain(hdspm, i, j, gain);
1801763f356cSTakashi Iwai 		}
1802763f356cSTakashi Iwai }
1803763f356cSTakashi Iwai 
1804763f356cSTakashi Iwai /*----------------------------------------------------------------------------
1805763f356cSTakashi Iwai    MIDI
1806763f356cSTakashi Iwai   ----------------------------------------------------------------------------*/
1807763f356cSTakashi Iwai 
snd_hdspm_midi_read_byte(struct hdspm * hdspm,int id)1808ef5fa1a4STakashi Iwai static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm,
1809ef5fa1a4STakashi Iwai 						      int id)
1810763f356cSTakashi Iwai {
1811763f356cSTakashi Iwai 	/* the hardware already does the relevant bit-mask with 0xff */
18120dca1793SAdrian Knoth 	return hdspm_read(hdspm, hdspm->midi[id].dataIn);
1813763f356cSTakashi Iwai }
1814763f356cSTakashi Iwai 
snd_hdspm_midi_write_byte(struct hdspm * hdspm,int id,int val)1815ef5fa1a4STakashi Iwai static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id,
1816ef5fa1a4STakashi Iwai 					      int val)
1817763f356cSTakashi Iwai {
1818763f356cSTakashi Iwai 	/* the hardware already does the relevant bit-mask with 0xff */
18190dca1793SAdrian Knoth 	return hdspm_write(hdspm, hdspm->midi[id].dataOut, val);
1820763f356cSTakashi Iwai }
1821763f356cSTakashi Iwai 
snd_hdspm_midi_input_available(struct hdspm * hdspm,int id)182298274f07STakashi Iwai static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id)
1823763f356cSTakashi Iwai {
18240dca1793SAdrian Knoth 	return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF;
1825763f356cSTakashi Iwai }
1826763f356cSTakashi Iwai 
snd_hdspm_midi_output_possible(struct hdspm * hdspm,int id)182798274f07STakashi Iwai static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id)
1828763f356cSTakashi Iwai {
1829763f356cSTakashi Iwai 	int fifo_bytes_used;
1830763f356cSTakashi Iwai 
18310dca1793SAdrian Knoth 	fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF;
1832763f356cSTakashi Iwai 
1833763f356cSTakashi Iwai 	if (fifo_bytes_used < 128)
1834763f356cSTakashi Iwai 		return  128 - fifo_bytes_used;
1835763f356cSTakashi Iwai 	else
1836763f356cSTakashi Iwai 		return 0;
1837763f356cSTakashi Iwai }
1838763f356cSTakashi Iwai 
snd_hdspm_flush_midi_input(struct hdspm * hdspm,int id)183962cef821SDenys Vlasenko static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id)
1840763f356cSTakashi Iwai {
1841*6cc4e5eaSTakashi Iwai 	int count = 256;
1842*6cc4e5eaSTakashi Iwai 
1843*6cc4e5eaSTakashi Iwai 	while (snd_hdspm_midi_input_available(hdspm, id) && --count)
1844763f356cSTakashi Iwai 		snd_hdspm_midi_read_byte(hdspm, id);
1845763f356cSTakashi Iwai }
1846763f356cSTakashi Iwai 
snd_hdspm_midi_output_write(struct hdspm_midi * hmidi)184798274f07STakashi Iwai static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
1848763f356cSTakashi Iwai {
1849763f356cSTakashi Iwai 	unsigned long flags;
1850763f356cSTakashi Iwai 	int n_pending;
1851763f356cSTakashi Iwai 	int to_write;
1852763f356cSTakashi Iwai 	int i;
1853763f356cSTakashi Iwai 	unsigned char buf[128];
1854763f356cSTakashi Iwai 
1855763f356cSTakashi Iwai 	/* Output is not interrupt driven */
1856763f356cSTakashi Iwai 
1857763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1858ef5fa1a4STakashi Iwai 	if (hmidi->output &&
1859ef5fa1a4STakashi Iwai 	    !snd_rawmidi_transmit_empty (hmidi->output)) {
1860ef5fa1a4STakashi Iwai 		n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm,
1861ef5fa1a4STakashi Iwai 							    hmidi->id);
1862ef5fa1a4STakashi Iwai 		if (n_pending > 0) {
1863763f356cSTakashi Iwai 			if (n_pending > (int)sizeof (buf))
1864763f356cSTakashi Iwai 				n_pending = sizeof (buf);
1865763f356cSTakashi Iwai 
1866ef5fa1a4STakashi Iwai 			to_write = snd_rawmidi_transmit (hmidi->output, buf,
1867ef5fa1a4STakashi Iwai 							 n_pending);
1868ef5fa1a4STakashi Iwai 			if (to_write > 0) {
1869763f356cSTakashi Iwai 				for (i = 0; i < to_write; ++i)
1870ef5fa1a4STakashi Iwai 					snd_hdspm_midi_write_byte (hmidi->hdspm,
1871ef5fa1a4STakashi Iwai 								   hmidi->id,
1872ef5fa1a4STakashi Iwai 								   buf[i]);
1873763f356cSTakashi Iwai 			}
1874763f356cSTakashi Iwai 		}
1875763f356cSTakashi Iwai 	}
1876763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1877763f356cSTakashi Iwai 	return 0;
1878763f356cSTakashi Iwai }
1879763f356cSTakashi Iwai 
snd_hdspm_midi_input_read(struct hdspm_midi * hmidi)188098274f07STakashi Iwai static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi)
1881763f356cSTakashi Iwai {
1882ef5fa1a4STakashi Iwai 	unsigned char buf[128]; /* this buffer is designed to match the MIDI
1883ef5fa1a4STakashi Iwai 				 * input FIFO size
1884ef5fa1a4STakashi Iwai 				 */
1885763f356cSTakashi Iwai 	unsigned long flags;
1886763f356cSTakashi Iwai 	int n_pending;
1887763f356cSTakashi Iwai 	int i;
1888763f356cSTakashi Iwai 
1889763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1890ef5fa1a4STakashi Iwai 	n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id);
1891ef5fa1a4STakashi Iwai 	if (n_pending > 0) {
1892763f356cSTakashi Iwai 		if (hmidi->input) {
1893ef5fa1a4STakashi Iwai 			if (n_pending > (int)sizeof (buf))
1894763f356cSTakashi Iwai 				n_pending = sizeof (buf);
1895ef5fa1a4STakashi Iwai 			for (i = 0; i < n_pending; ++i)
1896ef5fa1a4STakashi Iwai 				buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm,
1897ef5fa1a4STakashi Iwai 								   hmidi->id);
1898ef5fa1a4STakashi Iwai 			if (n_pending)
1899ef5fa1a4STakashi Iwai 				snd_rawmidi_receive (hmidi->input, buf,
1900ef5fa1a4STakashi Iwai 						     n_pending);
1901763f356cSTakashi Iwai 		} else {
1902763f356cSTakashi Iwai 			/* flush the MIDI input FIFO */
1903ef5fa1a4STakashi Iwai 			while (n_pending--)
1904ef5fa1a4STakashi Iwai 				snd_hdspm_midi_read_byte (hmidi->hdspm,
1905ef5fa1a4STakashi Iwai 							  hmidi->id);
1906763f356cSTakashi Iwai 		}
1907763f356cSTakashi Iwai 	}
1908763f356cSTakashi Iwai 	hmidi->pending = 0;
1909c0da0014SAdrian Knoth 	spin_unlock_irqrestore(&hmidi->lock, flags);
19100dca1793SAdrian Knoth 
1911c0da0014SAdrian Knoth 	spin_lock_irqsave(&hmidi->hdspm->lock, flags);
19120dca1793SAdrian Knoth 	hmidi->hdspm->control_register |= hmidi->ie;
1913ef5fa1a4STakashi Iwai 	hdspm_write(hmidi->hdspm, HDSPM_controlRegister,
1914ef5fa1a4STakashi Iwai 		    hmidi->hdspm->control_register);
1915c0da0014SAdrian Knoth 	spin_unlock_irqrestore(&hmidi->hdspm->lock, flags);
19160dca1793SAdrian Knoth 
1917763f356cSTakashi Iwai 	return snd_hdspm_midi_output_write (hmidi);
1918763f356cSTakashi Iwai }
1919763f356cSTakashi Iwai 
1920ef5fa1a4STakashi Iwai static void
snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream * substream,int up)1921ef5fa1a4STakashi Iwai snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
1922763f356cSTakashi Iwai {
192398274f07STakashi Iwai 	struct hdspm *hdspm;
192498274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1925763f356cSTakashi Iwai 	unsigned long flags;
1926763f356cSTakashi Iwai 
1927ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1928763f356cSTakashi Iwai 	hdspm = hmidi->hdspm;
19290dca1793SAdrian Knoth 
1930763f356cSTakashi Iwai 	spin_lock_irqsave (&hdspm->lock, flags);
1931763f356cSTakashi Iwai 	if (up) {
19320dca1793SAdrian Knoth 		if (!(hdspm->control_register & hmidi->ie)) {
1933763f356cSTakashi Iwai 			snd_hdspm_flush_midi_input (hdspm, hmidi->id);
19340dca1793SAdrian Knoth 			hdspm->control_register |= hmidi->ie;
1935763f356cSTakashi Iwai 		}
1936763f356cSTakashi Iwai 	} else {
19370dca1793SAdrian Knoth 		hdspm->control_register &= ~hmidi->ie;
1938763f356cSTakashi Iwai 	}
1939763f356cSTakashi Iwai 
1940763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
1941763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hdspm->lock, flags);
1942763f356cSTakashi Iwai }
1943763f356cSTakashi Iwai 
snd_hdspm_midi_output_timer(struct timer_list * t)19447211ec63SKees Cook static void snd_hdspm_midi_output_timer(struct timer_list *t)
1945763f356cSTakashi Iwai {
19467211ec63SKees Cook 	struct hdspm_midi *hmidi = from_timer(hmidi, t, timer);
1947763f356cSTakashi Iwai 	unsigned long flags;
1948763f356cSTakashi Iwai 
1949763f356cSTakashi Iwai 	snd_hdspm_midi_output_write(hmidi);
1950763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1951763f356cSTakashi Iwai 
1952763f356cSTakashi Iwai 	/* this does not bump hmidi->istimer, because the
1953763f356cSTakashi Iwai 	   kernel automatically removed the timer when it
1954763f356cSTakashi Iwai 	   expired, and we are now adding it back, thus
1955763f356cSTakashi Iwai 	   leaving istimer wherever it was set before.
1956763f356cSTakashi Iwai 	*/
1957763f356cSTakashi Iwai 
195804018e13STakashi Iwai 	if (hmidi->istimer)
195904018e13STakashi Iwai 		mod_timer(&hmidi->timer, 1 + jiffies);
1960763f356cSTakashi Iwai 
1961763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1962763f356cSTakashi Iwai }
1963763f356cSTakashi Iwai 
1964ef5fa1a4STakashi Iwai static void
snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream * substream,int up)1965ef5fa1a4STakashi Iwai snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
1966763f356cSTakashi Iwai {
196798274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1968763f356cSTakashi Iwai 	unsigned long flags;
1969763f356cSTakashi Iwai 
1970ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1971763f356cSTakashi Iwai 	spin_lock_irqsave (&hmidi->lock, flags);
1972763f356cSTakashi Iwai 	if (up) {
1973763f356cSTakashi Iwai 		if (!hmidi->istimer) {
19747211ec63SKees Cook 			timer_setup(&hmidi->timer,
19757211ec63SKees Cook 				    snd_hdspm_midi_output_timer, 0);
197604018e13STakashi Iwai 			mod_timer(&hmidi->timer, 1 + jiffies);
1977763f356cSTakashi Iwai 			hmidi->istimer++;
1978763f356cSTakashi Iwai 		}
1979763f356cSTakashi Iwai 	} else {
1980ef5fa1a4STakashi Iwai 		if (hmidi->istimer && --hmidi->istimer <= 0)
1981763f356cSTakashi Iwai 			del_timer (&hmidi->timer);
1982763f356cSTakashi Iwai 	}
1983763f356cSTakashi Iwai 	spin_unlock_irqrestore (&hmidi->lock, flags);
1984763f356cSTakashi Iwai 	if (up)
1985763f356cSTakashi Iwai 		snd_hdspm_midi_output_write(hmidi);
1986763f356cSTakashi Iwai }
1987763f356cSTakashi Iwai 
snd_hdspm_midi_input_open(struct snd_rawmidi_substream * substream)198898274f07STakashi Iwai static int snd_hdspm_midi_input_open(struct snd_rawmidi_substream *substream)
1989763f356cSTakashi Iwai {
199098274f07STakashi Iwai 	struct hdspm_midi *hmidi;
1991763f356cSTakashi Iwai 
1992ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
1993763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
1994763f356cSTakashi Iwai 	snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id);
1995763f356cSTakashi Iwai 	hmidi->input = substream;
1996763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
1997763f356cSTakashi Iwai 
1998763f356cSTakashi Iwai 	return 0;
1999763f356cSTakashi Iwai }
2000763f356cSTakashi Iwai 
snd_hdspm_midi_output_open(struct snd_rawmidi_substream * substream)200198274f07STakashi Iwai static int snd_hdspm_midi_output_open(struct snd_rawmidi_substream *substream)
2002763f356cSTakashi Iwai {
200398274f07STakashi Iwai 	struct hdspm_midi *hmidi;
2004763f356cSTakashi Iwai 
2005ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
2006763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
2007763f356cSTakashi Iwai 	hmidi->output = substream;
2008763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
2009763f356cSTakashi Iwai 
2010763f356cSTakashi Iwai 	return 0;
2011763f356cSTakashi Iwai }
2012763f356cSTakashi Iwai 
snd_hdspm_midi_input_close(struct snd_rawmidi_substream * substream)201398274f07STakashi Iwai static int snd_hdspm_midi_input_close(struct snd_rawmidi_substream *substream)
2014763f356cSTakashi Iwai {
201598274f07STakashi Iwai 	struct hdspm_midi *hmidi;
2016763f356cSTakashi Iwai 
2017763f356cSTakashi Iwai 	snd_hdspm_midi_input_trigger (substream, 0);
2018763f356cSTakashi Iwai 
2019ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
2020763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
2021763f356cSTakashi Iwai 	hmidi->input = NULL;
2022763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
2023763f356cSTakashi Iwai 
2024763f356cSTakashi Iwai 	return 0;
2025763f356cSTakashi Iwai }
2026763f356cSTakashi Iwai 
snd_hdspm_midi_output_close(struct snd_rawmidi_substream * substream)202798274f07STakashi Iwai static int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream)
2028763f356cSTakashi Iwai {
202998274f07STakashi Iwai 	struct hdspm_midi *hmidi;
2030763f356cSTakashi Iwai 
2031763f356cSTakashi Iwai 	snd_hdspm_midi_output_trigger (substream, 0);
2032763f356cSTakashi Iwai 
2033ef5fa1a4STakashi Iwai 	hmidi = substream->rmidi->private_data;
2034763f356cSTakashi Iwai 	spin_lock_irq (&hmidi->lock);
2035763f356cSTakashi Iwai 	hmidi->output = NULL;
2036763f356cSTakashi Iwai 	spin_unlock_irq (&hmidi->lock);
2037763f356cSTakashi Iwai 
2038763f356cSTakashi Iwai 	return 0;
2039763f356cSTakashi Iwai }
2040763f356cSTakashi Iwai 
2041485885b9STakashi Iwai static const struct snd_rawmidi_ops snd_hdspm_midi_output =
2042763f356cSTakashi Iwai {
2043763f356cSTakashi Iwai 	.open =		snd_hdspm_midi_output_open,
2044763f356cSTakashi Iwai 	.close =	snd_hdspm_midi_output_close,
2045763f356cSTakashi Iwai 	.trigger =	snd_hdspm_midi_output_trigger,
2046763f356cSTakashi Iwai };
2047763f356cSTakashi Iwai 
2048485885b9STakashi Iwai static const struct snd_rawmidi_ops snd_hdspm_midi_input =
2049763f356cSTakashi Iwai {
2050763f356cSTakashi Iwai 	.open =		snd_hdspm_midi_input_open,
2051763f356cSTakashi Iwai 	.close =	snd_hdspm_midi_input_close,
2052763f356cSTakashi Iwai 	.trigger =	snd_hdspm_midi_input_trigger,
2053763f356cSTakashi Iwai };
2054763f356cSTakashi Iwai 
snd_hdspm_create_midi(struct snd_card * card,struct hdspm * hdspm,int id)2055e23e7a14SBill Pemberton static int snd_hdspm_create_midi(struct snd_card *card,
2056ef5fa1a4STakashi Iwai 				 struct hdspm *hdspm, int id)
2057763f356cSTakashi Iwai {
2058763f356cSTakashi Iwai 	int err;
20597ad210acSArnd Bergmann 	char buf[64];
2060763f356cSTakashi Iwai 
2061763f356cSTakashi Iwai 	hdspm->midi[id].id = id;
2062763f356cSTakashi Iwai 	hdspm->midi[id].hdspm = hdspm;
2063763f356cSTakashi Iwai 	spin_lock_init (&hdspm->midi[id].lock);
2064763f356cSTakashi Iwai 
20650dca1793SAdrian Knoth 	if (0 == id) {
20660dca1793SAdrian Knoth 		if (MADIface == hdspm->io_type) {
20670dca1793SAdrian Knoth 			/* MIDI-over-MADI on HDSPe MADIface */
20680dca1793SAdrian Knoth 			hdspm->midi[0].dataIn = HDSPM_midiDataIn2;
20690dca1793SAdrian Knoth 			hdspm->midi[0].statusIn = HDSPM_midiStatusIn2;
20700dca1793SAdrian Knoth 			hdspm->midi[0].dataOut = HDSPM_midiDataOut2;
20710dca1793SAdrian Knoth 			hdspm->midi[0].statusOut = HDSPM_midiStatusOut2;
20720dca1793SAdrian Knoth 			hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable;
20730dca1793SAdrian Knoth 			hdspm->midi[0].irq = HDSPM_midi2IRQPending;
20740dca1793SAdrian Knoth 		} else {
20750dca1793SAdrian Knoth 			hdspm->midi[0].dataIn = HDSPM_midiDataIn0;
20760dca1793SAdrian Knoth 			hdspm->midi[0].statusIn = HDSPM_midiStatusIn0;
20770dca1793SAdrian Knoth 			hdspm->midi[0].dataOut = HDSPM_midiDataOut0;
20780dca1793SAdrian Knoth 			hdspm->midi[0].statusOut = HDSPM_midiStatusOut0;
20790dca1793SAdrian Knoth 			hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable;
20800dca1793SAdrian Knoth 			hdspm->midi[0].irq = HDSPM_midi0IRQPending;
20810dca1793SAdrian Knoth 		}
20820dca1793SAdrian Knoth 	} else if (1 == id) {
20830dca1793SAdrian Knoth 		hdspm->midi[1].dataIn = HDSPM_midiDataIn1;
20840dca1793SAdrian Knoth 		hdspm->midi[1].statusIn = HDSPM_midiStatusIn1;
20850dca1793SAdrian Knoth 		hdspm->midi[1].dataOut = HDSPM_midiDataOut1;
20860dca1793SAdrian Knoth 		hdspm->midi[1].statusOut = HDSPM_midiStatusOut1;
20870dca1793SAdrian Knoth 		hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable;
20880dca1793SAdrian Knoth 		hdspm->midi[1].irq = HDSPM_midi1IRQPending;
20890dca1793SAdrian Knoth 	} else if ((2 == id) && (MADI == hdspm->io_type)) {
20900dca1793SAdrian Knoth 		/* MIDI-over-MADI on HDSPe MADI */
20910dca1793SAdrian Knoth 		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
20920dca1793SAdrian Knoth 		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
20930dca1793SAdrian Knoth 		hdspm->midi[2].dataOut = HDSPM_midiDataOut2;
20940dca1793SAdrian Knoth 		hdspm->midi[2].statusOut = HDSPM_midiStatusOut2;
20950dca1793SAdrian Knoth 		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
20960dca1793SAdrian Knoth 		hdspm->midi[2].irq = HDSPM_midi2IRQPending;
20970dca1793SAdrian Knoth 	} else if (2 == id) {
20980dca1793SAdrian Knoth 		/* TCO MTC, read only */
20990dca1793SAdrian Knoth 		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
21000dca1793SAdrian Knoth 		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
21010dca1793SAdrian Knoth 		hdspm->midi[2].dataOut = -1;
21020dca1793SAdrian Knoth 		hdspm->midi[2].statusOut = -1;
21030dca1793SAdrian Knoth 		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
21040dca1793SAdrian Knoth 		hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES;
21050dca1793SAdrian Knoth 	} else if (3 == id) {
21060dca1793SAdrian Knoth 		/* TCO MTC on HDSPe MADI */
21070dca1793SAdrian Knoth 		hdspm->midi[3].dataIn = HDSPM_midiDataIn3;
21080dca1793SAdrian Knoth 		hdspm->midi[3].statusIn = HDSPM_midiStatusIn3;
21090dca1793SAdrian Knoth 		hdspm->midi[3].dataOut = -1;
21100dca1793SAdrian Knoth 		hdspm->midi[3].statusOut = -1;
21110dca1793SAdrian Knoth 		hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable;
21120dca1793SAdrian Knoth 		hdspm->midi[3].irq = HDSPM_midi3IRQPending;
21130dca1793SAdrian Knoth 	}
21140dca1793SAdrian Knoth 
21150dca1793SAdrian Knoth 	if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) ||
21160dca1793SAdrian Knoth 					(MADIface == hdspm->io_type)))) {
21170dca1793SAdrian Knoth 		if ((id == 0) && (MADIface == hdspm->io_type)) {
21187ad210acSArnd Bergmann 			snprintf(buf, sizeof(buf), "%s MIDIoverMADI",
21197ad210acSArnd Bergmann 				 card->shortname);
21200dca1793SAdrian Knoth 		} else if ((id == 2) && (MADI == hdspm->io_type)) {
21217ad210acSArnd Bergmann 			snprintf(buf, sizeof(buf), "%s MIDIoverMADI",
21227ad210acSArnd Bergmann 				 card->shortname);
21230dca1793SAdrian Knoth 		} else {
21247ad210acSArnd Bergmann 			snprintf(buf, sizeof(buf), "%s MIDI %d",
21257ad210acSArnd Bergmann 				 card->shortname, id+1);
21260dca1793SAdrian Knoth 		}
21270dca1793SAdrian Knoth 		err = snd_rawmidi_new(card, buf, id, 1, 1,
21280dca1793SAdrian Knoth 				&hdspm->midi[id].rmidi);
2129ef5fa1a4STakashi Iwai 		if (err < 0)
2130763f356cSTakashi Iwai 			return err;
2131763f356cSTakashi Iwai 
21327ad210acSArnd Bergmann 		snprintf(hdspm->midi[id].rmidi->name,
21337ad210acSArnd Bergmann 			 sizeof(hdspm->midi[id].rmidi->name),
21347ad210acSArnd Bergmann 			 "%s MIDI %d", card->id, id+1);
2135763f356cSTakashi Iwai 		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
2136763f356cSTakashi Iwai 
21370dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
21380dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_OUTPUT,
2139ef5fa1a4STakashi Iwai 				&snd_hdspm_midi_output);
21400dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
21410dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_INPUT,
2142ef5fa1a4STakashi Iwai 				&snd_hdspm_midi_input);
2143763f356cSTakashi Iwai 
21440dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->info_flags |=
21450dca1793SAdrian Knoth 			SNDRV_RAWMIDI_INFO_OUTPUT |
2146763f356cSTakashi Iwai 			SNDRV_RAWMIDI_INFO_INPUT |
2147763f356cSTakashi Iwai 			SNDRV_RAWMIDI_INFO_DUPLEX;
21480dca1793SAdrian Knoth 	} else {
21490dca1793SAdrian Knoth 		/* TCO MTC, read only */
21507ad210acSArnd Bergmann 		snprintf(buf, sizeof(buf), "%s MTC %d",
21517ad210acSArnd Bergmann 			 card->shortname, id+1);
21520dca1793SAdrian Knoth 		err = snd_rawmidi_new(card, buf, id, 1, 1,
21530dca1793SAdrian Knoth 				&hdspm->midi[id].rmidi);
21540dca1793SAdrian Knoth 		if (err < 0)
21550dca1793SAdrian Knoth 			return err;
21560dca1793SAdrian Knoth 
21577ad210acSArnd Bergmann 		snprintf(hdspm->midi[id].rmidi->name,
21587ad210acSArnd Bergmann 			 sizeof(hdspm->midi[id].rmidi->name),
21590dca1793SAdrian Knoth 			 "%s MTC %d", card->id, id+1);
21600dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
21610dca1793SAdrian Knoth 
21620dca1793SAdrian Knoth 		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
21630dca1793SAdrian Knoth 				SNDRV_RAWMIDI_STREAM_INPUT,
21640dca1793SAdrian Knoth 				&snd_hdspm_midi_input);
21650dca1793SAdrian Knoth 
21660dca1793SAdrian Knoth 		hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
21670dca1793SAdrian Knoth 	}
2168763f356cSTakashi Iwai 
2169763f356cSTakashi Iwai 	return 0;
2170763f356cSTakashi Iwai }
2171763f356cSTakashi Iwai 
2172763f356cSTakashi Iwai 
hdspm_midi_work(struct work_struct * work)2173a2e527c5STakashi Iwai static void hdspm_midi_work(struct work_struct *work)
2174763f356cSTakashi Iwai {
2175a2e527c5STakashi Iwai 	struct hdspm *hdspm = container_of(work, struct hdspm, midi_work);
21760dca1793SAdrian Knoth 	int i = 0;
2177763f356cSTakashi Iwai 
21780dca1793SAdrian Knoth 	while (i < hdspm->midiPorts) {
21790dca1793SAdrian Knoth 		if (hdspm->midi[i].pending)
21800dca1793SAdrian Knoth 			snd_hdspm_midi_input_read(&hdspm->midi[i]);
21810dca1793SAdrian Knoth 
21820dca1793SAdrian Knoth 		i++;
21830dca1793SAdrian Knoth 	}
2184763f356cSTakashi Iwai }
2185763f356cSTakashi Iwai 
2186763f356cSTakashi Iwai 
2187763f356cSTakashi Iwai /*-----------------------------------------------------------------------------
2188763f356cSTakashi Iwai   Status Interface
2189763f356cSTakashi Iwai   ----------------------------------------------------------------------------*/
2190763f356cSTakashi Iwai 
2191763f356cSTakashi Iwai /* get the system sample rate which is set */
2192763f356cSTakashi Iwai 
21930dca1793SAdrian Knoth 
hdspm_get_pll_freq(struct hdspm * hdspm)21943f7bf918SAdrian Knoth static inline int hdspm_get_pll_freq(struct hdspm *hdspm)
21953f7bf918SAdrian Knoth {
21963f7bf918SAdrian Knoth 	unsigned int period, rate;
21973f7bf918SAdrian Knoth 
21983f7bf918SAdrian Knoth 	period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
21993f7bf918SAdrian Knoth 	rate = hdspm_calc_dds_value(hdspm, period);
22003f7bf918SAdrian Knoth 
22013f7bf918SAdrian Knoth 	return rate;
22023f7bf918SAdrian Knoth }
22033f7bf918SAdrian Knoth 
2204ddcecf6bSTakashi Iwai /*
22050dca1793SAdrian Knoth  * Calculate the real sample rate from the
22060dca1793SAdrian Knoth  * current DDS value.
2207ddcecf6bSTakashi Iwai  */
hdspm_get_system_sample_rate(struct hdspm * hdspm)22080dca1793SAdrian Knoth static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
22090dca1793SAdrian Knoth {
22103f7bf918SAdrian Knoth 	unsigned int rate;
22110dca1793SAdrian Knoth 
22123f7bf918SAdrian Knoth 	rate = hdspm_get_pll_freq(hdspm);
22130dca1793SAdrian Knoth 
2214a97bda7dSAdrian Knoth 	if (rate > 207000) {
221521a164dfSAdrian Knoth 		/* Unreasonable high sample rate as seen on PCI MADI cards. */
221621a164dfSAdrian Knoth 		if (0 == hdspm_system_clock_mode(hdspm)) {
221721a164dfSAdrian Knoth 			/* master mode, return internal sample rate */
2218a97bda7dSAdrian Knoth 			rate = hdspm->system_sample_rate;
221921a164dfSAdrian Knoth 		} else {
222021a164dfSAdrian Knoth 			/* slave mode, return external sample rate */
222121a164dfSAdrian Knoth 			rate = hdspm_external_sample_rate(hdspm);
2222c1099c32STakashi Iwai 			if (!rate)
2223c1099c32STakashi Iwai 				rate = hdspm->system_sample_rate;
222421a164dfSAdrian Knoth 		}
2225a97bda7dSAdrian Knoth 	}
2226a97bda7dSAdrian Knoth 
22270dca1793SAdrian Knoth 	return rate;
22280dca1793SAdrian Knoth }
22290dca1793SAdrian Knoth 
22300dca1793SAdrian Knoth 
2231763f356cSTakashi Iwai #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
223267ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2233763f356cSTakashi Iwai 	.name = xname, \
2234763f356cSTakashi Iwai 	.index = xindex, \
223541285a98SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
223641285a98SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2237763f356cSTakashi Iwai 	.info = snd_hdspm_info_system_sample_rate, \
223841285a98SAdrian Knoth 	.put = snd_hdspm_put_system_sample_rate, \
2239763f356cSTakashi Iwai 	.get = snd_hdspm_get_system_sample_rate \
2240763f356cSTakashi Iwai }
2241763f356cSTakashi Iwai 
snd_hdspm_info_system_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)224298274f07STakashi Iwai static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol,
224398274f07STakashi Iwai 					     struct snd_ctl_elem_info *uinfo)
2244763f356cSTakashi Iwai {
2245763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2246763f356cSTakashi Iwai 	uinfo->count = 1;
22470dca1793SAdrian Knoth 	uinfo->value.integer.min = 27000;
22480dca1793SAdrian Knoth 	uinfo->value.integer.max = 207000;
22490dca1793SAdrian Knoth 	uinfo->value.integer.step = 1;
2250763f356cSTakashi Iwai 	return 0;
2251763f356cSTakashi Iwai }
2252763f356cSTakashi Iwai 
22530dca1793SAdrian Knoth 
snd_hdspm_get_system_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)225498274f07STakashi Iwai static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol,
225598274f07STakashi Iwai 					    struct snd_ctl_elem_value *
2256763f356cSTakashi Iwai 					    ucontrol)
2257763f356cSTakashi Iwai {
225898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2259763f356cSTakashi Iwai 
22600dca1793SAdrian Knoth 	ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm);
2261763f356cSTakashi Iwai 	return 0;
2262763f356cSTakashi Iwai }
2263763f356cSTakashi Iwai 
snd_hdspm_put_system_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)226441285a98SAdrian Knoth static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol,
226541285a98SAdrian Knoth 					    struct snd_ctl_elem_value *
226641285a98SAdrian Knoth 					    ucontrol)
226741285a98SAdrian Knoth {
226841285a98SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2269c1099c32STakashi Iwai 	int rate = ucontrol->value.integer.value[0];
227041285a98SAdrian Knoth 
2271c1099c32STakashi Iwai 	if (rate < 27000 || rate > 207000)
2272c1099c32STakashi Iwai 		return -EINVAL;
2273537e4813STakashi Iwai 	hdspm_set_dds_value(hdspm, ucontrol->value.integer.value[0]);
227441285a98SAdrian Knoth 	return 0;
227541285a98SAdrian Knoth }
227641285a98SAdrian Knoth 
22770dca1793SAdrian Knoth 
2278ddcecf6bSTakashi Iwai /*
22790dca1793SAdrian Knoth  * Returns the WordClock sample rate class for the given card.
2280ddcecf6bSTakashi Iwai  */
hdspm_get_wc_sample_rate(struct hdspm * hdspm)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;
2290a57fea8eSAdrian Knoth 	case AES32:
2291a57fea8eSAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
2292a57fea8eSAdrian Knoth 		return (status >> HDSPM_AES32_wcFreq_bit) & 0xF;
22930dca1793SAdrian Knoth 	default:
22940dca1793SAdrian Knoth 		break;
22950dca1793SAdrian Knoth 	}
22960dca1793SAdrian Knoth 
22970dca1793SAdrian Knoth 
22980dca1793SAdrian Knoth 	return 0;
22990dca1793SAdrian Knoth }
23000dca1793SAdrian Knoth 
23010dca1793SAdrian Knoth 
2302ddcecf6bSTakashi Iwai /*
23030dca1793SAdrian Knoth  * Returns the TCO sample rate class for the given card.
2304ddcecf6bSTakashi Iwai  */
hdspm_get_tco_sample_rate(struct hdspm * hdspm)23050dca1793SAdrian Knoth static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
23060dca1793SAdrian Knoth {
23070dca1793SAdrian Knoth 	int status;
23080dca1793SAdrian Knoth 
23090dca1793SAdrian Knoth 	if (hdspm->tco) {
23100dca1793SAdrian Knoth 		switch (hdspm->io_type) {
23110dca1793SAdrian Knoth 		case RayDAT:
23120dca1793SAdrian Knoth 		case AIO:
23130dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
23140dca1793SAdrian Knoth 			return (status >> 20) & 0xF;
2315051c44feSAdrian Knoth 		case AES32:
2316051c44feSAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_statusRegister);
2317051c44feSAdrian Knoth 			return (status >> 1) & 0xF;
23180dca1793SAdrian Knoth 		default:
23190dca1793SAdrian Knoth 			break;
23200dca1793SAdrian Knoth 		}
23210dca1793SAdrian Knoth 	}
23220dca1793SAdrian Knoth 
23230dca1793SAdrian Knoth 	return 0;
23240dca1793SAdrian Knoth }
23250dca1793SAdrian Knoth 
23260dca1793SAdrian Knoth 
2327ddcecf6bSTakashi Iwai /*
23280dca1793SAdrian Knoth  * Returns the SYNC_IN sample rate class for the given card.
2329ddcecf6bSTakashi Iwai  */
hdspm_get_sync_in_sample_rate(struct hdspm * hdspm)23300dca1793SAdrian Knoth static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
23310dca1793SAdrian Knoth {
23320dca1793SAdrian Knoth 	int status;
23330dca1793SAdrian Knoth 
23340dca1793SAdrian Knoth 	if (hdspm->tco) {
23350dca1793SAdrian Knoth 		switch (hdspm->io_type) {
23360dca1793SAdrian Knoth 		case RayDAT:
23370dca1793SAdrian Knoth 		case AIO:
23380dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
23390dca1793SAdrian Knoth 			return (status >> 12) & 0xF;
23400dca1793SAdrian Knoth 		default:
23410dca1793SAdrian Knoth 			break;
23420dca1793SAdrian Knoth 		}
23430dca1793SAdrian Knoth 	}
23440dca1793SAdrian Knoth 
23450dca1793SAdrian Knoth 	return 0;
23460dca1793SAdrian Knoth }
23470dca1793SAdrian Knoth 
2348ddcecf6bSTakashi Iwai /*
2349d3c36ed8SAdrian Knoth  * Returns the AES sample rate class for the given card.
2350ddcecf6bSTakashi Iwai  */
hdspm_get_aes_sample_rate(struct hdspm * hdspm,int index)2351d3c36ed8SAdrian Knoth static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index)
2352d3c36ed8SAdrian Knoth {
2353d3c36ed8SAdrian Knoth 	int timecode;
2354d3c36ed8SAdrian Knoth 
2355d3c36ed8SAdrian Knoth 	switch (hdspm->io_type) {
2356d3c36ed8SAdrian Knoth 	case AES32:
2357d3c36ed8SAdrian Knoth 		timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
2358d3c36ed8SAdrian Knoth 		return (timecode >> (4*index)) & 0xF;
2359d3c36ed8SAdrian Knoth 	default:
2360d3c36ed8SAdrian Knoth 		break;
2361d3c36ed8SAdrian Knoth 	}
2362d3c36ed8SAdrian Knoth 	return 0;
2363d3c36ed8SAdrian Knoth }
23640dca1793SAdrian Knoth 
2365ddcecf6bSTakashi Iwai /*
23660dca1793SAdrian Knoth  * Returns the sample rate class for input source <idx> for
23670dca1793SAdrian Knoth  * 'new style' cards like the AIO and RayDAT.
2368ddcecf6bSTakashi Iwai  */
hdspm_get_s1_sample_rate(struct hdspm * hdspm,unsigned int idx)23690dca1793SAdrian Knoth static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
23700dca1793SAdrian Knoth {
23710dca1793SAdrian Knoth 	int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
23720dca1793SAdrian Knoth 
23730dca1793SAdrian Knoth 	return (status >> (idx*4)) & 0xF;
23740dca1793SAdrian Knoth }
23750dca1793SAdrian Knoth 
23768cea5710SAdrian Knoth #define ENUMERATED_CTL_INFO(info, texts) \
237738816545SAdrian Knoth 	snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts)
23788cea5710SAdrian Knoth 
23790dca1793SAdrian Knoth 
23802336142fSAdrian Knoth /* Helper function to query the external sample rate and return the
23812336142fSAdrian Knoth  * corresponding enum to be returned to userspace.
23822336142fSAdrian Knoth  */
hdspm_external_rate_to_enum(struct hdspm * hdspm)23832336142fSAdrian Knoth static int hdspm_external_rate_to_enum(struct hdspm *hdspm)
23842336142fSAdrian Knoth {
23852336142fSAdrian Knoth 	int rate = hdspm_external_sample_rate(hdspm);
23862336142fSAdrian Knoth 	int i, selected_rate = 0;
23872336142fSAdrian Knoth 	for (i = 1; i < 10; i++)
23882336142fSAdrian Knoth 		if (HDSPM_bit2freq(i) == rate) {
23892336142fSAdrian Knoth 			selected_rate = i;
23902336142fSAdrian Knoth 			break;
23912336142fSAdrian Knoth 		}
23922336142fSAdrian Knoth 	return selected_rate;
23932336142fSAdrian Knoth }
23942336142fSAdrian Knoth 
23950dca1793SAdrian Knoth 
2396763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
239767ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2398763f356cSTakashi Iwai 	.name = xname, \
23990dca1793SAdrian Knoth 	.private_value = xindex, \
2400763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ, \
2401763f356cSTakashi Iwai 	.info = snd_hdspm_info_autosync_sample_rate, \
2402763f356cSTakashi Iwai 	.get = snd_hdspm_get_autosync_sample_rate \
2403763f356cSTakashi Iwai }
2404763f356cSTakashi Iwai 
24050dca1793SAdrian Knoth 
snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)240698274f07STakashi Iwai static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
240798274f07STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
2408763f356cSTakashi Iwai {
2409e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts_freq);
2410763f356cSTakashi Iwai 	return 0;
2411763f356cSTakashi Iwai }
2412763f356cSTakashi Iwai 
24130dca1793SAdrian Knoth 
snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)241498274f07STakashi Iwai static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
241598274f07STakashi Iwai 					      struct snd_ctl_elem_value *
2416763f356cSTakashi Iwai 					      ucontrol)
2417763f356cSTakashi Iwai {
241898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2419763f356cSTakashi Iwai 
24200dca1793SAdrian Knoth 	switch (hdspm->io_type) {
24210dca1793SAdrian Knoth 	case RayDAT:
24220dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
24230dca1793SAdrian Knoth 		case 0:
24240dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24250dca1793SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
2426763f356cSTakashi Iwai 			break;
24270dca1793SAdrian Knoth 		case 7:
24280dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24290dca1793SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
2430763f356cSTakashi Iwai 			break;
24310dca1793SAdrian Knoth 		case 8:
24320dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24330dca1793SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
2434763f356cSTakashi Iwai 			break;
2435763f356cSTakashi Iwai 		default:
24360dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24370dca1793SAdrian Knoth 				hdspm_get_s1_sample_rate(hdspm,
24380dca1793SAdrian Knoth 						kcontrol->private_value-1);
2439763f356cSTakashi Iwai 		}
2440d681deaaSAdrian Knoth 		break;
24410dca1793SAdrian Knoth 
24420dca1793SAdrian Knoth 	case AIO:
24430dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
24440dca1793SAdrian Knoth 		case 0: /* WC */
24450dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24460dca1793SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
24470dca1793SAdrian Knoth 			break;
24480dca1793SAdrian Knoth 		case 4: /* TCO */
24490dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24500dca1793SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
24510dca1793SAdrian Knoth 			break;
24520dca1793SAdrian Knoth 		case 5: /* SYNC_IN */
24530dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24540dca1793SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
24550dca1793SAdrian Knoth 			break;
24560dca1793SAdrian Knoth 		default:
24570dca1793SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24580dca1793SAdrian Knoth 				hdspm_get_s1_sample_rate(hdspm,
24591cb7dbf4SAdrian Knoth 						kcontrol->private_value-1);
24600dca1793SAdrian Knoth 		}
2461d681deaaSAdrian Knoth 		break;
24627c4a95b5SAdrian Knoth 
24637c4a95b5SAdrian Knoth 	case AES32:
24647c4a95b5SAdrian Knoth 
24657c4a95b5SAdrian Knoth 		switch (kcontrol->private_value) {
24667c4a95b5SAdrian Knoth 		case 0: /* WC */
24677c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24687c4a95b5SAdrian Knoth 				hdspm_get_wc_sample_rate(hdspm);
24697c4a95b5SAdrian Knoth 			break;
24707c4a95b5SAdrian Knoth 		case 9: /* TCO */
24717c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24727c4a95b5SAdrian Knoth 				hdspm_get_tco_sample_rate(hdspm);
24737c4a95b5SAdrian Knoth 			break;
24747c4a95b5SAdrian Knoth 		case 10: /* SYNC_IN */
24757c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24767c4a95b5SAdrian Knoth 				hdspm_get_sync_in_sample_rate(hdspm);
24777c4a95b5SAdrian Knoth 			break;
24782d63ec38SAdrian Knoth 		case 11: /* External Rate */
24792d63ec38SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24802d63ec38SAdrian Knoth 				hdspm_external_rate_to_enum(hdspm);
24812d63ec38SAdrian Knoth 			break;
24827c4a95b5SAdrian Knoth 		default: /* AES1 to AES8 */
24837c4a95b5SAdrian Knoth 			ucontrol->value.enumerated.item[0] =
24842d63ec38SAdrian Knoth 				hdspm_get_aes_sample_rate(hdspm,
24852d63ec38SAdrian Knoth 						kcontrol->private_value -
24862d63ec38SAdrian Knoth 						HDSPM_AES32_AUTOSYNC_FROM_AES1);
24877c4a95b5SAdrian Knoth 			break;
24887c4a95b5SAdrian Knoth 		}
2489d681deaaSAdrian Knoth 		break;
2490b8812c55SAdrian Knoth 
2491b8812c55SAdrian Knoth 	case MADI:
2492b8812c55SAdrian Knoth 	case MADIface:
24932336142fSAdrian Knoth 		ucontrol->value.enumerated.item[0] =
24942336142fSAdrian Knoth 			hdspm_external_rate_to_enum(hdspm);
2495b8812c55SAdrian Knoth 		break;
24960dca1793SAdrian Knoth 	default:
24970dca1793SAdrian Knoth 		break;
24980dca1793SAdrian Knoth 	}
24990dca1793SAdrian Knoth 
2500763f356cSTakashi Iwai 	return 0;
2501763f356cSTakashi Iwai }
2502763f356cSTakashi Iwai 
25030dca1793SAdrian Knoth 
2504763f356cSTakashi Iwai #define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \
250567ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2506763f356cSTakashi Iwai 	.name = xname, \
2507763f356cSTakashi Iwai 	.index = xindex, \
25080dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
25090dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2510763f356cSTakashi Iwai 	.info = snd_hdspm_info_system_clock_mode, \
2511763f356cSTakashi Iwai 	.get = snd_hdspm_get_system_clock_mode, \
25120dca1793SAdrian Knoth 	.put = snd_hdspm_put_system_clock_mode, \
2513763f356cSTakashi Iwai }
2514763f356cSTakashi Iwai 
2515763f356cSTakashi Iwai 
2516ddcecf6bSTakashi Iwai /*
25170dca1793SAdrian Knoth  * Returns the system clock mode for the given card.
25180dca1793SAdrian Knoth  * @returns 0 - master, 1 - slave
2519ddcecf6bSTakashi Iwai  */
hdspm_system_clock_mode(struct hdspm * hdspm)252098274f07STakashi Iwai static int hdspm_system_clock_mode(struct hdspm *hdspm)
2521763f356cSTakashi Iwai {
25220dca1793SAdrian Knoth 	switch (hdspm->io_type) {
25230dca1793SAdrian Knoth 	case AIO:
25240dca1793SAdrian Knoth 	case RayDAT:
25250dca1793SAdrian Knoth 		if (hdspm->settings_register & HDSPM_c0Master)
25260dca1793SAdrian Knoth 			return 0;
25270dca1793SAdrian Knoth 		break;
2528763f356cSTakashi Iwai 
25290dca1793SAdrian Knoth 	default:
2530763f356cSTakashi Iwai 		if (hdspm->control_register & HDSPM_ClockModeMaster)
2531763f356cSTakashi Iwai 			return 0;
25320dca1793SAdrian Knoth 	}
25330dca1793SAdrian Knoth 
2534763f356cSTakashi Iwai 	return 1;
2535763f356cSTakashi Iwai }
2536763f356cSTakashi Iwai 
25370dca1793SAdrian Knoth 
2538ddcecf6bSTakashi Iwai /*
25390dca1793SAdrian Knoth  * Sets the system clock mode.
25400dca1793SAdrian Knoth  * @param mode 0 - master, 1 - slave
2541ddcecf6bSTakashi Iwai  */
hdspm_set_system_clock_mode(struct hdspm * hdspm,int mode)25420dca1793SAdrian Knoth static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
25430dca1793SAdrian Knoth {
254434be7ebbSAdrian Knoth 	hdspm_set_toggle_setting(hdspm,
254534be7ebbSAdrian Knoth 			(hdspm_is_raydat_or_aio(hdspm)) ?
254634be7ebbSAdrian Knoth 			HDSPM_c0Master : HDSPM_ClockModeMaster,
254734be7ebbSAdrian Knoth 			(0 == mode));
25480dca1793SAdrian Knoth }
25490dca1793SAdrian Knoth 
25500dca1793SAdrian Knoth 
snd_hdspm_info_system_clock_mode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)255198274f07STakashi Iwai static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
255298274f07STakashi Iwai 					    struct snd_ctl_elem_info *uinfo)
2553763f356cSTakashi Iwai {
255438816545SAdrian Knoth 	static const char *const texts[] = { "Master", "AutoSync" };
2555e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
2556763f356cSTakashi Iwai 	return 0;
2557763f356cSTakashi Iwai }
2558763f356cSTakashi Iwai 
snd_hdspm_get_system_clock_mode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)255998274f07STakashi Iwai static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol,
256098274f07STakashi Iwai 					   struct snd_ctl_elem_value *ucontrol)
2561763f356cSTakashi Iwai {
256298274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2563763f356cSTakashi Iwai 
25640dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm);
2565763f356cSTakashi Iwai 	return 0;
2566763f356cSTakashi Iwai }
2567763f356cSTakashi Iwai 
snd_hdspm_put_system_clock_mode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)25680dca1793SAdrian Knoth static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol,
25690dca1793SAdrian Knoth 					   struct snd_ctl_elem_value *ucontrol)
25700dca1793SAdrian Knoth {
25710dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
25720dca1793SAdrian Knoth 	int val;
25730dca1793SAdrian Knoth 
25740dca1793SAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
25750dca1793SAdrian Knoth 		return -EBUSY;
25760dca1793SAdrian Knoth 
25770dca1793SAdrian Knoth 	val = ucontrol->value.enumerated.item[0];
25780dca1793SAdrian Knoth 	if (val < 0)
25790dca1793SAdrian Knoth 		val = 0;
25800dca1793SAdrian Knoth 	else if (val > 1)
25810dca1793SAdrian Knoth 		val = 1;
25820dca1793SAdrian Knoth 
25830dca1793SAdrian Knoth 	hdspm_set_system_clock_mode(hdspm, val);
25840dca1793SAdrian Knoth 
25850dca1793SAdrian Knoth 	return 0;
25860dca1793SAdrian Knoth }
25870dca1793SAdrian Knoth 
25880dca1793SAdrian Knoth 
25890dca1793SAdrian Knoth #define HDSPM_INTERNAL_CLOCK(xname, xindex) \
259067ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2591763f356cSTakashi Iwai 	.name = xname, \
2592763f356cSTakashi Iwai 	.index = xindex, \
2593763f356cSTakashi Iwai 	.info = snd_hdspm_info_clock_source, \
2594763f356cSTakashi Iwai 	.get = snd_hdspm_get_clock_source, \
2595763f356cSTakashi Iwai 	.put = snd_hdspm_put_clock_source \
2596763f356cSTakashi Iwai }
2597763f356cSTakashi Iwai 
25980dca1793SAdrian Knoth 
hdspm_clock_source(struct hdspm * hdspm)259998274f07STakashi Iwai static int hdspm_clock_source(struct hdspm * hdspm)
2600763f356cSTakashi Iwai {
2601763f356cSTakashi Iwai 	switch (hdspm->system_sample_rate) {
26020dca1793SAdrian Knoth 	case 32000: return 0;
26030dca1793SAdrian Knoth 	case 44100: return 1;
26040dca1793SAdrian Knoth 	case 48000: return 2;
26050dca1793SAdrian Knoth 	case 64000: return 3;
26060dca1793SAdrian Knoth 	case 88200: return 4;
26070dca1793SAdrian Knoth 	case 96000: return 5;
26080dca1793SAdrian Knoth 	case 128000: return 6;
26090dca1793SAdrian Knoth 	case 176400: return 7;
26100dca1793SAdrian Knoth 	case 192000: return 8;
2611763f356cSTakashi Iwai 	}
26120dca1793SAdrian Knoth 
26130dca1793SAdrian Knoth 	return -1;
2614763f356cSTakashi Iwai }
2615763f356cSTakashi Iwai 
hdspm_set_clock_source(struct hdspm * hdspm,int mode)261698274f07STakashi Iwai static int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
2617763f356cSTakashi Iwai {
2618763f356cSTakashi Iwai 	int rate;
2619763f356cSTakashi Iwai 	switch (mode) {
26200dca1793SAdrian Knoth 	case 0:
26210dca1793SAdrian Knoth 		rate = 32000; break;
26220dca1793SAdrian Knoth 	case 1:
26230dca1793SAdrian Knoth 		rate = 44100; break;
26240dca1793SAdrian Knoth 	case 2:
26250dca1793SAdrian Knoth 		rate = 48000; break;
26260dca1793SAdrian Knoth 	case 3:
26270dca1793SAdrian Knoth 		rate = 64000; break;
26280dca1793SAdrian Knoth 	case 4:
26290dca1793SAdrian Knoth 		rate = 88200; break;
26300dca1793SAdrian Knoth 	case 5:
26310dca1793SAdrian Knoth 		rate = 96000; break;
26320dca1793SAdrian Knoth 	case 6:
26330dca1793SAdrian Knoth 		rate = 128000; break;
26340dca1793SAdrian Knoth 	case 7:
26350dca1793SAdrian Knoth 		rate = 176400; break;
26360dca1793SAdrian Knoth 	case 8:
26370dca1793SAdrian Knoth 		rate = 192000; break;
2638763f356cSTakashi Iwai 	default:
26390dca1793SAdrian Knoth 		rate = 48000;
2640763f356cSTakashi Iwai 	}
2641763f356cSTakashi Iwai 	hdspm_set_rate(hdspm, rate, 1);
2642763f356cSTakashi Iwai 	return 0;
2643763f356cSTakashi Iwai }
2644763f356cSTakashi Iwai 
snd_hdspm_info_clock_source(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)264598274f07STakashi Iwai static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol,
264698274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
2647763f356cSTakashi Iwai {
2648c69a637bSTakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 9, texts_freq + 1);
2649763f356cSTakashi Iwai }
2650763f356cSTakashi Iwai 
snd_hdspm_get_clock_source(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)265198274f07STakashi Iwai static int snd_hdspm_get_clock_source(struct snd_kcontrol *kcontrol,
265298274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
2653763f356cSTakashi Iwai {
265498274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2655763f356cSTakashi Iwai 
2656763f356cSTakashi Iwai 	ucontrol->value.enumerated.item[0] = hdspm_clock_source(hdspm);
2657763f356cSTakashi Iwai 	return 0;
2658763f356cSTakashi Iwai }
2659763f356cSTakashi Iwai 
snd_hdspm_put_clock_source(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)266098274f07STakashi Iwai static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol,
266198274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
2662763f356cSTakashi Iwai {
266398274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
2664763f356cSTakashi Iwai 	int change;
2665763f356cSTakashi Iwai 	int val;
2666763f356cSTakashi Iwai 
2667763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2668763f356cSTakashi Iwai 		return -EBUSY;
2669763f356cSTakashi Iwai 	val = ucontrol->value.enumerated.item[0];
2670763f356cSTakashi Iwai 	if (val < 0)
2671763f356cSTakashi Iwai 		val = 0;
26726534599dSRemy Bruno 	if (val > 9)
26736534599dSRemy Bruno 		val = 9;
2674763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
2675763f356cSTakashi Iwai 	if (val != hdspm_clock_source(hdspm))
2676763f356cSTakashi Iwai 		change = (hdspm_set_clock_source(hdspm, val) == 0) ? 1 : 0;
2677763f356cSTakashi Iwai 	else
2678763f356cSTakashi Iwai 		change = 0;
2679763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
2680763f356cSTakashi Iwai 	return change;
2681763f356cSTakashi Iwai }
2682763f356cSTakashi Iwai 
26830dca1793SAdrian Knoth 
2684763f356cSTakashi Iwai #define HDSPM_PREF_SYNC_REF(xname, xindex) \
268567ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
2686763f356cSTakashi Iwai 	.name = xname, \
2687763f356cSTakashi Iwai 	.index = xindex, \
26880dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
26890dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
2690763f356cSTakashi Iwai 	.info = snd_hdspm_info_pref_sync_ref, \
2691763f356cSTakashi Iwai 	.get = snd_hdspm_get_pref_sync_ref, \
2692763f356cSTakashi Iwai 	.put = snd_hdspm_put_pref_sync_ref \
2693763f356cSTakashi Iwai }
2694763f356cSTakashi Iwai 
26950dca1793SAdrian Knoth 
2696ddcecf6bSTakashi Iwai /*
26970dca1793SAdrian Knoth  * Returns the current preferred sync reference setting.
26980dca1793SAdrian Knoth  * The semantics of the return value are depending on the
26990dca1793SAdrian Knoth  * card, please see the comments for clarification.
2700ddcecf6bSTakashi Iwai  */
hdspm_pref_sync_ref(struct hdspm * hdspm)270198274f07STakashi Iwai static int hdspm_pref_sync_ref(struct hdspm * hdspm)
2702763f356cSTakashi Iwai {
27030dca1793SAdrian Knoth 	switch (hdspm->io_type) {
27040dca1793SAdrian Knoth 	case AES32:
27053cee5a60SRemy Bruno 		switch (hdspm->control_register & HDSPM_SyncRefMask) {
27060dca1793SAdrian Knoth 		case 0: return 0;  /* WC */
27070dca1793SAdrian Knoth 		case HDSPM_SyncRef0: return 1; /* AES 1 */
27080dca1793SAdrian Knoth 		case HDSPM_SyncRef1: return 2; /* AES 2 */
27090dca1793SAdrian Knoth 		case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */
27100dca1793SAdrian Knoth 		case HDSPM_SyncRef2: return 4; /* AES 4 */
27110dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */
27120dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */
27130dca1793SAdrian Knoth 		case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0:
27140dca1793SAdrian Knoth 						    return 7; /* AES 7 */
27150dca1793SAdrian Knoth 		case HDSPM_SyncRef3: return 8; /* AES 8 */
27160dca1793SAdrian Knoth 		case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */
27170dca1793SAdrian Knoth 		}
27180dca1793SAdrian Knoth 		break;
27190dca1793SAdrian Knoth 
27200dca1793SAdrian Knoth 	case MADI:
27210dca1793SAdrian Knoth 	case MADIface:
27220dca1793SAdrian Knoth 		if (hdspm->tco) {
27230dca1793SAdrian Knoth 			switch (hdspm->control_register & HDSPM_SyncRefMask) {
27240dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27250dca1793SAdrian Knoth 			case HDSPM_SyncRef0: return 1;  /* MADI */
27260dca1793SAdrian Knoth 			case HDSPM_SyncRef1: return 2;  /* TCO */
27270dca1793SAdrian Knoth 			case HDSPM_SyncRef1+HDSPM_SyncRef0:
27280dca1793SAdrian Knoth 					     return 3;  /* SYNC_IN */
27293cee5a60SRemy Bruno 			}
27303cee5a60SRemy Bruno 		} else {
2731763f356cSTakashi Iwai 			switch (hdspm->control_register & HDSPM_SyncRefMask) {
27320dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27330dca1793SAdrian Knoth 			case HDSPM_SyncRef0: return 1;  /* MADI */
27340dca1793SAdrian Knoth 			case HDSPM_SyncRef1+HDSPM_SyncRef0:
27350dca1793SAdrian Knoth 					     return 2;  /* SYNC_IN */
27360dca1793SAdrian Knoth 			}
27370dca1793SAdrian Knoth 		}
27380dca1793SAdrian Knoth 		break;
27390dca1793SAdrian Knoth 
27400dca1793SAdrian Knoth 	case RayDAT:
27410dca1793SAdrian Knoth 		if (hdspm->tco) {
27420dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27430dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27440dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27450dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT 1 */
27460dca1793SAdrian Knoth 			case 4: return 2;  /* ADAT 2 */
27470dca1793SAdrian Knoth 			case 5: return 3;  /* ADAT 3 */
27480dca1793SAdrian Knoth 			case 6: return 4;  /* ADAT 4 */
27490dca1793SAdrian Knoth 			case 1: return 5;  /* AES */
27500dca1793SAdrian Knoth 			case 2: return 6;  /* SPDIF */
27510dca1793SAdrian Knoth 			case 9: return 7;  /* TCO */
27520dca1793SAdrian Knoth 			case 10: return 8; /* SYNC_IN */
27530dca1793SAdrian Knoth 			}
27540dca1793SAdrian Knoth 		} else {
27550dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27560dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27570dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27580dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT 1 */
27590dca1793SAdrian Knoth 			case 4: return 2;  /* ADAT 2 */
27600dca1793SAdrian Knoth 			case 5: return 3;  /* ADAT 3 */
27610dca1793SAdrian Knoth 			case 6: return 4;  /* ADAT 4 */
27620dca1793SAdrian Knoth 			case 1: return 5;  /* AES */
27630dca1793SAdrian Knoth 			case 2: return 6;  /* SPDIF */
27640dca1793SAdrian Knoth 			case 10: return 7; /* SYNC_IN */
2765763f356cSTakashi Iwai 			}
27663cee5a60SRemy Bruno 		}
2767763f356cSTakashi Iwai 
27680dca1793SAdrian Knoth 		break;
27690dca1793SAdrian Knoth 
27700dca1793SAdrian Knoth 	case AIO:
27710dca1793SAdrian Knoth 		if (hdspm->tco) {
27720dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27730dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27740dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27750dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT */
27760dca1793SAdrian Knoth 			case 1: return 2;  /* AES */
27770dca1793SAdrian Knoth 			case 2: return 3;  /* SPDIF */
27780dca1793SAdrian Knoth 			case 9: return 4;  /* TCO */
27790dca1793SAdrian Knoth 			case 10: return 5; /* SYNC_IN */
27800dca1793SAdrian Knoth 			}
27810dca1793SAdrian Knoth 		} else {
27820dca1793SAdrian Knoth 			switch ((hdspm->settings_register &
27830dca1793SAdrian Knoth 				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
27840dca1793SAdrian Knoth 			case 0: return 0;  /* WC */
27850dca1793SAdrian Knoth 			case 3: return 1;  /* ADAT */
27860dca1793SAdrian Knoth 			case 1: return 2;  /* AES */
27870dca1793SAdrian Knoth 			case 2: return 3;  /* SPDIF */
27880dca1793SAdrian Knoth 			case 10: return 4; /* SYNC_IN */
27890dca1793SAdrian Knoth 			}
2790763f356cSTakashi Iwai 		}
2791763f356cSTakashi Iwai 
27920dca1793SAdrian Knoth 		break;
27930dca1793SAdrian Knoth 	}
27940dca1793SAdrian Knoth 
27950dca1793SAdrian Knoth 	return -1;
27960dca1793SAdrian Knoth }
27970dca1793SAdrian Knoth 
27980dca1793SAdrian Knoth 
2799ddcecf6bSTakashi Iwai /*
28000dca1793SAdrian Knoth  * Set the preferred sync reference to <pref>. The semantics
28010dca1793SAdrian Knoth  * of <pref> are depending on the card type, see the comments
28020dca1793SAdrian Knoth  * for clarification.
2803ddcecf6bSTakashi Iwai  */
hdspm_set_pref_sync_ref(struct hdspm * hdspm,int pref)280498274f07STakashi Iwai static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
2805763f356cSTakashi Iwai {
28060dca1793SAdrian Knoth 	int p = 0;
2807763f356cSTakashi Iwai 
28080dca1793SAdrian Knoth 	switch (hdspm->io_type) {
28090dca1793SAdrian Knoth 	case AES32:
28100dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPM_SyncRefMask;
28113cee5a60SRemy Bruno 		switch (pref) {
28120dca1793SAdrian Knoth 		case 0: /* WC  */
28133cee5a60SRemy Bruno 			break;
28140dca1793SAdrian Knoth 		case 1: /* AES 1 */
28153cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef0;
28163cee5a60SRemy Bruno 			break;
28170dca1793SAdrian Knoth 		case 2: /* AES 2 */
28183cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef1;
28193cee5a60SRemy Bruno 			break;
28200dca1793SAdrian Knoth 		case 3: /* AES 3 */
28210dca1793SAdrian Knoth 			hdspm->control_register |=
28220dca1793SAdrian Knoth 				HDSPM_SyncRef1+HDSPM_SyncRef0;
28233cee5a60SRemy Bruno 			break;
28240dca1793SAdrian Knoth 		case 4: /* AES 4 */
28253cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef2;
28263cee5a60SRemy Bruno 			break;
28270dca1793SAdrian Knoth 		case 5: /* AES 5 */
28280dca1793SAdrian Knoth 			hdspm->control_register |=
28290dca1793SAdrian Knoth 				HDSPM_SyncRef2+HDSPM_SyncRef0;
28303cee5a60SRemy Bruno 			break;
28310dca1793SAdrian Knoth 		case 6: /* AES 6 */
28320dca1793SAdrian Knoth 			hdspm->control_register |=
28330dca1793SAdrian Knoth 				HDSPM_SyncRef2+HDSPM_SyncRef1;
28343cee5a60SRemy Bruno 			break;
28350dca1793SAdrian Knoth 		case 7: /* AES 7 */
2836ef5fa1a4STakashi Iwai 			hdspm->control_register |=
2837ef5fa1a4STakashi Iwai 				HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
28383cee5a60SRemy Bruno 			break;
28390dca1793SAdrian Knoth 		case 8: /* AES 8 */
28403cee5a60SRemy Bruno 			hdspm->control_register |= HDSPM_SyncRef3;
28413cee5a60SRemy Bruno 			break;
28420dca1793SAdrian Knoth 		case 9: /* TCO */
28430dca1793SAdrian Knoth 			hdspm->control_register |=
28440dca1793SAdrian Knoth 				HDSPM_SyncRef3+HDSPM_SyncRef0;
28450dca1793SAdrian Knoth 			break;
28460dca1793SAdrian Knoth 		default:
28470dca1793SAdrian Knoth 			return -1;
28480dca1793SAdrian Knoth 		}
28490dca1793SAdrian Knoth 
28500dca1793SAdrian Knoth 		break;
28510dca1793SAdrian Knoth 
28520dca1793SAdrian Knoth 	case MADI:
28530dca1793SAdrian Knoth 	case MADIface:
28540dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPM_SyncRefMask;
28550dca1793SAdrian Knoth 		if (hdspm->tco) {
28560dca1793SAdrian Knoth 			switch (pref) {
28570dca1793SAdrian Knoth 			case 0: /* WC */
28580dca1793SAdrian Knoth 				break;
28590dca1793SAdrian Knoth 			case 1: /* MADI */
28600dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef0;
28610dca1793SAdrian Knoth 				break;
28620dca1793SAdrian Knoth 			case 2: /* TCO */
28630dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef1;
28640dca1793SAdrian Knoth 				break;
28650dca1793SAdrian Knoth 			case 3: /* SYNC_IN */
28660dca1793SAdrian Knoth 				hdspm->control_register |=
28670dca1793SAdrian Knoth 					HDSPM_SyncRef0+HDSPM_SyncRef1;
28680dca1793SAdrian Knoth 				break;
28693cee5a60SRemy Bruno 			default:
28703cee5a60SRemy Bruno 				return -1;
28713cee5a60SRemy Bruno 			}
28723cee5a60SRemy Bruno 		} else {
2873763f356cSTakashi Iwai 			switch (pref) {
28740dca1793SAdrian Knoth 			case 0: /* WC */
2875763f356cSTakashi Iwai 				break;
28760dca1793SAdrian Knoth 			case 1: /* MADI */
28770dca1793SAdrian Knoth 				hdspm->control_register |= HDSPM_SyncRef0;
28780dca1793SAdrian Knoth 				break;
28790dca1793SAdrian Knoth 			case 2: /* SYNC_IN */
28800dca1793SAdrian Knoth 				hdspm->control_register |=
28810dca1793SAdrian Knoth 					HDSPM_SyncRef0+HDSPM_SyncRef1;
2882763f356cSTakashi Iwai 				break;
2883763f356cSTakashi Iwai 			default:
2884763f356cSTakashi Iwai 				return -1;
2885763f356cSTakashi Iwai 			}
28863cee5a60SRemy Bruno 		}
28870dca1793SAdrian Knoth 
28880dca1793SAdrian Knoth 		break;
28890dca1793SAdrian Knoth 
28900dca1793SAdrian Knoth 	case RayDAT:
28910dca1793SAdrian Knoth 		if (hdspm->tco) {
28920dca1793SAdrian Knoth 			switch (pref) {
28930dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
28940dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT 1 */
28950dca1793SAdrian Knoth 			case 2: p = 4; break;  /* ADAT 2 */
28960dca1793SAdrian Knoth 			case 3: p = 5; break;  /* ADAT 3 */
28970dca1793SAdrian Knoth 			case 4: p = 6; break;  /* ADAT 4 */
28980dca1793SAdrian Knoth 			case 5: p = 1; break;  /* AES */
28990dca1793SAdrian Knoth 			case 6: p = 2; break;  /* SPDIF */
29000dca1793SAdrian Knoth 			case 7: p = 9; break;  /* TCO */
29010dca1793SAdrian Knoth 			case 8: p = 10; break; /* SYNC_IN */
29020dca1793SAdrian Knoth 			default: return -1;
29030dca1793SAdrian Knoth 			}
29040dca1793SAdrian Knoth 		} else {
29050dca1793SAdrian Knoth 			switch (pref) {
29060dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
29070dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT 1 */
29080dca1793SAdrian Knoth 			case 2: p = 4; break;  /* ADAT 2 */
29090dca1793SAdrian Knoth 			case 3: p = 5; break;  /* ADAT 3 */
29100dca1793SAdrian Knoth 			case 4: p = 6; break;  /* ADAT 4 */
29110dca1793SAdrian Knoth 			case 5: p = 1; break;  /* AES */
29120dca1793SAdrian Knoth 			case 6: p = 2; break;  /* SPDIF */
29130dca1793SAdrian Knoth 			case 7: p = 10; break; /* SYNC_IN */
29140dca1793SAdrian Knoth 			default: return -1;
29150dca1793SAdrian Knoth 			}
29160dca1793SAdrian Knoth 		}
29170dca1793SAdrian Knoth 		break;
29180dca1793SAdrian Knoth 
29190dca1793SAdrian Knoth 	case AIO:
29200dca1793SAdrian Knoth 		if (hdspm->tco) {
29210dca1793SAdrian Knoth 			switch (pref) {
29220dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
29230dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT */
29240dca1793SAdrian Knoth 			case 2: p = 1; break;  /* AES */
29250dca1793SAdrian Knoth 			case 3: p = 2; break;  /* SPDIF */
29260dca1793SAdrian Knoth 			case 4: p = 9; break;  /* TCO */
29270dca1793SAdrian Knoth 			case 5: p = 10; break; /* SYNC_IN */
29280dca1793SAdrian Knoth 			default: return -1;
29290dca1793SAdrian Knoth 			}
29300dca1793SAdrian Knoth 		} else {
29310dca1793SAdrian Knoth 			switch (pref) {
29320dca1793SAdrian Knoth 			case 0: p = 0; break;  /* WC */
29330dca1793SAdrian Knoth 			case 1: p = 3; break;  /* ADAT */
29340dca1793SAdrian Knoth 			case 2: p = 1; break;  /* AES */
29350dca1793SAdrian Knoth 			case 3: p = 2; break;  /* SPDIF */
29360dca1793SAdrian Knoth 			case 4: p = 10; break; /* SYNC_IN */
29370dca1793SAdrian Knoth 			default: return -1;
29380dca1793SAdrian Knoth 			}
29390dca1793SAdrian Knoth 		}
29400dca1793SAdrian Knoth 		break;
29410dca1793SAdrian Knoth 	}
29420dca1793SAdrian Knoth 
29430dca1793SAdrian Knoth 	switch (hdspm->io_type) {
29440dca1793SAdrian Knoth 	case RayDAT:
29450dca1793SAdrian Knoth 	case AIO:
29460dca1793SAdrian Knoth 		hdspm->settings_register &= ~HDSPM_c0_SyncRefMask;
29470dca1793SAdrian Knoth 		hdspm->settings_register |= HDSPM_c0_SyncRef0 * p;
29480dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
29490dca1793SAdrian Knoth 		break;
29500dca1793SAdrian Knoth 
29510dca1793SAdrian Knoth 	case MADI:
29520dca1793SAdrian Knoth 	case MADIface:
29530dca1793SAdrian Knoth 	case AES32:
29540dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_controlRegister,
29550dca1793SAdrian Knoth 				hdspm->control_register);
29560dca1793SAdrian Knoth 	}
29570dca1793SAdrian Knoth 
2958763f356cSTakashi Iwai 	return 0;
2959763f356cSTakashi Iwai }
2960763f356cSTakashi Iwai 
29610dca1793SAdrian Knoth 
snd_hdspm_info_pref_sync_ref(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)296298274f07STakashi Iwai static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
296398274f07STakashi Iwai 					struct snd_ctl_elem_info *uinfo)
2964763f356cSTakashi Iwai {
29653cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
29663cee5a60SRemy Bruno 
2967eb0d4dbfSAdrian Knoth 	snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync);
2968763f356cSTakashi Iwai 
2969763f356cSTakashi Iwai 	return 0;
2970763f356cSTakashi Iwai }
2971763f356cSTakashi Iwai 
snd_hdspm_get_pref_sync_ref(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)297298274f07STakashi Iwai static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol,
297398274f07STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
2974763f356cSTakashi Iwai {
297598274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
29760dca1793SAdrian Knoth 	int psf = hdspm_pref_sync_ref(hdspm);
2977763f356cSTakashi Iwai 
29780dca1793SAdrian Knoth 	if (psf >= 0) {
29790dca1793SAdrian Knoth 		ucontrol->value.enumerated.item[0] = psf;
2980763f356cSTakashi Iwai 		return 0;
2981763f356cSTakashi Iwai 	}
2982763f356cSTakashi Iwai 
29830dca1793SAdrian Knoth 	return -1;
29840dca1793SAdrian Knoth }
29850dca1793SAdrian Knoth 
snd_hdspm_put_pref_sync_ref(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)298698274f07STakashi Iwai static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
298798274f07STakashi Iwai 				       struct snd_ctl_elem_value *ucontrol)
2988763f356cSTakashi Iwai {
298998274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
29900dca1793SAdrian Knoth 	int val, change = 0;
2991763f356cSTakashi Iwai 
2992763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
2993763f356cSTakashi Iwai 		return -EBUSY;
2994763f356cSTakashi Iwai 
29950dca1793SAdrian Knoth 	val = ucontrol->value.enumerated.item[0];
29960dca1793SAdrian Knoth 
29970dca1793SAdrian Knoth 	if (val < 0)
29980dca1793SAdrian Knoth 		val = 0;
29990dca1793SAdrian Knoth 	else if (val >= hdspm->texts_autosync_items)
30000dca1793SAdrian Knoth 		val = hdspm->texts_autosync_items-1;
3001763f356cSTakashi Iwai 
3002763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
30030dca1793SAdrian Knoth 	if (val != hdspm_pref_sync_ref(hdspm))
30040dca1793SAdrian Knoth 		change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0;
30050dca1793SAdrian Knoth 
3006763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3007763f356cSTakashi Iwai 	return change;
3008763f356cSTakashi Iwai }
3009763f356cSTakashi Iwai 
30100dca1793SAdrian Knoth 
3011763f356cSTakashi Iwai #define HDSPM_AUTOSYNC_REF(xname, xindex) \
301267ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3013763f356cSTakashi Iwai 	.name = xname, \
3014763f356cSTakashi Iwai 	.index = xindex, \
3015763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ, \
3016763f356cSTakashi Iwai 	.info = snd_hdspm_info_autosync_ref, \
3017763f356cSTakashi Iwai 	.get = snd_hdspm_get_autosync_ref, \
3018763f356cSTakashi Iwai }
3019763f356cSTakashi Iwai 
hdspm_autosync_ref(struct hdspm * hdspm)302098274f07STakashi Iwai static int hdspm_autosync_ref(struct hdspm *hdspm)
3021763f356cSTakashi Iwai {
3022763f356cSTakashi Iwai 	/* This looks at the autosync selected sync reference */
30232d60fc7fSAdrian Knoth 	if (AES32 == hdspm->io_type) {
3024763f356cSTakashi Iwai 
30252d60fc7fSAdrian Knoth 		unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
30262d60fc7fSAdrian Knoth 		unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
3027ab3ee092SPierre-Louis Bossart 		/* syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD is always true */
3028ab3ee092SPierre-Louis Bossart 		if (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN) {
30292d60fc7fSAdrian Knoth 			return syncref;
30302d60fc7fSAdrian Knoth 		}
30312d60fc7fSAdrian Knoth 		return HDSPM_AES32_AUTOSYNC_FROM_NONE;
30322d60fc7fSAdrian Knoth 
30332d60fc7fSAdrian Knoth 	} else if (MADI == hdspm->io_type) {
30342d60fc7fSAdrian Knoth 
30352d60fc7fSAdrian Knoth 		unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
3036763f356cSTakashi Iwai 		switch (status2 & HDSPM_SelSyncRefMask) {
3037763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_WORD:
3038763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_WORD;
3039763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_MADI:
3040763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_MADI;
30410dca1793SAdrian Knoth 		case HDSPM_SelSyncRef_TCO:
30420dca1793SAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_TCO;
30430dca1793SAdrian Knoth 		case HDSPM_SelSyncRef_SyncIn:
30440dca1793SAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_SYNC_IN;
3045763f356cSTakashi Iwai 		case HDSPM_SelSyncRef_NVALID:
3046763f356cSTakashi Iwai 			return HDSPM_AUTOSYNC_FROM_NONE;
3047763f356cSTakashi Iwai 		default:
3048e71b95adSAdrian Knoth 			return HDSPM_AUTOSYNC_FROM_NONE;
3049763f356cSTakashi Iwai 		}
3050763f356cSTakashi Iwai 
30510dca1793SAdrian Knoth 	}
3052763f356cSTakashi Iwai 	return 0;
3053763f356cSTakashi Iwai }
30540dca1793SAdrian Knoth 
3055763f356cSTakashi Iwai 
snd_hdspm_info_autosync_ref(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)305698274f07STakashi Iwai static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
305798274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
3058763f356cSTakashi Iwai {
30593cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
30603cee5a60SRemy Bruno 
30610dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
306204659f9eSAdrian Knoth 		static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3",
3063db2d1a91SAdrian Knoth 			"AES4",	"AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"};
30643cee5a60SRemy Bruno 
306504659f9eSAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts);
30660dca1793SAdrian Knoth 	} else if (MADI == hdspm->io_type) {
306704659f9eSAdrian Knoth 		static const char *const texts[] = {"Word Clock", "MADI", "TCO",
30680dca1793SAdrian Knoth 			"Sync In", "None" };
3069763f356cSTakashi Iwai 
307004659f9eSAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts);
30713cee5a60SRemy Bruno 	}
3072763f356cSTakashi Iwai 	return 0;
3073763f356cSTakashi Iwai }
3074763f356cSTakashi Iwai 
snd_hdspm_get_autosync_ref(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)307598274f07STakashi Iwai static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
307698274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3077763f356cSTakashi Iwai {
307898274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3079763f356cSTakashi Iwai 
30806534599dSRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_autosync_ref(hdspm);
3081763f356cSTakashi Iwai 	return 0;
3082763f356cSTakashi Iwai }
3083763f356cSTakashi Iwai 
3084f99c7881SAdrian Knoth 
3085f99c7881SAdrian Knoth 
3086f99c7881SAdrian Knoth #define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \
3087f99c7881SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3088f99c7881SAdrian Knoth 	.name = xname, \
3089f99c7881SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READ |\
3090f99c7881SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3091f99c7881SAdrian Knoth 	.info = snd_hdspm_info_tco_video_input_format, \
3092f99c7881SAdrian Knoth 	.get = snd_hdspm_get_tco_video_input_format, \
3093f99c7881SAdrian Knoth }
3094f99c7881SAdrian Knoth 
snd_hdspm_info_tco_video_input_format(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)3095f99c7881SAdrian Knoth static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
3096f99c7881SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3097f99c7881SAdrian Knoth {
309838816545SAdrian Knoth 	static const char *const texts[] = {"No video", "NTSC", "PAL"};
3099f99c7881SAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3100f99c7881SAdrian Knoth 	return 0;
3101f99c7881SAdrian Knoth }
3102f99c7881SAdrian Knoth 
snd_hdspm_get_tco_video_input_format(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3103f99c7881SAdrian Knoth static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
3104f99c7881SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3105f99c7881SAdrian Knoth {
3106f99c7881SAdrian Knoth 	u32 status;
3107f99c7881SAdrian Knoth 	int ret = 0;
3108f99c7881SAdrian Knoth 
3109f99c7881SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3110f99c7881SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
3111f99c7881SAdrian Knoth 	switch (status & (HDSPM_TCO1_Video_Input_Format_NTSC |
3112f99c7881SAdrian Knoth 			HDSPM_TCO1_Video_Input_Format_PAL)) {
3113f99c7881SAdrian Knoth 	case HDSPM_TCO1_Video_Input_Format_NTSC:
3114f99c7881SAdrian Knoth 		/* ntsc */
3115f99c7881SAdrian Knoth 		ret = 1;
3116f99c7881SAdrian Knoth 		break;
3117f99c7881SAdrian Knoth 	case HDSPM_TCO1_Video_Input_Format_PAL:
3118f99c7881SAdrian Knoth 		/* pal */
3119f99c7881SAdrian Knoth 		ret = 2;
3120f99c7881SAdrian Knoth 		break;
3121f99c7881SAdrian Knoth 	default:
3122f99c7881SAdrian Knoth 		/* no video */
3123f99c7881SAdrian Knoth 		ret = 0;
3124f99c7881SAdrian Knoth 		break;
3125f99c7881SAdrian Knoth 	}
3126f99c7881SAdrian Knoth 	ucontrol->value.enumerated.item[0] = ret;
3127f99c7881SAdrian Knoth 	return 0;
3128f99c7881SAdrian Knoth }
3129f99c7881SAdrian Knoth 
3130f99c7881SAdrian Knoth 
3131f99c7881SAdrian Knoth 
3132f99c7881SAdrian Knoth #define HDSPM_TCO_LTC_FRAMES(xname, xindex) \
3133f99c7881SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3134f99c7881SAdrian Knoth 	.name = xname, \
3135f99c7881SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READ |\
3136f99c7881SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3137f99c7881SAdrian Knoth 	.info = snd_hdspm_info_tco_ltc_frames, \
3138f99c7881SAdrian Knoth 	.get = snd_hdspm_get_tco_ltc_frames, \
3139f99c7881SAdrian Knoth }
3140f99c7881SAdrian Knoth 
snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)3141f99c7881SAdrian Knoth static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
3142f99c7881SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3143f99c7881SAdrian Knoth {
314438816545SAdrian Knoth 	static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
3145f99c7881SAdrian Knoth 				"30 fps"};
3146f99c7881SAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3147f99c7881SAdrian Knoth 	return 0;
3148f99c7881SAdrian Knoth }
3149f99c7881SAdrian Knoth 
hdspm_tco_ltc_frames(struct hdspm * hdspm)3150f99c7881SAdrian Knoth static int hdspm_tco_ltc_frames(struct hdspm *hdspm)
3151f99c7881SAdrian Knoth {
3152f99c7881SAdrian Knoth 	u32 status;
3153f99c7881SAdrian Knoth 	int ret = 0;
3154f99c7881SAdrian Knoth 
3155f99c7881SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
3156f99c7881SAdrian Knoth 	if (status & HDSPM_TCO1_LTC_Input_valid) {
3157f99c7881SAdrian Knoth 		switch (status & (HDSPM_TCO1_LTC_Format_LSB |
3158f99c7881SAdrian Knoth 					HDSPM_TCO1_LTC_Format_MSB)) {
3159f99c7881SAdrian Knoth 		case 0:
3160f99c7881SAdrian Knoth 			/* 24 fps */
31611568b880SAdrian Knoth 			ret = fps_24;
3162f99c7881SAdrian Knoth 			break;
3163f99c7881SAdrian Knoth 		case HDSPM_TCO1_LTC_Format_LSB:
3164f99c7881SAdrian Knoth 			/* 25 fps */
31651568b880SAdrian Knoth 			ret = fps_25;
3166f99c7881SAdrian Knoth 			break;
3167f99c7881SAdrian Knoth 		case HDSPM_TCO1_LTC_Format_MSB:
31681568b880SAdrian Knoth 			/* 29.97 fps */
31691568b880SAdrian Knoth 			ret = fps_2997;
3170f99c7881SAdrian Knoth 			break;
3171f99c7881SAdrian Knoth 		default:
3172f99c7881SAdrian Knoth 			/* 30 fps */
31731568b880SAdrian Knoth 			ret = fps_30;
3174f99c7881SAdrian Knoth 			break;
3175f99c7881SAdrian Knoth 		}
3176f99c7881SAdrian Knoth 	}
3177f99c7881SAdrian Knoth 
3178f99c7881SAdrian Knoth 	return ret;
3179f99c7881SAdrian Knoth }
3180f99c7881SAdrian Knoth 
snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3181f99c7881SAdrian Knoth static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
3182f99c7881SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3183f99c7881SAdrian Knoth {
3184f99c7881SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3185f99c7881SAdrian Knoth 
3186f99c7881SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_tco_ltc_frames(hdspm);
3187f99c7881SAdrian Knoth 	return 0;
3188f99c7881SAdrian Knoth }
3189f99c7881SAdrian Knoth 
3190bf0ff87bSAdrian Knoth #define HDSPM_TOGGLE_SETTING(xname, xindex) \
3191bf0ff87bSAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3192bf0ff87bSAdrian Knoth 	.name = xname, \
3193bf0ff87bSAdrian Knoth 	.private_value = xindex, \
3194bf0ff87bSAdrian Knoth 	.info = snd_hdspm_info_toggle_setting, \
3195bf0ff87bSAdrian Knoth 	.get = snd_hdspm_get_toggle_setting, \
3196bf0ff87bSAdrian Knoth 	.put = snd_hdspm_put_toggle_setting \
3197bf0ff87bSAdrian Knoth }
3198bf0ff87bSAdrian Knoth 
hdspm_toggle_setting(struct hdspm * hdspm,u32 regmask)3199bf0ff87bSAdrian Knoth static int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask)
3200bf0ff87bSAdrian Knoth {
3201ce13f3f3SAdrian Knoth 	u32 reg;
3202ce13f3f3SAdrian Knoth 
3203ce13f3f3SAdrian Knoth 	if (hdspm_is_raydat_or_aio(hdspm))
3204ce13f3f3SAdrian Knoth 		reg = hdspm->settings_register;
3205ce13f3f3SAdrian Knoth 	else
3206ce13f3f3SAdrian Knoth 		reg = hdspm->control_register;
3207ce13f3f3SAdrian Knoth 
3208ce13f3f3SAdrian Knoth 	return (reg & regmask) ? 1 : 0;
3209bf0ff87bSAdrian Knoth }
3210bf0ff87bSAdrian Knoth 
hdspm_set_toggle_setting(struct hdspm * hdspm,u32 regmask,int out)3211bf0ff87bSAdrian Knoth static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out)
3212bf0ff87bSAdrian Knoth {
3213ce13f3f3SAdrian Knoth 	u32 *reg;
3214ce13f3f3SAdrian Knoth 	u32 target_reg;
3215ce13f3f3SAdrian Knoth 
3216ce13f3f3SAdrian Knoth 	if (hdspm_is_raydat_or_aio(hdspm)) {
3217ce13f3f3SAdrian Knoth 		reg = &(hdspm->settings_register);
3218ce13f3f3SAdrian Knoth 		target_reg = HDSPM_WR_SETTINGS;
3219ce13f3f3SAdrian Knoth 	} else {
3220ce13f3f3SAdrian Knoth 		reg = &(hdspm->control_register);
3221ce13f3f3SAdrian Knoth 		target_reg = HDSPM_controlRegister;
3222ce13f3f3SAdrian Knoth 	}
3223ce13f3f3SAdrian Knoth 
3224bf0ff87bSAdrian Knoth 	if (out)
3225ce13f3f3SAdrian Knoth 		*reg |= regmask;
3226bf0ff87bSAdrian Knoth 	else
3227ce13f3f3SAdrian Knoth 		*reg &= ~regmask;
3228ce13f3f3SAdrian Knoth 
3229ce13f3f3SAdrian Knoth 	hdspm_write(hdspm, target_reg, *reg);
3230bf0ff87bSAdrian Knoth 
3231bf0ff87bSAdrian Knoth 	return 0;
3232bf0ff87bSAdrian Knoth }
3233bf0ff87bSAdrian Knoth 
3234bf0ff87bSAdrian Knoth #define snd_hdspm_info_toggle_setting		snd_ctl_boolean_mono_info
3235bf0ff87bSAdrian Knoth 
snd_hdspm_get_toggle_setting(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3236bf0ff87bSAdrian Knoth static int snd_hdspm_get_toggle_setting(struct snd_kcontrol *kcontrol,
3237bf0ff87bSAdrian Knoth 			       struct snd_ctl_elem_value *ucontrol)
3238bf0ff87bSAdrian Knoth {
3239bf0ff87bSAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3240bf0ff87bSAdrian Knoth 	u32 regmask = kcontrol->private_value;
3241bf0ff87bSAdrian Knoth 
3242bf0ff87bSAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3243bf0ff87bSAdrian Knoth 	ucontrol->value.integer.value[0] = hdspm_toggle_setting(hdspm, regmask);
3244bf0ff87bSAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3245bf0ff87bSAdrian Knoth 	return 0;
3246bf0ff87bSAdrian Knoth }
3247bf0ff87bSAdrian Knoth 
snd_hdspm_put_toggle_setting(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3248bf0ff87bSAdrian Knoth static int snd_hdspm_put_toggle_setting(struct snd_kcontrol *kcontrol,
3249bf0ff87bSAdrian Knoth 			       struct snd_ctl_elem_value *ucontrol)
3250bf0ff87bSAdrian Knoth {
3251bf0ff87bSAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3252bf0ff87bSAdrian Knoth 	u32 regmask = kcontrol->private_value;
3253bf0ff87bSAdrian Knoth 	int change;
3254bf0ff87bSAdrian Knoth 	unsigned int val;
3255bf0ff87bSAdrian Knoth 
3256bf0ff87bSAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
3257bf0ff87bSAdrian Knoth 		return -EBUSY;
3258bf0ff87bSAdrian Knoth 	val = ucontrol->value.integer.value[0] & 1;
3259bf0ff87bSAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3260bf0ff87bSAdrian Knoth 	change = (int) val != hdspm_toggle_setting(hdspm, regmask);
3261bf0ff87bSAdrian Knoth 	hdspm_set_toggle_setting(hdspm, regmask, val);
3262bf0ff87bSAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3263bf0ff87bSAdrian Knoth 	return change;
3264bf0ff87bSAdrian Knoth }
3265bf0ff87bSAdrian Knoth 
3266763f356cSTakashi Iwai #define HDSPM_INPUT_SELECT(xname, xindex) \
326767ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3268763f356cSTakashi Iwai 	.name = xname, \
3269763f356cSTakashi Iwai 	.index = xindex, \
3270763f356cSTakashi Iwai 	.info = snd_hdspm_info_input_select, \
3271763f356cSTakashi Iwai 	.get = snd_hdspm_get_input_select, \
3272763f356cSTakashi Iwai 	.put = snd_hdspm_put_input_select \
3273763f356cSTakashi Iwai }
3274763f356cSTakashi Iwai 
hdspm_input_select(struct hdspm * hdspm)327598274f07STakashi Iwai static int hdspm_input_select(struct hdspm * hdspm)
3276763f356cSTakashi Iwai {
3277763f356cSTakashi Iwai 	return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
3278763f356cSTakashi Iwai }
3279763f356cSTakashi Iwai 
hdspm_set_input_select(struct hdspm * hdspm,int out)328098274f07STakashi Iwai static int hdspm_set_input_select(struct hdspm * hdspm, int out)
3281763f356cSTakashi Iwai {
3282763f356cSTakashi Iwai 	if (out)
3283763f356cSTakashi Iwai 		hdspm->control_register |= HDSPM_InputSelect0;
3284763f356cSTakashi Iwai 	else
3285763f356cSTakashi Iwai 		hdspm->control_register &= ~HDSPM_InputSelect0;
3286763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
3287763f356cSTakashi Iwai 
3288763f356cSTakashi Iwai 	return 0;
3289763f356cSTakashi Iwai }
3290763f356cSTakashi Iwai 
snd_hdspm_info_input_select(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)329198274f07STakashi Iwai static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
329298274f07STakashi Iwai 				       struct snd_ctl_elem_info *uinfo)
3293763f356cSTakashi Iwai {
329438816545SAdrian Knoth 	static const char *const texts[] = { "optical", "coaxial" };
3295e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3296763f356cSTakashi Iwai 	return 0;
3297763f356cSTakashi Iwai }
3298763f356cSTakashi Iwai 
snd_hdspm_get_input_select(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)329998274f07STakashi Iwai static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
330098274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3301763f356cSTakashi Iwai {
330298274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3303763f356cSTakashi Iwai 
3304763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3305763f356cSTakashi Iwai 	ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
3306763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3307763f356cSTakashi Iwai 	return 0;
3308763f356cSTakashi Iwai }
3309763f356cSTakashi Iwai 
snd_hdspm_put_input_select(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)331098274f07STakashi Iwai static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
331198274f07STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3312763f356cSTakashi Iwai {
331398274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3314763f356cSTakashi Iwai 	int change;
3315763f356cSTakashi Iwai 	unsigned int val;
3316763f356cSTakashi Iwai 
3317763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3318763f356cSTakashi Iwai 		return -EBUSY;
3319763f356cSTakashi Iwai 	val = ucontrol->value.integer.value[0] & 1;
3320763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3321763f356cSTakashi Iwai 	change = (int) val != hdspm_input_select(hdspm);
3322763f356cSTakashi Iwai 	hdspm_set_input_select(hdspm, val);
3323763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3324763f356cSTakashi Iwai 	return change;
3325763f356cSTakashi Iwai }
3326763f356cSTakashi Iwai 
33270dca1793SAdrian Knoth 
33283cee5a60SRemy Bruno #define HDSPM_DS_WIRE(xname, xindex) \
33293cee5a60SRemy Bruno {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
33303cee5a60SRemy Bruno 	.name = xname, \
33313cee5a60SRemy Bruno 	.index = xindex, \
33323cee5a60SRemy Bruno 	.info = snd_hdspm_info_ds_wire, \
33333cee5a60SRemy Bruno 	.get = snd_hdspm_get_ds_wire, \
33343cee5a60SRemy Bruno 	.put = snd_hdspm_put_ds_wire \
33353cee5a60SRemy Bruno }
33363cee5a60SRemy Bruno 
hdspm_ds_wire(struct hdspm * hdspm)33373cee5a60SRemy Bruno static int hdspm_ds_wire(struct hdspm * hdspm)
33383cee5a60SRemy Bruno {
33393cee5a60SRemy Bruno 	return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
33403cee5a60SRemy Bruno }
33413cee5a60SRemy Bruno 
hdspm_set_ds_wire(struct hdspm * hdspm,int ds)33423cee5a60SRemy Bruno static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
33433cee5a60SRemy Bruno {
33443cee5a60SRemy Bruno 	if (ds)
33453cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_DS_DoubleWire;
33463cee5a60SRemy Bruno 	else
33473cee5a60SRemy Bruno 		hdspm->control_register &= ~HDSPM_DS_DoubleWire;
33483cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
33493cee5a60SRemy Bruno 
33503cee5a60SRemy Bruno 	return 0;
33513cee5a60SRemy Bruno }
33523cee5a60SRemy Bruno 
snd_hdspm_info_ds_wire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)33533cee5a60SRemy Bruno static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
33543cee5a60SRemy Bruno 				  struct snd_ctl_elem_info *uinfo)
33553cee5a60SRemy Bruno {
335638816545SAdrian Knoth 	static const char *const texts[] = { "Single", "Double" };
3357e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
33583cee5a60SRemy Bruno 	return 0;
33593cee5a60SRemy Bruno }
33603cee5a60SRemy Bruno 
snd_hdspm_get_ds_wire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)33613cee5a60SRemy Bruno static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
33623cee5a60SRemy Bruno 				 struct snd_ctl_elem_value *ucontrol)
33633cee5a60SRemy Bruno {
33643cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33653cee5a60SRemy Bruno 
33663cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
33673cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
33683cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
33693cee5a60SRemy Bruno 	return 0;
33703cee5a60SRemy Bruno }
33713cee5a60SRemy Bruno 
snd_hdspm_put_ds_wire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)33723cee5a60SRemy Bruno static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
33733cee5a60SRemy Bruno 				 struct snd_ctl_elem_value *ucontrol)
33743cee5a60SRemy Bruno {
33753cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
33763cee5a60SRemy Bruno 	int change;
33773cee5a60SRemy Bruno 	unsigned int val;
33783cee5a60SRemy Bruno 
33793cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
33803cee5a60SRemy Bruno 		return -EBUSY;
33813cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0] & 1;
33823cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
33833cee5a60SRemy Bruno 	change = (int) val != hdspm_ds_wire(hdspm);
33843cee5a60SRemy Bruno 	hdspm_set_ds_wire(hdspm, val);
33853cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
33863cee5a60SRemy Bruno 	return change;
33873cee5a60SRemy Bruno }
33883cee5a60SRemy Bruno 
33890dca1793SAdrian Knoth 
33903cee5a60SRemy Bruno #define HDSPM_QS_WIRE(xname, xindex) \
33913cee5a60SRemy Bruno {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
33923cee5a60SRemy Bruno 	.name = xname, \
33933cee5a60SRemy Bruno 	.index = xindex, \
33943cee5a60SRemy Bruno 	.info = snd_hdspm_info_qs_wire, \
33953cee5a60SRemy Bruno 	.get = snd_hdspm_get_qs_wire, \
33963cee5a60SRemy Bruno 	.put = snd_hdspm_put_qs_wire \
33973cee5a60SRemy Bruno }
33983cee5a60SRemy Bruno 
hdspm_qs_wire(struct hdspm * hdspm)33993cee5a60SRemy Bruno static int hdspm_qs_wire(struct hdspm * hdspm)
34003cee5a60SRemy Bruno {
34013cee5a60SRemy Bruno 	if (hdspm->control_register & HDSPM_QS_DoubleWire)
34023cee5a60SRemy Bruno 		return 1;
34033cee5a60SRemy Bruno 	if (hdspm->control_register & HDSPM_QS_QuadWire)
34043cee5a60SRemy Bruno 		return 2;
34053cee5a60SRemy Bruno 	return 0;
34063cee5a60SRemy Bruno }
34073cee5a60SRemy Bruno 
hdspm_set_qs_wire(struct hdspm * hdspm,int mode)34083cee5a60SRemy Bruno static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
34093cee5a60SRemy Bruno {
34103cee5a60SRemy Bruno 	hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
34113cee5a60SRemy Bruno 	switch (mode) {
34123cee5a60SRemy Bruno 	case 0:
34133cee5a60SRemy Bruno 		break;
34143cee5a60SRemy Bruno 	case 1:
34153cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_QS_DoubleWire;
34163cee5a60SRemy Bruno 		break;
34173cee5a60SRemy Bruno 	case 2:
34183cee5a60SRemy Bruno 		hdspm->control_register |= HDSPM_QS_QuadWire;
34193cee5a60SRemy Bruno 		break;
34203cee5a60SRemy Bruno 	}
34213cee5a60SRemy Bruno 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
34223cee5a60SRemy Bruno 
34233cee5a60SRemy Bruno 	return 0;
34243cee5a60SRemy Bruno }
34253cee5a60SRemy Bruno 
snd_hdspm_info_qs_wire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)34263cee5a60SRemy Bruno static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
34273cee5a60SRemy Bruno 				       struct snd_ctl_elem_info *uinfo)
34283cee5a60SRemy Bruno {
342938816545SAdrian Knoth 	static const char *const texts[] = { "Single", "Double", "Quad" };
3430e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
34313cee5a60SRemy Bruno 	return 0;
34323cee5a60SRemy Bruno }
34333cee5a60SRemy Bruno 
snd_hdspm_get_qs_wire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)34343cee5a60SRemy Bruno static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
34353cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
34363cee5a60SRemy Bruno {
34373cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
34383cee5a60SRemy Bruno 
34393cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
34403cee5a60SRemy Bruno 	ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
34413cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
34423cee5a60SRemy Bruno 	return 0;
34433cee5a60SRemy Bruno }
34443cee5a60SRemy Bruno 
snd_hdspm_put_qs_wire(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)34453cee5a60SRemy Bruno static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
34463cee5a60SRemy Bruno 				      struct snd_ctl_elem_value *ucontrol)
34473cee5a60SRemy Bruno {
34483cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
34493cee5a60SRemy Bruno 	int change;
34503cee5a60SRemy Bruno 	int val;
34513cee5a60SRemy Bruno 
34523cee5a60SRemy Bruno 	if (!snd_hdspm_use_is_exclusive(hdspm))
34533cee5a60SRemy Bruno 		return -EBUSY;
34543cee5a60SRemy Bruno 	val = ucontrol->value.integer.value[0];
34553cee5a60SRemy Bruno 	if (val < 0)
34563cee5a60SRemy Bruno 		val = 0;
34573cee5a60SRemy Bruno 	if (val > 2)
34583cee5a60SRemy Bruno 		val = 2;
34593cee5a60SRemy Bruno 	spin_lock_irq(&hdspm->lock);
3460ef5fa1a4STakashi Iwai 	change = val != hdspm_qs_wire(hdspm);
34613cee5a60SRemy Bruno 	hdspm_set_qs_wire(hdspm, val);
34623cee5a60SRemy Bruno 	spin_unlock_irq(&hdspm->lock);
34633cee5a60SRemy Bruno 	return change;
34643cee5a60SRemy Bruno }
34653cee5a60SRemy Bruno 
3466acf14767SAdrian Knoth #define HDSPM_CONTROL_TRISTATE(xname, xindex) \
3467acf14767SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3468acf14767SAdrian Knoth 	.name = xname, \
3469acf14767SAdrian Knoth 	.private_value = xindex, \
3470acf14767SAdrian Knoth 	.info = snd_hdspm_info_tristate, \
3471acf14767SAdrian Knoth 	.get = snd_hdspm_get_tristate, \
3472acf14767SAdrian Knoth 	.put = snd_hdspm_put_tristate \
3473acf14767SAdrian Knoth }
3474acf14767SAdrian Knoth 
hdspm_tristate(struct hdspm * hdspm,u32 regmask)3475acf14767SAdrian Knoth static int hdspm_tristate(struct hdspm *hdspm, u32 regmask)
3476acf14767SAdrian Knoth {
3477acf14767SAdrian Knoth 	u32 reg = hdspm->settings_register & (regmask * 3);
3478acf14767SAdrian Knoth 	return reg / regmask;
3479acf14767SAdrian Knoth }
3480acf14767SAdrian Knoth 
hdspm_set_tristate(struct hdspm * hdspm,int mode,u32 regmask)3481acf14767SAdrian Knoth static int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask)
3482acf14767SAdrian Knoth {
3483acf14767SAdrian Knoth 	hdspm->settings_register &= ~(regmask * 3);
3484acf14767SAdrian Knoth 	hdspm->settings_register |= (regmask * mode);
3485acf14767SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
3486acf14767SAdrian Knoth 
3487acf14767SAdrian Knoth 	return 0;
3488acf14767SAdrian Knoth }
3489acf14767SAdrian Knoth 
snd_hdspm_info_tristate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)3490acf14767SAdrian Knoth static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol,
3491acf14767SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3492acf14767SAdrian Knoth {
3493acf14767SAdrian Knoth 	u32 regmask = kcontrol->private_value;
3494acf14767SAdrian Knoth 
349538816545SAdrian Knoth 	static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" };
349638816545SAdrian Knoth 	static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" };
3497acf14767SAdrian Knoth 
3498acf14767SAdrian Knoth 	switch (regmask) {
3499acf14767SAdrian Knoth 	case HDSPM_c0_Input0:
3500acf14767SAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts_spdif);
3501acf14767SAdrian Knoth 		break;
3502acf14767SAdrian Knoth 	default:
3503acf14767SAdrian Knoth 		ENUMERATED_CTL_INFO(uinfo, texts_levels);
3504acf14767SAdrian Knoth 		break;
3505acf14767SAdrian Knoth 	}
3506acf14767SAdrian Knoth 	return 0;
3507acf14767SAdrian Knoth }
3508acf14767SAdrian Knoth 
snd_hdspm_get_tristate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3509acf14767SAdrian Knoth static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol,
3510acf14767SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3511acf14767SAdrian Knoth {
3512acf14767SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3513acf14767SAdrian Knoth 	u32 regmask = kcontrol->private_value;
3514acf14767SAdrian Knoth 
3515acf14767SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3516acf14767SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask);
3517acf14767SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3518acf14767SAdrian Knoth 	return 0;
3519acf14767SAdrian Knoth }
3520acf14767SAdrian Knoth 
snd_hdspm_put_tristate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3521acf14767SAdrian Knoth static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol,
3522acf14767SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3523acf14767SAdrian Knoth {
3524acf14767SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3525acf14767SAdrian Knoth 	u32 regmask = kcontrol->private_value;
3526acf14767SAdrian Knoth 	int change;
3527acf14767SAdrian Knoth 	int val;
3528acf14767SAdrian Knoth 
3529acf14767SAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
3530acf14767SAdrian Knoth 		return -EBUSY;
3531acf14767SAdrian Knoth 	val = ucontrol->value.integer.value[0];
3532acf14767SAdrian Knoth 	if (val < 0)
3533acf14767SAdrian Knoth 		val = 0;
3534acf14767SAdrian Knoth 	if (val > 2)
3535acf14767SAdrian Knoth 		val = 2;
3536acf14767SAdrian Knoth 
3537acf14767SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3538acf14767SAdrian Knoth 	change = val != hdspm_tristate(hdspm, regmask);
3539acf14767SAdrian Knoth 	hdspm_set_tristate(hdspm, val, regmask);
3540acf14767SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3541acf14767SAdrian Knoth 	return change;
3542acf14767SAdrian Knoth }
3543acf14767SAdrian Knoth 
3544700d1ef3SAdrian Knoth #define HDSPM_MADI_SPEEDMODE(xname, xindex) \
3545700d1ef3SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3546700d1ef3SAdrian Knoth 	.name = xname, \
3547700d1ef3SAdrian Knoth 	.index = xindex, \
3548700d1ef3SAdrian Knoth 	.info = snd_hdspm_info_madi_speedmode, \
3549700d1ef3SAdrian Knoth 	.get = snd_hdspm_get_madi_speedmode, \
3550700d1ef3SAdrian Knoth 	.put = snd_hdspm_put_madi_speedmode \
3551700d1ef3SAdrian Knoth }
3552700d1ef3SAdrian Knoth 
hdspm_madi_speedmode(struct hdspm * hdspm)3553700d1ef3SAdrian Knoth static int hdspm_madi_speedmode(struct hdspm *hdspm)
3554700d1ef3SAdrian Knoth {
3555700d1ef3SAdrian Knoth 	if (hdspm->control_register & HDSPM_QuadSpeed)
3556700d1ef3SAdrian Knoth 		return 2;
3557700d1ef3SAdrian Knoth 	if (hdspm->control_register & HDSPM_DoubleSpeed)
3558700d1ef3SAdrian Knoth 		return 1;
3559700d1ef3SAdrian Knoth 	return 0;
3560700d1ef3SAdrian Knoth }
3561700d1ef3SAdrian Knoth 
hdspm_set_madi_speedmode(struct hdspm * hdspm,int mode)3562700d1ef3SAdrian Knoth static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
3563700d1ef3SAdrian Knoth {
3564700d1ef3SAdrian Knoth 	hdspm->control_register &= ~(HDSPM_DoubleSpeed | HDSPM_QuadSpeed);
3565700d1ef3SAdrian Knoth 	switch (mode) {
3566700d1ef3SAdrian Knoth 	case 0:
3567700d1ef3SAdrian Knoth 		break;
3568700d1ef3SAdrian Knoth 	case 1:
3569700d1ef3SAdrian Knoth 		hdspm->control_register |= HDSPM_DoubleSpeed;
3570700d1ef3SAdrian Knoth 		break;
3571700d1ef3SAdrian Knoth 	case 2:
3572700d1ef3SAdrian Knoth 		hdspm->control_register |= HDSPM_QuadSpeed;
3573700d1ef3SAdrian Knoth 		break;
3574700d1ef3SAdrian Knoth 	}
3575700d1ef3SAdrian Knoth 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
3576700d1ef3SAdrian Knoth 
3577700d1ef3SAdrian Knoth 	return 0;
3578700d1ef3SAdrian Knoth }
3579700d1ef3SAdrian Knoth 
snd_hdspm_info_madi_speedmode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)3580700d1ef3SAdrian Knoth static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
3581700d1ef3SAdrian Knoth 				       struct snd_ctl_elem_info *uinfo)
3582700d1ef3SAdrian Knoth {
358338816545SAdrian Knoth 	static const char *const texts[] = { "Single", "Double", "Quad" };
3584e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3585700d1ef3SAdrian Knoth 	return 0;
3586700d1ef3SAdrian Knoth }
3587700d1ef3SAdrian Knoth 
snd_hdspm_get_madi_speedmode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3588700d1ef3SAdrian Knoth static int snd_hdspm_get_madi_speedmode(struct snd_kcontrol *kcontrol,
3589700d1ef3SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3590700d1ef3SAdrian Knoth {
3591700d1ef3SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3592700d1ef3SAdrian Knoth 
3593700d1ef3SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3594700d1ef3SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm_madi_speedmode(hdspm);
3595700d1ef3SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3596700d1ef3SAdrian Knoth 	return 0;
3597700d1ef3SAdrian Knoth }
3598700d1ef3SAdrian Knoth 
snd_hdspm_put_madi_speedmode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3599700d1ef3SAdrian Knoth static int snd_hdspm_put_madi_speedmode(struct snd_kcontrol *kcontrol,
3600700d1ef3SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
3601700d1ef3SAdrian Knoth {
3602700d1ef3SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3603700d1ef3SAdrian Knoth 	int change;
3604700d1ef3SAdrian Knoth 	int val;
3605700d1ef3SAdrian Knoth 
3606700d1ef3SAdrian Knoth 	if (!snd_hdspm_use_is_exclusive(hdspm))
3607700d1ef3SAdrian Knoth 		return -EBUSY;
3608700d1ef3SAdrian Knoth 	val = ucontrol->value.integer.value[0];
3609700d1ef3SAdrian Knoth 	if (val < 0)
3610700d1ef3SAdrian Knoth 		val = 0;
3611700d1ef3SAdrian Knoth 	if (val > 2)
3612700d1ef3SAdrian Knoth 		val = 2;
3613700d1ef3SAdrian Knoth 	spin_lock_irq(&hdspm->lock);
3614700d1ef3SAdrian Knoth 	change = val != hdspm_madi_speedmode(hdspm);
3615700d1ef3SAdrian Knoth 	hdspm_set_madi_speedmode(hdspm, val);
3616700d1ef3SAdrian Knoth 	spin_unlock_irq(&hdspm->lock);
3617700d1ef3SAdrian Knoth 	return change;
3618700d1ef3SAdrian Knoth }
3619763f356cSTakashi Iwai 
3620763f356cSTakashi Iwai #define HDSPM_MIXER(xname, xindex) \
3621763f356cSTakashi Iwai {	.iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
3622763f356cSTakashi Iwai 	.name = xname, \
3623763f356cSTakashi Iwai 	.index = xindex, \
362467ed4161SClemens Ladisch 	.device = 0, \
3625763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
3626763f356cSTakashi Iwai 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3627763f356cSTakashi Iwai 	.info = snd_hdspm_info_mixer, \
3628763f356cSTakashi Iwai 	.get = snd_hdspm_get_mixer, \
3629763f356cSTakashi Iwai 	.put = snd_hdspm_put_mixer \
3630763f356cSTakashi Iwai }
3631763f356cSTakashi Iwai 
snd_hdspm_info_mixer(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)363298274f07STakashi Iwai static int snd_hdspm_info_mixer(struct snd_kcontrol *kcontrol,
363398274f07STakashi Iwai 				struct snd_ctl_elem_info *uinfo)
3634763f356cSTakashi Iwai {
3635763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3636763f356cSTakashi Iwai 	uinfo->count = 3;
3637763f356cSTakashi Iwai 	uinfo->value.integer.min = 0;
3638763f356cSTakashi Iwai 	uinfo->value.integer.max = 65535;
3639763f356cSTakashi Iwai 	uinfo->value.integer.step = 1;
3640763f356cSTakashi Iwai 	return 0;
3641763f356cSTakashi Iwai }
3642763f356cSTakashi Iwai 
snd_hdspm_get_mixer(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)364398274f07STakashi Iwai static int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
364498274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
3645763f356cSTakashi Iwai {
364698274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3647763f356cSTakashi Iwai 	int source;
3648763f356cSTakashi Iwai 	int destination;
3649763f356cSTakashi Iwai 
3650763f356cSTakashi Iwai 	source = ucontrol->value.integer.value[0];
3651763f356cSTakashi Iwai 	if (source < 0)
3652763f356cSTakashi Iwai 		source = 0;
3653763f356cSTakashi Iwai 	else if (source >= 2 * HDSPM_MAX_CHANNELS)
3654763f356cSTakashi Iwai 		source = 2 * HDSPM_MAX_CHANNELS - 1;
3655763f356cSTakashi Iwai 
3656763f356cSTakashi Iwai 	destination = ucontrol->value.integer.value[1];
3657763f356cSTakashi Iwai 	if (destination < 0)
3658763f356cSTakashi Iwai 		destination = 0;
3659763f356cSTakashi Iwai 	else if (destination >= HDSPM_MAX_CHANNELS)
3660763f356cSTakashi Iwai 		destination = HDSPM_MAX_CHANNELS - 1;
3661763f356cSTakashi Iwai 
3662763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3663763f356cSTakashi Iwai 	if (source >= HDSPM_MAX_CHANNELS)
3664763f356cSTakashi Iwai 		ucontrol->value.integer.value[2] =
3665763f356cSTakashi Iwai 		    hdspm_read_pb_gain(hdspm, destination,
3666763f356cSTakashi Iwai 				       source - HDSPM_MAX_CHANNELS);
3667763f356cSTakashi Iwai 	else
3668763f356cSTakashi Iwai 		ucontrol->value.integer.value[2] =
3669763f356cSTakashi Iwai 		    hdspm_read_in_gain(hdspm, destination, source);
3670763f356cSTakashi Iwai 
3671763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3672763f356cSTakashi Iwai 
3673763f356cSTakashi Iwai 	return 0;
3674763f356cSTakashi Iwai }
3675763f356cSTakashi Iwai 
snd_hdspm_put_mixer(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)367698274f07STakashi Iwai static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
367798274f07STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
3678763f356cSTakashi Iwai {
367998274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3680763f356cSTakashi Iwai 	int change;
3681763f356cSTakashi Iwai 	int source;
3682763f356cSTakashi Iwai 	int destination;
3683763f356cSTakashi Iwai 	int gain;
3684763f356cSTakashi Iwai 
3685763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3686763f356cSTakashi Iwai 		return -EBUSY;
3687763f356cSTakashi Iwai 
3688763f356cSTakashi Iwai 	source = ucontrol->value.integer.value[0];
3689763f356cSTakashi Iwai 	destination = ucontrol->value.integer.value[1];
3690763f356cSTakashi Iwai 
3691763f356cSTakashi Iwai 	if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
3692763f356cSTakashi Iwai 		return -1;
3693763f356cSTakashi Iwai 	if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
3694763f356cSTakashi Iwai 		return -1;
3695763f356cSTakashi Iwai 
3696763f356cSTakashi Iwai 	gain = ucontrol->value.integer.value[2];
3697763f356cSTakashi Iwai 
3698763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3699763f356cSTakashi Iwai 
3700763f356cSTakashi Iwai 	if (source >= HDSPM_MAX_CHANNELS)
3701763f356cSTakashi Iwai 		change = gain != hdspm_read_pb_gain(hdspm, destination,
3702763f356cSTakashi Iwai 						    source -
3703763f356cSTakashi Iwai 						    HDSPM_MAX_CHANNELS);
3704763f356cSTakashi Iwai 	else
3705ef5fa1a4STakashi Iwai 		change = gain != hdspm_read_in_gain(hdspm, destination,
3706ef5fa1a4STakashi Iwai 						    source);
3707763f356cSTakashi Iwai 
3708763f356cSTakashi Iwai 	if (change) {
3709763f356cSTakashi Iwai 		if (source >= HDSPM_MAX_CHANNELS)
3710763f356cSTakashi Iwai 			hdspm_write_pb_gain(hdspm, destination,
3711763f356cSTakashi Iwai 					    source - HDSPM_MAX_CHANNELS,
3712763f356cSTakashi Iwai 					    gain);
3713763f356cSTakashi Iwai 		else
3714763f356cSTakashi Iwai 			hdspm_write_in_gain(hdspm, destination, source,
3715763f356cSTakashi Iwai 					    gain);
3716763f356cSTakashi Iwai 	}
3717763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3718763f356cSTakashi Iwai 
3719763f356cSTakashi Iwai 	return change;
3720763f356cSTakashi Iwai }
3721763f356cSTakashi Iwai 
3722763f356cSTakashi Iwai /* The simple mixer control(s) provide gain control for the
3723763f356cSTakashi Iwai    basic 1:1 mappings of playback streams to output
3724763f356cSTakashi Iwai    streams.
3725763f356cSTakashi Iwai */
3726763f356cSTakashi Iwai 
3727763f356cSTakashi Iwai #define HDSPM_PLAYBACK_MIXER \
3728763f356cSTakashi Iwai {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3729763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
3730763f356cSTakashi Iwai 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3731763f356cSTakashi Iwai 	.info = snd_hdspm_info_playback_mixer, \
3732763f356cSTakashi Iwai 	.get = snd_hdspm_get_playback_mixer, \
3733763f356cSTakashi Iwai 	.put = snd_hdspm_put_playback_mixer \
3734763f356cSTakashi Iwai }
3735763f356cSTakashi Iwai 
snd_hdspm_info_playback_mixer(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)373698274f07STakashi Iwai static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
373798274f07STakashi Iwai 					 struct snd_ctl_elem_info *uinfo)
3738763f356cSTakashi Iwai {
3739763f356cSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3740763f356cSTakashi Iwai 	uinfo->count = 1;
3741763f356cSTakashi Iwai 	uinfo->value.integer.min = 0;
37420dca1793SAdrian Knoth 	uinfo->value.integer.max = 64;
3743763f356cSTakashi Iwai 	uinfo->value.integer.step = 1;
3744763f356cSTakashi Iwai 	return 0;
3745763f356cSTakashi Iwai }
3746763f356cSTakashi Iwai 
snd_hdspm_get_playback_mixer(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)374798274f07STakashi Iwai static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
374898274f07STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
3749763f356cSTakashi Iwai {
375098274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3751763f356cSTakashi Iwai 	int channel;
3752763f356cSTakashi Iwai 
3753763f356cSTakashi Iwai 	channel = ucontrol->id.index - 1;
3754763f356cSTakashi Iwai 
3755da3cec35STakashi Iwai 	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
3756da3cec35STakashi Iwai 		return -EINVAL;
3757763f356cSTakashi Iwai 
3758763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3759763f356cSTakashi Iwai 	ucontrol->value.integer.value[0] =
37600dca1793SAdrian Knoth 	  (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
3761763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3762763f356cSTakashi Iwai 
3763763f356cSTakashi Iwai 	return 0;
3764763f356cSTakashi Iwai }
3765763f356cSTakashi Iwai 
snd_hdspm_put_playback_mixer(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)376698274f07STakashi Iwai static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
376798274f07STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
3768763f356cSTakashi Iwai {
376998274f07STakashi Iwai 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
3770763f356cSTakashi Iwai 	int change;
3771763f356cSTakashi Iwai 	int channel;
3772763f356cSTakashi Iwai 	int gain;
3773763f356cSTakashi Iwai 
3774763f356cSTakashi Iwai 	if (!snd_hdspm_use_is_exclusive(hdspm))
3775763f356cSTakashi Iwai 		return -EBUSY;
3776763f356cSTakashi Iwai 
3777763f356cSTakashi Iwai 	channel = ucontrol->id.index - 1;
3778763f356cSTakashi Iwai 
3779da3cec35STakashi Iwai 	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
3780da3cec35STakashi Iwai 		return -EINVAL;
3781763f356cSTakashi Iwai 
37820dca1793SAdrian Knoth 	gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
3783763f356cSTakashi Iwai 
3784763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
3785763f356cSTakashi Iwai 	change =
37860dca1793SAdrian Knoth 	    gain != hdspm_read_pb_gain(hdspm, channel,
37870dca1793SAdrian Knoth 				       channel);
3788763f356cSTakashi Iwai 	if (change)
37890dca1793SAdrian Knoth 		hdspm_write_pb_gain(hdspm, channel, channel,
3790763f356cSTakashi Iwai 				    gain);
3791763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
3792763f356cSTakashi Iwai 	return change;
3793763f356cSTakashi Iwai }
3794763f356cSTakashi Iwai 
37950dca1793SAdrian Knoth #define HDSPM_SYNC_CHECK(xname, xindex) \
379667ed4161SClemens Ladisch {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3797763f356cSTakashi Iwai 	.name = xname, \
37980dca1793SAdrian Knoth 	.private_value = xindex, \
3799763f356cSTakashi Iwai 	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
3800763f356cSTakashi Iwai 	.info = snd_hdspm_info_sync_check, \
38010dca1793SAdrian Knoth 	.get = snd_hdspm_get_sync_check \
3802763f356cSTakashi Iwai }
3803763f356cSTakashi Iwai 
380434542213SAdrian Knoth #define HDSPM_TCO_LOCK_CHECK(xname, xindex) \
380534542213SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
380634542213SAdrian Knoth 	.name = xname, \
380734542213SAdrian Knoth 	.private_value = xindex, \
380834542213SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
380934542213SAdrian Knoth 	.info = snd_hdspm_tco_info_lock_check, \
381034542213SAdrian Knoth 	.get = snd_hdspm_get_sync_check \
381134542213SAdrian Knoth }
381234542213SAdrian Knoth 
381334542213SAdrian Knoth 
38140dca1793SAdrian Knoth 
snd_hdspm_info_sync_check(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)381598274f07STakashi Iwai static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
381698274f07STakashi Iwai 				     struct snd_ctl_elem_info *uinfo)
3817763f356cSTakashi Iwai {
381838816545SAdrian Knoth 	static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" };
3819e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
3820763f356cSTakashi Iwai 	return 0;
3821763f356cSTakashi Iwai }
3822763f356cSTakashi Iwai 
snd_hdspm_tco_info_lock_check(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)382334542213SAdrian Knoth static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
382434542213SAdrian Knoth 				     struct snd_ctl_elem_info *uinfo)
382534542213SAdrian Knoth {
382638816545SAdrian Knoth 	static const char *const texts[] = { "No Lock", "Lock" };
382734542213SAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
382834542213SAdrian Knoth 	return 0;
382934542213SAdrian Knoth }
383034542213SAdrian Knoth 
hdspm_wc_sync_check(struct hdspm * hdspm)383198274f07STakashi Iwai static int hdspm_wc_sync_check(struct hdspm *hdspm)
3832763f356cSTakashi Iwai {
38330dca1793SAdrian Knoth 	int status, status2;
38340dca1793SAdrian Knoth 
38350dca1793SAdrian Knoth 	switch (hdspm->io_type) {
38360dca1793SAdrian Knoth 	case AES32:
38370dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
383856bde0f3SAndre Schramm 		if (status & HDSPM_AES32_wcLock) {
383956bde0f3SAndre Schramm 			if (status & HDSPM_AES32_wcSync)
38403cee5a60SRemy Bruno 				return 2;
384156bde0f3SAndre Schramm 			else
38420dca1793SAdrian Knoth 				return 1;
384356bde0f3SAndre Schramm 		}
38443cee5a60SRemy Bruno 		return 0;
38450dca1793SAdrian Knoth 
38460dca1793SAdrian Knoth 	case MADI:
38470dca1793SAdrian Knoth 		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
3848763f356cSTakashi Iwai 		if (status2 & HDSPM_wcLock) {
3849763f356cSTakashi Iwai 			if (status2 & HDSPM_wcSync)
3850763f356cSTakashi Iwai 				return 2;
3851763f356cSTakashi Iwai 			else
3852763f356cSTakashi Iwai 				return 1;
3853763f356cSTakashi Iwai 		}
3854763f356cSTakashi Iwai 		return 0;
3855763f356cSTakashi Iwai 
38560dca1793SAdrian Knoth 	case RayDAT:
38570dca1793SAdrian Knoth 	case AIO:
38580dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
3859763f356cSTakashi Iwai 
38600dca1793SAdrian Knoth 		if (status & 0x2000000)
38610dca1793SAdrian Knoth 			return 2;
38620dca1793SAdrian Knoth 		else if (status & 0x1000000)
38630dca1793SAdrian Knoth 			return 1;
3864763f356cSTakashi Iwai 		return 0;
38650dca1793SAdrian Knoth 
38660dca1793SAdrian Knoth 	case MADIface:
38670dca1793SAdrian Knoth 		break;
3868763f356cSTakashi Iwai 	}
3869763f356cSTakashi Iwai 
3870763f356cSTakashi Iwai 
38710dca1793SAdrian Knoth 	return 3;
3872763f356cSTakashi Iwai }
3873763f356cSTakashi Iwai 
38740dca1793SAdrian Knoth 
hdspm_madi_sync_check(struct hdspm * hdspm)38750dca1793SAdrian Knoth static int hdspm_madi_sync_check(struct hdspm *hdspm)
3876763f356cSTakashi Iwai {
3877763f356cSTakashi Iwai 	int status = hdspm_read(hdspm, HDSPM_statusRegister);
3878763f356cSTakashi Iwai 	if (status & HDSPM_madiLock) {
3879763f356cSTakashi Iwai 		if (status & HDSPM_madiSync)
3880763f356cSTakashi Iwai 			return 2;
3881763f356cSTakashi Iwai 		else
3882763f356cSTakashi Iwai 			return 1;
3883763f356cSTakashi Iwai 	}
3884763f356cSTakashi Iwai 	return 0;
3885763f356cSTakashi Iwai }
3886763f356cSTakashi Iwai 
3887763f356cSTakashi Iwai 
hdspm_s1_sync_check(struct hdspm * hdspm,int idx)38880dca1793SAdrian Knoth static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx)
38890dca1793SAdrian Knoth {
38900dca1793SAdrian Knoth 	int status, lock, sync;
38910dca1793SAdrian Knoth 
38920dca1793SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
38930dca1793SAdrian Knoth 
38940dca1793SAdrian Knoth 	lock = (status & (0x1<<idx)) ? 1 : 0;
38950dca1793SAdrian Knoth 	sync = (status & (0x100<<idx)) ? 1 : 0;
38960dca1793SAdrian Knoth 
38970dca1793SAdrian Knoth 	if (lock && sync)
38980dca1793SAdrian Knoth 		return 2;
38990dca1793SAdrian Knoth 	else if (lock)
39000dca1793SAdrian Knoth 		return 1;
3901763f356cSTakashi Iwai 	return 0;
3902763f356cSTakashi Iwai }
3903763f356cSTakashi Iwai 
3904763f356cSTakashi Iwai 
hdspm_sync_in_sync_check(struct hdspm * hdspm)39050dca1793SAdrian Knoth static int hdspm_sync_in_sync_check(struct hdspm *hdspm)
39060dca1793SAdrian Knoth {
39070dca1793SAdrian Knoth 	int status, lock = 0, sync = 0;
39080dca1793SAdrian Knoth 
39090dca1793SAdrian Knoth 	switch (hdspm->io_type) {
39100dca1793SAdrian Knoth 	case RayDAT:
39110dca1793SAdrian Knoth 	case AIO:
39120dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_RD_STATUS_3);
39130dca1793SAdrian Knoth 		lock = (status & 0x400) ? 1 : 0;
39140dca1793SAdrian Knoth 		sync = (status & 0x800) ? 1 : 0;
39150dca1793SAdrian Knoth 		break;
39160dca1793SAdrian Knoth 
39170dca1793SAdrian Knoth 	case MADI:
39182e0452f5SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister);
39192e0452f5SAdrian Knoth 		lock = (status & HDSPM_syncInLock) ? 1 : 0;
39202e0452f5SAdrian Knoth 		sync = (status & HDSPM_syncInSync) ? 1 : 0;
39212e0452f5SAdrian Knoth 		break;
39222e0452f5SAdrian Knoth 
39230dca1793SAdrian Knoth 	case AES32:
39240dca1793SAdrian Knoth 		status = hdspm_read(hdspm, HDSPM_statusRegister2);
39259a215f47SAdrian Knoth 		lock = (status & 0x100000) ? 1 : 0;
39269a215f47SAdrian Knoth 		sync = (status & 0x200000) ? 1 : 0;
39270dca1793SAdrian Knoth 		break;
39280dca1793SAdrian Knoth 
39290dca1793SAdrian Knoth 	case MADIface:
39300dca1793SAdrian Knoth 		break;
39310dca1793SAdrian Knoth 	}
39320dca1793SAdrian Knoth 
39330dca1793SAdrian Knoth 	if (lock && sync)
39340dca1793SAdrian Knoth 		return 2;
39350dca1793SAdrian Knoth 	else if (lock)
39360dca1793SAdrian Knoth 		return 1;
39370dca1793SAdrian Knoth 
39380dca1793SAdrian Knoth 	return 0;
39393cee5a60SRemy Bruno }
39403cee5a60SRemy Bruno 
hdspm_aes_sync_check(struct hdspm * hdspm,int idx)39413cee5a60SRemy Bruno static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx)
39423cee5a60SRemy Bruno {
39430dca1793SAdrian Knoth 	int status2, lock, sync;
39440dca1793SAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
39450dca1793SAdrian Knoth 
39460dca1793SAdrian Knoth 	lock = (status2 & (0x0080 >> idx)) ? 1 : 0;
39470dca1793SAdrian Knoth 	sync = (status2 & (0x8000 >> idx)) ? 1 : 0;
39480dca1793SAdrian Knoth 
39490dca1793SAdrian Knoth 	if (sync)
39503cee5a60SRemy Bruno 		return 2;
39510dca1793SAdrian Knoth 	else if (lock)
39520dca1793SAdrian Knoth 		return 1;
39533cee5a60SRemy Bruno 	return 0;
39543cee5a60SRemy Bruno }
39553cee5a60SRemy Bruno 
hdspm_tco_input_check(struct hdspm * hdspm,u32 mask)395634542213SAdrian Knoth static int hdspm_tco_input_check(struct hdspm *hdspm, u32 mask)
395734542213SAdrian Knoth {
395834542213SAdrian Knoth 	u32 status;
395934542213SAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
396034542213SAdrian Knoth 
396134542213SAdrian Knoth 	return (status & mask) ? 1 : 0;
396234542213SAdrian Knoth }
396334542213SAdrian Knoth 
39640dca1793SAdrian Knoth 
hdspm_tco_sync_check(struct hdspm * hdspm)39650dca1793SAdrian Knoth static int hdspm_tco_sync_check(struct hdspm *hdspm)
39660dca1793SAdrian Knoth {
39670dca1793SAdrian Knoth 	int status;
39680dca1793SAdrian Knoth 
39690dca1793SAdrian Knoth 	if (hdspm->tco) {
39700dca1793SAdrian Knoth 		switch (hdspm->io_type) {
39710dca1793SAdrian Knoth 		case MADI:
3972b0bf5504SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_statusRegister);
3973b0bf5504SAdrian Knoth 			if (status & HDSPM_tcoLockMadi) {
3974b0bf5504SAdrian Knoth 				if (status & HDSPM_tcoSync)
3975b0bf5504SAdrian Knoth 					return 2;
3976b0bf5504SAdrian Knoth 				else
3977b0bf5504SAdrian Knoth 					return 1;
3978b0bf5504SAdrian Knoth 			}
3979b0bf5504SAdrian Knoth 			return 0;
39800dca1793SAdrian Knoth 		case AES32:
39810dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_statusRegister);
3982b0bf5504SAdrian Knoth 			if (status & HDSPM_tcoLockAes) {
39830dca1793SAdrian Knoth 				if (status & HDSPM_tcoSync)
39840dca1793SAdrian Knoth 					return 2;
39850dca1793SAdrian Knoth 				else
39860dca1793SAdrian Knoth 					return 1;
39870dca1793SAdrian Knoth 			}
39880dca1793SAdrian Knoth 			return 0;
39890dca1793SAdrian Knoth 		case RayDAT:
39900dca1793SAdrian Knoth 		case AIO:
39910dca1793SAdrian Knoth 			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
39920dca1793SAdrian Knoth 
39930dca1793SAdrian Knoth 			if (status & 0x8000000)
39940dca1793SAdrian Knoth 				return 2; /* Sync */
39950dca1793SAdrian Knoth 			if (status & 0x4000000)
39960dca1793SAdrian Knoth 				return 1; /* Lock */
39970dca1793SAdrian Knoth 			return 0; /* No signal */
39980dca1793SAdrian Knoth 
39990dca1793SAdrian Knoth 		default:
40000dca1793SAdrian Knoth 			break;
40010dca1793SAdrian Knoth 		}
40020dca1793SAdrian Knoth 	}
40030dca1793SAdrian Knoth 
40040dca1793SAdrian Knoth 	return 3; /* N/A */
40050dca1793SAdrian Knoth }
40060dca1793SAdrian Knoth 
40070dca1793SAdrian Knoth 
snd_hdspm_get_sync_check(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)40080dca1793SAdrian Knoth static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
40093cee5a60SRemy Bruno 				    struct snd_ctl_elem_value *ucontrol)
40103cee5a60SRemy Bruno {
40113cee5a60SRemy Bruno 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
40120dca1793SAdrian Knoth 	int val = -1;
40133cee5a60SRemy Bruno 
40140dca1793SAdrian Knoth 	switch (hdspm->io_type) {
40150dca1793SAdrian Knoth 	case RayDAT:
40160dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40170dca1793SAdrian Knoth 		case 0: /* WC */
40180dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40190dca1793SAdrian Knoth 		case 7: /* TCO */
40200dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40210dca1793SAdrian Knoth 		case 8: /* SYNC IN */
40220dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40230dca1793SAdrian Knoth 		default:
4024d1a3c98dSAdrian Knoth 			val = hdspm_s1_sync_check(hdspm,
4025d1a3c98dSAdrian Knoth 					kcontrol->private_value-1);
40260dca1793SAdrian Knoth 		}
4027fba30fd3SAdrian Knoth 		break;
40283cee5a60SRemy Bruno 
40290dca1793SAdrian Knoth 	case AIO:
40300dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40310dca1793SAdrian Knoth 		case 0: /* WC */
40320dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40330dca1793SAdrian Knoth 		case 4: /* TCO */
40340dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40350dca1793SAdrian Knoth 		case 5: /* SYNC IN */
40360dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40370dca1793SAdrian Knoth 		default:
40381cb7dbf4SAdrian Knoth 			val = hdspm_s1_sync_check(hdspm,
40391cb7dbf4SAdrian Knoth 					kcontrol->private_value-1);
40400dca1793SAdrian Knoth 		}
4041fba30fd3SAdrian Knoth 		break;
40420dca1793SAdrian Knoth 
40430dca1793SAdrian Knoth 	case MADI:
40440dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40450dca1793SAdrian Knoth 		case 0: /* WC */
40460dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40470dca1793SAdrian Knoth 		case 1: /* MADI */
40480dca1793SAdrian Knoth 			val = hdspm_madi_sync_check(hdspm); break;
40490dca1793SAdrian Knoth 		case 2: /* TCO */
40500dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40510dca1793SAdrian Knoth 		case 3: /* SYNC_IN */
40520dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40530dca1793SAdrian Knoth 		}
4054fba30fd3SAdrian Knoth 		break;
40550dca1793SAdrian Knoth 
40560dca1793SAdrian Knoth 	case MADIface:
40570dca1793SAdrian Knoth 		val = hdspm_madi_sync_check(hdspm); /* MADI */
40580dca1793SAdrian Knoth 		break;
40590dca1793SAdrian Knoth 
40600dca1793SAdrian Knoth 	case AES32:
40610dca1793SAdrian Knoth 		switch (kcontrol->private_value) {
40620dca1793SAdrian Knoth 		case 0: /* WC */
40630dca1793SAdrian Knoth 			val = hdspm_wc_sync_check(hdspm); break;
40640dca1793SAdrian Knoth 		case 9: /* TCO */
40650dca1793SAdrian Knoth 			val = hdspm_tco_sync_check(hdspm); break;
40660dca1793SAdrian Knoth 		case 10 /* SYNC IN */:
40670dca1793SAdrian Knoth 			val = hdspm_sync_in_sync_check(hdspm); break;
40687c4a95b5SAdrian Knoth 		default: /* AES1 to AES8 */
40690dca1793SAdrian Knoth 			 val = hdspm_aes_sync_check(hdspm,
40707c4a95b5SAdrian Knoth 					 kcontrol->private_value-1);
40710dca1793SAdrian Knoth 		}
4072fba30fd3SAdrian Knoth 		break;
40730dca1793SAdrian Knoth 
40740dca1793SAdrian Knoth 	}
40750dca1793SAdrian Knoth 
407634542213SAdrian Knoth 	if (hdspm->tco) {
407734542213SAdrian Knoth 		switch (kcontrol->private_value) {
407834542213SAdrian Knoth 		case 11:
407934542213SAdrian Knoth 			/* Check TCO for lock state of its current input */
408034542213SAdrian Knoth 			val = hdspm_tco_input_check(hdspm, HDSPM_TCO1_TCO_lock);
408134542213SAdrian Knoth 			break;
408234542213SAdrian Knoth 		case 12:
408334542213SAdrian Knoth 			/* Check TCO for valid time code on LTC input. */
408434542213SAdrian Knoth 			val = hdspm_tco_input_check(hdspm,
408534542213SAdrian Knoth 				HDSPM_TCO1_LTC_Input_valid);
408634542213SAdrian Knoth 			break;
408734542213SAdrian Knoth 		default:
408834542213SAdrian Knoth 			break;
408934542213SAdrian Knoth 		}
409034542213SAdrian Knoth 	}
409134542213SAdrian Knoth 
40920dca1793SAdrian Knoth 	if (-1 == val)
40930dca1793SAdrian Knoth 		val = 3;
40940dca1793SAdrian Knoth 
40950dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = val;
40963cee5a60SRemy Bruno 	return 0;
40973cee5a60SRemy Bruno }
4098763f356cSTakashi Iwai 
4099763f356cSTakashi Iwai 
41000dca1793SAdrian Knoth 
4101ddcecf6bSTakashi Iwai /*
41020dca1793SAdrian Knoth  * TCO controls
4103ddcecf6bSTakashi Iwai  */
hdspm_tco_write(struct hdspm * hdspm)41040dca1793SAdrian Knoth static void hdspm_tco_write(struct hdspm *hdspm)
41050dca1793SAdrian Knoth {
41060dca1793SAdrian Knoth 	unsigned int tc[4] = { 0, 0, 0, 0};
41070dca1793SAdrian Knoth 
41080dca1793SAdrian Knoth 	switch (hdspm->tco->input) {
41090dca1793SAdrian Knoth 	case 0:
41100dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_input_MSB;
41110dca1793SAdrian Knoth 		break;
41120dca1793SAdrian Knoth 	case 1:
41130dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_input_LSB;
41140dca1793SAdrian Knoth 		break;
41150dca1793SAdrian Knoth 	default:
41160dca1793SAdrian Knoth 		break;
41170dca1793SAdrian Knoth 	}
41180dca1793SAdrian Knoth 
41190dca1793SAdrian Knoth 	switch (hdspm->tco->framerate) {
41200dca1793SAdrian Knoth 	case 1:
41210dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB;
41220dca1793SAdrian Knoth 		break;
41230dca1793SAdrian Knoth 	case 2:
41240dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_MSB;
41250dca1793SAdrian Knoth 		break;
41260dca1793SAdrian Knoth 	case 3:
41270dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_MSB +
41280dca1793SAdrian Knoth 			HDSPM_TCO1_set_drop_frame_flag;
41290dca1793SAdrian Knoth 		break;
41300dca1793SAdrian Knoth 	case 4:
41310dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
41320dca1793SAdrian Knoth 			HDSPM_TCO1_LTC_Format_MSB;
41330dca1793SAdrian Knoth 		break;
41340dca1793SAdrian Knoth 	case 5:
41350dca1793SAdrian Knoth 		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
41360dca1793SAdrian Knoth 			HDSPM_TCO1_LTC_Format_MSB +
41370dca1793SAdrian Knoth 			HDSPM_TCO1_set_drop_frame_flag;
41380dca1793SAdrian Knoth 		break;
41390dca1793SAdrian Knoth 	default:
41400dca1793SAdrian Knoth 		break;
41410dca1793SAdrian Knoth 	}
41420dca1793SAdrian Knoth 
41430dca1793SAdrian Knoth 	switch (hdspm->tco->wordclock) {
41440dca1793SAdrian Knoth 	case 1:
41450dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB;
41460dca1793SAdrian Knoth 		break;
41470dca1793SAdrian Knoth 	case 2:
41480dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB;
41490dca1793SAdrian Knoth 		break;
41500dca1793SAdrian Knoth 	default:
41510dca1793SAdrian Knoth 		break;
41520dca1793SAdrian Knoth 	}
41530dca1793SAdrian Knoth 
41540dca1793SAdrian Knoth 	switch (hdspm->tco->samplerate) {
41550dca1793SAdrian Knoth 	case 1:
41560dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_freq;
41570dca1793SAdrian Knoth 		break;
41580dca1793SAdrian Knoth 	case 2:
41590dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_freq_from_app;
41600dca1793SAdrian Knoth 		break;
41610dca1793SAdrian Knoth 	default:
41620dca1793SAdrian Knoth 		break;
41630dca1793SAdrian Knoth 	}
41640dca1793SAdrian Knoth 
41650dca1793SAdrian Knoth 	switch (hdspm->tco->pull) {
41660dca1793SAdrian Knoth 	case 1:
41670dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_up;
41680dca1793SAdrian Knoth 		break;
41690dca1793SAdrian Knoth 	case 2:
41700dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_down;
41710dca1793SAdrian Knoth 		break;
41720dca1793SAdrian Knoth 	case 3:
41730dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4;
41740dca1793SAdrian Knoth 		break;
41750dca1793SAdrian Knoth 	case 4:
41760dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4;
41770dca1793SAdrian Knoth 		break;
41780dca1793SAdrian Knoth 	default:
41790dca1793SAdrian Knoth 		break;
41800dca1793SAdrian Knoth 	}
41810dca1793SAdrian Knoth 
41820dca1793SAdrian Knoth 	if (1 == hdspm->tco->term) {
41830dca1793SAdrian Knoth 		tc[2] |= HDSPM_TCO2_set_term_75R;
41840dca1793SAdrian Knoth 	}
41850dca1793SAdrian Knoth 
41860dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]);
41870dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]);
41880dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]);
41890dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]);
41900dca1793SAdrian Knoth }
41910dca1793SAdrian Knoth 
41920dca1793SAdrian Knoth 
41930dca1793SAdrian Knoth #define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \
41940dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
41950dca1793SAdrian Knoth 	.name = xname, \
41960dca1793SAdrian Knoth 	.index = xindex, \
41970dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
41980dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
41990dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_sample_rate, \
42000dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_sample_rate, \
42010dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_sample_rate \
42020dca1793SAdrian Knoth }
42030dca1793SAdrian Knoth 
snd_hdspm_info_tco_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)42040dca1793SAdrian Knoth static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
42050dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
42060dca1793SAdrian Knoth {
420769358fcaSMartin Dausel 	/* TODO freq from app could be supported here, see tco->samplerate */
420838816545SAdrian Knoth 	static const char *const texts[] = { "44.1 kHz", "48 kHz" };
4209e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
42100dca1793SAdrian Knoth 	return 0;
42110dca1793SAdrian Knoth }
42120dca1793SAdrian Knoth 
snd_hdspm_get_tco_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)42130dca1793SAdrian Knoth static int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol,
42140dca1793SAdrian Knoth 				      struct snd_ctl_elem_value *ucontrol)
42150dca1793SAdrian Knoth {
42160dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42170dca1793SAdrian Knoth 
42180dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate;
42190dca1793SAdrian Knoth 
42200dca1793SAdrian Knoth 	return 0;
42210dca1793SAdrian Knoth }
42220dca1793SAdrian Knoth 
snd_hdspm_put_tco_sample_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)42230dca1793SAdrian Knoth static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
42240dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
42250dca1793SAdrian Knoth {
42260dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42270dca1793SAdrian Knoth 
42280dca1793SAdrian Knoth 	if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) {
42290dca1793SAdrian Knoth 		hdspm->tco->samplerate = ucontrol->value.enumerated.item[0];
42300dca1793SAdrian Knoth 
42310dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
42320dca1793SAdrian Knoth 
42330dca1793SAdrian Knoth 		return 1;
42340dca1793SAdrian Knoth 	}
42350dca1793SAdrian Knoth 
42360dca1793SAdrian Knoth 	return 0;
42370dca1793SAdrian Knoth }
42380dca1793SAdrian Knoth 
42390dca1793SAdrian Knoth 
42400dca1793SAdrian Knoth #define HDSPM_TCO_PULL(xname, xindex) \
42410dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
42420dca1793SAdrian Knoth 	.name = xname, \
42430dca1793SAdrian Knoth 	.index = xindex, \
42440dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
42450dca1793SAdrian Knoth 		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
42460dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_pull, \
42470dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_pull, \
42480dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_pull \
42490dca1793SAdrian Knoth }
42500dca1793SAdrian Knoth 
snd_hdspm_info_tco_pull(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)42510dca1793SAdrian Knoth static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
42520dca1793SAdrian Knoth 				   struct snd_ctl_elem_info *uinfo)
42530dca1793SAdrian Knoth {
425438816545SAdrian Knoth 	static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %",
425538816545SAdrian Knoth 		"+ 4 %", "- 4 %" };
4256e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
42570dca1793SAdrian Knoth 	return 0;
42580dca1793SAdrian Knoth }
42590dca1793SAdrian Knoth 
snd_hdspm_get_tco_pull(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)42600dca1793SAdrian Knoth static int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol,
42610dca1793SAdrian Knoth 				  struct snd_ctl_elem_value *ucontrol)
42620dca1793SAdrian Knoth {
42630dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42640dca1793SAdrian Knoth 
42650dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->pull;
42660dca1793SAdrian Knoth 
42670dca1793SAdrian Knoth 	return 0;
42680dca1793SAdrian Knoth }
42690dca1793SAdrian Knoth 
snd_hdspm_put_tco_pull(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)42700dca1793SAdrian Knoth static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
42710dca1793SAdrian Knoth 				  struct snd_ctl_elem_value *ucontrol)
42720dca1793SAdrian Knoth {
42730dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
42740dca1793SAdrian Knoth 
42750dca1793SAdrian Knoth 	if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) {
42760dca1793SAdrian Knoth 		hdspm->tco->pull = ucontrol->value.enumerated.item[0];
42770dca1793SAdrian Knoth 
42780dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
42790dca1793SAdrian Knoth 
42800dca1793SAdrian Knoth 		return 1;
42810dca1793SAdrian Knoth 	}
42820dca1793SAdrian Knoth 
42830dca1793SAdrian Knoth 	return 0;
42840dca1793SAdrian Knoth }
42850dca1793SAdrian Knoth 
42860dca1793SAdrian Knoth #define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \
42870dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
42880dca1793SAdrian Knoth 	.name = xname, \
42890dca1793SAdrian Knoth 	.index = xindex, \
42900dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
42910dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
42920dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_wck_conversion, \
42930dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_wck_conversion, \
42940dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_wck_conversion \
42950dca1793SAdrian Knoth }
42960dca1793SAdrian Knoth 
snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)42970dca1793SAdrian Knoth static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
42980dca1793SAdrian Knoth 					     struct snd_ctl_elem_info *uinfo)
42990dca1793SAdrian Knoth {
430038816545SAdrian Knoth 	static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
4301e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
43020dca1793SAdrian Knoth 	return 0;
43030dca1793SAdrian Knoth }
43040dca1793SAdrian Knoth 
snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)43050dca1793SAdrian Knoth static int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol,
43060dca1793SAdrian Knoth 					    struct snd_ctl_elem_value *ucontrol)
43070dca1793SAdrian Knoth {
43080dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43090dca1793SAdrian Knoth 
43100dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock;
43110dca1793SAdrian Knoth 
43120dca1793SAdrian Knoth 	return 0;
43130dca1793SAdrian Knoth }
43140dca1793SAdrian Knoth 
snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)43150dca1793SAdrian Knoth static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
43160dca1793SAdrian Knoth 					    struct snd_ctl_elem_value *ucontrol)
43170dca1793SAdrian Knoth {
43180dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43190dca1793SAdrian Knoth 
43200dca1793SAdrian Knoth 	if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) {
43210dca1793SAdrian Knoth 		hdspm->tco->wordclock = ucontrol->value.enumerated.item[0];
43220dca1793SAdrian Knoth 
43230dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
43240dca1793SAdrian Knoth 
43250dca1793SAdrian Knoth 		return 1;
43260dca1793SAdrian Knoth 	}
43270dca1793SAdrian Knoth 
43280dca1793SAdrian Knoth 	return 0;
43290dca1793SAdrian Knoth }
43300dca1793SAdrian Knoth 
43310dca1793SAdrian Knoth 
43320dca1793SAdrian Knoth #define HDSPM_TCO_FRAME_RATE(xname, xindex) \
43330dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
43340dca1793SAdrian Knoth 	.name = xname, \
43350dca1793SAdrian Knoth 	.index = xindex, \
43360dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
43370dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
43380dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_frame_rate, \
43390dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_frame_rate, \
43400dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_frame_rate \
43410dca1793SAdrian Knoth }
43420dca1793SAdrian Knoth 
snd_hdspm_info_tco_frame_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)43430dca1793SAdrian Knoth static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
43440dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
43450dca1793SAdrian Knoth {
434638816545SAdrian Knoth 	static const char *const texts[] = { "24 fps", "25 fps", "29.97fps",
43470dca1793SAdrian Knoth 		"29.97 dfps", "30 fps", "30 dfps" };
4348e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
43490dca1793SAdrian Knoth 	return 0;
43500dca1793SAdrian Knoth }
43510dca1793SAdrian Knoth 
snd_hdspm_get_tco_frame_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)43520dca1793SAdrian Knoth static int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol,
43530dca1793SAdrian Knoth 					struct snd_ctl_elem_value *ucontrol)
43540dca1793SAdrian Knoth {
43550dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43560dca1793SAdrian Knoth 
43570dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->framerate;
43580dca1793SAdrian Knoth 
43590dca1793SAdrian Knoth 	return 0;
43600dca1793SAdrian Knoth }
43610dca1793SAdrian Knoth 
snd_hdspm_put_tco_frame_rate(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)43620dca1793SAdrian Knoth static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
43630dca1793SAdrian Knoth 					struct snd_ctl_elem_value *ucontrol)
43640dca1793SAdrian Knoth {
43650dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
43660dca1793SAdrian Knoth 
43670dca1793SAdrian Knoth 	if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) {
43680dca1793SAdrian Knoth 		hdspm->tco->framerate = ucontrol->value.enumerated.item[0];
43690dca1793SAdrian Knoth 
43700dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
43710dca1793SAdrian Knoth 
43720dca1793SAdrian Knoth 		return 1;
43730dca1793SAdrian Knoth 	}
43740dca1793SAdrian Knoth 
43750dca1793SAdrian Knoth 	return 0;
43760dca1793SAdrian Knoth }
43770dca1793SAdrian Knoth 
43780dca1793SAdrian Knoth 
43790dca1793SAdrian Knoth #define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \
43800dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
43810dca1793SAdrian Knoth 	.name = xname, \
43820dca1793SAdrian Knoth 	.index = xindex, \
43830dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
43840dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
43850dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_sync_source, \
43860dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_sync_source, \
43870dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_sync_source \
43880dca1793SAdrian Knoth }
43890dca1793SAdrian Knoth 
snd_hdspm_info_tco_sync_source(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)43900dca1793SAdrian Knoth static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
43910dca1793SAdrian Knoth 					  struct snd_ctl_elem_info *uinfo)
43920dca1793SAdrian Knoth {
439338816545SAdrian Knoth 	static const char *const texts[] = { "LTC", "Video", "WCK" };
4394e5b7b1feSAdrian Knoth 	ENUMERATED_CTL_INFO(uinfo, texts);
43950dca1793SAdrian Knoth 	return 0;
43960dca1793SAdrian Knoth }
43970dca1793SAdrian Knoth 
snd_hdspm_get_tco_sync_source(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)43980dca1793SAdrian Knoth static int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol,
43990dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
44000dca1793SAdrian Knoth {
44010dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44020dca1793SAdrian Knoth 
44030dca1793SAdrian Knoth 	ucontrol->value.enumerated.item[0] = hdspm->tco->input;
44040dca1793SAdrian Knoth 
44050dca1793SAdrian Knoth 	return 0;
44060dca1793SAdrian Knoth }
44070dca1793SAdrian Knoth 
snd_hdspm_put_tco_sync_source(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)44080dca1793SAdrian Knoth static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol,
44090dca1793SAdrian Knoth 					 struct snd_ctl_elem_value *ucontrol)
44100dca1793SAdrian Knoth {
44110dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44120dca1793SAdrian Knoth 
44130dca1793SAdrian Knoth 	if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) {
44140dca1793SAdrian Knoth 		hdspm->tco->input = ucontrol->value.enumerated.item[0];
44150dca1793SAdrian Knoth 
44160dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
44170dca1793SAdrian Knoth 
44180dca1793SAdrian Knoth 		return 1;
44190dca1793SAdrian Knoth 	}
44200dca1793SAdrian Knoth 
44210dca1793SAdrian Knoth 	return 0;
44220dca1793SAdrian Knoth }
44230dca1793SAdrian Knoth 
44240dca1793SAdrian Knoth 
44250dca1793SAdrian Knoth #define HDSPM_TCO_WORD_TERM(xname, xindex) \
44260dca1793SAdrian Knoth {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
44270dca1793SAdrian Knoth 	.name = xname, \
44280dca1793SAdrian Knoth 	.index = xindex, \
44290dca1793SAdrian Knoth 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
44300dca1793SAdrian Knoth 			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
44310dca1793SAdrian Knoth 	.info = snd_hdspm_info_tco_word_term, \
44320dca1793SAdrian Knoth 	.get = snd_hdspm_get_tco_word_term, \
44330dca1793SAdrian Knoth 	.put = snd_hdspm_put_tco_word_term \
44340dca1793SAdrian Knoth }
44350dca1793SAdrian Knoth 
snd_hdspm_info_tco_word_term(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)44360dca1793SAdrian Knoth static int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol,
44370dca1793SAdrian Knoth 					struct snd_ctl_elem_info *uinfo)
44380dca1793SAdrian Knoth {
44390dca1793SAdrian Knoth 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
44400dca1793SAdrian Knoth 	uinfo->count = 1;
44410dca1793SAdrian Knoth 	uinfo->value.integer.min = 0;
44420dca1793SAdrian Knoth 	uinfo->value.integer.max = 1;
44430dca1793SAdrian Knoth 
44440dca1793SAdrian Knoth 	return 0;
44450dca1793SAdrian Knoth }
44460dca1793SAdrian Knoth 
44470dca1793SAdrian Knoth 
snd_hdspm_get_tco_word_term(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)44480dca1793SAdrian Knoth static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
44490dca1793SAdrian Knoth 				       struct snd_ctl_elem_value *ucontrol)
44500dca1793SAdrian Knoth {
44510dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44520dca1793SAdrian Knoth 
4453537e4813STakashi Iwai 	ucontrol->value.integer.value[0] = hdspm->tco->term;
44540dca1793SAdrian Knoth 
44550dca1793SAdrian Knoth 	return 0;
44560dca1793SAdrian Knoth }
44570dca1793SAdrian Knoth 
44580dca1793SAdrian Knoth 
snd_hdspm_put_tco_word_term(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)44590dca1793SAdrian Knoth static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
44600dca1793SAdrian Knoth 				       struct snd_ctl_elem_value *ucontrol)
44610dca1793SAdrian Knoth {
44620dca1793SAdrian Knoth 	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
44630dca1793SAdrian Knoth 
4464537e4813STakashi Iwai 	if (hdspm->tco->term != ucontrol->value.integer.value[0]) {
4465537e4813STakashi Iwai 		hdspm->tco->term = ucontrol->value.integer.value[0];
44660dca1793SAdrian Knoth 
44670dca1793SAdrian Knoth 		hdspm_tco_write(hdspm);
44680dca1793SAdrian Knoth 
44690dca1793SAdrian Knoth 		return 1;
44700dca1793SAdrian Knoth 	}
44710dca1793SAdrian Knoth 
44720dca1793SAdrian Knoth 	return 0;
44730dca1793SAdrian Knoth }
44740dca1793SAdrian Knoth 
44750dca1793SAdrian Knoth 
44760dca1793SAdrian Knoth 
44770dca1793SAdrian Knoth 
4478b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
4479763f356cSTakashi Iwai 	HDSPM_MIXER("Mixer", 0),
44800dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
4481763f356cSTakashi Iwai 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
4482763f356cSTakashi Iwai 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
4483763f356cSTakashi Iwai 	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
4484763f356cSTakashi Iwai 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
4485b8812c55SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
44860dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
44870dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("MADI SyncCheck", 1),
4488930f4ff0SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 2),
44890dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
4490c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
4491c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
4492696be0fbSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Disable 96K frames", HDSPM_SMUX),
4493c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
4494c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
4495700d1ef3SAdrian Knoth 	HDSPM_INPUT_SELECT("Input Select", 0),
4496700d1ef3SAdrian Knoth 	HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
44970dca1793SAdrian Knoth };
44980dca1793SAdrian Knoth 
44990dca1793SAdrian Knoth 
4500b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
45010dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
45020dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
45030dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
45040dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
45050dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
45060dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
4507c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
4508c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
4509c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
4510700d1ef3SAdrian Knoth 	HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
4511763f356cSTakashi Iwai };
4512763f356cSTakashi Iwai 
4513b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
45143cee5a60SRemy Bruno 	HDSPM_MIXER("Mixer", 0),
45150dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
45163cee5a60SRemy Bruno 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
45173cee5a60SRemy Bruno 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
45183cee5a60SRemy Bruno 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
45193cee5a60SRemy Bruno 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
45200dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
45210dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
45220dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
45230dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT SyncCheck", 3),
45240dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 4),
45250dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5),
45260dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
45270dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
45280dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
45290dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
45300dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
4531fb0f121eSAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5),
453242f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0),
4533fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt),
4534fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
4535fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1),
4536fb0f121eSAdrian Knoth 	HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db),
453742f4c12dSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48),
453842f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0),
453942f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0),
454042f4c12dSAdrian Knoth 	HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0)
45410dca1793SAdrian Knoth 
45420dca1793SAdrian Knoth 		/*
45430dca1793SAdrian Knoth 		   HDSPM_INPUT_SELECT("Input Select", 0),
45440dca1793SAdrian Knoth 		   HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0),
45450dca1793SAdrian Knoth 		   HDSPM_PROFESSIONAL("SPDIF Out Professional", 0);
45460dca1793SAdrian Knoth 		   HDSPM_SPDIF_IN("SPDIF In", 0);
45470dca1793SAdrian Knoth 		   HDSPM_BREAKOUT_CABLE("Breakout Cable", 0);
45480dca1793SAdrian Knoth 		   HDSPM_INPUT_LEVEL("Input Level", 0);
45490dca1793SAdrian Knoth 		   HDSPM_OUTPUT_LEVEL("Output Level", 0);
45500dca1793SAdrian Knoth 		   HDSPM_PHONES("Phones", 0);
45510dca1793SAdrian Knoth 		   */
45520dca1793SAdrian Knoth };
45530dca1793SAdrian Knoth 
4554b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
45550dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
45560dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
45570dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0),
45580dca1793SAdrian Knoth 	HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0),
45590dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
45600dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
45610dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
45620dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
45630dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3),
45640dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4),
45650dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5),
45660dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6),
45670dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO SyncCheck", 7),
45680dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8),
45690dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
45700dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
45710dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
45720dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3),
45730dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4),
45740dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
45750dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
45760dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
457711a5cd3cSAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8),
457811a5cd3cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
457911a5cd3cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48)
45800dca1793SAdrian Knoth };
45810dca1793SAdrian Knoth 
4582b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
45830dca1793SAdrian Knoth 	HDSPM_MIXER("Mixer", 0),
45840dca1793SAdrian Knoth 	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
45850dca1793SAdrian Knoth 	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
45860dca1793SAdrian Knoth 	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
45870dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
45880dca1793SAdrian Knoth 	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
45892d63ec38SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11),
45900dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("WC Sync Check", 0),
45910dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
45920dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
45930dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES3 Sync Check", 3),
45940dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES4 Sync Check", 4),
45950dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES5 Sync Check", 5),
45960dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES6 Sync Check", 6),
45970dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES7 Sync Check", 7),
45980dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("AES8 Sync Check", 8),
45990dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("TCO Sync Check", 9),
46000dca1793SAdrian Knoth 	HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10),
46010dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
46020dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1),
46030dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2),
46040dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3),
46050dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4),
46060dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5),
46070dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6),
46080dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7),
46090dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8),
46100dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9),
46110dca1793SAdrian Knoth 	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10),
4612c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
4613c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Emphasis", HDSPM_Emphasis),
4614c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Non Audio", HDSPM_Dolby),
4615c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Professional", HDSPM_Professional),
4616c9e1668cSAdrian Knoth 	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
46173cee5a60SRemy Bruno 	HDSPM_DS_WIRE("Double Speed Wire Mode", 0),
46183cee5a60SRemy Bruno 	HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
46193cee5a60SRemy Bruno };
46203cee5a60SRemy Bruno 
46210dca1793SAdrian Knoth 
46220dca1793SAdrian Knoth 
46230dca1793SAdrian Knoth /* Control elements for the optional TCO module */
4624b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
46250dca1793SAdrian Knoth 	HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0),
46260dca1793SAdrian Knoth 	HDSPM_TCO_PULL("TCO Pull", 0),
46270dca1793SAdrian Knoth 	HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
46280dca1793SAdrian Knoth 	HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
46290dca1793SAdrian Knoth 	HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
4630a817650eSAdrian Knoth 	HDSPM_TCO_WORD_TERM("TCO Word Term", 0),
4631a817650eSAdrian Knoth 	HDSPM_TCO_LOCK_CHECK("TCO Input Check", 11),
4632a817650eSAdrian Knoth 	HDSPM_TCO_LOCK_CHECK("TCO LTC Valid", 12),
4633a817650eSAdrian Knoth 	HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate", 0),
4634a817650eSAdrian Knoth 	HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format", 0)
46350dca1793SAdrian Knoth };
46360dca1793SAdrian Knoth 
46370dca1793SAdrian Knoth 
463898274f07STakashi Iwai static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
4639763f356cSTakashi Iwai 
4640763f356cSTakashi Iwai 
hdspm_update_simple_mixer_controls(struct hdspm * hdspm)464198274f07STakashi Iwai static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm)
4642763f356cSTakashi Iwai {
4643763f356cSTakashi Iwai 	int i;
4644763f356cSTakashi Iwai 
46450dca1793SAdrian Knoth 	for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) {
4646763f356cSTakashi Iwai 		if (hdspm->system_sample_rate > 48000) {
4647763f356cSTakashi Iwai 			hdspm->playback_mixer_ctls[i]->vd[0].access =
4648763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_INACTIVE |
4649763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_READ |
4650763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
4651763f356cSTakashi Iwai 		} else {
4652763f356cSTakashi Iwai 			hdspm->playback_mixer_ctls[i]->vd[0].access =
4653763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_READWRITE |
4654763f356cSTakashi Iwai 				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
4655763f356cSTakashi Iwai 		}
4656763f356cSTakashi Iwai 		snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
4657763f356cSTakashi Iwai 				SNDRV_CTL_EVENT_MASK_INFO,
4658763f356cSTakashi Iwai 				&hdspm->playback_mixer_ctls[i]->id);
4659763f356cSTakashi Iwai 	}
4660763f356cSTakashi Iwai 
4661763f356cSTakashi Iwai 	return 0;
4662763f356cSTakashi Iwai }
4663763f356cSTakashi Iwai 
4664763f356cSTakashi Iwai 
snd_hdspm_create_controls(struct snd_card * card,struct hdspm * hdspm)46650dca1793SAdrian Knoth static int snd_hdspm_create_controls(struct snd_card *card,
46660dca1793SAdrian Knoth 					struct hdspm *hdspm)
4667763f356cSTakashi Iwai {
4668763f356cSTakashi Iwai 	unsigned int idx, limit;
4669763f356cSTakashi Iwai 	int err;
467098274f07STakashi Iwai 	struct snd_kcontrol *kctl;
4671b4e5e707STakashi Iwai 	const struct snd_kcontrol_new *list = NULL;
4672763f356cSTakashi Iwai 
46730dca1793SAdrian Knoth 	switch (hdspm->io_type) {
46740dca1793SAdrian Knoth 	case MADI:
46750dca1793SAdrian Knoth 		list = snd_hdspm_controls_madi;
46760dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_madi);
46770dca1793SAdrian Knoth 		break;
46780dca1793SAdrian Knoth 	case MADIface:
46790dca1793SAdrian Knoth 		list = snd_hdspm_controls_madiface;
46800dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_madiface);
46810dca1793SAdrian Knoth 		break;
46820dca1793SAdrian Knoth 	case AIO:
46830dca1793SAdrian Knoth 		list = snd_hdspm_controls_aio;
46840dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_aio);
46850dca1793SAdrian Knoth 		break;
46860dca1793SAdrian Knoth 	case RayDAT:
46870dca1793SAdrian Knoth 		list = snd_hdspm_controls_raydat;
46880dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_raydat);
46890dca1793SAdrian Knoth 		break;
46900dca1793SAdrian Knoth 	case AES32:
46910dca1793SAdrian Knoth 		list = snd_hdspm_controls_aes32;
46920dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_aes32);
46930dca1793SAdrian Knoth 		break;
46940dca1793SAdrian Knoth 	}
4695763f356cSTakashi Iwai 
4696da2ea374SMarkus Elfring 	if (list) {
46970dca1793SAdrian Knoth 		for (idx = 0; idx < limit; idx++) {
46983cee5a60SRemy Bruno 			err = snd_ctl_add(card,
46990dca1793SAdrian Knoth 					snd_ctl_new1(&list[idx], hdspm));
47003cee5a60SRemy Bruno 			if (err < 0)
4701763f356cSTakashi Iwai 				return err;
4702763f356cSTakashi Iwai 		}
4703763f356cSTakashi Iwai 	}
4704763f356cSTakashi Iwai 
4705763f356cSTakashi Iwai 
47060dca1793SAdrian Knoth 	/* create simple 1:1 playback mixer controls */
4707763f356cSTakashi Iwai 	snd_hdspm_playback_mixer.name = "Chn";
47080dca1793SAdrian Knoth 	if (hdspm->system_sample_rate >= 128000) {
47090dca1793SAdrian Knoth 		limit = hdspm->qs_out_channels;
47100dca1793SAdrian Knoth 	} else if (hdspm->system_sample_rate >= 64000) {
47110dca1793SAdrian Knoth 		limit = hdspm->ds_out_channels;
47120dca1793SAdrian Knoth 	} else {
47130dca1793SAdrian Knoth 		limit = hdspm->ss_out_channels;
47140dca1793SAdrian Knoth 	}
4715763f356cSTakashi Iwai 	for (idx = 0; idx < limit; ++idx) {
4716763f356cSTakashi Iwai 		snd_hdspm_playback_mixer.index = idx + 1;
4717ef5fa1a4STakashi Iwai 		kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm);
4718ef5fa1a4STakashi Iwai 		err = snd_ctl_add(card, kctl);
4719ef5fa1a4STakashi Iwai 		if (err < 0)
4720763f356cSTakashi Iwai 			return err;
4721763f356cSTakashi Iwai 		hdspm->playback_mixer_ctls[idx] = kctl;
4722763f356cSTakashi Iwai 	}
4723763f356cSTakashi Iwai 
47240dca1793SAdrian Knoth 
47250dca1793SAdrian Knoth 	if (hdspm->tco) {
47260dca1793SAdrian Knoth 		/* add tco control elements */
47270dca1793SAdrian Knoth 		list = snd_hdspm_controls_tco;
47280dca1793SAdrian Knoth 		limit = ARRAY_SIZE(snd_hdspm_controls_tco);
47290dca1793SAdrian Knoth 		for (idx = 0; idx < limit; idx++) {
47300dca1793SAdrian Knoth 			err = snd_ctl_add(card,
47310dca1793SAdrian Knoth 					snd_ctl_new1(&list[idx], hdspm));
47320dca1793SAdrian Knoth 			if (err < 0)
47330dca1793SAdrian Knoth 				return err;
47340dca1793SAdrian Knoth 		}
47350dca1793SAdrian Knoth 	}
47360dca1793SAdrian Knoth 
4737763f356cSTakashi Iwai 	return 0;
4738763f356cSTakashi Iwai }
4739763f356cSTakashi Iwai 
4740763f356cSTakashi Iwai /*------------------------------------------------------------
4741763f356cSTakashi Iwai    /proc interface
4742763f356cSTakashi Iwai  ------------------------------------------------------------*/
4743763f356cSTakashi Iwai 
4744763f356cSTakashi Iwai static void
snd_hdspm_proc_read_tco(struct snd_info_entry * entry,struct snd_info_buffer * buffer)47455760107cSAdrian Knoth snd_hdspm_proc_read_tco(struct snd_info_entry *entry,
47463cee5a60SRemy Bruno 					struct snd_info_buffer *buffer)
4747763f356cSTakashi Iwai {
4748ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
47495760107cSAdrian Knoth 	unsigned int status, control;
47500dca1793SAdrian Knoth 	int a, ltc, frames, seconds, minutes, hours;
47510dca1793SAdrian Knoth 	unsigned int period;
47520dca1793SAdrian Knoth 	u64 freq_const = 0;
47530dca1793SAdrian Knoth 	u32 rate;
47540dca1793SAdrian Knoth 
47555760107cSAdrian Knoth 	snd_iprintf(buffer, "--- TCO ---\n");
47565760107cSAdrian Knoth 
4757763f356cSTakashi Iwai 	status = hdspm_read(hdspm, HDSPM_statusRegister);
47580dca1793SAdrian Knoth 	control = hdspm->control_register;
4759763f356cSTakashi Iwai 
4760763f356cSTakashi Iwai 
47610dca1793SAdrian Knoth 	if (status & HDSPM_tco_detect) {
47620dca1793SAdrian Knoth 		snd_iprintf(buffer, "TCO module detected.\n");
47630dca1793SAdrian Knoth 		a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
47640dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_LTC_Input_valid) {
47650dca1793SAdrian Knoth 			snd_iprintf(buffer, "  LTC valid, ");
47660dca1793SAdrian Knoth 			switch (a & (HDSPM_TCO1_LTC_Format_LSB |
47670dca1793SAdrian Knoth 						HDSPM_TCO1_LTC_Format_MSB)) {
47680dca1793SAdrian Knoth 			case 0:
47690dca1793SAdrian Knoth 				snd_iprintf(buffer, "24 fps, ");
47700dca1793SAdrian Knoth 				break;
47710dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_LSB:
47720dca1793SAdrian Knoth 				snd_iprintf(buffer, "25 fps, ");
47730dca1793SAdrian Knoth 				break;
47740dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_MSB:
47750dca1793SAdrian Knoth 				snd_iprintf(buffer, "29.97 fps, ");
47760dca1793SAdrian Knoth 				break;
47770dca1793SAdrian Knoth 			default:
47780dca1793SAdrian Knoth 				snd_iprintf(buffer, "30 fps, ");
47790dca1793SAdrian Knoth 				break;
47800dca1793SAdrian Knoth 			}
47810dca1793SAdrian Knoth 			if (a & HDSPM_TCO1_set_drop_frame_flag) {
47820dca1793SAdrian Knoth 				snd_iprintf(buffer, "drop frame\n");
47830dca1793SAdrian Knoth 			} else {
47840dca1793SAdrian Knoth 				snd_iprintf(buffer, "full frame\n");
47850dca1793SAdrian Knoth 			}
47860dca1793SAdrian Knoth 		} else {
47870dca1793SAdrian Knoth 			snd_iprintf(buffer, "  no LTC\n");
47880dca1793SAdrian Knoth 		}
47890dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_Video_Input_Format_NTSC) {
47900dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Video: NTSC\n");
47910dca1793SAdrian Knoth 		} else if (a & HDSPM_TCO1_Video_Input_Format_PAL) {
47920dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Video: PAL\n");
47930dca1793SAdrian Knoth 		} else {
47940dca1793SAdrian Knoth 			snd_iprintf(buffer, "  No video\n");
47950dca1793SAdrian Knoth 		}
47960dca1793SAdrian Knoth 		if (a & HDSPM_TCO1_TCO_lock) {
47970dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Sync: lock\n");
47980dca1793SAdrian Knoth 		} else {
47990dca1793SAdrian Knoth 			snd_iprintf(buffer, "  Sync: no lock\n");
48000dca1793SAdrian Knoth 		}
48010dca1793SAdrian Knoth 
48020dca1793SAdrian Knoth 		switch (hdspm->io_type) {
48030dca1793SAdrian Knoth 		case MADI:
48040dca1793SAdrian Knoth 		case AES32:
48050dca1793SAdrian Knoth 			freq_const = 110069313433624ULL;
48060dca1793SAdrian Knoth 			break;
48070dca1793SAdrian Knoth 		case RayDAT:
48080dca1793SAdrian Knoth 		case AIO:
48090dca1793SAdrian Knoth 			freq_const = 104857600000000ULL;
48100dca1793SAdrian Knoth 			break;
48110dca1793SAdrian Knoth 		case MADIface:
48120dca1793SAdrian Knoth 			break; /* no TCO possible */
48130dca1793SAdrian Knoth 		}
48140dca1793SAdrian Knoth 
48150dca1793SAdrian Knoth 		period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
48160dca1793SAdrian Knoth 		snd_iprintf(buffer, "    period: %u\n", period);
48170dca1793SAdrian Knoth 
48180dca1793SAdrian Knoth 
48190dca1793SAdrian Knoth 		/* rate = freq_const/period; */
48200dca1793SAdrian Knoth 		rate = div_u64(freq_const, period);
48210dca1793SAdrian Knoth 
48220dca1793SAdrian Knoth 		if (control & HDSPM_QuadSpeed) {
48230dca1793SAdrian Knoth 			rate *= 4;
48240dca1793SAdrian Knoth 		} else if (control & HDSPM_DoubleSpeed) {
48250dca1793SAdrian Knoth 			rate *= 2;
48260dca1793SAdrian Knoth 		}
48270dca1793SAdrian Knoth 
48280dca1793SAdrian Knoth 		snd_iprintf(buffer, "  Frequency: %u Hz\n",
48290dca1793SAdrian Knoth 				(unsigned int) rate);
48300dca1793SAdrian Knoth 
48310dca1793SAdrian Knoth 		ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
48320dca1793SAdrian Knoth 		frames = ltc & 0xF;
48330dca1793SAdrian Knoth 		ltc >>= 4;
48340dca1793SAdrian Knoth 		frames += (ltc & 0x3) * 10;
48350dca1793SAdrian Knoth 		ltc >>= 4;
48360dca1793SAdrian Knoth 		seconds = ltc & 0xF;
48370dca1793SAdrian Knoth 		ltc >>= 4;
48380dca1793SAdrian Knoth 		seconds += (ltc & 0x7) * 10;
48390dca1793SAdrian Knoth 		ltc >>= 4;
48400dca1793SAdrian Knoth 		minutes = ltc & 0xF;
48410dca1793SAdrian Knoth 		ltc >>= 4;
48420dca1793SAdrian Knoth 		minutes += (ltc & 0x7) * 10;
48430dca1793SAdrian Knoth 		ltc >>= 4;
48440dca1793SAdrian Knoth 		hours = ltc & 0xF;
48450dca1793SAdrian Knoth 		ltc >>= 4;
48460dca1793SAdrian Knoth 		hours += (ltc & 0x3) * 10;
48470dca1793SAdrian Knoth 		snd_iprintf(buffer,
48480dca1793SAdrian Knoth 			"  LTC In: %02d:%02d:%02d:%02d\n",
48490dca1793SAdrian Knoth 			hours, minutes, seconds, frames);
48500dca1793SAdrian Knoth 
48510dca1793SAdrian Knoth 	} else {
48520dca1793SAdrian Knoth 		snd_iprintf(buffer, "No TCO module detected.\n");
48530dca1793SAdrian Knoth 	}
48545760107cSAdrian Knoth }
48555760107cSAdrian Knoth 
48565760107cSAdrian Knoth static void
snd_hdspm_proc_read_madi(struct snd_info_entry * entry,struct snd_info_buffer * buffer)48575760107cSAdrian Knoth snd_hdspm_proc_read_madi(struct snd_info_entry *entry,
48585760107cSAdrian Knoth 			 struct snd_info_buffer *buffer)
48595760107cSAdrian Knoth {
48605760107cSAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
4861df57de17SSudip Mukherjee 	unsigned int status, status2;
48625760107cSAdrian Knoth 
48635760107cSAdrian Knoth 	char *pref_sync_ref;
48645760107cSAdrian Knoth 	char *autosync_ref;
48655760107cSAdrian Knoth 	char *system_clock_mode;
48665760107cSAdrian Knoth 	int x, x2;
48675760107cSAdrian Knoth 
48685760107cSAdrian Knoth 	status = hdspm_read(hdspm, HDSPM_statusRegister);
48695760107cSAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
48705760107cSAdrian Knoth 
48715760107cSAdrian Knoth 	snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
48725760107cSAdrian Knoth 			hdspm->card_name, hdspm->card->number + 1,
48735760107cSAdrian Knoth 			hdspm->firmware_rev,
48745760107cSAdrian Knoth 			(status2 & HDSPM_version0) |
48755760107cSAdrian Knoth 			(status2 & HDSPM_version1) | (status2 &
48765760107cSAdrian Knoth 				HDSPM_version2));
48775760107cSAdrian Knoth 
48785760107cSAdrian Knoth 	snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
48795760107cSAdrian Knoth 			(hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
48805760107cSAdrian Knoth 			hdspm->serial);
48815760107cSAdrian Knoth 
48825760107cSAdrian Knoth 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
48835760107cSAdrian Knoth 			hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
48845760107cSAdrian Knoth 
48855760107cSAdrian Knoth 	snd_iprintf(buffer, "--- System ---\n");
48865760107cSAdrian Knoth 
48875760107cSAdrian Knoth 	snd_iprintf(buffer,
48885760107cSAdrian Knoth 		"IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
48895760107cSAdrian Knoth 		status & HDSPM_audioIRQPending,
48905760107cSAdrian Knoth 		(status & HDSPM_midi0IRQPending) ? 1 : 0,
48915760107cSAdrian Knoth 		(status & HDSPM_midi1IRQPending) ? 1 : 0,
48925760107cSAdrian Knoth 		hdspm->irq_count);
48935760107cSAdrian Knoth 	snd_iprintf(buffer,
48945760107cSAdrian Knoth 		"HW pointer: id = %d, rawptr = %d (%d->%d) "
48955760107cSAdrian Knoth 		"estimated= %ld (bytes)\n",
48965760107cSAdrian Knoth 		((status & HDSPM_BufferID) ? 1 : 0),
48975760107cSAdrian Knoth 		(status & HDSPM_BufferPositionMask),
48985760107cSAdrian Knoth 		(status & HDSPM_BufferPositionMask) %
48995760107cSAdrian Knoth 		(2 * (int)hdspm->period_bytes),
49005760107cSAdrian Knoth 		((status & HDSPM_BufferPositionMask) - 64) %
49015760107cSAdrian Knoth 		(2 * (int)hdspm->period_bytes),
49025760107cSAdrian Knoth 		(long) hdspm_hw_pointer(hdspm) * 4);
49035760107cSAdrian Knoth 
49045760107cSAdrian Knoth 	snd_iprintf(buffer,
49055760107cSAdrian Knoth 		"MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
49065760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
49075760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
49085760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
49095760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
49105760107cSAdrian Knoth 	snd_iprintf(buffer,
49115760107cSAdrian Knoth 		"MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
49125760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
49135760107cSAdrian Knoth 		hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
49145760107cSAdrian Knoth 	snd_iprintf(buffer,
49155760107cSAdrian Knoth 		"Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
49165760107cSAdrian Knoth 		"status2=0x%x\n",
49175760107cSAdrian Knoth 		hdspm->control_register, hdspm->control2_register,
49185760107cSAdrian Knoth 		status, status2);
49195760107cSAdrian Knoth 
4920763f356cSTakashi Iwai 
4921763f356cSTakashi Iwai 	snd_iprintf(buffer, "--- Settings ---\n");
4922763f356cSTakashi Iwai 
49237cb155ffSAdrian Knoth 	x = hdspm_get_latency(hdspm);
4924763f356cSTakashi Iwai 
4925763f356cSTakashi Iwai 	snd_iprintf(buffer,
4926763f356cSTakashi Iwai 		"Size (Latency): %d samples (2 periods of %lu bytes)\n",
4927763f356cSTakashi Iwai 		x, (unsigned long) hdspm->period_bytes);
4928763f356cSTakashi Iwai 
49290dca1793SAdrian Knoth 	snd_iprintf(buffer, "Line out: %s\n",
49300dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
4931763f356cSTakashi Iwai 
4932763f356cSTakashi Iwai 	snd_iprintf(buffer,
4933ef5fa1a4STakashi Iwai 		"ClearTrackMarker = %s, Transmit in %s Channel Mode, "
4934ef5fa1a4STakashi Iwai 		"Auto Input %s\n",
49350dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
49360dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
49370dca1793SAdrian Knoth 		(hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
4938763f356cSTakashi Iwai 
49390dca1793SAdrian Knoth 
49403cee5a60SRemy Bruno 	if (!(hdspm->control_register & HDSPM_ClockModeMaster))
49410dca1793SAdrian Knoth 		system_clock_mode = "AutoSync";
49423cee5a60SRemy Bruno 	else
4943763f356cSTakashi Iwai 		system_clock_mode = "Master";
49440dca1793SAdrian Knoth 	snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode);
4945763f356cSTakashi Iwai 
4946763f356cSTakashi Iwai 	switch (hdspm_pref_sync_ref(hdspm)) {
4947763f356cSTakashi Iwai 	case HDSPM_SYNC_FROM_WORD:
4948763f356cSTakashi Iwai 		pref_sync_ref = "Word Clock";
4949763f356cSTakashi Iwai 		break;
4950763f356cSTakashi Iwai 	case HDSPM_SYNC_FROM_MADI:
4951763f356cSTakashi Iwai 		pref_sync_ref = "MADI Sync";
4952763f356cSTakashi Iwai 		break;
49530dca1793SAdrian Knoth 	case HDSPM_SYNC_FROM_TCO:
49540dca1793SAdrian Knoth 		pref_sync_ref = "TCO";
49550dca1793SAdrian Knoth 		break;
49560dca1793SAdrian Knoth 	case HDSPM_SYNC_FROM_SYNC_IN:
49570dca1793SAdrian Knoth 		pref_sync_ref = "Sync In";
49580dca1793SAdrian Knoth 		break;
4959763f356cSTakashi Iwai 	default:
4960763f356cSTakashi Iwai 		pref_sync_ref = "XXXX Clock";
4961763f356cSTakashi Iwai 		break;
4962763f356cSTakashi Iwai 	}
4963763f356cSTakashi Iwai 	snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
4964763f356cSTakashi Iwai 			pref_sync_ref);
4965763f356cSTakashi Iwai 
4966763f356cSTakashi Iwai 	snd_iprintf(buffer, "System Clock Frequency: %d\n",
4967763f356cSTakashi Iwai 			hdspm->system_sample_rate);
4968763f356cSTakashi Iwai 
4969763f356cSTakashi Iwai 
4970763f356cSTakashi Iwai 	snd_iprintf(buffer, "--- Status:\n");
4971763f356cSTakashi Iwai 
4972763f356cSTakashi Iwai 	x = status & HDSPM_madiSync;
4973763f356cSTakashi Iwai 	x2 = status2 & HDSPM_wcSync;
4974763f356cSTakashi Iwai 
4975763f356cSTakashi Iwai 	snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
4976763f356cSTakashi Iwai 			(status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
4977763f356cSTakashi Iwai 			"NoLock",
4978763f356cSTakashi Iwai 			(status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
4979763f356cSTakashi Iwai 			"NoLock");
4980763f356cSTakashi Iwai 
4981763f356cSTakashi Iwai 	switch (hdspm_autosync_ref(hdspm)) {
49820dca1793SAdrian Knoth 	case HDSPM_AUTOSYNC_FROM_SYNC_IN:
49830dca1793SAdrian Knoth 		autosync_ref = "Sync In";
49840dca1793SAdrian Knoth 		break;
49850dca1793SAdrian Knoth 	case HDSPM_AUTOSYNC_FROM_TCO:
49860dca1793SAdrian Knoth 		autosync_ref = "TCO";
49870dca1793SAdrian Knoth 		break;
4988763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_WORD:
4989763f356cSTakashi Iwai 		autosync_ref = "Word Clock";
4990763f356cSTakashi Iwai 		break;
4991763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_MADI:
4992763f356cSTakashi Iwai 		autosync_ref = "MADI Sync";
4993763f356cSTakashi Iwai 		break;
4994763f356cSTakashi Iwai 	case HDSPM_AUTOSYNC_FROM_NONE:
4995763f356cSTakashi Iwai 		autosync_ref = "Input not valid";
4996763f356cSTakashi Iwai 		break;
4997763f356cSTakashi Iwai 	default:
4998763f356cSTakashi Iwai 		autosync_ref = "---";
4999763f356cSTakashi Iwai 		break;
5000763f356cSTakashi Iwai 	}
5001763f356cSTakashi Iwai 	snd_iprintf(buffer,
5002763f356cSTakashi Iwai 		"AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
5003763f356cSTakashi Iwai 		autosync_ref, hdspm_external_sample_rate(hdspm),
5004763f356cSTakashi Iwai 		(status & HDSPM_madiFreqMask) >> 22,
5005763f356cSTakashi Iwai 		(status2 & HDSPM_wcFreqMask) >> 5);
5006763f356cSTakashi Iwai 
5007763f356cSTakashi Iwai 	snd_iprintf(buffer, "Input: %s, Mode=%s\n",
5008763f356cSTakashi Iwai 		(status & HDSPM_AB_int) ? "Coax" : "Optical",
5009763f356cSTakashi Iwai 		(status & HDSPM_RX_64ch) ? "64 channels" :
5010763f356cSTakashi Iwai 		"56 channels");
5011763f356cSTakashi Iwai 
50125760107cSAdrian Knoth 	/* call readout function for TCO specific status */
50135760107cSAdrian Knoth 	snd_hdspm_proc_read_tco(entry, buffer);
50145760107cSAdrian Knoth 
5015763f356cSTakashi Iwai 	snd_iprintf(buffer, "\n");
5016763f356cSTakashi Iwai }
5017763f356cSTakashi Iwai 
50183cee5a60SRemy Bruno static void
snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,struct snd_info_buffer * buffer)50193cee5a60SRemy Bruno snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
50203cee5a60SRemy Bruno 			  struct snd_info_buffer *buffer)
50213cee5a60SRemy Bruno {
5022ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
50233cee5a60SRemy Bruno 	unsigned int status;
50243cee5a60SRemy Bruno 	unsigned int status2;
50253cee5a60SRemy Bruno 	unsigned int timecode;
502656bde0f3SAndre Schramm 	unsigned int wcLock, wcSync;
50273cee5a60SRemy Bruno 	int pref_syncref;
50283cee5a60SRemy Bruno 	char *autosync_ref;
50293cee5a60SRemy Bruno 	int x;
50303cee5a60SRemy Bruno 
50313cee5a60SRemy Bruno 	status = hdspm_read(hdspm, HDSPM_statusRegister);
50323cee5a60SRemy Bruno 	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
50333cee5a60SRemy Bruno 	timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
50343cee5a60SRemy Bruno 
50353cee5a60SRemy Bruno 	snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n",
50363cee5a60SRemy Bruno 		    hdspm->card_name, hdspm->card->number + 1,
50373cee5a60SRemy Bruno 		    hdspm->firmware_rev);
50383cee5a60SRemy Bruno 
50393cee5a60SRemy Bruno 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
50403cee5a60SRemy Bruno 		    hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
50413cee5a60SRemy Bruno 
50423cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- System ---\n");
50433cee5a60SRemy Bruno 
50443cee5a60SRemy Bruno 	snd_iprintf(buffer,
50453cee5a60SRemy Bruno 		    "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
50463cee5a60SRemy Bruno 		    status & HDSPM_audioIRQPending,
50473cee5a60SRemy Bruno 		    (status & HDSPM_midi0IRQPending) ? 1 : 0,
50483cee5a60SRemy Bruno 		    (status & HDSPM_midi1IRQPending) ? 1 : 0,
50493cee5a60SRemy Bruno 		    hdspm->irq_count);
50503cee5a60SRemy Bruno 	snd_iprintf(buffer,
5051ef5fa1a4STakashi Iwai 		    "HW pointer: id = %d, rawptr = %d (%d->%d) "
5052ef5fa1a4STakashi Iwai 		    "estimated= %ld (bytes)\n",
50533cee5a60SRemy Bruno 		    ((status & HDSPM_BufferID) ? 1 : 0),
50543cee5a60SRemy Bruno 		    (status & HDSPM_BufferPositionMask),
5055ef5fa1a4STakashi Iwai 		    (status & HDSPM_BufferPositionMask) %
5056ef5fa1a4STakashi Iwai 		    (2 * (int)hdspm->period_bytes),
5057ef5fa1a4STakashi Iwai 		    ((status & HDSPM_BufferPositionMask) - 64) %
5058ef5fa1a4STakashi Iwai 		    (2 * (int)hdspm->period_bytes),
50593cee5a60SRemy Bruno 		    (long) hdspm_hw_pointer(hdspm) * 4);
50603cee5a60SRemy Bruno 
50613cee5a60SRemy Bruno 	snd_iprintf(buffer,
50623cee5a60SRemy Bruno 		    "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
50633cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
50643cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
50653cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
50663cee5a60SRemy Bruno 		    hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
50673cee5a60SRemy Bruno 	snd_iprintf(buffer,
50680dca1793SAdrian Knoth 		    "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
50690dca1793SAdrian Knoth 		    hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
50700dca1793SAdrian Knoth 		    hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
50710dca1793SAdrian Knoth 	snd_iprintf(buffer,
50720dca1793SAdrian Knoth 		    "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
50730dca1793SAdrian Knoth 		    "status2=0x%x\n",
50740dca1793SAdrian Knoth 		    hdspm->control_register, hdspm->control2_register,
50750dca1793SAdrian Knoth 		    status, status2);
50763cee5a60SRemy Bruno 
50773cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- Settings ---\n");
50783cee5a60SRemy Bruno 
50797cb155ffSAdrian Knoth 	x = hdspm_get_latency(hdspm);
50803cee5a60SRemy Bruno 
50813cee5a60SRemy Bruno 	snd_iprintf(buffer,
50823cee5a60SRemy Bruno 		    "Size (Latency): %d samples (2 periods of %lu bytes)\n",
50833cee5a60SRemy Bruno 		    x, (unsigned long) hdspm->period_bytes);
50843cee5a60SRemy Bruno 
50850dca1793SAdrian Knoth 	snd_iprintf(buffer, "Line out: %s\n",
50863cee5a60SRemy Bruno 		    (hdspm->
50870dca1793SAdrian Knoth 		     control_register & HDSPM_LineOut) ? "on " : "off");
50883cee5a60SRemy Bruno 
50893cee5a60SRemy Bruno 	snd_iprintf(buffer,
50903cee5a60SRemy Bruno 		    "ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
50913cee5a60SRemy Bruno 		    (hdspm->
50923cee5a60SRemy Bruno 		     control_register & HDSPM_clr_tms) ? "on" : "off",
50933cee5a60SRemy Bruno 		    (hdspm->
50943cee5a60SRemy Bruno 		     control_register & HDSPM_Emphasis) ? "on" : "off",
50953cee5a60SRemy Bruno 		    (hdspm->
50963cee5a60SRemy Bruno 		     control_register & HDSPM_Dolby) ? "on" : "off");
50973cee5a60SRemy Bruno 
50983cee5a60SRemy Bruno 
50993cee5a60SRemy Bruno 	pref_syncref = hdspm_pref_sync_ref(hdspm);
51003cee5a60SRemy Bruno 	if (pref_syncref == 0)
51013cee5a60SRemy Bruno 		snd_iprintf(buffer, "Preferred Sync Reference: Word Clock\n");
51023cee5a60SRemy Bruno 	else
51033cee5a60SRemy Bruno 		snd_iprintf(buffer, "Preferred Sync Reference: AES%d\n",
51043cee5a60SRemy Bruno 				pref_syncref);
51053cee5a60SRemy Bruno 
51063cee5a60SRemy Bruno 	snd_iprintf(buffer, "System Clock Frequency: %d\n",
51073cee5a60SRemy Bruno 		    hdspm->system_sample_rate);
51083cee5a60SRemy Bruno 
51093cee5a60SRemy Bruno 	snd_iprintf(buffer, "Double speed: %s\n",
51103cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_DS_DoubleWire?
51113cee5a60SRemy Bruno 			"Double wire" : "Single wire");
51123cee5a60SRemy Bruno 	snd_iprintf(buffer, "Quad speed: %s\n",
51133cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_QS_DoubleWire?
51143cee5a60SRemy Bruno 			"Double wire" :
51153cee5a60SRemy Bruno 			hdspm->control_register & HDSPM_QS_QuadWire?
51163cee5a60SRemy Bruno 			"Quad wire" : "Single wire");
51173cee5a60SRemy Bruno 
51183cee5a60SRemy Bruno 	snd_iprintf(buffer, "--- Status:\n");
51193cee5a60SRemy Bruno 
512056bde0f3SAndre Schramm 	wcLock = status & HDSPM_AES32_wcLock;
512156bde0f3SAndre Schramm 	wcSync = wcLock && (status & HDSPM_AES32_wcSync);
512256bde0f3SAndre Schramm 
51233cee5a60SRemy Bruno 	snd_iprintf(buffer, "Word: %s  Frequency: %d\n",
512456bde0f3SAndre Schramm 		    (wcLock) ? (wcSync ? "Sync   " : "Lock   ") : "No Lock",
51253cee5a60SRemy Bruno 		    HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
51263cee5a60SRemy Bruno 
51273cee5a60SRemy Bruno 	for (x = 0; x < 8; x++) {
51283cee5a60SRemy Bruno 		snd_iprintf(buffer, "AES%d: %s  Frequency: %d\n",
51293cee5a60SRemy Bruno 			    x+1,
5130ef5fa1a4STakashi Iwai 			    (status2 & (HDSPM_LockAES >> x)) ?
5131ef5fa1a4STakashi Iwai 			    "Sync   " : "No Lock",
51323cee5a60SRemy Bruno 			    HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
51333cee5a60SRemy Bruno 	}
51343cee5a60SRemy Bruno 
51353cee5a60SRemy Bruno 	switch (hdspm_autosync_ref(hdspm)) {
51360dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_NONE:
51370dca1793SAdrian Knoth 		autosync_ref = "None"; break;
51380dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_WORD:
51390dca1793SAdrian Knoth 		autosync_ref = "Word Clock"; break;
51400dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES1:
51410dca1793SAdrian Knoth 		autosync_ref = "AES1"; break;
51420dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES2:
51430dca1793SAdrian Knoth 		autosync_ref = "AES2"; break;
51440dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES3:
51450dca1793SAdrian Knoth 		autosync_ref = "AES3"; break;
51460dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES4:
51470dca1793SAdrian Knoth 		autosync_ref = "AES4"; break;
51480dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES5:
51490dca1793SAdrian Knoth 		autosync_ref = "AES5"; break;
51500dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES6:
51510dca1793SAdrian Knoth 		autosync_ref = "AES6"; break;
51520dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES7:
51530dca1793SAdrian Knoth 		autosync_ref = "AES7"; break;
51540dca1793SAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_AES8:
51550dca1793SAdrian Knoth 		autosync_ref = "AES8"; break;
5156194062daSAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_TCO:
5157194062daSAdrian Knoth 		autosync_ref = "TCO"; break;
5158194062daSAdrian Knoth 	case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN:
5159194062daSAdrian Knoth 		autosync_ref = "Sync In"; break;
51600dca1793SAdrian Knoth 	default:
51610dca1793SAdrian Knoth 		autosync_ref = "---"; break;
51623cee5a60SRemy Bruno 	}
51633cee5a60SRemy Bruno 	snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
51643cee5a60SRemy Bruno 
5165194062daSAdrian Knoth 	/* call readout function for TCO specific status */
5166194062daSAdrian Knoth 	snd_hdspm_proc_read_tco(entry, buffer);
5167194062daSAdrian Knoth 
51683cee5a60SRemy Bruno 	snd_iprintf(buffer, "\n");
51693cee5a60SRemy Bruno }
51703cee5a60SRemy Bruno 
51710dca1793SAdrian Knoth static void
snd_hdspm_proc_read_raydat(struct snd_info_entry * entry,struct snd_info_buffer * buffer)51720dca1793SAdrian Knoth snd_hdspm_proc_read_raydat(struct snd_info_entry *entry,
51730dca1793SAdrian Knoth 			 struct snd_info_buffer *buffer)
51740dca1793SAdrian Knoth {
51750dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
5176df57de17SSudip Mukherjee 	unsigned int status1, status2, status3, i;
51770dca1793SAdrian Knoth 	unsigned int lock, sync;
51780dca1793SAdrian Knoth 
51790dca1793SAdrian Knoth 	status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */
51800dca1793SAdrian Knoth 	status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */
51810dca1793SAdrian Knoth 	status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */
51820dca1793SAdrian Knoth 
51830dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1);
51840dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2);
51850dca1793SAdrian Knoth 	snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3);
51860dca1793SAdrian Knoth 
51870dca1793SAdrian Knoth 
51880dca1793SAdrian Knoth 	snd_iprintf(buffer, "\n*** CLOCK MODE\n\n");
51890dca1793SAdrian Knoth 
51900dca1793SAdrian Knoth 	snd_iprintf(buffer, "Clock mode      : %s\n",
51910dca1793SAdrian Knoth 		(hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave");
51920dca1793SAdrian Knoth 	snd_iprintf(buffer, "System frequency: %d Hz\n",
51930dca1793SAdrian Knoth 		hdspm_get_system_sample_rate(hdspm));
51940dca1793SAdrian Knoth 
51950dca1793SAdrian Knoth 	snd_iprintf(buffer, "\n*** INPUT STATUS\n\n");
51960dca1793SAdrian Knoth 
51970dca1793SAdrian Knoth 	lock = 0x1;
51980dca1793SAdrian Knoth 	sync = 0x100;
51990dca1793SAdrian Knoth 
52000dca1793SAdrian Knoth 	for (i = 0; i < 8; i++) {
52010dca1793SAdrian Knoth 		snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n",
52020dca1793SAdrian Knoth 				i,
52030dca1793SAdrian Knoth 				(status1 & lock) ? 1 : 0,
52040dca1793SAdrian Knoth 				(status1 & sync) ? 1 : 0,
52050dca1793SAdrian Knoth 				texts_freq[(status2 >> (i * 4)) & 0xF]);
52060dca1793SAdrian Knoth 
52070dca1793SAdrian Knoth 		lock = lock<<1;
52080dca1793SAdrian Knoth 		sync = sync<<1;
52090dca1793SAdrian Knoth 	}
52100dca1793SAdrian Knoth 
52110dca1793SAdrian Knoth 	snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n",
52120dca1793SAdrian Knoth 			(status1 & 0x1000000) ? 1 : 0,
52130dca1793SAdrian Knoth 			(status1 & 0x2000000) ? 1 : 0,
52140dca1793SAdrian Knoth 			texts_freq[(status1 >> 16) & 0xF]);
52150dca1793SAdrian Knoth 
52160dca1793SAdrian Knoth 	snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n",
52170dca1793SAdrian Knoth 			(status1 & 0x4000000) ? 1 : 0,
52180dca1793SAdrian Knoth 			(status1 & 0x8000000) ? 1 : 0,
52190dca1793SAdrian Knoth 			texts_freq[(status1 >> 20) & 0xF]);
52200dca1793SAdrian Knoth 
52210dca1793SAdrian Knoth 	snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n",
52220dca1793SAdrian Knoth 			(status3 & 0x400) ? 1 : 0,
52230dca1793SAdrian Knoth 			(status3 & 0x800) ? 1 : 0,
52240dca1793SAdrian Knoth 			texts_freq[(status2 >> 12) & 0xF]);
52250dca1793SAdrian Knoth 
52260dca1793SAdrian Knoth }
52270dca1793SAdrian Knoth 
52283cee5a60SRemy Bruno #ifdef CONFIG_SND_DEBUG
52293cee5a60SRemy Bruno static void
snd_hdspm_proc_read_debug(struct snd_info_entry * entry,struct snd_info_buffer * buffer)52303cee5a60SRemy Bruno snd_hdspm_proc_read_debug(struct snd_info_entry *entry,
52313cee5a60SRemy Bruno 			  struct snd_info_buffer *buffer)
52323cee5a60SRemy Bruno {
5233ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = entry->private_data;
52343cee5a60SRemy Bruno 
52353cee5a60SRemy Bruno 	int j,i;
52363cee5a60SRemy Bruno 
5237ef5fa1a4STakashi Iwai 	for (i = 0; i < 256 /* 1024*64 */; i += j) {
52383cee5a60SRemy Bruno 		snd_iprintf(buffer, "0x%08X: ", i);
52393cee5a60SRemy Bruno 		for (j = 0; j < 16; j += 4)
52403cee5a60SRemy Bruno 			snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j));
52413cee5a60SRemy Bruno 		snd_iprintf(buffer, "\n");
52423cee5a60SRemy Bruno 	}
52433cee5a60SRemy Bruno }
52443cee5a60SRemy Bruno #endif
52453cee5a60SRemy Bruno 
52463cee5a60SRemy Bruno 
snd_hdspm_proc_ports_in(struct snd_info_entry * entry,struct snd_info_buffer * buffer)52470dca1793SAdrian Knoth static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry,
52480dca1793SAdrian Knoth 			  struct snd_info_buffer *buffer)
52490dca1793SAdrian Knoth {
52500dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
52510dca1793SAdrian Knoth 	int i;
52520dca1793SAdrian Knoth 
52530dca1793SAdrian Knoth 	snd_iprintf(buffer, "# generated by hdspm\n");
52540dca1793SAdrian Knoth 
52550dca1793SAdrian Knoth 	for (i = 0; i < hdspm->max_channels_in; i++) {
52560dca1793SAdrian Knoth 		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]);
52570dca1793SAdrian Knoth 	}
52580dca1793SAdrian Knoth }
52590dca1793SAdrian Knoth 
snd_hdspm_proc_ports_out(struct snd_info_entry * entry,struct snd_info_buffer * buffer)52600dca1793SAdrian Knoth static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry,
52610dca1793SAdrian Knoth 			  struct snd_info_buffer *buffer)
52620dca1793SAdrian Knoth {
52630dca1793SAdrian Knoth 	struct hdspm *hdspm = entry->private_data;
52640dca1793SAdrian Knoth 	int i;
52650dca1793SAdrian Knoth 
52660dca1793SAdrian Knoth 	snd_iprintf(buffer, "# generated by hdspm\n");
52670dca1793SAdrian Knoth 
52680dca1793SAdrian Knoth 	for (i = 0; i < hdspm->max_channels_out; i++) {
52690dca1793SAdrian Knoth 		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]);
52700dca1793SAdrian Knoth 	}
52710dca1793SAdrian Knoth }
52720dca1793SAdrian Knoth 
52733cee5a60SRemy Bruno 
snd_hdspm_proc_init(struct hdspm * hdspm)5274e23e7a14SBill Pemberton static void snd_hdspm_proc_init(struct hdspm *hdspm)
5275763f356cSTakashi Iwai {
527647f2769bSTakashi Iwai 	void (*read)(struct snd_info_entry *, struct snd_info_buffer *) = NULL;
5277763f356cSTakashi Iwai 
52780dca1793SAdrian Knoth 	switch (hdspm->io_type) {
52790dca1793SAdrian Knoth 	case AES32:
528047f2769bSTakashi Iwai 		read = snd_hdspm_proc_read_aes32;
52810dca1793SAdrian Knoth 		break;
52820dca1793SAdrian Knoth 	case MADI:
528347f2769bSTakashi Iwai 		read = snd_hdspm_proc_read_madi;
52840dca1793SAdrian Knoth 		break;
52850dca1793SAdrian Knoth 	case MADIface:
528647f2769bSTakashi Iwai 		/* read = snd_hdspm_proc_read_madiface; */
52870dca1793SAdrian Knoth 		break;
52880dca1793SAdrian Knoth 	case RayDAT:
528947f2769bSTakashi Iwai 		read = snd_hdspm_proc_read_raydat;
52900dca1793SAdrian Knoth 		break;
52910dca1793SAdrian Knoth 	case AIO:
52920dca1793SAdrian Knoth 		break;
52930dca1793SAdrian Knoth 	}
52940dca1793SAdrian Knoth 
529547f2769bSTakashi Iwai 	snd_card_ro_proc_new(hdspm->card, "hdspm", hdspm, read);
529647f2769bSTakashi Iwai 	snd_card_ro_proc_new(hdspm->card, "ports.in", hdspm,
529747f2769bSTakashi Iwai 			     snd_hdspm_proc_ports_in);
529847f2769bSTakashi Iwai 	snd_card_ro_proc_new(hdspm->card, "ports.out", hdspm,
529947f2769bSTakashi Iwai 			     snd_hdspm_proc_ports_out);
53000dca1793SAdrian Knoth 
53013cee5a60SRemy Bruno #ifdef CONFIG_SND_DEBUG
53023cee5a60SRemy Bruno 	/* debug file to read all hdspm registers */
530347f2769bSTakashi Iwai 	snd_card_ro_proc_new(hdspm->card, "debug", hdspm,
53043cee5a60SRemy Bruno 			     snd_hdspm_proc_read_debug);
53053cee5a60SRemy Bruno #endif
5306763f356cSTakashi Iwai }
5307763f356cSTakashi Iwai 
5308763f356cSTakashi Iwai /*------------------------------------------------------------
5309763f356cSTakashi Iwai    hdspm intitialize
5310763f356cSTakashi Iwai  ------------------------------------------------------------*/
5311763f356cSTakashi Iwai 
snd_hdspm_set_defaults(struct hdspm * hdspm)531298274f07STakashi Iwai static int snd_hdspm_set_defaults(struct hdspm * hdspm)
5313763f356cSTakashi Iwai {
5314763f356cSTakashi Iwai 	/* ASSUMPTION: hdspm->lock is either held, or there is no need to
5315561de31aSJoe Perches 	   hold it (e.g. during module initialization).
5316763f356cSTakashi Iwai 	   */
5317763f356cSTakashi Iwai 
5318763f356cSTakashi Iwai 	/* set defaults:       */
5319763f356cSTakashi Iwai 
53200dca1793SAdrian Knoth 	hdspm->settings_register = 0;
53210dca1793SAdrian Knoth 
53220dca1793SAdrian Knoth 	switch (hdspm->io_type) {
53230dca1793SAdrian Knoth 	case MADI:
53240dca1793SAdrian Knoth 	case MADIface:
53250dca1793SAdrian Knoth 		hdspm->control_register =
53260dca1793SAdrian Knoth 			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
53270dca1793SAdrian Knoth 		break;
53280dca1793SAdrian Knoth 
53290dca1793SAdrian Knoth 	case RayDAT:
53300dca1793SAdrian Knoth 	case AIO:
53310dca1793SAdrian Knoth 		hdspm->settings_register = 0x1 + 0x1000;
53320dca1793SAdrian Knoth 		/* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0,
53330dca1793SAdrian Knoth 		 * line_out */
53340dca1793SAdrian Knoth 		hdspm->control_register =
53350dca1793SAdrian Knoth 			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
53360dca1793SAdrian Knoth 		break;
53370dca1793SAdrian Knoth 
53380dca1793SAdrian Knoth 	case AES32:
5339ef5fa1a4STakashi Iwai 		hdspm->control_register =
5340e71b95adSAdrian Knoth 			HDSPM_ClockModeMaster |	/* Master Clock Mode on */
53410dca1793SAdrian Knoth 			hdspm_encode_latency(7) | /* latency max=8192samples */
53423cee5a60SRemy Bruno 			HDSPM_SyncRef0 |	/* AES1 is syncclock */
53433cee5a60SRemy Bruno 			HDSPM_LineOut |	/* Analog output in */
53443cee5a60SRemy Bruno 			HDSPM_Professional;  /* Professional mode */
53450dca1793SAdrian Knoth 		break;
53460dca1793SAdrian Knoth 	}
5347763f356cSTakashi Iwai 
5348763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
5349763f356cSTakashi Iwai 
53500dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
5351ffb2c3c0SRemy Bruno 		/* No control2 register for AES32 */
5352763f356cSTakashi Iwai #ifdef SNDRV_BIG_ENDIAN
5353763f356cSTakashi Iwai 		hdspm->control2_register = HDSPM_BIGENDIAN_MODE;
5354763f356cSTakashi Iwai #else
5355763f356cSTakashi Iwai 		hdspm->control2_register = 0;
5356763f356cSTakashi Iwai #endif
5357763f356cSTakashi Iwai 
5358763f356cSTakashi Iwai 		hdspm_write(hdspm, HDSPM_control2Reg, hdspm->control2_register);
5359ffb2c3c0SRemy Bruno 	}
5360763f356cSTakashi Iwai 	hdspm_compute_period_size(hdspm);
5361763f356cSTakashi Iwai 
5362763f356cSTakashi Iwai 	/* silence everything */
5363763f356cSTakashi Iwai 
5364763f356cSTakashi Iwai 	all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
5365763f356cSTakashi Iwai 
5366b2ed6326SAdrian Knoth 	if (hdspm_is_raydat_or_aio(hdspm))
53670dca1793SAdrian Knoth 		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
5368763f356cSTakashi Iwai 
5369763f356cSTakashi Iwai 	/* set a default rate so that the channel map is set up. */
53700dca1793SAdrian Knoth 	hdspm_set_rate(hdspm, 48000, 1);
5371763f356cSTakashi Iwai 
5372763f356cSTakashi Iwai 	return 0;
5373763f356cSTakashi Iwai }
5374763f356cSTakashi Iwai 
5375763f356cSTakashi Iwai 
5376763f356cSTakashi Iwai /*------------------------------------------------------------
5377561de31aSJoe Perches    interrupt
5378763f356cSTakashi Iwai  ------------------------------------------------------------*/
5379763f356cSTakashi Iwai 
snd_hdspm_interrupt(int irq,void * dev_id)53807d12e780SDavid Howells static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
5381763f356cSTakashi Iwai {
538298274f07STakashi Iwai 	struct hdspm *hdspm = (struct hdspm *) dev_id;
5383763f356cSTakashi Iwai 	unsigned int status;
53840dca1793SAdrian Knoth 	int i, audio, midi, schedule = 0;
53850dca1793SAdrian Knoth 	/* cycles_t now; */
5386763f356cSTakashi Iwai 
5387763f356cSTakashi Iwai 	status = hdspm_read(hdspm, HDSPM_statusRegister);
5388763f356cSTakashi Iwai 
5389763f356cSTakashi Iwai 	audio = status & HDSPM_audioIRQPending;
53900dca1793SAdrian Knoth 	midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending |
53910dca1793SAdrian Knoth 			HDSPM_midi2IRQPending | HDSPM_midi3IRQPending);
5392763f356cSTakashi Iwai 
53930dca1793SAdrian Knoth 	/* now = get_cycles(); */
5394ddcecf6bSTakashi Iwai 	/*
53950dca1793SAdrian Knoth 	 *   LAT_2..LAT_0 period  counter (win)  counter (mac)
53960dca1793SAdrian Knoth 	 *          6       4096   ~256053425     ~514672358
53970dca1793SAdrian Knoth 	 *          5       2048   ~128024983     ~257373821
53980dca1793SAdrian Knoth 	 *          4       1024    ~64023706     ~128718089
53990dca1793SAdrian Knoth 	 *          3        512    ~32005945      ~64385999
54000dca1793SAdrian Knoth 	 *          2        256    ~16003039      ~32260176
54010dca1793SAdrian Knoth 	 *          1        128     ~7998738      ~16194507
54020dca1793SAdrian Knoth 	 *          0         64     ~3998231       ~8191558
5403ddcecf6bSTakashi Iwai 	 */
54040dca1793SAdrian Knoth 	/*
5405e3a471d6STakashi Iwai 	  dev_info(hdspm->card->dev, "snd_hdspm_interrupt %llu @ %llx\n",
54060dca1793SAdrian Knoth 	   now-hdspm->last_interrupt, status & 0xFFC0);
54070dca1793SAdrian Knoth 	   hdspm->last_interrupt = now;
54080dca1793SAdrian Knoth 	*/
54090dca1793SAdrian Knoth 
54100dca1793SAdrian Knoth 	if (!audio && !midi)
5411763f356cSTakashi Iwai 		return IRQ_NONE;
5412763f356cSTakashi Iwai 
5413763f356cSTakashi Iwai 	hdspm_write(hdspm, HDSPM_interruptConfirmation, 0);
5414763f356cSTakashi Iwai 	hdspm->irq_count++;
5415763f356cSTakashi Iwai 
5416763f356cSTakashi Iwai 
5417763f356cSTakashi Iwai 	if (audio) {
5418763f356cSTakashi Iwai 		if (hdspm->capture_substream)
5419ef5fa1a4STakashi Iwai 			snd_pcm_period_elapsed(hdspm->capture_substream);
5420763f356cSTakashi Iwai 
5421763f356cSTakashi Iwai 		if (hdspm->playback_substream)
5422ef5fa1a4STakashi Iwai 			snd_pcm_period_elapsed(hdspm->playback_substream);
5423763f356cSTakashi Iwai 	}
5424763f356cSTakashi Iwai 
54250dca1793SAdrian Knoth 	if (midi) {
54260dca1793SAdrian Knoth 		i = 0;
54270dca1793SAdrian Knoth 		while (i < hdspm->midiPorts) {
54280dca1793SAdrian Knoth 			if ((hdspm_read(hdspm,
54290dca1793SAdrian Knoth 				hdspm->midi[i].statusIn) & 0xff) &&
54300dca1793SAdrian Knoth 					(status & hdspm->midi[i].irq)) {
54310dca1793SAdrian Knoth 				/* we disable interrupts for this input until
54320dca1793SAdrian Knoth 				 * processing is done
5433ef5fa1a4STakashi Iwai 				 */
54340dca1793SAdrian Knoth 				hdspm->control_register &= ~hdspm->midi[i].ie;
5435763f356cSTakashi Iwai 				hdspm_write(hdspm, HDSPM_controlRegister,
5436763f356cSTakashi Iwai 						hdspm->control_register);
54370dca1793SAdrian Knoth 				hdspm->midi[i].pending = 1;
5438763f356cSTakashi Iwai 				schedule = 1;
5439763f356cSTakashi Iwai 			}
54400dca1793SAdrian Knoth 
54410dca1793SAdrian Knoth 			i++;
5442763f356cSTakashi Iwai 		}
54430dca1793SAdrian Knoth 
5444763f356cSTakashi Iwai 		if (schedule)
5445a2e527c5STakashi Iwai 			queue_work(system_highpri_wq, &hdspm->midi_work);
54460dca1793SAdrian Knoth 	}
54470dca1793SAdrian Knoth 
5448763f356cSTakashi Iwai 	return IRQ_HANDLED;
5449763f356cSTakashi Iwai }
5450763f356cSTakashi Iwai 
5451763f356cSTakashi Iwai /*------------------------------------------------------------
5452763f356cSTakashi Iwai    pcm interface
5453763f356cSTakashi Iwai   ------------------------------------------------------------*/
5454763f356cSTakashi Iwai 
5455763f356cSTakashi Iwai 
snd_hdspm_hw_pointer(struct snd_pcm_substream * substream)54560dca1793SAdrian Knoth static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream
54570dca1793SAdrian Knoth 					      *substream)
5458763f356cSTakashi Iwai {
545998274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5460763f356cSTakashi Iwai 	return hdspm_hw_pointer(hdspm);
5461763f356cSTakashi Iwai }
5462763f356cSTakashi Iwai 
5463763f356cSTakashi Iwai 
snd_hdspm_reset(struct snd_pcm_substream * substream)546498274f07STakashi Iwai static int snd_hdspm_reset(struct snd_pcm_substream *substream)
5465763f356cSTakashi Iwai {
546698274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
546798274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
546898274f07STakashi Iwai 	struct snd_pcm_substream *other;
5469763f356cSTakashi Iwai 
5470763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5471763f356cSTakashi Iwai 		other = hdspm->capture_substream;
5472763f356cSTakashi Iwai 	else
5473763f356cSTakashi Iwai 		other = hdspm->playback_substream;
5474763f356cSTakashi Iwai 
5475763f356cSTakashi Iwai 	if (hdspm->running)
5476763f356cSTakashi Iwai 		runtime->status->hw_ptr = hdspm_hw_pointer(hdspm);
5477763f356cSTakashi Iwai 	else
5478763f356cSTakashi Iwai 		runtime->status->hw_ptr = 0;
5479763f356cSTakashi Iwai 	if (other) {
548098274f07STakashi Iwai 		struct snd_pcm_substream *s;
548198274f07STakashi Iwai 		struct snd_pcm_runtime *oruntime = other->runtime;
5482ef991b95STakashi Iwai 		snd_pcm_group_for_each_entry(s, substream) {
5483763f356cSTakashi Iwai 			if (s == other) {
5484763f356cSTakashi Iwai 				oruntime->status->hw_ptr =
5485763f356cSTakashi Iwai 					runtime->status->hw_ptr;
5486763f356cSTakashi Iwai 				break;
5487763f356cSTakashi Iwai 			}
5488763f356cSTakashi Iwai 		}
5489763f356cSTakashi Iwai 	}
5490763f356cSTakashi Iwai 	return 0;
5491763f356cSTakashi Iwai }
5492763f356cSTakashi Iwai 
snd_hdspm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)549398274f07STakashi Iwai static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
549498274f07STakashi Iwai 			       struct snd_pcm_hw_params *params)
5495763f356cSTakashi Iwai {
549698274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5497763f356cSTakashi Iwai 	int err;
5498763f356cSTakashi Iwai 	int i;
5499763f356cSTakashi Iwai 	pid_t this_pid;
5500763f356cSTakashi Iwai 	pid_t other_pid;
5501763f356cSTakashi Iwai 
5502763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5503763f356cSTakashi Iwai 
5504763f356cSTakashi Iwai 	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5505763f356cSTakashi Iwai 		this_pid = hdspm->playback_pid;
5506763f356cSTakashi Iwai 		other_pid = hdspm->capture_pid;
5507763f356cSTakashi Iwai 	} else {
5508763f356cSTakashi Iwai 		this_pid = hdspm->capture_pid;
5509763f356cSTakashi Iwai 		other_pid = hdspm->playback_pid;
5510763f356cSTakashi Iwai 	}
5511763f356cSTakashi Iwai 
5512ef5fa1a4STakashi Iwai 	if (other_pid > 0 && this_pid != other_pid) {
5513763f356cSTakashi Iwai 
5514763f356cSTakashi Iwai 		/* The other stream is open, and not by the same
5515763f356cSTakashi Iwai 		   task as this one. Make sure that the parameters
5516763f356cSTakashi Iwai 		   that matter are the same.
5517763f356cSTakashi Iwai 		   */
5518763f356cSTakashi Iwai 
5519763f356cSTakashi Iwai 		if (params_rate(params) != hdspm->system_sample_rate) {
5520763f356cSTakashi Iwai 			spin_unlock_irq(&hdspm->lock);
5521763f356cSTakashi Iwai 			_snd_pcm_hw_param_setempty(params,
5522763f356cSTakashi Iwai 					SNDRV_PCM_HW_PARAM_RATE);
5523763f356cSTakashi Iwai 			return -EBUSY;
5524763f356cSTakashi Iwai 		}
5525763f356cSTakashi Iwai 
5526763f356cSTakashi Iwai 		if (params_period_size(params) != hdspm->period_bytes / 4) {
5527763f356cSTakashi Iwai 			spin_unlock_irq(&hdspm->lock);
5528763f356cSTakashi Iwai 			_snd_pcm_hw_param_setempty(params,
5529763f356cSTakashi Iwai 					SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
5530763f356cSTakashi Iwai 			return -EBUSY;
5531763f356cSTakashi Iwai 		}
5532763f356cSTakashi Iwai 
5533763f356cSTakashi Iwai 	}
5534763f356cSTakashi Iwai 	/* We're fine. */
5535763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5536763f356cSTakashi Iwai 
5537763f356cSTakashi Iwai 	/* how to make sure that the rate matches an externally-set one ?   */
5538763f356cSTakashi Iwai 
5539763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
5540ef5fa1a4STakashi Iwai 	err = hdspm_set_rate(hdspm, params_rate(params), 0);
5541ef5fa1a4STakashi Iwai 	if (err < 0) {
5542e3a471d6STakashi Iwai 		dev_info(hdspm->card->dev, "err on hdspm_set_rate: %d\n", err);
5543763f356cSTakashi Iwai 		spin_unlock_irq(&hdspm->lock);
5544763f356cSTakashi Iwai 		_snd_pcm_hw_param_setempty(params,
5545763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_RATE);
5546763f356cSTakashi Iwai 		return err;
5547763f356cSTakashi Iwai 	}
5548763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
5549763f356cSTakashi Iwai 
5550ef5fa1a4STakashi Iwai 	err = hdspm_set_interrupt_interval(hdspm,
5551ef5fa1a4STakashi Iwai 			params_period_size(params));
5552ef5fa1a4STakashi Iwai 	if (err < 0) {
5553e3a471d6STakashi Iwai 		dev_info(hdspm->card->dev,
5554e3a471d6STakashi Iwai 			 "err on hdspm_set_interrupt_interval: %d\n", err);
5555763f356cSTakashi Iwai 		_snd_pcm_hw_param_setempty(params,
5556763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
5557763f356cSTakashi Iwai 		return err;
5558763f356cSTakashi Iwai 	}
5559763f356cSTakashi Iwai 
5560ef5fa1a4STakashi Iwai 	/* Memory allocation, takashi's method, dont know if we should
5561ef5fa1a4STakashi Iwai 	 * spinlock
5562ef5fa1a4STakashi Iwai 	 */
5563763f356cSTakashi Iwai 	/* malloc all buffer even if not enabled to get sure */
5564ffb2c3c0SRemy Bruno 	/* Update for MADI rev 204: we need to allocate for all channels,
5565ffb2c3c0SRemy Bruno 	 * otherwise it doesn't work at 96kHz */
55660dca1793SAdrian Knoth 
5567763f356cSTakashi Iwai 	err =
5568ffb2c3c0SRemy Bruno 		snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
55690dca1793SAdrian Knoth 	if (err < 0) {
5570e3a471d6STakashi Iwai 		dev_info(hdspm->card->dev,
5571e3a471d6STakashi Iwai 			 "err on snd_pcm_lib_malloc_pages: %d\n", err);
5572763f356cSTakashi Iwai 		return err;
55730dca1793SAdrian Knoth 	}
5574763f356cSTakashi Iwai 
5575763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5576763f356cSTakashi Iwai 
5577e4e07c6cSPhilippe Bekaert 		for (i = 0; i < params_channels(params); ++i) {
5578e4e07c6cSPhilippe Bekaert 			int c = hdspm->channel_map_out[i];
5579763f356cSTakashi Iwai 
5580e4e07c6cSPhilippe Bekaert 			if (c < 0)
5581e4e07c6cSPhilippe Bekaert 				continue;      /* just make sure */
5582e4e07c6cSPhilippe Bekaert 			hdspm_set_channel_dma_addr(hdspm, substream,
5583e4e07c6cSPhilippe Bekaert 						   HDSPM_pageAddressBufferOut,
5584e4e07c6cSPhilippe Bekaert 						   c);
5585e4e07c6cSPhilippe Bekaert 			snd_hdspm_enable_out(hdspm, c, 1);
5586e4e07c6cSPhilippe Bekaert 		}
5587763f356cSTakashi Iwai 
5588763f356cSTakashi Iwai 		hdspm->playback_buffer =
5589763f356cSTakashi Iwai 			(unsigned char *) substream->runtime->dma_area;
5590e3a471d6STakashi Iwai 		dev_dbg(hdspm->card->dev,
5591e3a471d6STakashi Iwai 			"Allocated sample buffer for playback at %p\n",
55923cee5a60SRemy Bruno 				hdspm->playback_buffer);
5593763f356cSTakashi Iwai 	} else {
5594e4e07c6cSPhilippe Bekaert 		for (i = 0; i < params_channels(params); ++i) {
5595e4e07c6cSPhilippe Bekaert 			int c = hdspm->channel_map_in[i];
5596763f356cSTakashi Iwai 
5597e4e07c6cSPhilippe Bekaert 			if (c < 0)
5598e4e07c6cSPhilippe Bekaert 				continue;
5599e4e07c6cSPhilippe Bekaert 			hdspm_set_channel_dma_addr(hdspm, substream,
5600e4e07c6cSPhilippe Bekaert 						   HDSPM_pageAddressBufferIn,
5601e4e07c6cSPhilippe Bekaert 						   c);
5602e4e07c6cSPhilippe Bekaert 			snd_hdspm_enable_in(hdspm, c, 1);
5603e4e07c6cSPhilippe Bekaert 		}
5604763f356cSTakashi Iwai 
5605763f356cSTakashi Iwai 		hdspm->capture_buffer =
5606763f356cSTakashi Iwai 			(unsigned char *) substream->runtime->dma_area;
5607e3a471d6STakashi Iwai 		dev_dbg(hdspm->card->dev,
5608e3a471d6STakashi Iwai 			"Allocated sample buffer for capture at %p\n",
56093cee5a60SRemy Bruno 				hdspm->capture_buffer);
5610763f356cSTakashi Iwai 	}
56110dca1793SAdrian Knoth 
56123cee5a60SRemy Bruno 	/*
5613e3a471d6STakashi Iwai 	   dev_dbg(hdspm->card->dev,
5614e3a471d6STakashi Iwai 	   "Allocated sample buffer for %s at 0x%08X\n",
56153cee5a60SRemy Bruno 	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
56163cee5a60SRemy Bruno 	   "playback" : "capture",
561777a23f26STakashi Iwai 	   snd_pcm_sgbuf_get_addr(substream, 0));
56183cee5a60SRemy Bruno 	   */
5619ffb2c3c0SRemy Bruno 	/*
5620e3a471d6STakashi Iwai 	   dev_dbg(hdspm->card->dev,
5621e3a471d6STakashi Iwai 	   "set_hwparams: %s %d Hz, %d channels, bs = %d\n",
5622ffb2c3c0SRemy Bruno 	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
5623ffb2c3c0SRemy Bruno 	   "playback" : "capture",
5624ffb2c3c0SRemy Bruno 	   params_rate(params), params_channels(params),
5625ffb2c3c0SRemy Bruno 	   params_buffer_size(params));
5626ffb2c3c0SRemy Bruno 	   */
56270dca1793SAdrian Knoth 
56280dca1793SAdrian Knoth 
56293ac9b0acSAdrian Knoth 	/*  For AES cards, the float format bit is the same as the
56303ac9b0acSAdrian Knoth 	 *  preferred sync reference. Since we don't want to break
56313ac9b0acSAdrian Knoth 	 *  sync settings, we have to skip the remaining part of this
56323ac9b0acSAdrian Knoth 	 *  function.
56333ac9b0acSAdrian Knoth 	 */
56343ac9b0acSAdrian Knoth 	if (hdspm->io_type == AES32) {
56353ac9b0acSAdrian Knoth 		return 0;
56363ac9b0acSAdrian Knoth 	}
56373ac9b0acSAdrian Knoth 
56383ac9b0acSAdrian Knoth 
56390dca1793SAdrian Knoth 	/* Switch to native float format if requested */
56400dca1793SAdrian Knoth 	if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
56410dca1793SAdrian Knoth 		if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
5642e3a471d6STakashi Iwai 			dev_info(hdspm->card->dev,
5643e3a471d6STakashi Iwai 				 "Switching to native 32bit LE float format.\n");
56440dca1793SAdrian Knoth 
56450dca1793SAdrian Knoth 		hdspm->control_register |= HDSPe_FLOAT_FORMAT;
56460dca1793SAdrian Knoth 	} else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) {
56470dca1793SAdrian Knoth 		if (hdspm->control_register & HDSPe_FLOAT_FORMAT)
5648e3a471d6STakashi Iwai 			dev_info(hdspm->card->dev,
5649e3a471d6STakashi Iwai 				 "Switching to native 32bit LE integer format.\n");
56500dca1793SAdrian Knoth 
56510dca1793SAdrian Knoth 		hdspm->control_register &= ~HDSPe_FLOAT_FORMAT;
56520dca1793SAdrian Knoth 	}
56530dca1793SAdrian Knoth 	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
56540dca1793SAdrian Knoth 
5655763f356cSTakashi Iwai 	return 0;
5656763f356cSTakashi Iwai }
5657763f356cSTakashi Iwai 
snd_hdspm_hw_free(struct snd_pcm_substream * substream)565898274f07STakashi Iwai static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
5659763f356cSTakashi Iwai {
5660763f356cSTakashi Iwai 	int i;
566198274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
5662763f356cSTakashi Iwai 
5663763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5664e4e07c6cSPhilippe Bekaert 		/* Just disable all channels. The saving when disabling a */
5665e4e07c6cSPhilippe Bekaert 		/* smaller set is not worth the trouble. */
5666e4e07c6cSPhilippe Bekaert 		for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
5667763f356cSTakashi Iwai 			snd_hdspm_enable_out(hdspm, i, 0);
5668763f356cSTakashi Iwai 
5669763f356cSTakashi Iwai 		hdspm->playback_buffer = NULL;
5670763f356cSTakashi Iwai 	} else {
5671e4e07c6cSPhilippe Bekaert 		for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
5672763f356cSTakashi Iwai 			snd_hdspm_enable_in(hdspm, i, 0);
5673763f356cSTakashi Iwai 
5674763f356cSTakashi Iwai 		hdspm->capture_buffer = NULL;
5675763f356cSTakashi Iwai 	}
5676763f356cSTakashi Iwai 
5677763f356cSTakashi Iwai 	snd_pcm_lib_free_pages(substream);
5678763f356cSTakashi Iwai 
5679763f356cSTakashi Iwai 	return 0;
5680763f356cSTakashi Iwai }
5681763f356cSTakashi Iwai 
56820dca1793SAdrian Knoth 
snd_hdspm_channel_info(struct snd_pcm_substream * substream,struct snd_pcm_channel_info * info)568398274f07STakashi Iwai static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
568498274f07STakashi Iwai 		struct snd_pcm_channel_info *info)
5685763f356cSTakashi Iwai {
568698274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
568710513142STakashi Iwai 	unsigned int channel = info->channel;
5688763f356cSTakashi Iwai 
56890dca1793SAdrian Knoth 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
569010513142STakashi Iwai 		if (snd_BUG_ON(channel >= hdspm->max_channels_out)) {
5691e3a471d6STakashi Iwai 			dev_info(hdspm->card->dev,
5692e3a471d6STakashi Iwai 				 "snd_hdspm_channel_info: output channel out of range (%d)\n",
569310513142STakashi Iwai 				 channel);
5694da3cec35STakashi Iwai 			return -EINVAL;
56950dca1793SAdrian Knoth 		}
5696763f356cSTakashi Iwai 
569710513142STakashi Iwai 		channel = array_index_nospec(channel, hdspm->max_channels_out);
569810513142STakashi Iwai 		if (hdspm->channel_map_out[channel] < 0) {
5699e3a471d6STakashi Iwai 			dev_info(hdspm->card->dev,
5700e3a471d6STakashi Iwai 				 "snd_hdspm_channel_info: output channel %d mapped out\n",
570110513142STakashi Iwai 				 channel);
5702763f356cSTakashi Iwai 			return -EINVAL;
57030dca1793SAdrian Knoth 		}
5704763f356cSTakashi Iwai 
570510513142STakashi Iwai 		info->offset = hdspm->channel_map_out[channel] *
57060dca1793SAdrian Knoth 			HDSPM_CHANNEL_BUFFER_BYTES;
57070dca1793SAdrian Knoth 	} else {
570810513142STakashi Iwai 		if (snd_BUG_ON(channel >= hdspm->max_channels_in)) {
5709e3a471d6STakashi Iwai 			dev_info(hdspm->card->dev,
5710e3a471d6STakashi Iwai 				 "snd_hdspm_channel_info: input channel out of range (%d)\n",
571110513142STakashi Iwai 				 channel);
57120dca1793SAdrian Knoth 			return -EINVAL;
57130dca1793SAdrian Knoth 		}
57140dca1793SAdrian Knoth 
571510513142STakashi Iwai 		channel = array_index_nospec(channel, hdspm->max_channels_in);
571610513142STakashi Iwai 		if (hdspm->channel_map_in[channel] < 0) {
5717e3a471d6STakashi Iwai 			dev_info(hdspm->card->dev,
5718e3a471d6STakashi Iwai 				 "snd_hdspm_channel_info: input channel %d mapped out\n",
571910513142STakashi Iwai 				 channel);
57200dca1793SAdrian Knoth 			return -EINVAL;
57210dca1793SAdrian Knoth 		}
57220dca1793SAdrian Knoth 
572310513142STakashi Iwai 		info->offset = hdspm->channel_map_in[channel] *
57240dca1793SAdrian Knoth 			HDSPM_CHANNEL_BUFFER_BYTES;
57250dca1793SAdrian Knoth 	}
57260dca1793SAdrian Knoth 
5727763f356cSTakashi Iwai 	info->first = 0;
5728763f356cSTakashi Iwai 	info->step = 32;
5729763f356cSTakashi Iwai 	return 0;
5730763f356cSTakashi Iwai }
5731763f356cSTakashi Iwai 
57320dca1793SAdrian Knoth 
snd_hdspm_ioctl(struct snd_pcm_substream * substream,unsigned int cmd,void * arg)573398274f07STakashi Iwai static int snd_hdspm_ioctl(struct snd_pcm_substream *substream,
5734763f356cSTakashi Iwai 		unsigned int cmd, void *arg)
5735763f356cSTakashi Iwai {
5736763f356cSTakashi Iwai 	switch (cmd) {
5737763f356cSTakashi Iwai 	case SNDRV_PCM_IOCTL1_RESET:
5738763f356cSTakashi Iwai 		return snd_hdspm_reset(substream);
5739763f356cSTakashi Iwai 
5740763f356cSTakashi Iwai 	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
5741763f356cSTakashi Iwai 		{
574298274f07STakashi Iwai 			struct snd_pcm_channel_info *info = arg;
5743763f356cSTakashi Iwai 			return snd_hdspm_channel_info(substream, info);
5744763f356cSTakashi Iwai 		}
5745763f356cSTakashi Iwai 	default:
5746763f356cSTakashi Iwai 		break;
5747763f356cSTakashi Iwai 	}
5748763f356cSTakashi Iwai 
5749763f356cSTakashi Iwai 	return snd_pcm_lib_ioctl(substream, cmd, arg);
5750763f356cSTakashi Iwai }
5751763f356cSTakashi Iwai 
snd_hdspm_trigger(struct snd_pcm_substream * substream,int cmd)575298274f07STakashi Iwai static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
5753763f356cSTakashi Iwai {
575498274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
575598274f07STakashi Iwai 	struct snd_pcm_substream *other;
5756763f356cSTakashi Iwai 	int running;
5757763f356cSTakashi Iwai 
5758763f356cSTakashi Iwai 	spin_lock(&hdspm->lock);
5759763f356cSTakashi Iwai 	running = hdspm->running;
5760763f356cSTakashi Iwai 	switch (cmd) {
5761763f356cSTakashi Iwai 	case SNDRV_PCM_TRIGGER_START:
5762763f356cSTakashi Iwai 		running |= 1 << substream->stream;
5763763f356cSTakashi Iwai 		break;
5764763f356cSTakashi Iwai 	case SNDRV_PCM_TRIGGER_STOP:
5765763f356cSTakashi Iwai 		running &= ~(1 << substream->stream);
5766763f356cSTakashi Iwai 		break;
5767763f356cSTakashi Iwai 	default:
5768763f356cSTakashi Iwai 		snd_BUG();
5769763f356cSTakashi Iwai 		spin_unlock(&hdspm->lock);
5770763f356cSTakashi Iwai 		return -EINVAL;
5771763f356cSTakashi Iwai 	}
5772763f356cSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5773763f356cSTakashi Iwai 		other = hdspm->capture_substream;
5774763f356cSTakashi Iwai 	else
5775763f356cSTakashi Iwai 		other = hdspm->playback_substream;
5776763f356cSTakashi Iwai 
5777763f356cSTakashi Iwai 	if (other) {
577898274f07STakashi Iwai 		struct snd_pcm_substream *s;
5779ef991b95STakashi Iwai 		snd_pcm_group_for_each_entry(s, substream) {
5780763f356cSTakashi Iwai 			if (s == other) {
5781763f356cSTakashi Iwai 				snd_pcm_trigger_done(s, substream);
5782763f356cSTakashi Iwai 				if (cmd == SNDRV_PCM_TRIGGER_START)
5783763f356cSTakashi Iwai 					running |= 1 << s->stream;
5784763f356cSTakashi Iwai 				else
5785763f356cSTakashi Iwai 					running &= ~(1 << s->stream);
5786763f356cSTakashi Iwai 				goto _ok;
5787763f356cSTakashi Iwai 			}
5788763f356cSTakashi Iwai 		}
5789763f356cSTakashi Iwai 		if (cmd == SNDRV_PCM_TRIGGER_START) {
5790763f356cSTakashi Iwai 			if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
5791763f356cSTakashi Iwai 					&& substream->stream ==
5792763f356cSTakashi Iwai 					SNDRV_PCM_STREAM_CAPTURE)
5793763f356cSTakashi Iwai 				hdspm_silence_playback(hdspm);
5794763f356cSTakashi Iwai 		} else {
5795763f356cSTakashi Iwai 			if (running &&
5796763f356cSTakashi Iwai 				substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5797763f356cSTakashi Iwai 				hdspm_silence_playback(hdspm);
5798763f356cSTakashi Iwai 		}
5799763f356cSTakashi Iwai 	} else {
5800763f356cSTakashi Iwai 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
5801763f356cSTakashi Iwai 			hdspm_silence_playback(hdspm);
5802763f356cSTakashi Iwai 	}
5803763f356cSTakashi Iwai _ok:
5804763f356cSTakashi Iwai 	snd_pcm_trigger_done(substream, substream);
5805763f356cSTakashi Iwai 	if (!hdspm->running && running)
5806763f356cSTakashi Iwai 		hdspm_start_audio(hdspm);
5807763f356cSTakashi Iwai 	else if (hdspm->running && !running)
5808763f356cSTakashi Iwai 		hdspm_stop_audio(hdspm);
5809763f356cSTakashi Iwai 	hdspm->running = running;
5810763f356cSTakashi Iwai 	spin_unlock(&hdspm->lock);
5811763f356cSTakashi Iwai 
5812763f356cSTakashi Iwai 	return 0;
5813763f356cSTakashi Iwai }
5814763f356cSTakashi Iwai 
snd_hdspm_prepare(struct snd_pcm_substream * substream)581598274f07STakashi Iwai static int snd_hdspm_prepare(struct snd_pcm_substream *substream)
5816763f356cSTakashi Iwai {
5817763f356cSTakashi Iwai 	return 0;
5818763f356cSTakashi Iwai }
5819763f356cSTakashi Iwai 
5820c1c3981fSTakashi Iwai static const struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
5821763f356cSTakashi Iwai 	.info = (SNDRV_PCM_INFO_MMAP |
5822763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_MMAP_VALID |
5823763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_NONINTERLEAVED |
5824763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_DOUBLE),
5825763f356cSTakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
5826763f356cSTakashi Iwai 	.rates = (SNDRV_PCM_RATE_32000 |
5827763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_44100 |
5828763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_48000 |
5829763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_64000 |
58303cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
58313cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 ),
5832763f356cSTakashi Iwai 	.rate_min = 32000,
58333cee5a60SRemy Bruno 	.rate_max = 192000,
5834763f356cSTakashi Iwai 	.channels_min = 1,
5835763f356cSTakashi Iwai 	.channels_max = HDSPM_MAX_CHANNELS,
5836763f356cSTakashi Iwai 	.buffer_bytes_max =
5837763f356cSTakashi Iwai 	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
58381b6fa108SAdrian Knoth 	.period_bytes_min = (32 * 4),
583952e6fb48STakashi Iwai 	.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
5840763f356cSTakashi Iwai 	.periods_min = 2,
58410dca1793SAdrian Knoth 	.periods_max = 512,
5842763f356cSTakashi Iwai 	.fifo_size = 0
5843763f356cSTakashi Iwai };
5844763f356cSTakashi Iwai 
5845c1c3981fSTakashi Iwai static const struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
5846763f356cSTakashi Iwai 	.info = (SNDRV_PCM_INFO_MMAP |
5847763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_MMAP_VALID |
5848763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_NONINTERLEAVED |
5849763f356cSTakashi Iwai 		 SNDRV_PCM_INFO_SYNC_START),
5850763f356cSTakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
5851763f356cSTakashi Iwai 	.rates = (SNDRV_PCM_RATE_32000 |
5852763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_44100 |
5853763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_48000 |
5854763f356cSTakashi Iwai 		  SNDRV_PCM_RATE_64000 |
58553cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
58563cee5a60SRemy Bruno 		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
5857763f356cSTakashi Iwai 	.rate_min = 32000,
58583cee5a60SRemy Bruno 	.rate_max = 192000,
5859763f356cSTakashi Iwai 	.channels_min = 1,
5860763f356cSTakashi Iwai 	.channels_max = HDSPM_MAX_CHANNELS,
5861763f356cSTakashi Iwai 	.buffer_bytes_max =
5862763f356cSTakashi Iwai 	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
58631b6fa108SAdrian Knoth 	.period_bytes_min = (32 * 4),
586452e6fb48STakashi Iwai 	.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
5865763f356cSTakashi Iwai 	.periods_min = 2,
58660dca1793SAdrian Knoth 	.periods_max = 512,
5867763f356cSTakashi Iwai 	.fifo_size = 0
5868763f356cSTakashi Iwai };
5869763f356cSTakashi Iwai 
snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)58700dca1793SAdrian Knoth static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params,
587198274f07STakashi Iwai 					   struct snd_pcm_hw_rule *rule)
5872763f356cSTakashi Iwai {
587398274f07STakashi Iwai 	struct hdspm *hdspm = rule->private;
587498274f07STakashi Iwai 	struct snd_interval *c =
5875763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
587698274f07STakashi Iwai 	struct snd_interval *r =
5877763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5878763f356cSTakashi Iwai 
58790dca1793SAdrian Knoth 	if (r->min > 96000 && r->max <= 192000) {
588098274f07STakashi Iwai 		struct snd_interval t = {
58810dca1793SAdrian Knoth 			.min = hdspm->qs_in_channels,
58820dca1793SAdrian Knoth 			.max = hdspm->qs_in_channels,
58830dca1793SAdrian Knoth 			.integer = 1,
58840dca1793SAdrian Knoth 		};
58850dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
58860dca1793SAdrian Knoth 	} else if (r->min > 48000 && r->max <= 96000) {
58870dca1793SAdrian Knoth 		struct snd_interval t = {
58880dca1793SAdrian Knoth 			.min = hdspm->ds_in_channels,
58890dca1793SAdrian Knoth 			.max = hdspm->ds_in_channels,
5890763f356cSTakashi Iwai 			.integer = 1,
5891763f356cSTakashi Iwai 		};
5892763f356cSTakashi Iwai 		return snd_interval_refine(c, &t);
5893763f356cSTakashi Iwai 	} else if (r->max < 64000) {
589498274f07STakashi Iwai 		struct snd_interval t = {
58950dca1793SAdrian Knoth 			.min = hdspm->ss_in_channels,
58960dca1793SAdrian Knoth 			.max = hdspm->ss_in_channels,
5897763f356cSTakashi Iwai 			.integer = 1,
5898763f356cSTakashi Iwai 		};
5899763f356cSTakashi Iwai 		return snd_interval_refine(c, &t);
5900763f356cSTakashi Iwai 	}
59010dca1793SAdrian Knoth 
5902763f356cSTakashi Iwai 	return 0;
5903763f356cSTakashi Iwai }
5904763f356cSTakashi Iwai 
snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)59050dca1793SAdrian Knoth static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params,
590698274f07STakashi Iwai 					   struct snd_pcm_hw_rule * rule)
5907763f356cSTakashi Iwai {
590898274f07STakashi Iwai 	struct hdspm *hdspm = rule->private;
590998274f07STakashi Iwai 	struct snd_interval *c =
5910763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
591198274f07STakashi Iwai 	struct snd_interval *r =
5912763f356cSTakashi Iwai 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5913763f356cSTakashi Iwai 
59140dca1793SAdrian Knoth 	if (r->min > 96000 && r->max <= 192000) {
59150dca1793SAdrian Knoth 		struct snd_interval t = {
59160dca1793SAdrian Knoth 			.min = hdspm->qs_out_channels,
59170dca1793SAdrian Knoth 			.max = hdspm->qs_out_channels,
59180dca1793SAdrian Knoth 			.integer = 1,
59190dca1793SAdrian Knoth 		};
59200dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
59210dca1793SAdrian Knoth 	} else if (r->min > 48000 && r->max <= 96000) {
59220dca1793SAdrian Knoth 		struct snd_interval t = {
59230dca1793SAdrian Knoth 			.min = hdspm->ds_out_channels,
59240dca1793SAdrian Knoth 			.max = hdspm->ds_out_channels,
59250dca1793SAdrian Knoth 			.integer = 1,
59260dca1793SAdrian Knoth 		};
59270dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
59280dca1793SAdrian Knoth 	} else if (r->max < 64000) {
59290dca1793SAdrian Knoth 		struct snd_interval t = {
59300dca1793SAdrian Knoth 			.min = hdspm->ss_out_channels,
59310dca1793SAdrian Knoth 			.max = hdspm->ss_out_channels,
59320dca1793SAdrian Knoth 			.integer = 1,
59330dca1793SAdrian Knoth 		};
59340dca1793SAdrian Knoth 		return snd_interval_refine(c, &t);
59350dca1793SAdrian Knoth 	} else {
59360dca1793SAdrian Knoth 	}
59370dca1793SAdrian Knoth 	return 0;
59380dca1793SAdrian Knoth }
59390dca1793SAdrian Knoth 
snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)59400dca1793SAdrian Knoth static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params,
59410dca1793SAdrian Knoth 					   struct snd_pcm_hw_rule * rule)
59420dca1793SAdrian Knoth {
59430dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
59440dca1793SAdrian Knoth 	struct snd_interval *c =
59450dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
59460dca1793SAdrian Knoth 	struct snd_interval *r =
59470dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
59480dca1793SAdrian Knoth 
59490dca1793SAdrian Knoth 	if (c->min >= hdspm->ss_in_channels) {
595098274f07STakashi Iwai 		struct snd_interval t = {
5951763f356cSTakashi Iwai 			.min = 32000,
5952763f356cSTakashi Iwai 			.max = 48000,
5953763f356cSTakashi Iwai 			.integer = 1,
5954763f356cSTakashi Iwai 		};
5955763f356cSTakashi Iwai 		return snd_interval_refine(r, &t);
59560dca1793SAdrian Knoth 	} else if (c->max <= hdspm->qs_in_channels) {
59570dca1793SAdrian Knoth 		struct snd_interval t = {
59580dca1793SAdrian Knoth 			.min = 128000,
59590dca1793SAdrian Knoth 			.max = 192000,
59600dca1793SAdrian Knoth 			.integer = 1,
59610dca1793SAdrian Knoth 		};
59620dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
59630dca1793SAdrian Knoth 	} else if (c->max <= hdspm->ds_in_channels) {
596498274f07STakashi Iwai 		struct snd_interval t = {
5965763f356cSTakashi Iwai 			.min = 64000,
5966763f356cSTakashi Iwai 			.max = 96000,
5967763f356cSTakashi Iwai 			.integer = 1,
5968763f356cSTakashi Iwai 		};
5969763f356cSTakashi Iwai 		return snd_interval_refine(r, &t);
5970763f356cSTakashi Iwai 	}
59710dca1793SAdrian Knoth 
59720dca1793SAdrian Knoth 	return 0;
59730dca1793SAdrian Knoth }
snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)59740dca1793SAdrian Knoth static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params,
59750dca1793SAdrian Knoth 					   struct snd_pcm_hw_rule *rule)
59760dca1793SAdrian Knoth {
59770dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
59780dca1793SAdrian Knoth 	struct snd_interval *c =
59790dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
59800dca1793SAdrian Knoth 	struct snd_interval *r =
59810dca1793SAdrian Knoth 	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
59820dca1793SAdrian Knoth 
59830dca1793SAdrian Knoth 	if (c->min >= hdspm->ss_out_channels) {
59840dca1793SAdrian Knoth 		struct snd_interval t = {
59850dca1793SAdrian Knoth 			.min = 32000,
59860dca1793SAdrian Knoth 			.max = 48000,
59870dca1793SAdrian Knoth 			.integer = 1,
59880dca1793SAdrian Knoth 		};
59890dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
59900dca1793SAdrian Knoth 	} else if (c->max <= hdspm->qs_out_channels) {
59910dca1793SAdrian Knoth 		struct snd_interval t = {
59920dca1793SAdrian Knoth 			.min = 128000,
59930dca1793SAdrian Knoth 			.max = 192000,
59940dca1793SAdrian Knoth 			.integer = 1,
59950dca1793SAdrian Knoth 		};
59960dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
59970dca1793SAdrian Knoth 	} else if (c->max <= hdspm->ds_out_channels) {
59980dca1793SAdrian Knoth 		struct snd_interval t = {
59990dca1793SAdrian Knoth 			.min = 64000,
60000dca1793SAdrian Knoth 			.max = 96000,
60010dca1793SAdrian Knoth 			.integer = 1,
60020dca1793SAdrian Knoth 		};
60030dca1793SAdrian Knoth 		return snd_interval_refine(r, &t);
60040dca1793SAdrian Knoth 	}
60050dca1793SAdrian Knoth 
6006763f356cSTakashi Iwai 	return 0;
6007763f356cSTakashi Iwai }
6008763f356cSTakashi Iwai 
snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)60090dca1793SAdrian Knoth static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params,
6010ffb2c3c0SRemy Bruno 				      struct snd_pcm_hw_rule *rule)
6011ffb2c3c0SRemy Bruno {
6012ffb2c3c0SRemy Bruno 	unsigned int list[3];
6013ffb2c3c0SRemy Bruno 	struct hdspm *hdspm = rule->private;
6014ffb2c3c0SRemy Bruno 	struct snd_interval *c = hw_param_interval(params,
6015ffb2c3c0SRemy Bruno 			SNDRV_PCM_HW_PARAM_CHANNELS);
60160dca1793SAdrian Knoth 
60170dca1793SAdrian Knoth 	list[0] = hdspm->qs_in_channels;
60180dca1793SAdrian Knoth 	list[1] = hdspm->ds_in_channels;
60190dca1793SAdrian Knoth 	list[2] = hdspm->ss_in_channels;
6020ffb2c3c0SRemy Bruno 	return snd_interval_list(c, 3, list, 0);
6021ffb2c3c0SRemy Bruno }
60220dca1793SAdrian Knoth 
snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)60230dca1793SAdrian Knoth static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params,
60240dca1793SAdrian Knoth 				      struct snd_pcm_hw_rule *rule)
60250dca1793SAdrian Knoth {
60260dca1793SAdrian Knoth 	unsigned int list[3];
60270dca1793SAdrian Knoth 	struct hdspm *hdspm = rule->private;
60280dca1793SAdrian Knoth 	struct snd_interval *c = hw_param_interval(params,
60290dca1793SAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS);
60300dca1793SAdrian Knoth 
60310dca1793SAdrian Knoth 	list[0] = hdspm->qs_out_channels;
60320dca1793SAdrian Knoth 	list[1] = hdspm->ds_out_channels;
60330dca1793SAdrian Knoth 	list[2] = hdspm->ss_out_channels;
60340dca1793SAdrian Knoth 	return snd_interval_list(c, 3, list, 0);
6035ffb2c3c0SRemy Bruno }
6036ffb2c3c0SRemy Bruno 
6037ffb2c3c0SRemy Bruno 
6038bdf84db7STakashi Iwai static const unsigned int hdspm_aes32_sample_rates[] = {
6039ef5fa1a4STakashi Iwai 	32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
6040ef5fa1a4STakashi Iwai };
6041ffb2c3c0SRemy Bruno 
6042bdf84db7STakashi Iwai static const struct snd_pcm_hw_constraint_list
6043ef5fa1a4STakashi Iwai hdspm_hw_constraints_aes32_sample_rates = {
6044ffb2c3c0SRemy Bruno 	.count = ARRAY_SIZE(hdspm_aes32_sample_rates),
6045ffb2c3c0SRemy Bruno 	.list = hdspm_aes32_sample_rates,
6046ffb2c3c0SRemy Bruno 	.mask = 0
6047ffb2c3c0SRemy Bruno };
6048ffb2c3c0SRemy Bruno 
snd_hdspm_open(struct snd_pcm_substream * substream)60495ecc5dc7SAdrian Knoth static int snd_hdspm_open(struct snd_pcm_substream *substream)
6050763f356cSTakashi Iwai {
605198274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
605298274f07STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
60535ecc5dc7SAdrian Knoth 	bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
6054763f356cSTakashi Iwai 
6055763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
6056763f356cSTakashi Iwai 	snd_pcm_set_sync(substream);
60575ecc5dc7SAdrian Knoth 	runtime->hw = (playback) ? snd_hdspm_playback_subinfo :
60585ecc5dc7SAdrian Knoth 		snd_hdspm_capture_subinfo;
6059763f356cSTakashi Iwai 
60605ecc5dc7SAdrian Knoth 	if (playback) {
6061da2ea374SMarkus Elfring 		if (!hdspm->capture_substream)
6062763f356cSTakashi Iwai 			hdspm_stop_audio(hdspm);
6063763f356cSTakashi Iwai 
6064763f356cSTakashi Iwai 		hdspm->playback_pid = current->pid;
6065763f356cSTakashi Iwai 		hdspm->playback_substream = substream;
60665ecc5dc7SAdrian Knoth 	} else {
6067da2ea374SMarkus Elfring 		if (!hdspm->playback_substream)
60685ecc5dc7SAdrian Knoth 			hdspm_stop_audio(hdspm);
60695ecc5dc7SAdrian Knoth 
60705ecc5dc7SAdrian Knoth 		hdspm->capture_pid = current->pid;
60715ecc5dc7SAdrian Knoth 		hdspm->capture_substream = substream;
60725ecc5dc7SAdrian Knoth 	}
6073763f356cSTakashi Iwai 
6074763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
6075763f356cSTakashi Iwai 
6076763f356cSTakashi Iwai 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
6077d877681dSTakashi Iwai 	snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
6078763f356cSTakashi Iwai 
60790dca1793SAdrian Knoth 	switch (hdspm->io_type) {
60800dca1793SAdrian Knoth 	case AIO:
60810dca1793SAdrian Knoth 	case RayDAT:
6082d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
6083763f356cSTakashi Iwai 					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
6084d877681dSTakashi Iwai 					     32, 4096);
6085d877681dSTakashi Iwai 		/* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
6086b4ffc1beSLars-Peter Clausen 		snd_pcm_hw_constraint_single(runtime,
60870dca1793SAdrian Knoth 					     SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
6088b4ffc1beSLars-Peter Clausen 					     16384);
60890dca1793SAdrian Knoth 		break;
60900dca1793SAdrian Knoth 
60910dca1793SAdrian Knoth 	default:
6092d877681dSTakashi Iwai 		snd_pcm_hw_constraint_minmax(runtime,
60930dca1793SAdrian Knoth 					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
6094d877681dSTakashi Iwai 					     64, 8192);
6095b4ffc1beSLars-Peter Clausen 		snd_pcm_hw_constraint_single(runtime,
6096b4ffc1beSLars-Peter Clausen 					     SNDRV_PCM_HW_PARAM_PERIODS, 2);
6097d877681dSTakashi Iwai 		break;
60980dca1793SAdrian Knoth 	}
60990dca1793SAdrian Knoth 
61000dca1793SAdrian Knoth 	if (AES32 == hdspm->io_type) {
61013fa9e3d2STakashi Iwai 		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
6102ffb2c3c0SRemy Bruno 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
6103ffb2c3c0SRemy Bruno 				&hdspm_hw_constraints_aes32_sample_rates);
6104ffb2c3c0SRemy Bruno 	} else {
6105763f356cSTakashi Iwai 		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
61065ecc5dc7SAdrian Knoth 				(playback ?
61075ecc5dc7SAdrian Knoth 				 snd_hdspm_hw_rule_rate_out_channels :
61085ecc5dc7SAdrian Knoth 				 snd_hdspm_hw_rule_rate_in_channels), hdspm,
6109763f356cSTakashi Iwai 				SNDRV_PCM_HW_PARAM_CHANNELS, -1);
6110ffb2c3c0SRemy Bruno 	}
611188fabbfcSAdrian Knoth 
611288fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
61135ecc5dc7SAdrian Knoth 			(playback ? snd_hdspm_hw_rule_out_channels :
61145ecc5dc7SAdrian Knoth 			 snd_hdspm_hw_rule_in_channels), hdspm,
611588fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_CHANNELS, -1);
611688fabbfcSAdrian Knoth 
611788fabbfcSAdrian Knoth 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
61185ecc5dc7SAdrian Knoth 			(playback ? snd_hdspm_hw_rule_out_channels_rate :
61195ecc5dc7SAdrian Knoth 			 snd_hdspm_hw_rule_in_channels_rate), hdspm,
612088fabbfcSAdrian Knoth 			SNDRV_PCM_HW_PARAM_RATE, -1);
612188fabbfcSAdrian Knoth 
6122763f356cSTakashi Iwai 	return 0;
6123763f356cSTakashi Iwai }
6124763f356cSTakashi Iwai 
snd_hdspm_release(struct snd_pcm_substream * substream)61258b73b867SAdrian Knoth static int snd_hdspm_release(struct snd_pcm_substream *substream)
6126763f356cSTakashi Iwai {
612798274f07STakashi Iwai 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
61288b73b867SAdrian Knoth 	bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
6129763f356cSTakashi Iwai 
6130763f356cSTakashi Iwai 	spin_lock_irq(&hdspm->lock);
6131763f356cSTakashi Iwai 
61328b73b867SAdrian Knoth 	if (playback) {
6133763f356cSTakashi Iwai 		hdspm->playback_pid = -1;
6134763f356cSTakashi Iwai 		hdspm->playback_substream = NULL;
61358b73b867SAdrian Knoth 	} else {
6136763f356cSTakashi Iwai 		hdspm->capture_pid = -1;
6137763f356cSTakashi Iwai 		hdspm->capture_substream = NULL;
61388b73b867SAdrian Knoth 	}
6139763f356cSTakashi Iwai 
6140763f356cSTakashi Iwai 	spin_unlock_irq(&hdspm->lock);
61418b73b867SAdrian Knoth 
6142763f356cSTakashi Iwai 	return 0;
6143763f356cSTakashi Iwai }
6144763f356cSTakashi Iwai 
snd_hdspm_hwdep_dummy_op(struct snd_hwdep * hw,struct file * file)61450dca1793SAdrian Knoth static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
6146763f356cSTakashi Iwai {
61470dca1793SAdrian Knoth 	/* we have nothing to initialize but the call is required */
61480dca1793SAdrian Knoth 	return 0;
61490dca1793SAdrian Knoth }
61500dca1793SAdrian Knoth 
snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw,struct file * file,unsigned int cmd,unsigned long arg)61510dca1793SAdrian Knoth static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
61522ca595abSDan Carpenter 		unsigned int cmd, unsigned long arg)
61530dca1793SAdrian Knoth {
61540dca1793SAdrian Knoth 	void __user *argp = (void __user *)arg;
6155ef5fa1a4STakashi Iwai 	struct hdspm *hdspm = hw->private_data;
615698274f07STakashi Iwai 	struct hdspm_mixer_ioctl mixer;
61570dca1793SAdrian Knoth 	struct hdspm_config info;
61580dca1793SAdrian Knoth 	struct hdspm_status status;
615998274f07STakashi Iwai 	struct hdspm_version hdspm_version;
6160730a5865SJaroslav Kysela 	struct hdspm_peak_rms *levels;
61610dca1793SAdrian Knoth 	struct hdspm_ltc ltc;
61620dca1793SAdrian Knoth 	unsigned int statusregister;
61630dca1793SAdrian Knoth 	long unsigned int s;
61640dca1793SAdrian Knoth 	int i = 0;
6165763f356cSTakashi Iwai 
6166763f356cSTakashi Iwai 	switch (cmd) {
6167763f356cSTakashi Iwai 
6168763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS:
6169730a5865SJaroslav Kysela 		levels = &hdspm->peak_rms;
61700dca1793SAdrian Knoth 		for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
6171730a5865SJaroslav Kysela 			levels->input_peaks[i] =
61720dca1793SAdrian Knoth 				readl(hdspm->iobase +
61730dca1793SAdrian Knoth 						HDSPM_MADI_INPUT_PEAK + i*4);
6174730a5865SJaroslav Kysela 			levels->playback_peaks[i] =
61750dca1793SAdrian Knoth 				readl(hdspm->iobase +
61760dca1793SAdrian Knoth 						HDSPM_MADI_PLAYBACK_PEAK + i*4);
6177730a5865SJaroslav Kysela 			levels->output_peaks[i] =
61780dca1793SAdrian Knoth 				readl(hdspm->iobase +
61790dca1793SAdrian Knoth 						HDSPM_MADI_OUTPUT_PEAK + i*4);
61800dca1793SAdrian Knoth 
6181730a5865SJaroslav Kysela 			levels->input_rms[i] =
61820dca1793SAdrian Knoth 				((uint64_t) readl(hdspm->iobase +
61830dca1793SAdrian Knoth 					HDSPM_MADI_INPUT_RMS_H + i*4) << 32) |
61840dca1793SAdrian Knoth 				(uint64_t) readl(hdspm->iobase +
61850dca1793SAdrian Knoth 						HDSPM_MADI_INPUT_RMS_L + i*4);
6186730a5865SJaroslav Kysela 			levels->playback_rms[i] =
61870dca1793SAdrian Knoth 				((uint64_t)readl(hdspm->iobase +
61880dca1793SAdrian Knoth 					HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) |
61890dca1793SAdrian Knoth 				(uint64_t)readl(hdspm->iobase +
61900dca1793SAdrian Knoth 					HDSPM_MADI_PLAYBACK_RMS_L + i*4);
6191730a5865SJaroslav Kysela 			levels->output_rms[i] =
61920dca1793SAdrian Knoth 				((uint64_t)readl(hdspm->iobase +
61930dca1793SAdrian Knoth 					HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) |
61940dca1793SAdrian Knoth 				(uint64_t)readl(hdspm->iobase +
61950dca1793SAdrian Knoth 						HDSPM_MADI_OUTPUT_RMS_L + i*4);
61960dca1793SAdrian Knoth 		}
61970dca1793SAdrian Knoth 
61980dca1793SAdrian Knoth 		if (hdspm->system_sample_rate > 96000) {
6199730a5865SJaroslav Kysela 			levels->speed = qs;
62000dca1793SAdrian Knoth 		} else if (hdspm->system_sample_rate > 48000) {
6201730a5865SJaroslav Kysela 			levels->speed = ds;
62020dca1793SAdrian Knoth 		} else {
6203730a5865SJaroslav Kysela 			levels->speed = ss;
62040dca1793SAdrian Knoth 		}
6205730a5865SJaroslav Kysela 		levels->status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
62060dca1793SAdrian Knoth 
62077dfec507SMarkus Elfring 		s = copy_to_user(argp, levels, sizeof(*levels));
62080dca1793SAdrian Knoth 		if (0 != s) {
6209e3a471d6STakashi Iwai 			/* dev_err(hdspm->card->dev, "copy_to_user(.., .., %lu): %lu
62100dca1793SAdrian Knoth 			 [Levels]\n", sizeof(struct hdspm_peak_rms), s);
6211ef5fa1a4STakashi Iwai 			 */
6212763f356cSTakashi Iwai 			return -EFAULT;
62130dca1793SAdrian Knoth 		}
62140dca1793SAdrian Knoth 		break;
62150dca1793SAdrian Knoth 
62160dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_LTC:
62170dca1793SAdrian Knoth 		ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
62180dca1793SAdrian Knoth 		i = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
62190dca1793SAdrian Knoth 		if (i & HDSPM_TCO1_LTC_Input_valid) {
62200dca1793SAdrian Knoth 			switch (i & (HDSPM_TCO1_LTC_Format_LSB |
62210dca1793SAdrian Knoth 				HDSPM_TCO1_LTC_Format_MSB)) {
62220dca1793SAdrian Knoth 			case 0:
62230dca1793SAdrian Knoth 				ltc.format = fps_24;
62240dca1793SAdrian Knoth 				break;
62250dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_LSB:
62260dca1793SAdrian Knoth 				ltc.format = fps_25;
62270dca1793SAdrian Knoth 				break;
62280dca1793SAdrian Knoth 			case HDSPM_TCO1_LTC_Format_MSB:
62290dca1793SAdrian Knoth 				ltc.format = fps_2997;
62300dca1793SAdrian Knoth 				break;
62310dca1793SAdrian Knoth 			default:
623217d2f008SAdrian Knoth 				ltc.format = fps_30;
62330dca1793SAdrian Knoth 				break;
62340dca1793SAdrian Knoth 			}
62350dca1793SAdrian Knoth 			if (i & HDSPM_TCO1_set_drop_frame_flag) {
62360dca1793SAdrian Knoth 				ltc.frame = drop_frame;
62370dca1793SAdrian Knoth 			} else {
62380dca1793SAdrian Knoth 				ltc.frame = full_frame;
62390dca1793SAdrian Knoth 			}
62400dca1793SAdrian Knoth 		} else {
62410dca1793SAdrian Knoth 			ltc.format = format_invalid;
62420dca1793SAdrian Knoth 			ltc.frame = frame_invalid;
62430dca1793SAdrian Knoth 		}
62440dca1793SAdrian Knoth 		if (i & HDSPM_TCO1_Video_Input_Format_NTSC) {
62450dca1793SAdrian Knoth 			ltc.input_format = ntsc;
62460dca1793SAdrian Knoth 		} else if (i & HDSPM_TCO1_Video_Input_Format_PAL) {
62470dca1793SAdrian Knoth 			ltc.input_format = pal;
62480dca1793SAdrian Knoth 		} else {
62490dca1793SAdrian Knoth 			ltc.input_format = no_video;
62500dca1793SAdrian Knoth 		}
62510dca1793SAdrian Knoth 
62527dfec507SMarkus Elfring 		s = copy_to_user(argp, &ltc, sizeof(ltc));
62530dca1793SAdrian Knoth 		if (0 != s) {
62540dca1793SAdrian Knoth 			/*
6255e3a471d6STakashi Iwai 			  dev_err(hdspm->card->dev, "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */
62560dca1793SAdrian Knoth 			return -EFAULT;
62570dca1793SAdrian Knoth 		}
6258763f356cSTakashi Iwai 
6259763f356cSTakashi Iwai 		break;
6260763f356cSTakashi Iwai 
62610dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_CONFIG:
6262763f356cSTakashi Iwai 
62634ab69a2bSAdrian Knoth 		memset(&info, 0, sizeof(info));
6264763f356cSTakashi Iwai 		spin_lock_irq(&hdspm->lock);
6265ef5fa1a4STakashi Iwai 		info.pref_sync_ref = hdspm_pref_sync_ref(hdspm);
6266ef5fa1a4STakashi Iwai 		info.wordclock_sync_check = hdspm_wc_sync_check(hdspm);
6267763f356cSTakashi Iwai 
6268763f356cSTakashi Iwai 		info.system_sample_rate = hdspm->system_sample_rate;
6269763f356cSTakashi Iwai 		info.autosync_sample_rate =
6270763f356cSTakashi Iwai 			hdspm_external_sample_rate(hdspm);
6271ef5fa1a4STakashi Iwai 		info.system_clock_mode = hdspm_system_clock_mode(hdspm);
6272ef5fa1a4STakashi Iwai 		info.clock_source = hdspm_clock_source(hdspm);
6273ef5fa1a4STakashi Iwai 		info.autosync_ref = hdspm_autosync_ref(hdspm);
6274c9e1668cSAdrian Knoth 		info.line_out = hdspm_toggle_setting(hdspm, HDSPM_LineOut);
6275763f356cSTakashi Iwai 		info.passthru = 0;
6276763f356cSTakashi Iwai 		spin_unlock_irq(&hdspm->lock);
62772ca595abSDan Carpenter 		if (copy_to_user(argp, &info, sizeof(info)))
6278763f356cSTakashi Iwai 			return -EFAULT;
6279763f356cSTakashi Iwai 		break;
6280763f356cSTakashi Iwai 
62810dca1793SAdrian Knoth 	case SNDRV_HDSPM_IOCTL_GET_STATUS:
6282643d6bbbSDan Carpenter 		memset(&status, 0, sizeof(status));
6283643d6bbbSDan Carpenter 
62840dca1793SAdrian Knoth 		status.card_type = hdspm->io_type;
62850dca1793SAdrian Knoth 
62860dca1793SAdrian Knoth 		status.autosync_source = hdspm_autosync_ref(hdspm);
62870dca1793SAdrian Knoth 
62880dca1793SAdrian Knoth 		status.card_clock = 110069313433624ULL;
62890dca1793SAdrian Knoth 		status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
62900dca1793SAdrian Knoth 
62910dca1793SAdrian Knoth 		switch (hdspm->io_type) {
62920dca1793SAdrian Knoth 		case MADI:
62930dca1793SAdrian Knoth 		case MADIface:
62940dca1793SAdrian Knoth 			status.card_specific.madi.sync_wc =
62950dca1793SAdrian Knoth 				hdspm_wc_sync_check(hdspm);
62960dca1793SAdrian Knoth 			status.card_specific.madi.sync_madi =
62970dca1793SAdrian Knoth 				hdspm_madi_sync_check(hdspm);
62980dca1793SAdrian Knoth 			status.card_specific.madi.sync_tco =
62990dca1793SAdrian Knoth 				hdspm_tco_sync_check(hdspm);
63000dca1793SAdrian Knoth 			status.card_specific.madi.sync_in =
63010dca1793SAdrian Knoth 				hdspm_sync_in_sync_check(hdspm);
63020dca1793SAdrian Knoth 
63030dca1793SAdrian Knoth 			statusregister =
63040dca1793SAdrian Knoth 				hdspm_read(hdspm, HDSPM_statusRegister);
63050dca1793SAdrian Knoth 			status.card_specific.madi.madi_input =
63060dca1793SAdrian Knoth 				(statusregister & HDSPM_AB_int) ? 1 : 0;
63070dca1793SAdrian Knoth 			status.card_specific.madi.channel_format =
63089e6ff520SAdrian Knoth 				(statusregister & HDSPM_RX_64ch) ? 1 : 0;
63090dca1793SAdrian Knoth 			/* TODO: Mac driver sets it when f_s>48kHz */
63100dca1793SAdrian Knoth 			status.card_specific.madi.frame_format = 0;
6311704cbc4eSGustavo A. R. Silva 			break;
63120dca1793SAdrian Knoth 
63130dca1793SAdrian Knoth 		default:
63140dca1793SAdrian Knoth 			break;
63150dca1793SAdrian Knoth 		}
63160dca1793SAdrian Knoth 
63172ca595abSDan Carpenter 		if (copy_to_user(argp, &status, sizeof(status)))
63180dca1793SAdrian Knoth 			return -EFAULT;
63190dca1793SAdrian Knoth 
63200dca1793SAdrian Knoth 
63210dca1793SAdrian Knoth 		break;
63220dca1793SAdrian Knoth 
6323763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_VERSION:
6324643d6bbbSDan Carpenter 		memset(&hdspm_version, 0, sizeof(hdspm_version));
6325643d6bbbSDan Carpenter 
63260dca1793SAdrian Knoth 		hdspm_version.card_type = hdspm->io_type;
632775b1a8f9SJoe Perches 		strscpy(hdspm_version.cardname, hdspm->card_name,
63280dca1793SAdrian Knoth 				sizeof(hdspm_version.cardname));
63297d53a631SAdrian Knoth 		hdspm_version.serial = hdspm->serial;
6330763f356cSTakashi Iwai 		hdspm_version.firmware_rev = hdspm->firmware_rev;
63310dca1793SAdrian Knoth 		hdspm_version.addons = 0;
63320dca1793SAdrian Knoth 		if (hdspm->tco)
63330dca1793SAdrian Knoth 			hdspm_version.addons |= HDSPM_ADDON_TCO;
63340dca1793SAdrian Knoth 
63352ca595abSDan Carpenter 		if (copy_to_user(argp, &hdspm_version,
6336763f356cSTakashi Iwai 					sizeof(hdspm_version)))
6337763f356cSTakashi Iwai 			return -EFAULT;
6338763f356cSTakashi Iwai 		break;
6339763f356cSTakashi Iwai 
6340763f356cSTakashi Iwai 	case SNDRV_HDSPM_IOCTL_GET_MIXER:
63412ca595abSDan Carpenter 		if (copy_from_user(&mixer, argp, sizeof(mixer)))
6342763f356cSTakashi Iwai 			return -EFAULT;
6343ef5fa1a4STakashi Iwai 		if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer,
63447dfec507SMarkus Elfring 				 sizeof(*mixer.mixer)))
6345763f356cSTakashi Iwai 			return -EFAULT;
6346763f356cSTakashi Iwai 		break;
6347763f356cSTakashi Iwai 
6348763f356cSTakashi Iwai 	default:
6349763f356cSTakashi Iwai 		return -EINVAL;
6350763f356cSTakashi Iwai 	}
6351763f356cSTakashi Iwai 	return 0;
6352763f356cSTakashi Iwai }
6353763f356cSTakashi Iwai 
63546769e988SJulia Lawall static const struct snd_pcm_ops snd_hdspm_ops = {
63555ecc5dc7SAdrian Knoth 	.open = snd_hdspm_open,
63568b73b867SAdrian Knoth 	.close = snd_hdspm_release,
6357763f356cSTakashi Iwai 	.ioctl = snd_hdspm_ioctl,
6358763f356cSTakashi Iwai 	.hw_params = snd_hdspm_hw_params,
6359763f356cSTakashi Iwai 	.hw_free = snd_hdspm_hw_free,
6360763f356cSTakashi Iwai 	.prepare = snd_hdspm_prepare,
6361763f356cSTakashi Iwai 	.trigger = snd_hdspm_trigger,
6362763f356cSTakashi Iwai 	.pointer = snd_hdspm_hw_pointer,
6363763f356cSTakashi Iwai };
6364763f356cSTakashi Iwai 
snd_hdspm_create_hwdep(struct snd_card * card,struct hdspm * hdspm)6365e23e7a14SBill Pemberton static int snd_hdspm_create_hwdep(struct snd_card *card,
636698274f07STakashi Iwai 				  struct hdspm *hdspm)
6367763f356cSTakashi Iwai {
636898274f07STakashi Iwai 	struct snd_hwdep *hw;
6369763f356cSTakashi Iwai 	int err;
6370763f356cSTakashi Iwai 
6371ef5fa1a4STakashi Iwai 	err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw);
6372ef5fa1a4STakashi Iwai 	if (err < 0)
6373763f356cSTakashi Iwai 		return err;
6374763f356cSTakashi Iwai 
6375763f356cSTakashi Iwai 	hdspm->hwdep = hw;
6376763f356cSTakashi Iwai 	hw->private_data = hdspm;
6377763f356cSTakashi Iwai 	strcpy(hw->name, "HDSPM hwdep interface");
6378763f356cSTakashi Iwai 
63790dca1793SAdrian Knoth 	hw->ops.open = snd_hdspm_hwdep_dummy_op;
6380763f356cSTakashi Iwai 	hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
63818de5d6f1SAdrian Knoth 	hw->ops.ioctl_compat = snd_hdspm_hwdep_ioctl;
63820dca1793SAdrian Knoth 	hw->ops.release = snd_hdspm_hwdep_dummy_op;
6383763f356cSTakashi Iwai 
6384763f356cSTakashi Iwai 	return 0;
6385763f356cSTakashi Iwai }
6386763f356cSTakashi Iwai 
6387763f356cSTakashi Iwai 
6388763f356cSTakashi Iwai /*------------------------------------------------------------
6389763f356cSTakashi Iwai    memory interface
6390763f356cSTakashi Iwai  ------------------------------------------------------------*/
snd_hdspm_preallocate_memory(struct hdspm * hdspm)6391e23e7a14SBill Pemberton static int snd_hdspm_preallocate_memory(struct hdspm *hdspm)
6392763f356cSTakashi Iwai {
639398274f07STakashi Iwai 	struct snd_pcm *pcm;
6394763f356cSTakashi Iwai 	size_t wanted;
6395763f356cSTakashi Iwai 
6396763f356cSTakashi Iwai 	pcm = hdspm->pcm;
6397763f356cSTakashi Iwai 
63983cee5a60SRemy Bruno 	wanted = HDSPM_DMA_AREA_BYTES;
6399763f356cSTakashi Iwai 
64005116b94aSTakashi Iwai 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
64011a810436STakashi Iwai 					      &hdspm->pci->dev,
64025116b94aSTakashi Iwai 					      wanted, wanted);
64035116b94aSTakashi Iwai 	dev_dbg(hdspm->card->dev, " Preallocated %zd Bytes\n", wanted);
6404763f356cSTakashi Iwai 	return 0;
6405763f356cSTakashi Iwai }
6406763f356cSTakashi Iwai 
6407e4e07c6cSPhilippe Bekaert /* Inform the card what DMA addresses to use for the indicated channel. */
6408e4e07c6cSPhilippe Bekaert /* Each channel got 16 4K pages allocated for DMA transfers. */
hdspm_set_channel_dma_addr(struct hdspm * hdspm,struct snd_pcm_substream * substream,unsigned int reg,int channel)6409e4e07c6cSPhilippe Bekaert static void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
641077a23f26STakashi Iwai 				       struct snd_pcm_substream *substream,
6411e4e07c6cSPhilippe Bekaert 				       unsigned int reg, int channel)
6412763f356cSTakashi Iwai {
6413763f356cSTakashi Iwai 	int i;
64140dca1793SAdrian Knoth 
6415e4e07c6cSPhilippe Bekaert 	for (i = channel * 16; i < channel * 16 + 16; i++)
6416763f356cSTakashi Iwai 		hdspm_write(hdspm, reg + 4 * i,
641777a23f26STakashi Iwai 			    snd_pcm_sgbuf_get_addr(substream, 4096 * i));
6418763f356cSTakashi Iwai }
6419763f356cSTakashi Iwai 
64200dca1793SAdrian Knoth 
6421763f356cSTakashi Iwai /* ------------- ALSA Devices ---------------------------- */
snd_hdspm_create_pcm(struct snd_card * card,struct hdspm * hdspm)6422e23e7a14SBill Pemberton static int snd_hdspm_create_pcm(struct snd_card *card,
642398274f07STakashi Iwai 				struct hdspm *hdspm)
6424763f356cSTakashi Iwai {
642598274f07STakashi Iwai 	struct snd_pcm *pcm;
6426763f356cSTakashi Iwai 	int err;
6427763f356cSTakashi Iwai 
6428ef5fa1a4STakashi Iwai 	err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm);
6429ef5fa1a4STakashi Iwai 	if (err < 0)
6430763f356cSTakashi Iwai 		return err;
6431763f356cSTakashi Iwai 
6432763f356cSTakashi Iwai 	hdspm->pcm = pcm;
6433763f356cSTakashi Iwai 	pcm->private_data = hdspm;
6434763f356cSTakashi Iwai 	strcpy(pcm->name, hdspm->card_name);
6435763f356cSTakashi Iwai 
6436763f356cSTakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
64370c8d9485SAdrian Knoth 			&snd_hdspm_ops);
6438763f356cSTakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
64390c8d9485SAdrian Knoth 			&snd_hdspm_ops);
6440763f356cSTakashi Iwai 
6441763f356cSTakashi Iwai 	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
6442763f356cSTakashi Iwai 
6443ef5fa1a4STakashi Iwai 	err = snd_hdspm_preallocate_memory(hdspm);
6444ef5fa1a4STakashi Iwai 	if (err < 0)
6445763f356cSTakashi Iwai 		return err;
6446763f356cSTakashi Iwai 
6447763f356cSTakashi Iwai 	return 0;
6448763f356cSTakashi Iwai }
6449763f356cSTakashi Iwai 
snd_hdspm_initialize_midi_flush(struct hdspm * hdspm)645098274f07STakashi Iwai static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm)
6451763f356cSTakashi Iwai {
64527c7102b7SAdrian Knoth 	int i;
64537c7102b7SAdrian Knoth 
64547c7102b7SAdrian Knoth 	for (i = 0; i < hdspm->midiPorts; i++)
64557c7102b7SAdrian Knoth 		snd_hdspm_flush_midi_input(hdspm, i);
6456763f356cSTakashi Iwai }
6457763f356cSTakashi Iwai 
snd_hdspm_create_alsa_devices(struct snd_card * card,struct hdspm * hdspm)6458e23e7a14SBill Pemberton static int snd_hdspm_create_alsa_devices(struct snd_card *card,
645998274f07STakashi Iwai 					 struct hdspm *hdspm)
6460763f356cSTakashi Iwai {
64610dca1793SAdrian Knoth 	int err, i;
6462763f356cSTakashi Iwai 
6463e3a471d6STakashi Iwai 	dev_dbg(card->dev, "Create card...\n");
6464ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_pcm(card, hdspm);
6465ef5fa1a4STakashi Iwai 	if (err < 0)
6466763f356cSTakashi Iwai 		return err;
6467763f356cSTakashi Iwai 
64680dca1793SAdrian Knoth 	i = 0;
64690dca1793SAdrian Knoth 	while (i < hdspm->midiPorts) {
64700dca1793SAdrian Knoth 		err = snd_hdspm_create_midi(card, hdspm, i);
64710dca1793SAdrian Knoth 		if (err < 0) {
6472763f356cSTakashi Iwai 			return err;
64730dca1793SAdrian Knoth 		}
64740dca1793SAdrian Knoth 		i++;
64750dca1793SAdrian Knoth 	}
6476763f356cSTakashi Iwai 
6477ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_controls(card, hdspm);
6478ef5fa1a4STakashi Iwai 	if (err < 0)
6479763f356cSTakashi Iwai 		return err;
6480763f356cSTakashi Iwai 
6481ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_hwdep(card, hdspm);
6482ef5fa1a4STakashi Iwai 	if (err < 0)
6483763f356cSTakashi Iwai 		return err;
6484763f356cSTakashi Iwai 
6485e3a471d6STakashi Iwai 	dev_dbg(card->dev, "proc init...\n");
6486763f356cSTakashi Iwai 	snd_hdspm_proc_init(hdspm);
6487763f356cSTakashi Iwai 
6488763f356cSTakashi Iwai 	hdspm->system_sample_rate = -1;
6489763f356cSTakashi Iwai 	hdspm->last_external_sample_rate = -1;
6490763f356cSTakashi Iwai 	hdspm->last_internal_sample_rate = -1;
6491763f356cSTakashi Iwai 	hdspm->playback_pid = -1;
6492763f356cSTakashi Iwai 	hdspm->capture_pid = -1;
6493763f356cSTakashi Iwai 	hdspm->capture_substream = NULL;
6494763f356cSTakashi Iwai 	hdspm->playback_substream = NULL;
6495763f356cSTakashi Iwai 
6496e3a471d6STakashi Iwai 	dev_dbg(card->dev, "Set defaults...\n");
6497ef5fa1a4STakashi Iwai 	err = snd_hdspm_set_defaults(hdspm);
6498ef5fa1a4STakashi Iwai 	if (err < 0)
6499763f356cSTakashi Iwai 		return err;
6500763f356cSTakashi Iwai 
6501e3a471d6STakashi Iwai 	dev_dbg(card->dev, "Update mixer controls...\n");
6502763f356cSTakashi Iwai 	hdspm_update_simple_mixer_controls(hdspm);
6503763f356cSTakashi Iwai 
65041bb6d9e2SColin Ian King 	dev_dbg(card->dev, "Initializing complete?\n");
6505763f356cSTakashi Iwai 
6506ef5fa1a4STakashi Iwai 	err = snd_card_register(card);
6507ef5fa1a4STakashi Iwai 	if (err < 0) {
6508e3a471d6STakashi Iwai 		dev_err(card->dev, "error registering card\n");
6509763f356cSTakashi Iwai 		return err;
6510763f356cSTakashi Iwai 	}
6511763f356cSTakashi Iwai 
6512e3a471d6STakashi Iwai 	dev_dbg(card->dev, "... yes now\n");
6513763f356cSTakashi Iwai 
6514763f356cSTakashi Iwai 	return 0;
6515763f356cSTakashi Iwai }
6516763f356cSTakashi Iwai 
snd_hdspm_create(struct snd_card * card,struct hdspm * hdspm)6517e23e7a14SBill Pemberton static int snd_hdspm_create(struct snd_card *card,
6518e23e7a14SBill Pemberton 			    struct hdspm *hdspm)
6519e23e7a14SBill Pemberton {
65200dca1793SAdrian Knoth 
6521763f356cSTakashi Iwai 	struct pci_dev *pci = hdspm->pci;
6522763f356cSTakashi Iwai 	int err;
6523763f356cSTakashi Iwai 	unsigned long io_extent;
6524763f356cSTakashi Iwai 
6525763f356cSTakashi Iwai 	hdspm->irq = -1;
6526763f356cSTakashi Iwai 	hdspm->card = card;
6527763f356cSTakashi Iwai 
6528763f356cSTakashi Iwai 	spin_lock_init(&hdspm->lock);
6529a2e527c5STakashi Iwai 	INIT_WORK(&hdspm->midi_work, hdspm_midi_work);
6530763f356cSTakashi Iwai 
6531763f356cSTakashi Iwai 	pci_read_config_word(hdspm->pci,
6532763f356cSTakashi Iwai 			PCI_CLASS_REVISION, &hdspm->firmware_rev);
6533763f356cSTakashi Iwai 
6534763f356cSTakashi Iwai 	strcpy(card->mixername, "Xilinx FPGA");
65353cee5a60SRemy Bruno 	strcpy(card->driver, "HDSPM");
65360dca1793SAdrian Knoth 
65370dca1793SAdrian Knoth 	switch (hdspm->firmware_rev) {
65380dca1793SAdrian Knoth 	case HDSPM_RAYDAT_REV:
65390dca1793SAdrian Knoth 		hdspm->io_type = RayDAT;
65400dca1793SAdrian Knoth 		hdspm->card_name = "RME RayDAT";
65410dca1793SAdrian Knoth 		hdspm->midiPorts = 2;
65420dca1793SAdrian Knoth 		break;
65430dca1793SAdrian Knoth 	case HDSPM_AIO_REV:
65440dca1793SAdrian Knoth 		hdspm->io_type = AIO;
65450dca1793SAdrian Knoth 		hdspm->card_name = "RME AIO";
65460dca1793SAdrian Knoth 		hdspm->midiPorts = 1;
65470dca1793SAdrian Knoth 		break;
65480dca1793SAdrian Knoth 	case HDSPM_MADIFACE_REV:
65490dca1793SAdrian Knoth 		hdspm->io_type = MADIface;
65500dca1793SAdrian Knoth 		hdspm->card_name = "RME MADIface";
65510dca1793SAdrian Knoth 		hdspm->midiPorts = 1;
65520dca1793SAdrian Knoth 		break;
6553c09403dcSAdrian Knoth 	default:
6554c09403dcSAdrian Knoth 		if ((hdspm->firmware_rev == 0xf0) ||
6555c09403dcSAdrian Knoth 			((hdspm->firmware_rev >= 0xe6) &&
6556c09403dcSAdrian Knoth 					(hdspm->firmware_rev <= 0xea))) {
65570dca1793SAdrian Knoth 			hdspm->io_type = AES32;
65580dca1793SAdrian Knoth 			hdspm->card_name = "RME AES32";
65590dca1793SAdrian Knoth 			hdspm->midiPorts = 2;
656005c7cc9cSAdrian Knoth 		} else if ((hdspm->firmware_rev == 0xd2) ||
6561c09403dcSAdrian Knoth 			((hdspm->firmware_rev >= 0xc8)  &&
6562c09403dcSAdrian Knoth 				(hdspm->firmware_rev <= 0xcf))) {
6563c09403dcSAdrian Knoth 			hdspm->io_type = MADI;
6564c09403dcSAdrian Knoth 			hdspm->card_name = "RME MADI";
6565c09403dcSAdrian Knoth 			hdspm->midiPorts = 3;
6566c09403dcSAdrian Knoth 		} else {
6567e3a471d6STakashi Iwai 			dev_err(card->dev,
6568e3a471d6STakashi Iwai 				"unknown firmware revision %x\n",
65695027f347SAdrian Knoth 				hdspm->firmware_rev);
65705027f347SAdrian Knoth 			return -ENODEV;
65713cee5a60SRemy Bruno 		}
6572c09403dcSAdrian Knoth 	}
6573763f356cSTakashi Iwai 
65740195ca5fSTakashi Iwai 	err = pcim_enable_device(pci);
6575ef5fa1a4STakashi Iwai 	if (err < 0)
6576763f356cSTakashi Iwai 		return err;
6577763f356cSTakashi Iwai 
6578763f356cSTakashi Iwai 	pci_set_master(hdspm->pci);
6579763f356cSTakashi Iwai 
65800195ca5fSTakashi Iwai 	err = pcim_iomap_regions(pci, 1 << 0, "hdspm");
6581ef5fa1a4STakashi Iwai 	if (err < 0)
6582763f356cSTakashi Iwai 		return err;
6583763f356cSTakashi Iwai 
6584763f356cSTakashi Iwai 	hdspm->port = pci_resource_start(pci, 0);
6585763f356cSTakashi Iwai 	io_extent = pci_resource_len(pci, 0);
65860195ca5fSTakashi Iwai 	hdspm->iobase = pcim_iomap_table(pci)[0];
6587e3a471d6STakashi Iwai 	dev_dbg(card->dev, "remapped region (0x%lx) 0x%lx-0x%lx\n",
6588763f356cSTakashi Iwai 			(unsigned long)hdspm->iobase, hdspm->port,
6589763f356cSTakashi Iwai 			hdspm->port + io_extent - 1);
6590763f356cSTakashi Iwai 
65910195ca5fSTakashi Iwai 	if (devm_request_irq(&pci->dev, pci->irq, snd_hdspm_interrupt,
6592934c2b6dSTakashi Iwai 			     IRQF_SHARED, KBUILD_MODNAME, hdspm)) {
6593e3a471d6STakashi Iwai 		dev_err(card->dev, "unable to use IRQ %d\n", pci->irq);
6594763f356cSTakashi Iwai 		return -EBUSY;
6595763f356cSTakashi Iwai 	}
6596763f356cSTakashi Iwai 
6597e3a471d6STakashi Iwai 	dev_dbg(card->dev, "use IRQ %d\n", pci->irq);
6598763f356cSTakashi Iwai 
6599763f356cSTakashi Iwai 	hdspm->irq = pci->irq;
660039cccf45STakashi Iwai 	card->sync_irq = hdspm->irq;
6601763f356cSTakashi Iwai 
6602e3a471d6STakashi Iwai 	dev_dbg(card->dev, "kmalloc Mixer memory of %zd Bytes\n",
66037dfec507SMarkus Elfring 		sizeof(*hdspm->mixer));
66040195ca5fSTakashi Iwai 	hdspm->mixer = devm_kzalloc(&pci->dev, sizeof(*hdspm->mixer), GFP_KERNEL);
66059dba5429SMarkus Elfring 	if (!hdspm->mixer)
6606b17cbdd8SJulia Lawall 		return -ENOMEM;
6607763f356cSTakashi Iwai 
66080dca1793SAdrian Knoth 	hdspm->port_names_in = NULL;
66090dca1793SAdrian Knoth 	hdspm->port_names_out = NULL;
66100dca1793SAdrian Knoth 
66110dca1793SAdrian Knoth 	switch (hdspm->io_type) {
66120dca1793SAdrian Knoth 	case AES32:
6613d2d10a21SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels = AES32_CHANNELS;
6614d2d10a21SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels = AES32_CHANNELS;
6615d2d10a21SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels = AES32_CHANNELS;
6616432d2500SAdrian Knoth 
6617432d2500SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
6618432d2500SAdrian Knoth 			channel_map_aes32;
6619432d2500SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
6620432d2500SAdrian Knoth 			channel_map_aes32;
6621432d2500SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
6622432d2500SAdrian Knoth 			channel_map_aes32;
6623432d2500SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
6624432d2500SAdrian Knoth 			texts_ports_aes32;
6625432d2500SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
6626432d2500SAdrian Knoth 			texts_ports_aes32;
6627432d2500SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
6628432d2500SAdrian Knoth 			texts_ports_aes32;
6629432d2500SAdrian Knoth 
6630d2d10a21SAdrian Knoth 		hdspm->max_channels_out = hdspm->max_channels_in =
6631d2d10a21SAdrian Knoth 			AES32_CHANNELS;
6632432d2500SAdrian Knoth 		hdspm->port_names_in = hdspm->port_names_out =
6633432d2500SAdrian Knoth 			texts_ports_aes32;
6634432d2500SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_out =
6635432d2500SAdrian Knoth 			channel_map_aes32;
6636432d2500SAdrian Knoth 
66370dca1793SAdrian Knoth 		break;
66380dca1793SAdrian Knoth 
66390dca1793SAdrian Knoth 	case MADI:
66400dca1793SAdrian Knoth 	case MADIface:
66410dca1793SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels =
66420dca1793SAdrian Knoth 			MADI_SS_CHANNELS;
66430dca1793SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels =
66440dca1793SAdrian Knoth 			MADI_DS_CHANNELS;
66450dca1793SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels =
66460dca1793SAdrian Knoth 			MADI_QS_CHANNELS;
66470dca1793SAdrian Knoth 
66480dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
66490dca1793SAdrian Knoth 			channel_map_unity_ss;
665001e96078SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
66510dca1793SAdrian Knoth 			channel_map_unity_ss;
665201e96078SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
66530dca1793SAdrian Knoth 			channel_map_unity_ss;
66540dca1793SAdrian Knoth 
66550dca1793SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
66560dca1793SAdrian Knoth 			texts_ports_madi;
66570dca1793SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
66580dca1793SAdrian Knoth 			texts_ports_madi;
66590dca1793SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
66600dca1793SAdrian Knoth 			texts_ports_madi;
66610dca1793SAdrian Knoth 		break;
66620dca1793SAdrian Knoth 
66630dca1793SAdrian Knoth 	case AIO:
66640dca1793SAdrian Knoth 		hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
66650dca1793SAdrian Knoth 		hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
66660dca1793SAdrian Knoth 		hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
66670dca1793SAdrian Knoth 		hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS;
66680dca1793SAdrian Knoth 		hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
66690dca1793SAdrian Knoth 		hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
66700dca1793SAdrian Knoth 
66713de9db26SAdrian Knoth 		if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
6672e3a471d6STakashi Iwai 			dev_info(card->dev, "AEB input board found\n");
66733de9db26SAdrian Knoth 			hdspm->ss_in_channels += 4;
66743de9db26SAdrian Knoth 			hdspm->ds_in_channels += 4;
66753de9db26SAdrian Knoth 			hdspm->qs_in_channels += 4;
66763de9db26SAdrian Knoth 		}
66773de9db26SAdrian Knoth 
66783de9db26SAdrian Knoth 		if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) {
6679e3a471d6STakashi Iwai 			dev_info(card->dev, "AEB output board found\n");
66803de9db26SAdrian Knoth 			hdspm->ss_out_channels += 4;
66813de9db26SAdrian Knoth 			hdspm->ds_out_channels += 4;
66823de9db26SAdrian Knoth 			hdspm->qs_out_channels += 4;
66833de9db26SAdrian Knoth 		}
66843de9db26SAdrian Knoth 
66850dca1793SAdrian Knoth 		hdspm->channel_map_out_ss = channel_map_aio_out_ss;
66860dca1793SAdrian Knoth 		hdspm->channel_map_out_ds = channel_map_aio_out_ds;
66870dca1793SAdrian Knoth 		hdspm->channel_map_out_qs = channel_map_aio_out_qs;
66880dca1793SAdrian Knoth 
66890dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = channel_map_aio_in_ss;
66900dca1793SAdrian Knoth 		hdspm->channel_map_in_ds = channel_map_aio_in_ds;
66910dca1793SAdrian Knoth 		hdspm->channel_map_in_qs = channel_map_aio_in_qs;
66920dca1793SAdrian Knoth 
66930dca1793SAdrian Knoth 		hdspm->port_names_in_ss = texts_ports_aio_in_ss;
66940dca1793SAdrian Knoth 		hdspm->port_names_out_ss = texts_ports_aio_out_ss;
66950dca1793SAdrian Knoth 		hdspm->port_names_in_ds = texts_ports_aio_in_ds;
66960dca1793SAdrian Knoth 		hdspm->port_names_out_ds = texts_ports_aio_out_ds;
66970dca1793SAdrian Knoth 		hdspm->port_names_in_qs = texts_ports_aio_in_qs;
66980dca1793SAdrian Knoth 		hdspm->port_names_out_qs = texts_ports_aio_out_qs;
66990dca1793SAdrian Knoth 
67000dca1793SAdrian Knoth 		break;
67010dca1793SAdrian Knoth 
67020dca1793SAdrian Knoth 	case RayDAT:
67030dca1793SAdrian Knoth 		hdspm->ss_in_channels = hdspm->ss_out_channels =
67040dca1793SAdrian Knoth 			RAYDAT_SS_CHANNELS;
67050dca1793SAdrian Knoth 		hdspm->ds_in_channels = hdspm->ds_out_channels =
67060dca1793SAdrian Knoth 			RAYDAT_DS_CHANNELS;
67070dca1793SAdrian Knoth 		hdspm->qs_in_channels = hdspm->qs_out_channels =
67080dca1793SAdrian Knoth 			RAYDAT_QS_CHANNELS;
67090dca1793SAdrian Knoth 
67100dca1793SAdrian Knoth 		hdspm->max_channels_in = RAYDAT_SS_CHANNELS;
67110dca1793SAdrian Knoth 		hdspm->max_channels_out = RAYDAT_SS_CHANNELS;
67120dca1793SAdrian Knoth 
67130dca1793SAdrian Knoth 		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
67140dca1793SAdrian Knoth 			channel_map_raydat_ss;
67150dca1793SAdrian Knoth 		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
67160dca1793SAdrian Knoth 			channel_map_raydat_ds;
67170dca1793SAdrian Knoth 		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
67180dca1793SAdrian Knoth 			channel_map_raydat_qs;
67190dca1793SAdrian Knoth 		hdspm->channel_map_in = hdspm->channel_map_out =
67200dca1793SAdrian Knoth 			channel_map_raydat_ss;
67210dca1793SAdrian Knoth 
67220dca1793SAdrian Knoth 		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
67230dca1793SAdrian Knoth 			texts_ports_raydat_ss;
67240dca1793SAdrian Knoth 		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
67250dca1793SAdrian Knoth 			texts_ports_raydat_ds;
67260dca1793SAdrian Knoth 		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
67270dca1793SAdrian Knoth 			texts_ports_raydat_qs;
67280dca1793SAdrian Knoth 
67290dca1793SAdrian Knoth 
67300dca1793SAdrian Knoth 		break;
67310dca1793SAdrian Knoth 
67320dca1793SAdrian Knoth 	}
67330dca1793SAdrian Knoth 
67340dca1793SAdrian Knoth 	/* TCO detection */
67350dca1793SAdrian Knoth 	switch (hdspm->io_type) {
67360dca1793SAdrian Knoth 	case AIO:
67370dca1793SAdrian Knoth 	case RayDAT:
67380dca1793SAdrian Knoth 		if (hdspm_read(hdspm, HDSPM_statusRegister2) &
67390dca1793SAdrian Knoth 				HDSPM_s2_tco_detect) {
67400dca1793SAdrian Knoth 			hdspm->midiPorts++;
67417dfec507SMarkus Elfring 			hdspm->tco = kzalloc(sizeof(*hdspm->tco), GFP_KERNEL);
6742da2ea374SMarkus Elfring 			if (hdspm->tco)
67430dca1793SAdrian Knoth 				hdspm_tco_write(hdspm);
6744da2ea374SMarkus Elfring 
6745e3a471d6STakashi Iwai 			dev_info(card->dev, "AIO/RayDAT TCO module found\n");
67460dca1793SAdrian Knoth 		} else {
67470dca1793SAdrian Knoth 			hdspm->tco = NULL;
67480dca1793SAdrian Knoth 		}
67490dca1793SAdrian Knoth 		break;
67500dca1793SAdrian Knoth 
67510dca1793SAdrian Knoth 	case MADI:
67520dc831b9SAdrian Knoth 	case AES32:
67530dca1793SAdrian Knoth 		if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
67540dca1793SAdrian Knoth 			hdspm->midiPorts++;
67557dfec507SMarkus Elfring 			hdspm->tco = kzalloc(sizeof(*hdspm->tco), GFP_KERNEL);
6756da2ea374SMarkus Elfring 			if (hdspm->tco)
67570dca1793SAdrian Knoth 				hdspm_tco_write(hdspm);
6758da2ea374SMarkus Elfring 
6759e3a471d6STakashi Iwai 			dev_info(card->dev, "MADI/AES TCO module found\n");
67600dca1793SAdrian Knoth 		} else {
67610dca1793SAdrian Knoth 			hdspm->tco = NULL;
67620dca1793SAdrian Knoth 		}
67630dca1793SAdrian Knoth 		break;
67640dca1793SAdrian Knoth 
67650dca1793SAdrian Knoth 	default:
67660dca1793SAdrian Knoth 		hdspm->tco = NULL;
67670dca1793SAdrian Knoth 	}
67680dca1793SAdrian Knoth 
67690dca1793SAdrian Knoth 	/* texts */
67700dca1793SAdrian Knoth 	switch (hdspm->io_type) {
67710dca1793SAdrian Knoth 	case AES32:
67720dca1793SAdrian Knoth 		if (hdspm->tco) {
67730dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aes_tco;
6774e71b95adSAdrian Knoth 			hdspm->texts_autosync_items =
6775e71b95adSAdrian Knoth 				ARRAY_SIZE(texts_autosync_aes_tco);
67760dca1793SAdrian Knoth 		} else {
67770dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aes;
6778e71b95adSAdrian Knoth 			hdspm->texts_autosync_items =
6779e71b95adSAdrian Knoth 				ARRAY_SIZE(texts_autosync_aes);
67800dca1793SAdrian Knoth 		}
67810dca1793SAdrian Knoth 		break;
67820dca1793SAdrian Knoth 
67830dca1793SAdrian Knoth 	case MADI:
67840dca1793SAdrian Knoth 		if (hdspm->tco) {
67850dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_madi_tco;
67860dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 4;
67870dca1793SAdrian Knoth 		} else {
67880dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_madi;
67890dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 3;
67900dca1793SAdrian Knoth 		}
67910dca1793SAdrian Knoth 		break;
67920dca1793SAdrian Knoth 
67930dca1793SAdrian Knoth 	case MADIface:
67940dca1793SAdrian Knoth 
67950dca1793SAdrian Knoth 		break;
67960dca1793SAdrian Knoth 
67970dca1793SAdrian Knoth 	case RayDAT:
67980dca1793SAdrian Knoth 		if (hdspm->tco) {
67990dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_raydat_tco;
68000dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 9;
68010dca1793SAdrian Knoth 		} else {
68020dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_raydat;
68030dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 8;
68040dca1793SAdrian Knoth 		}
68050dca1793SAdrian Knoth 		break;
68060dca1793SAdrian Knoth 
68070dca1793SAdrian Knoth 	case AIO:
68080dca1793SAdrian Knoth 		if (hdspm->tco) {
68090dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aio_tco;
68100dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 6;
68110dca1793SAdrian Knoth 		} else {
68120dca1793SAdrian Knoth 			hdspm->texts_autosync = texts_autosync_aio;
68130dca1793SAdrian Knoth 			hdspm->texts_autosync_items = 5;
68140dca1793SAdrian Knoth 		}
68150dca1793SAdrian Knoth 		break;
68160dca1793SAdrian Knoth 
68170dca1793SAdrian Knoth 	}
68180dca1793SAdrian Knoth 
6819f7de8ba3SAdrian Knoth 	if (hdspm->io_type != MADIface) {
6820f7de8ba3SAdrian Knoth 		hdspm->serial = (hdspm_read(hdspm,
6821f7de8ba3SAdrian Knoth 				HDSPM_midiStatusIn0)>>8) & 0xFFFFFF;
6822f7de8ba3SAdrian Knoth 		/* id contains either a user-provided value or the default
6823f7de8ba3SAdrian Knoth 		 * NULL. If it's the default, we're safe to
6824f7de8ba3SAdrian Knoth 		 * fill card->id with the serial number.
6825f7de8ba3SAdrian Knoth 		 *
6826f7de8ba3SAdrian Knoth 		 * If the serial number is 0xFFFFFF, then we're dealing with
6827f7de8ba3SAdrian Knoth 		 * an old PCI revision that comes without a sane number. In
6828f7de8ba3SAdrian Knoth 		 * this case, we don't set card->id to avoid collisions
6829f7de8ba3SAdrian Knoth 		 * when running with multiple cards.
6830f7de8ba3SAdrian Knoth 		 */
6831da2ea374SMarkus Elfring 		if (!id[hdspm->dev] && hdspm->serial != 0xFFFFFF) {
68327ad210acSArnd Bergmann 			snprintf(card->id, sizeof(card->id),
68337ad210acSArnd Bergmann 				 "HDSPMx%06x", hdspm->serial);
6834f7de8ba3SAdrian Knoth 			snd_card_set_id(card, card->id);
6835f7de8ba3SAdrian Knoth 		}
6836f7de8ba3SAdrian Knoth 	}
6837f7de8ba3SAdrian Knoth 
6838e3a471d6STakashi Iwai 	dev_dbg(card->dev, "create alsa devices.\n");
6839ef5fa1a4STakashi Iwai 	err = snd_hdspm_create_alsa_devices(card, hdspm);
6840ef5fa1a4STakashi Iwai 	if (err < 0)
6841763f356cSTakashi Iwai 		return err;
6842763f356cSTakashi Iwai 
6843763f356cSTakashi Iwai 	snd_hdspm_initialize_midi_flush(hdspm);
6844763f356cSTakashi Iwai 
6845763f356cSTakashi Iwai 	return 0;
6846763f356cSTakashi Iwai }
6847763f356cSTakashi Iwai 
68480dca1793SAdrian Knoth 
snd_hdspm_card_free(struct snd_card * card)68490195ca5fSTakashi Iwai static void snd_hdspm_card_free(struct snd_card *card)
6850763f356cSTakashi Iwai {
68510195ca5fSTakashi Iwai 	struct hdspm *hdspm = card->private_data;
6852763f356cSTakashi Iwai 
6853763f356cSTakashi Iwai 	if (hdspm->port) {
6854a2e527c5STakashi Iwai 		cancel_work_sync(&hdspm->midi_work);
6855763f356cSTakashi Iwai 
6856763f356cSTakashi Iwai 		/* stop th audio, and cancel all interrupts */
6857763f356cSTakashi Iwai 		hdspm->control_register &=
6858ef5fa1a4STakashi Iwai 		    ~(HDSPM_Start | HDSPM_AudioInterruptEnable |
68590dca1793SAdrian Knoth 		      HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable |
68600dca1793SAdrian Knoth 		      HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable);
6861763f356cSTakashi Iwai 		hdspm_write(hdspm, HDSPM_controlRegister,
6862763f356cSTakashi Iwai 			    hdspm->control_register);
6863763f356cSTakashi Iwai 	}
6864763f356cSTakashi Iwai }
6865763f356cSTakashi Iwai 
68660dca1793SAdrian Knoth 
snd_hdspm_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)6867e23e7a14SBill Pemberton static int snd_hdspm_probe(struct pci_dev *pci,
6868763f356cSTakashi Iwai 			   const struct pci_device_id *pci_id)
6869763f356cSTakashi Iwai {
6870763f356cSTakashi Iwai 	static int dev;
687198274f07STakashi Iwai 	struct hdspm *hdspm;
687298274f07STakashi Iwai 	struct snd_card *card;
6873763f356cSTakashi Iwai 	int err;
6874763f356cSTakashi Iwai 
6875763f356cSTakashi Iwai 	if (dev >= SNDRV_CARDS)
6876763f356cSTakashi Iwai 		return -ENODEV;
6877763f356cSTakashi Iwai 	if (!enable[dev]) {
6878763f356cSTakashi Iwai 		dev++;
6879763f356cSTakashi Iwai 		return -ENOENT;
6880763f356cSTakashi Iwai 	}
6881763f356cSTakashi Iwai 
68820195ca5fSTakashi Iwai 	err = snd_devm_card_new(&pci->dev, index[dev], id[dev],
68837dfec507SMarkus Elfring 				THIS_MODULE, sizeof(*hdspm), &card);
6884e58de7baSTakashi Iwai 	if (err < 0)
6885e58de7baSTakashi Iwai 		return err;
6886763f356cSTakashi Iwai 
6887ef5fa1a4STakashi Iwai 	hdspm = card->private_data;
6888763f356cSTakashi Iwai 	card->private_free = snd_hdspm_card_free;
6889763f356cSTakashi Iwai 	hdspm->dev = dev;
6890763f356cSTakashi Iwai 	hdspm->pci = pci;
6891763f356cSTakashi Iwai 
68920dca1793SAdrian Knoth 	err = snd_hdspm_create(card, hdspm);
6893e35e9ddfSMarkus Elfring 	if (err < 0)
6894eab521aeSTakashi Iwai 		goto error;
6895763f356cSTakashi Iwai 
68960dca1793SAdrian Knoth 	if (hdspm->io_type != MADIface) {
68977ad210acSArnd Bergmann 		snprintf(card->shortname, sizeof(card->shortname), "%s_%x",
68987ad210acSArnd Bergmann 			hdspm->card_name, hdspm->serial);
68997ad210acSArnd Bergmann 		snprintf(card->longname, sizeof(card->longname),
69007ad210acSArnd Bergmann 			 "%s S/N 0x%x at 0x%lx, irq %d",
69017ad210acSArnd Bergmann 			 hdspm->card_name, hdspm->serial,
6902763f356cSTakashi Iwai 			 hdspm->port, hdspm->irq);
69030dca1793SAdrian Knoth 	} else {
69047ad210acSArnd Bergmann 		snprintf(card->shortname, sizeof(card->shortname), "%s",
69057ad210acSArnd Bergmann 			 hdspm->card_name);
69067ad210acSArnd Bergmann 		snprintf(card->longname, sizeof(card->longname),
69077ad210acSArnd Bergmann 			 "%s at 0x%lx, irq %d",
69080dca1793SAdrian Knoth 			 hdspm->card_name, hdspm->port, hdspm->irq);
69090dca1793SAdrian Knoth 	}
6910763f356cSTakashi Iwai 
6911ef5fa1a4STakashi Iwai 	err = snd_card_register(card);
6912e35e9ddfSMarkus Elfring 	if (err < 0)
6913eab521aeSTakashi Iwai 		goto error;
6914763f356cSTakashi Iwai 
6915763f356cSTakashi Iwai 	pci_set_drvdata(pci, card);
6916763f356cSTakashi Iwai 
6917763f356cSTakashi Iwai 	dev++;
6918763f356cSTakashi Iwai 	return 0;
6919eab521aeSTakashi Iwai 
6920eab521aeSTakashi Iwai  error:
6921eab521aeSTakashi Iwai 	snd_card_free(card);
6922eab521aeSTakashi Iwai 	return err;
6923763f356cSTakashi Iwai }
6924763f356cSTakashi Iwai 
6925e9f66d9bSTakashi Iwai static struct pci_driver hdspm_driver = {
69263733e424STakashi Iwai 	.name = KBUILD_MODNAME,
6927763f356cSTakashi Iwai 	.id_table = snd_hdspm_ids,
6928763f356cSTakashi Iwai 	.probe = snd_hdspm_probe,
6929763f356cSTakashi Iwai };
6930763f356cSTakashi Iwai 
6931e9f66d9bSTakashi Iwai module_pci_driver(hdspm_driver);
6932