162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   ALSA driver for RME Hammerfall DSP MADI audio interface(s)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *      Copyright (c) 2003 Winfried Ritsch (IEM)
662306a36Sopenharmony_ci *      code based on hdsp.c   Paul Davis
762306a36Sopenharmony_ci *                             Marcus Andersson
862306a36Sopenharmony_ci *                             Thomas Charbonnel
962306a36Sopenharmony_ci *      Modified 2006-06-01 for AES32 support by Remy Bruno
1062306a36Sopenharmony_ci *                                               <remy.bruno@trinnov.com>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *      Modified 2009-04-13 for proper metering by Florian Faber
1362306a36Sopenharmony_ci *                                               <faber@faberman.de>
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *      Modified 2009-04-14 for native float support by Florian Faber
1662306a36Sopenharmony_ci *                                               <faber@faberman.de>
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      Modified 2009-04-26 fixed bug in rms metering by Florian Faber
1962306a36Sopenharmony_ci *                                               <faber@faberman.de>
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *      Modified 2009-04-30 added hw serial number support by Florian Faber
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *      Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci *	Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *      Modified 2019-05-23 fix AIO single speed ADAT capture and playback
2862306a36Sopenharmony_ci *      by Philippe.Bekaert@uhasselt.be
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* *************    Register Documentation   *******************************************************
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Work in progress! Documentation is based on the code in this file.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * --------- HDSPM_controlRegister ---------
3662306a36Sopenharmony_ci * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
3762306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||:
3862306a36Sopenharmony_ci * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
3962306a36Sopenharmony_ci * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
4062306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||:
4162306a36Sopenharmony_ci * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
4262306a36Sopenharmony_ci * :    .    :    .    :    .    :  x .    :  HDSPM_AudioInterruptEnable \_ setting both bits
4362306a36Sopenharmony_ci * :    .    :    .    :    .    :    .   x:  HDSPM_Start                /  enables audio IO
4462306a36Sopenharmony_ci * :    .    :    .    :    .    :   x.    :  HDSPM_ClockModeMaster - 1: Master, 0: Slave
4562306a36Sopenharmony_ci * :    .    :    .    :    .    :    .210 :  HDSPM_LatencyMask - 3 Bit value for latency
4662306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :      0:64, 1:128, 2:256, 3:512,
4762306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :      4:1024, 5:2048, 6:4096, 7:8192
4862306a36Sopenharmony_ci * :x   .    :    .    :    .   x:xx  .    :  HDSPM_FrequencyMask
4962306a36Sopenharmony_ci * :    .    :    .    :    .    :10  .    :  HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=??
5062306a36Sopenharmony_ci * :    .    :    .    :    .   x:    .    :  <MADI> HDSPM_DoubleSpeed
5162306a36Sopenharmony_ci * :x   .    :    .    :    .    :    .    :  <MADI> HDSPM_QuadSpeed
5262306a36Sopenharmony_ci * :    .  3 :    .  10:  2 .    :    .    :  HDSPM_SyncRefMask :
5362306a36Sopenharmony_ci * :    .    :    .   x:    .    :    .    :  HDSPM_SyncRef0
5462306a36Sopenharmony_ci * :    .    :    .  x :    .    :    .    :  HDSPM_SyncRef1
5562306a36Sopenharmony_ci * :    .    :    .    :  x .    :    .    :  <AES32> HDSPM_SyncRef2
5662306a36Sopenharmony_ci * :    .  x :    .    :    .    :    .    :  <AES32> HDSPM_SyncRef3
5762306a36Sopenharmony_ci * :    .    :    .  10:    .    :    .    :  <MADI> sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn
5862306a36Sopenharmony_ci * :    .  3 :    .  10:  2 .    :    .    :  <AES32>  0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn?
5962306a36Sopenharmony_ci * :    .  x :    .    :    .    :    .    :  <MADIe> HDSPe_FLOAT_FORMAT
6062306a36Sopenharmony_ci * :    .    :    .    : x  .    :    .    :  <MADI> HDSPM_InputSelect0 : 0=optical,1=coax
6162306a36Sopenharmony_ci * :    .    :    .    :x   .    :    .    :  <MADI> HDSPM_InputSelect1
6262306a36Sopenharmony_ci * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
6362306a36Sopenharmony_ci * :    .    :    .    :    . x  :    .    :  <MADI> HDSPM_TX_64ch
6462306a36Sopenharmony_ci * :    .    :    .    :    . x  :    .    :  <AES32> HDSPM_Emphasis
6562306a36Sopenharmony_ci * :    .    :    .    :    .x   :    .    :  <MADI> HDSPM_AutoInp
6662306a36Sopenharmony_ci * :    .    :    . x  :    .    :    .    :  <MADI> HDSPM_SMUX
6762306a36Sopenharmony_ci * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
6862306a36Sopenharmony_ci * :    .    :   x.    :    .    :    .    :  <MADI> HDSPM_taxi_reset
6962306a36Sopenharmony_ci * :    .   x:    .    :    .    :    .    :  <MADI> HDSPM_LineOut
7062306a36Sopenharmony_ci * :    .   x:    .    :    .    :    .    :  <AES32> ??????????????????
7162306a36Sopenharmony_ci * :    .    :   x.    :    .    :    .    :  <AES32> HDSPM_WCK48
7262306a36Sopenharmony_ci * :    .    :    .    :    .x   :    .    :  <AES32> HDSPM_Dolby
7362306a36Sopenharmony_ci * :    .    : x  .    :    .    :    .    :  HDSPM_Midi0InterruptEnable
7462306a36Sopenharmony_ci * :    .    :x   .    :    .    :    .    :  HDSPM_Midi1InterruptEnable
7562306a36Sopenharmony_ci * :    .    :  x .    :    .    :    .    :  HDSPM_Midi2InterruptEnable
7662306a36Sopenharmony_ci * :    . x  :    .    :    .    :    .    :  <MADI> HDSPM_Midi3InterruptEnable
7762306a36Sopenharmony_ci * :    . x  :    .    :    .    :    .    :  <AES32> HDSPM_DS_DoubleWire
7862306a36Sopenharmony_ci * :    .x   :    .    :    .    :    .    :  <AES32> HDSPM_QS_DoubleWire
7962306a36Sopenharmony_ci * :   x.    :    .    :    .    :    .    :  <AES32> HDSPM_QS_QuadWire
8062306a36Sopenharmony_ci * :    .    :    .    :    .  x :    .    :  <AES32> HDSPM_Professional
8162306a36Sopenharmony_ci * : x  .    :    .    :    .    :    .    :  HDSPM_wclk_sel
8262306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :
8362306a36Sopenharmony_ci * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
8462306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||:
8562306a36Sopenharmony_ci * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
8662306a36Sopenharmony_ci * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
8762306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||:
8862306a36Sopenharmony_ci * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci * AIO / RayDAT only
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * ------------ HDSPM_WR_SETTINGS ----------
9562306a36Sopenharmony_ci * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
9662306a36Sopenharmony_ci * :1098.7654:3210.9876:5432.1098:7654.3210:
9762306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
9862306a36Sopenharmony_ci * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
9962306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||:
10062306a36Sopenharmony_ci * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
10162306a36Sopenharmony_ci * :    .    :    .    :    .    :    .   x: HDSPM_c0Master 1: Master, 0: Slave
10262306a36Sopenharmony_ci * :    .    :    .    :    .    :    .  x : HDSPM_c0_SyncRef0
10362306a36Sopenharmony_ci * :    .    :    .    :    .    :    . x  : HDSPM_c0_SyncRef1
10462306a36Sopenharmony_ci * :    .    :    .    :    .    :    .x   : HDSPM_c0_SyncRef2
10562306a36Sopenharmony_ci * :    .    :    .    :    .    :   x.    : HDSPM_c0_SyncRef3
10662306a36Sopenharmony_ci * :    .    :    .    :    .    :   3.210 : HDSPM_c0_SyncRefMask:
10762306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :  RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4,
10862306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
10962306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :  AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT,
11062306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
11162306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :
11262306a36Sopenharmony_ci * :    .    :    .    :    .    :    .    :
11362306a36Sopenharmony_ci * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
11462306a36Sopenharmony_ci * :1098.7654:3210.9876:5432.1098:7654.3210:
11562306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
11662306a36Sopenharmony_ci * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
11762306a36Sopenharmony_ci * :||||.||||:||||.||||:||||.||||:||||.||||:
11862306a36Sopenharmony_ci * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ci#include <linux/init.h>
12262306a36Sopenharmony_ci#include <linux/delay.h>
12362306a36Sopenharmony_ci#include <linux/interrupt.h>
12462306a36Sopenharmony_ci#include <linux/module.h>
12562306a36Sopenharmony_ci#include <linux/slab.h>
12662306a36Sopenharmony_ci#include <linux/pci.h>
12762306a36Sopenharmony_ci#include <linux/math64.h>
12862306a36Sopenharmony_ci#include <linux/io.h>
12962306a36Sopenharmony_ci#include <linux/nospec.h>
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#include <sound/core.h>
13262306a36Sopenharmony_ci#include <sound/control.h>
13362306a36Sopenharmony_ci#include <sound/pcm.h>
13462306a36Sopenharmony_ci#include <sound/pcm_params.h>
13562306a36Sopenharmony_ci#include <sound/info.h>
13662306a36Sopenharmony_ci#include <sound/asoundef.h>
13762306a36Sopenharmony_ci#include <sound/rawmidi.h>
13862306a36Sopenharmony_ci#include <sound/hwdep.h>
13962306a36Sopenharmony_ci#include <sound/initval.h>
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#include <sound/hdspm.h>
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	  /* Index 0-MAX */
14462306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	  /* ID for this card */
14562306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
14862306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
15162306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
15462306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ciMODULE_AUTHOR
15862306a36Sopenharmony_ci(
15962306a36Sopenharmony_ci	"Winfried Ritsch <ritsch_AT_iem.at>, "
16062306a36Sopenharmony_ci	"Paul Davis <paul@linuxaudiosystems.com>, "
16162306a36Sopenharmony_ci	"Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
16262306a36Sopenharmony_ci	"Remy Bruno <remy.bruno@trinnov.com>, "
16362306a36Sopenharmony_ci	"Florian Faber <faberman@linuxproaudio.org>, "
16462306a36Sopenharmony_ci	"Adrian Knoth <adi@drcomp.erfurt.thur.de>"
16562306a36Sopenharmony_ci);
16662306a36Sopenharmony_ciMODULE_DESCRIPTION("RME HDSPM");
16762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/* --- Write registers. ---
17062306a36Sopenharmony_ci  These are defined as byte-offsets from the iobase value.  */
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci#define HDSPM_WR_SETTINGS             0
17362306a36Sopenharmony_ci#define HDSPM_outputBufferAddress    32
17462306a36Sopenharmony_ci#define HDSPM_inputBufferAddress     36
17562306a36Sopenharmony_ci#define HDSPM_controlRegister	     64
17662306a36Sopenharmony_ci#define HDSPM_interruptConfirmation  96
17762306a36Sopenharmony_ci#define HDSPM_control2Reg	     256  /* not in specs ???????? */
17862306a36Sopenharmony_ci#define HDSPM_freqReg                256  /* for setting arbitrary clock values (DDS feature) */
17962306a36Sopenharmony_ci#define HDSPM_midiDataOut0	     352  /* just believe in old code */
18062306a36Sopenharmony_ci#define HDSPM_midiDataOut1	     356
18162306a36Sopenharmony_ci#define HDSPM_eeprom_wr		     384  /* for AES32 */
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* DMA enable for 64 channels, only Bit 0 is relevant */
18462306a36Sopenharmony_ci#define HDSPM_outputEnableBase       512  /* 512-767  input  DMA */
18562306a36Sopenharmony_ci#define HDSPM_inputEnableBase        768  /* 768-1023 output DMA */
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* 16 page addresses for each of the 64 channels DMA buffer in and out
18862306a36Sopenharmony_ci   (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
18962306a36Sopenharmony_ci#define HDSPM_pageAddressBufferOut       8192
19062306a36Sopenharmony_ci#define HDSPM_pageAddressBufferIn        (HDSPM_pageAddressBufferOut+64*16*4)
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci#define HDSPM_MADI_mixerBase    32768	/* 32768-65535 for 2x64x64 Fader */
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci#define HDSPM_MATRIX_MIXER_SIZE  8192	/* = 2*64*64 * 4 Byte => 32kB */
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/* --- Read registers. ---
19762306a36Sopenharmony_ci   These are defined as byte-offsets from the iobase value */
19862306a36Sopenharmony_ci#define HDSPM_statusRegister    0
19962306a36Sopenharmony_ci/*#define HDSPM_statusRegister2  96 */
20062306a36Sopenharmony_ci/* after RME Windows driver sources, status2 is 4-byte word # 48 = word at
20162306a36Sopenharmony_ci * offset 192, for AES32 *and* MADI
20262306a36Sopenharmony_ci * => need to check that offset 192 is working on MADI */
20362306a36Sopenharmony_ci#define HDSPM_statusRegister2  192
20462306a36Sopenharmony_ci#define HDSPM_timecodeRegister 128
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* AIO, RayDAT */
20762306a36Sopenharmony_ci#define HDSPM_RD_STATUS_0 0
20862306a36Sopenharmony_ci#define HDSPM_RD_STATUS_1 64
20962306a36Sopenharmony_ci#define HDSPM_RD_STATUS_2 128
21062306a36Sopenharmony_ci#define HDSPM_RD_STATUS_3 192
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci#define HDSPM_RD_TCO           256
21362306a36Sopenharmony_ci#define HDSPM_RD_PLL_FREQ      512
21462306a36Sopenharmony_ci#define HDSPM_WR_TCO           128
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci#define HDSPM_TCO1_TCO_lock			0x00000001
21762306a36Sopenharmony_ci#define HDSPM_TCO1_WCK_Input_Range_LSB		0x00000002
21862306a36Sopenharmony_ci#define HDSPM_TCO1_WCK_Input_Range_MSB		0x00000004
21962306a36Sopenharmony_ci#define HDSPM_TCO1_LTC_Input_valid		0x00000008
22062306a36Sopenharmony_ci#define HDSPM_TCO1_WCK_Input_valid		0x00000010
22162306a36Sopenharmony_ci#define HDSPM_TCO1_Video_Input_Format_NTSC	0x00000020
22262306a36Sopenharmony_ci#define HDSPM_TCO1_Video_Input_Format_PAL	0x00000040
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci#define HDSPM_TCO1_set_TC			0x00000100
22562306a36Sopenharmony_ci#define HDSPM_TCO1_set_drop_frame_flag		0x00000200
22662306a36Sopenharmony_ci#define HDSPM_TCO1_LTC_Format_LSB		0x00000400
22762306a36Sopenharmony_ci#define HDSPM_TCO1_LTC_Format_MSB		0x00000800
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci#define HDSPM_TCO2_TC_run			0x00010000
23062306a36Sopenharmony_ci#define HDSPM_TCO2_WCK_IO_ratio_LSB		0x00020000
23162306a36Sopenharmony_ci#define HDSPM_TCO2_WCK_IO_ratio_MSB		0x00040000
23262306a36Sopenharmony_ci#define HDSPM_TCO2_set_num_drop_frames_LSB	0x00080000
23362306a36Sopenharmony_ci#define HDSPM_TCO2_set_num_drop_frames_MSB	0x00100000
23462306a36Sopenharmony_ci#define HDSPM_TCO2_set_jam_sync			0x00200000
23562306a36Sopenharmony_ci#define HDSPM_TCO2_set_flywheel			0x00400000
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci#define HDSPM_TCO2_set_01_4			0x01000000
23862306a36Sopenharmony_ci#define HDSPM_TCO2_set_pull_down		0x02000000
23962306a36Sopenharmony_ci#define HDSPM_TCO2_set_pull_up			0x04000000
24062306a36Sopenharmony_ci#define HDSPM_TCO2_set_freq			0x08000000
24162306a36Sopenharmony_ci#define HDSPM_TCO2_set_term_75R			0x10000000
24262306a36Sopenharmony_ci#define HDSPM_TCO2_set_input_LSB		0x20000000
24362306a36Sopenharmony_ci#define HDSPM_TCO2_set_input_MSB		0x40000000
24462306a36Sopenharmony_ci#define HDSPM_TCO2_set_freq_from_app		0x80000000
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci#define HDSPM_midiDataOut0    352
24862306a36Sopenharmony_ci#define HDSPM_midiDataOut1    356
24962306a36Sopenharmony_ci#define HDSPM_midiDataOut2    368
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci#define HDSPM_midiDataIn0     360
25262306a36Sopenharmony_ci#define HDSPM_midiDataIn1     364
25362306a36Sopenharmony_ci#define HDSPM_midiDataIn2     372
25462306a36Sopenharmony_ci#define HDSPM_midiDataIn3     376
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/* status is data bytes in MIDI-FIFO (0-128) */
25762306a36Sopenharmony_ci#define HDSPM_midiStatusOut0  384
25862306a36Sopenharmony_ci#define HDSPM_midiStatusOut1  388
25962306a36Sopenharmony_ci#define HDSPM_midiStatusOut2  400
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci#define HDSPM_midiStatusIn0   392
26262306a36Sopenharmony_ci#define HDSPM_midiStatusIn1   396
26362306a36Sopenharmony_ci#define HDSPM_midiStatusIn2   404
26462306a36Sopenharmony_ci#define HDSPM_midiStatusIn3   408
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/* the meters are regular i/o-mapped registers, but offset
26862306a36Sopenharmony_ci   considerably from the rest. the peak registers are reset
26962306a36Sopenharmony_ci   when read; the least-significant 4 bits are full-scale counters;
27062306a36Sopenharmony_ci   the actual peak value is in the most-significant 24 bits.
27162306a36Sopenharmony_ci*/
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci#define HDSPM_MADI_INPUT_PEAK		4096
27462306a36Sopenharmony_ci#define HDSPM_MADI_PLAYBACK_PEAK	4352
27562306a36Sopenharmony_ci#define HDSPM_MADI_OUTPUT_PEAK		4608
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci#define HDSPM_MADI_INPUT_RMS_L		6144
27862306a36Sopenharmony_ci#define HDSPM_MADI_PLAYBACK_RMS_L	6400
27962306a36Sopenharmony_ci#define HDSPM_MADI_OUTPUT_RMS_L		6656
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci#define HDSPM_MADI_INPUT_RMS_H		7168
28262306a36Sopenharmony_ci#define HDSPM_MADI_PLAYBACK_RMS_H	7424
28362306a36Sopenharmony_ci#define HDSPM_MADI_OUTPUT_RMS_H		7680
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/* --- Control Register bits --------- */
28662306a36Sopenharmony_ci#define HDSPM_Start                (1<<0) /* start engine */
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci#define HDSPM_Latency0             (1<<1) /* buffer size = 2^n */
28962306a36Sopenharmony_ci#define HDSPM_Latency1             (1<<2) /* where n is defined */
29062306a36Sopenharmony_ci#define HDSPM_Latency2             (1<<3) /* by Latency{2,1,0} */
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci#define HDSPM_ClockModeMaster      (1<<4) /* 1=Master, 0=Autosync */
29362306a36Sopenharmony_ci#define HDSPM_c0Master		0x1    /* Master clock bit in settings
29462306a36Sopenharmony_ci					  register [RayDAT, AIO] */
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci#define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci#define HDSPM_Frequency0  (1<<6)  /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */
29962306a36Sopenharmony_ci#define HDSPM_Frequency1  (1<<7)  /* 0=32kHz/64kHz */
30062306a36Sopenharmony_ci#define HDSPM_DoubleSpeed (1<<8)  /* 0=normal speed, 1=double speed */
30162306a36Sopenharmony_ci#define HDSPM_QuadSpeed   (1<<31) /* quad speed bit */
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci#define HDSPM_Professional (1<<9) /* Professional */ /* AES32 ONLY */
30462306a36Sopenharmony_ci#define HDSPM_TX_64ch     (1<<10) /* Output 64channel MODE=1,
30562306a36Sopenharmony_ci				     56channelMODE=0 */ /* MADI ONLY*/
30662306a36Sopenharmony_ci#define HDSPM_Emphasis    (1<<10) /* Emphasis */ /* AES32 ONLY */
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci#define HDSPM_AutoInp     (1<<11) /* Auto Input (takeover) == Safe Mode,
30962306a36Sopenharmony_ci                                     0=off, 1=on  */ /* MADI ONLY */
31062306a36Sopenharmony_ci#define HDSPM_Dolby       (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax
31362306a36Sopenharmony_ci				    * -- MADI ONLY
31462306a36Sopenharmony_ci				    */
31562306a36Sopenharmony_ci#define HDSPM_InputSelect1 (1<<15) /* should be 0 */
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci#define HDSPM_SyncRef2     (1<<13)
31862306a36Sopenharmony_ci#define HDSPM_SyncRef3     (1<<25)
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci#define HDSPM_SMUX         (1<<18) /* Frame ??? */ /* MADI ONY */
32162306a36Sopenharmony_ci#define HDSPM_clr_tms      (1<<19) /* clear track marker, do not use
32262306a36Sopenharmony_ci                                      AES additional bits in
32362306a36Sopenharmony_ci				      lower 5 Audiodatabits ??? */
32462306a36Sopenharmony_ci#define HDSPM_taxi_reset   (1<<20) /* ??? */ /* MADI ONLY ? */
32562306a36Sopenharmony_ci#define HDSPM_WCK48        (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci#define HDSPM_Midi0InterruptEnable 0x0400000
32862306a36Sopenharmony_ci#define HDSPM_Midi1InterruptEnable 0x0800000
32962306a36Sopenharmony_ci#define HDSPM_Midi2InterruptEnable 0x0200000
33062306a36Sopenharmony_ci#define HDSPM_Midi3InterruptEnable 0x4000000
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci#define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
33362306a36Sopenharmony_ci#define HDSPe_FLOAT_FORMAT         0x2000000
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci#define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
33662306a36Sopenharmony_ci#define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
33762306a36Sopenharmony_ci#define HDSPM_QS_QuadWire   (1<<28) /* AES32 ONLY */
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci#define HDSPM_wclk_sel (1<<30)
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/* additional control register bits for AIO*/
34262306a36Sopenharmony_ci#define HDSPM_c0_Wck48				0x20 /* also RayDAT */
34362306a36Sopenharmony_ci#define HDSPM_c0_Input0				0x1000
34462306a36Sopenharmony_ci#define HDSPM_c0_Input1				0x2000
34562306a36Sopenharmony_ci#define HDSPM_c0_Spdif_Opt			0x4000
34662306a36Sopenharmony_ci#define HDSPM_c0_Pro				0x8000
34762306a36Sopenharmony_ci#define HDSPM_c0_clr_tms			0x10000
34862306a36Sopenharmony_ci#define HDSPM_c0_AEB1				0x20000
34962306a36Sopenharmony_ci#define HDSPM_c0_AEB2				0x40000
35062306a36Sopenharmony_ci#define HDSPM_c0_LineOut			0x80000
35162306a36Sopenharmony_ci#define HDSPM_c0_AD_GAIN0			0x100000
35262306a36Sopenharmony_ci#define HDSPM_c0_AD_GAIN1			0x200000
35362306a36Sopenharmony_ci#define HDSPM_c0_DA_GAIN0			0x400000
35462306a36Sopenharmony_ci#define HDSPM_c0_DA_GAIN1			0x800000
35562306a36Sopenharmony_ci#define HDSPM_c0_PH_GAIN0			0x1000000
35662306a36Sopenharmony_ci#define HDSPM_c0_PH_GAIN1			0x2000000
35762306a36Sopenharmony_ci#define HDSPM_c0_Sym6db				0x4000000
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/* --- bit helper defines */
36162306a36Sopenharmony_ci#define HDSPM_LatencyMask    (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
36262306a36Sopenharmony_ci#define HDSPM_FrequencyMask  (HDSPM_Frequency0|HDSPM_Frequency1|\
36362306a36Sopenharmony_ci			      HDSPM_DoubleSpeed|HDSPM_QuadSpeed)
36462306a36Sopenharmony_ci#define HDSPM_InputMask      (HDSPM_InputSelect0|HDSPM_InputSelect1)
36562306a36Sopenharmony_ci#define HDSPM_InputOptical   0
36662306a36Sopenharmony_ci#define HDSPM_InputCoaxial   (HDSPM_InputSelect0)
36762306a36Sopenharmony_ci#define HDSPM_SyncRefMask    (HDSPM_SyncRef0|HDSPM_SyncRef1|\
36862306a36Sopenharmony_ci			      HDSPM_SyncRef2|HDSPM_SyncRef3)
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci#define HDSPM_c0_SyncRef0      0x2
37162306a36Sopenharmony_ci#define HDSPM_c0_SyncRef1      0x4
37262306a36Sopenharmony_ci#define HDSPM_c0_SyncRef2      0x8
37362306a36Sopenharmony_ci#define HDSPM_c0_SyncRef3      0x10
37462306a36Sopenharmony_ci#define HDSPM_c0_SyncRefMask   (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\
37562306a36Sopenharmony_ci				HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3)
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci#define HDSPM_SYNC_FROM_WORD    0	/* Preferred sync reference */
37862306a36Sopenharmony_ci#define HDSPM_SYNC_FROM_MADI    1	/* choices - used by "pref_sync_ref" */
37962306a36Sopenharmony_ci#define HDSPM_SYNC_FROM_TCO     2
38062306a36Sopenharmony_ci#define HDSPM_SYNC_FROM_SYNC_IN 3
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci#define HDSPM_Frequency32KHz    HDSPM_Frequency0
38362306a36Sopenharmony_ci#define HDSPM_Frequency44_1KHz  HDSPM_Frequency1
38462306a36Sopenharmony_ci#define HDSPM_Frequency48KHz   (HDSPM_Frequency1|HDSPM_Frequency0)
38562306a36Sopenharmony_ci#define HDSPM_Frequency64KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency0)
38662306a36Sopenharmony_ci#define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
38762306a36Sopenharmony_ci#define HDSPM_Frequency96KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency1|\
38862306a36Sopenharmony_ci				HDSPM_Frequency0)
38962306a36Sopenharmony_ci#define HDSPM_Frequency128KHz   (HDSPM_QuadSpeed|HDSPM_Frequency0)
39062306a36Sopenharmony_ci#define HDSPM_Frequency176_4KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1)
39162306a36Sopenharmony_ci#define HDSPM_Frequency192KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1|\
39262306a36Sopenharmony_ci				 HDSPM_Frequency0)
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/* Synccheck Status */
39662306a36Sopenharmony_ci#define HDSPM_SYNC_CHECK_NO_LOCK 0
39762306a36Sopenharmony_ci#define HDSPM_SYNC_CHECK_LOCK    1
39862306a36Sopenharmony_ci#define HDSPM_SYNC_CHECK_SYNC	 2
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/* AutoSync References - used by "autosync_ref" control switch */
40162306a36Sopenharmony_ci#define HDSPM_AUTOSYNC_FROM_WORD      0
40262306a36Sopenharmony_ci#define HDSPM_AUTOSYNC_FROM_MADI      1
40362306a36Sopenharmony_ci#define HDSPM_AUTOSYNC_FROM_TCO       2
40462306a36Sopenharmony_ci#define HDSPM_AUTOSYNC_FROM_SYNC_IN   3
40562306a36Sopenharmony_ci#define HDSPM_AUTOSYNC_FROM_NONE      4
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/* Possible sources of MADI input */
40862306a36Sopenharmony_ci#define HDSPM_OPTICAL 0		/* optical   */
40962306a36Sopenharmony_ci#define HDSPM_COAXIAL 1		/* BNC */
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci#define hdspm_encode_latency(x)       (((x)<<1) & HDSPM_LatencyMask)
41262306a36Sopenharmony_ci#define hdspm_decode_latency(x)       ((((x) & HDSPM_LatencyMask)>>1))
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci#define hdspm_encode_in(x) (((x)&0x3)<<14)
41562306a36Sopenharmony_ci#define hdspm_decode_in(x) (((x)>>14)&0x3)
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/* --- control2 register bits --- */
41862306a36Sopenharmony_ci#define HDSPM_TMS             (1<<0)
41962306a36Sopenharmony_ci#define HDSPM_TCK             (1<<1)
42062306a36Sopenharmony_ci#define HDSPM_TDI             (1<<2)
42162306a36Sopenharmony_ci#define HDSPM_JTAG            (1<<3)
42262306a36Sopenharmony_ci#define HDSPM_PWDN            (1<<4)
42362306a36Sopenharmony_ci#define HDSPM_PROGRAM	      (1<<5)
42462306a36Sopenharmony_ci#define HDSPM_CONFIG_MODE_0   (1<<6)
42562306a36Sopenharmony_ci#define HDSPM_CONFIG_MODE_1   (1<<7)
42662306a36Sopenharmony_ci/*#define HDSPM_VERSION_BIT     (1<<8) not defined any more*/
42762306a36Sopenharmony_ci#define HDSPM_BIGENDIAN_MODE  (1<<9)
42862306a36Sopenharmony_ci#define HDSPM_RD_MULTIPLE     (1<<10)
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci/* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and
43162306a36Sopenharmony_ci     that do not conflict with specific bits for AES32 seem to be valid also
43262306a36Sopenharmony_ci     for the AES32
43362306a36Sopenharmony_ci */
43462306a36Sopenharmony_ci#define HDSPM_audioIRQPending    (1<<0)	/* IRQ is high and pending */
43562306a36Sopenharmony_ci#define HDSPM_RX_64ch            (1<<1)	/* Input 64chan. MODE=1, 56chn MODE=0 */
43662306a36Sopenharmony_ci#define HDSPM_AB_int             (1<<2)	/* InputChannel Opt=0, Coax=1
43762306a36Sopenharmony_ci					 * (like inp0)
43862306a36Sopenharmony_ci					 */
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci#define HDSPM_madiLock           (1<<3)	/* MADI Locked =1, no=0 */
44162306a36Sopenharmony_ci#define HDSPM_madiSync          (1<<18) /* MADI is in sync */
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci#define HDSPM_tcoLockMadi    0x00000020 /* Optional TCO locked status for HDSPe MADI*/
44462306a36Sopenharmony_ci#define HDSPM_tcoSync    0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci#define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */
44762306a36Sopenharmony_ci#define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci#define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
45062306a36Sopenharmony_ci			/* since 64byte accurate, last 6 bits are not used */
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci#define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci#define HDSPM_madiFreq0         (1<<22)	/* system freq 0=error */
45762306a36Sopenharmony_ci#define HDSPM_madiFreq1         (1<<23)	/* 1=32, 2=44.1 3=48 */
45862306a36Sopenharmony_ci#define HDSPM_madiFreq2         (1<<24)	/* 4=64, 5=88.2 6=96 */
45962306a36Sopenharmony_ci#define HDSPM_madiFreq3         (1<<25)	/* 7=128, 8=176.4 9=192 */
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci#define HDSPM_BufferID          (1<<26)	/* (Double)Buffer ID toggles with
46262306a36Sopenharmony_ci					 * Interrupt
46362306a36Sopenharmony_ci					 */
46462306a36Sopenharmony_ci#define HDSPM_tco_detect         0x08000000
46562306a36Sopenharmony_ci#define HDSPM_tcoLockAes         0x20000000 /* Optional TCO locked status for HDSPe AES */
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci#define HDSPM_s2_tco_detect      0x00000040
46862306a36Sopenharmony_ci#define HDSPM_s2_AEBO_D          0x00000080
46962306a36Sopenharmony_ci#define HDSPM_s2_AEBI_D          0x00000100
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci#define HDSPM_midi0IRQPending    0x40000000
47362306a36Sopenharmony_ci#define HDSPM_midi1IRQPending    0x80000000
47462306a36Sopenharmony_ci#define HDSPM_midi2IRQPending    0x20000000
47562306a36Sopenharmony_ci#define HDSPM_midi2IRQPendingAES 0x00000020
47662306a36Sopenharmony_ci#define HDSPM_midi3IRQPending    0x00200000
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/* --- status bit helpers */
47962306a36Sopenharmony_ci#define HDSPM_madiFreqMask  (HDSPM_madiFreq0|HDSPM_madiFreq1|\
48062306a36Sopenharmony_ci			     HDSPM_madiFreq2|HDSPM_madiFreq3)
48162306a36Sopenharmony_ci#define HDSPM_madiFreq32    (HDSPM_madiFreq0)
48262306a36Sopenharmony_ci#define HDSPM_madiFreq44_1  (HDSPM_madiFreq1)
48362306a36Sopenharmony_ci#define HDSPM_madiFreq48    (HDSPM_madiFreq0|HDSPM_madiFreq1)
48462306a36Sopenharmony_ci#define HDSPM_madiFreq64    (HDSPM_madiFreq2)
48562306a36Sopenharmony_ci#define HDSPM_madiFreq88_2  (HDSPM_madiFreq0|HDSPM_madiFreq2)
48662306a36Sopenharmony_ci#define HDSPM_madiFreq96    (HDSPM_madiFreq1|HDSPM_madiFreq2)
48762306a36Sopenharmony_ci#define HDSPM_madiFreq128   (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2)
48862306a36Sopenharmony_ci#define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
48962306a36Sopenharmony_ci#define HDSPM_madiFreq192   (HDSPM_madiFreq3|HDSPM_madiFreq0)
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/* Status2 Register bits */ /* MADI ONLY */
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci#define HDSPM_version0 (1<<0)	/* not really defined but I guess */
49462306a36Sopenharmony_ci#define HDSPM_version1 (1<<1)	/* in former cards it was ??? */
49562306a36Sopenharmony_ci#define HDSPM_version2 (1<<2)
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci#define HDSPM_wcLock (1<<3)	/* Wordclock is detected and locked */
49862306a36Sopenharmony_ci#define HDSPM_wcSync (1<<4)	/* Wordclock is in sync with systemclock */
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci#define HDSPM_wc_freq0 (1<<5)	/* input freq detected via autosync  */
50162306a36Sopenharmony_ci#define HDSPM_wc_freq1 (1<<6)	/* 001=32, 010==44.1, 011=48, */
50262306a36Sopenharmony_ci#define HDSPM_wc_freq2 (1<<7)	/* 100=64, 101=88.2, 110=96, 111=128 */
50362306a36Sopenharmony_ci#define HDSPM_wc_freq3 0x800	/* 1000=176.4, 1001=192 */
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci#define HDSPM_SyncRef0 0x10000  /* Sync Reference */
50662306a36Sopenharmony_ci#define HDSPM_SyncRef1 0x20000
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci#define HDSPM_SelSyncRef0 (1<<8)	/* AutoSync Source */
50962306a36Sopenharmony_ci#define HDSPM_SelSyncRef1 (1<<9)	/* 000=word, 001=MADI, */
51062306a36Sopenharmony_ci#define HDSPM_SelSyncRef2 (1<<10)	/* 111=no valid signal */
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci#define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci#define HDSPM_wcFreqMask  (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2|\
51562306a36Sopenharmony_ci			    HDSPM_wc_freq3)
51662306a36Sopenharmony_ci#define HDSPM_wcFreq32    (HDSPM_wc_freq0)
51762306a36Sopenharmony_ci#define HDSPM_wcFreq44_1  (HDSPM_wc_freq1)
51862306a36Sopenharmony_ci#define HDSPM_wcFreq48    (HDSPM_wc_freq0|HDSPM_wc_freq1)
51962306a36Sopenharmony_ci#define HDSPM_wcFreq64    (HDSPM_wc_freq2)
52062306a36Sopenharmony_ci#define HDSPM_wcFreq88_2  (HDSPM_wc_freq0|HDSPM_wc_freq2)
52162306a36Sopenharmony_ci#define HDSPM_wcFreq96    (HDSPM_wc_freq1|HDSPM_wc_freq2)
52262306a36Sopenharmony_ci#define HDSPM_wcFreq128   (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
52362306a36Sopenharmony_ci#define HDSPM_wcFreq176_4 (HDSPM_wc_freq3)
52462306a36Sopenharmony_ci#define HDSPM_wcFreq192   (HDSPM_wc_freq0|HDSPM_wc_freq3)
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci#define HDSPM_status1_F_0 0x0400000
52762306a36Sopenharmony_ci#define HDSPM_status1_F_1 0x0800000
52862306a36Sopenharmony_ci#define HDSPM_status1_F_2 0x1000000
52962306a36Sopenharmony_ci#define HDSPM_status1_F_3 0x2000000
53062306a36Sopenharmony_ci#define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3)
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci#define HDSPM_SelSyncRefMask       (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
53462306a36Sopenharmony_ci				    HDSPM_SelSyncRef2)
53562306a36Sopenharmony_ci#define HDSPM_SelSyncRef_WORD      0
53662306a36Sopenharmony_ci#define HDSPM_SelSyncRef_MADI      (HDSPM_SelSyncRef0)
53762306a36Sopenharmony_ci#define HDSPM_SelSyncRef_TCO       (HDSPM_SelSyncRef1)
53862306a36Sopenharmony_ci#define HDSPM_SelSyncRef_SyncIn    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1)
53962306a36Sopenharmony_ci#define HDSPM_SelSyncRef_NVALID    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
54062306a36Sopenharmony_ci				    HDSPM_SelSyncRef2)
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/*
54362306a36Sopenharmony_ci   For AES32, bits for status, status2 and timecode are different
54462306a36Sopenharmony_ci*/
54562306a36Sopenharmony_ci/* status */
54662306a36Sopenharmony_ci#define HDSPM_AES32_wcLock	0x0200000
54762306a36Sopenharmony_ci#define HDSPM_AES32_wcSync	0x0100000
54862306a36Sopenharmony_ci#define HDSPM_AES32_wcFreq_bit  22
54962306a36Sopenharmony_ci/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
55062306a36Sopenharmony_ci  HDSPM_bit2freq */
55162306a36Sopenharmony_ci#define HDSPM_AES32_syncref_bit  16
55262306a36Sopenharmony_ci/* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_WORD 0
55562306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES1 1
55662306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES2 2
55762306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES3 3
55862306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES4 4
55962306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES5 5
56062306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
56162306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
56262306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
56362306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_TCO 9
56462306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10
56562306a36Sopenharmony_ci#define HDSPM_AES32_AUTOSYNC_FROM_NONE 11
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci/*  status2 */
56862306a36Sopenharmony_ci/* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
56962306a36Sopenharmony_ci#define HDSPM_LockAES   0x80
57062306a36Sopenharmony_ci#define HDSPM_LockAES1  0x80
57162306a36Sopenharmony_ci#define HDSPM_LockAES2  0x40
57262306a36Sopenharmony_ci#define HDSPM_LockAES3  0x20
57362306a36Sopenharmony_ci#define HDSPM_LockAES4  0x10
57462306a36Sopenharmony_ci#define HDSPM_LockAES5  0x8
57562306a36Sopenharmony_ci#define HDSPM_LockAES6  0x4
57662306a36Sopenharmony_ci#define HDSPM_LockAES7  0x2
57762306a36Sopenharmony_ci#define HDSPM_LockAES8  0x1
57862306a36Sopenharmony_ci/*
57962306a36Sopenharmony_ci   Timecode
58062306a36Sopenharmony_ci   After windows driver sources, bits 4*i to 4*i+3 give the input frequency on
58162306a36Sopenharmony_ci   AES i+1
58262306a36Sopenharmony_ci bits 3210
58362306a36Sopenharmony_ci      0001  32kHz
58462306a36Sopenharmony_ci      0010  44.1kHz
58562306a36Sopenharmony_ci      0011  48kHz
58662306a36Sopenharmony_ci      0100  64kHz
58762306a36Sopenharmony_ci      0101  88.2kHz
58862306a36Sopenharmony_ci      0110  96kHz
58962306a36Sopenharmony_ci      0111  128kHz
59062306a36Sopenharmony_ci      1000  176.4kHz
59162306a36Sopenharmony_ci      1001  192kHz
59262306a36Sopenharmony_ci  NB: Timecode register doesn't seem to work on AES32 card revision 230
59362306a36Sopenharmony_ci*/
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci/* Mixer Values */
59662306a36Sopenharmony_ci#define UNITY_GAIN          32768	/* = 65536/2 */
59762306a36Sopenharmony_ci#define MINUS_INFINITY_GAIN 0
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/* Number of channels for different Speed Modes */
60062306a36Sopenharmony_ci#define MADI_SS_CHANNELS       64
60162306a36Sopenharmony_ci#define MADI_DS_CHANNELS       32
60262306a36Sopenharmony_ci#define MADI_QS_CHANNELS       16
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci#define RAYDAT_SS_CHANNELS     36
60562306a36Sopenharmony_ci#define RAYDAT_DS_CHANNELS     20
60662306a36Sopenharmony_ci#define RAYDAT_QS_CHANNELS     12
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci#define AIO_IN_SS_CHANNELS        14
60962306a36Sopenharmony_ci#define AIO_IN_DS_CHANNELS        10
61062306a36Sopenharmony_ci#define AIO_IN_QS_CHANNELS        8
61162306a36Sopenharmony_ci#define AIO_OUT_SS_CHANNELS        16
61262306a36Sopenharmony_ci#define AIO_OUT_DS_CHANNELS        12
61362306a36Sopenharmony_ci#define AIO_OUT_QS_CHANNELS        10
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci#define AES32_CHANNELS		16
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/* the size of a substream (1 mono data stream) */
61862306a36Sopenharmony_ci#define HDSPM_CHANNEL_BUFFER_SAMPLES  (16*1024)
61962306a36Sopenharmony_ci#define HDSPM_CHANNEL_BUFFER_BYTES    (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/* the size of the area we need to allocate for DMA transfers. the
62262306a36Sopenharmony_ci   size is the same regardless of the number of channels, and
62362306a36Sopenharmony_ci   also the latency to use.
62462306a36Sopenharmony_ci   for one direction !!!
62562306a36Sopenharmony_ci*/
62662306a36Sopenharmony_ci#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
62762306a36Sopenharmony_ci#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci#define HDSPM_RAYDAT_REV	211
63062306a36Sopenharmony_ci#define HDSPM_AIO_REV		212
63162306a36Sopenharmony_ci#define HDSPM_MADIFACE_REV	213
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci/* speed factor modes */
63462306a36Sopenharmony_ci#define HDSPM_SPEED_SINGLE 0
63562306a36Sopenharmony_ci#define HDSPM_SPEED_DOUBLE 1
63662306a36Sopenharmony_ci#define HDSPM_SPEED_QUAD   2
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/* names for speed modes */
63962306a36Sopenharmony_cistatic const char * const hdspm_speed_names[] = { "single", "double", "quad" };
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic const char *const texts_autosync_aes_tco[] = { "Word Clock",
64262306a36Sopenharmony_ci					  "AES1", "AES2", "AES3", "AES4",
64362306a36Sopenharmony_ci					  "AES5", "AES6", "AES7", "AES8",
64462306a36Sopenharmony_ci					  "TCO", "Sync In"
64562306a36Sopenharmony_ci};
64662306a36Sopenharmony_cistatic const char *const texts_autosync_aes[] = { "Word Clock",
64762306a36Sopenharmony_ci				      "AES1", "AES2", "AES3", "AES4",
64862306a36Sopenharmony_ci				      "AES5", "AES6", "AES7", "AES8",
64962306a36Sopenharmony_ci				      "Sync In"
65062306a36Sopenharmony_ci};
65162306a36Sopenharmony_cistatic const char *const texts_autosync_madi_tco[] = { "Word Clock",
65262306a36Sopenharmony_ci					   "MADI", "TCO", "Sync In" };
65362306a36Sopenharmony_cistatic const char *const texts_autosync_madi[] = { "Word Clock",
65462306a36Sopenharmony_ci				       "MADI", "Sync In" };
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic const char *const texts_autosync_raydat_tco[] = {
65762306a36Sopenharmony_ci	"Word Clock",
65862306a36Sopenharmony_ci	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
65962306a36Sopenharmony_ci	"AES", "SPDIF", "TCO", "Sync In"
66062306a36Sopenharmony_ci};
66162306a36Sopenharmony_cistatic const char *const texts_autosync_raydat[] = {
66262306a36Sopenharmony_ci	"Word Clock",
66362306a36Sopenharmony_ci	"ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
66462306a36Sopenharmony_ci	"AES", "SPDIF", "Sync In"
66562306a36Sopenharmony_ci};
66662306a36Sopenharmony_cistatic const char *const texts_autosync_aio_tco[] = {
66762306a36Sopenharmony_ci	"Word Clock",
66862306a36Sopenharmony_ci	"ADAT", "AES", "SPDIF", "TCO", "Sync In"
66962306a36Sopenharmony_ci};
67062306a36Sopenharmony_cistatic const char *const texts_autosync_aio[] = { "Word Clock",
67162306a36Sopenharmony_ci				      "ADAT", "AES", "SPDIF", "Sync In" };
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic const char *const texts_freq[] = {
67462306a36Sopenharmony_ci	"No Lock",
67562306a36Sopenharmony_ci	"32 kHz",
67662306a36Sopenharmony_ci	"44.1 kHz",
67762306a36Sopenharmony_ci	"48 kHz",
67862306a36Sopenharmony_ci	"64 kHz",
67962306a36Sopenharmony_ci	"88.2 kHz",
68062306a36Sopenharmony_ci	"96 kHz",
68162306a36Sopenharmony_ci	"128 kHz",
68262306a36Sopenharmony_ci	"176.4 kHz",
68362306a36Sopenharmony_ci	"192 kHz"
68462306a36Sopenharmony_ci};
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic const char * const texts_ports_madi[] = {
68762306a36Sopenharmony_ci	"MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6",
68862306a36Sopenharmony_ci	"MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12",
68962306a36Sopenharmony_ci	"MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18",
69062306a36Sopenharmony_ci	"MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24",
69162306a36Sopenharmony_ci	"MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30",
69262306a36Sopenharmony_ci	"MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36",
69362306a36Sopenharmony_ci	"MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42",
69462306a36Sopenharmony_ci	"MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48",
69562306a36Sopenharmony_ci	"MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54",
69662306a36Sopenharmony_ci	"MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60",
69762306a36Sopenharmony_ci	"MADI.61", "MADI.62", "MADI.63", "MADI.64",
69862306a36Sopenharmony_ci};
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic const char * const texts_ports_raydat_ss[] = {
70262306a36Sopenharmony_ci	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6",
70362306a36Sopenharmony_ci	"ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
70462306a36Sopenharmony_ci	"ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2",
70562306a36Sopenharmony_ci	"ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8",
70662306a36Sopenharmony_ci	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6",
70762306a36Sopenharmony_ci	"ADAT4.7", "ADAT4.8",
70862306a36Sopenharmony_ci	"AES.L", "AES.R",
70962306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R"
71062306a36Sopenharmony_ci};
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic const char * const texts_ports_raydat_ds[] = {
71362306a36Sopenharmony_ci	"ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4",
71462306a36Sopenharmony_ci	"ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
71562306a36Sopenharmony_ci	"ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4",
71662306a36Sopenharmony_ci	"ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4",
71762306a36Sopenharmony_ci	"AES.L", "AES.R",
71862306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R"
71962306a36Sopenharmony_ci};
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic const char * const texts_ports_raydat_qs[] = {
72262306a36Sopenharmony_ci	"ADAT1.1", "ADAT1.2",
72362306a36Sopenharmony_ci	"ADAT2.1", "ADAT2.2",
72462306a36Sopenharmony_ci	"ADAT3.1", "ADAT3.2",
72562306a36Sopenharmony_ci	"ADAT4.1", "ADAT4.2",
72662306a36Sopenharmony_ci	"AES.L", "AES.R",
72762306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R"
72862306a36Sopenharmony_ci};
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic const char * const texts_ports_aio_in_ss[] = {
73262306a36Sopenharmony_ci	"Analogue.L", "Analogue.R",
73362306a36Sopenharmony_ci	"AES.L", "AES.R",
73462306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R",
73562306a36Sopenharmony_ci	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
73662306a36Sopenharmony_ci	"ADAT.7", "ADAT.8",
73762306a36Sopenharmony_ci	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
73862306a36Sopenharmony_ci};
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic const char * const texts_ports_aio_out_ss[] = {
74162306a36Sopenharmony_ci	"Analogue.L", "Analogue.R",
74262306a36Sopenharmony_ci	"AES.L", "AES.R",
74362306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R",
74462306a36Sopenharmony_ci	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
74562306a36Sopenharmony_ci	"ADAT.7", "ADAT.8",
74662306a36Sopenharmony_ci	"Phone.L", "Phone.R",
74762306a36Sopenharmony_ci	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
74862306a36Sopenharmony_ci};
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic const char * const texts_ports_aio_in_ds[] = {
75162306a36Sopenharmony_ci	"Analogue.L", "Analogue.R",
75262306a36Sopenharmony_ci	"AES.L", "AES.R",
75362306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R",
75462306a36Sopenharmony_ci	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
75562306a36Sopenharmony_ci	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
75662306a36Sopenharmony_ci};
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic const char * const texts_ports_aio_out_ds[] = {
75962306a36Sopenharmony_ci	"Analogue.L", "Analogue.R",
76062306a36Sopenharmony_ci	"AES.L", "AES.R",
76162306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R",
76262306a36Sopenharmony_ci	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
76362306a36Sopenharmony_ci	"Phone.L", "Phone.R",
76462306a36Sopenharmony_ci	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
76562306a36Sopenharmony_ci};
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic const char * const texts_ports_aio_in_qs[] = {
76862306a36Sopenharmony_ci	"Analogue.L", "Analogue.R",
76962306a36Sopenharmony_ci	"AES.L", "AES.R",
77062306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R",
77162306a36Sopenharmony_ci	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
77262306a36Sopenharmony_ci	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
77362306a36Sopenharmony_ci};
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic const char * const texts_ports_aio_out_qs[] = {
77662306a36Sopenharmony_ci	"Analogue.L", "Analogue.R",
77762306a36Sopenharmony_ci	"AES.L", "AES.R",
77862306a36Sopenharmony_ci	"SPDIF.L", "SPDIF.R",
77962306a36Sopenharmony_ci	"ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
78062306a36Sopenharmony_ci	"Phone.L", "Phone.R",
78162306a36Sopenharmony_ci	"AEB.1", "AEB.2", "AEB.3", "AEB.4"
78262306a36Sopenharmony_ci};
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic const char * const texts_ports_aes32[] = {
78562306a36Sopenharmony_ci	"AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7",
78662306a36Sopenharmony_ci	"AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14",
78762306a36Sopenharmony_ci	"AES.15", "AES.16"
78862306a36Sopenharmony_ci};
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci/* These tables map the ALSA channels 1..N to the channels that we
79162306a36Sopenharmony_ci   need to use in order to find the relevant channel buffer. RME
79262306a36Sopenharmony_ci   refers to this kind of mapping as between "the ADAT channel and
79362306a36Sopenharmony_ci   the DMA channel." We index it using the logical audio channel,
79462306a36Sopenharmony_ci   and the value is the DMA channel (i.e. channel buffer number)
79562306a36Sopenharmony_ci   where the data for that channel can be read/written from/to.
79662306a36Sopenharmony_ci*/
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic const char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
79962306a36Sopenharmony_ci	0, 1, 2, 3, 4, 5, 6, 7,
80062306a36Sopenharmony_ci	8, 9, 10, 11, 12, 13, 14, 15,
80162306a36Sopenharmony_ci	16, 17, 18, 19, 20, 21, 22, 23,
80262306a36Sopenharmony_ci	24, 25, 26, 27, 28, 29, 30, 31,
80362306a36Sopenharmony_ci	32, 33, 34, 35, 36, 37, 38, 39,
80462306a36Sopenharmony_ci	40, 41, 42, 43, 44, 45, 46, 47,
80562306a36Sopenharmony_ci	48, 49, 50, 51, 52, 53, 54, 55,
80662306a36Sopenharmony_ci	56, 57, 58, 59, 60, 61, 62, 63
80762306a36Sopenharmony_ci};
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic const char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
81062306a36Sopenharmony_ci	4, 5, 6, 7, 8, 9, 10, 11,	/* ADAT 1 */
81162306a36Sopenharmony_ci	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT 2 */
81262306a36Sopenharmony_ci	20, 21, 22, 23, 24, 25, 26, 27,	/* ADAT 3 */
81362306a36Sopenharmony_ci	28, 29, 30, 31, 32, 33, 34, 35,	/* ADAT 4 */
81462306a36Sopenharmony_ci	0, 1,			/* AES */
81562306a36Sopenharmony_ci	2, 3,			/* SPDIF */
81662306a36Sopenharmony_ci	-1, -1, -1, -1,
81762306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
81862306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
81962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
82062306a36Sopenharmony_ci};
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic const char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
82362306a36Sopenharmony_ci	4, 5, 6, 7,		/* ADAT 1 */
82462306a36Sopenharmony_ci	8, 9, 10, 11,		/* ADAT 2 */
82562306a36Sopenharmony_ci	12, 13, 14, 15,		/* ADAT 3 */
82662306a36Sopenharmony_ci	16, 17, 18, 19,		/* ADAT 4 */
82762306a36Sopenharmony_ci	0, 1,			/* AES */
82862306a36Sopenharmony_ci	2, 3,			/* SPDIF */
82962306a36Sopenharmony_ci	-1, -1, -1, -1,
83062306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
83162306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
83262306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
83362306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
83462306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
83562306a36Sopenharmony_ci};
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic const char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
83862306a36Sopenharmony_ci	4, 5,			/* ADAT 1 */
83962306a36Sopenharmony_ci	6, 7,			/* ADAT 2 */
84062306a36Sopenharmony_ci	8, 9,			/* ADAT 3 */
84162306a36Sopenharmony_ci	10, 11,			/* ADAT 4 */
84262306a36Sopenharmony_ci	0, 1,			/* AES */
84362306a36Sopenharmony_ci	2, 3,			/* SPDIF */
84462306a36Sopenharmony_ci	-1, -1, -1, -1,
84562306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
84662306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
84762306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
84862306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
84962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
85062306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
85162306a36Sopenharmony_ci};
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic const char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
85462306a36Sopenharmony_ci	0, 1,			/* line in */
85562306a36Sopenharmony_ci	8, 9,			/* aes in, */
85662306a36Sopenharmony_ci	10, 11,			/* spdif in */
85762306a36Sopenharmony_ci	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT in */
85862306a36Sopenharmony_ci	2, 3, 4, 5,		/* AEB */
85962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1,
86062306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
86162306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
86262306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
86362306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
86462306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
86562306a36Sopenharmony_ci};
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic const char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
86862306a36Sopenharmony_ci	0, 1,			/* line out */
86962306a36Sopenharmony_ci	8, 9,			/* aes out */
87062306a36Sopenharmony_ci	10, 11,			/* spdif out */
87162306a36Sopenharmony_ci	12, 13, 14, 15, 16, 17, 18, 19,	/* ADAT out */
87262306a36Sopenharmony_ci	6, 7,			/* phone out */
87362306a36Sopenharmony_ci	2, 3, 4, 5,		/* AEB */
87462306a36Sopenharmony_ci	-1, -1, -1, -1,
87562306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
87662306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
87762306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
87862306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
87962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
88062306a36Sopenharmony_ci};
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic const char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
88362306a36Sopenharmony_ci	0, 1,			/* line in */
88462306a36Sopenharmony_ci	8, 9,			/* aes in */
88562306a36Sopenharmony_ci	10, 11,			/* spdif in */
88662306a36Sopenharmony_ci	12, 14, 16, 18,		/* adat in */
88762306a36Sopenharmony_ci	2, 3, 4, 5,		/* AEB */
88862306a36Sopenharmony_ci	-1, -1,
88962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
89062306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
89162306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
89262306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
89362306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
89462306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1
89562306a36Sopenharmony_ci};
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic const char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
89862306a36Sopenharmony_ci	0, 1,			/* line out */
89962306a36Sopenharmony_ci	8, 9,			/* aes out */
90062306a36Sopenharmony_ci	10, 11,			/* spdif out */
90162306a36Sopenharmony_ci	12, 14, 16, 18,		/* adat out */
90262306a36Sopenharmony_ci	6, 7,			/* phone out */
90362306a36Sopenharmony_ci	2, 3, 4, 5,		/* AEB */
90462306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
90562306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
90662306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
90762306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
90862306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
90962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1
91062306a36Sopenharmony_ci};
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic const char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
91362306a36Sopenharmony_ci	0, 1,			/* line in */
91462306a36Sopenharmony_ci	8, 9,			/* aes in */
91562306a36Sopenharmony_ci	10, 11,			/* spdif in */
91662306a36Sopenharmony_ci	12, 16,			/* adat in */
91762306a36Sopenharmony_ci	2, 3, 4, 5,		/* AEB */
91862306a36Sopenharmony_ci	-1, -1, -1, -1,
91962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
92062306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
92162306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
92262306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
92362306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
92462306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1
92562306a36Sopenharmony_ci};
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic const char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
92862306a36Sopenharmony_ci	0, 1,			/* line out */
92962306a36Sopenharmony_ci	8, 9,			/* aes out */
93062306a36Sopenharmony_ci	10, 11,			/* spdif out */
93162306a36Sopenharmony_ci	12, 16,			/* adat out */
93262306a36Sopenharmony_ci	6, 7,			/* phone out */
93362306a36Sopenharmony_ci	2, 3, 4, 5,		/* AEB */
93462306a36Sopenharmony_ci	-1, -1,
93562306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
93662306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
93762306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
93862306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
93962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
94062306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1
94162306a36Sopenharmony_ci};
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic const char channel_map_aes32[HDSPM_MAX_CHANNELS] = {
94462306a36Sopenharmony_ci	0, 1, 2, 3, 4, 5, 6, 7,
94562306a36Sopenharmony_ci	8, 9, 10, 11, 12, 13, 14, 15,
94662306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
94762306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
94862306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
94962306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
95062306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
95162306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1
95262306a36Sopenharmony_ci};
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistruct hdspm_midi {
95562306a36Sopenharmony_ci	struct hdspm *hdspm;
95662306a36Sopenharmony_ci	int id;
95762306a36Sopenharmony_ci	struct snd_rawmidi *rmidi;
95862306a36Sopenharmony_ci	struct snd_rawmidi_substream *input;
95962306a36Sopenharmony_ci	struct snd_rawmidi_substream *output;
96062306a36Sopenharmony_ci	char istimer;		/* timer in use */
96162306a36Sopenharmony_ci	struct timer_list timer;
96262306a36Sopenharmony_ci	spinlock_t lock;
96362306a36Sopenharmony_ci	int pending;
96462306a36Sopenharmony_ci	int dataIn;
96562306a36Sopenharmony_ci	int statusIn;
96662306a36Sopenharmony_ci	int dataOut;
96762306a36Sopenharmony_ci	int statusOut;
96862306a36Sopenharmony_ci	int ie;
96962306a36Sopenharmony_ci	int irq;
97062306a36Sopenharmony_ci};
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistruct hdspm_tco {
97362306a36Sopenharmony_ci	int input; /* 0: LTC, 1:Video, 2: WC*/
97462306a36Sopenharmony_ci	int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */
97562306a36Sopenharmony_ci	int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */
97662306a36Sopenharmony_ci	int samplerate; /* 0=44.1, 1=48, 2= freq from app */
97762306a36Sopenharmony_ci	int pull; /*   0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/
97862306a36Sopenharmony_ci	int term; /* 0 = off, 1 = on */
97962306a36Sopenharmony_ci};
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistruct hdspm {
98262306a36Sopenharmony_ci        spinlock_t lock;
98362306a36Sopenharmony_ci	/* only one playback and/or capture stream */
98462306a36Sopenharmony_ci        struct snd_pcm_substream *capture_substream;
98562306a36Sopenharmony_ci        struct snd_pcm_substream *playback_substream;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	char *card_name;	     /* for procinfo */
98862306a36Sopenharmony_ci	unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	uint8_t io_type;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	int monitor_outs;	/* set up monitoring outs init flag */
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	u32 control_register;	/* cached value */
99562306a36Sopenharmony_ci	u32 control2_register;	/* cached value */
99662306a36Sopenharmony_ci	u32 settings_register;  /* cached value for AIO / RayDat (sync reference, master/slave) */
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	struct hdspm_midi midi[4];
99962306a36Sopenharmony_ci	struct work_struct midi_work;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	size_t period_bytes;
100262306a36Sopenharmony_ci	unsigned char ss_in_channels;
100362306a36Sopenharmony_ci	unsigned char ds_in_channels;
100462306a36Sopenharmony_ci	unsigned char qs_in_channels;
100562306a36Sopenharmony_ci	unsigned char ss_out_channels;
100662306a36Sopenharmony_ci	unsigned char ds_out_channels;
100762306a36Sopenharmony_ci	unsigned char qs_out_channels;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	unsigned char max_channels_in;
101062306a36Sopenharmony_ci	unsigned char max_channels_out;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	const signed char *channel_map_in;
101362306a36Sopenharmony_ci	const signed char *channel_map_out;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	const signed char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
101662306a36Sopenharmony_ci	const signed char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	const char * const *port_names_in;
101962306a36Sopenharmony_ci	const char * const *port_names_out;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	const char * const *port_names_in_ss;
102262306a36Sopenharmony_ci	const char * const *port_names_in_ds;
102362306a36Sopenharmony_ci	const char * const *port_names_in_qs;
102462306a36Sopenharmony_ci	const char * const *port_names_out_ss;
102562306a36Sopenharmony_ci	const char * const *port_names_out_ds;
102662306a36Sopenharmony_ci	const char * const *port_names_out_qs;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	unsigned char *playback_buffer;	/* suitably aligned address */
102962306a36Sopenharmony_ci	unsigned char *capture_buffer;	/* suitably aligned address */
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	pid_t capture_pid;	/* process id which uses capture */
103262306a36Sopenharmony_ci	pid_t playback_pid;	/* process id which uses capture */
103362306a36Sopenharmony_ci	int running;		/* running status */
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	int last_external_sample_rate;	/* samplerate mystic ... */
103662306a36Sopenharmony_ci	int last_internal_sample_rate;
103762306a36Sopenharmony_ci	int system_sample_rate;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	int dev;		/* Hardware vars... */
104062306a36Sopenharmony_ci	int irq;
104162306a36Sopenharmony_ci	unsigned long port;
104262306a36Sopenharmony_ci	void __iomem *iobase;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	int irq_count;		/* for debug */
104562306a36Sopenharmony_ci	int midiPorts;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	struct snd_card *card;	/* one card */
104862306a36Sopenharmony_ci	struct snd_pcm *pcm;		/* has one pcm */
104962306a36Sopenharmony_ci	struct snd_hwdep *hwdep;	/* and a hwdep for additional ioctl */
105062306a36Sopenharmony_ci	struct pci_dev *pci;	/* and an pci info */
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* Mixer vars */
105362306a36Sopenharmony_ci	/* fast alsa mixer */
105462306a36Sopenharmony_ci	struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS];
105562306a36Sopenharmony_ci	/* but input to much, so not used */
105662306a36Sopenharmony_ci	struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS];
105762306a36Sopenharmony_ci	/* full mixer accessible over mixer ioctl or hwdep-device */
105862306a36Sopenharmony_ci	struct hdspm_mixer *mixer;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	struct hdspm_tco *tco;  /* NULL if no TCO detected */
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	const char *const *texts_autosync;
106362306a36Sopenharmony_ci	int texts_autosync_items;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	cycles_t last_interrupt;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	unsigned int serial;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	struct hdspm_peak_rms peak_rms;
107062306a36Sopenharmony_ci};
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic const struct pci_device_id snd_hdspm_ids[] = {
107462306a36Sopenharmony_ci	{
107562306a36Sopenharmony_ci	 .vendor = PCI_VENDOR_ID_XILINX,
107662306a36Sopenharmony_ci	 .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
107762306a36Sopenharmony_ci	 .subvendor = PCI_ANY_ID,
107862306a36Sopenharmony_ci	 .subdevice = PCI_ANY_ID,
107962306a36Sopenharmony_ci	 .class = 0,
108062306a36Sopenharmony_ci	 .class_mask = 0,
108162306a36Sopenharmony_ci	 .driver_data = 0},
108262306a36Sopenharmony_ci	{0,}
108362306a36Sopenharmony_ci};
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_hdspm_ids);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci/* prototypes */
108862306a36Sopenharmony_cistatic int snd_hdspm_create_alsa_devices(struct snd_card *card,
108962306a36Sopenharmony_ci					 struct hdspm *hdspm);
109062306a36Sopenharmony_cistatic int snd_hdspm_create_pcm(struct snd_card *card,
109162306a36Sopenharmony_ci				struct hdspm *hdspm);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
109462306a36Sopenharmony_cistatic inline int hdspm_get_pll_freq(struct hdspm *hdspm);
109562306a36Sopenharmony_cistatic int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
109662306a36Sopenharmony_cistatic int hdspm_autosync_ref(struct hdspm *hdspm);
109762306a36Sopenharmony_cistatic int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out);
109862306a36Sopenharmony_cistatic int snd_hdspm_set_defaults(struct hdspm *hdspm);
109962306a36Sopenharmony_cistatic int hdspm_system_clock_mode(struct hdspm *hdspm);
110062306a36Sopenharmony_cistatic void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
110162306a36Sopenharmony_ci				       struct snd_pcm_substream *substream,
110262306a36Sopenharmony_ci				       unsigned int reg, int channels);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic int hdspm_aes_sync_check(struct hdspm *hdspm, int idx);
110562306a36Sopenharmony_cistatic int hdspm_wc_sync_check(struct hdspm *hdspm);
110662306a36Sopenharmony_cistatic int hdspm_tco_sync_check(struct hdspm *hdspm);
110762306a36Sopenharmony_cistatic int hdspm_sync_in_sync_check(struct hdspm *hdspm);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index);
111062306a36Sopenharmony_cistatic int hdspm_get_tco_sample_rate(struct hdspm *hdspm);
111162306a36Sopenharmony_cistatic int hdspm_get_wc_sample_rate(struct hdspm *hdspm);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic inline int HDSPM_bit2freq(int n)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	static const int bit2freq_tab[] = {
111862306a36Sopenharmony_ci		0, 32000, 44100, 48000, 64000, 88200,
111962306a36Sopenharmony_ci		96000, 128000, 176400, 192000 };
112062306a36Sopenharmony_ci	if (n < 1 || n > 9)
112162306a36Sopenharmony_ci		return 0;
112262306a36Sopenharmony_ci	return bit2freq_tab[n];
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_cistatic bool hdspm_is_raydat_or_aio(struct hdspm *hdspm)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type));
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci/* Write/read to/from HDSPM with Adresses in Bytes
113262306a36Sopenharmony_ci   not words but only 32Bit writes are allowed */
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic inline void hdspm_write(struct hdspm * hdspm, unsigned int reg,
113562306a36Sopenharmony_ci			       unsigned int val)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	writel(val, hdspm->iobase + reg);
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	return readl(hdspm->iobase + reg);
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
114662306a36Sopenharmony_ci   mixer is write only on hardware so we have to cache him for read
114762306a36Sopenharmony_ci   each fader is a u32, but uses only the first 16 bit */
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan,
115062306a36Sopenharmony_ci				     unsigned int in)
115162306a36Sopenharmony_ci{
115262306a36Sopenharmony_ci	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
115362306a36Sopenharmony_ci		return 0;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	return hdspm->mixer->ch[chan].in[in];
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic inline int hdspm_read_pb_gain(struct hdspm * hdspm, unsigned int chan,
115962306a36Sopenharmony_ci				     unsigned int pb)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
116262306a36Sopenharmony_ci		return 0;
116362306a36Sopenharmony_ci	return hdspm->mixer->ch[chan].pb[pb];
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int hdspm_write_in_gain(struct hdspm *hdspm, unsigned int chan,
116762306a36Sopenharmony_ci				      unsigned int in, unsigned short data)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
117062306a36Sopenharmony_ci		return -1;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	hdspm_write(hdspm,
117362306a36Sopenharmony_ci		    HDSPM_MADI_mixerBase +
117462306a36Sopenharmony_ci		    ((in + 128 * chan) * sizeof(u32)),
117562306a36Sopenharmony_ci		    (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF));
117662306a36Sopenharmony_ci	return 0;
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic int hdspm_write_pb_gain(struct hdspm *hdspm, unsigned int chan,
118062306a36Sopenharmony_ci				      unsigned int pb, unsigned short data)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
118362306a36Sopenharmony_ci		return -1;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	hdspm_write(hdspm,
118662306a36Sopenharmony_ci		    HDSPM_MADI_mixerBase +
118762306a36Sopenharmony_ci		    ((64 + pb + 128 * chan) * sizeof(u32)),
118862306a36Sopenharmony_ci		    (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF));
118962306a36Sopenharmony_ci	return 0;
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci/* enable DMA for specific channels, now available for DSP-MADI */
119462306a36Sopenharmony_cistatic inline void snd_hdspm_enable_in(struct hdspm * hdspm, int i, int v)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_inputEnableBase + (4 * i), v);
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_outputEnableBase + (4 * i), v);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci/* check if same process is writing and reading */
120562306a36Sopenharmony_cistatic int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	unsigned long flags;
120862306a36Sopenharmony_ci	int ret = 1;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	spin_lock_irqsave(&hdspm->lock, flags);
121162306a36Sopenharmony_ci	if ((hdspm->playback_pid != hdspm->capture_pid) &&
121262306a36Sopenharmony_ci	    (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) {
121362306a36Sopenharmony_ci		ret = 0;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci	spin_unlock_irqrestore(&hdspm->lock, flags);
121662306a36Sopenharmony_ci	return ret;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci/* round arbitrary sample rates to commonly known rates */
122062306a36Sopenharmony_cistatic int hdspm_round_frequency(int rate)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	if (rate < 38050)
122362306a36Sopenharmony_ci		return 32000;
122462306a36Sopenharmony_ci	if (rate < 46008)
122562306a36Sopenharmony_ci		return 44100;
122662306a36Sopenharmony_ci	else
122762306a36Sopenharmony_ci		return 48000;
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci/* QS and DS rates normally can not be detected
123162306a36Sopenharmony_ci * automatically by the card. Only exception is MADI
123262306a36Sopenharmony_ci * in 96k frame mode.
123362306a36Sopenharmony_ci *
123462306a36Sopenharmony_ci * So if we read SS values (32 .. 48k), check for
123562306a36Sopenharmony_ci * user-provided DS/QS bits in the control register
123662306a36Sopenharmony_ci * and multiply the base frequency accordingly.
123762306a36Sopenharmony_ci */
123862306a36Sopenharmony_cistatic int hdspm_rate_multiplier(struct hdspm *hdspm, int rate)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	if (rate <= 48000) {
124162306a36Sopenharmony_ci		if (hdspm->control_register & HDSPM_QuadSpeed)
124262306a36Sopenharmony_ci			return rate * 4;
124362306a36Sopenharmony_ci		else if (hdspm->control_register &
124462306a36Sopenharmony_ci				HDSPM_DoubleSpeed)
124562306a36Sopenharmony_ci			return rate * 2;
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci	return rate;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/* check for external sample rate, returns the sample rate in Hz*/
125162306a36Sopenharmony_cistatic int hdspm_external_sample_rate(struct hdspm *hdspm)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	unsigned int status, status2;
125462306a36Sopenharmony_ci	int syncref, rate = 0, rate_bits;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	switch (hdspm->io_type) {
125762306a36Sopenharmony_ci	case AES32:
125862306a36Sopenharmony_ci		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
125962306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci		syncref = hdspm_autosync_ref(hdspm);
126262306a36Sopenharmony_ci		switch (syncref) {
126362306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_WORD:
126462306a36Sopenharmony_ci		/* Check WC sync and get sample rate */
126562306a36Sopenharmony_ci			if (hdspm_wc_sync_check(hdspm))
126662306a36Sopenharmony_ci				return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm));
126762306a36Sopenharmony_ci			break;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES1:
127062306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES2:
127162306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES3:
127262306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES4:
127362306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES5:
127462306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES6:
127562306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES7:
127662306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_AES8:
127762306a36Sopenharmony_ci		/* Check AES sync and get sample rate */
127862306a36Sopenharmony_ci			if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))
127962306a36Sopenharmony_ci				return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm,
128062306a36Sopenharmony_ci							syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1));
128162306a36Sopenharmony_ci			break;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci		case HDSPM_AES32_AUTOSYNC_FROM_TCO:
128562306a36Sopenharmony_ci		/* Check TCO sync and get sample rate */
128662306a36Sopenharmony_ci			if (hdspm_tco_sync_check(hdspm))
128762306a36Sopenharmony_ci				return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm));
128862306a36Sopenharmony_ci			break;
128962306a36Sopenharmony_ci		default:
129062306a36Sopenharmony_ci			return 0;
129162306a36Sopenharmony_ci		} /* end switch(syncref) */
129262306a36Sopenharmony_ci		break;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	case MADIface:
129562306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci		if (!(status & HDSPM_madiLock)) {
129862306a36Sopenharmony_ci			rate = 0;  /* no lock */
129962306a36Sopenharmony_ci		} else {
130062306a36Sopenharmony_ci			switch (status & (HDSPM_status1_freqMask)) {
130162306a36Sopenharmony_ci			case HDSPM_status1_F_0*1:
130262306a36Sopenharmony_ci				rate = 32000; break;
130362306a36Sopenharmony_ci			case HDSPM_status1_F_0*2:
130462306a36Sopenharmony_ci				rate = 44100; break;
130562306a36Sopenharmony_ci			case HDSPM_status1_F_0*3:
130662306a36Sopenharmony_ci				rate = 48000; break;
130762306a36Sopenharmony_ci			case HDSPM_status1_F_0*4:
130862306a36Sopenharmony_ci				rate = 64000; break;
130962306a36Sopenharmony_ci			case HDSPM_status1_F_0*5:
131062306a36Sopenharmony_ci				rate = 88200; break;
131162306a36Sopenharmony_ci			case HDSPM_status1_F_0*6:
131262306a36Sopenharmony_ci				rate = 96000; break;
131362306a36Sopenharmony_ci			case HDSPM_status1_F_0*7:
131462306a36Sopenharmony_ci				rate = 128000; break;
131562306a36Sopenharmony_ci			case HDSPM_status1_F_0*8:
131662306a36Sopenharmony_ci				rate = 176400; break;
131762306a36Sopenharmony_ci			case HDSPM_status1_F_0*9:
131862306a36Sopenharmony_ci				rate = 192000; break;
131962306a36Sopenharmony_ci			default:
132062306a36Sopenharmony_ci				rate = 0; break;
132162306a36Sopenharmony_ci			}
132262306a36Sopenharmony_ci		}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		break;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	case MADI:
132762306a36Sopenharmony_ci	case AIO:
132862306a36Sopenharmony_ci	case RayDAT:
132962306a36Sopenharmony_ci		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
133062306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister);
133162306a36Sopenharmony_ci		rate = 0;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		/* if wordclock has synced freq and wordclock is valid */
133462306a36Sopenharmony_ci		if ((status2 & HDSPM_wcLock) != 0 &&
133562306a36Sopenharmony_ci				(status2 & HDSPM_SelSyncRef0) == 0) {
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci			rate_bits = status2 & HDSPM_wcFreqMask;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci			switch (rate_bits) {
134162306a36Sopenharmony_ci			case HDSPM_wcFreq32:
134262306a36Sopenharmony_ci				rate = 32000;
134362306a36Sopenharmony_ci				break;
134462306a36Sopenharmony_ci			case HDSPM_wcFreq44_1:
134562306a36Sopenharmony_ci				rate = 44100;
134662306a36Sopenharmony_ci				break;
134762306a36Sopenharmony_ci			case HDSPM_wcFreq48:
134862306a36Sopenharmony_ci				rate = 48000;
134962306a36Sopenharmony_ci				break;
135062306a36Sopenharmony_ci			case HDSPM_wcFreq64:
135162306a36Sopenharmony_ci				rate = 64000;
135262306a36Sopenharmony_ci				break;
135362306a36Sopenharmony_ci			case HDSPM_wcFreq88_2:
135462306a36Sopenharmony_ci				rate = 88200;
135562306a36Sopenharmony_ci				break;
135662306a36Sopenharmony_ci			case HDSPM_wcFreq96:
135762306a36Sopenharmony_ci				rate = 96000;
135862306a36Sopenharmony_ci				break;
135962306a36Sopenharmony_ci			case HDSPM_wcFreq128:
136062306a36Sopenharmony_ci				rate = 128000;
136162306a36Sopenharmony_ci				break;
136262306a36Sopenharmony_ci			case HDSPM_wcFreq176_4:
136362306a36Sopenharmony_ci				rate = 176400;
136462306a36Sopenharmony_ci				break;
136562306a36Sopenharmony_ci			case HDSPM_wcFreq192:
136662306a36Sopenharmony_ci				rate = 192000;
136762306a36Sopenharmony_ci				break;
136862306a36Sopenharmony_ci			default:
136962306a36Sopenharmony_ci				rate = 0;
137062306a36Sopenharmony_ci				break;
137162306a36Sopenharmony_ci			}
137262306a36Sopenharmony_ci		}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci		/* if rate detected and Syncref is Word than have it,
137562306a36Sopenharmony_ci		 * word has priority to MADI
137662306a36Sopenharmony_ci		 */
137762306a36Sopenharmony_ci		if (rate != 0 &&
137862306a36Sopenharmony_ci		(status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
137962306a36Sopenharmony_ci			return hdspm_rate_multiplier(hdspm, rate);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		/* maybe a madi input (which is taken if sel sync is madi) */
138262306a36Sopenharmony_ci		if (status & HDSPM_madiLock) {
138362306a36Sopenharmony_ci			rate_bits = status & HDSPM_madiFreqMask;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci			switch (rate_bits) {
138662306a36Sopenharmony_ci			case HDSPM_madiFreq32:
138762306a36Sopenharmony_ci				rate = 32000;
138862306a36Sopenharmony_ci				break;
138962306a36Sopenharmony_ci			case HDSPM_madiFreq44_1:
139062306a36Sopenharmony_ci				rate = 44100;
139162306a36Sopenharmony_ci				break;
139262306a36Sopenharmony_ci			case HDSPM_madiFreq48:
139362306a36Sopenharmony_ci				rate = 48000;
139462306a36Sopenharmony_ci				break;
139562306a36Sopenharmony_ci			case HDSPM_madiFreq64:
139662306a36Sopenharmony_ci				rate = 64000;
139762306a36Sopenharmony_ci				break;
139862306a36Sopenharmony_ci			case HDSPM_madiFreq88_2:
139962306a36Sopenharmony_ci				rate = 88200;
140062306a36Sopenharmony_ci				break;
140162306a36Sopenharmony_ci			case HDSPM_madiFreq96:
140262306a36Sopenharmony_ci				rate = 96000;
140362306a36Sopenharmony_ci				break;
140462306a36Sopenharmony_ci			case HDSPM_madiFreq128:
140562306a36Sopenharmony_ci				rate = 128000;
140662306a36Sopenharmony_ci				break;
140762306a36Sopenharmony_ci			case HDSPM_madiFreq176_4:
140862306a36Sopenharmony_ci				rate = 176400;
140962306a36Sopenharmony_ci				break;
141062306a36Sopenharmony_ci			case HDSPM_madiFreq192:
141162306a36Sopenharmony_ci				rate = 192000;
141262306a36Sopenharmony_ci				break;
141362306a36Sopenharmony_ci			default:
141462306a36Sopenharmony_ci				rate = 0;
141562306a36Sopenharmony_ci				break;
141662306a36Sopenharmony_ci			}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci		} /* endif HDSPM_madiLock */
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		/* check sample rate from TCO or SYNC_IN */
142162306a36Sopenharmony_ci		{
142262306a36Sopenharmony_ci			bool is_valid_input = 0;
142362306a36Sopenharmony_ci			bool has_sync = 0;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci			syncref = hdspm_autosync_ref(hdspm);
142662306a36Sopenharmony_ci			if (HDSPM_AUTOSYNC_FROM_TCO == syncref) {
142762306a36Sopenharmony_ci				is_valid_input = 1;
142862306a36Sopenharmony_ci				has_sync = (HDSPM_SYNC_CHECK_SYNC ==
142962306a36Sopenharmony_ci					hdspm_tco_sync_check(hdspm));
143062306a36Sopenharmony_ci			} else if (HDSPM_AUTOSYNC_FROM_SYNC_IN == syncref) {
143162306a36Sopenharmony_ci				is_valid_input = 1;
143262306a36Sopenharmony_ci				has_sync = (HDSPM_SYNC_CHECK_SYNC ==
143362306a36Sopenharmony_ci					hdspm_sync_in_sync_check(hdspm));
143462306a36Sopenharmony_ci			}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci			if (is_valid_input && has_sync) {
143762306a36Sopenharmony_ci				rate = hdspm_round_frequency(
143862306a36Sopenharmony_ci					hdspm_get_pll_freq(hdspm));
143962306a36Sopenharmony_ci			}
144062306a36Sopenharmony_ci		}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci		rate = hdspm_rate_multiplier(hdspm, rate);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci		break;
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	return rate;
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci/* return latency in samples per period */
145162306a36Sopenharmony_cistatic int hdspm_get_latency(struct hdspm *hdspm)
145262306a36Sopenharmony_ci{
145362306a36Sopenharmony_ci	int n;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	n = hdspm_decode_latency(hdspm->control_register);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	/* Special case for new RME cards with 32 samples period size.
145862306a36Sopenharmony_ci	 * The three latency bits in the control register
145962306a36Sopenharmony_ci	 * (HDSP_LatencyMask) encode latency values of 64 samples as
146062306a36Sopenharmony_ci	 * 0, 128 samples as 1 ... 4096 samples as 6. For old cards, 7
146162306a36Sopenharmony_ci	 * denotes 8192 samples, but on new cards like RayDAT or AIO,
146262306a36Sopenharmony_ci	 * it corresponds to 32 samples.
146362306a36Sopenharmony_ci	 */
146462306a36Sopenharmony_ci	if ((7 == n) && (RayDAT == hdspm->io_type || AIO == hdspm->io_type))
146562306a36Sopenharmony_ci		n = -1;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return 1 << (n + 6);
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci/* Latency function */
147162306a36Sopenharmony_cistatic inline void hdspm_compute_period_size(struct hdspm *hdspm)
147262306a36Sopenharmony_ci{
147362306a36Sopenharmony_ci	hdspm->period_bytes = 4 * hdspm_get_latency(hdspm);
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	int position;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	position = hdspm_read(hdspm, HDSPM_statusRegister);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	switch (hdspm->io_type) {
148462306a36Sopenharmony_ci	case RayDAT:
148562306a36Sopenharmony_ci	case AIO:
148662306a36Sopenharmony_ci		position &= HDSPM_BufferPositionMask;
148762306a36Sopenharmony_ci		position /= 4; /* Bytes per sample */
148862306a36Sopenharmony_ci		break;
148962306a36Sopenharmony_ci	default:
149062306a36Sopenharmony_ci		position = (position & HDSPM_BufferID) ?
149162306a36Sopenharmony_ci			(hdspm->period_bytes / 4) : 0;
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	return position;
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic inline void hdspm_start_audio(struct hdspm * s)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	s->control_register |= (HDSPM_AudioInterruptEnable | HDSPM_Start);
150162306a36Sopenharmony_ci	hdspm_write(s, HDSPM_controlRegister, s->control_register);
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic inline void hdspm_stop_audio(struct hdspm * s)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	s->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable);
150762306a36Sopenharmony_ci	hdspm_write(s, HDSPM_controlRegister, s->control_register);
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci/* should I silence all or only opened ones ? doit all for first even is 4MB*/
151162306a36Sopenharmony_cistatic void hdspm_silence_playback(struct hdspm *hdspm)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	int i;
151462306a36Sopenharmony_ci	int n = hdspm->period_bytes;
151562306a36Sopenharmony_ci	void *buf = hdspm->playback_buffer;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	if (!buf)
151862306a36Sopenharmony_ci		return;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
152162306a36Sopenharmony_ci		memset(buf, 0, n);
152262306a36Sopenharmony_ci		buf += HDSPM_CHANNEL_BUFFER_BYTES;
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	int n;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	spin_lock_irq(&s->lock);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	if (32 == frames) {
153362306a36Sopenharmony_ci		/* Special case for new RME cards like RayDAT/AIO which
153462306a36Sopenharmony_ci		 * support period sizes of 32 samples. Since latency is
153562306a36Sopenharmony_ci		 * encoded in the three bits of HDSP_LatencyMask, we can only
153662306a36Sopenharmony_ci		 * have values from 0 .. 7. While 0 still means 64 samples and
153762306a36Sopenharmony_ci		 * 6 represents 4096 samples on all cards, 7 represents 8192
153862306a36Sopenharmony_ci		 * on older cards and 32 samples on new cards.
153962306a36Sopenharmony_ci		 *
154062306a36Sopenharmony_ci		 * In other words, period size in samples is calculated by
154162306a36Sopenharmony_ci		 * 2^(n+6) with n ranging from 0 .. 7.
154262306a36Sopenharmony_ci		 */
154362306a36Sopenharmony_ci		n = 7;
154462306a36Sopenharmony_ci	} else {
154562306a36Sopenharmony_ci		frames >>= 7;
154662306a36Sopenharmony_ci		n = 0;
154762306a36Sopenharmony_ci		while (frames) {
154862306a36Sopenharmony_ci			n++;
154962306a36Sopenharmony_ci			frames >>= 1;
155062306a36Sopenharmony_ci		}
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	s->control_register &= ~HDSPM_LatencyMask;
155462306a36Sopenharmony_ci	s->control_register |= hdspm_encode_latency(n);
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	hdspm_write(s, HDSPM_controlRegister, s->control_register);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	hdspm_compute_period_size(s);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	spin_unlock_irq(&s->lock);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return 0;
156362306a36Sopenharmony_ci}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_cistatic u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period)
156662306a36Sopenharmony_ci{
156762306a36Sopenharmony_ci	u64 freq_const;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	if (period == 0)
157062306a36Sopenharmony_ci		return 0;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	switch (hdspm->io_type) {
157362306a36Sopenharmony_ci	case MADI:
157462306a36Sopenharmony_ci	case AES32:
157562306a36Sopenharmony_ci		freq_const = 110069313433624ULL;
157662306a36Sopenharmony_ci		break;
157762306a36Sopenharmony_ci	case RayDAT:
157862306a36Sopenharmony_ci	case AIO:
157962306a36Sopenharmony_ci		freq_const = 104857600000000ULL;
158062306a36Sopenharmony_ci		break;
158162306a36Sopenharmony_ci	case MADIface:
158262306a36Sopenharmony_ci		freq_const = 131072000000000ULL;
158362306a36Sopenharmony_ci		break;
158462306a36Sopenharmony_ci	default:
158562306a36Sopenharmony_ci		snd_BUG();
158662306a36Sopenharmony_ci		return 0;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	return div_u64(freq_const, period);
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_cistatic void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
159462306a36Sopenharmony_ci{
159562306a36Sopenharmony_ci	u64 n;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	if (snd_BUG_ON(rate <= 0))
159862306a36Sopenharmony_ci		return;
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	if (rate >= 112000)
160162306a36Sopenharmony_ci		rate /= 4;
160262306a36Sopenharmony_ci	else if (rate >= 56000)
160362306a36Sopenharmony_ci		rate /= 2;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	switch (hdspm->io_type) {
160662306a36Sopenharmony_ci	case MADIface:
160762306a36Sopenharmony_ci		n = 131072000000000ULL;  /* 125 MHz */
160862306a36Sopenharmony_ci		break;
160962306a36Sopenharmony_ci	case MADI:
161062306a36Sopenharmony_ci	case AES32:
161162306a36Sopenharmony_ci		n = 110069313433624ULL;  /* 105 MHz */
161262306a36Sopenharmony_ci		break;
161362306a36Sopenharmony_ci	case RayDAT:
161462306a36Sopenharmony_ci	case AIO:
161562306a36Sopenharmony_ci		n = 104857600000000ULL;  /* 100 MHz */
161662306a36Sopenharmony_ci		break;
161762306a36Sopenharmony_ci	default:
161862306a36Sopenharmony_ci		snd_BUG();
161962306a36Sopenharmony_ci		return;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	n = div_u64(n, rate);
162362306a36Sopenharmony_ci	/* n should be less than 2^32 for being written to FREQ register */
162462306a36Sopenharmony_ci	snd_BUG_ON(n >> 32);
162562306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci/* dummy set rate lets see what happens */
162962306a36Sopenharmony_cistatic int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
163062306a36Sopenharmony_ci{
163162306a36Sopenharmony_ci	int current_rate;
163262306a36Sopenharmony_ci	int rate_bits;
163362306a36Sopenharmony_ci	int not_set = 0;
163462306a36Sopenharmony_ci	int current_speed, target_speed;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	/* ASSUMPTION: hdspm->lock is either set, or there is no need for
163762306a36Sopenharmony_ci	   it (e.g. during module initialization).
163862306a36Sopenharmony_ci	 */
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci		/* SLAVE --- */
164362306a36Sopenharmony_ci		if (called_internally) {
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci			/* request from ctl or card initialization
164662306a36Sopenharmony_ci			   just make a warning an remember setting
164762306a36Sopenharmony_ci			   for future master mode switching */
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci			dev_warn(hdspm->card->dev,
165062306a36Sopenharmony_ci				 "Warning: device is not running as a clock master.\n");
165162306a36Sopenharmony_ci			not_set = 1;
165262306a36Sopenharmony_ci		} else {
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci			/* hw_param request while in AutoSync mode */
165562306a36Sopenharmony_ci			int external_freq =
165662306a36Sopenharmony_ci			    hdspm_external_sample_rate(hdspm);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci			if (hdspm_autosync_ref(hdspm) ==
165962306a36Sopenharmony_ci			    HDSPM_AUTOSYNC_FROM_NONE) {
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci				dev_warn(hdspm->card->dev,
166262306a36Sopenharmony_ci					 "Detected no External Sync\n");
166362306a36Sopenharmony_ci				not_set = 1;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci			} else if (rate != external_freq) {
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci				dev_warn(hdspm->card->dev,
166862306a36Sopenharmony_ci					 "Warning: No AutoSync source for requested rate\n");
166962306a36Sopenharmony_ci				not_set = 1;
167062306a36Sopenharmony_ci			}
167162306a36Sopenharmony_ci		}
167262306a36Sopenharmony_ci	}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	current_rate = hdspm->system_sample_rate;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	/* Changing between Singe, Double and Quad speed is not
167762306a36Sopenharmony_ci	   allowed if any substreams are open. This is because such a change
167862306a36Sopenharmony_ci	   causes a shift in the location of the DMA buffers and a reduction
167962306a36Sopenharmony_ci	   in the number of available buffers.
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	   Note that a similar but essentially insoluble problem exists for
168262306a36Sopenharmony_ci	   externally-driven rate changes. All we can do is to flag rate
168362306a36Sopenharmony_ci	   changes in the read/write routines.
168462306a36Sopenharmony_ci	 */
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (current_rate <= 48000)
168762306a36Sopenharmony_ci		current_speed = HDSPM_SPEED_SINGLE;
168862306a36Sopenharmony_ci	else if (current_rate <= 96000)
168962306a36Sopenharmony_ci		current_speed = HDSPM_SPEED_DOUBLE;
169062306a36Sopenharmony_ci	else
169162306a36Sopenharmony_ci		current_speed = HDSPM_SPEED_QUAD;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (rate <= 48000)
169462306a36Sopenharmony_ci		target_speed = HDSPM_SPEED_SINGLE;
169562306a36Sopenharmony_ci	else if (rate <= 96000)
169662306a36Sopenharmony_ci		target_speed = HDSPM_SPEED_DOUBLE;
169762306a36Sopenharmony_ci	else
169862306a36Sopenharmony_ci		target_speed = HDSPM_SPEED_QUAD;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	switch (rate) {
170162306a36Sopenharmony_ci	case 32000:
170262306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency32KHz;
170362306a36Sopenharmony_ci		break;
170462306a36Sopenharmony_ci	case 44100:
170562306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency44_1KHz;
170662306a36Sopenharmony_ci		break;
170762306a36Sopenharmony_ci	case 48000:
170862306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency48KHz;
170962306a36Sopenharmony_ci		break;
171062306a36Sopenharmony_ci	case 64000:
171162306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency64KHz;
171262306a36Sopenharmony_ci		break;
171362306a36Sopenharmony_ci	case 88200:
171462306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency88_2KHz;
171562306a36Sopenharmony_ci		break;
171662306a36Sopenharmony_ci	case 96000:
171762306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency96KHz;
171862306a36Sopenharmony_ci		break;
171962306a36Sopenharmony_ci	case 128000:
172062306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency128KHz;
172162306a36Sopenharmony_ci		break;
172262306a36Sopenharmony_ci	case 176400:
172362306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency176_4KHz;
172462306a36Sopenharmony_ci		break;
172562306a36Sopenharmony_ci	case 192000:
172662306a36Sopenharmony_ci		rate_bits = HDSPM_Frequency192KHz;
172762306a36Sopenharmony_ci		break;
172862306a36Sopenharmony_ci	default:
172962306a36Sopenharmony_ci		return -EINVAL;
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	if (current_speed != target_speed
173362306a36Sopenharmony_ci	    && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
173462306a36Sopenharmony_ci		dev_err(hdspm->card->dev,
173562306a36Sopenharmony_ci			"cannot change from %s speed to %s speed mode (capture PID = %d, playback PID = %d)\n",
173662306a36Sopenharmony_ci			hdspm_speed_names[current_speed],
173762306a36Sopenharmony_ci			hdspm_speed_names[target_speed],
173862306a36Sopenharmony_ci			hdspm->capture_pid, hdspm->playback_pid);
173962306a36Sopenharmony_ci		return -EBUSY;
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	hdspm->control_register &= ~HDSPM_FrequencyMask;
174362306a36Sopenharmony_ci	hdspm->control_register |= rate_bits;
174462306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	/* For AES32, need to set DDS value in FREQ register
174762306a36Sopenharmony_ci	   For MADI, also apparently */
174862306a36Sopenharmony_ci	hdspm_set_dds_value(hdspm, rate);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	if (AES32 == hdspm->io_type && rate != current_rate)
175162306a36Sopenharmony_ci		hdspm_write(hdspm, HDSPM_eeprom_wr, 0);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	hdspm->system_sample_rate = rate;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (rate <= 48000) {
175662306a36Sopenharmony_ci		hdspm->channel_map_in = hdspm->channel_map_in_ss;
175762306a36Sopenharmony_ci		hdspm->channel_map_out = hdspm->channel_map_out_ss;
175862306a36Sopenharmony_ci		hdspm->max_channels_in = hdspm->ss_in_channels;
175962306a36Sopenharmony_ci		hdspm->max_channels_out = hdspm->ss_out_channels;
176062306a36Sopenharmony_ci		hdspm->port_names_in = hdspm->port_names_in_ss;
176162306a36Sopenharmony_ci		hdspm->port_names_out = hdspm->port_names_out_ss;
176262306a36Sopenharmony_ci	} else if (rate <= 96000) {
176362306a36Sopenharmony_ci		hdspm->channel_map_in = hdspm->channel_map_in_ds;
176462306a36Sopenharmony_ci		hdspm->channel_map_out = hdspm->channel_map_out_ds;
176562306a36Sopenharmony_ci		hdspm->max_channels_in = hdspm->ds_in_channels;
176662306a36Sopenharmony_ci		hdspm->max_channels_out = hdspm->ds_out_channels;
176762306a36Sopenharmony_ci		hdspm->port_names_in = hdspm->port_names_in_ds;
176862306a36Sopenharmony_ci		hdspm->port_names_out = hdspm->port_names_out_ds;
176962306a36Sopenharmony_ci	} else {
177062306a36Sopenharmony_ci		hdspm->channel_map_in = hdspm->channel_map_in_qs;
177162306a36Sopenharmony_ci		hdspm->channel_map_out = hdspm->channel_map_out_qs;
177262306a36Sopenharmony_ci		hdspm->max_channels_in = hdspm->qs_in_channels;
177362306a36Sopenharmony_ci		hdspm->max_channels_out = hdspm->qs_out_channels;
177462306a36Sopenharmony_ci		hdspm->port_names_in = hdspm->port_names_in_qs;
177562306a36Sopenharmony_ci		hdspm->port_names_out = hdspm->port_names_out_qs;
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	if (not_set != 0)
177962306a36Sopenharmony_ci		return -1;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	return 0;
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci/* mainly for init to 0 on load */
178562306a36Sopenharmony_cistatic void all_in_all_mixer(struct hdspm * hdspm, int sgain)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	int i, j;
178862306a36Sopenharmony_ci	unsigned int gain;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	if (sgain > UNITY_GAIN)
179162306a36Sopenharmony_ci		gain = UNITY_GAIN;
179262306a36Sopenharmony_ci	else if (sgain < 0)
179362306a36Sopenharmony_ci		gain = 0;
179462306a36Sopenharmony_ci	else
179562306a36Sopenharmony_ci		gain = sgain;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	for (i = 0; i < HDSPM_MIXER_CHANNELS; i++)
179862306a36Sopenharmony_ci		for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) {
179962306a36Sopenharmony_ci			hdspm_write_in_gain(hdspm, i, j, gain);
180062306a36Sopenharmony_ci			hdspm_write_pb_gain(hdspm, i, j, gain);
180162306a36Sopenharmony_ci		}
180262306a36Sopenharmony_ci}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci/*----------------------------------------------------------------------------
180562306a36Sopenharmony_ci   MIDI
180662306a36Sopenharmony_ci  ----------------------------------------------------------------------------*/
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_cistatic inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm,
180962306a36Sopenharmony_ci						      int id)
181062306a36Sopenharmony_ci{
181162306a36Sopenharmony_ci	/* the hardware already does the relevant bit-mask with 0xff */
181262306a36Sopenharmony_ci	return hdspm_read(hdspm, hdspm->midi[id].dataIn);
181362306a36Sopenharmony_ci}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_cistatic inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id,
181662306a36Sopenharmony_ci					      int val)
181762306a36Sopenharmony_ci{
181862306a36Sopenharmony_ci	/* the hardware already does the relevant bit-mask with 0xff */
181962306a36Sopenharmony_ci	return hdspm_write(hdspm, hdspm->midi[id].dataOut, val);
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_cistatic inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id)
182362306a36Sopenharmony_ci{
182462306a36Sopenharmony_ci	return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF;
182562306a36Sopenharmony_ci}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_cistatic inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id)
182862306a36Sopenharmony_ci{
182962306a36Sopenharmony_ci	int fifo_bytes_used;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	if (fifo_bytes_used < 128)
183462306a36Sopenharmony_ci		return  128 - fifo_bytes_used;
183562306a36Sopenharmony_ci	else
183662306a36Sopenharmony_ci		return 0;
183762306a36Sopenharmony_ci}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_cistatic void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id)
184062306a36Sopenharmony_ci{
184162306a36Sopenharmony_ci	while (snd_hdspm_midi_input_available (hdspm, id))
184262306a36Sopenharmony_ci		snd_hdspm_midi_read_byte (hdspm, id);
184362306a36Sopenharmony_ci}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_cistatic int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
184662306a36Sopenharmony_ci{
184762306a36Sopenharmony_ci	unsigned long flags;
184862306a36Sopenharmony_ci	int n_pending;
184962306a36Sopenharmony_ci	int to_write;
185062306a36Sopenharmony_ci	int i;
185162306a36Sopenharmony_ci	unsigned char buf[128];
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	/* Output is not interrupt driven */
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	spin_lock_irqsave (&hmidi->lock, flags);
185662306a36Sopenharmony_ci	if (hmidi->output &&
185762306a36Sopenharmony_ci	    !snd_rawmidi_transmit_empty (hmidi->output)) {
185862306a36Sopenharmony_ci		n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm,
185962306a36Sopenharmony_ci							    hmidi->id);
186062306a36Sopenharmony_ci		if (n_pending > 0) {
186162306a36Sopenharmony_ci			if (n_pending > (int)sizeof (buf))
186262306a36Sopenharmony_ci				n_pending = sizeof (buf);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci			to_write = snd_rawmidi_transmit (hmidi->output, buf,
186562306a36Sopenharmony_ci							 n_pending);
186662306a36Sopenharmony_ci			if (to_write > 0) {
186762306a36Sopenharmony_ci				for (i = 0; i < to_write; ++i)
186862306a36Sopenharmony_ci					snd_hdspm_midi_write_byte (hmidi->hdspm,
186962306a36Sopenharmony_ci								   hmidi->id,
187062306a36Sopenharmony_ci								   buf[i]);
187162306a36Sopenharmony_ci			}
187262306a36Sopenharmony_ci		}
187362306a36Sopenharmony_ci	}
187462306a36Sopenharmony_ci	spin_unlock_irqrestore (&hmidi->lock, flags);
187562306a36Sopenharmony_ci	return 0;
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_cistatic int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	unsigned char buf[128]; /* this buffer is designed to match the MIDI
188162306a36Sopenharmony_ci				 * input FIFO size
188262306a36Sopenharmony_ci				 */
188362306a36Sopenharmony_ci	unsigned long flags;
188462306a36Sopenharmony_ci	int n_pending;
188562306a36Sopenharmony_ci	int i;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	spin_lock_irqsave (&hmidi->lock, flags);
188862306a36Sopenharmony_ci	n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id);
188962306a36Sopenharmony_ci	if (n_pending > 0) {
189062306a36Sopenharmony_ci		if (hmidi->input) {
189162306a36Sopenharmony_ci			if (n_pending > (int)sizeof (buf))
189262306a36Sopenharmony_ci				n_pending = sizeof (buf);
189362306a36Sopenharmony_ci			for (i = 0; i < n_pending; ++i)
189462306a36Sopenharmony_ci				buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm,
189562306a36Sopenharmony_ci								   hmidi->id);
189662306a36Sopenharmony_ci			if (n_pending)
189762306a36Sopenharmony_ci				snd_rawmidi_receive (hmidi->input, buf,
189862306a36Sopenharmony_ci						     n_pending);
189962306a36Sopenharmony_ci		} else {
190062306a36Sopenharmony_ci			/* flush the MIDI input FIFO */
190162306a36Sopenharmony_ci			while (n_pending--)
190262306a36Sopenharmony_ci				snd_hdspm_midi_read_byte (hmidi->hdspm,
190362306a36Sopenharmony_ci							  hmidi->id);
190462306a36Sopenharmony_ci		}
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci	hmidi->pending = 0;
190762306a36Sopenharmony_ci	spin_unlock_irqrestore(&hmidi->lock, flags);
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	spin_lock_irqsave(&hmidi->hdspm->lock, flags);
191062306a36Sopenharmony_ci	hmidi->hdspm->control_register |= hmidi->ie;
191162306a36Sopenharmony_ci	hdspm_write(hmidi->hdspm, HDSPM_controlRegister,
191262306a36Sopenharmony_ci		    hmidi->hdspm->control_register);
191362306a36Sopenharmony_ci	spin_unlock_irqrestore(&hmidi->hdspm->lock, flags);
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	return snd_hdspm_midi_output_write (hmidi);
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_cistatic void
191962306a36Sopenharmony_cisnd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
192062306a36Sopenharmony_ci{
192162306a36Sopenharmony_ci	struct hdspm *hdspm;
192262306a36Sopenharmony_ci	struct hdspm_midi *hmidi;
192362306a36Sopenharmony_ci	unsigned long flags;
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	hmidi = substream->rmidi->private_data;
192662306a36Sopenharmony_ci	hdspm = hmidi->hdspm;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	spin_lock_irqsave (&hdspm->lock, flags);
192962306a36Sopenharmony_ci	if (up) {
193062306a36Sopenharmony_ci		if (!(hdspm->control_register & hmidi->ie)) {
193162306a36Sopenharmony_ci			snd_hdspm_flush_midi_input (hdspm, hmidi->id);
193262306a36Sopenharmony_ci			hdspm->control_register |= hmidi->ie;
193362306a36Sopenharmony_ci		}
193462306a36Sopenharmony_ci	} else {
193562306a36Sopenharmony_ci		hdspm->control_register &= ~hmidi->ie;
193662306a36Sopenharmony_ci	}
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
193962306a36Sopenharmony_ci	spin_unlock_irqrestore (&hdspm->lock, flags);
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_cistatic void snd_hdspm_midi_output_timer(struct timer_list *t)
194362306a36Sopenharmony_ci{
194462306a36Sopenharmony_ci	struct hdspm_midi *hmidi = from_timer(hmidi, t, timer);
194562306a36Sopenharmony_ci	unsigned long flags;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	snd_hdspm_midi_output_write(hmidi);
194862306a36Sopenharmony_ci	spin_lock_irqsave (&hmidi->lock, flags);
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	/* this does not bump hmidi->istimer, because the
195162306a36Sopenharmony_ci	   kernel automatically removed the timer when it
195262306a36Sopenharmony_ci	   expired, and we are now adding it back, thus
195362306a36Sopenharmony_ci	   leaving istimer wherever it was set before.
195462306a36Sopenharmony_ci	*/
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	if (hmidi->istimer)
195762306a36Sopenharmony_ci		mod_timer(&hmidi->timer, 1 + jiffies);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	spin_unlock_irqrestore (&hmidi->lock, flags);
196062306a36Sopenharmony_ci}
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_cistatic void
196362306a36Sopenharmony_cisnd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
196462306a36Sopenharmony_ci{
196562306a36Sopenharmony_ci	struct hdspm_midi *hmidi;
196662306a36Sopenharmony_ci	unsigned long flags;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	hmidi = substream->rmidi->private_data;
196962306a36Sopenharmony_ci	spin_lock_irqsave (&hmidi->lock, flags);
197062306a36Sopenharmony_ci	if (up) {
197162306a36Sopenharmony_ci		if (!hmidi->istimer) {
197262306a36Sopenharmony_ci			timer_setup(&hmidi->timer,
197362306a36Sopenharmony_ci				    snd_hdspm_midi_output_timer, 0);
197462306a36Sopenharmony_ci			mod_timer(&hmidi->timer, 1 + jiffies);
197562306a36Sopenharmony_ci			hmidi->istimer++;
197662306a36Sopenharmony_ci		}
197762306a36Sopenharmony_ci	} else {
197862306a36Sopenharmony_ci		if (hmidi->istimer && --hmidi->istimer <= 0)
197962306a36Sopenharmony_ci			del_timer (&hmidi->timer);
198062306a36Sopenharmony_ci	}
198162306a36Sopenharmony_ci	spin_unlock_irqrestore (&hmidi->lock, flags);
198262306a36Sopenharmony_ci	if (up)
198362306a36Sopenharmony_ci		snd_hdspm_midi_output_write(hmidi);
198462306a36Sopenharmony_ci}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_cistatic int snd_hdspm_midi_input_open(struct snd_rawmidi_substream *substream)
198762306a36Sopenharmony_ci{
198862306a36Sopenharmony_ci	struct hdspm_midi *hmidi;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	hmidi = substream->rmidi->private_data;
199162306a36Sopenharmony_ci	spin_lock_irq (&hmidi->lock);
199262306a36Sopenharmony_ci	snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id);
199362306a36Sopenharmony_ci	hmidi->input = substream;
199462306a36Sopenharmony_ci	spin_unlock_irq (&hmidi->lock);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	return 0;
199762306a36Sopenharmony_ci}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic int snd_hdspm_midi_output_open(struct snd_rawmidi_substream *substream)
200062306a36Sopenharmony_ci{
200162306a36Sopenharmony_ci	struct hdspm_midi *hmidi;
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	hmidi = substream->rmidi->private_data;
200462306a36Sopenharmony_ci	spin_lock_irq (&hmidi->lock);
200562306a36Sopenharmony_ci	hmidi->output = substream;
200662306a36Sopenharmony_ci	spin_unlock_irq (&hmidi->lock);
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	return 0;
200962306a36Sopenharmony_ci}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_cistatic int snd_hdspm_midi_input_close(struct snd_rawmidi_substream *substream)
201262306a36Sopenharmony_ci{
201362306a36Sopenharmony_ci	struct hdspm_midi *hmidi;
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	snd_hdspm_midi_input_trigger (substream, 0);
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	hmidi = substream->rmidi->private_data;
201862306a36Sopenharmony_ci	spin_lock_irq (&hmidi->lock);
201962306a36Sopenharmony_ci	hmidi->input = NULL;
202062306a36Sopenharmony_ci	spin_unlock_irq (&hmidi->lock);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	return 0;
202362306a36Sopenharmony_ci}
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_cistatic int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream)
202662306a36Sopenharmony_ci{
202762306a36Sopenharmony_ci	struct hdspm_midi *hmidi;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	snd_hdspm_midi_output_trigger (substream, 0);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	hmidi = substream->rmidi->private_data;
203262306a36Sopenharmony_ci	spin_lock_irq (&hmidi->lock);
203362306a36Sopenharmony_ci	hmidi->output = NULL;
203462306a36Sopenharmony_ci	spin_unlock_irq (&hmidi->lock);
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	return 0;
203762306a36Sopenharmony_ci}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_hdspm_midi_output =
204062306a36Sopenharmony_ci{
204162306a36Sopenharmony_ci	.open =		snd_hdspm_midi_output_open,
204262306a36Sopenharmony_ci	.close =	snd_hdspm_midi_output_close,
204362306a36Sopenharmony_ci	.trigger =	snd_hdspm_midi_output_trigger,
204462306a36Sopenharmony_ci};
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_hdspm_midi_input =
204762306a36Sopenharmony_ci{
204862306a36Sopenharmony_ci	.open =		snd_hdspm_midi_input_open,
204962306a36Sopenharmony_ci	.close =	snd_hdspm_midi_input_close,
205062306a36Sopenharmony_ci	.trigger =	snd_hdspm_midi_input_trigger,
205162306a36Sopenharmony_ci};
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_cistatic int snd_hdspm_create_midi(struct snd_card *card,
205462306a36Sopenharmony_ci				 struct hdspm *hdspm, int id)
205562306a36Sopenharmony_ci{
205662306a36Sopenharmony_ci	int err;
205762306a36Sopenharmony_ci	char buf[64];
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	hdspm->midi[id].id = id;
206062306a36Sopenharmony_ci	hdspm->midi[id].hdspm = hdspm;
206162306a36Sopenharmony_ci	spin_lock_init (&hdspm->midi[id].lock);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	if (0 == id) {
206462306a36Sopenharmony_ci		if (MADIface == hdspm->io_type) {
206562306a36Sopenharmony_ci			/* MIDI-over-MADI on HDSPe MADIface */
206662306a36Sopenharmony_ci			hdspm->midi[0].dataIn = HDSPM_midiDataIn2;
206762306a36Sopenharmony_ci			hdspm->midi[0].statusIn = HDSPM_midiStatusIn2;
206862306a36Sopenharmony_ci			hdspm->midi[0].dataOut = HDSPM_midiDataOut2;
206962306a36Sopenharmony_ci			hdspm->midi[0].statusOut = HDSPM_midiStatusOut2;
207062306a36Sopenharmony_ci			hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable;
207162306a36Sopenharmony_ci			hdspm->midi[0].irq = HDSPM_midi2IRQPending;
207262306a36Sopenharmony_ci		} else {
207362306a36Sopenharmony_ci			hdspm->midi[0].dataIn = HDSPM_midiDataIn0;
207462306a36Sopenharmony_ci			hdspm->midi[0].statusIn = HDSPM_midiStatusIn0;
207562306a36Sopenharmony_ci			hdspm->midi[0].dataOut = HDSPM_midiDataOut0;
207662306a36Sopenharmony_ci			hdspm->midi[0].statusOut = HDSPM_midiStatusOut0;
207762306a36Sopenharmony_ci			hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable;
207862306a36Sopenharmony_ci			hdspm->midi[0].irq = HDSPM_midi0IRQPending;
207962306a36Sopenharmony_ci		}
208062306a36Sopenharmony_ci	} else if (1 == id) {
208162306a36Sopenharmony_ci		hdspm->midi[1].dataIn = HDSPM_midiDataIn1;
208262306a36Sopenharmony_ci		hdspm->midi[1].statusIn = HDSPM_midiStatusIn1;
208362306a36Sopenharmony_ci		hdspm->midi[1].dataOut = HDSPM_midiDataOut1;
208462306a36Sopenharmony_ci		hdspm->midi[1].statusOut = HDSPM_midiStatusOut1;
208562306a36Sopenharmony_ci		hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable;
208662306a36Sopenharmony_ci		hdspm->midi[1].irq = HDSPM_midi1IRQPending;
208762306a36Sopenharmony_ci	} else if ((2 == id) && (MADI == hdspm->io_type)) {
208862306a36Sopenharmony_ci		/* MIDI-over-MADI on HDSPe MADI */
208962306a36Sopenharmony_ci		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
209062306a36Sopenharmony_ci		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
209162306a36Sopenharmony_ci		hdspm->midi[2].dataOut = HDSPM_midiDataOut2;
209262306a36Sopenharmony_ci		hdspm->midi[2].statusOut = HDSPM_midiStatusOut2;
209362306a36Sopenharmony_ci		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
209462306a36Sopenharmony_ci		hdspm->midi[2].irq = HDSPM_midi2IRQPending;
209562306a36Sopenharmony_ci	} else if (2 == id) {
209662306a36Sopenharmony_ci		/* TCO MTC, read only */
209762306a36Sopenharmony_ci		hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
209862306a36Sopenharmony_ci		hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
209962306a36Sopenharmony_ci		hdspm->midi[2].dataOut = -1;
210062306a36Sopenharmony_ci		hdspm->midi[2].statusOut = -1;
210162306a36Sopenharmony_ci		hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
210262306a36Sopenharmony_ci		hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES;
210362306a36Sopenharmony_ci	} else if (3 == id) {
210462306a36Sopenharmony_ci		/* TCO MTC on HDSPe MADI */
210562306a36Sopenharmony_ci		hdspm->midi[3].dataIn = HDSPM_midiDataIn3;
210662306a36Sopenharmony_ci		hdspm->midi[3].statusIn = HDSPM_midiStatusIn3;
210762306a36Sopenharmony_ci		hdspm->midi[3].dataOut = -1;
210862306a36Sopenharmony_ci		hdspm->midi[3].statusOut = -1;
210962306a36Sopenharmony_ci		hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable;
211062306a36Sopenharmony_ci		hdspm->midi[3].irq = HDSPM_midi3IRQPending;
211162306a36Sopenharmony_ci	}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) ||
211462306a36Sopenharmony_ci					(MADIface == hdspm->io_type)))) {
211562306a36Sopenharmony_ci		if ((id == 0) && (MADIface == hdspm->io_type)) {
211662306a36Sopenharmony_ci			snprintf(buf, sizeof(buf), "%s MIDIoverMADI",
211762306a36Sopenharmony_ci				 card->shortname);
211862306a36Sopenharmony_ci		} else if ((id == 2) && (MADI == hdspm->io_type)) {
211962306a36Sopenharmony_ci			snprintf(buf, sizeof(buf), "%s MIDIoverMADI",
212062306a36Sopenharmony_ci				 card->shortname);
212162306a36Sopenharmony_ci		} else {
212262306a36Sopenharmony_ci			snprintf(buf, sizeof(buf), "%s MIDI %d",
212362306a36Sopenharmony_ci				 card->shortname, id+1);
212462306a36Sopenharmony_ci		}
212562306a36Sopenharmony_ci		err = snd_rawmidi_new(card, buf, id, 1, 1,
212662306a36Sopenharmony_ci				&hdspm->midi[id].rmidi);
212762306a36Sopenharmony_ci		if (err < 0)
212862306a36Sopenharmony_ci			return err;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci		snprintf(hdspm->midi[id].rmidi->name,
213162306a36Sopenharmony_ci			 sizeof(hdspm->midi[id].rmidi->name),
213262306a36Sopenharmony_ci			 "%s MIDI %d", card->id, id+1);
213362306a36Sopenharmony_ci		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
213662306a36Sopenharmony_ci				SNDRV_RAWMIDI_STREAM_OUTPUT,
213762306a36Sopenharmony_ci				&snd_hdspm_midi_output);
213862306a36Sopenharmony_ci		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
213962306a36Sopenharmony_ci				SNDRV_RAWMIDI_STREAM_INPUT,
214062306a36Sopenharmony_ci				&snd_hdspm_midi_input);
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci		hdspm->midi[id].rmidi->info_flags |=
214362306a36Sopenharmony_ci			SNDRV_RAWMIDI_INFO_OUTPUT |
214462306a36Sopenharmony_ci			SNDRV_RAWMIDI_INFO_INPUT |
214562306a36Sopenharmony_ci			SNDRV_RAWMIDI_INFO_DUPLEX;
214662306a36Sopenharmony_ci	} else {
214762306a36Sopenharmony_ci		/* TCO MTC, read only */
214862306a36Sopenharmony_ci		snprintf(buf, sizeof(buf), "%s MTC %d",
214962306a36Sopenharmony_ci			 card->shortname, id+1);
215062306a36Sopenharmony_ci		err = snd_rawmidi_new(card, buf, id, 1, 1,
215162306a36Sopenharmony_ci				&hdspm->midi[id].rmidi);
215262306a36Sopenharmony_ci		if (err < 0)
215362306a36Sopenharmony_ci			return err;
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci		snprintf(hdspm->midi[id].rmidi->name,
215662306a36Sopenharmony_ci			 sizeof(hdspm->midi[id].rmidi->name),
215762306a36Sopenharmony_ci			 "%s MTC %d", card->id, id+1);
215862306a36Sopenharmony_ci		hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci		snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
216162306a36Sopenharmony_ci				SNDRV_RAWMIDI_STREAM_INPUT,
216262306a36Sopenharmony_ci				&snd_hdspm_midi_input);
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci		hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	return 0;
216862306a36Sopenharmony_ci}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_cistatic void hdspm_midi_work(struct work_struct *work)
217262306a36Sopenharmony_ci{
217362306a36Sopenharmony_ci	struct hdspm *hdspm = container_of(work, struct hdspm, midi_work);
217462306a36Sopenharmony_ci	int i = 0;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	while (i < hdspm->midiPorts) {
217762306a36Sopenharmony_ci		if (hdspm->midi[i].pending)
217862306a36Sopenharmony_ci			snd_hdspm_midi_input_read(&hdspm->midi[i]);
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci		i++;
218162306a36Sopenharmony_ci	}
218262306a36Sopenharmony_ci}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci/*-----------------------------------------------------------------------------
218662306a36Sopenharmony_ci  Status Interface
218762306a36Sopenharmony_ci  ----------------------------------------------------------------------------*/
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci/* get the system sample rate which is set */
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_cistatic inline int hdspm_get_pll_freq(struct hdspm *hdspm)
219362306a36Sopenharmony_ci{
219462306a36Sopenharmony_ci	unsigned int period, rate;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
219762306a36Sopenharmony_ci	rate = hdspm_calc_dds_value(hdspm, period);
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	return rate;
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci/*
220362306a36Sopenharmony_ci * Calculate the real sample rate from the
220462306a36Sopenharmony_ci * current DDS value.
220562306a36Sopenharmony_ci */
220662306a36Sopenharmony_cistatic int hdspm_get_system_sample_rate(struct hdspm *hdspm)
220762306a36Sopenharmony_ci{
220862306a36Sopenharmony_ci	unsigned int rate;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	rate = hdspm_get_pll_freq(hdspm);
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	if (rate > 207000) {
221362306a36Sopenharmony_ci		/* Unreasonable high sample rate as seen on PCI MADI cards. */
221462306a36Sopenharmony_ci		if (0 == hdspm_system_clock_mode(hdspm)) {
221562306a36Sopenharmony_ci			/* master mode, return internal sample rate */
221662306a36Sopenharmony_ci			rate = hdspm->system_sample_rate;
221762306a36Sopenharmony_ci		} else {
221862306a36Sopenharmony_ci			/* slave mode, return external sample rate */
221962306a36Sopenharmony_ci			rate = hdspm_external_sample_rate(hdspm);
222062306a36Sopenharmony_ci			if (!rate)
222162306a36Sopenharmony_ci				rate = hdspm->system_sample_rate;
222262306a36Sopenharmony_ci		}
222362306a36Sopenharmony_ci	}
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	return rate;
222662306a36Sopenharmony_ci}
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci#define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
223062306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
223162306a36Sopenharmony_ci	.name = xname, \
223262306a36Sopenharmony_ci	.index = xindex, \
223362306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
223462306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
223562306a36Sopenharmony_ci	.info = snd_hdspm_info_system_sample_rate, \
223662306a36Sopenharmony_ci	.put = snd_hdspm_put_system_sample_rate, \
223762306a36Sopenharmony_ci	.get = snd_hdspm_get_system_sample_rate \
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_cistatic int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol,
224162306a36Sopenharmony_ci					     struct snd_ctl_elem_info *uinfo)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
224462306a36Sopenharmony_ci	uinfo->count = 1;
224562306a36Sopenharmony_ci	uinfo->value.integer.min = 27000;
224662306a36Sopenharmony_ci	uinfo->value.integer.max = 207000;
224762306a36Sopenharmony_ci	uinfo->value.integer.step = 1;
224862306a36Sopenharmony_ci	return 0;
224962306a36Sopenharmony_ci}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_cistatic int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol,
225362306a36Sopenharmony_ci					    struct snd_ctl_elem_value *
225462306a36Sopenharmony_ci					    ucontrol)
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm);
225962306a36Sopenharmony_ci	return 0;
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_cistatic int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol,
226362306a36Sopenharmony_ci					    struct snd_ctl_elem_value *
226462306a36Sopenharmony_ci					    ucontrol)
226562306a36Sopenharmony_ci{
226662306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
226762306a36Sopenharmony_ci	int rate = ucontrol->value.integer.value[0];
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	if (rate < 27000 || rate > 207000)
227062306a36Sopenharmony_ci		return -EINVAL;
227162306a36Sopenharmony_ci	hdspm_set_dds_value(hdspm, ucontrol->value.integer.value[0]);
227262306a36Sopenharmony_ci	return 0;
227362306a36Sopenharmony_ci}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci/*
227762306a36Sopenharmony_ci * Returns the WordClock sample rate class for the given card.
227862306a36Sopenharmony_ci */
227962306a36Sopenharmony_cistatic int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
228062306a36Sopenharmony_ci{
228162306a36Sopenharmony_ci	int status;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	switch (hdspm->io_type) {
228462306a36Sopenharmony_ci	case RayDAT:
228562306a36Sopenharmony_ci	case AIO:
228662306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
228762306a36Sopenharmony_ci		return (status >> 16) & 0xF;
228862306a36Sopenharmony_ci	case AES32:
228962306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister);
229062306a36Sopenharmony_ci		return (status >> HDSPM_AES32_wcFreq_bit) & 0xF;
229162306a36Sopenharmony_ci	default:
229262306a36Sopenharmony_ci		break;
229362306a36Sopenharmony_ci	}
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	return 0;
229762306a36Sopenharmony_ci}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci/*
230162306a36Sopenharmony_ci * Returns the TCO sample rate class for the given card.
230262306a36Sopenharmony_ci */
230362306a36Sopenharmony_cistatic int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
230462306a36Sopenharmony_ci{
230562306a36Sopenharmony_ci	int status;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	if (hdspm->tco) {
230862306a36Sopenharmony_ci		switch (hdspm->io_type) {
230962306a36Sopenharmony_ci		case RayDAT:
231062306a36Sopenharmony_ci		case AIO:
231162306a36Sopenharmony_ci			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
231262306a36Sopenharmony_ci			return (status >> 20) & 0xF;
231362306a36Sopenharmony_ci		case AES32:
231462306a36Sopenharmony_ci			status = hdspm_read(hdspm, HDSPM_statusRegister);
231562306a36Sopenharmony_ci			return (status >> 1) & 0xF;
231662306a36Sopenharmony_ci		default:
231762306a36Sopenharmony_ci			break;
231862306a36Sopenharmony_ci		}
231962306a36Sopenharmony_ci	}
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	return 0;
232262306a36Sopenharmony_ci}
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci/*
232662306a36Sopenharmony_ci * Returns the SYNC_IN sample rate class for the given card.
232762306a36Sopenharmony_ci */
232862306a36Sopenharmony_cistatic int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
232962306a36Sopenharmony_ci{
233062306a36Sopenharmony_ci	int status;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (hdspm->tco) {
233362306a36Sopenharmony_ci		switch (hdspm->io_type) {
233462306a36Sopenharmony_ci		case RayDAT:
233562306a36Sopenharmony_ci		case AIO:
233662306a36Sopenharmony_ci			status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
233762306a36Sopenharmony_ci			return (status >> 12) & 0xF;
233862306a36Sopenharmony_ci		default:
233962306a36Sopenharmony_ci			break;
234062306a36Sopenharmony_ci		}
234162306a36Sopenharmony_ci	}
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	return 0;
234462306a36Sopenharmony_ci}
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci/*
234762306a36Sopenharmony_ci * Returns the AES sample rate class for the given card.
234862306a36Sopenharmony_ci */
234962306a36Sopenharmony_cistatic int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index)
235062306a36Sopenharmony_ci{
235162306a36Sopenharmony_ci	int timecode;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	switch (hdspm->io_type) {
235462306a36Sopenharmony_ci	case AES32:
235562306a36Sopenharmony_ci		timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
235662306a36Sopenharmony_ci		return (timecode >> (4*index)) & 0xF;
235762306a36Sopenharmony_ci	default:
235862306a36Sopenharmony_ci		break;
235962306a36Sopenharmony_ci	}
236062306a36Sopenharmony_ci	return 0;
236162306a36Sopenharmony_ci}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci/*
236462306a36Sopenharmony_ci * Returns the sample rate class for input source <idx> for
236562306a36Sopenharmony_ci * 'new style' cards like the AIO and RayDAT.
236662306a36Sopenharmony_ci */
236762306a36Sopenharmony_cistatic int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
236862306a36Sopenharmony_ci{
236962306a36Sopenharmony_ci	int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	return (status >> (idx*4)) & 0xF;
237262306a36Sopenharmony_ci}
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci#define ENUMERATED_CTL_INFO(info, texts) \
237562306a36Sopenharmony_ci	snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts)
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci/* Helper function to query the external sample rate and return the
237962306a36Sopenharmony_ci * corresponding enum to be returned to userspace.
238062306a36Sopenharmony_ci */
238162306a36Sopenharmony_cistatic int hdspm_external_rate_to_enum(struct hdspm *hdspm)
238262306a36Sopenharmony_ci{
238362306a36Sopenharmony_ci	int rate = hdspm_external_sample_rate(hdspm);
238462306a36Sopenharmony_ci	int i, selected_rate = 0;
238562306a36Sopenharmony_ci	for (i = 1; i < 10; i++)
238662306a36Sopenharmony_ci		if (HDSPM_bit2freq(i) == rate) {
238762306a36Sopenharmony_ci			selected_rate = i;
238862306a36Sopenharmony_ci			break;
238962306a36Sopenharmony_ci		}
239062306a36Sopenharmony_ci	return selected_rate;
239162306a36Sopenharmony_ci}
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
239562306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
239662306a36Sopenharmony_ci	.name = xname, \
239762306a36Sopenharmony_ci	.private_value = xindex, \
239862306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ, \
239962306a36Sopenharmony_ci	.info = snd_hdspm_info_autosync_sample_rate, \
240062306a36Sopenharmony_ci	.get = snd_hdspm_get_autosync_sample_rate \
240162306a36Sopenharmony_ci}
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_cistatic int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
240562306a36Sopenharmony_ci					       struct snd_ctl_elem_info *uinfo)
240662306a36Sopenharmony_ci{
240762306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts_freq);
240862306a36Sopenharmony_ci	return 0;
240962306a36Sopenharmony_ci}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_cistatic int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
241362306a36Sopenharmony_ci					      struct snd_ctl_elem_value *
241462306a36Sopenharmony_ci					      ucontrol)
241562306a36Sopenharmony_ci{
241662306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	switch (hdspm->io_type) {
241962306a36Sopenharmony_ci	case RayDAT:
242062306a36Sopenharmony_ci		switch (kcontrol->private_value) {
242162306a36Sopenharmony_ci		case 0:
242262306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
242362306a36Sopenharmony_ci				hdspm_get_wc_sample_rate(hdspm);
242462306a36Sopenharmony_ci			break;
242562306a36Sopenharmony_ci		case 7:
242662306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
242762306a36Sopenharmony_ci				hdspm_get_tco_sample_rate(hdspm);
242862306a36Sopenharmony_ci			break;
242962306a36Sopenharmony_ci		case 8:
243062306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
243162306a36Sopenharmony_ci				hdspm_get_sync_in_sample_rate(hdspm);
243262306a36Sopenharmony_ci			break;
243362306a36Sopenharmony_ci		default:
243462306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
243562306a36Sopenharmony_ci				hdspm_get_s1_sample_rate(hdspm,
243662306a36Sopenharmony_ci						kcontrol->private_value-1);
243762306a36Sopenharmony_ci		}
243862306a36Sopenharmony_ci		break;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	case AIO:
244162306a36Sopenharmony_ci		switch (kcontrol->private_value) {
244262306a36Sopenharmony_ci		case 0: /* WC */
244362306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
244462306a36Sopenharmony_ci				hdspm_get_wc_sample_rate(hdspm);
244562306a36Sopenharmony_ci			break;
244662306a36Sopenharmony_ci		case 4: /* TCO */
244762306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
244862306a36Sopenharmony_ci				hdspm_get_tco_sample_rate(hdspm);
244962306a36Sopenharmony_ci			break;
245062306a36Sopenharmony_ci		case 5: /* SYNC_IN */
245162306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
245262306a36Sopenharmony_ci				hdspm_get_sync_in_sample_rate(hdspm);
245362306a36Sopenharmony_ci			break;
245462306a36Sopenharmony_ci		default:
245562306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
245662306a36Sopenharmony_ci				hdspm_get_s1_sample_rate(hdspm,
245762306a36Sopenharmony_ci						kcontrol->private_value-1);
245862306a36Sopenharmony_ci		}
245962306a36Sopenharmony_ci		break;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci	case AES32:
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci		switch (kcontrol->private_value) {
246462306a36Sopenharmony_ci		case 0: /* WC */
246562306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
246662306a36Sopenharmony_ci				hdspm_get_wc_sample_rate(hdspm);
246762306a36Sopenharmony_ci			break;
246862306a36Sopenharmony_ci		case 9: /* TCO */
246962306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
247062306a36Sopenharmony_ci				hdspm_get_tco_sample_rate(hdspm);
247162306a36Sopenharmony_ci			break;
247262306a36Sopenharmony_ci		case 10: /* SYNC_IN */
247362306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
247462306a36Sopenharmony_ci				hdspm_get_sync_in_sample_rate(hdspm);
247562306a36Sopenharmony_ci			break;
247662306a36Sopenharmony_ci		case 11: /* External Rate */
247762306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
247862306a36Sopenharmony_ci				hdspm_external_rate_to_enum(hdspm);
247962306a36Sopenharmony_ci			break;
248062306a36Sopenharmony_ci		default: /* AES1 to AES8 */
248162306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] =
248262306a36Sopenharmony_ci				hdspm_get_aes_sample_rate(hdspm,
248362306a36Sopenharmony_ci						kcontrol->private_value -
248462306a36Sopenharmony_ci						HDSPM_AES32_AUTOSYNC_FROM_AES1);
248562306a36Sopenharmony_ci			break;
248662306a36Sopenharmony_ci		}
248762306a36Sopenharmony_ci		break;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	case MADI:
249062306a36Sopenharmony_ci	case MADIface:
249162306a36Sopenharmony_ci		ucontrol->value.enumerated.item[0] =
249262306a36Sopenharmony_ci			hdspm_external_rate_to_enum(hdspm);
249362306a36Sopenharmony_ci		break;
249462306a36Sopenharmony_ci	default:
249562306a36Sopenharmony_ci		break;
249662306a36Sopenharmony_ci	}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	return 0;
249962306a36Sopenharmony_ci}
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \
250362306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
250462306a36Sopenharmony_ci	.name = xname, \
250562306a36Sopenharmony_ci	.index = xindex, \
250662306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
250762306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
250862306a36Sopenharmony_ci	.info = snd_hdspm_info_system_clock_mode, \
250962306a36Sopenharmony_ci	.get = snd_hdspm_get_system_clock_mode, \
251062306a36Sopenharmony_ci	.put = snd_hdspm_put_system_clock_mode, \
251162306a36Sopenharmony_ci}
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci/*
251562306a36Sopenharmony_ci * Returns the system clock mode for the given card.
251662306a36Sopenharmony_ci * @returns 0 - master, 1 - slave
251762306a36Sopenharmony_ci */
251862306a36Sopenharmony_cistatic int hdspm_system_clock_mode(struct hdspm *hdspm)
251962306a36Sopenharmony_ci{
252062306a36Sopenharmony_ci	switch (hdspm->io_type) {
252162306a36Sopenharmony_ci	case AIO:
252262306a36Sopenharmony_ci	case RayDAT:
252362306a36Sopenharmony_ci		if (hdspm->settings_register & HDSPM_c0Master)
252462306a36Sopenharmony_ci			return 0;
252562306a36Sopenharmony_ci		break;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	default:
252862306a36Sopenharmony_ci		if (hdspm->control_register & HDSPM_ClockModeMaster)
252962306a36Sopenharmony_ci			return 0;
253062306a36Sopenharmony_ci	}
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	return 1;
253362306a36Sopenharmony_ci}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci/*
253762306a36Sopenharmony_ci * Sets the system clock mode.
253862306a36Sopenharmony_ci * @param mode 0 - master, 1 - slave
253962306a36Sopenharmony_ci */
254062306a36Sopenharmony_cistatic void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
254162306a36Sopenharmony_ci{
254262306a36Sopenharmony_ci	hdspm_set_toggle_setting(hdspm,
254362306a36Sopenharmony_ci			(hdspm_is_raydat_or_aio(hdspm)) ?
254462306a36Sopenharmony_ci			HDSPM_c0Master : HDSPM_ClockModeMaster,
254562306a36Sopenharmony_ci			(0 == mode));
254662306a36Sopenharmony_ci}
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_cistatic int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
255062306a36Sopenharmony_ci					    struct snd_ctl_elem_info *uinfo)
255162306a36Sopenharmony_ci{
255262306a36Sopenharmony_ci	static const char *const texts[] = { "Master", "AutoSync" };
255362306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
255462306a36Sopenharmony_ci	return 0;
255562306a36Sopenharmony_ci}
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_cistatic int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol,
255862306a36Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
255962306a36Sopenharmony_ci{
256062306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm);
256362306a36Sopenharmony_ci	return 0;
256462306a36Sopenharmony_ci}
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_cistatic int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol,
256762306a36Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
256862306a36Sopenharmony_ci{
256962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
257062306a36Sopenharmony_ci	int val;
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
257362306a36Sopenharmony_ci		return -EBUSY;
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	val = ucontrol->value.enumerated.item[0];
257662306a36Sopenharmony_ci	if (val < 0)
257762306a36Sopenharmony_ci		val = 0;
257862306a36Sopenharmony_ci	else if (val > 1)
257962306a36Sopenharmony_ci		val = 1;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	hdspm_set_system_clock_mode(hdspm, val);
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	return 0;
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci#define HDSPM_INTERNAL_CLOCK(xname, xindex) \
258862306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
258962306a36Sopenharmony_ci	.name = xname, \
259062306a36Sopenharmony_ci	.index = xindex, \
259162306a36Sopenharmony_ci	.info = snd_hdspm_info_clock_source, \
259262306a36Sopenharmony_ci	.get = snd_hdspm_get_clock_source, \
259362306a36Sopenharmony_ci	.put = snd_hdspm_put_clock_source \
259462306a36Sopenharmony_ci}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_cistatic int hdspm_clock_source(struct hdspm * hdspm)
259862306a36Sopenharmony_ci{
259962306a36Sopenharmony_ci	switch (hdspm->system_sample_rate) {
260062306a36Sopenharmony_ci	case 32000: return 0;
260162306a36Sopenharmony_ci	case 44100: return 1;
260262306a36Sopenharmony_ci	case 48000: return 2;
260362306a36Sopenharmony_ci	case 64000: return 3;
260462306a36Sopenharmony_ci	case 88200: return 4;
260562306a36Sopenharmony_ci	case 96000: return 5;
260662306a36Sopenharmony_ci	case 128000: return 6;
260762306a36Sopenharmony_ci	case 176400: return 7;
260862306a36Sopenharmony_ci	case 192000: return 8;
260962306a36Sopenharmony_ci	}
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	return -1;
261262306a36Sopenharmony_ci}
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_cistatic int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
261562306a36Sopenharmony_ci{
261662306a36Sopenharmony_ci	int rate;
261762306a36Sopenharmony_ci	switch (mode) {
261862306a36Sopenharmony_ci	case 0:
261962306a36Sopenharmony_ci		rate = 32000; break;
262062306a36Sopenharmony_ci	case 1:
262162306a36Sopenharmony_ci		rate = 44100; break;
262262306a36Sopenharmony_ci	case 2:
262362306a36Sopenharmony_ci		rate = 48000; break;
262462306a36Sopenharmony_ci	case 3:
262562306a36Sopenharmony_ci		rate = 64000; break;
262662306a36Sopenharmony_ci	case 4:
262762306a36Sopenharmony_ci		rate = 88200; break;
262862306a36Sopenharmony_ci	case 5:
262962306a36Sopenharmony_ci		rate = 96000; break;
263062306a36Sopenharmony_ci	case 6:
263162306a36Sopenharmony_ci		rate = 128000; break;
263262306a36Sopenharmony_ci	case 7:
263362306a36Sopenharmony_ci		rate = 176400; break;
263462306a36Sopenharmony_ci	case 8:
263562306a36Sopenharmony_ci		rate = 192000; break;
263662306a36Sopenharmony_ci	default:
263762306a36Sopenharmony_ci		rate = 48000;
263862306a36Sopenharmony_ci	}
263962306a36Sopenharmony_ci	hdspm_set_rate(hdspm, rate, 1);
264062306a36Sopenharmony_ci	return 0;
264162306a36Sopenharmony_ci}
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_cistatic int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol,
264462306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
264562306a36Sopenharmony_ci{
264662306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 9, texts_freq + 1);
264762306a36Sopenharmony_ci}
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_cistatic int snd_hdspm_get_clock_source(struct snd_kcontrol *kcontrol,
265062306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
265162306a36Sopenharmony_ci{
265262306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_clock_source(hdspm);
265562306a36Sopenharmony_ci	return 0;
265662306a36Sopenharmony_ci}
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_cistatic int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol,
265962306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
266062306a36Sopenharmony_ci{
266162306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
266262306a36Sopenharmony_ci	int change;
266362306a36Sopenharmony_ci	int val;
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
266662306a36Sopenharmony_ci		return -EBUSY;
266762306a36Sopenharmony_ci	val = ucontrol->value.enumerated.item[0];
266862306a36Sopenharmony_ci	if (val < 0)
266962306a36Sopenharmony_ci		val = 0;
267062306a36Sopenharmony_ci	if (val > 9)
267162306a36Sopenharmony_ci		val = 9;
267262306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
267362306a36Sopenharmony_ci	if (val != hdspm_clock_source(hdspm))
267462306a36Sopenharmony_ci		change = (hdspm_set_clock_source(hdspm, val) == 0) ? 1 : 0;
267562306a36Sopenharmony_ci	else
267662306a36Sopenharmony_ci		change = 0;
267762306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
267862306a36Sopenharmony_ci	return change;
267962306a36Sopenharmony_ci}
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci#define HDSPM_PREF_SYNC_REF(xname, xindex) \
268362306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
268462306a36Sopenharmony_ci	.name = xname, \
268562306a36Sopenharmony_ci	.index = xindex, \
268662306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
268762306a36Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
268862306a36Sopenharmony_ci	.info = snd_hdspm_info_pref_sync_ref, \
268962306a36Sopenharmony_ci	.get = snd_hdspm_get_pref_sync_ref, \
269062306a36Sopenharmony_ci	.put = snd_hdspm_put_pref_sync_ref \
269162306a36Sopenharmony_ci}
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci/*
269562306a36Sopenharmony_ci * Returns the current preferred sync reference setting.
269662306a36Sopenharmony_ci * The semantics of the return value are depending on the
269762306a36Sopenharmony_ci * card, please see the comments for clarification.
269862306a36Sopenharmony_ci */
269962306a36Sopenharmony_cistatic int hdspm_pref_sync_ref(struct hdspm * hdspm)
270062306a36Sopenharmony_ci{
270162306a36Sopenharmony_ci	switch (hdspm->io_type) {
270262306a36Sopenharmony_ci	case AES32:
270362306a36Sopenharmony_ci		switch (hdspm->control_register & HDSPM_SyncRefMask) {
270462306a36Sopenharmony_ci		case 0: return 0;  /* WC */
270562306a36Sopenharmony_ci		case HDSPM_SyncRef0: return 1; /* AES 1 */
270662306a36Sopenharmony_ci		case HDSPM_SyncRef1: return 2; /* AES 2 */
270762306a36Sopenharmony_ci		case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */
270862306a36Sopenharmony_ci		case HDSPM_SyncRef2: return 4; /* AES 4 */
270962306a36Sopenharmony_ci		case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */
271062306a36Sopenharmony_ci		case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */
271162306a36Sopenharmony_ci		case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0:
271262306a36Sopenharmony_ci						    return 7; /* AES 7 */
271362306a36Sopenharmony_ci		case HDSPM_SyncRef3: return 8; /* AES 8 */
271462306a36Sopenharmony_ci		case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */
271562306a36Sopenharmony_ci		}
271662306a36Sopenharmony_ci		break;
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	case MADI:
271962306a36Sopenharmony_ci	case MADIface:
272062306a36Sopenharmony_ci		if (hdspm->tco) {
272162306a36Sopenharmony_ci			switch (hdspm->control_register & HDSPM_SyncRefMask) {
272262306a36Sopenharmony_ci			case 0: return 0;  /* WC */
272362306a36Sopenharmony_ci			case HDSPM_SyncRef0: return 1;  /* MADI */
272462306a36Sopenharmony_ci			case HDSPM_SyncRef1: return 2;  /* TCO */
272562306a36Sopenharmony_ci			case HDSPM_SyncRef1+HDSPM_SyncRef0:
272662306a36Sopenharmony_ci					     return 3;  /* SYNC_IN */
272762306a36Sopenharmony_ci			}
272862306a36Sopenharmony_ci		} else {
272962306a36Sopenharmony_ci			switch (hdspm->control_register & HDSPM_SyncRefMask) {
273062306a36Sopenharmony_ci			case 0: return 0;  /* WC */
273162306a36Sopenharmony_ci			case HDSPM_SyncRef0: return 1;  /* MADI */
273262306a36Sopenharmony_ci			case HDSPM_SyncRef1+HDSPM_SyncRef0:
273362306a36Sopenharmony_ci					     return 2;  /* SYNC_IN */
273462306a36Sopenharmony_ci			}
273562306a36Sopenharmony_ci		}
273662306a36Sopenharmony_ci		break;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	case RayDAT:
273962306a36Sopenharmony_ci		if (hdspm->tco) {
274062306a36Sopenharmony_ci			switch ((hdspm->settings_register &
274162306a36Sopenharmony_ci				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
274262306a36Sopenharmony_ci			case 0: return 0;  /* WC */
274362306a36Sopenharmony_ci			case 3: return 1;  /* ADAT 1 */
274462306a36Sopenharmony_ci			case 4: return 2;  /* ADAT 2 */
274562306a36Sopenharmony_ci			case 5: return 3;  /* ADAT 3 */
274662306a36Sopenharmony_ci			case 6: return 4;  /* ADAT 4 */
274762306a36Sopenharmony_ci			case 1: return 5;  /* AES */
274862306a36Sopenharmony_ci			case 2: return 6;  /* SPDIF */
274962306a36Sopenharmony_ci			case 9: return 7;  /* TCO */
275062306a36Sopenharmony_ci			case 10: return 8; /* SYNC_IN */
275162306a36Sopenharmony_ci			}
275262306a36Sopenharmony_ci		} else {
275362306a36Sopenharmony_ci			switch ((hdspm->settings_register &
275462306a36Sopenharmony_ci				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
275562306a36Sopenharmony_ci			case 0: return 0;  /* WC */
275662306a36Sopenharmony_ci			case 3: return 1;  /* ADAT 1 */
275762306a36Sopenharmony_ci			case 4: return 2;  /* ADAT 2 */
275862306a36Sopenharmony_ci			case 5: return 3;  /* ADAT 3 */
275962306a36Sopenharmony_ci			case 6: return 4;  /* ADAT 4 */
276062306a36Sopenharmony_ci			case 1: return 5;  /* AES */
276162306a36Sopenharmony_ci			case 2: return 6;  /* SPDIF */
276262306a36Sopenharmony_ci			case 10: return 7; /* SYNC_IN */
276362306a36Sopenharmony_ci			}
276462306a36Sopenharmony_ci		}
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci		break;
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	case AIO:
276962306a36Sopenharmony_ci		if (hdspm->tco) {
277062306a36Sopenharmony_ci			switch ((hdspm->settings_register &
277162306a36Sopenharmony_ci				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
277262306a36Sopenharmony_ci			case 0: return 0;  /* WC */
277362306a36Sopenharmony_ci			case 3: return 1;  /* ADAT */
277462306a36Sopenharmony_ci			case 1: return 2;  /* AES */
277562306a36Sopenharmony_ci			case 2: return 3;  /* SPDIF */
277662306a36Sopenharmony_ci			case 9: return 4;  /* TCO */
277762306a36Sopenharmony_ci			case 10: return 5; /* SYNC_IN */
277862306a36Sopenharmony_ci			}
277962306a36Sopenharmony_ci		} else {
278062306a36Sopenharmony_ci			switch ((hdspm->settings_register &
278162306a36Sopenharmony_ci				HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
278262306a36Sopenharmony_ci			case 0: return 0;  /* WC */
278362306a36Sopenharmony_ci			case 3: return 1;  /* ADAT */
278462306a36Sopenharmony_ci			case 1: return 2;  /* AES */
278562306a36Sopenharmony_ci			case 2: return 3;  /* SPDIF */
278662306a36Sopenharmony_ci			case 10: return 4; /* SYNC_IN */
278762306a36Sopenharmony_ci			}
278862306a36Sopenharmony_ci		}
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci		break;
279162306a36Sopenharmony_ci	}
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci	return -1;
279462306a36Sopenharmony_ci}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci/*
279862306a36Sopenharmony_ci * Set the preferred sync reference to <pref>. The semantics
279962306a36Sopenharmony_ci * of <pref> are depending on the card type, see the comments
280062306a36Sopenharmony_ci * for clarification.
280162306a36Sopenharmony_ci */
280262306a36Sopenharmony_cistatic int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
280362306a36Sopenharmony_ci{
280462306a36Sopenharmony_ci	int p = 0;
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	switch (hdspm->io_type) {
280762306a36Sopenharmony_ci	case AES32:
280862306a36Sopenharmony_ci		hdspm->control_register &= ~HDSPM_SyncRefMask;
280962306a36Sopenharmony_ci		switch (pref) {
281062306a36Sopenharmony_ci		case 0: /* WC  */
281162306a36Sopenharmony_ci			break;
281262306a36Sopenharmony_ci		case 1: /* AES 1 */
281362306a36Sopenharmony_ci			hdspm->control_register |= HDSPM_SyncRef0;
281462306a36Sopenharmony_ci			break;
281562306a36Sopenharmony_ci		case 2: /* AES 2 */
281662306a36Sopenharmony_ci			hdspm->control_register |= HDSPM_SyncRef1;
281762306a36Sopenharmony_ci			break;
281862306a36Sopenharmony_ci		case 3: /* AES 3 */
281962306a36Sopenharmony_ci			hdspm->control_register |=
282062306a36Sopenharmony_ci				HDSPM_SyncRef1+HDSPM_SyncRef0;
282162306a36Sopenharmony_ci			break;
282262306a36Sopenharmony_ci		case 4: /* AES 4 */
282362306a36Sopenharmony_ci			hdspm->control_register |= HDSPM_SyncRef2;
282462306a36Sopenharmony_ci			break;
282562306a36Sopenharmony_ci		case 5: /* AES 5 */
282662306a36Sopenharmony_ci			hdspm->control_register |=
282762306a36Sopenharmony_ci				HDSPM_SyncRef2+HDSPM_SyncRef0;
282862306a36Sopenharmony_ci			break;
282962306a36Sopenharmony_ci		case 6: /* AES 6 */
283062306a36Sopenharmony_ci			hdspm->control_register |=
283162306a36Sopenharmony_ci				HDSPM_SyncRef2+HDSPM_SyncRef1;
283262306a36Sopenharmony_ci			break;
283362306a36Sopenharmony_ci		case 7: /* AES 7 */
283462306a36Sopenharmony_ci			hdspm->control_register |=
283562306a36Sopenharmony_ci				HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
283662306a36Sopenharmony_ci			break;
283762306a36Sopenharmony_ci		case 8: /* AES 8 */
283862306a36Sopenharmony_ci			hdspm->control_register |= HDSPM_SyncRef3;
283962306a36Sopenharmony_ci			break;
284062306a36Sopenharmony_ci		case 9: /* TCO */
284162306a36Sopenharmony_ci			hdspm->control_register |=
284262306a36Sopenharmony_ci				HDSPM_SyncRef3+HDSPM_SyncRef0;
284362306a36Sopenharmony_ci			break;
284462306a36Sopenharmony_ci		default:
284562306a36Sopenharmony_ci			return -1;
284662306a36Sopenharmony_ci		}
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci		break;
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	case MADI:
285162306a36Sopenharmony_ci	case MADIface:
285262306a36Sopenharmony_ci		hdspm->control_register &= ~HDSPM_SyncRefMask;
285362306a36Sopenharmony_ci		if (hdspm->tco) {
285462306a36Sopenharmony_ci			switch (pref) {
285562306a36Sopenharmony_ci			case 0: /* WC */
285662306a36Sopenharmony_ci				break;
285762306a36Sopenharmony_ci			case 1: /* MADI */
285862306a36Sopenharmony_ci				hdspm->control_register |= HDSPM_SyncRef0;
285962306a36Sopenharmony_ci				break;
286062306a36Sopenharmony_ci			case 2: /* TCO */
286162306a36Sopenharmony_ci				hdspm->control_register |= HDSPM_SyncRef1;
286262306a36Sopenharmony_ci				break;
286362306a36Sopenharmony_ci			case 3: /* SYNC_IN */
286462306a36Sopenharmony_ci				hdspm->control_register |=
286562306a36Sopenharmony_ci					HDSPM_SyncRef0+HDSPM_SyncRef1;
286662306a36Sopenharmony_ci				break;
286762306a36Sopenharmony_ci			default:
286862306a36Sopenharmony_ci				return -1;
286962306a36Sopenharmony_ci			}
287062306a36Sopenharmony_ci		} else {
287162306a36Sopenharmony_ci			switch (pref) {
287262306a36Sopenharmony_ci			case 0: /* WC */
287362306a36Sopenharmony_ci				break;
287462306a36Sopenharmony_ci			case 1: /* MADI */
287562306a36Sopenharmony_ci				hdspm->control_register |= HDSPM_SyncRef0;
287662306a36Sopenharmony_ci				break;
287762306a36Sopenharmony_ci			case 2: /* SYNC_IN */
287862306a36Sopenharmony_ci				hdspm->control_register |=
287962306a36Sopenharmony_ci					HDSPM_SyncRef0+HDSPM_SyncRef1;
288062306a36Sopenharmony_ci				break;
288162306a36Sopenharmony_ci			default:
288262306a36Sopenharmony_ci				return -1;
288362306a36Sopenharmony_ci			}
288462306a36Sopenharmony_ci		}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci		break;
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci	case RayDAT:
288962306a36Sopenharmony_ci		if (hdspm->tco) {
289062306a36Sopenharmony_ci			switch (pref) {
289162306a36Sopenharmony_ci			case 0: p = 0; break;  /* WC */
289262306a36Sopenharmony_ci			case 1: p = 3; break;  /* ADAT 1 */
289362306a36Sopenharmony_ci			case 2: p = 4; break;  /* ADAT 2 */
289462306a36Sopenharmony_ci			case 3: p = 5; break;  /* ADAT 3 */
289562306a36Sopenharmony_ci			case 4: p = 6; break;  /* ADAT 4 */
289662306a36Sopenharmony_ci			case 5: p = 1; break;  /* AES */
289762306a36Sopenharmony_ci			case 6: p = 2; break;  /* SPDIF */
289862306a36Sopenharmony_ci			case 7: p = 9; break;  /* TCO */
289962306a36Sopenharmony_ci			case 8: p = 10; break; /* SYNC_IN */
290062306a36Sopenharmony_ci			default: return -1;
290162306a36Sopenharmony_ci			}
290262306a36Sopenharmony_ci		} else {
290362306a36Sopenharmony_ci			switch (pref) {
290462306a36Sopenharmony_ci			case 0: p = 0; break;  /* WC */
290562306a36Sopenharmony_ci			case 1: p = 3; break;  /* ADAT 1 */
290662306a36Sopenharmony_ci			case 2: p = 4; break;  /* ADAT 2 */
290762306a36Sopenharmony_ci			case 3: p = 5; break;  /* ADAT 3 */
290862306a36Sopenharmony_ci			case 4: p = 6; break;  /* ADAT 4 */
290962306a36Sopenharmony_ci			case 5: p = 1; break;  /* AES */
291062306a36Sopenharmony_ci			case 6: p = 2; break;  /* SPDIF */
291162306a36Sopenharmony_ci			case 7: p = 10; break; /* SYNC_IN */
291262306a36Sopenharmony_ci			default: return -1;
291362306a36Sopenharmony_ci			}
291462306a36Sopenharmony_ci		}
291562306a36Sopenharmony_ci		break;
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	case AIO:
291862306a36Sopenharmony_ci		if (hdspm->tco) {
291962306a36Sopenharmony_ci			switch (pref) {
292062306a36Sopenharmony_ci			case 0: p = 0; break;  /* WC */
292162306a36Sopenharmony_ci			case 1: p = 3; break;  /* ADAT */
292262306a36Sopenharmony_ci			case 2: p = 1; break;  /* AES */
292362306a36Sopenharmony_ci			case 3: p = 2; break;  /* SPDIF */
292462306a36Sopenharmony_ci			case 4: p = 9; break;  /* TCO */
292562306a36Sopenharmony_ci			case 5: p = 10; break; /* SYNC_IN */
292662306a36Sopenharmony_ci			default: return -1;
292762306a36Sopenharmony_ci			}
292862306a36Sopenharmony_ci		} else {
292962306a36Sopenharmony_ci			switch (pref) {
293062306a36Sopenharmony_ci			case 0: p = 0; break;  /* WC */
293162306a36Sopenharmony_ci			case 1: p = 3; break;  /* ADAT */
293262306a36Sopenharmony_ci			case 2: p = 1; break;  /* AES */
293362306a36Sopenharmony_ci			case 3: p = 2; break;  /* SPDIF */
293462306a36Sopenharmony_ci			case 4: p = 10; break; /* SYNC_IN */
293562306a36Sopenharmony_ci			default: return -1;
293662306a36Sopenharmony_ci			}
293762306a36Sopenharmony_ci		}
293862306a36Sopenharmony_ci		break;
293962306a36Sopenharmony_ci	}
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	switch (hdspm->io_type) {
294262306a36Sopenharmony_ci	case RayDAT:
294362306a36Sopenharmony_ci	case AIO:
294462306a36Sopenharmony_ci		hdspm->settings_register &= ~HDSPM_c0_SyncRefMask;
294562306a36Sopenharmony_ci		hdspm->settings_register |= HDSPM_c0_SyncRef0 * p;
294662306a36Sopenharmony_ci		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
294762306a36Sopenharmony_ci		break;
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci	case MADI:
295062306a36Sopenharmony_ci	case MADIface:
295162306a36Sopenharmony_ci	case AES32:
295262306a36Sopenharmony_ci		hdspm_write(hdspm, HDSPM_controlRegister,
295362306a36Sopenharmony_ci				hdspm->control_register);
295462306a36Sopenharmony_ci	}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	return 0;
295762306a36Sopenharmony_ci}
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_cistatic int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
296162306a36Sopenharmony_ci					struct snd_ctl_elem_info *uinfo)
296262306a36Sopenharmony_ci{
296362306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync);
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	return 0;
296862306a36Sopenharmony_ci}
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_cistatic int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol,
297162306a36Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
297262306a36Sopenharmony_ci{
297362306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
297462306a36Sopenharmony_ci	int psf = hdspm_pref_sync_ref(hdspm);
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	if (psf >= 0) {
297762306a36Sopenharmony_ci		ucontrol->value.enumerated.item[0] = psf;
297862306a36Sopenharmony_ci		return 0;
297962306a36Sopenharmony_ci	}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	return -1;
298262306a36Sopenharmony_ci}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_cistatic int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
298562306a36Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
298662306a36Sopenharmony_ci{
298762306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
298862306a36Sopenharmony_ci	int val, change = 0;
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
299162306a36Sopenharmony_ci		return -EBUSY;
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci	val = ucontrol->value.enumerated.item[0];
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	if (val < 0)
299662306a36Sopenharmony_ci		val = 0;
299762306a36Sopenharmony_ci	else if (val >= hdspm->texts_autosync_items)
299862306a36Sopenharmony_ci		val = hdspm->texts_autosync_items-1;
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
300162306a36Sopenharmony_ci	if (val != hdspm_pref_sync_ref(hdspm))
300262306a36Sopenharmony_ci		change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0;
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
300562306a36Sopenharmony_ci	return change;
300662306a36Sopenharmony_ci}
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci#define HDSPM_AUTOSYNC_REF(xname, xindex) \
301062306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
301162306a36Sopenharmony_ci	.name = xname, \
301262306a36Sopenharmony_ci	.index = xindex, \
301362306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ, \
301462306a36Sopenharmony_ci	.info = snd_hdspm_info_autosync_ref, \
301562306a36Sopenharmony_ci	.get = snd_hdspm_get_autosync_ref, \
301662306a36Sopenharmony_ci}
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_cistatic int hdspm_autosync_ref(struct hdspm *hdspm)
301962306a36Sopenharmony_ci{
302062306a36Sopenharmony_ci	/* This looks at the autosync selected sync reference */
302162306a36Sopenharmony_ci	if (AES32 == hdspm->io_type) {
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci		unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
302462306a36Sopenharmony_ci		unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
302562306a36Sopenharmony_ci		/* syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD is always true */
302662306a36Sopenharmony_ci		if (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN) {
302762306a36Sopenharmony_ci			return syncref;
302862306a36Sopenharmony_ci		}
302962306a36Sopenharmony_ci		return HDSPM_AES32_AUTOSYNC_FROM_NONE;
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	} else if (MADI == hdspm->io_type) {
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci		unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
303462306a36Sopenharmony_ci		switch (status2 & HDSPM_SelSyncRefMask) {
303562306a36Sopenharmony_ci		case HDSPM_SelSyncRef_WORD:
303662306a36Sopenharmony_ci			return HDSPM_AUTOSYNC_FROM_WORD;
303762306a36Sopenharmony_ci		case HDSPM_SelSyncRef_MADI:
303862306a36Sopenharmony_ci			return HDSPM_AUTOSYNC_FROM_MADI;
303962306a36Sopenharmony_ci		case HDSPM_SelSyncRef_TCO:
304062306a36Sopenharmony_ci			return HDSPM_AUTOSYNC_FROM_TCO;
304162306a36Sopenharmony_ci		case HDSPM_SelSyncRef_SyncIn:
304262306a36Sopenharmony_ci			return HDSPM_AUTOSYNC_FROM_SYNC_IN;
304362306a36Sopenharmony_ci		case HDSPM_SelSyncRef_NVALID:
304462306a36Sopenharmony_ci			return HDSPM_AUTOSYNC_FROM_NONE;
304562306a36Sopenharmony_ci		default:
304662306a36Sopenharmony_ci			return HDSPM_AUTOSYNC_FROM_NONE;
304762306a36Sopenharmony_ci		}
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_ci	}
305062306a36Sopenharmony_ci	return 0;
305162306a36Sopenharmony_ci}
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_cistatic int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
305562306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
305662306a36Sopenharmony_ci{
305762306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ci	if (AES32 == hdspm->io_type) {
306062306a36Sopenharmony_ci		static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3",
306162306a36Sopenharmony_ci			"AES4",	"AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"};
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ci		ENUMERATED_CTL_INFO(uinfo, texts);
306462306a36Sopenharmony_ci	} else if (MADI == hdspm->io_type) {
306562306a36Sopenharmony_ci		static const char *const texts[] = {"Word Clock", "MADI", "TCO",
306662306a36Sopenharmony_ci			"Sync In", "None" };
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci		ENUMERATED_CTL_INFO(uinfo, texts);
306962306a36Sopenharmony_ci	}
307062306a36Sopenharmony_ci	return 0;
307162306a36Sopenharmony_ci}
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_cistatic int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
307462306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
307562306a36Sopenharmony_ci{
307662306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_autosync_ref(hdspm);
307962306a36Sopenharmony_ci	return 0;
308062306a36Sopenharmony_ci}
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \
308562306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
308662306a36Sopenharmony_ci	.name = xname, \
308762306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ |\
308862306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
308962306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_video_input_format, \
309062306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_video_input_format, \
309162306a36Sopenharmony_ci}
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_cistatic int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
309462306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
309562306a36Sopenharmony_ci{
309662306a36Sopenharmony_ci	static const char *const texts[] = {"No video", "NTSC", "PAL"};
309762306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
309862306a36Sopenharmony_ci	return 0;
309962306a36Sopenharmony_ci}
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_cistatic int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
310262306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
310362306a36Sopenharmony_ci{
310462306a36Sopenharmony_ci	u32 status;
310562306a36Sopenharmony_ci	int ret = 0;
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
310862306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
310962306a36Sopenharmony_ci	switch (status & (HDSPM_TCO1_Video_Input_Format_NTSC |
311062306a36Sopenharmony_ci			HDSPM_TCO1_Video_Input_Format_PAL)) {
311162306a36Sopenharmony_ci	case HDSPM_TCO1_Video_Input_Format_NTSC:
311262306a36Sopenharmony_ci		/* ntsc */
311362306a36Sopenharmony_ci		ret = 1;
311462306a36Sopenharmony_ci		break;
311562306a36Sopenharmony_ci	case HDSPM_TCO1_Video_Input_Format_PAL:
311662306a36Sopenharmony_ci		/* pal */
311762306a36Sopenharmony_ci		ret = 2;
311862306a36Sopenharmony_ci		break;
311962306a36Sopenharmony_ci	default:
312062306a36Sopenharmony_ci		/* no video */
312162306a36Sopenharmony_ci		ret = 0;
312262306a36Sopenharmony_ci		break;
312362306a36Sopenharmony_ci	}
312462306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = ret;
312562306a36Sopenharmony_ci	return 0;
312662306a36Sopenharmony_ci}
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci#define HDSPM_TCO_LTC_FRAMES(xname, xindex) \
313162306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
313262306a36Sopenharmony_ci	.name = xname, \
313362306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ |\
313462306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
313562306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_ltc_frames, \
313662306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_ltc_frames, \
313762306a36Sopenharmony_ci}
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_cistatic int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
314062306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
314162306a36Sopenharmony_ci{
314262306a36Sopenharmony_ci	static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
314362306a36Sopenharmony_ci				"30 fps"};
314462306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
314562306a36Sopenharmony_ci	return 0;
314662306a36Sopenharmony_ci}
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_cistatic int hdspm_tco_ltc_frames(struct hdspm *hdspm)
314962306a36Sopenharmony_ci{
315062306a36Sopenharmony_ci	u32 status;
315162306a36Sopenharmony_ci	int ret = 0;
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
315462306a36Sopenharmony_ci	if (status & HDSPM_TCO1_LTC_Input_valid) {
315562306a36Sopenharmony_ci		switch (status & (HDSPM_TCO1_LTC_Format_LSB |
315662306a36Sopenharmony_ci					HDSPM_TCO1_LTC_Format_MSB)) {
315762306a36Sopenharmony_ci		case 0:
315862306a36Sopenharmony_ci			/* 24 fps */
315962306a36Sopenharmony_ci			ret = fps_24;
316062306a36Sopenharmony_ci			break;
316162306a36Sopenharmony_ci		case HDSPM_TCO1_LTC_Format_LSB:
316262306a36Sopenharmony_ci			/* 25 fps */
316362306a36Sopenharmony_ci			ret = fps_25;
316462306a36Sopenharmony_ci			break;
316562306a36Sopenharmony_ci		case HDSPM_TCO1_LTC_Format_MSB:
316662306a36Sopenharmony_ci			/* 29.97 fps */
316762306a36Sopenharmony_ci			ret = fps_2997;
316862306a36Sopenharmony_ci			break;
316962306a36Sopenharmony_ci		default:
317062306a36Sopenharmony_ci			/* 30 fps */
317162306a36Sopenharmony_ci			ret = fps_30;
317262306a36Sopenharmony_ci			break;
317362306a36Sopenharmony_ci		}
317462306a36Sopenharmony_ci	}
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci	return ret;
317762306a36Sopenharmony_ci}
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_cistatic int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
318062306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
318162306a36Sopenharmony_ci{
318262306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_tco_ltc_frames(hdspm);
318562306a36Sopenharmony_ci	return 0;
318662306a36Sopenharmony_ci}
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci#define HDSPM_TOGGLE_SETTING(xname, xindex) \
318962306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
319062306a36Sopenharmony_ci	.name = xname, \
319162306a36Sopenharmony_ci	.private_value = xindex, \
319262306a36Sopenharmony_ci	.info = snd_hdspm_info_toggle_setting, \
319362306a36Sopenharmony_ci	.get = snd_hdspm_get_toggle_setting, \
319462306a36Sopenharmony_ci	.put = snd_hdspm_put_toggle_setting \
319562306a36Sopenharmony_ci}
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_cistatic int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask)
319862306a36Sopenharmony_ci{
319962306a36Sopenharmony_ci	u32 reg;
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci	if (hdspm_is_raydat_or_aio(hdspm))
320262306a36Sopenharmony_ci		reg = hdspm->settings_register;
320362306a36Sopenharmony_ci	else
320462306a36Sopenharmony_ci		reg = hdspm->control_register;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	return (reg & regmask) ? 1 : 0;
320762306a36Sopenharmony_ci}
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_cistatic int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out)
321062306a36Sopenharmony_ci{
321162306a36Sopenharmony_ci	u32 *reg;
321262306a36Sopenharmony_ci	u32 target_reg;
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	if (hdspm_is_raydat_or_aio(hdspm)) {
321562306a36Sopenharmony_ci		reg = &(hdspm->settings_register);
321662306a36Sopenharmony_ci		target_reg = HDSPM_WR_SETTINGS;
321762306a36Sopenharmony_ci	} else {
321862306a36Sopenharmony_ci		reg = &(hdspm->control_register);
321962306a36Sopenharmony_ci		target_reg = HDSPM_controlRegister;
322062306a36Sopenharmony_ci	}
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	if (out)
322362306a36Sopenharmony_ci		*reg |= regmask;
322462306a36Sopenharmony_ci	else
322562306a36Sopenharmony_ci		*reg &= ~regmask;
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	hdspm_write(hdspm, target_reg, *reg);
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci	return 0;
323062306a36Sopenharmony_ci}
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci#define snd_hdspm_info_toggle_setting		snd_ctl_boolean_mono_info
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_cistatic int snd_hdspm_get_toggle_setting(struct snd_kcontrol *kcontrol,
323562306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
323662306a36Sopenharmony_ci{
323762306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
323862306a36Sopenharmony_ci	u32 regmask = kcontrol->private_value;
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
324162306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = hdspm_toggle_setting(hdspm, regmask);
324262306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
324362306a36Sopenharmony_ci	return 0;
324462306a36Sopenharmony_ci}
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_cistatic int snd_hdspm_put_toggle_setting(struct snd_kcontrol *kcontrol,
324762306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
324862306a36Sopenharmony_ci{
324962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
325062306a36Sopenharmony_ci	u32 regmask = kcontrol->private_value;
325162306a36Sopenharmony_ci	int change;
325262306a36Sopenharmony_ci	unsigned int val;
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
325562306a36Sopenharmony_ci		return -EBUSY;
325662306a36Sopenharmony_ci	val = ucontrol->value.integer.value[0] & 1;
325762306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
325862306a36Sopenharmony_ci	change = (int) val != hdspm_toggle_setting(hdspm, regmask);
325962306a36Sopenharmony_ci	hdspm_set_toggle_setting(hdspm, regmask, val);
326062306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
326162306a36Sopenharmony_ci	return change;
326262306a36Sopenharmony_ci}
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci#define HDSPM_INPUT_SELECT(xname, xindex) \
326562306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
326662306a36Sopenharmony_ci	.name = xname, \
326762306a36Sopenharmony_ci	.index = xindex, \
326862306a36Sopenharmony_ci	.info = snd_hdspm_info_input_select, \
326962306a36Sopenharmony_ci	.get = snd_hdspm_get_input_select, \
327062306a36Sopenharmony_ci	.put = snd_hdspm_put_input_select \
327162306a36Sopenharmony_ci}
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_cistatic int hdspm_input_select(struct hdspm * hdspm)
327462306a36Sopenharmony_ci{
327562306a36Sopenharmony_ci	return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
327662306a36Sopenharmony_ci}
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_cistatic int hdspm_set_input_select(struct hdspm * hdspm, int out)
327962306a36Sopenharmony_ci{
328062306a36Sopenharmony_ci	if (out)
328162306a36Sopenharmony_ci		hdspm->control_register |= HDSPM_InputSelect0;
328262306a36Sopenharmony_ci	else
328362306a36Sopenharmony_ci		hdspm->control_register &= ~HDSPM_InputSelect0;
328462306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci	return 0;
328762306a36Sopenharmony_ci}
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_cistatic int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
329062306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
329162306a36Sopenharmony_ci{
329262306a36Sopenharmony_ci	static const char *const texts[] = { "optical", "coaxial" };
329362306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
329462306a36Sopenharmony_ci	return 0;
329562306a36Sopenharmony_ci}
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_cistatic int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
329862306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
329962306a36Sopenharmony_ci{
330062306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
330362306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
330462306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
330562306a36Sopenharmony_ci	return 0;
330662306a36Sopenharmony_ci}
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_cistatic int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
330962306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
331062306a36Sopenharmony_ci{
331162306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
331262306a36Sopenharmony_ci	int change;
331362306a36Sopenharmony_ci	unsigned int val;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
331662306a36Sopenharmony_ci		return -EBUSY;
331762306a36Sopenharmony_ci	val = ucontrol->value.integer.value[0] & 1;
331862306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
331962306a36Sopenharmony_ci	change = (int) val != hdspm_input_select(hdspm);
332062306a36Sopenharmony_ci	hdspm_set_input_select(hdspm, val);
332162306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
332262306a36Sopenharmony_ci	return change;
332362306a36Sopenharmony_ci}
332462306a36Sopenharmony_ci
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci#define HDSPM_DS_WIRE(xname, xindex) \
332762306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
332862306a36Sopenharmony_ci	.name = xname, \
332962306a36Sopenharmony_ci	.index = xindex, \
333062306a36Sopenharmony_ci	.info = snd_hdspm_info_ds_wire, \
333162306a36Sopenharmony_ci	.get = snd_hdspm_get_ds_wire, \
333262306a36Sopenharmony_ci	.put = snd_hdspm_put_ds_wire \
333362306a36Sopenharmony_ci}
333462306a36Sopenharmony_ci
333562306a36Sopenharmony_cistatic int hdspm_ds_wire(struct hdspm * hdspm)
333662306a36Sopenharmony_ci{
333762306a36Sopenharmony_ci	return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
333862306a36Sopenharmony_ci}
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_cistatic int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
334162306a36Sopenharmony_ci{
334262306a36Sopenharmony_ci	if (ds)
334362306a36Sopenharmony_ci		hdspm->control_register |= HDSPM_DS_DoubleWire;
334462306a36Sopenharmony_ci	else
334562306a36Sopenharmony_ci		hdspm->control_register &= ~HDSPM_DS_DoubleWire;
334662306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
334762306a36Sopenharmony_ci
334862306a36Sopenharmony_ci	return 0;
334962306a36Sopenharmony_ci}
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_cistatic int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
335262306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
335362306a36Sopenharmony_ci{
335462306a36Sopenharmony_ci	static const char *const texts[] = { "Single", "Double" };
335562306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
335662306a36Sopenharmony_ci	return 0;
335762306a36Sopenharmony_ci}
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_cistatic int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
336062306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
336162306a36Sopenharmony_ci{
336262306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
336562306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
336662306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
336762306a36Sopenharmony_ci	return 0;
336862306a36Sopenharmony_ci}
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_cistatic int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
337162306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
337262306a36Sopenharmony_ci{
337362306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
337462306a36Sopenharmony_ci	int change;
337562306a36Sopenharmony_ci	unsigned int val;
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
337862306a36Sopenharmony_ci		return -EBUSY;
337962306a36Sopenharmony_ci	val = ucontrol->value.integer.value[0] & 1;
338062306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
338162306a36Sopenharmony_ci	change = (int) val != hdspm_ds_wire(hdspm);
338262306a36Sopenharmony_ci	hdspm_set_ds_wire(hdspm, val);
338362306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
338462306a36Sopenharmony_ci	return change;
338562306a36Sopenharmony_ci}
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_ci#define HDSPM_QS_WIRE(xname, xindex) \
338962306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
339062306a36Sopenharmony_ci	.name = xname, \
339162306a36Sopenharmony_ci	.index = xindex, \
339262306a36Sopenharmony_ci	.info = snd_hdspm_info_qs_wire, \
339362306a36Sopenharmony_ci	.get = snd_hdspm_get_qs_wire, \
339462306a36Sopenharmony_ci	.put = snd_hdspm_put_qs_wire \
339562306a36Sopenharmony_ci}
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_cistatic int hdspm_qs_wire(struct hdspm * hdspm)
339862306a36Sopenharmony_ci{
339962306a36Sopenharmony_ci	if (hdspm->control_register & HDSPM_QS_DoubleWire)
340062306a36Sopenharmony_ci		return 1;
340162306a36Sopenharmony_ci	if (hdspm->control_register & HDSPM_QS_QuadWire)
340262306a36Sopenharmony_ci		return 2;
340362306a36Sopenharmony_ci	return 0;
340462306a36Sopenharmony_ci}
340562306a36Sopenharmony_ci
340662306a36Sopenharmony_cistatic int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
340762306a36Sopenharmony_ci{
340862306a36Sopenharmony_ci	hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
340962306a36Sopenharmony_ci	switch (mode) {
341062306a36Sopenharmony_ci	case 0:
341162306a36Sopenharmony_ci		break;
341262306a36Sopenharmony_ci	case 1:
341362306a36Sopenharmony_ci		hdspm->control_register |= HDSPM_QS_DoubleWire;
341462306a36Sopenharmony_ci		break;
341562306a36Sopenharmony_ci	case 2:
341662306a36Sopenharmony_ci		hdspm->control_register |= HDSPM_QS_QuadWire;
341762306a36Sopenharmony_ci		break;
341862306a36Sopenharmony_ci	}
341962306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	return 0;
342262306a36Sopenharmony_ci}
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_cistatic int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
342562306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
342662306a36Sopenharmony_ci{
342762306a36Sopenharmony_ci	static const char *const texts[] = { "Single", "Double", "Quad" };
342862306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
342962306a36Sopenharmony_ci	return 0;
343062306a36Sopenharmony_ci}
343162306a36Sopenharmony_ci
343262306a36Sopenharmony_cistatic int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
343362306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
343462306a36Sopenharmony_ci{
343562306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
343662306a36Sopenharmony_ci
343762306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
343862306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
343962306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
344062306a36Sopenharmony_ci	return 0;
344162306a36Sopenharmony_ci}
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_cistatic int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
344462306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
344562306a36Sopenharmony_ci{
344662306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
344762306a36Sopenharmony_ci	int change;
344862306a36Sopenharmony_ci	int val;
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
345162306a36Sopenharmony_ci		return -EBUSY;
345262306a36Sopenharmony_ci	val = ucontrol->value.integer.value[0];
345362306a36Sopenharmony_ci	if (val < 0)
345462306a36Sopenharmony_ci		val = 0;
345562306a36Sopenharmony_ci	if (val > 2)
345662306a36Sopenharmony_ci		val = 2;
345762306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
345862306a36Sopenharmony_ci	change = val != hdspm_qs_wire(hdspm);
345962306a36Sopenharmony_ci	hdspm_set_qs_wire(hdspm, val);
346062306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
346162306a36Sopenharmony_ci	return change;
346262306a36Sopenharmony_ci}
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci#define HDSPM_CONTROL_TRISTATE(xname, xindex) \
346562306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
346662306a36Sopenharmony_ci	.name = xname, \
346762306a36Sopenharmony_ci	.private_value = xindex, \
346862306a36Sopenharmony_ci	.info = snd_hdspm_info_tristate, \
346962306a36Sopenharmony_ci	.get = snd_hdspm_get_tristate, \
347062306a36Sopenharmony_ci	.put = snd_hdspm_put_tristate \
347162306a36Sopenharmony_ci}
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_cistatic int hdspm_tristate(struct hdspm *hdspm, u32 regmask)
347462306a36Sopenharmony_ci{
347562306a36Sopenharmony_ci	u32 reg = hdspm->settings_register & (regmask * 3);
347662306a36Sopenharmony_ci	return reg / regmask;
347762306a36Sopenharmony_ci}
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_cistatic int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask)
348062306a36Sopenharmony_ci{
348162306a36Sopenharmony_ci	hdspm->settings_register &= ~(regmask * 3);
348262306a36Sopenharmony_ci	hdspm->settings_register |= (regmask * mode);
348362306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	return 0;
348662306a36Sopenharmony_ci}
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_cistatic int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol,
348962306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
349062306a36Sopenharmony_ci{
349162306a36Sopenharmony_ci	u32 regmask = kcontrol->private_value;
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" };
349462306a36Sopenharmony_ci	static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" };
349562306a36Sopenharmony_ci
349662306a36Sopenharmony_ci	switch (regmask) {
349762306a36Sopenharmony_ci	case HDSPM_c0_Input0:
349862306a36Sopenharmony_ci		ENUMERATED_CTL_INFO(uinfo, texts_spdif);
349962306a36Sopenharmony_ci		break;
350062306a36Sopenharmony_ci	default:
350162306a36Sopenharmony_ci		ENUMERATED_CTL_INFO(uinfo, texts_levels);
350262306a36Sopenharmony_ci		break;
350362306a36Sopenharmony_ci	}
350462306a36Sopenharmony_ci	return 0;
350562306a36Sopenharmony_ci}
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_cistatic int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol,
350862306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
350962306a36Sopenharmony_ci{
351062306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
351162306a36Sopenharmony_ci	u32 regmask = kcontrol->private_value;
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
351462306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask);
351562306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
351662306a36Sopenharmony_ci	return 0;
351762306a36Sopenharmony_ci}
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_cistatic int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol,
352062306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
352162306a36Sopenharmony_ci{
352262306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
352362306a36Sopenharmony_ci	u32 regmask = kcontrol->private_value;
352462306a36Sopenharmony_ci	int change;
352562306a36Sopenharmony_ci	int val;
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
352862306a36Sopenharmony_ci		return -EBUSY;
352962306a36Sopenharmony_ci	val = ucontrol->value.integer.value[0];
353062306a36Sopenharmony_ci	if (val < 0)
353162306a36Sopenharmony_ci		val = 0;
353262306a36Sopenharmony_ci	if (val > 2)
353362306a36Sopenharmony_ci		val = 2;
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
353662306a36Sopenharmony_ci	change = val != hdspm_tristate(hdspm, regmask);
353762306a36Sopenharmony_ci	hdspm_set_tristate(hdspm, val, regmask);
353862306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
353962306a36Sopenharmony_ci	return change;
354062306a36Sopenharmony_ci}
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci#define HDSPM_MADI_SPEEDMODE(xname, xindex) \
354362306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
354462306a36Sopenharmony_ci	.name = xname, \
354562306a36Sopenharmony_ci	.index = xindex, \
354662306a36Sopenharmony_ci	.info = snd_hdspm_info_madi_speedmode, \
354762306a36Sopenharmony_ci	.get = snd_hdspm_get_madi_speedmode, \
354862306a36Sopenharmony_ci	.put = snd_hdspm_put_madi_speedmode \
354962306a36Sopenharmony_ci}
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_cistatic int hdspm_madi_speedmode(struct hdspm *hdspm)
355262306a36Sopenharmony_ci{
355362306a36Sopenharmony_ci	if (hdspm->control_register & HDSPM_QuadSpeed)
355462306a36Sopenharmony_ci		return 2;
355562306a36Sopenharmony_ci	if (hdspm->control_register & HDSPM_DoubleSpeed)
355662306a36Sopenharmony_ci		return 1;
355762306a36Sopenharmony_ci	return 0;
355862306a36Sopenharmony_ci}
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_cistatic int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
356162306a36Sopenharmony_ci{
356262306a36Sopenharmony_ci	hdspm->control_register &= ~(HDSPM_DoubleSpeed | HDSPM_QuadSpeed);
356362306a36Sopenharmony_ci	switch (mode) {
356462306a36Sopenharmony_ci	case 0:
356562306a36Sopenharmony_ci		break;
356662306a36Sopenharmony_ci	case 1:
356762306a36Sopenharmony_ci		hdspm->control_register |= HDSPM_DoubleSpeed;
356862306a36Sopenharmony_ci		break;
356962306a36Sopenharmony_ci	case 2:
357062306a36Sopenharmony_ci		hdspm->control_register |= HDSPM_QuadSpeed;
357162306a36Sopenharmony_ci		break;
357262306a36Sopenharmony_ci	}
357362306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ci	return 0;
357662306a36Sopenharmony_ci}
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_cistatic int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
357962306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
358062306a36Sopenharmony_ci{
358162306a36Sopenharmony_ci	static const char *const texts[] = { "Single", "Double", "Quad" };
358262306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
358362306a36Sopenharmony_ci	return 0;
358462306a36Sopenharmony_ci}
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_cistatic int snd_hdspm_get_madi_speedmode(struct snd_kcontrol *kcontrol,
358762306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
358862306a36Sopenharmony_ci{
358962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
359262306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm_madi_speedmode(hdspm);
359362306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
359462306a36Sopenharmony_ci	return 0;
359562306a36Sopenharmony_ci}
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_cistatic int snd_hdspm_put_madi_speedmode(struct snd_kcontrol *kcontrol,
359862306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
359962306a36Sopenharmony_ci{
360062306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
360162306a36Sopenharmony_ci	int change;
360262306a36Sopenharmony_ci	int val;
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
360562306a36Sopenharmony_ci		return -EBUSY;
360662306a36Sopenharmony_ci	val = ucontrol->value.integer.value[0];
360762306a36Sopenharmony_ci	if (val < 0)
360862306a36Sopenharmony_ci		val = 0;
360962306a36Sopenharmony_ci	if (val > 2)
361062306a36Sopenharmony_ci		val = 2;
361162306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
361262306a36Sopenharmony_ci	change = val != hdspm_madi_speedmode(hdspm);
361362306a36Sopenharmony_ci	hdspm_set_madi_speedmode(hdspm, val);
361462306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
361562306a36Sopenharmony_ci	return change;
361662306a36Sopenharmony_ci}
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci#define HDSPM_MIXER(xname, xindex) \
361962306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
362062306a36Sopenharmony_ci	.name = xname, \
362162306a36Sopenharmony_ci	.index = xindex, \
362262306a36Sopenharmony_ci	.device = 0, \
362362306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
362462306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
362562306a36Sopenharmony_ci	.info = snd_hdspm_info_mixer, \
362662306a36Sopenharmony_ci	.get = snd_hdspm_get_mixer, \
362762306a36Sopenharmony_ci	.put = snd_hdspm_put_mixer \
362862306a36Sopenharmony_ci}
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_cistatic int snd_hdspm_info_mixer(struct snd_kcontrol *kcontrol,
363162306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
363262306a36Sopenharmony_ci{
363362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
363462306a36Sopenharmony_ci	uinfo->count = 3;
363562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
363662306a36Sopenharmony_ci	uinfo->value.integer.max = 65535;
363762306a36Sopenharmony_ci	uinfo->value.integer.step = 1;
363862306a36Sopenharmony_ci	return 0;
363962306a36Sopenharmony_ci}
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_cistatic int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
364262306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
364362306a36Sopenharmony_ci{
364462306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
364562306a36Sopenharmony_ci	int source;
364662306a36Sopenharmony_ci	int destination;
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci	source = ucontrol->value.integer.value[0];
364962306a36Sopenharmony_ci	if (source < 0)
365062306a36Sopenharmony_ci		source = 0;
365162306a36Sopenharmony_ci	else if (source >= 2 * HDSPM_MAX_CHANNELS)
365262306a36Sopenharmony_ci		source = 2 * HDSPM_MAX_CHANNELS - 1;
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ci	destination = ucontrol->value.integer.value[1];
365562306a36Sopenharmony_ci	if (destination < 0)
365662306a36Sopenharmony_ci		destination = 0;
365762306a36Sopenharmony_ci	else if (destination >= HDSPM_MAX_CHANNELS)
365862306a36Sopenharmony_ci		destination = HDSPM_MAX_CHANNELS - 1;
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
366162306a36Sopenharmony_ci	if (source >= HDSPM_MAX_CHANNELS)
366262306a36Sopenharmony_ci		ucontrol->value.integer.value[2] =
366362306a36Sopenharmony_ci		    hdspm_read_pb_gain(hdspm, destination,
366462306a36Sopenharmony_ci				       source - HDSPM_MAX_CHANNELS);
366562306a36Sopenharmony_ci	else
366662306a36Sopenharmony_ci		ucontrol->value.integer.value[2] =
366762306a36Sopenharmony_ci		    hdspm_read_in_gain(hdspm, destination, source);
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci	return 0;
367262306a36Sopenharmony_ci}
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_cistatic int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
367562306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
367662306a36Sopenharmony_ci{
367762306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
367862306a36Sopenharmony_ci	int change;
367962306a36Sopenharmony_ci	int source;
368062306a36Sopenharmony_ci	int destination;
368162306a36Sopenharmony_ci	int gain;
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
368462306a36Sopenharmony_ci		return -EBUSY;
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	source = ucontrol->value.integer.value[0];
368762306a36Sopenharmony_ci	destination = ucontrol->value.integer.value[1];
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci	if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
369062306a36Sopenharmony_ci		return -1;
369162306a36Sopenharmony_ci	if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
369262306a36Sopenharmony_ci		return -1;
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci	gain = ucontrol->value.integer.value[2];
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
369762306a36Sopenharmony_ci
369862306a36Sopenharmony_ci	if (source >= HDSPM_MAX_CHANNELS)
369962306a36Sopenharmony_ci		change = gain != hdspm_read_pb_gain(hdspm, destination,
370062306a36Sopenharmony_ci						    source -
370162306a36Sopenharmony_ci						    HDSPM_MAX_CHANNELS);
370262306a36Sopenharmony_ci	else
370362306a36Sopenharmony_ci		change = gain != hdspm_read_in_gain(hdspm, destination,
370462306a36Sopenharmony_ci						    source);
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_ci	if (change) {
370762306a36Sopenharmony_ci		if (source >= HDSPM_MAX_CHANNELS)
370862306a36Sopenharmony_ci			hdspm_write_pb_gain(hdspm, destination,
370962306a36Sopenharmony_ci					    source - HDSPM_MAX_CHANNELS,
371062306a36Sopenharmony_ci					    gain);
371162306a36Sopenharmony_ci		else
371262306a36Sopenharmony_ci			hdspm_write_in_gain(hdspm, destination, source,
371362306a36Sopenharmony_ci					    gain);
371462306a36Sopenharmony_ci	}
371562306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
371662306a36Sopenharmony_ci
371762306a36Sopenharmony_ci	return change;
371862306a36Sopenharmony_ci}
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci/* The simple mixer control(s) provide gain control for the
372162306a36Sopenharmony_ci   basic 1:1 mappings of playback streams to output
372262306a36Sopenharmony_ci   streams.
372362306a36Sopenharmony_ci*/
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci#define HDSPM_PLAYBACK_MIXER \
372662306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
372762306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
372862306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
372962306a36Sopenharmony_ci	.info = snd_hdspm_info_playback_mixer, \
373062306a36Sopenharmony_ci	.get = snd_hdspm_get_playback_mixer, \
373162306a36Sopenharmony_ci	.put = snd_hdspm_put_playback_mixer \
373262306a36Sopenharmony_ci}
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_cistatic int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
373562306a36Sopenharmony_ci					 struct snd_ctl_elem_info *uinfo)
373662306a36Sopenharmony_ci{
373762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
373862306a36Sopenharmony_ci	uinfo->count = 1;
373962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
374062306a36Sopenharmony_ci	uinfo->value.integer.max = 64;
374162306a36Sopenharmony_ci	uinfo->value.integer.step = 1;
374262306a36Sopenharmony_ci	return 0;
374362306a36Sopenharmony_ci}
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_cistatic int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
374662306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
374762306a36Sopenharmony_ci{
374862306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
374962306a36Sopenharmony_ci	int channel;
375062306a36Sopenharmony_ci
375162306a36Sopenharmony_ci	channel = ucontrol->id.index - 1;
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_ci	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
375462306a36Sopenharmony_ci		return -EINVAL;
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
375762306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
375862306a36Sopenharmony_ci	  (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
375962306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
376062306a36Sopenharmony_ci
376162306a36Sopenharmony_ci	return 0;
376262306a36Sopenharmony_ci}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_cistatic int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
376562306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
376662306a36Sopenharmony_ci{
376762306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
376862306a36Sopenharmony_ci	int change;
376962306a36Sopenharmony_ci	int channel;
377062306a36Sopenharmony_ci	int gain;
377162306a36Sopenharmony_ci
377262306a36Sopenharmony_ci	if (!snd_hdspm_use_is_exclusive(hdspm))
377362306a36Sopenharmony_ci		return -EBUSY;
377462306a36Sopenharmony_ci
377562306a36Sopenharmony_ci	channel = ucontrol->id.index - 1;
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
377862306a36Sopenharmony_ci		return -EINVAL;
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
378362306a36Sopenharmony_ci	change =
378462306a36Sopenharmony_ci	    gain != hdspm_read_pb_gain(hdspm, channel,
378562306a36Sopenharmony_ci				       channel);
378662306a36Sopenharmony_ci	if (change)
378762306a36Sopenharmony_ci		hdspm_write_pb_gain(hdspm, channel, channel,
378862306a36Sopenharmony_ci				    gain);
378962306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
379062306a36Sopenharmony_ci	return change;
379162306a36Sopenharmony_ci}
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci#define HDSPM_SYNC_CHECK(xname, xindex) \
379462306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
379562306a36Sopenharmony_ci	.name = xname, \
379662306a36Sopenharmony_ci	.private_value = xindex, \
379762306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
379862306a36Sopenharmony_ci	.info = snd_hdspm_info_sync_check, \
379962306a36Sopenharmony_ci	.get = snd_hdspm_get_sync_check \
380062306a36Sopenharmony_ci}
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_ci#define HDSPM_TCO_LOCK_CHECK(xname, xindex) \
380362306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
380462306a36Sopenharmony_ci	.name = xname, \
380562306a36Sopenharmony_ci	.private_value = xindex, \
380662306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
380762306a36Sopenharmony_ci	.info = snd_hdspm_tco_info_lock_check, \
380862306a36Sopenharmony_ci	.get = snd_hdspm_get_sync_check \
380962306a36Sopenharmony_ci}
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_cistatic int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
381462306a36Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
381562306a36Sopenharmony_ci{
381662306a36Sopenharmony_ci	static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" };
381762306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
381862306a36Sopenharmony_ci	return 0;
381962306a36Sopenharmony_ci}
382062306a36Sopenharmony_ci
382162306a36Sopenharmony_cistatic int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
382262306a36Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
382362306a36Sopenharmony_ci{
382462306a36Sopenharmony_ci	static const char *const texts[] = { "No Lock", "Lock" };
382562306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
382662306a36Sopenharmony_ci	return 0;
382762306a36Sopenharmony_ci}
382862306a36Sopenharmony_ci
382962306a36Sopenharmony_cistatic int hdspm_wc_sync_check(struct hdspm *hdspm)
383062306a36Sopenharmony_ci{
383162306a36Sopenharmony_ci	int status, status2;
383262306a36Sopenharmony_ci
383362306a36Sopenharmony_ci	switch (hdspm->io_type) {
383462306a36Sopenharmony_ci	case AES32:
383562306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister);
383662306a36Sopenharmony_ci		if (status & HDSPM_AES32_wcLock) {
383762306a36Sopenharmony_ci			if (status & HDSPM_AES32_wcSync)
383862306a36Sopenharmony_ci				return 2;
383962306a36Sopenharmony_ci			else
384062306a36Sopenharmony_ci				return 1;
384162306a36Sopenharmony_ci		}
384262306a36Sopenharmony_ci		return 0;
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci	case MADI:
384562306a36Sopenharmony_ci		status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
384662306a36Sopenharmony_ci		if (status2 & HDSPM_wcLock) {
384762306a36Sopenharmony_ci			if (status2 & HDSPM_wcSync)
384862306a36Sopenharmony_ci				return 2;
384962306a36Sopenharmony_ci			else
385062306a36Sopenharmony_ci				return 1;
385162306a36Sopenharmony_ci		}
385262306a36Sopenharmony_ci		return 0;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	case RayDAT:
385562306a36Sopenharmony_ci	case AIO:
385662306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister);
385762306a36Sopenharmony_ci
385862306a36Sopenharmony_ci		if (status & 0x2000000)
385962306a36Sopenharmony_ci			return 2;
386062306a36Sopenharmony_ci		else if (status & 0x1000000)
386162306a36Sopenharmony_ci			return 1;
386262306a36Sopenharmony_ci		return 0;
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	case MADIface:
386562306a36Sopenharmony_ci		break;
386662306a36Sopenharmony_ci	}
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ci	return 3;
387062306a36Sopenharmony_ci}
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_cistatic int hdspm_madi_sync_check(struct hdspm *hdspm)
387462306a36Sopenharmony_ci{
387562306a36Sopenharmony_ci	int status = hdspm_read(hdspm, HDSPM_statusRegister);
387662306a36Sopenharmony_ci	if (status & HDSPM_madiLock) {
387762306a36Sopenharmony_ci		if (status & HDSPM_madiSync)
387862306a36Sopenharmony_ci			return 2;
387962306a36Sopenharmony_ci		else
388062306a36Sopenharmony_ci			return 1;
388162306a36Sopenharmony_ci	}
388262306a36Sopenharmony_ci	return 0;
388362306a36Sopenharmony_ci}
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_cistatic int hdspm_s1_sync_check(struct hdspm *hdspm, int idx)
388762306a36Sopenharmony_ci{
388862306a36Sopenharmony_ci	int status, lock, sync;
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	lock = (status & (0x1<<idx)) ? 1 : 0;
389362306a36Sopenharmony_ci	sync = (status & (0x100<<idx)) ? 1 : 0;
389462306a36Sopenharmony_ci
389562306a36Sopenharmony_ci	if (lock && sync)
389662306a36Sopenharmony_ci		return 2;
389762306a36Sopenharmony_ci	else if (lock)
389862306a36Sopenharmony_ci		return 1;
389962306a36Sopenharmony_ci	return 0;
390062306a36Sopenharmony_ci}
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_cistatic int hdspm_sync_in_sync_check(struct hdspm *hdspm)
390462306a36Sopenharmony_ci{
390562306a36Sopenharmony_ci	int status, lock = 0, sync = 0;
390662306a36Sopenharmony_ci
390762306a36Sopenharmony_ci	switch (hdspm->io_type) {
390862306a36Sopenharmony_ci	case RayDAT:
390962306a36Sopenharmony_ci	case AIO:
391062306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_RD_STATUS_3);
391162306a36Sopenharmony_ci		lock = (status & 0x400) ? 1 : 0;
391262306a36Sopenharmony_ci		sync = (status & 0x800) ? 1 : 0;
391362306a36Sopenharmony_ci		break;
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	case MADI:
391662306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister);
391762306a36Sopenharmony_ci		lock = (status & HDSPM_syncInLock) ? 1 : 0;
391862306a36Sopenharmony_ci		sync = (status & HDSPM_syncInSync) ? 1 : 0;
391962306a36Sopenharmony_ci		break;
392062306a36Sopenharmony_ci
392162306a36Sopenharmony_ci	case AES32:
392262306a36Sopenharmony_ci		status = hdspm_read(hdspm, HDSPM_statusRegister2);
392362306a36Sopenharmony_ci		lock = (status & 0x100000) ? 1 : 0;
392462306a36Sopenharmony_ci		sync = (status & 0x200000) ? 1 : 0;
392562306a36Sopenharmony_ci		break;
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci	case MADIface:
392862306a36Sopenharmony_ci		break;
392962306a36Sopenharmony_ci	}
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ci	if (lock && sync)
393262306a36Sopenharmony_ci		return 2;
393362306a36Sopenharmony_ci	else if (lock)
393462306a36Sopenharmony_ci		return 1;
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	return 0;
393762306a36Sopenharmony_ci}
393862306a36Sopenharmony_ci
393962306a36Sopenharmony_cistatic int hdspm_aes_sync_check(struct hdspm *hdspm, int idx)
394062306a36Sopenharmony_ci{
394162306a36Sopenharmony_ci	int status2, lock, sync;
394262306a36Sopenharmony_ci	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_ci	lock = (status2 & (0x0080 >> idx)) ? 1 : 0;
394562306a36Sopenharmony_ci	sync = (status2 & (0x8000 >> idx)) ? 1 : 0;
394662306a36Sopenharmony_ci
394762306a36Sopenharmony_ci	if (sync)
394862306a36Sopenharmony_ci		return 2;
394962306a36Sopenharmony_ci	else if (lock)
395062306a36Sopenharmony_ci		return 1;
395162306a36Sopenharmony_ci	return 0;
395262306a36Sopenharmony_ci}
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_cistatic int hdspm_tco_input_check(struct hdspm *hdspm, u32 mask)
395562306a36Sopenharmony_ci{
395662306a36Sopenharmony_ci	u32 status;
395762306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	return (status & mask) ? 1 : 0;
396062306a36Sopenharmony_ci}
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_cistatic int hdspm_tco_sync_check(struct hdspm *hdspm)
396462306a36Sopenharmony_ci{
396562306a36Sopenharmony_ci	int status;
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci	if (hdspm->tco) {
396862306a36Sopenharmony_ci		switch (hdspm->io_type) {
396962306a36Sopenharmony_ci		case MADI:
397062306a36Sopenharmony_ci			status = hdspm_read(hdspm, HDSPM_statusRegister);
397162306a36Sopenharmony_ci			if (status & HDSPM_tcoLockMadi) {
397262306a36Sopenharmony_ci				if (status & HDSPM_tcoSync)
397362306a36Sopenharmony_ci					return 2;
397462306a36Sopenharmony_ci				else
397562306a36Sopenharmony_ci					return 1;
397662306a36Sopenharmony_ci			}
397762306a36Sopenharmony_ci			return 0;
397862306a36Sopenharmony_ci		case AES32:
397962306a36Sopenharmony_ci			status = hdspm_read(hdspm, HDSPM_statusRegister);
398062306a36Sopenharmony_ci			if (status & HDSPM_tcoLockAes) {
398162306a36Sopenharmony_ci				if (status & HDSPM_tcoSync)
398262306a36Sopenharmony_ci					return 2;
398362306a36Sopenharmony_ci				else
398462306a36Sopenharmony_ci					return 1;
398562306a36Sopenharmony_ci			}
398662306a36Sopenharmony_ci			return 0;
398762306a36Sopenharmony_ci		case RayDAT:
398862306a36Sopenharmony_ci		case AIO:
398962306a36Sopenharmony_ci			status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_ci			if (status & 0x8000000)
399262306a36Sopenharmony_ci				return 2; /* Sync */
399362306a36Sopenharmony_ci			if (status & 0x4000000)
399462306a36Sopenharmony_ci				return 1; /* Lock */
399562306a36Sopenharmony_ci			return 0; /* No signal */
399662306a36Sopenharmony_ci
399762306a36Sopenharmony_ci		default:
399862306a36Sopenharmony_ci			break;
399962306a36Sopenharmony_ci		}
400062306a36Sopenharmony_ci	}
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci	return 3; /* N/A */
400362306a36Sopenharmony_ci}
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_ci
400662306a36Sopenharmony_cistatic int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
400762306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
400862306a36Sopenharmony_ci{
400962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
401062306a36Sopenharmony_ci	int val = -1;
401162306a36Sopenharmony_ci
401262306a36Sopenharmony_ci	switch (hdspm->io_type) {
401362306a36Sopenharmony_ci	case RayDAT:
401462306a36Sopenharmony_ci		switch (kcontrol->private_value) {
401562306a36Sopenharmony_ci		case 0: /* WC */
401662306a36Sopenharmony_ci			val = hdspm_wc_sync_check(hdspm); break;
401762306a36Sopenharmony_ci		case 7: /* TCO */
401862306a36Sopenharmony_ci			val = hdspm_tco_sync_check(hdspm); break;
401962306a36Sopenharmony_ci		case 8: /* SYNC IN */
402062306a36Sopenharmony_ci			val = hdspm_sync_in_sync_check(hdspm); break;
402162306a36Sopenharmony_ci		default:
402262306a36Sopenharmony_ci			val = hdspm_s1_sync_check(hdspm,
402362306a36Sopenharmony_ci					kcontrol->private_value-1);
402462306a36Sopenharmony_ci		}
402562306a36Sopenharmony_ci		break;
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_ci	case AIO:
402862306a36Sopenharmony_ci		switch (kcontrol->private_value) {
402962306a36Sopenharmony_ci		case 0: /* WC */
403062306a36Sopenharmony_ci			val = hdspm_wc_sync_check(hdspm); break;
403162306a36Sopenharmony_ci		case 4: /* TCO */
403262306a36Sopenharmony_ci			val = hdspm_tco_sync_check(hdspm); break;
403362306a36Sopenharmony_ci		case 5: /* SYNC IN */
403462306a36Sopenharmony_ci			val = hdspm_sync_in_sync_check(hdspm); break;
403562306a36Sopenharmony_ci		default:
403662306a36Sopenharmony_ci			val = hdspm_s1_sync_check(hdspm,
403762306a36Sopenharmony_ci					kcontrol->private_value-1);
403862306a36Sopenharmony_ci		}
403962306a36Sopenharmony_ci		break;
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	case MADI:
404262306a36Sopenharmony_ci		switch (kcontrol->private_value) {
404362306a36Sopenharmony_ci		case 0: /* WC */
404462306a36Sopenharmony_ci			val = hdspm_wc_sync_check(hdspm); break;
404562306a36Sopenharmony_ci		case 1: /* MADI */
404662306a36Sopenharmony_ci			val = hdspm_madi_sync_check(hdspm); break;
404762306a36Sopenharmony_ci		case 2: /* TCO */
404862306a36Sopenharmony_ci			val = hdspm_tco_sync_check(hdspm); break;
404962306a36Sopenharmony_ci		case 3: /* SYNC_IN */
405062306a36Sopenharmony_ci			val = hdspm_sync_in_sync_check(hdspm); break;
405162306a36Sopenharmony_ci		}
405262306a36Sopenharmony_ci		break;
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_ci	case MADIface:
405562306a36Sopenharmony_ci		val = hdspm_madi_sync_check(hdspm); /* MADI */
405662306a36Sopenharmony_ci		break;
405762306a36Sopenharmony_ci
405862306a36Sopenharmony_ci	case AES32:
405962306a36Sopenharmony_ci		switch (kcontrol->private_value) {
406062306a36Sopenharmony_ci		case 0: /* WC */
406162306a36Sopenharmony_ci			val = hdspm_wc_sync_check(hdspm); break;
406262306a36Sopenharmony_ci		case 9: /* TCO */
406362306a36Sopenharmony_ci			val = hdspm_tco_sync_check(hdspm); break;
406462306a36Sopenharmony_ci		case 10 /* SYNC IN */:
406562306a36Sopenharmony_ci			val = hdspm_sync_in_sync_check(hdspm); break;
406662306a36Sopenharmony_ci		default: /* AES1 to AES8 */
406762306a36Sopenharmony_ci			 val = hdspm_aes_sync_check(hdspm,
406862306a36Sopenharmony_ci					 kcontrol->private_value-1);
406962306a36Sopenharmony_ci		}
407062306a36Sopenharmony_ci		break;
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci	}
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	if (hdspm->tco) {
407562306a36Sopenharmony_ci		switch (kcontrol->private_value) {
407662306a36Sopenharmony_ci		case 11:
407762306a36Sopenharmony_ci			/* Check TCO for lock state of its current input */
407862306a36Sopenharmony_ci			val = hdspm_tco_input_check(hdspm, HDSPM_TCO1_TCO_lock);
407962306a36Sopenharmony_ci			break;
408062306a36Sopenharmony_ci		case 12:
408162306a36Sopenharmony_ci			/* Check TCO for valid time code on LTC input. */
408262306a36Sopenharmony_ci			val = hdspm_tco_input_check(hdspm,
408362306a36Sopenharmony_ci				HDSPM_TCO1_LTC_Input_valid);
408462306a36Sopenharmony_ci			break;
408562306a36Sopenharmony_ci		default:
408662306a36Sopenharmony_ci			break;
408762306a36Sopenharmony_ci		}
408862306a36Sopenharmony_ci	}
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci	if (-1 == val)
409162306a36Sopenharmony_ci		val = 3;
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = val;
409462306a36Sopenharmony_ci	return 0;
409562306a36Sopenharmony_ci}
409662306a36Sopenharmony_ci
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci/*
410062306a36Sopenharmony_ci * TCO controls
410162306a36Sopenharmony_ci */
410262306a36Sopenharmony_cistatic void hdspm_tco_write(struct hdspm *hdspm)
410362306a36Sopenharmony_ci{
410462306a36Sopenharmony_ci	unsigned int tc[4] = { 0, 0, 0, 0};
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_ci	switch (hdspm->tco->input) {
410762306a36Sopenharmony_ci	case 0:
410862306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_input_MSB;
410962306a36Sopenharmony_ci		break;
411062306a36Sopenharmony_ci	case 1:
411162306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_input_LSB;
411262306a36Sopenharmony_ci		break;
411362306a36Sopenharmony_ci	default:
411462306a36Sopenharmony_ci		break;
411562306a36Sopenharmony_ci	}
411662306a36Sopenharmony_ci
411762306a36Sopenharmony_ci	switch (hdspm->tco->framerate) {
411862306a36Sopenharmony_ci	case 1:
411962306a36Sopenharmony_ci		tc[1] |= HDSPM_TCO1_LTC_Format_LSB;
412062306a36Sopenharmony_ci		break;
412162306a36Sopenharmony_ci	case 2:
412262306a36Sopenharmony_ci		tc[1] |= HDSPM_TCO1_LTC_Format_MSB;
412362306a36Sopenharmony_ci		break;
412462306a36Sopenharmony_ci	case 3:
412562306a36Sopenharmony_ci		tc[1] |= HDSPM_TCO1_LTC_Format_MSB +
412662306a36Sopenharmony_ci			HDSPM_TCO1_set_drop_frame_flag;
412762306a36Sopenharmony_ci		break;
412862306a36Sopenharmony_ci	case 4:
412962306a36Sopenharmony_ci		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
413062306a36Sopenharmony_ci			HDSPM_TCO1_LTC_Format_MSB;
413162306a36Sopenharmony_ci		break;
413262306a36Sopenharmony_ci	case 5:
413362306a36Sopenharmony_ci		tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
413462306a36Sopenharmony_ci			HDSPM_TCO1_LTC_Format_MSB +
413562306a36Sopenharmony_ci			HDSPM_TCO1_set_drop_frame_flag;
413662306a36Sopenharmony_ci		break;
413762306a36Sopenharmony_ci	default:
413862306a36Sopenharmony_ci		break;
413962306a36Sopenharmony_ci	}
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci	switch (hdspm->tco->wordclock) {
414262306a36Sopenharmony_ci	case 1:
414362306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB;
414462306a36Sopenharmony_ci		break;
414562306a36Sopenharmony_ci	case 2:
414662306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB;
414762306a36Sopenharmony_ci		break;
414862306a36Sopenharmony_ci	default:
414962306a36Sopenharmony_ci		break;
415062306a36Sopenharmony_ci	}
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci	switch (hdspm->tco->samplerate) {
415362306a36Sopenharmony_ci	case 1:
415462306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_freq;
415562306a36Sopenharmony_ci		break;
415662306a36Sopenharmony_ci	case 2:
415762306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_freq_from_app;
415862306a36Sopenharmony_ci		break;
415962306a36Sopenharmony_ci	default:
416062306a36Sopenharmony_ci		break;
416162306a36Sopenharmony_ci	}
416262306a36Sopenharmony_ci
416362306a36Sopenharmony_ci	switch (hdspm->tco->pull) {
416462306a36Sopenharmony_ci	case 1:
416562306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_pull_up;
416662306a36Sopenharmony_ci		break;
416762306a36Sopenharmony_ci	case 2:
416862306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_pull_down;
416962306a36Sopenharmony_ci		break;
417062306a36Sopenharmony_ci	case 3:
417162306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4;
417262306a36Sopenharmony_ci		break;
417362306a36Sopenharmony_ci	case 4:
417462306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4;
417562306a36Sopenharmony_ci		break;
417662306a36Sopenharmony_ci	default:
417762306a36Sopenharmony_ci		break;
417862306a36Sopenharmony_ci	}
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci	if (1 == hdspm->tco->term) {
418162306a36Sopenharmony_ci		tc[2] |= HDSPM_TCO2_set_term_75R;
418262306a36Sopenharmony_ci	}
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]);
418562306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]);
418662306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]);
418762306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]);
418862306a36Sopenharmony_ci}
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci
419162306a36Sopenharmony_ci#define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \
419262306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
419362306a36Sopenharmony_ci	.name = xname, \
419462306a36Sopenharmony_ci	.index = xindex, \
419562306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
419662306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
419762306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_sample_rate, \
419862306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_sample_rate, \
419962306a36Sopenharmony_ci	.put = snd_hdspm_put_tco_sample_rate \
420062306a36Sopenharmony_ci}
420162306a36Sopenharmony_ci
420262306a36Sopenharmony_cistatic int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
420362306a36Sopenharmony_ci					  struct snd_ctl_elem_info *uinfo)
420462306a36Sopenharmony_ci{
420562306a36Sopenharmony_ci	/* TODO freq from app could be supported here, see tco->samplerate */
420662306a36Sopenharmony_ci	static const char *const texts[] = { "44.1 kHz", "48 kHz" };
420762306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
420862306a36Sopenharmony_ci	return 0;
420962306a36Sopenharmony_ci}
421062306a36Sopenharmony_ci
421162306a36Sopenharmony_cistatic int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol,
421262306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
421362306a36Sopenharmony_ci{
421462306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate;
421762306a36Sopenharmony_ci
421862306a36Sopenharmony_ci	return 0;
421962306a36Sopenharmony_ci}
422062306a36Sopenharmony_ci
422162306a36Sopenharmony_cistatic int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
422262306a36Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
422362306a36Sopenharmony_ci{
422462306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) {
422762306a36Sopenharmony_ci		hdspm->tco->samplerate = ucontrol->value.enumerated.item[0];
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci		hdspm_tco_write(hdspm);
423062306a36Sopenharmony_ci
423162306a36Sopenharmony_ci		return 1;
423262306a36Sopenharmony_ci	}
423362306a36Sopenharmony_ci
423462306a36Sopenharmony_ci	return 0;
423562306a36Sopenharmony_ci}
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_ci#define HDSPM_TCO_PULL(xname, xindex) \
423962306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
424062306a36Sopenharmony_ci	.name = xname, \
424162306a36Sopenharmony_ci	.index = xindex, \
424262306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
424362306a36Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
424462306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_pull, \
424562306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_pull, \
424662306a36Sopenharmony_ci	.put = snd_hdspm_put_tco_pull \
424762306a36Sopenharmony_ci}
424862306a36Sopenharmony_ci
424962306a36Sopenharmony_cistatic int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
425062306a36Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
425162306a36Sopenharmony_ci{
425262306a36Sopenharmony_ci	static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %",
425362306a36Sopenharmony_ci		"+ 4 %", "- 4 %" };
425462306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
425562306a36Sopenharmony_ci	return 0;
425662306a36Sopenharmony_ci}
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_cistatic int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol,
425962306a36Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
426062306a36Sopenharmony_ci{
426162306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
426262306a36Sopenharmony_ci
426362306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm->tco->pull;
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_ci	return 0;
426662306a36Sopenharmony_ci}
426762306a36Sopenharmony_ci
426862306a36Sopenharmony_cistatic int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
426962306a36Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
427062306a36Sopenharmony_ci{
427162306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
427262306a36Sopenharmony_ci
427362306a36Sopenharmony_ci	if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) {
427462306a36Sopenharmony_ci		hdspm->tco->pull = ucontrol->value.enumerated.item[0];
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci		hdspm_tco_write(hdspm);
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_ci		return 1;
427962306a36Sopenharmony_ci	}
428062306a36Sopenharmony_ci
428162306a36Sopenharmony_ci	return 0;
428262306a36Sopenharmony_ci}
428362306a36Sopenharmony_ci
428462306a36Sopenharmony_ci#define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \
428562306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
428662306a36Sopenharmony_ci	.name = xname, \
428762306a36Sopenharmony_ci	.index = xindex, \
428862306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
428962306a36Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
429062306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_wck_conversion, \
429162306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_wck_conversion, \
429262306a36Sopenharmony_ci	.put = snd_hdspm_put_tco_wck_conversion \
429362306a36Sopenharmony_ci}
429462306a36Sopenharmony_ci
429562306a36Sopenharmony_cistatic int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
429662306a36Sopenharmony_ci					     struct snd_ctl_elem_info *uinfo)
429762306a36Sopenharmony_ci{
429862306a36Sopenharmony_ci	static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
429962306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
430062306a36Sopenharmony_ci	return 0;
430162306a36Sopenharmony_ci}
430262306a36Sopenharmony_ci
430362306a36Sopenharmony_cistatic int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol,
430462306a36Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
430562306a36Sopenharmony_ci{
430662306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock;
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_ci	return 0;
431162306a36Sopenharmony_ci}
431262306a36Sopenharmony_ci
431362306a36Sopenharmony_cistatic int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
431462306a36Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
431562306a36Sopenharmony_ci{
431662306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) {
431962306a36Sopenharmony_ci		hdspm->tco->wordclock = ucontrol->value.enumerated.item[0];
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_ci		hdspm_tco_write(hdspm);
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ci		return 1;
432462306a36Sopenharmony_ci	}
432562306a36Sopenharmony_ci
432662306a36Sopenharmony_ci	return 0;
432762306a36Sopenharmony_ci}
432862306a36Sopenharmony_ci
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci#define HDSPM_TCO_FRAME_RATE(xname, xindex) \
433162306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
433262306a36Sopenharmony_ci	.name = xname, \
433362306a36Sopenharmony_ci	.index = xindex, \
433462306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
433562306a36Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
433662306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_frame_rate, \
433762306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_frame_rate, \
433862306a36Sopenharmony_ci	.put = snd_hdspm_put_tco_frame_rate \
433962306a36Sopenharmony_ci}
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_cistatic int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
434262306a36Sopenharmony_ci					  struct snd_ctl_elem_info *uinfo)
434362306a36Sopenharmony_ci{
434462306a36Sopenharmony_ci	static const char *const texts[] = { "24 fps", "25 fps", "29.97fps",
434562306a36Sopenharmony_ci		"29.97 dfps", "30 fps", "30 dfps" };
434662306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
434762306a36Sopenharmony_ci	return 0;
434862306a36Sopenharmony_ci}
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_cistatic int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol,
435162306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
435262306a36Sopenharmony_ci{
435362306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
435462306a36Sopenharmony_ci
435562306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm->tco->framerate;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	return 0;
435862306a36Sopenharmony_ci}
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_cistatic int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
436162306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
436262306a36Sopenharmony_ci{
436362306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
436462306a36Sopenharmony_ci
436562306a36Sopenharmony_ci	if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) {
436662306a36Sopenharmony_ci		hdspm->tco->framerate = ucontrol->value.enumerated.item[0];
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_ci		hdspm_tco_write(hdspm);
436962306a36Sopenharmony_ci
437062306a36Sopenharmony_ci		return 1;
437162306a36Sopenharmony_ci	}
437262306a36Sopenharmony_ci
437362306a36Sopenharmony_ci	return 0;
437462306a36Sopenharmony_ci}
437562306a36Sopenharmony_ci
437662306a36Sopenharmony_ci
437762306a36Sopenharmony_ci#define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \
437862306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
437962306a36Sopenharmony_ci	.name = xname, \
438062306a36Sopenharmony_ci	.index = xindex, \
438162306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
438262306a36Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
438362306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_sync_source, \
438462306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_sync_source, \
438562306a36Sopenharmony_ci	.put = snd_hdspm_put_tco_sync_source \
438662306a36Sopenharmony_ci}
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_cistatic int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
438962306a36Sopenharmony_ci					  struct snd_ctl_elem_info *uinfo)
439062306a36Sopenharmony_ci{
439162306a36Sopenharmony_ci	static const char *const texts[] = { "LTC", "Video", "WCK" };
439262306a36Sopenharmony_ci	ENUMERATED_CTL_INFO(uinfo, texts);
439362306a36Sopenharmony_ci	return 0;
439462306a36Sopenharmony_ci}
439562306a36Sopenharmony_ci
439662306a36Sopenharmony_cistatic int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol,
439762306a36Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
439862306a36Sopenharmony_ci{
439962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
440062306a36Sopenharmony_ci
440162306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = hdspm->tco->input;
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci	return 0;
440462306a36Sopenharmony_ci}
440562306a36Sopenharmony_ci
440662306a36Sopenharmony_cistatic int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol,
440762306a36Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
440862306a36Sopenharmony_ci{
440962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci	if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) {
441262306a36Sopenharmony_ci		hdspm->tco->input = ucontrol->value.enumerated.item[0];
441362306a36Sopenharmony_ci
441462306a36Sopenharmony_ci		hdspm_tco_write(hdspm);
441562306a36Sopenharmony_ci
441662306a36Sopenharmony_ci		return 1;
441762306a36Sopenharmony_ci	}
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci	return 0;
442062306a36Sopenharmony_ci}
442162306a36Sopenharmony_ci
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci#define HDSPM_TCO_WORD_TERM(xname, xindex) \
442462306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
442562306a36Sopenharmony_ci	.name = xname, \
442662306a36Sopenharmony_ci	.index = xindex, \
442762306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
442862306a36Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
442962306a36Sopenharmony_ci	.info = snd_hdspm_info_tco_word_term, \
443062306a36Sopenharmony_ci	.get = snd_hdspm_get_tco_word_term, \
443162306a36Sopenharmony_ci	.put = snd_hdspm_put_tco_word_term \
443262306a36Sopenharmony_ci}
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_cistatic int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol,
443562306a36Sopenharmony_ci					struct snd_ctl_elem_info *uinfo)
443662306a36Sopenharmony_ci{
443762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
443862306a36Sopenharmony_ci	uinfo->count = 1;
443962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
444062306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
444162306a36Sopenharmony_ci
444262306a36Sopenharmony_ci	return 0;
444362306a36Sopenharmony_ci}
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_ci
444662306a36Sopenharmony_cistatic int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
444762306a36Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
444862306a36Sopenharmony_ci{
444962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
445062306a36Sopenharmony_ci
445162306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = hdspm->tco->term;
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci	return 0;
445462306a36Sopenharmony_ci}
445562306a36Sopenharmony_ci
445662306a36Sopenharmony_ci
445762306a36Sopenharmony_cistatic int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
445862306a36Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
445962306a36Sopenharmony_ci{
446062306a36Sopenharmony_ci	struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
446162306a36Sopenharmony_ci
446262306a36Sopenharmony_ci	if (hdspm->tco->term != ucontrol->value.integer.value[0]) {
446362306a36Sopenharmony_ci		hdspm->tco->term = ucontrol->value.integer.value[0];
446462306a36Sopenharmony_ci
446562306a36Sopenharmony_ci		hdspm_tco_write(hdspm);
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ci		return 1;
446862306a36Sopenharmony_ci	}
446962306a36Sopenharmony_ci
447062306a36Sopenharmony_ci	return 0;
447162306a36Sopenharmony_ci}
447262306a36Sopenharmony_ci
447362306a36Sopenharmony_ci
447462306a36Sopenharmony_ci
447562306a36Sopenharmony_ci
447662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
447762306a36Sopenharmony_ci	HDSPM_MIXER("Mixer", 0),
447862306a36Sopenharmony_ci	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
447962306a36Sopenharmony_ci	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
448062306a36Sopenharmony_ci	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
448162306a36Sopenharmony_ci	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
448262306a36Sopenharmony_ci	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
448362306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
448462306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
448562306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("MADI SyncCheck", 1),
448662306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("TCO SyncCheck", 2),
448762306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
448862306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
448962306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
449062306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Disable 96K frames", HDSPM_SMUX),
449162306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
449262306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
449362306a36Sopenharmony_ci	HDSPM_INPUT_SELECT("Input Select", 0),
449462306a36Sopenharmony_ci	HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
449562306a36Sopenharmony_ci};
449662306a36Sopenharmony_ci
449762306a36Sopenharmony_ci
449862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
449962306a36Sopenharmony_ci	HDSPM_MIXER("Mixer", 0),
450062306a36Sopenharmony_ci	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
450162306a36Sopenharmony_ci	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
450262306a36Sopenharmony_ci	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
450362306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
450462306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
450562306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
450662306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
450762306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
450862306a36Sopenharmony_ci	HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
450962306a36Sopenharmony_ci};
451062306a36Sopenharmony_ci
451162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
451262306a36Sopenharmony_ci	HDSPM_MIXER("Mixer", 0),
451362306a36Sopenharmony_ci	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
451462306a36Sopenharmony_ci	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
451562306a36Sopenharmony_ci	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
451662306a36Sopenharmony_ci	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
451762306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
451862306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
451962306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
452062306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
452162306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("ADAT SyncCheck", 3),
452262306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("TCO SyncCheck", 4),
452362306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5),
452462306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
452562306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
452662306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
452762306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
452862306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
452962306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5),
453062306a36Sopenharmony_ci	HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0),
453162306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt),
453262306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
453362306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1),
453462306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db),
453562306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48),
453662306a36Sopenharmony_ci	HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0),
453762306a36Sopenharmony_ci	HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0),
453862306a36Sopenharmony_ci	HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0)
453962306a36Sopenharmony_ci
454062306a36Sopenharmony_ci		/*
454162306a36Sopenharmony_ci		   HDSPM_INPUT_SELECT("Input Select", 0),
454262306a36Sopenharmony_ci		   HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0),
454362306a36Sopenharmony_ci		   HDSPM_PROFESSIONAL("SPDIF Out Professional", 0);
454462306a36Sopenharmony_ci		   HDSPM_SPDIF_IN("SPDIF In", 0);
454562306a36Sopenharmony_ci		   HDSPM_BREAKOUT_CABLE("Breakout Cable", 0);
454662306a36Sopenharmony_ci		   HDSPM_INPUT_LEVEL("Input Level", 0);
454762306a36Sopenharmony_ci		   HDSPM_OUTPUT_LEVEL("Output Level", 0);
454862306a36Sopenharmony_ci		   HDSPM_PHONES("Phones", 0);
454962306a36Sopenharmony_ci		   */
455062306a36Sopenharmony_ci};
455162306a36Sopenharmony_ci
455262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
455362306a36Sopenharmony_ci	HDSPM_MIXER("Mixer", 0),
455462306a36Sopenharmony_ci	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
455562306a36Sopenharmony_ci	HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0),
455662306a36Sopenharmony_ci	HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0),
455762306a36Sopenharmony_ci	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
455862306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("WC SyncCheck", 0),
455962306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES SyncCheck", 1),
456062306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
456162306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3),
456262306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4),
456362306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5),
456462306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6),
456562306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("TCO SyncCheck", 7),
456662306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8),
456762306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
456862306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
456962306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
457062306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3),
457162306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4),
457262306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
457362306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
457462306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
457562306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8),
457662306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
457762306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48)
457862306a36Sopenharmony_ci};
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
458162306a36Sopenharmony_ci	HDSPM_MIXER("Mixer", 0),
458262306a36Sopenharmony_ci	HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
458362306a36Sopenharmony_ci	HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
458462306a36Sopenharmony_ci	HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
458562306a36Sopenharmony_ci	HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
458662306a36Sopenharmony_ci	HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
458762306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11),
458862306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("WC Sync Check", 0),
458962306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
459062306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
459162306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES3 Sync Check", 3),
459262306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES4 Sync Check", 4),
459362306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES5 Sync Check", 5),
459462306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES6 Sync Check", 6),
459562306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES7 Sync Check", 7),
459662306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("AES8 Sync Check", 8),
459762306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("TCO Sync Check", 9),
459862306a36Sopenharmony_ci	HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10),
459962306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
460062306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1),
460162306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2),
460262306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3),
460362306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4),
460462306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5),
460562306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6),
460662306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7),
460762306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8),
460862306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9),
460962306a36Sopenharmony_ci	HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10),
461062306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
461162306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Emphasis", HDSPM_Emphasis),
461262306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Non Audio", HDSPM_Dolby),
461362306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Professional", HDSPM_Professional),
461462306a36Sopenharmony_ci	HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
461562306a36Sopenharmony_ci	HDSPM_DS_WIRE("Double Speed Wire Mode", 0),
461662306a36Sopenharmony_ci	HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
461762306a36Sopenharmony_ci};
461862306a36Sopenharmony_ci
461962306a36Sopenharmony_ci
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_ci/* Control elements for the optional TCO module */
462262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
462362306a36Sopenharmony_ci	HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0),
462462306a36Sopenharmony_ci	HDSPM_TCO_PULL("TCO Pull", 0),
462562306a36Sopenharmony_ci	HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
462662306a36Sopenharmony_ci	HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
462762306a36Sopenharmony_ci	HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
462862306a36Sopenharmony_ci	HDSPM_TCO_WORD_TERM("TCO Word Term", 0),
462962306a36Sopenharmony_ci	HDSPM_TCO_LOCK_CHECK("TCO Input Check", 11),
463062306a36Sopenharmony_ci	HDSPM_TCO_LOCK_CHECK("TCO LTC Valid", 12),
463162306a36Sopenharmony_ci	HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate", 0),
463262306a36Sopenharmony_ci	HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format", 0)
463362306a36Sopenharmony_ci};
463462306a36Sopenharmony_ci
463562306a36Sopenharmony_ci
463662306a36Sopenharmony_cistatic struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci
463962306a36Sopenharmony_cistatic int hdspm_update_simple_mixer_controls(struct hdspm * hdspm)
464062306a36Sopenharmony_ci{
464162306a36Sopenharmony_ci	int i;
464262306a36Sopenharmony_ci
464362306a36Sopenharmony_ci	for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) {
464462306a36Sopenharmony_ci		if (hdspm->system_sample_rate > 48000) {
464562306a36Sopenharmony_ci			hdspm->playback_mixer_ctls[i]->vd[0].access =
464662306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_INACTIVE |
464762306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_READ |
464862306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
464962306a36Sopenharmony_ci		} else {
465062306a36Sopenharmony_ci			hdspm->playback_mixer_ctls[i]->vd[0].access =
465162306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_READWRITE |
465262306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_VOLATILE;
465362306a36Sopenharmony_ci		}
465462306a36Sopenharmony_ci		snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
465562306a36Sopenharmony_ci				SNDRV_CTL_EVENT_MASK_INFO,
465662306a36Sopenharmony_ci				&hdspm->playback_mixer_ctls[i]->id);
465762306a36Sopenharmony_ci	}
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci	return 0;
466062306a36Sopenharmony_ci}
466162306a36Sopenharmony_ci
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_cistatic int snd_hdspm_create_controls(struct snd_card *card,
466462306a36Sopenharmony_ci					struct hdspm *hdspm)
466562306a36Sopenharmony_ci{
466662306a36Sopenharmony_ci	unsigned int idx, limit;
466762306a36Sopenharmony_ci	int err;
466862306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
466962306a36Sopenharmony_ci	const struct snd_kcontrol_new *list = NULL;
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	switch (hdspm->io_type) {
467262306a36Sopenharmony_ci	case MADI:
467362306a36Sopenharmony_ci		list = snd_hdspm_controls_madi;
467462306a36Sopenharmony_ci		limit = ARRAY_SIZE(snd_hdspm_controls_madi);
467562306a36Sopenharmony_ci		break;
467662306a36Sopenharmony_ci	case MADIface:
467762306a36Sopenharmony_ci		list = snd_hdspm_controls_madiface;
467862306a36Sopenharmony_ci		limit = ARRAY_SIZE(snd_hdspm_controls_madiface);
467962306a36Sopenharmony_ci		break;
468062306a36Sopenharmony_ci	case AIO:
468162306a36Sopenharmony_ci		list = snd_hdspm_controls_aio;
468262306a36Sopenharmony_ci		limit = ARRAY_SIZE(snd_hdspm_controls_aio);
468362306a36Sopenharmony_ci		break;
468462306a36Sopenharmony_ci	case RayDAT:
468562306a36Sopenharmony_ci		list = snd_hdspm_controls_raydat;
468662306a36Sopenharmony_ci		limit = ARRAY_SIZE(snd_hdspm_controls_raydat);
468762306a36Sopenharmony_ci		break;
468862306a36Sopenharmony_ci	case AES32:
468962306a36Sopenharmony_ci		list = snd_hdspm_controls_aes32;
469062306a36Sopenharmony_ci		limit = ARRAY_SIZE(snd_hdspm_controls_aes32);
469162306a36Sopenharmony_ci		break;
469262306a36Sopenharmony_ci	}
469362306a36Sopenharmony_ci
469462306a36Sopenharmony_ci	if (list) {
469562306a36Sopenharmony_ci		for (idx = 0; idx < limit; idx++) {
469662306a36Sopenharmony_ci			err = snd_ctl_add(card,
469762306a36Sopenharmony_ci					snd_ctl_new1(&list[idx], hdspm));
469862306a36Sopenharmony_ci			if (err < 0)
469962306a36Sopenharmony_ci				return err;
470062306a36Sopenharmony_ci		}
470162306a36Sopenharmony_ci	}
470262306a36Sopenharmony_ci
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci	/* create simple 1:1 playback mixer controls */
470562306a36Sopenharmony_ci	snd_hdspm_playback_mixer.name = "Chn";
470662306a36Sopenharmony_ci	if (hdspm->system_sample_rate >= 128000) {
470762306a36Sopenharmony_ci		limit = hdspm->qs_out_channels;
470862306a36Sopenharmony_ci	} else if (hdspm->system_sample_rate >= 64000) {
470962306a36Sopenharmony_ci		limit = hdspm->ds_out_channels;
471062306a36Sopenharmony_ci	} else {
471162306a36Sopenharmony_ci		limit = hdspm->ss_out_channels;
471262306a36Sopenharmony_ci	}
471362306a36Sopenharmony_ci	for (idx = 0; idx < limit; ++idx) {
471462306a36Sopenharmony_ci		snd_hdspm_playback_mixer.index = idx + 1;
471562306a36Sopenharmony_ci		kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm);
471662306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl);
471762306a36Sopenharmony_ci		if (err < 0)
471862306a36Sopenharmony_ci			return err;
471962306a36Sopenharmony_ci		hdspm->playback_mixer_ctls[idx] = kctl;
472062306a36Sopenharmony_ci	}
472162306a36Sopenharmony_ci
472262306a36Sopenharmony_ci
472362306a36Sopenharmony_ci	if (hdspm->tco) {
472462306a36Sopenharmony_ci		/* add tco control elements */
472562306a36Sopenharmony_ci		list = snd_hdspm_controls_tco;
472662306a36Sopenharmony_ci		limit = ARRAY_SIZE(snd_hdspm_controls_tco);
472762306a36Sopenharmony_ci		for (idx = 0; idx < limit; idx++) {
472862306a36Sopenharmony_ci			err = snd_ctl_add(card,
472962306a36Sopenharmony_ci					snd_ctl_new1(&list[idx], hdspm));
473062306a36Sopenharmony_ci			if (err < 0)
473162306a36Sopenharmony_ci				return err;
473262306a36Sopenharmony_ci		}
473362306a36Sopenharmony_ci	}
473462306a36Sopenharmony_ci
473562306a36Sopenharmony_ci	return 0;
473662306a36Sopenharmony_ci}
473762306a36Sopenharmony_ci
473862306a36Sopenharmony_ci/*------------------------------------------------------------
473962306a36Sopenharmony_ci   /proc interface
474062306a36Sopenharmony_ci ------------------------------------------------------------*/
474162306a36Sopenharmony_ci
474262306a36Sopenharmony_cistatic void
474362306a36Sopenharmony_cisnd_hdspm_proc_read_tco(struct snd_info_entry *entry,
474462306a36Sopenharmony_ci					struct snd_info_buffer *buffer)
474562306a36Sopenharmony_ci{
474662306a36Sopenharmony_ci	struct hdspm *hdspm = entry->private_data;
474762306a36Sopenharmony_ci	unsigned int status, control;
474862306a36Sopenharmony_ci	int a, ltc, frames, seconds, minutes, hours;
474962306a36Sopenharmony_ci	unsigned int period;
475062306a36Sopenharmony_ci	u64 freq_const = 0;
475162306a36Sopenharmony_ci	u32 rate;
475262306a36Sopenharmony_ci
475362306a36Sopenharmony_ci	snd_iprintf(buffer, "--- TCO ---\n");
475462306a36Sopenharmony_ci
475562306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_statusRegister);
475662306a36Sopenharmony_ci	control = hdspm->control_register;
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci
475962306a36Sopenharmony_ci	if (status & HDSPM_tco_detect) {
476062306a36Sopenharmony_ci		snd_iprintf(buffer, "TCO module detected.\n");
476162306a36Sopenharmony_ci		a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
476262306a36Sopenharmony_ci		if (a & HDSPM_TCO1_LTC_Input_valid) {
476362306a36Sopenharmony_ci			snd_iprintf(buffer, "  LTC valid, ");
476462306a36Sopenharmony_ci			switch (a & (HDSPM_TCO1_LTC_Format_LSB |
476562306a36Sopenharmony_ci						HDSPM_TCO1_LTC_Format_MSB)) {
476662306a36Sopenharmony_ci			case 0:
476762306a36Sopenharmony_ci				snd_iprintf(buffer, "24 fps, ");
476862306a36Sopenharmony_ci				break;
476962306a36Sopenharmony_ci			case HDSPM_TCO1_LTC_Format_LSB:
477062306a36Sopenharmony_ci				snd_iprintf(buffer, "25 fps, ");
477162306a36Sopenharmony_ci				break;
477262306a36Sopenharmony_ci			case HDSPM_TCO1_LTC_Format_MSB:
477362306a36Sopenharmony_ci				snd_iprintf(buffer, "29.97 fps, ");
477462306a36Sopenharmony_ci				break;
477562306a36Sopenharmony_ci			default:
477662306a36Sopenharmony_ci				snd_iprintf(buffer, "30 fps, ");
477762306a36Sopenharmony_ci				break;
477862306a36Sopenharmony_ci			}
477962306a36Sopenharmony_ci			if (a & HDSPM_TCO1_set_drop_frame_flag) {
478062306a36Sopenharmony_ci				snd_iprintf(buffer, "drop frame\n");
478162306a36Sopenharmony_ci			} else {
478262306a36Sopenharmony_ci				snd_iprintf(buffer, "full frame\n");
478362306a36Sopenharmony_ci			}
478462306a36Sopenharmony_ci		} else {
478562306a36Sopenharmony_ci			snd_iprintf(buffer, "  no LTC\n");
478662306a36Sopenharmony_ci		}
478762306a36Sopenharmony_ci		if (a & HDSPM_TCO1_Video_Input_Format_NTSC) {
478862306a36Sopenharmony_ci			snd_iprintf(buffer, "  Video: NTSC\n");
478962306a36Sopenharmony_ci		} else if (a & HDSPM_TCO1_Video_Input_Format_PAL) {
479062306a36Sopenharmony_ci			snd_iprintf(buffer, "  Video: PAL\n");
479162306a36Sopenharmony_ci		} else {
479262306a36Sopenharmony_ci			snd_iprintf(buffer, "  No video\n");
479362306a36Sopenharmony_ci		}
479462306a36Sopenharmony_ci		if (a & HDSPM_TCO1_TCO_lock) {
479562306a36Sopenharmony_ci			snd_iprintf(buffer, "  Sync: lock\n");
479662306a36Sopenharmony_ci		} else {
479762306a36Sopenharmony_ci			snd_iprintf(buffer, "  Sync: no lock\n");
479862306a36Sopenharmony_ci		}
479962306a36Sopenharmony_ci
480062306a36Sopenharmony_ci		switch (hdspm->io_type) {
480162306a36Sopenharmony_ci		case MADI:
480262306a36Sopenharmony_ci		case AES32:
480362306a36Sopenharmony_ci			freq_const = 110069313433624ULL;
480462306a36Sopenharmony_ci			break;
480562306a36Sopenharmony_ci		case RayDAT:
480662306a36Sopenharmony_ci		case AIO:
480762306a36Sopenharmony_ci			freq_const = 104857600000000ULL;
480862306a36Sopenharmony_ci			break;
480962306a36Sopenharmony_ci		case MADIface:
481062306a36Sopenharmony_ci			break; /* no TCO possible */
481162306a36Sopenharmony_ci		}
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci		period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
481462306a36Sopenharmony_ci		snd_iprintf(buffer, "    period: %u\n", period);
481562306a36Sopenharmony_ci
481662306a36Sopenharmony_ci
481762306a36Sopenharmony_ci		/* rate = freq_const/period; */
481862306a36Sopenharmony_ci		rate = div_u64(freq_const, period);
481962306a36Sopenharmony_ci
482062306a36Sopenharmony_ci		if (control & HDSPM_QuadSpeed) {
482162306a36Sopenharmony_ci			rate *= 4;
482262306a36Sopenharmony_ci		} else if (control & HDSPM_DoubleSpeed) {
482362306a36Sopenharmony_ci			rate *= 2;
482462306a36Sopenharmony_ci		}
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci		snd_iprintf(buffer, "  Frequency: %u Hz\n",
482762306a36Sopenharmony_ci				(unsigned int) rate);
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci		ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
483062306a36Sopenharmony_ci		frames = ltc & 0xF;
483162306a36Sopenharmony_ci		ltc >>= 4;
483262306a36Sopenharmony_ci		frames += (ltc & 0x3) * 10;
483362306a36Sopenharmony_ci		ltc >>= 4;
483462306a36Sopenharmony_ci		seconds = ltc & 0xF;
483562306a36Sopenharmony_ci		ltc >>= 4;
483662306a36Sopenharmony_ci		seconds += (ltc & 0x7) * 10;
483762306a36Sopenharmony_ci		ltc >>= 4;
483862306a36Sopenharmony_ci		minutes = ltc & 0xF;
483962306a36Sopenharmony_ci		ltc >>= 4;
484062306a36Sopenharmony_ci		minutes += (ltc & 0x7) * 10;
484162306a36Sopenharmony_ci		ltc >>= 4;
484262306a36Sopenharmony_ci		hours = ltc & 0xF;
484362306a36Sopenharmony_ci		ltc >>= 4;
484462306a36Sopenharmony_ci		hours += (ltc & 0x3) * 10;
484562306a36Sopenharmony_ci		snd_iprintf(buffer,
484662306a36Sopenharmony_ci			"  LTC In: %02d:%02d:%02d:%02d\n",
484762306a36Sopenharmony_ci			hours, minutes, seconds, frames);
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_ci	} else {
485062306a36Sopenharmony_ci		snd_iprintf(buffer, "No TCO module detected.\n");
485162306a36Sopenharmony_ci	}
485262306a36Sopenharmony_ci}
485362306a36Sopenharmony_ci
485462306a36Sopenharmony_cistatic void
485562306a36Sopenharmony_cisnd_hdspm_proc_read_madi(struct snd_info_entry *entry,
485662306a36Sopenharmony_ci			 struct snd_info_buffer *buffer)
485762306a36Sopenharmony_ci{
485862306a36Sopenharmony_ci	struct hdspm *hdspm = entry->private_data;
485962306a36Sopenharmony_ci	unsigned int status, status2;
486062306a36Sopenharmony_ci
486162306a36Sopenharmony_ci	char *pref_sync_ref;
486262306a36Sopenharmony_ci	char *autosync_ref;
486362306a36Sopenharmony_ci	char *system_clock_mode;
486462306a36Sopenharmony_ci	int x, x2;
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_statusRegister);
486762306a36Sopenharmony_ci	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
486862306a36Sopenharmony_ci
486962306a36Sopenharmony_ci	snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
487062306a36Sopenharmony_ci			hdspm->card_name, hdspm->card->number + 1,
487162306a36Sopenharmony_ci			hdspm->firmware_rev,
487262306a36Sopenharmony_ci			(status2 & HDSPM_version0) |
487362306a36Sopenharmony_ci			(status2 & HDSPM_version1) | (status2 &
487462306a36Sopenharmony_ci				HDSPM_version2));
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci	snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
487762306a36Sopenharmony_ci			(hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
487862306a36Sopenharmony_ci			hdspm->serial);
487962306a36Sopenharmony_ci
488062306a36Sopenharmony_ci	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
488162306a36Sopenharmony_ci			hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
488262306a36Sopenharmony_ci
488362306a36Sopenharmony_ci	snd_iprintf(buffer, "--- System ---\n");
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci	snd_iprintf(buffer,
488662306a36Sopenharmony_ci		"IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
488762306a36Sopenharmony_ci		status & HDSPM_audioIRQPending,
488862306a36Sopenharmony_ci		(status & HDSPM_midi0IRQPending) ? 1 : 0,
488962306a36Sopenharmony_ci		(status & HDSPM_midi1IRQPending) ? 1 : 0,
489062306a36Sopenharmony_ci		hdspm->irq_count);
489162306a36Sopenharmony_ci	snd_iprintf(buffer,
489262306a36Sopenharmony_ci		"HW pointer: id = %d, rawptr = %d (%d->%d) "
489362306a36Sopenharmony_ci		"estimated= %ld (bytes)\n",
489462306a36Sopenharmony_ci		((status & HDSPM_BufferID) ? 1 : 0),
489562306a36Sopenharmony_ci		(status & HDSPM_BufferPositionMask),
489662306a36Sopenharmony_ci		(status & HDSPM_BufferPositionMask) %
489762306a36Sopenharmony_ci		(2 * (int)hdspm->period_bytes),
489862306a36Sopenharmony_ci		((status & HDSPM_BufferPositionMask) - 64) %
489962306a36Sopenharmony_ci		(2 * (int)hdspm->period_bytes),
490062306a36Sopenharmony_ci		(long) hdspm_hw_pointer(hdspm) * 4);
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_ci	snd_iprintf(buffer,
490362306a36Sopenharmony_ci		"MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
490462306a36Sopenharmony_ci		hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
490562306a36Sopenharmony_ci		hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
490662306a36Sopenharmony_ci		hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
490762306a36Sopenharmony_ci		hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
490862306a36Sopenharmony_ci	snd_iprintf(buffer,
490962306a36Sopenharmony_ci		"MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
491062306a36Sopenharmony_ci		hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
491162306a36Sopenharmony_ci		hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
491262306a36Sopenharmony_ci	snd_iprintf(buffer,
491362306a36Sopenharmony_ci		"Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
491462306a36Sopenharmony_ci		"status2=0x%x\n",
491562306a36Sopenharmony_ci		hdspm->control_register, hdspm->control2_register,
491662306a36Sopenharmony_ci		status, status2);
491762306a36Sopenharmony_ci
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci	snd_iprintf(buffer, "--- Settings ---\n");
492062306a36Sopenharmony_ci
492162306a36Sopenharmony_ci	x = hdspm_get_latency(hdspm);
492262306a36Sopenharmony_ci
492362306a36Sopenharmony_ci	snd_iprintf(buffer,
492462306a36Sopenharmony_ci		"Size (Latency): %d samples (2 periods of %lu bytes)\n",
492562306a36Sopenharmony_ci		x, (unsigned long) hdspm->period_bytes);
492662306a36Sopenharmony_ci
492762306a36Sopenharmony_ci	snd_iprintf(buffer, "Line out: %s\n",
492862306a36Sopenharmony_ci		(hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
492962306a36Sopenharmony_ci
493062306a36Sopenharmony_ci	snd_iprintf(buffer,
493162306a36Sopenharmony_ci		"ClearTrackMarker = %s, Transmit in %s Channel Mode, "
493262306a36Sopenharmony_ci		"Auto Input %s\n",
493362306a36Sopenharmony_ci		(hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
493462306a36Sopenharmony_ci		(hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
493562306a36Sopenharmony_ci		(hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
493662306a36Sopenharmony_ci
493762306a36Sopenharmony_ci
493862306a36Sopenharmony_ci	if (!(hdspm->control_register & HDSPM_ClockModeMaster))
493962306a36Sopenharmony_ci		system_clock_mode = "AutoSync";
494062306a36Sopenharmony_ci	else
494162306a36Sopenharmony_ci		system_clock_mode = "Master";
494262306a36Sopenharmony_ci	snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode);
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci	switch (hdspm_pref_sync_ref(hdspm)) {
494562306a36Sopenharmony_ci	case HDSPM_SYNC_FROM_WORD:
494662306a36Sopenharmony_ci		pref_sync_ref = "Word Clock";
494762306a36Sopenharmony_ci		break;
494862306a36Sopenharmony_ci	case HDSPM_SYNC_FROM_MADI:
494962306a36Sopenharmony_ci		pref_sync_ref = "MADI Sync";
495062306a36Sopenharmony_ci		break;
495162306a36Sopenharmony_ci	case HDSPM_SYNC_FROM_TCO:
495262306a36Sopenharmony_ci		pref_sync_ref = "TCO";
495362306a36Sopenharmony_ci		break;
495462306a36Sopenharmony_ci	case HDSPM_SYNC_FROM_SYNC_IN:
495562306a36Sopenharmony_ci		pref_sync_ref = "Sync In";
495662306a36Sopenharmony_ci		break;
495762306a36Sopenharmony_ci	default:
495862306a36Sopenharmony_ci		pref_sync_ref = "XXXX Clock";
495962306a36Sopenharmony_ci		break;
496062306a36Sopenharmony_ci	}
496162306a36Sopenharmony_ci	snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
496262306a36Sopenharmony_ci			pref_sync_ref);
496362306a36Sopenharmony_ci
496462306a36Sopenharmony_ci	snd_iprintf(buffer, "System Clock Frequency: %d\n",
496562306a36Sopenharmony_ci			hdspm->system_sample_rate);
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci	snd_iprintf(buffer, "--- Status:\n");
496962306a36Sopenharmony_ci
497062306a36Sopenharmony_ci	x = status & HDSPM_madiSync;
497162306a36Sopenharmony_ci	x2 = status2 & HDSPM_wcSync;
497262306a36Sopenharmony_ci
497362306a36Sopenharmony_ci	snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
497462306a36Sopenharmony_ci			(status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
497562306a36Sopenharmony_ci			"NoLock",
497662306a36Sopenharmony_ci			(status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
497762306a36Sopenharmony_ci			"NoLock");
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	switch (hdspm_autosync_ref(hdspm)) {
498062306a36Sopenharmony_ci	case HDSPM_AUTOSYNC_FROM_SYNC_IN:
498162306a36Sopenharmony_ci		autosync_ref = "Sync In";
498262306a36Sopenharmony_ci		break;
498362306a36Sopenharmony_ci	case HDSPM_AUTOSYNC_FROM_TCO:
498462306a36Sopenharmony_ci		autosync_ref = "TCO";
498562306a36Sopenharmony_ci		break;
498662306a36Sopenharmony_ci	case HDSPM_AUTOSYNC_FROM_WORD:
498762306a36Sopenharmony_ci		autosync_ref = "Word Clock";
498862306a36Sopenharmony_ci		break;
498962306a36Sopenharmony_ci	case HDSPM_AUTOSYNC_FROM_MADI:
499062306a36Sopenharmony_ci		autosync_ref = "MADI Sync";
499162306a36Sopenharmony_ci		break;
499262306a36Sopenharmony_ci	case HDSPM_AUTOSYNC_FROM_NONE:
499362306a36Sopenharmony_ci		autosync_ref = "Input not valid";
499462306a36Sopenharmony_ci		break;
499562306a36Sopenharmony_ci	default:
499662306a36Sopenharmony_ci		autosync_ref = "---";
499762306a36Sopenharmony_ci		break;
499862306a36Sopenharmony_ci	}
499962306a36Sopenharmony_ci	snd_iprintf(buffer,
500062306a36Sopenharmony_ci		"AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
500162306a36Sopenharmony_ci		autosync_ref, hdspm_external_sample_rate(hdspm),
500262306a36Sopenharmony_ci		(status & HDSPM_madiFreqMask) >> 22,
500362306a36Sopenharmony_ci		(status2 & HDSPM_wcFreqMask) >> 5);
500462306a36Sopenharmony_ci
500562306a36Sopenharmony_ci	snd_iprintf(buffer, "Input: %s, Mode=%s\n",
500662306a36Sopenharmony_ci		(status & HDSPM_AB_int) ? "Coax" : "Optical",
500762306a36Sopenharmony_ci		(status & HDSPM_RX_64ch) ? "64 channels" :
500862306a36Sopenharmony_ci		"56 channels");
500962306a36Sopenharmony_ci
501062306a36Sopenharmony_ci	/* call readout function for TCO specific status */
501162306a36Sopenharmony_ci	snd_hdspm_proc_read_tco(entry, buffer);
501262306a36Sopenharmony_ci
501362306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
501462306a36Sopenharmony_ci}
501562306a36Sopenharmony_ci
501662306a36Sopenharmony_cistatic void
501762306a36Sopenharmony_cisnd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
501862306a36Sopenharmony_ci			  struct snd_info_buffer *buffer)
501962306a36Sopenharmony_ci{
502062306a36Sopenharmony_ci	struct hdspm *hdspm = entry->private_data;
502162306a36Sopenharmony_ci	unsigned int status;
502262306a36Sopenharmony_ci	unsigned int status2;
502362306a36Sopenharmony_ci	unsigned int timecode;
502462306a36Sopenharmony_ci	unsigned int wcLock, wcSync;
502562306a36Sopenharmony_ci	int pref_syncref;
502662306a36Sopenharmony_ci	char *autosync_ref;
502762306a36Sopenharmony_ci	int x;
502862306a36Sopenharmony_ci
502962306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_statusRegister);
503062306a36Sopenharmony_ci	status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
503162306a36Sopenharmony_ci	timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
503262306a36Sopenharmony_ci
503362306a36Sopenharmony_ci	snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n",
503462306a36Sopenharmony_ci		    hdspm->card_name, hdspm->card->number + 1,
503562306a36Sopenharmony_ci		    hdspm->firmware_rev);
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
503862306a36Sopenharmony_ci		    hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
503962306a36Sopenharmony_ci
504062306a36Sopenharmony_ci	snd_iprintf(buffer, "--- System ---\n");
504162306a36Sopenharmony_ci
504262306a36Sopenharmony_ci	snd_iprintf(buffer,
504362306a36Sopenharmony_ci		    "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
504462306a36Sopenharmony_ci		    status & HDSPM_audioIRQPending,
504562306a36Sopenharmony_ci		    (status & HDSPM_midi0IRQPending) ? 1 : 0,
504662306a36Sopenharmony_ci		    (status & HDSPM_midi1IRQPending) ? 1 : 0,
504762306a36Sopenharmony_ci		    hdspm->irq_count);
504862306a36Sopenharmony_ci	snd_iprintf(buffer,
504962306a36Sopenharmony_ci		    "HW pointer: id = %d, rawptr = %d (%d->%d) "
505062306a36Sopenharmony_ci		    "estimated= %ld (bytes)\n",
505162306a36Sopenharmony_ci		    ((status & HDSPM_BufferID) ? 1 : 0),
505262306a36Sopenharmony_ci		    (status & HDSPM_BufferPositionMask),
505362306a36Sopenharmony_ci		    (status & HDSPM_BufferPositionMask) %
505462306a36Sopenharmony_ci		    (2 * (int)hdspm->period_bytes),
505562306a36Sopenharmony_ci		    ((status & HDSPM_BufferPositionMask) - 64) %
505662306a36Sopenharmony_ci		    (2 * (int)hdspm->period_bytes),
505762306a36Sopenharmony_ci		    (long) hdspm_hw_pointer(hdspm) * 4);
505862306a36Sopenharmony_ci
505962306a36Sopenharmony_ci	snd_iprintf(buffer,
506062306a36Sopenharmony_ci		    "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
506162306a36Sopenharmony_ci		    hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
506262306a36Sopenharmony_ci		    hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
506362306a36Sopenharmony_ci		    hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
506462306a36Sopenharmony_ci		    hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
506562306a36Sopenharmony_ci	snd_iprintf(buffer,
506662306a36Sopenharmony_ci		    "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
506762306a36Sopenharmony_ci		    hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
506862306a36Sopenharmony_ci		    hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
506962306a36Sopenharmony_ci	snd_iprintf(buffer,
507062306a36Sopenharmony_ci		    "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
507162306a36Sopenharmony_ci		    "status2=0x%x\n",
507262306a36Sopenharmony_ci		    hdspm->control_register, hdspm->control2_register,
507362306a36Sopenharmony_ci		    status, status2);
507462306a36Sopenharmony_ci
507562306a36Sopenharmony_ci	snd_iprintf(buffer, "--- Settings ---\n");
507662306a36Sopenharmony_ci
507762306a36Sopenharmony_ci	x = hdspm_get_latency(hdspm);
507862306a36Sopenharmony_ci
507962306a36Sopenharmony_ci	snd_iprintf(buffer,
508062306a36Sopenharmony_ci		    "Size (Latency): %d samples (2 periods of %lu bytes)\n",
508162306a36Sopenharmony_ci		    x, (unsigned long) hdspm->period_bytes);
508262306a36Sopenharmony_ci
508362306a36Sopenharmony_ci	snd_iprintf(buffer, "Line out: %s\n",
508462306a36Sopenharmony_ci		    (hdspm->
508562306a36Sopenharmony_ci		     control_register & HDSPM_LineOut) ? "on " : "off");
508662306a36Sopenharmony_ci
508762306a36Sopenharmony_ci	snd_iprintf(buffer,
508862306a36Sopenharmony_ci		    "ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
508962306a36Sopenharmony_ci		    (hdspm->
509062306a36Sopenharmony_ci		     control_register & HDSPM_clr_tms) ? "on" : "off",
509162306a36Sopenharmony_ci		    (hdspm->
509262306a36Sopenharmony_ci		     control_register & HDSPM_Emphasis) ? "on" : "off",
509362306a36Sopenharmony_ci		    (hdspm->
509462306a36Sopenharmony_ci		     control_register & HDSPM_Dolby) ? "on" : "off");
509562306a36Sopenharmony_ci
509662306a36Sopenharmony_ci
509762306a36Sopenharmony_ci	pref_syncref = hdspm_pref_sync_ref(hdspm);
509862306a36Sopenharmony_ci	if (pref_syncref == 0)
509962306a36Sopenharmony_ci		snd_iprintf(buffer, "Preferred Sync Reference: Word Clock\n");
510062306a36Sopenharmony_ci	else
510162306a36Sopenharmony_ci		snd_iprintf(buffer, "Preferred Sync Reference: AES%d\n",
510262306a36Sopenharmony_ci				pref_syncref);
510362306a36Sopenharmony_ci
510462306a36Sopenharmony_ci	snd_iprintf(buffer, "System Clock Frequency: %d\n",
510562306a36Sopenharmony_ci		    hdspm->system_sample_rate);
510662306a36Sopenharmony_ci
510762306a36Sopenharmony_ci	snd_iprintf(buffer, "Double speed: %s\n",
510862306a36Sopenharmony_ci			hdspm->control_register & HDSPM_DS_DoubleWire?
510962306a36Sopenharmony_ci			"Double wire" : "Single wire");
511062306a36Sopenharmony_ci	snd_iprintf(buffer, "Quad speed: %s\n",
511162306a36Sopenharmony_ci			hdspm->control_register & HDSPM_QS_DoubleWire?
511262306a36Sopenharmony_ci			"Double wire" :
511362306a36Sopenharmony_ci			hdspm->control_register & HDSPM_QS_QuadWire?
511462306a36Sopenharmony_ci			"Quad wire" : "Single wire");
511562306a36Sopenharmony_ci
511662306a36Sopenharmony_ci	snd_iprintf(buffer, "--- Status:\n");
511762306a36Sopenharmony_ci
511862306a36Sopenharmony_ci	wcLock = status & HDSPM_AES32_wcLock;
511962306a36Sopenharmony_ci	wcSync = wcLock && (status & HDSPM_AES32_wcSync);
512062306a36Sopenharmony_ci
512162306a36Sopenharmony_ci	snd_iprintf(buffer, "Word: %s  Frequency: %d\n",
512262306a36Sopenharmony_ci		    (wcLock) ? (wcSync ? "Sync   " : "Lock   ") : "No Lock",
512362306a36Sopenharmony_ci		    HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
512462306a36Sopenharmony_ci
512562306a36Sopenharmony_ci	for (x = 0; x < 8; x++) {
512662306a36Sopenharmony_ci		snd_iprintf(buffer, "AES%d: %s  Frequency: %d\n",
512762306a36Sopenharmony_ci			    x+1,
512862306a36Sopenharmony_ci			    (status2 & (HDSPM_LockAES >> x)) ?
512962306a36Sopenharmony_ci			    "Sync   " : "No Lock",
513062306a36Sopenharmony_ci			    HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
513162306a36Sopenharmony_ci	}
513262306a36Sopenharmony_ci
513362306a36Sopenharmony_ci	switch (hdspm_autosync_ref(hdspm)) {
513462306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_NONE:
513562306a36Sopenharmony_ci		autosync_ref = "None"; break;
513662306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_WORD:
513762306a36Sopenharmony_ci		autosync_ref = "Word Clock"; break;
513862306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES1:
513962306a36Sopenharmony_ci		autosync_ref = "AES1"; break;
514062306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES2:
514162306a36Sopenharmony_ci		autosync_ref = "AES2"; break;
514262306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES3:
514362306a36Sopenharmony_ci		autosync_ref = "AES3"; break;
514462306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES4:
514562306a36Sopenharmony_ci		autosync_ref = "AES4"; break;
514662306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES5:
514762306a36Sopenharmony_ci		autosync_ref = "AES5"; break;
514862306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES6:
514962306a36Sopenharmony_ci		autosync_ref = "AES6"; break;
515062306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES7:
515162306a36Sopenharmony_ci		autosync_ref = "AES7"; break;
515262306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_AES8:
515362306a36Sopenharmony_ci		autosync_ref = "AES8"; break;
515462306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_TCO:
515562306a36Sopenharmony_ci		autosync_ref = "TCO"; break;
515662306a36Sopenharmony_ci	case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN:
515762306a36Sopenharmony_ci		autosync_ref = "Sync In"; break;
515862306a36Sopenharmony_ci	default:
515962306a36Sopenharmony_ci		autosync_ref = "---"; break;
516062306a36Sopenharmony_ci	}
516162306a36Sopenharmony_ci	snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
516262306a36Sopenharmony_ci
516362306a36Sopenharmony_ci	/* call readout function for TCO specific status */
516462306a36Sopenharmony_ci	snd_hdspm_proc_read_tco(entry, buffer);
516562306a36Sopenharmony_ci
516662306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
516762306a36Sopenharmony_ci}
516862306a36Sopenharmony_ci
516962306a36Sopenharmony_cistatic void
517062306a36Sopenharmony_cisnd_hdspm_proc_read_raydat(struct snd_info_entry *entry,
517162306a36Sopenharmony_ci			 struct snd_info_buffer *buffer)
517262306a36Sopenharmony_ci{
517362306a36Sopenharmony_ci	struct hdspm *hdspm = entry->private_data;
517462306a36Sopenharmony_ci	unsigned int status1, status2, status3, i;
517562306a36Sopenharmony_ci	unsigned int lock, sync;
517662306a36Sopenharmony_ci
517762306a36Sopenharmony_ci	status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */
517862306a36Sopenharmony_ci	status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */
517962306a36Sopenharmony_ci	status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */
518062306a36Sopenharmony_ci
518162306a36Sopenharmony_ci	snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1);
518262306a36Sopenharmony_ci	snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2);
518362306a36Sopenharmony_ci	snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3);
518462306a36Sopenharmony_ci
518562306a36Sopenharmony_ci
518662306a36Sopenharmony_ci	snd_iprintf(buffer, "\n*** CLOCK MODE\n\n");
518762306a36Sopenharmony_ci
518862306a36Sopenharmony_ci	snd_iprintf(buffer, "Clock mode      : %s\n",
518962306a36Sopenharmony_ci		(hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave");
519062306a36Sopenharmony_ci	snd_iprintf(buffer, "System frequency: %d Hz\n",
519162306a36Sopenharmony_ci		hdspm_get_system_sample_rate(hdspm));
519262306a36Sopenharmony_ci
519362306a36Sopenharmony_ci	snd_iprintf(buffer, "\n*** INPUT STATUS\n\n");
519462306a36Sopenharmony_ci
519562306a36Sopenharmony_ci	lock = 0x1;
519662306a36Sopenharmony_ci	sync = 0x100;
519762306a36Sopenharmony_ci
519862306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
519962306a36Sopenharmony_ci		snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n",
520062306a36Sopenharmony_ci				i,
520162306a36Sopenharmony_ci				(status1 & lock) ? 1 : 0,
520262306a36Sopenharmony_ci				(status1 & sync) ? 1 : 0,
520362306a36Sopenharmony_ci				texts_freq[(status2 >> (i * 4)) & 0xF]);
520462306a36Sopenharmony_ci
520562306a36Sopenharmony_ci		lock = lock<<1;
520662306a36Sopenharmony_ci		sync = sync<<1;
520762306a36Sopenharmony_ci	}
520862306a36Sopenharmony_ci
520962306a36Sopenharmony_ci	snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n",
521062306a36Sopenharmony_ci			(status1 & 0x1000000) ? 1 : 0,
521162306a36Sopenharmony_ci			(status1 & 0x2000000) ? 1 : 0,
521262306a36Sopenharmony_ci			texts_freq[(status1 >> 16) & 0xF]);
521362306a36Sopenharmony_ci
521462306a36Sopenharmony_ci	snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n",
521562306a36Sopenharmony_ci			(status1 & 0x4000000) ? 1 : 0,
521662306a36Sopenharmony_ci			(status1 & 0x8000000) ? 1 : 0,
521762306a36Sopenharmony_ci			texts_freq[(status1 >> 20) & 0xF]);
521862306a36Sopenharmony_ci
521962306a36Sopenharmony_ci	snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n",
522062306a36Sopenharmony_ci			(status3 & 0x400) ? 1 : 0,
522162306a36Sopenharmony_ci			(status3 & 0x800) ? 1 : 0,
522262306a36Sopenharmony_ci			texts_freq[(status2 >> 12) & 0xF]);
522362306a36Sopenharmony_ci
522462306a36Sopenharmony_ci}
522562306a36Sopenharmony_ci
522662306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
522762306a36Sopenharmony_cistatic void
522862306a36Sopenharmony_cisnd_hdspm_proc_read_debug(struct snd_info_entry *entry,
522962306a36Sopenharmony_ci			  struct snd_info_buffer *buffer)
523062306a36Sopenharmony_ci{
523162306a36Sopenharmony_ci	struct hdspm *hdspm = entry->private_data;
523262306a36Sopenharmony_ci
523362306a36Sopenharmony_ci	int j,i;
523462306a36Sopenharmony_ci
523562306a36Sopenharmony_ci	for (i = 0; i < 256 /* 1024*64 */; i += j) {
523662306a36Sopenharmony_ci		snd_iprintf(buffer, "0x%08X: ", i);
523762306a36Sopenharmony_ci		for (j = 0; j < 16; j += 4)
523862306a36Sopenharmony_ci			snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j));
523962306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
524062306a36Sopenharmony_ci	}
524162306a36Sopenharmony_ci}
524262306a36Sopenharmony_ci#endif
524362306a36Sopenharmony_ci
524462306a36Sopenharmony_ci
524562306a36Sopenharmony_cistatic void snd_hdspm_proc_ports_in(struct snd_info_entry *entry,
524662306a36Sopenharmony_ci			  struct snd_info_buffer *buffer)
524762306a36Sopenharmony_ci{
524862306a36Sopenharmony_ci	struct hdspm *hdspm = entry->private_data;
524962306a36Sopenharmony_ci	int i;
525062306a36Sopenharmony_ci
525162306a36Sopenharmony_ci	snd_iprintf(buffer, "# generated by hdspm\n");
525262306a36Sopenharmony_ci
525362306a36Sopenharmony_ci	for (i = 0; i < hdspm->max_channels_in; i++) {
525462306a36Sopenharmony_ci		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]);
525562306a36Sopenharmony_ci	}
525662306a36Sopenharmony_ci}
525762306a36Sopenharmony_ci
525862306a36Sopenharmony_cistatic void snd_hdspm_proc_ports_out(struct snd_info_entry *entry,
525962306a36Sopenharmony_ci			  struct snd_info_buffer *buffer)
526062306a36Sopenharmony_ci{
526162306a36Sopenharmony_ci	struct hdspm *hdspm = entry->private_data;
526262306a36Sopenharmony_ci	int i;
526362306a36Sopenharmony_ci
526462306a36Sopenharmony_ci	snd_iprintf(buffer, "# generated by hdspm\n");
526562306a36Sopenharmony_ci
526662306a36Sopenharmony_ci	for (i = 0; i < hdspm->max_channels_out; i++) {
526762306a36Sopenharmony_ci		snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]);
526862306a36Sopenharmony_ci	}
526962306a36Sopenharmony_ci}
527062306a36Sopenharmony_ci
527162306a36Sopenharmony_ci
527262306a36Sopenharmony_cistatic void snd_hdspm_proc_init(struct hdspm *hdspm)
527362306a36Sopenharmony_ci{
527462306a36Sopenharmony_ci	void (*read)(struct snd_info_entry *, struct snd_info_buffer *) = NULL;
527562306a36Sopenharmony_ci
527662306a36Sopenharmony_ci	switch (hdspm->io_type) {
527762306a36Sopenharmony_ci	case AES32:
527862306a36Sopenharmony_ci		read = snd_hdspm_proc_read_aes32;
527962306a36Sopenharmony_ci		break;
528062306a36Sopenharmony_ci	case MADI:
528162306a36Sopenharmony_ci		read = snd_hdspm_proc_read_madi;
528262306a36Sopenharmony_ci		break;
528362306a36Sopenharmony_ci	case MADIface:
528462306a36Sopenharmony_ci		/* read = snd_hdspm_proc_read_madiface; */
528562306a36Sopenharmony_ci		break;
528662306a36Sopenharmony_ci	case RayDAT:
528762306a36Sopenharmony_ci		read = snd_hdspm_proc_read_raydat;
528862306a36Sopenharmony_ci		break;
528962306a36Sopenharmony_ci	case AIO:
529062306a36Sopenharmony_ci		break;
529162306a36Sopenharmony_ci	}
529262306a36Sopenharmony_ci
529362306a36Sopenharmony_ci	snd_card_ro_proc_new(hdspm->card, "hdspm", hdspm, read);
529462306a36Sopenharmony_ci	snd_card_ro_proc_new(hdspm->card, "ports.in", hdspm,
529562306a36Sopenharmony_ci			     snd_hdspm_proc_ports_in);
529662306a36Sopenharmony_ci	snd_card_ro_proc_new(hdspm->card, "ports.out", hdspm,
529762306a36Sopenharmony_ci			     snd_hdspm_proc_ports_out);
529862306a36Sopenharmony_ci
529962306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
530062306a36Sopenharmony_ci	/* debug file to read all hdspm registers */
530162306a36Sopenharmony_ci	snd_card_ro_proc_new(hdspm->card, "debug", hdspm,
530262306a36Sopenharmony_ci			     snd_hdspm_proc_read_debug);
530362306a36Sopenharmony_ci#endif
530462306a36Sopenharmony_ci}
530562306a36Sopenharmony_ci
530662306a36Sopenharmony_ci/*------------------------------------------------------------
530762306a36Sopenharmony_ci   hdspm intitialize
530862306a36Sopenharmony_ci ------------------------------------------------------------*/
530962306a36Sopenharmony_ci
531062306a36Sopenharmony_cistatic int snd_hdspm_set_defaults(struct hdspm * hdspm)
531162306a36Sopenharmony_ci{
531262306a36Sopenharmony_ci	/* ASSUMPTION: hdspm->lock is either held, or there is no need to
531362306a36Sopenharmony_ci	   hold it (e.g. during module initialization).
531462306a36Sopenharmony_ci	   */
531562306a36Sopenharmony_ci
531662306a36Sopenharmony_ci	/* set defaults:       */
531762306a36Sopenharmony_ci
531862306a36Sopenharmony_ci	hdspm->settings_register = 0;
531962306a36Sopenharmony_ci
532062306a36Sopenharmony_ci	switch (hdspm->io_type) {
532162306a36Sopenharmony_ci	case MADI:
532262306a36Sopenharmony_ci	case MADIface:
532362306a36Sopenharmony_ci		hdspm->control_register =
532462306a36Sopenharmony_ci			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
532562306a36Sopenharmony_ci		break;
532662306a36Sopenharmony_ci
532762306a36Sopenharmony_ci	case RayDAT:
532862306a36Sopenharmony_ci	case AIO:
532962306a36Sopenharmony_ci		hdspm->settings_register = 0x1 + 0x1000;
533062306a36Sopenharmony_ci		/* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0,
533162306a36Sopenharmony_ci		 * line_out */
533262306a36Sopenharmony_ci		hdspm->control_register =
533362306a36Sopenharmony_ci			0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
533462306a36Sopenharmony_ci		break;
533562306a36Sopenharmony_ci
533662306a36Sopenharmony_ci	case AES32:
533762306a36Sopenharmony_ci		hdspm->control_register =
533862306a36Sopenharmony_ci			HDSPM_ClockModeMaster |	/* Master Clock Mode on */
533962306a36Sopenharmony_ci			hdspm_encode_latency(7) | /* latency max=8192samples */
534062306a36Sopenharmony_ci			HDSPM_SyncRef0 |	/* AES1 is syncclock */
534162306a36Sopenharmony_ci			HDSPM_LineOut |	/* Analog output in */
534262306a36Sopenharmony_ci			HDSPM_Professional;  /* Professional mode */
534362306a36Sopenharmony_ci		break;
534462306a36Sopenharmony_ci	}
534562306a36Sopenharmony_ci
534662306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
534762306a36Sopenharmony_ci
534862306a36Sopenharmony_ci	if (AES32 == hdspm->io_type) {
534962306a36Sopenharmony_ci		/* No control2 register for AES32 */
535062306a36Sopenharmony_ci#ifdef SNDRV_BIG_ENDIAN
535162306a36Sopenharmony_ci		hdspm->control2_register = HDSPM_BIGENDIAN_MODE;
535262306a36Sopenharmony_ci#else
535362306a36Sopenharmony_ci		hdspm->control2_register = 0;
535462306a36Sopenharmony_ci#endif
535562306a36Sopenharmony_ci
535662306a36Sopenharmony_ci		hdspm_write(hdspm, HDSPM_control2Reg, hdspm->control2_register);
535762306a36Sopenharmony_ci	}
535862306a36Sopenharmony_ci	hdspm_compute_period_size(hdspm);
535962306a36Sopenharmony_ci
536062306a36Sopenharmony_ci	/* silence everything */
536162306a36Sopenharmony_ci
536262306a36Sopenharmony_ci	all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
536362306a36Sopenharmony_ci
536462306a36Sopenharmony_ci	if (hdspm_is_raydat_or_aio(hdspm))
536562306a36Sopenharmony_ci		hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
536662306a36Sopenharmony_ci
536762306a36Sopenharmony_ci	/* set a default rate so that the channel map is set up. */
536862306a36Sopenharmony_ci	hdspm_set_rate(hdspm, 48000, 1);
536962306a36Sopenharmony_ci
537062306a36Sopenharmony_ci	return 0;
537162306a36Sopenharmony_ci}
537262306a36Sopenharmony_ci
537362306a36Sopenharmony_ci
537462306a36Sopenharmony_ci/*------------------------------------------------------------
537562306a36Sopenharmony_ci   interrupt
537662306a36Sopenharmony_ci ------------------------------------------------------------*/
537762306a36Sopenharmony_ci
537862306a36Sopenharmony_cistatic irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
537962306a36Sopenharmony_ci{
538062306a36Sopenharmony_ci	struct hdspm *hdspm = (struct hdspm *) dev_id;
538162306a36Sopenharmony_ci	unsigned int status;
538262306a36Sopenharmony_ci	int i, audio, midi, schedule = 0;
538362306a36Sopenharmony_ci	/* cycles_t now; */
538462306a36Sopenharmony_ci
538562306a36Sopenharmony_ci	status = hdspm_read(hdspm, HDSPM_statusRegister);
538662306a36Sopenharmony_ci
538762306a36Sopenharmony_ci	audio = status & HDSPM_audioIRQPending;
538862306a36Sopenharmony_ci	midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending |
538962306a36Sopenharmony_ci			HDSPM_midi2IRQPending | HDSPM_midi3IRQPending);
539062306a36Sopenharmony_ci
539162306a36Sopenharmony_ci	/* now = get_cycles(); */
539262306a36Sopenharmony_ci	/*
539362306a36Sopenharmony_ci	 *   LAT_2..LAT_0 period  counter (win)  counter (mac)
539462306a36Sopenharmony_ci	 *          6       4096   ~256053425     ~514672358
539562306a36Sopenharmony_ci	 *          5       2048   ~128024983     ~257373821
539662306a36Sopenharmony_ci	 *          4       1024    ~64023706     ~128718089
539762306a36Sopenharmony_ci	 *          3        512    ~32005945      ~64385999
539862306a36Sopenharmony_ci	 *          2        256    ~16003039      ~32260176
539962306a36Sopenharmony_ci	 *          1        128     ~7998738      ~16194507
540062306a36Sopenharmony_ci	 *          0         64     ~3998231       ~8191558
540162306a36Sopenharmony_ci	 */
540262306a36Sopenharmony_ci	/*
540362306a36Sopenharmony_ci	  dev_info(hdspm->card->dev, "snd_hdspm_interrupt %llu @ %llx\n",
540462306a36Sopenharmony_ci	   now-hdspm->last_interrupt, status & 0xFFC0);
540562306a36Sopenharmony_ci	   hdspm->last_interrupt = now;
540662306a36Sopenharmony_ci	*/
540762306a36Sopenharmony_ci
540862306a36Sopenharmony_ci	if (!audio && !midi)
540962306a36Sopenharmony_ci		return IRQ_NONE;
541062306a36Sopenharmony_ci
541162306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_interruptConfirmation, 0);
541262306a36Sopenharmony_ci	hdspm->irq_count++;
541362306a36Sopenharmony_ci
541462306a36Sopenharmony_ci
541562306a36Sopenharmony_ci	if (audio) {
541662306a36Sopenharmony_ci		if (hdspm->capture_substream)
541762306a36Sopenharmony_ci			snd_pcm_period_elapsed(hdspm->capture_substream);
541862306a36Sopenharmony_ci
541962306a36Sopenharmony_ci		if (hdspm->playback_substream)
542062306a36Sopenharmony_ci			snd_pcm_period_elapsed(hdspm->playback_substream);
542162306a36Sopenharmony_ci	}
542262306a36Sopenharmony_ci
542362306a36Sopenharmony_ci	if (midi) {
542462306a36Sopenharmony_ci		i = 0;
542562306a36Sopenharmony_ci		while (i < hdspm->midiPorts) {
542662306a36Sopenharmony_ci			if ((hdspm_read(hdspm,
542762306a36Sopenharmony_ci				hdspm->midi[i].statusIn) & 0xff) &&
542862306a36Sopenharmony_ci					(status & hdspm->midi[i].irq)) {
542962306a36Sopenharmony_ci				/* we disable interrupts for this input until
543062306a36Sopenharmony_ci				 * processing is done
543162306a36Sopenharmony_ci				 */
543262306a36Sopenharmony_ci				hdspm->control_register &= ~hdspm->midi[i].ie;
543362306a36Sopenharmony_ci				hdspm_write(hdspm, HDSPM_controlRegister,
543462306a36Sopenharmony_ci						hdspm->control_register);
543562306a36Sopenharmony_ci				hdspm->midi[i].pending = 1;
543662306a36Sopenharmony_ci				schedule = 1;
543762306a36Sopenharmony_ci			}
543862306a36Sopenharmony_ci
543962306a36Sopenharmony_ci			i++;
544062306a36Sopenharmony_ci		}
544162306a36Sopenharmony_ci
544262306a36Sopenharmony_ci		if (schedule)
544362306a36Sopenharmony_ci			queue_work(system_highpri_wq, &hdspm->midi_work);
544462306a36Sopenharmony_ci	}
544562306a36Sopenharmony_ci
544662306a36Sopenharmony_ci	return IRQ_HANDLED;
544762306a36Sopenharmony_ci}
544862306a36Sopenharmony_ci
544962306a36Sopenharmony_ci/*------------------------------------------------------------
545062306a36Sopenharmony_ci   pcm interface
545162306a36Sopenharmony_ci  ------------------------------------------------------------*/
545262306a36Sopenharmony_ci
545362306a36Sopenharmony_ci
545462306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream
545562306a36Sopenharmony_ci					      *substream)
545662306a36Sopenharmony_ci{
545762306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
545862306a36Sopenharmony_ci	return hdspm_hw_pointer(hdspm);
545962306a36Sopenharmony_ci}
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci
546262306a36Sopenharmony_cistatic int snd_hdspm_reset(struct snd_pcm_substream *substream)
546362306a36Sopenharmony_ci{
546462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
546562306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
546662306a36Sopenharmony_ci	struct snd_pcm_substream *other;
546762306a36Sopenharmony_ci
546862306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
546962306a36Sopenharmony_ci		other = hdspm->capture_substream;
547062306a36Sopenharmony_ci	else
547162306a36Sopenharmony_ci		other = hdspm->playback_substream;
547262306a36Sopenharmony_ci
547362306a36Sopenharmony_ci	if (hdspm->running)
547462306a36Sopenharmony_ci		runtime->status->hw_ptr = hdspm_hw_pointer(hdspm);
547562306a36Sopenharmony_ci	else
547662306a36Sopenharmony_ci		runtime->status->hw_ptr = 0;
547762306a36Sopenharmony_ci	if (other) {
547862306a36Sopenharmony_ci		struct snd_pcm_substream *s;
547962306a36Sopenharmony_ci		struct snd_pcm_runtime *oruntime = other->runtime;
548062306a36Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
548162306a36Sopenharmony_ci			if (s == other) {
548262306a36Sopenharmony_ci				oruntime->status->hw_ptr =
548362306a36Sopenharmony_ci					runtime->status->hw_ptr;
548462306a36Sopenharmony_ci				break;
548562306a36Sopenharmony_ci			}
548662306a36Sopenharmony_ci		}
548762306a36Sopenharmony_ci	}
548862306a36Sopenharmony_ci	return 0;
548962306a36Sopenharmony_ci}
549062306a36Sopenharmony_ci
549162306a36Sopenharmony_cistatic int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
549262306a36Sopenharmony_ci			       struct snd_pcm_hw_params *params)
549362306a36Sopenharmony_ci{
549462306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
549562306a36Sopenharmony_ci	int err;
549662306a36Sopenharmony_ci	int i;
549762306a36Sopenharmony_ci	pid_t this_pid;
549862306a36Sopenharmony_ci	pid_t other_pid;
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
550162306a36Sopenharmony_ci
550262306a36Sopenharmony_ci	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
550362306a36Sopenharmony_ci		this_pid = hdspm->playback_pid;
550462306a36Sopenharmony_ci		other_pid = hdspm->capture_pid;
550562306a36Sopenharmony_ci	} else {
550662306a36Sopenharmony_ci		this_pid = hdspm->capture_pid;
550762306a36Sopenharmony_ci		other_pid = hdspm->playback_pid;
550862306a36Sopenharmony_ci	}
550962306a36Sopenharmony_ci
551062306a36Sopenharmony_ci	if (other_pid > 0 && this_pid != other_pid) {
551162306a36Sopenharmony_ci
551262306a36Sopenharmony_ci		/* The other stream is open, and not by the same
551362306a36Sopenharmony_ci		   task as this one. Make sure that the parameters
551462306a36Sopenharmony_ci		   that matter are the same.
551562306a36Sopenharmony_ci		   */
551662306a36Sopenharmony_ci
551762306a36Sopenharmony_ci		if (params_rate(params) != hdspm->system_sample_rate) {
551862306a36Sopenharmony_ci			spin_unlock_irq(&hdspm->lock);
551962306a36Sopenharmony_ci			_snd_pcm_hw_param_setempty(params,
552062306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_RATE);
552162306a36Sopenharmony_ci			return -EBUSY;
552262306a36Sopenharmony_ci		}
552362306a36Sopenharmony_ci
552462306a36Sopenharmony_ci		if (params_period_size(params) != hdspm->period_bytes / 4) {
552562306a36Sopenharmony_ci			spin_unlock_irq(&hdspm->lock);
552662306a36Sopenharmony_ci			_snd_pcm_hw_param_setempty(params,
552762306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
552862306a36Sopenharmony_ci			return -EBUSY;
552962306a36Sopenharmony_ci		}
553062306a36Sopenharmony_ci
553162306a36Sopenharmony_ci	}
553262306a36Sopenharmony_ci	/* We're fine. */
553362306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
553462306a36Sopenharmony_ci
553562306a36Sopenharmony_ci	/* how to make sure that the rate matches an externally-set one ?   */
553662306a36Sopenharmony_ci
553762306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
553862306a36Sopenharmony_ci	err = hdspm_set_rate(hdspm, params_rate(params), 0);
553962306a36Sopenharmony_ci	if (err < 0) {
554062306a36Sopenharmony_ci		dev_info(hdspm->card->dev, "err on hdspm_set_rate: %d\n", err);
554162306a36Sopenharmony_ci		spin_unlock_irq(&hdspm->lock);
554262306a36Sopenharmony_ci		_snd_pcm_hw_param_setempty(params,
554362306a36Sopenharmony_ci				SNDRV_PCM_HW_PARAM_RATE);
554462306a36Sopenharmony_ci		return err;
554562306a36Sopenharmony_ci	}
554662306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
554762306a36Sopenharmony_ci
554862306a36Sopenharmony_ci	err = hdspm_set_interrupt_interval(hdspm,
554962306a36Sopenharmony_ci			params_period_size(params));
555062306a36Sopenharmony_ci	if (err < 0) {
555162306a36Sopenharmony_ci		dev_info(hdspm->card->dev,
555262306a36Sopenharmony_ci			 "err on hdspm_set_interrupt_interval: %d\n", err);
555362306a36Sopenharmony_ci		_snd_pcm_hw_param_setempty(params,
555462306a36Sopenharmony_ci				SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
555562306a36Sopenharmony_ci		return err;
555662306a36Sopenharmony_ci	}
555762306a36Sopenharmony_ci
555862306a36Sopenharmony_ci	/* Memory allocation, takashi's method, dont know if we should
555962306a36Sopenharmony_ci	 * spinlock
556062306a36Sopenharmony_ci	 */
556162306a36Sopenharmony_ci	/* malloc all buffer even if not enabled to get sure */
556262306a36Sopenharmony_ci	/* Update for MADI rev 204: we need to allocate for all channels,
556362306a36Sopenharmony_ci	 * otherwise it doesn't work at 96kHz */
556462306a36Sopenharmony_ci
556562306a36Sopenharmony_ci	err =
556662306a36Sopenharmony_ci		snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
556762306a36Sopenharmony_ci	if (err < 0) {
556862306a36Sopenharmony_ci		dev_info(hdspm->card->dev,
556962306a36Sopenharmony_ci			 "err on snd_pcm_lib_malloc_pages: %d\n", err);
557062306a36Sopenharmony_ci		return err;
557162306a36Sopenharmony_ci	}
557262306a36Sopenharmony_ci
557362306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
557462306a36Sopenharmony_ci
557562306a36Sopenharmony_ci		for (i = 0; i < params_channels(params); ++i) {
557662306a36Sopenharmony_ci			int c = hdspm->channel_map_out[i];
557762306a36Sopenharmony_ci
557862306a36Sopenharmony_ci			if (c < 0)
557962306a36Sopenharmony_ci				continue;      /* just make sure */
558062306a36Sopenharmony_ci			hdspm_set_channel_dma_addr(hdspm, substream,
558162306a36Sopenharmony_ci						   HDSPM_pageAddressBufferOut,
558262306a36Sopenharmony_ci						   c);
558362306a36Sopenharmony_ci			snd_hdspm_enable_out(hdspm, c, 1);
558462306a36Sopenharmony_ci		}
558562306a36Sopenharmony_ci
558662306a36Sopenharmony_ci		hdspm->playback_buffer =
558762306a36Sopenharmony_ci			(unsigned char *) substream->runtime->dma_area;
558862306a36Sopenharmony_ci		dev_dbg(hdspm->card->dev,
558962306a36Sopenharmony_ci			"Allocated sample buffer for playback at %p\n",
559062306a36Sopenharmony_ci				hdspm->playback_buffer);
559162306a36Sopenharmony_ci	} else {
559262306a36Sopenharmony_ci		for (i = 0; i < params_channels(params); ++i) {
559362306a36Sopenharmony_ci			int c = hdspm->channel_map_in[i];
559462306a36Sopenharmony_ci
559562306a36Sopenharmony_ci			if (c < 0)
559662306a36Sopenharmony_ci				continue;
559762306a36Sopenharmony_ci			hdspm_set_channel_dma_addr(hdspm, substream,
559862306a36Sopenharmony_ci						   HDSPM_pageAddressBufferIn,
559962306a36Sopenharmony_ci						   c);
560062306a36Sopenharmony_ci			snd_hdspm_enable_in(hdspm, c, 1);
560162306a36Sopenharmony_ci		}
560262306a36Sopenharmony_ci
560362306a36Sopenharmony_ci		hdspm->capture_buffer =
560462306a36Sopenharmony_ci			(unsigned char *) substream->runtime->dma_area;
560562306a36Sopenharmony_ci		dev_dbg(hdspm->card->dev,
560662306a36Sopenharmony_ci			"Allocated sample buffer for capture at %p\n",
560762306a36Sopenharmony_ci				hdspm->capture_buffer);
560862306a36Sopenharmony_ci	}
560962306a36Sopenharmony_ci
561062306a36Sopenharmony_ci	/*
561162306a36Sopenharmony_ci	   dev_dbg(hdspm->card->dev,
561262306a36Sopenharmony_ci	   "Allocated sample buffer for %s at 0x%08X\n",
561362306a36Sopenharmony_ci	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
561462306a36Sopenharmony_ci	   "playback" : "capture",
561562306a36Sopenharmony_ci	   snd_pcm_sgbuf_get_addr(substream, 0));
561662306a36Sopenharmony_ci	   */
561762306a36Sopenharmony_ci	/*
561862306a36Sopenharmony_ci	   dev_dbg(hdspm->card->dev,
561962306a36Sopenharmony_ci	   "set_hwparams: %s %d Hz, %d channels, bs = %d\n",
562062306a36Sopenharmony_ci	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
562162306a36Sopenharmony_ci	   "playback" : "capture",
562262306a36Sopenharmony_ci	   params_rate(params), params_channels(params),
562362306a36Sopenharmony_ci	   params_buffer_size(params));
562462306a36Sopenharmony_ci	   */
562562306a36Sopenharmony_ci
562662306a36Sopenharmony_ci
562762306a36Sopenharmony_ci	/*  For AES cards, the float format bit is the same as the
562862306a36Sopenharmony_ci	 *  preferred sync reference. Since we don't want to break
562962306a36Sopenharmony_ci	 *  sync settings, we have to skip the remaining part of this
563062306a36Sopenharmony_ci	 *  function.
563162306a36Sopenharmony_ci	 */
563262306a36Sopenharmony_ci	if (hdspm->io_type == AES32) {
563362306a36Sopenharmony_ci		return 0;
563462306a36Sopenharmony_ci	}
563562306a36Sopenharmony_ci
563662306a36Sopenharmony_ci
563762306a36Sopenharmony_ci	/* Switch to native float format if requested */
563862306a36Sopenharmony_ci	if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
563962306a36Sopenharmony_ci		if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
564062306a36Sopenharmony_ci			dev_info(hdspm->card->dev,
564162306a36Sopenharmony_ci				 "Switching to native 32bit LE float format.\n");
564262306a36Sopenharmony_ci
564362306a36Sopenharmony_ci		hdspm->control_register |= HDSPe_FLOAT_FORMAT;
564462306a36Sopenharmony_ci	} else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) {
564562306a36Sopenharmony_ci		if (hdspm->control_register & HDSPe_FLOAT_FORMAT)
564662306a36Sopenharmony_ci			dev_info(hdspm->card->dev,
564762306a36Sopenharmony_ci				 "Switching to native 32bit LE integer format.\n");
564862306a36Sopenharmony_ci
564962306a36Sopenharmony_ci		hdspm->control_register &= ~HDSPe_FLOAT_FORMAT;
565062306a36Sopenharmony_ci	}
565162306a36Sopenharmony_ci	hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
565262306a36Sopenharmony_ci
565362306a36Sopenharmony_ci	return 0;
565462306a36Sopenharmony_ci}
565562306a36Sopenharmony_ci
565662306a36Sopenharmony_cistatic int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
565762306a36Sopenharmony_ci{
565862306a36Sopenharmony_ci	int i;
565962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
566062306a36Sopenharmony_ci
566162306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
566262306a36Sopenharmony_ci		/* Just disable all channels. The saving when disabling a */
566362306a36Sopenharmony_ci		/* smaller set is not worth the trouble. */
566462306a36Sopenharmony_ci		for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
566562306a36Sopenharmony_ci			snd_hdspm_enable_out(hdspm, i, 0);
566662306a36Sopenharmony_ci
566762306a36Sopenharmony_ci		hdspm->playback_buffer = NULL;
566862306a36Sopenharmony_ci	} else {
566962306a36Sopenharmony_ci		for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
567062306a36Sopenharmony_ci			snd_hdspm_enable_in(hdspm, i, 0);
567162306a36Sopenharmony_ci
567262306a36Sopenharmony_ci		hdspm->capture_buffer = NULL;
567362306a36Sopenharmony_ci	}
567462306a36Sopenharmony_ci
567562306a36Sopenharmony_ci	snd_pcm_lib_free_pages(substream);
567662306a36Sopenharmony_ci
567762306a36Sopenharmony_ci	return 0;
567862306a36Sopenharmony_ci}
567962306a36Sopenharmony_ci
568062306a36Sopenharmony_ci
568162306a36Sopenharmony_cistatic int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
568262306a36Sopenharmony_ci		struct snd_pcm_channel_info *info)
568362306a36Sopenharmony_ci{
568462306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
568562306a36Sopenharmony_ci	unsigned int channel = info->channel;
568662306a36Sopenharmony_ci
568762306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
568862306a36Sopenharmony_ci		if (snd_BUG_ON(channel >= hdspm->max_channels_out)) {
568962306a36Sopenharmony_ci			dev_info(hdspm->card->dev,
569062306a36Sopenharmony_ci				 "snd_hdspm_channel_info: output channel out of range (%d)\n",
569162306a36Sopenharmony_ci				 channel);
569262306a36Sopenharmony_ci			return -EINVAL;
569362306a36Sopenharmony_ci		}
569462306a36Sopenharmony_ci
569562306a36Sopenharmony_ci		channel = array_index_nospec(channel, hdspm->max_channels_out);
569662306a36Sopenharmony_ci		if (hdspm->channel_map_out[channel] < 0) {
569762306a36Sopenharmony_ci			dev_info(hdspm->card->dev,
569862306a36Sopenharmony_ci				 "snd_hdspm_channel_info: output channel %d mapped out\n",
569962306a36Sopenharmony_ci				 channel);
570062306a36Sopenharmony_ci			return -EINVAL;
570162306a36Sopenharmony_ci		}
570262306a36Sopenharmony_ci
570362306a36Sopenharmony_ci		info->offset = hdspm->channel_map_out[channel] *
570462306a36Sopenharmony_ci			HDSPM_CHANNEL_BUFFER_BYTES;
570562306a36Sopenharmony_ci	} else {
570662306a36Sopenharmony_ci		if (snd_BUG_ON(channel >= hdspm->max_channels_in)) {
570762306a36Sopenharmony_ci			dev_info(hdspm->card->dev,
570862306a36Sopenharmony_ci				 "snd_hdspm_channel_info: input channel out of range (%d)\n",
570962306a36Sopenharmony_ci				 channel);
571062306a36Sopenharmony_ci			return -EINVAL;
571162306a36Sopenharmony_ci		}
571262306a36Sopenharmony_ci
571362306a36Sopenharmony_ci		channel = array_index_nospec(channel, hdspm->max_channels_in);
571462306a36Sopenharmony_ci		if (hdspm->channel_map_in[channel] < 0) {
571562306a36Sopenharmony_ci			dev_info(hdspm->card->dev,
571662306a36Sopenharmony_ci				 "snd_hdspm_channel_info: input channel %d mapped out\n",
571762306a36Sopenharmony_ci				 channel);
571862306a36Sopenharmony_ci			return -EINVAL;
571962306a36Sopenharmony_ci		}
572062306a36Sopenharmony_ci
572162306a36Sopenharmony_ci		info->offset = hdspm->channel_map_in[channel] *
572262306a36Sopenharmony_ci			HDSPM_CHANNEL_BUFFER_BYTES;
572362306a36Sopenharmony_ci	}
572462306a36Sopenharmony_ci
572562306a36Sopenharmony_ci	info->first = 0;
572662306a36Sopenharmony_ci	info->step = 32;
572762306a36Sopenharmony_ci	return 0;
572862306a36Sopenharmony_ci}
572962306a36Sopenharmony_ci
573062306a36Sopenharmony_ci
573162306a36Sopenharmony_cistatic int snd_hdspm_ioctl(struct snd_pcm_substream *substream,
573262306a36Sopenharmony_ci		unsigned int cmd, void *arg)
573362306a36Sopenharmony_ci{
573462306a36Sopenharmony_ci	switch (cmd) {
573562306a36Sopenharmony_ci	case SNDRV_PCM_IOCTL1_RESET:
573662306a36Sopenharmony_ci		return snd_hdspm_reset(substream);
573762306a36Sopenharmony_ci
573862306a36Sopenharmony_ci	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
573962306a36Sopenharmony_ci		{
574062306a36Sopenharmony_ci			struct snd_pcm_channel_info *info = arg;
574162306a36Sopenharmony_ci			return snd_hdspm_channel_info(substream, info);
574262306a36Sopenharmony_ci		}
574362306a36Sopenharmony_ci	default:
574462306a36Sopenharmony_ci		break;
574562306a36Sopenharmony_ci	}
574662306a36Sopenharmony_ci
574762306a36Sopenharmony_ci	return snd_pcm_lib_ioctl(substream, cmd, arg);
574862306a36Sopenharmony_ci}
574962306a36Sopenharmony_ci
575062306a36Sopenharmony_cistatic int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
575162306a36Sopenharmony_ci{
575262306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
575362306a36Sopenharmony_ci	struct snd_pcm_substream *other;
575462306a36Sopenharmony_ci	int running;
575562306a36Sopenharmony_ci
575662306a36Sopenharmony_ci	spin_lock(&hdspm->lock);
575762306a36Sopenharmony_ci	running = hdspm->running;
575862306a36Sopenharmony_ci	switch (cmd) {
575962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
576062306a36Sopenharmony_ci		running |= 1 << substream->stream;
576162306a36Sopenharmony_ci		break;
576262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
576362306a36Sopenharmony_ci		running &= ~(1 << substream->stream);
576462306a36Sopenharmony_ci		break;
576562306a36Sopenharmony_ci	default:
576662306a36Sopenharmony_ci		snd_BUG();
576762306a36Sopenharmony_ci		spin_unlock(&hdspm->lock);
576862306a36Sopenharmony_ci		return -EINVAL;
576962306a36Sopenharmony_ci	}
577062306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
577162306a36Sopenharmony_ci		other = hdspm->capture_substream;
577262306a36Sopenharmony_ci	else
577362306a36Sopenharmony_ci		other = hdspm->playback_substream;
577462306a36Sopenharmony_ci
577562306a36Sopenharmony_ci	if (other) {
577662306a36Sopenharmony_ci		struct snd_pcm_substream *s;
577762306a36Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
577862306a36Sopenharmony_ci			if (s == other) {
577962306a36Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
578062306a36Sopenharmony_ci				if (cmd == SNDRV_PCM_TRIGGER_START)
578162306a36Sopenharmony_ci					running |= 1 << s->stream;
578262306a36Sopenharmony_ci				else
578362306a36Sopenharmony_ci					running &= ~(1 << s->stream);
578462306a36Sopenharmony_ci				goto _ok;
578562306a36Sopenharmony_ci			}
578662306a36Sopenharmony_ci		}
578762306a36Sopenharmony_ci		if (cmd == SNDRV_PCM_TRIGGER_START) {
578862306a36Sopenharmony_ci			if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
578962306a36Sopenharmony_ci					&& substream->stream ==
579062306a36Sopenharmony_ci					SNDRV_PCM_STREAM_CAPTURE)
579162306a36Sopenharmony_ci				hdspm_silence_playback(hdspm);
579262306a36Sopenharmony_ci		} else {
579362306a36Sopenharmony_ci			if (running &&
579462306a36Sopenharmony_ci				substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
579562306a36Sopenharmony_ci				hdspm_silence_playback(hdspm);
579662306a36Sopenharmony_ci		}
579762306a36Sopenharmony_ci	} else {
579862306a36Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
579962306a36Sopenharmony_ci			hdspm_silence_playback(hdspm);
580062306a36Sopenharmony_ci	}
580162306a36Sopenharmony_ci_ok:
580262306a36Sopenharmony_ci	snd_pcm_trigger_done(substream, substream);
580362306a36Sopenharmony_ci	if (!hdspm->running && running)
580462306a36Sopenharmony_ci		hdspm_start_audio(hdspm);
580562306a36Sopenharmony_ci	else if (hdspm->running && !running)
580662306a36Sopenharmony_ci		hdspm_stop_audio(hdspm);
580762306a36Sopenharmony_ci	hdspm->running = running;
580862306a36Sopenharmony_ci	spin_unlock(&hdspm->lock);
580962306a36Sopenharmony_ci
581062306a36Sopenharmony_ci	return 0;
581162306a36Sopenharmony_ci}
581262306a36Sopenharmony_ci
581362306a36Sopenharmony_cistatic int snd_hdspm_prepare(struct snd_pcm_substream *substream)
581462306a36Sopenharmony_ci{
581562306a36Sopenharmony_ci	return 0;
581662306a36Sopenharmony_ci}
581762306a36Sopenharmony_ci
581862306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
581962306a36Sopenharmony_ci	.info = (SNDRV_PCM_INFO_MMAP |
582062306a36Sopenharmony_ci		 SNDRV_PCM_INFO_MMAP_VALID |
582162306a36Sopenharmony_ci		 SNDRV_PCM_INFO_NONINTERLEAVED |
582262306a36Sopenharmony_ci		 SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_DOUBLE),
582362306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE,
582462306a36Sopenharmony_ci	.rates = (SNDRV_PCM_RATE_32000 |
582562306a36Sopenharmony_ci		  SNDRV_PCM_RATE_44100 |
582662306a36Sopenharmony_ci		  SNDRV_PCM_RATE_48000 |
582762306a36Sopenharmony_ci		  SNDRV_PCM_RATE_64000 |
582862306a36Sopenharmony_ci		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
582962306a36Sopenharmony_ci		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 ),
583062306a36Sopenharmony_ci	.rate_min = 32000,
583162306a36Sopenharmony_ci	.rate_max = 192000,
583262306a36Sopenharmony_ci	.channels_min = 1,
583362306a36Sopenharmony_ci	.channels_max = HDSPM_MAX_CHANNELS,
583462306a36Sopenharmony_ci	.buffer_bytes_max =
583562306a36Sopenharmony_ci	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
583662306a36Sopenharmony_ci	.period_bytes_min = (32 * 4),
583762306a36Sopenharmony_ci	.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
583862306a36Sopenharmony_ci	.periods_min = 2,
583962306a36Sopenharmony_ci	.periods_max = 512,
584062306a36Sopenharmony_ci	.fifo_size = 0
584162306a36Sopenharmony_ci};
584262306a36Sopenharmony_ci
584362306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
584462306a36Sopenharmony_ci	.info = (SNDRV_PCM_INFO_MMAP |
584562306a36Sopenharmony_ci		 SNDRV_PCM_INFO_MMAP_VALID |
584662306a36Sopenharmony_ci		 SNDRV_PCM_INFO_NONINTERLEAVED |
584762306a36Sopenharmony_ci		 SNDRV_PCM_INFO_SYNC_START),
584862306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE,
584962306a36Sopenharmony_ci	.rates = (SNDRV_PCM_RATE_32000 |
585062306a36Sopenharmony_ci		  SNDRV_PCM_RATE_44100 |
585162306a36Sopenharmony_ci		  SNDRV_PCM_RATE_48000 |
585262306a36Sopenharmony_ci		  SNDRV_PCM_RATE_64000 |
585362306a36Sopenharmony_ci		  SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
585462306a36Sopenharmony_ci		  SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
585562306a36Sopenharmony_ci	.rate_min = 32000,
585662306a36Sopenharmony_ci	.rate_max = 192000,
585762306a36Sopenharmony_ci	.channels_min = 1,
585862306a36Sopenharmony_ci	.channels_max = HDSPM_MAX_CHANNELS,
585962306a36Sopenharmony_ci	.buffer_bytes_max =
586062306a36Sopenharmony_ci	    HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
586162306a36Sopenharmony_ci	.period_bytes_min = (32 * 4),
586262306a36Sopenharmony_ci	.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
586362306a36Sopenharmony_ci	.periods_min = 2,
586462306a36Sopenharmony_ci	.periods_max = 512,
586562306a36Sopenharmony_ci	.fifo_size = 0
586662306a36Sopenharmony_ci};
586762306a36Sopenharmony_ci
586862306a36Sopenharmony_cistatic int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params,
586962306a36Sopenharmony_ci					   struct snd_pcm_hw_rule *rule)
587062306a36Sopenharmony_ci{
587162306a36Sopenharmony_ci	struct hdspm *hdspm = rule->private;
587262306a36Sopenharmony_ci	struct snd_interval *c =
587362306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
587462306a36Sopenharmony_ci	struct snd_interval *r =
587562306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
587662306a36Sopenharmony_ci
587762306a36Sopenharmony_ci	if (r->min > 96000 && r->max <= 192000) {
587862306a36Sopenharmony_ci		struct snd_interval t = {
587962306a36Sopenharmony_ci			.min = hdspm->qs_in_channels,
588062306a36Sopenharmony_ci			.max = hdspm->qs_in_channels,
588162306a36Sopenharmony_ci			.integer = 1,
588262306a36Sopenharmony_ci		};
588362306a36Sopenharmony_ci		return snd_interval_refine(c, &t);
588462306a36Sopenharmony_ci	} else if (r->min > 48000 && r->max <= 96000) {
588562306a36Sopenharmony_ci		struct snd_interval t = {
588662306a36Sopenharmony_ci			.min = hdspm->ds_in_channels,
588762306a36Sopenharmony_ci			.max = hdspm->ds_in_channels,
588862306a36Sopenharmony_ci			.integer = 1,
588962306a36Sopenharmony_ci		};
589062306a36Sopenharmony_ci		return snd_interval_refine(c, &t);
589162306a36Sopenharmony_ci	} else if (r->max < 64000) {
589262306a36Sopenharmony_ci		struct snd_interval t = {
589362306a36Sopenharmony_ci			.min = hdspm->ss_in_channels,
589462306a36Sopenharmony_ci			.max = hdspm->ss_in_channels,
589562306a36Sopenharmony_ci			.integer = 1,
589662306a36Sopenharmony_ci		};
589762306a36Sopenharmony_ci		return snd_interval_refine(c, &t);
589862306a36Sopenharmony_ci	}
589962306a36Sopenharmony_ci
590062306a36Sopenharmony_ci	return 0;
590162306a36Sopenharmony_ci}
590262306a36Sopenharmony_ci
590362306a36Sopenharmony_cistatic int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params,
590462306a36Sopenharmony_ci					   struct snd_pcm_hw_rule * rule)
590562306a36Sopenharmony_ci{
590662306a36Sopenharmony_ci	struct hdspm *hdspm = rule->private;
590762306a36Sopenharmony_ci	struct snd_interval *c =
590862306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
590962306a36Sopenharmony_ci	struct snd_interval *r =
591062306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
591162306a36Sopenharmony_ci
591262306a36Sopenharmony_ci	if (r->min > 96000 && r->max <= 192000) {
591362306a36Sopenharmony_ci		struct snd_interval t = {
591462306a36Sopenharmony_ci			.min = hdspm->qs_out_channels,
591562306a36Sopenharmony_ci			.max = hdspm->qs_out_channels,
591662306a36Sopenharmony_ci			.integer = 1,
591762306a36Sopenharmony_ci		};
591862306a36Sopenharmony_ci		return snd_interval_refine(c, &t);
591962306a36Sopenharmony_ci	} else if (r->min > 48000 && r->max <= 96000) {
592062306a36Sopenharmony_ci		struct snd_interval t = {
592162306a36Sopenharmony_ci			.min = hdspm->ds_out_channels,
592262306a36Sopenharmony_ci			.max = hdspm->ds_out_channels,
592362306a36Sopenharmony_ci			.integer = 1,
592462306a36Sopenharmony_ci		};
592562306a36Sopenharmony_ci		return snd_interval_refine(c, &t);
592662306a36Sopenharmony_ci	} else if (r->max < 64000) {
592762306a36Sopenharmony_ci		struct snd_interval t = {
592862306a36Sopenharmony_ci			.min = hdspm->ss_out_channels,
592962306a36Sopenharmony_ci			.max = hdspm->ss_out_channels,
593062306a36Sopenharmony_ci			.integer = 1,
593162306a36Sopenharmony_ci		};
593262306a36Sopenharmony_ci		return snd_interval_refine(c, &t);
593362306a36Sopenharmony_ci	} else {
593462306a36Sopenharmony_ci	}
593562306a36Sopenharmony_ci	return 0;
593662306a36Sopenharmony_ci}
593762306a36Sopenharmony_ci
593862306a36Sopenharmony_cistatic int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params,
593962306a36Sopenharmony_ci					   struct snd_pcm_hw_rule * rule)
594062306a36Sopenharmony_ci{
594162306a36Sopenharmony_ci	struct hdspm *hdspm = rule->private;
594262306a36Sopenharmony_ci	struct snd_interval *c =
594362306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
594462306a36Sopenharmony_ci	struct snd_interval *r =
594562306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
594662306a36Sopenharmony_ci
594762306a36Sopenharmony_ci	if (c->min >= hdspm->ss_in_channels) {
594862306a36Sopenharmony_ci		struct snd_interval t = {
594962306a36Sopenharmony_ci			.min = 32000,
595062306a36Sopenharmony_ci			.max = 48000,
595162306a36Sopenharmony_ci			.integer = 1,
595262306a36Sopenharmony_ci		};
595362306a36Sopenharmony_ci		return snd_interval_refine(r, &t);
595462306a36Sopenharmony_ci	} else if (c->max <= hdspm->qs_in_channels) {
595562306a36Sopenharmony_ci		struct snd_interval t = {
595662306a36Sopenharmony_ci			.min = 128000,
595762306a36Sopenharmony_ci			.max = 192000,
595862306a36Sopenharmony_ci			.integer = 1,
595962306a36Sopenharmony_ci		};
596062306a36Sopenharmony_ci		return snd_interval_refine(r, &t);
596162306a36Sopenharmony_ci	} else if (c->max <= hdspm->ds_in_channels) {
596262306a36Sopenharmony_ci		struct snd_interval t = {
596362306a36Sopenharmony_ci			.min = 64000,
596462306a36Sopenharmony_ci			.max = 96000,
596562306a36Sopenharmony_ci			.integer = 1,
596662306a36Sopenharmony_ci		};
596762306a36Sopenharmony_ci		return snd_interval_refine(r, &t);
596862306a36Sopenharmony_ci	}
596962306a36Sopenharmony_ci
597062306a36Sopenharmony_ci	return 0;
597162306a36Sopenharmony_ci}
597262306a36Sopenharmony_cistatic int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params,
597362306a36Sopenharmony_ci					   struct snd_pcm_hw_rule *rule)
597462306a36Sopenharmony_ci{
597562306a36Sopenharmony_ci	struct hdspm *hdspm = rule->private;
597662306a36Sopenharmony_ci	struct snd_interval *c =
597762306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
597862306a36Sopenharmony_ci	struct snd_interval *r =
597962306a36Sopenharmony_ci	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
598062306a36Sopenharmony_ci
598162306a36Sopenharmony_ci	if (c->min >= hdspm->ss_out_channels) {
598262306a36Sopenharmony_ci		struct snd_interval t = {
598362306a36Sopenharmony_ci			.min = 32000,
598462306a36Sopenharmony_ci			.max = 48000,
598562306a36Sopenharmony_ci			.integer = 1,
598662306a36Sopenharmony_ci		};
598762306a36Sopenharmony_ci		return snd_interval_refine(r, &t);
598862306a36Sopenharmony_ci	} else if (c->max <= hdspm->qs_out_channels) {
598962306a36Sopenharmony_ci		struct snd_interval t = {
599062306a36Sopenharmony_ci			.min = 128000,
599162306a36Sopenharmony_ci			.max = 192000,
599262306a36Sopenharmony_ci			.integer = 1,
599362306a36Sopenharmony_ci		};
599462306a36Sopenharmony_ci		return snd_interval_refine(r, &t);
599562306a36Sopenharmony_ci	} else if (c->max <= hdspm->ds_out_channels) {
599662306a36Sopenharmony_ci		struct snd_interval t = {
599762306a36Sopenharmony_ci			.min = 64000,
599862306a36Sopenharmony_ci			.max = 96000,
599962306a36Sopenharmony_ci			.integer = 1,
600062306a36Sopenharmony_ci		};
600162306a36Sopenharmony_ci		return snd_interval_refine(r, &t);
600262306a36Sopenharmony_ci	}
600362306a36Sopenharmony_ci
600462306a36Sopenharmony_ci	return 0;
600562306a36Sopenharmony_ci}
600662306a36Sopenharmony_ci
600762306a36Sopenharmony_cistatic int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params,
600862306a36Sopenharmony_ci				      struct snd_pcm_hw_rule *rule)
600962306a36Sopenharmony_ci{
601062306a36Sopenharmony_ci	unsigned int list[3];
601162306a36Sopenharmony_ci	struct hdspm *hdspm = rule->private;
601262306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
601362306a36Sopenharmony_ci			SNDRV_PCM_HW_PARAM_CHANNELS);
601462306a36Sopenharmony_ci
601562306a36Sopenharmony_ci	list[0] = hdspm->qs_in_channels;
601662306a36Sopenharmony_ci	list[1] = hdspm->ds_in_channels;
601762306a36Sopenharmony_ci	list[2] = hdspm->ss_in_channels;
601862306a36Sopenharmony_ci	return snd_interval_list(c, 3, list, 0);
601962306a36Sopenharmony_ci}
602062306a36Sopenharmony_ci
602162306a36Sopenharmony_cistatic int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params,
602262306a36Sopenharmony_ci				      struct snd_pcm_hw_rule *rule)
602362306a36Sopenharmony_ci{
602462306a36Sopenharmony_ci	unsigned int list[3];
602562306a36Sopenharmony_ci	struct hdspm *hdspm = rule->private;
602662306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
602762306a36Sopenharmony_ci			SNDRV_PCM_HW_PARAM_CHANNELS);
602862306a36Sopenharmony_ci
602962306a36Sopenharmony_ci	list[0] = hdspm->qs_out_channels;
603062306a36Sopenharmony_ci	list[1] = hdspm->ds_out_channels;
603162306a36Sopenharmony_ci	list[2] = hdspm->ss_out_channels;
603262306a36Sopenharmony_ci	return snd_interval_list(c, 3, list, 0);
603362306a36Sopenharmony_ci}
603462306a36Sopenharmony_ci
603562306a36Sopenharmony_ci
603662306a36Sopenharmony_cistatic const unsigned int hdspm_aes32_sample_rates[] = {
603762306a36Sopenharmony_ci	32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
603862306a36Sopenharmony_ci};
603962306a36Sopenharmony_ci
604062306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list
604162306a36Sopenharmony_cihdspm_hw_constraints_aes32_sample_rates = {
604262306a36Sopenharmony_ci	.count = ARRAY_SIZE(hdspm_aes32_sample_rates),
604362306a36Sopenharmony_ci	.list = hdspm_aes32_sample_rates,
604462306a36Sopenharmony_ci	.mask = 0
604562306a36Sopenharmony_ci};
604662306a36Sopenharmony_ci
604762306a36Sopenharmony_cistatic int snd_hdspm_open(struct snd_pcm_substream *substream)
604862306a36Sopenharmony_ci{
604962306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
605062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
605162306a36Sopenharmony_ci	bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
605262306a36Sopenharmony_ci
605362306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
605462306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
605562306a36Sopenharmony_ci	runtime->hw = (playback) ? snd_hdspm_playback_subinfo :
605662306a36Sopenharmony_ci		snd_hdspm_capture_subinfo;
605762306a36Sopenharmony_ci
605862306a36Sopenharmony_ci	if (playback) {
605962306a36Sopenharmony_ci		if (!hdspm->capture_substream)
606062306a36Sopenharmony_ci			hdspm_stop_audio(hdspm);
606162306a36Sopenharmony_ci
606262306a36Sopenharmony_ci		hdspm->playback_pid = current->pid;
606362306a36Sopenharmony_ci		hdspm->playback_substream = substream;
606462306a36Sopenharmony_ci	} else {
606562306a36Sopenharmony_ci		if (!hdspm->playback_substream)
606662306a36Sopenharmony_ci			hdspm_stop_audio(hdspm);
606762306a36Sopenharmony_ci
606862306a36Sopenharmony_ci		hdspm->capture_pid = current->pid;
606962306a36Sopenharmony_ci		hdspm->capture_substream = substream;
607062306a36Sopenharmony_ci	}
607162306a36Sopenharmony_ci
607262306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
607362306a36Sopenharmony_ci
607462306a36Sopenharmony_ci	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
607562306a36Sopenharmony_ci	snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
607662306a36Sopenharmony_ci
607762306a36Sopenharmony_ci	switch (hdspm->io_type) {
607862306a36Sopenharmony_ci	case AIO:
607962306a36Sopenharmony_ci	case RayDAT:
608062306a36Sopenharmony_ci		snd_pcm_hw_constraint_minmax(runtime,
608162306a36Sopenharmony_ci					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
608262306a36Sopenharmony_ci					     32, 4096);
608362306a36Sopenharmony_ci		/* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
608462306a36Sopenharmony_ci		snd_pcm_hw_constraint_single(runtime,
608562306a36Sopenharmony_ci					     SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
608662306a36Sopenharmony_ci					     16384);
608762306a36Sopenharmony_ci		break;
608862306a36Sopenharmony_ci
608962306a36Sopenharmony_ci	default:
609062306a36Sopenharmony_ci		snd_pcm_hw_constraint_minmax(runtime,
609162306a36Sopenharmony_ci					     SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
609262306a36Sopenharmony_ci					     64, 8192);
609362306a36Sopenharmony_ci		snd_pcm_hw_constraint_single(runtime,
609462306a36Sopenharmony_ci					     SNDRV_PCM_HW_PARAM_PERIODS, 2);
609562306a36Sopenharmony_ci		break;
609662306a36Sopenharmony_ci	}
609762306a36Sopenharmony_ci
609862306a36Sopenharmony_ci	if (AES32 == hdspm->io_type) {
609962306a36Sopenharmony_ci		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
610062306a36Sopenharmony_ci		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
610162306a36Sopenharmony_ci				&hdspm_hw_constraints_aes32_sample_rates);
610262306a36Sopenharmony_ci	} else {
610362306a36Sopenharmony_ci		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
610462306a36Sopenharmony_ci				(playback ?
610562306a36Sopenharmony_ci				 snd_hdspm_hw_rule_rate_out_channels :
610662306a36Sopenharmony_ci				 snd_hdspm_hw_rule_rate_in_channels), hdspm,
610762306a36Sopenharmony_ci				SNDRV_PCM_HW_PARAM_CHANNELS, -1);
610862306a36Sopenharmony_ci	}
610962306a36Sopenharmony_ci
611062306a36Sopenharmony_ci	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
611162306a36Sopenharmony_ci			(playback ? snd_hdspm_hw_rule_out_channels :
611262306a36Sopenharmony_ci			 snd_hdspm_hw_rule_in_channels), hdspm,
611362306a36Sopenharmony_ci			SNDRV_PCM_HW_PARAM_CHANNELS, -1);
611462306a36Sopenharmony_ci
611562306a36Sopenharmony_ci	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
611662306a36Sopenharmony_ci			(playback ? snd_hdspm_hw_rule_out_channels_rate :
611762306a36Sopenharmony_ci			 snd_hdspm_hw_rule_in_channels_rate), hdspm,
611862306a36Sopenharmony_ci			SNDRV_PCM_HW_PARAM_RATE, -1);
611962306a36Sopenharmony_ci
612062306a36Sopenharmony_ci	return 0;
612162306a36Sopenharmony_ci}
612262306a36Sopenharmony_ci
612362306a36Sopenharmony_cistatic int snd_hdspm_release(struct snd_pcm_substream *substream)
612462306a36Sopenharmony_ci{
612562306a36Sopenharmony_ci	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
612662306a36Sopenharmony_ci	bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
612762306a36Sopenharmony_ci
612862306a36Sopenharmony_ci	spin_lock_irq(&hdspm->lock);
612962306a36Sopenharmony_ci
613062306a36Sopenharmony_ci	if (playback) {
613162306a36Sopenharmony_ci		hdspm->playback_pid = -1;
613262306a36Sopenharmony_ci		hdspm->playback_substream = NULL;
613362306a36Sopenharmony_ci	} else {
613462306a36Sopenharmony_ci		hdspm->capture_pid = -1;
613562306a36Sopenharmony_ci		hdspm->capture_substream = NULL;
613662306a36Sopenharmony_ci	}
613762306a36Sopenharmony_ci
613862306a36Sopenharmony_ci	spin_unlock_irq(&hdspm->lock);
613962306a36Sopenharmony_ci
614062306a36Sopenharmony_ci	return 0;
614162306a36Sopenharmony_ci}
614262306a36Sopenharmony_ci
614362306a36Sopenharmony_cistatic int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
614462306a36Sopenharmony_ci{
614562306a36Sopenharmony_ci	/* we have nothing to initialize but the call is required */
614662306a36Sopenharmony_ci	return 0;
614762306a36Sopenharmony_ci}
614862306a36Sopenharmony_ci
614962306a36Sopenharmony_cistatic int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
615062306a36Sopenharmony_ci		unsigned int cmd, unsigned long arg)
615162306a36Sopenharmony_ci{
615262306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
615362306a36Sopenharmony_ci	struct hdspm *hdspm = hw->private_data;
615462306a36Sopenharmony_ci	struct hdspm_mixer_ioctl mixer;
615562306a36Sopenharmony_ci	struct hdspm_config info;
615662306a36Sopenharmony_ci	struct hdspm_status status;
615762306a36Sopenharmony_ci	struct hdspm_version hdspm_version;
615862306a36Sopenharmony_ci	struct hdspm_peak_rms *levels;
615962306a36Sopenharmony_ci	struct hdspm_ltc ltc;
616062306a36Sopenharmony_ci	unsigned int statusregister;
616162306a36Sopenharmony_ci	long unsigned int s;
616262306a36Sopenharmony_ci	int i = 0;
616362306a36Sopenharmony_ci
616462306a36Sopenharmony_ci	switch (cmd) {
616562306a36Sopenharmony_ci
616662306a36Sopenharmony_ci	case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS:
616762306a36Sopenharmony_ci		levels = &hdspm->peak_rms;
616862306a36Sopenharmony_ci		for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
616962306a36Sopenharmony_ci			levels->input_peaks[i] =
617062306a36Sopenharmony_ci				readl(hdspm->iobase +
617162306a36Sopenharmony_ci						HDSPM_MADI_INPUT_PEAK + i*4);
617262306a36Sopenharmony_ci			levels->playback_peaks[i] =
617362306a36Sopenharmony_ci				readl(hdspm->iobase +
617462306a36Sopenharmony_ci						HDSPM_MADI_PLAYBACK_PEAK + i*4);
617562306a36Sopenharmony_ci			levels->output_peaks[i] =
617662306a36Sopenharmony_ci				readl(hdspm->iobase +
617762306a36Sopenharmony_ci						HDSPM_MADI_OUTPUT_PEAK + i*4);
617862306a36Sopenharmony_ci
617962306a36Sopenharmony_ci			levels->input_rms[i] =
618062306a36Sopenharmony_ci				((uint64_t) readl(hdspm->iobase +
618162306a36Sopenharmony_ci					HDSPM_MADI_INPUT_RMS_H + i*4) << 32) |
618262306a36Sopenharmony_ci				(uint64_t) readl(hdspm->iobase +
618362306a36Sopenharmony_ci						HDSPM_MADI_INPUT_RMS_L + i*4);
618462306a36Sopenharmony_ci			levels->playback_rms[i] =
618562306a36Sopenharmony_ci				((uint64_t)readl(hdspm->iobase +
618662306a36Sopenharmony_ci					HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) |
618762306a36Sopenharmony_ci				(uint64_t)readl(hdspm->iobase +
618862306a36Sopenharmony_ci					HDSPM_MADI_PLAYBACK_RMS_L + i*4);
618962306a36Sopenharmony_ci			levels->output_rms[i] =
619062306a36Sopenharmony_ci				((uint64_t)readl(hdspm->iobase +
619162306a36Sopenharmony_ci					HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) |
619262306a36Sopenharmony_ci				(uint64_t)readl(hdspm->iobase +
619362306a36Sopenharmony_ci						HDSPM_MADI_OUTPUT_RMS_L + i*4);
619462306a36Sopenharmony_ci		}
619562306a36Sopenharmony_ci
619662306a36Sopenharmony_ci		if (hdspm->system_sample_rate > 96000) {
619762306a36Sopenharmony_ci			levels->speed = qs;
619862306a36Sopenharmony_ci		} else if (hdspm->system_sample_rate > 48000) {
619962306a36Sopenharmony_ci			levels->speed = ds;
620062306a36Sopenharmony_ci		} else {
620162306a36Sopenharmony_ci			levels->speed = ss;
620262306a36Sopenharmony_ci		}
620362306a36Sopenharmony_ci		levels->status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
620462306a36Sopenharmony_ci
620562306a36Sopenharmony_ci		s = copy_to_user(argp, levels, sizeof(*levels));
620662306a36Sopenharmony_ci		if (0 != s) {
620762306a36Sopenharmony_ci			/* dev_err(hdspm->card->dev, "copy_to_user(.., .., %lu): %lu
620862306a36Sopenharmony_ci			 [Levels]\n", sizeof(struct hdspm_peak_rms), s);
620962306a36Sopenharmony_ci			 */
621062306a36Sopenharmony_ci			return -EFAULT;
621162306a36Sopenharmony_ci		}
621262306a36Sopenharmony_ci		break;
621362306a36Sopenharmony_ci
621462306a36Sopenharmony_ci	case SNDRV_HDSPM_IOCTL_GET_LTC:
621562306a36Sopenharmony_ci		ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
621662306a36Sopenharmony_ci		i = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
621762306a36Sopenharmony_ci		if (i & HDSPM_TCO1_LTC_Input_valid) {
621862306a36Sopenharmony_ci			switch (i & (HDSPM_TCO1_LTC_Format_LSB |
621962306a36Sopenharmony_ci				HDSPM_TCO1_LTC_Format_MSB)) {
622062306a36Sopenharmony_ci			case 0:
622162306a36Sopenharmony_ci				ltc.format = fps_24;
622262306a36Sopenharmony_ci				break;
622362306a36Sopenharmony_ci			case HDSPM_TCO1_LTC_Format_LSB:
622462306a36Sopenharmony_ci				ltc.format = fps_25;
622562306a36Sopenharmony_ci				break;
622662306a36Sopenharmony_ci			case HDSPM_TCO1_LTC_Format_MSB:
622762306a36Sopenharmony_ci				ltc.format = fps_2997;
622862306a36Sopenharmony_ci				break;
622962306a36Sopenharmony_ci			default:
623062306a36Sopenharmony_ci				ltc.format = fps_30;
623162306a36Sopenharmony_ci				break;
623262306a36Sopenharmony_ci			}
623362306a36Sopenharmony_ci			if (i & HDSPM_TCO1_set_drop_frame_flag) {
623462306a36Sopenharmony_ci				ltc.frame = drop_frame;
623562306a36Sopenharmony_ci			} else {
623662306a36Sopenharmony_ci				ltc.frame = full_frame;
623762306a36Sopenharmony_ci			}
623862306a36Sopenharmony_ci		} else {
623962306a36Sopenharmony_ci			ltc.format = format_invalid;
624062306a36Sopenharmony_ci			ltc.frame = frame_invalid;
624162306a36Sopenharmony_ci		}
624262306a36Sopenharmony_ci		if (i & HDSPM_TCO1_Video_Input_Format_NTSC) {
624362306a36Sopenharmony_ci			ltc.input_format = ntsc;
624462306a36Sopenharmony_ci		} else if (i & HDSPM_TCO1_Video_Input_Format_PAL) {
624562306a36Sopenharmony_ci			ltc.input_format = pal;
624662306a36Sopenharmony_ci		} else {
624762306a36Sopenharmony_ci			ltc.input_format = no_video;
624862306a36Sopenharmony_ci		}
624962306a36Sopenharmony_ci
625062306a36Sopenharmony_ci		s = copy_to_user(argp, &ltc, sizeof(ltc));
625162306a36Sopenharmony_ci		if (0 != s) {
625262306a36Sopenharmony_ci			/*
625362306a36Sopenharmony_ci			  dev_err(hdspm->card->dev, "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */
625462306a36Sopenharmony_ci			return -EFAULT;
625562306a36Sopenharmony_ci		}
625662306a36Sopenharmony_ci
625762306a36Sopenharmony_ci		break;
625862306a36Sopenharmony_ci
625962306a36Sopenharmony_ci	case SNDRV_HDSPM_IOCTL_GET_CONFIG:
626062306a36Sopenharmony_ci
626162306a36Sopenharmony_ci		memset(&info, 0, sizeof(info));
626262306a36Sopenharmony_ci		spin_lock_irq(&hdspm->lock);
626362306a36Sopenharmony_ci		info.pref_sync_ref = hdspm_pref_sync_ref(hdspm);
626462306a36Sopenharmony_ci		info.wordclock_sync_check = hdspm_wc_sync_check(hdspm);
626562306a36Sopenharmony_ci
626662306a36Sopenharmony_ci		info.system_sample_rate = hdspm->system_sample_rate;
626762306a36Sopenharmony_ci		info.autosync_sample_rate =
626862306a36Sopenharmony_ci			hdspm_external_sample_rate(hdspm);
626962306a36Sopenharmony_ci		info.system_clock_mode = hdspm_system_clock_mode(hdspm);
627062306a36Sopenharmony_ci		info.clock_source = hdspm_clock_source(hdspm);
627162306a36Sopenharmony_ci		info.autosync_ref = hdspm_autosync_ref(hdspm);
627262306a36Sopenharmony_ci		info.line_out = hdspm_toggle_setting(hdspm, HDSPM_LineOut);
627362306a36Sopenharmony_ci		info.passthru = 0;
627462306a36Sopenharmony_ci		spin_unlock_irq(&hdspm->lock);
627562306a36Sopenharmony_ci		if (copy_to_user(argp, &info, sizeof(info)))
627662306a36Sopenharmony_ci			return -EFAULT;
627762306a36Sopenharmony_ci		break;
627862306a36Sopenharmony_ci
627962306a36Sopenharmony_ci	case SNDRV_HDSPM_IOCTL_GET_STATUS:
628062306a36Sopenharmony_ci		memset(&status, 0, sizeof(status));
628162306a36Sopenharmony_ci
628262306a36Sopenharmony_ci		status.card_type = hdspm->io_type;
628362306a36Sopenharmony_ci
628462306a36Sopenharmony_ci		status.autosync_source = hdspm_autosync_ref(hdspm);
628562306a36Sopenharmony_ci
628662306a36Sopenharmony_ci		status.card_clock = 110069313433624ULL;
628762306a36Sopenharmony_ci		status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
628862306a36Sopenharmony_ci
628962306a36Sopenharmony_ci		switch (hdspm->io_type) {
629062306a36Sopenharmony_ci		case MADI:
629162306a36Sopenharmony_ci		case MADIface:
629262306a36Sopenharmony_ci			status.card_specific.madi.sync_wc =
629362306a36Sopenharmony_ci				hdspm_wc_sync_check(hdspm);
629462306a36Sopenharmony_ci			status.card_specific.madi.sync_madi =
629562306a36Sopenharmony_ci				hdspm_madi_sync_check(hdspm);
629662306a36Sopenharmony_ci			status.card_specific.madi.sync_tco =
629762306a36Sopenharmony_ci				hdspm_tco_sync_check(hdspm);
629862306a36Sopenharmony_ci			status.card_specific.madi.sync_in =
629962306a36Sopenharmony_ci				hdspm_sync_in_sync_check(hdspm);
630062306a36Sopenharmony_ci
630162306a36Sopenharmony_ci			statusregister =
630262306a36Sopenharmony_ci				hdspm_read(hdspm, HDSPM_statusRegister);
630362306a36Sopenharmony_ci			status.card_specific.madi.madi_input =
630462306a36Sopenharmony_ci				(statusregister & HDSPM_AB_int) ? 1 : 0;
630562306a36Sopenharmony_ci			status.card_specific.madi.channel_format =
630662306a36Sopenharmony_ci				(statusregister & HDSPM_RX_64ch) ? 1 : 0;
630762306a36Sopenharmony_ci			/* TODO: Mac driver sets it when f_s>48kHz */
630862306a36Sopenharmony_ci			status.card_specific.madi.frame_format = 0;
630962306a36Sopenharmony_ci			break;
631062306a36Sopenharmony_ci
631162306a36Sopenharmony_ci		default:
631262306a36Sopenharmony_ci			break;
631362306a36Sopenharmony_ci		}
631462306a36Sopenharmony_ci
631562306a36Sopenharmony_ci		if (copy_to_user(argp, &status, sizeof(status)))
631662306a36Sopenharmony_ci			return -EFAULT;
631762306a36Sopenharmony_ci
631862306a36Sopenharmony_ci
631962306a36Sopenharmony_ci		break;
632062306a36Sopenharmony_ci
632162306a36Sopenharmony_ci	case SNDRV_HDSPM_IOCTL_GET_VERSION:
632262306a36Sopenharmony_ci		memset(&hdspm_version, 0, sizeof(hdspm_version));
632362306a36Sopenharmony_ci
632462306a36Sopenharmony_ci		hdspm_version.card_type = hdspm->io_type;
632562306a36Sopenharmony_ci		strscpy(hdspm_version.cardname, hdspm->card_name,
632662306a36Sopenharmony_ci				sizeof(hdspm_version.cardname));
632762306a36Sopenharmony_ci		hdspm_version.serial = hdspm->serial;
632862306a36Sopenharmony_ci		hdspm_version.firmware_rev = hdspm->firmware_rev;
632962306a36Sopenharmony_ci		hdspm_version.addons = 0;
633062306a36Sopenharmony_ci		if (hdspm->tco)
633162306a36Sopenharmony_ci			hdspm_version.addons |= HDSPM_ADDON_TCO;
633262306a36Sopenharmony_ci
633362306a36Sopenharmony_ci		if (copy_to_user(argp, &hdspm_version,
633462306a36Sopenharmony_ci					sizeof(hdspm_version)))
633562306a36Sopenharmony_ci			return -EFAULT;
633662306a36Sopenharmony_ci		break;
633762306a36Sopenharmony_ci
633862306a36Sopenharmony_ci	case SNDRV_HDSPM_IOCTL_GET_MIXER:
633962306a36Sopenharmony_ci		if (copy_from_user(&mixer, argp, sizeof(mixer)))
634062306a36Sopenharmony_ci			return -EFAULT;
634162306a36Sopenharmony_ci		if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer,
634262306a36Sopenharmony_ci				 sizeof(*mixer.mixer)))
634362306a36Sopenharmony_ci			return -EFAULT;
634462306a36Sopenharmony_ci		break;
634562306a36Sopenharmony_ci
634662306a36Sopenharmony_ci	default:
634762306a36Sopenharmony_ci		return -EINVAL;
634862306a36Sopenharmony_ci	}
634962306a36Sopenharmony_ci	return 0;
635062306a36Sopenharmony_ci}
635162306a36Sopenharmony_ci
635262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_hdspm_ops = {
635362306a36Sopenharmony_ci	.open = snd_hdspm_open,
635462306a36Sopenharmony_ci	.close = snd_hdspm_release,
635562306a36Sopenharmony_ci	.ioctl = snd_hdspm_ioctl,
635662306a36Sopenharmony_ci	.hw_params = snd_hdspm_hw_params,
635762306a36Sopenharmony_ci	.hw_free = snd_hdspm_hw_free,
635862306a36Sopenharmony_ci	.prepare = snd_hdspm_prepare,
635962306a36Sopenharmony_ci	.trigger = snd_hdspm_trigger,
636062306a36Sopenharmony_ci	.pointer = snd_hdspm_hw_pointer,
636162306a36Sopenharmony_ci};
636262306a36Sopenharmony_ci
636362306a36Sopenharmony_cistatic int snd_hdspm_create_hwdep(struct snd_card *card,
636462306a36Sopenharmony_ci				  struct hdspm *hdspm)
636562306a36Sopenharmony_ci{
636662306a36Sopenharmony_ci	struct snd_hwdep *hw;
636762306a36Sopenharmony_ci	int err;
636862306a36Sopenharmony_ci
636962306a36Sopenharmony_ci	err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw);
637062306a36Sopenharmony_ci	if (err < 0)
637162306a36Sopenharmony_ci		return err;
637262306a36Sopenharmony_ci
637362306a36Sopenharmony_ci	hdspm->hwdep = hw;
637462306a36Sopenharmony_ci	hw->private_data = hdspm;
637562306a36Sopenharmony_ci	strcpy(hw->name, "HDSPM hwdep interface");
637662306a36Sopenharmony_ci
637762306a36Sopenharmony_ci	hw->ops.open = snd_hdspm_hwdep_dummy_op;
637862306a36Sopenharmony_ci	hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
637962306a36Sopenharmony_ci	hw->ops.ioctl_compat = snd_hdspm_hwdep_ioctl;
638062306a36Sopenharmony_ci	hw->ops.release = snd_hdspm_hwdep_dummy_op;
638162306a36Sopenharmony_ci
638262306a36Sopenharmony_ci	return 0;
638362306a36Sopenharmony_ci}
638462306a36Sopenharmony_ci
638562306a36Sopenharmony_ci
638662306a36Sopenharmony_ci/*------------------------------------------------------------
638762306a36Sopenharmony_ci   memory interface
638862306a36Sopenharmony_ci ------------------------------------------------------------*/
638962306a36Sopenharmony_cistatic int snd_hdspm_preallocate_memory(struct hdspm *hdspm)
639062306a36Sopenharmony_ci{
639162306a36Sopenharmony_ci	struct snd_pcm *pcm;
639262306a36Sopenharmony_ci	size_t wanted;
639362306a36Sopenharmony_ci
639462306a36Sopenharmony_ci	pcm = hdspm->pcm;
639562306a36Sopenharmony_ci
639662306a36Sopenharmony_ci	wanted = HDSPM_DMA_AREA_BYTES;
639762306a36Sopenharmony_ci
639862306a36Sopenharmony_ci	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
639962306a36Sopenharmony_ci					      &hdspm->pci->dev,
640062306a36Sopenharmony_ci					      wanted, wanted);
640162306a36Sopenharmony_ci	dev_dbg(hdspm->card->dev, " Preallocated %zd Bytes\n", wanted);
640262306a36Sopenharmony_ci	return 0;
640362306a36Sopenharmony_ci}
640462306a36Sopenharmony_ci
640562306a36Sopenharmony_ci/* Inform the card what DMA addresses to use for the indicated channel. */
640662306a36Sopenharmony_ci/* Each channel got 16 4K pages allocated for DMA transfers. */
640762306a36Sopenharmony_cistatic void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
640862306a36Sopenharmony_ci				       struct snd_pcm_substream *substream,
640962306a36Sopenharmony_ci				       unsigned int reg, int channel)
641062306a36Sopenharmony_ci{
641162306a36Sopenharmony_ci	int i;
641262306a36Sopenharmony_ci
641362306a36Sopenharmony_ci	for (i = channel * 16; i < channel * 16 + 16; i++)
641462306a36Sopenharmony_ci		hdspm_write(hdspm, reg + 4 * i,
641562306a36Sopenharmony_ci			    snd_pcm_sgbuf_get_addr(substream, 4096 * i));
641662306a36Sopenharmony_ci}
641762306a36Sopenharmony_ci
641862306a36Sopenharmony_ci
641962306a36Sopenharmony_ci/* ------------- ALSA Devices ---------------------------- */
642062306a36Sopenharmony_cistatic int snd_hdspm_create_pcm(struct snd_card *card,
642162306a36Sopenharmony_ci				struct hdspm *hdspm)
642262306a36Sopenharmony_ci{
642362306a36Sopenharmony_ci	struct snd_pcm *pcm;
642462306a36Sopenharmony_ci	int err;
642562306a36Sopenharmony_ci
642662306a36Sopenharmony_ci	err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm);
642762306a36Sopenharmony_ci	if (err < 0)
642862306a36Sopenharmony_ci		return err;
642962306a36Sopenharmony_ci
643062306a36Sopenharmony_ci	hdspm->pcm = pcm;
643162306a36Sopenharmony_ci	pcm->private_data = hdspm;
643262306a36Sopenharmony_ci	strcpy(pcm->name, hdspm->card_name);
643362306a36Sopenharmony_ci
643462306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
643562306a36Sopenharmony_ci			&snd_hdspm_ops);
643662306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
643762306a36Sopenharmony_ci			&snd_hdspm_ops);
643862306a36Sopenharmony_ci
643962306a36Sopenharmony_ci	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
644062306a36Sopenharmony_ci
644162306a36Sopenharmony_ci	err = snd_hdspm_preallocate_memory(hdspm);
644262306a36Sopenharmony_ci	if (err < 0)
644362306a36Sopenharmony_ci		return err;
644462306a36Sopenharmony_ci
644562306a36Sopenharmony_ci	return 0;
644662306a36Sopenharmony_ci}
644762306a36Sopenharmony_ci
644862306a36Sopenharmony_cistatic inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm)
644962306a36Sopenharmony_ci{
645062306a36Sopenharmony_ci	int i;
645162306a36Sopenharmony_ci
645262306a36Sopenharmony_ci	for (i = 0; i < hdspm->midiPorts; i++)
645362306a36Sopenharmony_ci		snd_hdspm_flush_midi_input(hdspm, i);
645462306a36Sopenharmony_ci}
645562306a36Sopenharmony_ci
645662306a36Sopenharmony_cistatic int snd_hdspm_create_alsa_devices(struct snd_card *card,
645762306a36Sopenharmony_ci					 struct hdspm *hdspm)
645862306a36Sopenharmony_ci{
645962306a36Sopenharmony_ci	int err, i;
646062306a36Sopenharmony_ci
646162306a36Sopenharmony_ci	dev_dbg(card->dev, "Create card...\n");
646262306a36Sopenharmony_ci	err = snd_hdspm_create_pcm(card, hdspm);
646362306a36Sopenharmony_ci	if (err < 0)
646462306a36Sopenharmony_ci		return err;
646562306a36Sopenharmony_ci
646662306a36Sopenharmony_ci	i = 0;
646762306a36Sopenharmony_ci	while (i < hdspm->midiPorts) {
646862306a36Sopenharmony_ci		err = snd_hdspm_create_midi(card, hdspm, i);
646962306a36Sopenharmony_ci		if (err < 0) {
647062306a36Sopenharmony_ci			return err;
647162306a36Sopenharmony_ci		}
647262306a36Sopenharmony_ci		i++;
647362306a36Sopenharmony_ci	}
647462306a36Sopenharmony_ci
647562306a36Sopenharmony_ci	err = snd_hdspm_create_controls(card, hdspm);
647662306a36Sopenharmony_ci	if (err < 0)
647762306a36Sopenharmony_ci		return err;
647862306a36Sopenharmony_ci
647962306a36Sopenharmony_ci	err = snd_hdspm_create_hwdep(card, hdspm);
648062306a36Sopenharmony_ci	if (err < 0)
648162306a36Sopenharmony_ci		return err;
648262306a36Sopenharmony_ci
648362306a36Sopenharmony_ci	dev_dbg(card->dev, "proc init...\n");
648462306a36Sopenharmony_ci	snd_hdspm_proc_init(hdspm);
648562306a36Sopenharmony_ci
648662306a36Sopenharmony_ci	hdspm->system_sample_rate = -1;
648762306a36Sopenharmony_ci	hdspm->last_external_sample_rate = -1;
648862306a36Sopenharmony_ci	hdspm->last_internal_sample_rate = -1;
648962306a36Sopenharmony_ci	hdspm->playback_pid = -1;
649062306a36Sopenharmony_ci	hdspm->capture_pid = -1;
649162306a36Sopenharmony_ci	hdspm->capture_substream = NULL;
649262306a36Sopenharmony_ci	hdspm->playback_substream = NULL;
649362306a36Sopenharmony_ci
649462306a36Sopenharmony_ci	dev_dbg(card->dev, "Set defaults...\n");
649562306a36Sopenharmony_ci	err = snd_hdspm_set_defaults(hdspm);
649662306a36Sopenharmony_ci	if (err < 0)
649762306a36Sopenharmony_ci		return err;
649862306a36Sopenharmony_ci
649962306a36Sopenharmony_ci	dev_dbg(card->dev, "Update mixer controls...\n");
650062306a36Sopenharmony_ci	hdspm_update_simple_mixer_controls(hdspm);
650162306a36Sopenharmony_ci
650262306a36Sopenharmony_ci	dev_dbg(card->dev, "Initializing complete?\n");
650362306a36Sopenharmony_ci
650462306a36Sopenharmony_ci	err = snd_card_register(card);
650562306a36Sopenharmony_ci	if (err < 0) {
650662306a36Sopenharmony_ci		dev_err(card->dev, "error registering card\n");
650762306a36Sopenharmony_ci		return err;
650862306a36Sopenharmony_ci	}
650962306a36Sopenharmony_ci
651062306a36Sopenharmony_ci	dev_dbg(card->dev, "... yes now\n");
651162306a36Sopenharmony_ci
651262306a36Sopenharmony_ci	return 0;
651362306a36Sopenharmony_ci}
651462306a36Sopenharmony_ci
651562306a36Sopenharmony_cistatic int snd_hdspm_create(struct snd_card *card,
651662306a36Sopenharmony_ci			    struct hdspm *hdspm)
651762306a36Sopenharmony_ci{
651862306a36Sopenharmony_ci
651962306a36Sopenharmony_ci	struct pci_dev *pci = hdspm->pci;
652062306a36Sopenharmony_ci	int err;
652162306a36Sopenharmony_ci	unsigned long io_extent;
652262306a36Sopenharmony_ci
652362306a36Sopenharmony_ci	hdspm->irq = -1;
652462306a36Sopenharmony_ci	hdspm->card = card;
652562306a36Sopenharmony_ci
652662306a36Sopenharmony_ci	spin_lock_init(&hdspm->lock);
652762306a36Sopenharmony_ci	INIT_WORK(&hdspm->midi_work, hdspm_midi_work);
652862306a36Sopenharmony_ci
652962306a36Sopenharmony_ci	pci_read_config_word(hdspm->pci,
653062306a36Sopenharmony_ci			PCI_CLASS_REVISION, &hdspm->firmware_rev);
653162306a36Sopenharmony_ci
653262306a36Sopenharmony_ci	strcpy(card->mixername, "Xilinx FPGA");
653362306a36Sopenharmony_ci	strcpy(card->driver, "HDSPM");
653462306a36Sopenharmony_ci
653562306a36Sopenharmony_ci	switch (hdspm->firmware_rev) {
653662306a36Sopenharmony_ci	case HDSPM_RAYDAT_REV:
653762306a36Sopenharmony_ci		hdspm->io_type = RayDAT;
653862306a36Sopenharmony_ci		hdspm->card_name = "RME RayDAT";
653962306a36Sopenharmony_ci		hdspm->midiPorts = 2;
654062306a36Sopenharmony_ci		break;
654162306a36Sopenharmony_ci	case HDSPM_AIO_REV:
654262306a36Sopenharmony_ci		hdspm->io_type = AIO;
654362306a36Sopenharmony_ci		hdspm->card_name = "RME AIO";
654462306a36Sopenharmony_ci		hdspm->midiPorts = 1;
654562306a36Sopenharmony_ci		break;
654662306a36Sopenharmony_ci	case HDSPM_MADIFACE_REV:
654762306a36Sopenharmony_ci		hdspm->io_type = MADIface;
654862306a36Sopenharmony_ci		hdspm->card_name = "RME MADIface";
654962306a36Sopenharmony_ci		hdspm->midiPorts = 1;
655062306a36Sopenharmony_ci		break;
655162306a36Sopenharmony_ci	default:
655262306a36Sopenharmony_ci		if ((hdspm->firmware_rev == 0xf0) ||
655362306a36Sopenharmony_ci			((hdspm->firmware_rev >= 0xe6) &&
655462306a36Sopenharmony_ci					(hdspm->firmware_rev <= 0xea))) {
655562306a36Sopenharmony_ci			hdspm->io_type = AES32;
655662306a36Sopenharmony_ci			hdspm->card_name = "RME AES32";
655762306a36Sopenharmony_ci			hdspm->midiPorts = 2;
655862306a36Sopenharmony_ci		} else if ((hdspm->firmware_rev == 0xd2) ||
655962306a36Sopenharmony_ci			((hdspm->firmware_rev >= 0xc8)  &&
656062306a36Sopenharmony_ci				(hdspm->firmware_rev <= 0xcf))) {
656162306a36Sopenharmony_ci			hdspm->io_type = MADI;
656262306a36Sopenharmony_ci			hdspm->card_name = "RME MADI";
656362306a36Sopenharmony_ci			hdspm->midiPorts = 3;
656462306a36Sopenharmony_ci		} else {
656562306a36Sopenharmony_ci			dev_err(card->dev,
656662306a36Sopenharmony_ci				"unknown firmware revision %x\n",
656762306a36Sopenharmony_ci				hdspm->firmware_rev);
656862306a36Sopenharmony_ci			return -ENODEV;
656962306a36Sopenharmony_ci		}
657062306a36Sopenharmony_ci	}
657162306a36Sopenharmony_ci
657262306a36Sopenharmony_ci	err = pcim_enable_device(pci);
657362306a36Sopenharmony_ci	if (err < 0)
657462306a36Sopenharmony_ci		return err;
657562306a36Sopenharmony_ci
657662306a36Sopenharmony_ci	pci_set_master(hdspm->pci);
657762306a36Sopenharmony_ci
657862306a36Sopenharmony_ci	err = pcim_iomap_regions(pci, 1 << 0, "hdspm");
657962306a36Sopenharmony_ci	if (err < 0)
658062306a36Sopenharmony_ci		return err;
658162306a36Sopenharmony_ci
658262306a36Sopenharmony_ci	hdspm->port = pci_resource_start(pci, 0);
658362306a36Sopenharmony_ci	io_extent = pci_resource_len(pci, 0);
658462306a36Sopenharmony_ci	hdspm->iobase = pcim_iomap_table(pci)[0];
658562306a36Sopenharmony_ci	dev_dbg(card->dev, "remapped region (0x%lx) 0x%lx-0x%lx\n",
658662306a36Sopenharmony_ci			(unsigned long)hdspm->iobase, hdspm->port,
658762306a36Sopenharmony_ci			hdspm->port + io_extent - 1);
658862306a36Sopenharmony_ci
658962306a36Sopenharmony_ci	if (devm_request_irq(&pci->dev, pci->irq, snd_hdspm_interrupt,
659062306a36Sopenharmony_ci			     IRQF_SHARED, KBUILD_MODNAME, hdspm)) {
659162306a36Sopenharmony_ci		dev_err(card->dev, "unable to use IRQ %d\n", pci->irq);
659262306a36Sopenharmony_ci		return -EBUSY;
659362306a36Sopenharmony_ci	}
659462306a36Sopenharmony_ci
659562306a36Sopenharmony_ci	dev_dbg(card->dev, "use IRQ %d\n", pci->irq);
659662306a36Sopenharmony_ci
659762306a36Sopenharmony_ci	hdspm->irq = pci->irq;
659862306a36Sopenharmony_ci	card->sync_irq = hdspm->irq;
659962306a36Sopenharmony_ci
660062306a36Sopenharmony_ci	dev_dbg(card->dev, "kmalloc Mixer memory of %zd Bytes\n",
660162306a36Sopenharmony_ci		sizeof(*hdspm->mixer));
660262306a36Sopenharmony_ci	hdspm->mixer = devm_kzalloc(&pci->dev, sizeof(*hdspm->mixer), GFP_KERNEL);
660362306a36Sopenharmony_ci	if (!hdspm->mixer)
660462306a36Sopenharmony_ci		return -ENOMEM;
660562306a36Sopenharmony_ci
660662306a36Sopenharmony_ci	hdspm->port_names_in = NULL;
660762306a36Sopenharmony_ci	hdspm->port_names_out = NULL;
660862306a36Sopenharmony_ci
660962306a36Sopenharmony_ci	switch (hdspm->io_type) {
661062306a36Sopenharmony_ci	case AES32:
661162306a36Sopenharmony_ci		hdspm->ss_in_channels = hdspm->ss_out_channels = AES32_CHANNELS;
661262306a36Sopenharmony_ci		hdspm->ds_in_channels = hdspm->ds_out_channels = AES32_CHANNELS;
661362306a36Sopenharmony_ci		hdspm->qs_in_channels = hdspm->qs_out_channels = AES32_CHANNELS;
661462306a36Sopenharmony_ci
661562306a36Sopenharmony_ci		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
661662306a36Sopenharmony_ci			channel_map_aes32;
661762306a36Sopenharmony_ci		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
661862306a36Sopenharmony_ci			channel_map_aes32;
661962306a36Sopenharmony_ci		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
662062306a36Sopenharmony_ci			channel_map_aes32;
662162306a36Sopenharmony_ci		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
662262306a36Sopenharmony_ci			texts_ports_aes32;
662362306a36Sopenharmony_ci		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
662462306a36Sopenharmony_ci			texts_ports_aes32;
662562306a36Sopenharmony_ci		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
662662306a36Sopenharmony_ci			texts_ports_aes32;
662762306a36Sopenharmony_ci
662862306a36Sopenharmony_ci		hdspm->max_channels_out = hdspm->max_channels_in =
662962306a36Sopenharmony_ci			AES32_CHANNELS;
663062306a36Sopenharmony_ci		hdspm->port_names_in = hdspm->port_names_out =
663162306a36Sopenharmony_ci			texts_ports_aes32;
663262306a36Sopenharmony_ci		hdspm->channel_map_in = hdspm->channel_map_out =
663362306a36Sopenharmony_ci			channel_map_aes32;
663462306a36Sopenharmony_ci
663562306a36Sopenharmony_ci		break;
663662306a36Sopenharmony_ci
663762306a36Sopenharmony_ci	case MADI:
663862306a36Sopenharmony_ci	case MADIface:
663962306a36Sopenharmony_ci		hdspm->ss_in_channels = hdspm->ss_out_channels =
664062306a36Sopenharmony_ci			MADI_SS_CHANNELS;
664162306a36Sopenharmony_ci		hdspm->ds_in_channels = hdspm->ds_out_channels =
664262306a36Sopenharmony_ci			MADI_DS_CHANNELS;
664362306a36Sopenharmony_ci		hdspm->qs_in_channels = hdspm->qs_out_channels =
664462306a36Sopenharmony_ci			MADI_QS_CHANNELS;
664562306a36Sopenharmony_ci
664662306a36Sopenharmony_ci		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
664762306a36Sopenharmony_ci			channel_map_unity_ss;
664862306a36Sopenharmony_ci		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
664962306a36Sopenharmony_ci			channel_map_unity_ss;
665062306a36Sopenharmony_ci		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
665162306a36Sopenharmony_ci			channel_map_unity_ss;
665262306a36Sopenharmony_ci
665362306a36Sopenharmony_ci		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
665462306a36Sopenharmony_ci			texts_ports_madi;
665562306a36Sopenharmony_ci		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
665662306a36Sopenharmony_ci			texts_ports_madi;
665762306a36Sopenharmony_ci		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
665862306a36Sopenharmony_ci			texts_ports_madi;
665962306a36Sopenharmony_ci		break;
666062306a36Sopenharmony_ci
666162306a36Sopenharmony_ci	case AIO:
666262306a36Sopenharmony_ci		hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
666362306a36Sopenharmony_ci		hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
666462306a36Sopenharmony_ci		hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
666562306a36Sopenharmony_ci		hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS;
666662306a36Sopenharmony_ci		hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
666762306a36Sopenharmony_ci		hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
666862306a36Sopenharmony_ci
666962306a36Sopenharmony_ci		if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
667062306a36Sopenharmony_ci			dev_info(card->dev, "AEB input board found\n");
667162306a36Sopenharmony_ci			hdspm->ss_in_channels += 4;
667262306a36Sopenharmony_ci			hdspm->ds_in_channels += 4;
667362306a36Sopenharmony_ci			hdspm->qs_in_channels += 4;
667462306a36Sopenharmony_ci		}
667562306a36Sopenharmony_ci
667662306a36Sopenharmony_ci		if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) {
667762306a36Sopenharmony_ci			dev_info(card->dev, "AEB output board found\n");
667862306a36Sopenharmony_ci			hdspm->ss_out_channels += 4;
667962306a36Sopenharmony_ci			hdspm->ds_out_channels += 4;
668062306a36Sopenharmony_ci			hdspm->qs_out_channels += 4;
668162306a36Sopenharmony_ci		}
668262306a36Sopenharmony_ci
668362306a36Sopenharmony_ci		hdspm->channel_map_out_ss = channel_map_aio_out_ss;
668462306a36Sopenharmony_ci		hdspm->channel_map_out_ds = channel_map_aio_out_ds;
668562306a36Sopenharmony_ci		hdspm->channel_map_out_qs = channel_map_aio_out_qs;
668662306a36Sopenharmony_ci
668762306a36Sopenharmony_ci		hdspm->channel_map_in_ss = channel_map_aio_in_ss;
668862306a36Sopenharmony_ci		hdspm->channel_map_in_ds = channel_map_aio_in_ds;
668962306a36Sopenharmony_ci		hdspm->channel_map_in_qs = channel_map_aio_in_qs;
669062306a36Sopenharmony_ci
669162306a36Sopenharmony_ci		hdspm->port_names_in_ss = texts_ports_aio_in_ss;
669262306a36Sopenharmony_ci		hdspm->port_names_out_ss = texts_ports_aio_out_ss;
669362306a36Sopenharmony_ci		hdspm->port_names_in_ds = texts_ports_aio_in_ds;
669462306a36Sopenharmony_ci		hdspm->port_names_out_ds = texts_ports_aio_out_ds;
669562306a36Sopenharmony_ci		hdspm->port_names_in_qs = texts_ports_aio_in_qs;
669662306a36Sopenharmony_ci		hdspm->port_names_out_qs = texts_ports_aio_out_qs;
669762306a36Sopenharmony_ci
669862306a36Sopenharmony_ci		break;
669962306a36Sopenharmony_ci
670062306a36Sopenharmony_ci	case RayDAT:
670162306a36Sopenharmony_ci		hdspm->ss_in_channels = hdspm->ss_out_channels =
670262306a36Sopenharmony_ci			RAYDAT_SS_CHANNELS;
670362306a36Sopenharmony_ci		hdspm->ds_in_channels = hdspm->ds_out_channels =
670462306a36Sopenharmony_ci			RAYDAT_DS_CHANNELS;
670562306a36Sopenharmony_ci		hdspm->qs_in_channels = hdspm->qs_out_channels =
670662306a36Sopenharmony_ci			RAYDAT_QS_CHANNELS;
670762306a36Sopenharmony_ci
670862306a36Sopenharmony_ci		hdspm->max_channels_in = RAYDAT_SS_CHANNELS;
670962306a36Sopenharmony_ci		hdspm->max_channels_out = RAYDAT_SS_CHANNELS;
671062306a36Sopenharmony_ci
671162306a36Sopenharmony_ci		hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
671262306a36Sopenharmony_ci			channel_map_raydat_ss;
671362306a36Sopenharmony_ci		hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
671462306a36Sopenharmony_ci			channel_map_raydat_ds;
671562306a36Sopenharmony_ci		hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
671662306a36Sopenharmony_ci			channel_map_raydat_qs;
671762306a36Sopenharmony_ci		hdspm->channel_map_in = hdspm->channel_map_out =
671862306a36Sopenharmony_ci			channel_map_raydat_ss;
671962306a36Sopenharmony_ci
672062306a36Sopenharmony_ci		hdspm->port_names_in_ss = hdspm->port_names_out_ss =
672162306a36Sopenharmony_ci			texts_ports_raydat_ss;
672262306a36Sopenharmony_ci		hdspm->port_names_in_ds = hdspm->port_names_out_ds =
672362306a36Sopenharmony_ci			texts_ports_raydat_ds;
672462306a36Sopenharmony_ci		hdspm->port_names_in_qs = hdspm->port_names_out_qs =
672562306a36Sopenharmony_ci			texts_ports_raydat_qs;
672662306a36Sopenharmony_ci
672762306a36Sopenharmony_ci
672862306a36Sopenharmony_ci		break;
672962306a36Sopenharmony_ci
673062306a36Sopenharmony_ci	}
673162306a36Sopenharmony_ci
673262306a36Sopenharmony_ci	/* TCO detection */
673362306a36Sopenharmony_ci	switch (hdspm->io_type) {
673462306a36Sopenharmony_ci	case AIO:
673562306a36Sopenharmony_ci	case RayDAT:
673662306a36Sopenharmony_ci		if (hdspm_read(hdspm, HDSPM_statusRegister2) &
673762306a36Sopenharmony_ci				HDSPM_s2_tco_detect) {
673862306a36Sopenharmony_ci			hdspm->midiPorts++;
673962306a36Sopenharmony_ci			hdspm->tco = kzalloc(sizeof(*hdspm->tco), GFP_KERNEL);
674062306a36Sopenharmony_ci			if (hdspm->tco)
674162306a36Sopenharmony_ci				hdspm_tco_write(hdspm);
674262306a36Sopenharmony_ci
674362306a36Sopenharmony_ci			dev_info(card->dev, "AIO/RayDAT TCO module found\n");
674462306a36Sopenharmony_ci		} else {
674562306a36Sopenharmony_ci			hdspm->tco = NULL;
674662306a36Sopenharmony_ci		}
674762306a36Sopenharmony_ci		break;
674862306a36Sopenharmony_ci
674962306a36Sopenharmony_ci	case MADI:
675062306a36Sopenharmony_ci	case AES32:
675162306a36Sopenharmony_ci		if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
675262306a36Sopenharmony_ci			hdspm->midiPorts++;
675362306a36Sopenharmony_ci			hdspm->tco = kzalloc(sizeof(*hdspm->tco), GFP_KERNEL);
675462306a36Sopenharmony_ci			if (hdspm->tco)
675562306a36Sopenharmony_ci				hdspm_tco_write(hdspm);
675662306a36Sopenharmony_ci
675762306a36Sopenharmony_ci			dev_info(card->dev, "MADI/AES TCO module found\n");
675862306a36Sopenharmony_ci		} else {
675962306a36Sopenharmony_ci			hdspm->tco = NULL;
676062306a36Sopenharmony_ci		}
676162306a36Sopenharmony_ci		break;
676262306a36Sopenharmony_ci
676362306a36Sopenharmony_ci	default:
676462306a36Sopenharmony_ci		hdspm->tco = NULL;
676562306a36Sopenharmony_ci	}
676662306a36Sopenharmony_ci
676762306a36Sopenharmony_ci	/* texts */
676862306a36Sopenharmony_ci	switch (hdspm->io_type) {
676962306a36Sopenharmony_ci	case AES32:
677062306a36Sopenharmony_ci		if (hdspm->tco) {
677162306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_aes_tco;
677262306a36Sopenharmony_ci			hdspm->texts_autosync_items =
677362306a36Sopenharmony_ci				ARRAY_SIZE(texts_autosync_aes_tco);
677462306a36Sopenharmony_ci		} else {
677562306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_aes;
677662306a36Sopenharmony_ci			hdspm->texts_autosync_items =
677762306a36Sopenharmony_ci				ARRAY_SIZE(texts_autosync_aes);
677862306a36Sopenharmony_ci		}
677962306a36Sopenharmony_ci		break;
678062306a36Sopenharmony_ci
678162306a36Sopenharmony_ci	case MADI:
678262306a36Sopenharmony_ci		if (hdspm->tco) {
678362306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_madi_tco;
678462306a36Sopenharmony_ci			hdspm->texts_autosync_items = 4;
678562306a36Sopenharmony_ci		} else {
678662306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_madi;
678762306a36Sopenharmony_ci			hdspm->texts_autosync_items = 3;
678862306a36Sopenharmony_ci		}
678962306a36Sopenharmony_ci		break;
679062306a36Sopenharmony_ci
679162306a36Sopenharmony_ci	case MADIface:
679262306a36Sopenharmony_ci
679362306a36Sopenharmony_ci		break;
679462306a36Sopenharmony_ci
679562306a36Sopenharmony_ci	case RayDAT:
679662306a36Sopenharmony_ci		if (hdspm->tco) {
679762306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_raydat_tco;
679862306a36Sopenharmony_ci			hdspm->texts_autosync_items = 9;
679962306a36Sopenharmony_ci		} else {
680062306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_raydat;
680162306a36Sopenharmony_ci			hdspm->texts_autosync_items = 8;
680262306a36Sopenharmony_ci		}
680362306a36Sopenharmony_ci		break;
680462306a36Sopenharmony_ci
680562306a36Sopenharmony_ci	case AIO:
680662306a36Sopenharmony_ci		if (hdspm->tco) {
680762306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_aio_tco;
680862306a36Sopenharmony_ci			hdspm->texts_autosync_items = 6;
680962306a36Sopenharmony_ci		} else {
681062306a36Sopenharmony_ci			hdspm->texts_autosync = texts_autosync_aio;
681162306a36Sopenharmony_ci			hdspm->texts_autosync_items = 5;
681262306a36Sopenharmony_ci		}
681362306a36Sopenharmony_ci		break;
681462306a36Sopenharmony_ci
681562306a36Sopenharmony_ci	}
681662306a36Sopenharmony_ci
681762306a36Sopenharmony_ci	if (hdspm->io_type != MADIface) {
681862306a36Sopenharmony_ci		hdspm->serial = (hdspm_read(hdspm,
681962306a36Sopenharmony_ci				HDSPM_midiStatusIn0)>>8) & 0xFFFFFF;
682062306a36Sopenharmony_ci		/* id contains either a user-provided value or the default
682162306a36Sopenharmony_ci		 * NULL. If it's the default, we're safe to
682262306a36Sopenharmony_ci		 * fill card->id with the serial number.
682362306a36Sopenharmony_ci		 *
682462306a36Sopenharmony_ci		 * If the serial number is 0xFFFFFF, then we're dealing with
682562306a36Sopenharmony_ci		 * an old PCI revision that comes without a sane number. In
682662306a36Sopenharmony_ci		 * this case, we don't set card->id to avoid collisions
682762306a36Sopenharmony_ci		 * when running with multiple cards.
682862306a36Sopenharmony_ci		 */
682962306a36Sopenharmony_ci		if (!id[hdspm->dev] && hdspm->serial != 0xFFFFFF) {
683062306a36Sopenharmony_ci			snprintf(card->id, sizeof(card->id),
683162306a36Sopenharmony_ci				 "HDSPMx%06x", hdspm->serial);
683262306a36Sopenharmony_ci			snd_card_set_id(card, card->id);
683362306a36Sopenharmony_ci		}
683462306a36Sopenharmony_ci	}
683562306a36Sopenharmony_ci
683662306a36Sopenharmony_ci	dev_dbg(card->dev, "create alsa devices.\n");
683762306a36Sopenharmony_ci	err = snd_hdspm_create_alsa_devices(card, hdspm);
683862306a36Sopenharmony_ci	if (err < 0)
683962306a36Sopenharmony_ci		return err;
684062306a36Sopenharmony_ci
684162306a36Sopenharmony_ci	snd_hdspm_initialize_midi_flush(hdspm);
684262306a36Sopenharmony_ci
684362306a36Sopenharmony_ci	return 0;
684462306a36Sopenharmony_ci}
684562306a36Sopenharmony_ci
684662306a36Sopenharmony_ci
684762306a36Sopenharmony_cistatic void snd_hdspm_card_free(struct snd_card *card)
684862306a36Sopenharmony_ci{
684962306a36Sopenharmony_ci	struct hdspm *hdspm = card->private_data;
685062306a36Sopenharmony_ci
685162306a36Sopenharmony_ci	if (hdspm->port) {
685262306a36Sopenharmony_ci		cancel_work_sync(&hdspm->midi_work);
685362306a36Sopenharmony_ci
685462306a36Sopenharmony_ci		/* stop th audio, and cancel all interrupts */
685562306a36Sopenharmony_ci		hdspm->control_register &=
685662306a36Sopenharmony_ci		    ~(HDSPM_Start | HDSPM_AudioInterruptEnable |
685762306a36Sopenharmony_ci		      HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable |
685862306a36Sopenharmony_ci		      HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable);
685962306a36Sopenharmony_ci		hdspm_write(hdspm, HDSPM_controlRegister,
686062306a36Sopenharmony_ci			    hdspm->control_register);
686162306a36Sopenharmony_ci	}
686262306a36Sopenharmony_ci}
686362306a36Sopenharmony_ci
686462306a36Sopenharmony_ci
686562306a36Sopenharmony_cistatic int snd_hdspm_probe(struct pci_dev *pci,
686662306a36Sopenharmony_ci			   const struct pci_device_id *pci_id)
686762306a36Sopenharmony_ci{
686862306a36Sopenharmony_ci	static int dev;
686962306a36Sopenharmony_ci	struct hdspm *hdspm;
687062306a36Sopenharmony_ci	struct snd_card *card;
687162306a36Sopenharmony_ci	int err;
687262306a36Sopenharmony_ci
687362306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
687462306a36Sopenharmony_ci		return -ENODEV;
687562306a36Sopenharmony_ci	if (!enable[dev]) {
687662306a36Sopenharmony_ci		dev++;
687762306a36Sopenharmony_ci		return -ENOENT;
687862306a36Sopenharmony_ci	}
687962306a36Sopenharmony_ci
688062306a36Sopenharmony_ci	err = snd_devm_card_new(&pci->dev, index[dev], id[dev],
688162306a36Sopenharmony_ci				THIS_MODULE, sizeof(*hdspm), &card);
688262306a36Sopenharmony_ci	if (err < 0)
688362306a36Sopenharmony_ci		return err;
688462306a36Sopenharmony_ci
688562306a36Sopenharmony_ci	hdspm = card->private_data;
688662306a36Sopenharmony_ci	card->private_free = snd_hdspm_card_free;
688762306a36Sopenharmony_ci	hdspm->dev = dev;
688862306a36Sopenharmony_ci	hdspm->pci = pci;
688962306a36Sopenharmony_ci
689062306a36Sopenharmony_ci	err = snd_hdspm_create(card, hdspm);
689162306a36Sopenharmony_ci	if (err < 0)
689262306a36Sopenharmony_ci		goto error;
689362306a36Sopenharmony_ci
689462306a36Sopenharmony_ci	if (hdspm->io_type != MADIface) {
689562306a36Sopenharmony_ci		snprintf(card->shortname, sizeof(card->shortname), "%s_%x",
689662306a36Sopenharmony_ci			hdspm->card_name, hdspm->serial);
689762306a36Sopenharmony_ci		snprintf(card->longname, sizeof(card->longname),
689862306a36Sopenharmony_ci			 "%s S/N 0x%x at 0x%lx, irq %d",
689962306a36Sopenharmony_ci			 hdspm->card_name, hdspm->serial,
690062306a36Sopenharmony_ci			 hdspm->port, hdspm->irq);
690162306a36Sopenharmony_ci	} else {
690262306a36Sopenharmony_ci		snprintf(card->shortname, sizeof(card->shortname), "%s",
690362306a36Sopenharmony_ci			 hdspm->card_name);
690462306a36Sopenharmony_ci		snprintf(card->longname, sizeof(card->longname),
690562306a36Sopenharmony_ci			 "%s at 0x%lx, irq %d",
690662306a36Sopenharmony_ci			 hdspm->card_name, hdspm->port, hdspm->irq);
690762306a36Sopenharmony_ci	}
690862306a36Sopenharmony_ci
690962306a36Sopenharmony_ci	err = snd_card_register(card);
691062306a36Sopenharmony_ci	if (err < 0)
691162306a36Sopenharmony_ci		goto error;
691262306a36Sopenharmony_ci
691362306a36Sopenharmony_ci	pci_set_drvdata(pci, card);
691462306a36Sopenharmony_ci
691562306a36Sopenharmony_ci	dev++;
691662306a36Sopenharmony_ci	return 0;
691762306a36Sopenharmony_ci
691862306a36Sopenharmony_ci error:
691962306a36Sopenharmony_ci	snd_card_free(card);
692062306a36Sopenharmony_ci	return err;
692162306a36Sopenharmony_ci}
692262306a36Sopenharmony_ci
692362306a36Sopenharmony_cistatic struct pci_driver hdspm_driver = {
692462306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
692562306a36Sopenharmony_ci	.id_table = snd_hdspm_ids,
692662306a36Sopenharmony_ci	.probe = snd_hdspm_probe,
692762306a36Sopenharmony_ci};
692862306a36Sopenharmony_ci
692962306a36Sopenharmony_cimodule_pci_driver(hdspm_driver);
6930