Commit 3cee5a60 authored by Remy Bruno's avatar Remy Bruno Committed by Jaroslav Kysela

[ALSA] hdspm: Add support for AES32

Add support for AES32. Difference between MADI and AES32 is done
through revision. Master support is not finished for now (RME so-called DDS
feature is not supported yet)
Signed-off-by: default avatarRemy Bruno <remy.bruno@trinnov.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent b3b9c1cb
......@@ -6,6 +6,8 @@
* code based on hdsp.c Paul Davis
* Marcus Andersson
* Thomas Charbonnel
* Modified 2006-06-01 for AES32 support by Remy Bruno
* <remy.bruno@trinnov.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -77,7 +79,8 @@ MODULE_PARM_DESC(enable_monitor,
MODULE_AUTHOR
("Winfried Ritsch <ritsch_AT_iem.at>, Paul Davis <paul@linuxaudiosystems.com>, "
"Marcus Andersson, Thomas Charbonnel <thomas@undata.org>");
"Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
"Remy Bruno <remy.bruno@trinnov.com>");
MODULE_DESCRIPTION("RME HDSPM");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
......@@ -107,7 +110,12 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
/* --- Read registers. ---
These are defined as byte-offsets from the iobase value */
#define HDSPM_statusRegister 0
#define HDSPM_statusRegister2 96
/*#define HDSPM_statusRegister2 96 */
/* after RME Windows driver sources, status2 is 4-byte word # 48 = word at
* offset 192, for AES32 *and* MADI
* => need to check that offset 192 is working on MADI */
#define HDSPM_statusRegister2 192
#define HDSPM_timecodeRegister 128
#define HDSPM_midiDataIn0 360
#define HDSPM_midiDataIn1 364
......@@ -140,37 +148,50 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_Frequency0 (1<<6) /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */
#define HDSPM_Frequency1 (1<<7) /* 0=32kHz/64kHz */
#define HDSPM_DoubleSpeed (1<<8) /* 0=normal speed, 1=double speed */
#define HDSPM_QuadSpeed (1<<31) /* quad speed bit, not implemented now */
#define HDSPM_QuadSpeed (1<<31) /* quad speed bit */
#define HDSPM_Professional (1<<9) /* Professional */ /* AES32 ONLY */
#define HDSPM_TX_64ch (1<<10) /* Output 64channel MODE=1,
56channelMODE=0 */
56channelMODE=0 */ /* MADI ONLY*/
#define HDSPM_Emphasis (1<<10) /* Emphasis */ /* AES32 ONLY */
#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode,
0=off, 1=on */
0=off, 1=on */ /* MADI ONLY */
#define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */
#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */ /* MADI ONLY*/
#define HDSPM_InputSelect1 (1<<15) /* should be 0 */
#define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */
#define HDSPM_SyncRef1 (1<<17) /* should be 0 */
#define HDSPM_SyncRef1 (1<<17) /* for AES32: SyncRefN codes the AES # */
#define HDSPM_SyncRef2 (1<<13)
#define HDSPM_SyncRef3 (1<<25)
#define HDSPM_SMUX (1<<18) /* Frame ??? */ /* MADI ONY */
#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use
AES additional bits in
lower 5 Audiodatabits ??? */
#define HDSPM_taxi_reset (1<<20) /* ??? */ /* MADI ONLY ? */
#define HDSPM_WCK48 (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
#define HDSPM_Midi0InterruptEnable (1<<22)
#define HDSPM_Midi1InterruptEnable (1<<23)
#define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
#define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
#define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
#define HDSPM_QS_QuadWire (1<<28) /* AES32 ONLY */
#define HDSPM_wclk_sel (1<<30)
/* --- bit helper defines */
#define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1)
#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|HDSPM_DoubleSpeed|HDSPM_QuadSpeed)
#define HDSPM_InputMask (HDSPM_InputSelect0|HDSPM_InputSelect1)
#define HDSPM_InputOptical 0
#define HDSPM_InputCoaxial (HDSPM_InputSelect0)
#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1)
#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|HDSPM_SyncRef2|HDSPM_SyncRef3)
#define HDSPM_SyncRef_Word 0
#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0)
......@@ -183,6 +204,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_Frequency64KHz (HDSPM_DoubleSpeed|HDSPM_Frequency0)
#define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
#define HDSPM_Frequency96KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1|HDSPM_Frequency0)
#define HDSPM_Frequency128KHz (HDSPM_QuadSpeed|HDSPM_Frequency0)
#define HDSPM_Frequency176_4KHz (HDSPM_QuadSpeed|HDSPM_Frequency1)
#define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|HDSPM_Frequency0)
/* --- for internal discrimination */
#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */
......@@ -229,7 +253,8 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_BIGENDIAN_MODE (1<<9)
#define HDSPM_RD_MULTIPLE (1<<10)
/* --- Status Register bits --- */
/* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and
that do not conflict with specific bits for AES32 seem to be valid also for the AES32 */
#define HDSPM_audioIRQPending (1<<0) /* IRQ is high and pending */
#define HDSPM_RX_64ch (1<<1) /* Input 64chan. MODE=1, 56chn. MODE=0 */
#define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 (like inp0) */
......@@ -263,7 +288,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
#define HDSPM_madiFreq192 (HDSPM_madiFreq3|HDSPM_madiFreq0)
/* Status2 Register bits */
/* Status2 Register bits */ /* MADI ONLY */
#define HDSPM_version0 (1<<0) /* not realy defined but I guess */
#define HDSPM_version1 (1<<1) /* in former cards it was ??? */
......@@ -297,6 +322,56 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
#define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0)
#define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2)
/*
For AES32, bits for status, status2 and timecode are different
*/
/* status */
#define HDSPM_AES32_wcLock 0x0200000
#define HDSPM_AES32_wcFreq_bit 22
/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
HDSPM_bit2freq */
#define HDSPM_AES32_syncref_bit 16
/* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
#define HDSPM_AES32_AUTOSYNC_FROM_WORD 0
#define HDSPM_AES32_AUTOSYNC_FROM_AES1 1
#define HDSPM_AES32_AUTOSYNC_FROM_AES2 2
#define HDSPM_AES32_AUTOSYNC_FROM_AES3 3
#define HDSPM_AES32_AUTOSYNC_FROM_AES4 4
#define HDSPM_AES32_AUTOSYNC_FROM_AES5 5
#define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
#define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
#define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
#define HDSPM_AES32_AUTOSYNC_FROM_NONE -1
/* status2 */
/* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
#define HDSPM_LockAES 0x80
#define HDSPM_LockAES1 0x80
#define HDSPM_LockAES2 0x40
#define HDSPM_LockAES3 0x20
#define HDSPM_LockAES4 0x10
#define HDSPM_LockAES5 0x8
#define HDSPM_LockAES6 0x4
#define HDSPM_LockAES7 0x2
#define HDSPM_LockAES8 0x1
/*
Timecode
After windows driver sources, bits 4*i to 4*i+3 give the input frequency on
AES i+1
bits 3210
0001 32kHz
0010 44.1kHz
0011 48kHz
0100 64kHz
0101 88.2kHz
0110 96kHz
0111 128kHz
1000 176.4kHz
1001 192kHz
NB: Timecode register doesn't seem to work on AES32 card revision 230
*/
/* Mixer Values */
#define UNITY_GAIN 32768 /* = 65536/2 */
#define MINUS_INFINITY_GAIN 0
......@@ -314,10 +389,14 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
size is the same regardless of the number of channels, and
also the latency to use.
for one direction !!!
=> need to mupltiply by 2!!
*/
#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
#define HDSPM_DMA_AREA_BYTES (2 * HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
/* revisions >= 230 indicate AES32 card */
#define HDSPM_AESREVISION 230
struct hdspm_midi {
struct hdspm *hdspm;
int id;
......@@ -336,7 +415,9 @@ struct hdspm {
struct snd_pcm_substream *playback_substream; /* and/or capture stream */
char *card_name; /* for procinfo */
unsigned short firmware_rev; /* dont know if relevant */
unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
unsigned char is_aes32; /* indicates if card is AES32 */
int precise_ptr; /* use precise pointers, to be tested */
int monitor_outs; /* set up monitoring outs init flag */
......@@ -453,6 +534,15 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm);
static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf,
unsigned int reg, int channels);
static inline int HDSPM_bit2freq(int n)
{
static int bit2freq_tab[] = { 0, 32000, 44100, 48000, 64000, 88200,
96000, 128000, 176400, 192000 };
if (n < 1 || n > 9)
return 0;
return bit2freq_tab[n];
}
/* Write/read to/from HDSPM with Adresses in Bytes
not words but only 32Bit writes are allowed */
......@@ -544,6 +634,24 @@ static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm)
/* check for external sample rate */
static inline int hdspm_external_sample_rate(struct hdspm * hdspm)
{
if (hdspm->is_aes32) {
unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
unsigned int timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
int syncref = hdspm_autosync_ref(hdspm);
if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD &&
status & HDSPM_AES32_wcLock)
return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF);
if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 &&
syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
status2 & (HDSPM_LockAES >>
(syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
return HDSPM_bit2freq((timecode >>
(4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
return 0;
} else {
unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
unsigned int rate_bits;
......@@ -582,8 +690,8 @@ static inline int hdspm_external_sample_rate(struct hdspm * hdspm)
}
/* if rate detected and Syncref is Word than have it, word has priority to MADI */
if (rate != 0
&& (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
if (rate != 0 &&
(status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
return rate;
/* maby a madi input (which is taken if sel sync is madi) */
......@@ -624,6 +732,7 @@ static inline int hdspm_external_sample_rate(struct hdspm * hdspm)
}
}
return rate;
}
}
/* Latency function */
......@@ -676,7 +785,8 @@ static inline void hdspm_silence_playback(struct hdspm * hdspm)
int n = hdspm->period_bytes;
void *buf = hdspm->playback_buffer;
snd_assert(buf != NULL, return);
if (buf == NULL)
return;
for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
memset(buf, 0, n);
......@@ -716,6 +826,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
int current_rate;
int rate_bits;
int not_set = 0;
int is_single, is_double, is_quad;
/* ASSUMPTION: hdspm->lock is either set, or there is no need for
it (e.g. during module initialization).
......@@ -766,43 +877,56 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
changes in the read/write routines.
*/
is_single = (current_rate <= 48000);
is_double = (current_rate > 48000 && current_rate <= 96000);
is_quad = (current_rate > 96000);
switch (rate) {
case 32000:
if (current_rate > 48000) {
if (!is_single)
reject_if_open = 1;
}
rate_bits = HDSPM_Frequency32KHz;
break;
case 44100:
if (current_rate > 48000) {
if (!is_single)
reject_if_open = 1;
}
rate_bits = HDSPM_Frequency44_1KHz;
break;
case 48000:
if (current_rate > 48000) {
if (!is_single)
reject_if_open = 1;
}
rate_bits = HDSPM_Frequency48KHz;
break;
case 64000:
if (current_rate <= 48000) {
if (!is_double)
reject_if_open = 1;
}
rate_bits = HDSPM_Frequency64KHz;
break;
case 88200:
if (current_rate <= 48000) {
if (!is_double)
reject_if_open = 1;
}
rate_bits = HDSPM_Frequency88_2KHz;
break;
case 96000:
if (current_rate <= 48000) {
if (!is_double)
reject_if_open = 1;
}
rate_bits = HDSPM_Frequency96KHz;
break;
case 128000:
if (!is_quad)
reject_if_open = 1;
rate_bits = HDSPM_Frequency128KHz;
break;
case 176400:
if (!is_quad)
reject_if_open = 1;
rate_bits = HDSPM_Frequency176_4KHz;
break;
case 192000:
if (!is_quad)
reject_if_open = 1;
rate_bits = HDSPM_Frequency192KHz;
break;
default:
return -EINVAL;
}
......@@ -819,7 +943,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
hdspm->control_register |= rate_bits;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
if (rate > 64000)
if (rate > 96000 /* 64000*/)
hdspm->channel_map = channel_map_madi_qs;
else if (rate > 48000)
hdspm->channel_map = channel_map_madi_ds;
......@@ -1455,12 +1579,28 @@ static int hdspm_pref_sync_ref(struct hdspm * hdspm)
/* Notice that this looks at the requested sync source,
not the one actually in use.
*/
if (hdspm->is_aes32) {
switch (hdspm->control_register & HDSPM_SyncRefMask) {
/* number gives AES index, except for 0 which
corresponds to WordClock */
case 0: return 0;
case HDSPM_SyncRef0: return 1;
case HDSPM_SyncRef1: return 2;
case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3;
case HDSPM_SyncRef2: return 4;
case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5;
case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6;
case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7;
case HDSPM_SyncRef3: return 8;
}
} else {
switch (hdspm->control_register & HDSPM_SyncRefMask) {
case HDSPM_SyncRef_Word:
return HDSPM_SYNC_FROM_WORD;
case HDSPM_SyncRef_MADI:
return HDSPM_SYNC_FROM_MADI;
}
}
return HDSPM_SYNC_FROM_WORD;
}
......@@ -1469,6 +1609,39 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
{
hdspm->control_register &= ~HDSPM_SyncRefMask;
if (hdspm->is_aes32) {
switch (pref) {
case 0:
hdspm->control_register |= 0;
break;
case 1:
hdspm->control_register |= HDSPM_SyncRef0;
break;
case 2:
hdspm->control_register |= HDSPM_SyncRef1;
break;
case 3:
hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0;
break;
case 4:
hdspm->control_register |= HDSPM_SyncRef2;
break;
case 5:
hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0;
break;
case 6:
hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1;
break;
case 7:
hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
break;
case 8:
hdspm->control_register |= HDSPM_SyncRef3;
break;
default:
return -1;
}
} else {
switch (pref) {
case HDSPM_SYNC_FROM_MADI:
hdspm->control_register |= HDSPM_SyncRef_MADI;
......@@ -1479,6 +1652,7 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
default:
return -1;
}
}
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
......@@ -1486,6 +1660,23 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
if (hdspm->is_aes32) {
static char *texts[] = { "Word", "AES1", "AES2", "AES3",
"AES4", "AES5", "AES6", "AES7", "AES8" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 9;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
} else {
static char *texts[] = { "Word", "MADI" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
......@@ -1498,6 +1689,7 @@ static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
}
return 0;
}
......@@ -1517,7 +1709,7 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
int change, max;
unsigned int val;
max = 2;
max = hdspm->is_aes32 ? 9 : 2;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
......@@ -1542,30 +1734,53 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
static int hdspm_autosync_ref(struct hdspm * hdspm)
{
if (hdspm->is_aes32) {
unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
if (syncref == 0)
return HDSPM_AES32_AUTOSYNC_FROM_WORD;
if (syncref <= 8)
return syncref;
return HDSPM_AES32_AUTOSYNC_FROM_NONE;
} else {
/* This looks at the autosync selected sync reference */
unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
switch (status2 & HDSPM_SelSyncRefMask) {
case HDSPM_SelSyncRef_WORD:
return HDSPM_AUTOSYNC_FROM_WORD;
case HDSPM_SelSyncRef_MADI:
return HDSPM_AUTOSYNC_FROM_MADI;
case HDSPM_SelSyncRef_NVALID:
return HDSPM_AUTOSYNC_FROM_NONE;
default:
return 0;
}
return 0;
}
}
static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
if (hdspm->is_aes32) {
static char *texts[] = { "WordClock", "AES1", "AES2", "AES3",
"AES4", "AES5", "AES6", "AES7", "AES8", "None"};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 10;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
}
else
{
static char *texts[] = { "WordClock", "MADI", "None" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
......@@ -1576,6 +1791,7 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
}
return 0;
}
......@@ -1841,61 +2057,53 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol,
return change;
}
#define HDSPM_INPUT_SELECT(xname, xindex) \
#define HDSPM_EMPHASIS(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_hdspm_info_input_select, \
.get = snd_hdspm_get_input_select, \
.put = snd_hdspm_put_input_select \
.info = snd_hdspm_info_emphasis, \
.get = snd_hdspm_get_emphasis, \
.put = snd_hdspm_put_emphasis \
}
static int hdspm_input_select(struct hdspm * hdspm)
static int hdspm_emphasis(struct hdspm * hdspm)
{
return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
return (hdspm->control_register & HDSPM_Emphasis) ? 1 : 0;
}
static int hdspm_set_input_select(struct hdspm * hdspm, int out)
static int hdspm_set_emphasis(struct hdspm * hdspm, int emp)
{
if (out)
hdspm->control_register |= HDSPM_InputSelect0;
if (emp)
hdspm->control_register |= HDSPM_Emphasis;
else
hdspm->control_register &= ~HDSPM_InputSelect0;
hdspm->control_register &= ~HDSPM_Emphasis;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
static int snd_hdspm_info_emphasis(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "optical", "coaxial" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
static int snd_hdspm_get_emphasis(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
ucontrol->value.enumerated.item[0] = hdspm_emphasis(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
......@@ -1906,169 +2114,523 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
change = (int) val != hdspm_input_select(hdspm);
hdspm_set_input_select(hdspm, val);
change = (int) val != hdspm_emphasis(hdspm);
hdspm_set_emphasis(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
/* Simple Mixer
deprecated since to much faders ???
MIXER interface says output (source, destination, value)
where source > MAX_channels are playback channels
on MADICARD
- playback mixer matrix: [channelout+64] [output] [value]
- input(thru) mixer matrix: [channelin] [output] [value]
(better do 2 kontrols for seperation ?)
*/
#define HDSPM_MIXER(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
#define HDSPM_DOLBY(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.device = 0, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_hdspm_info_mixer, \
.get = snd_hdspm_get_mixer, \
.put = snd_hdspm_put_mixer \
.info = snd_hdspm_info_dolby, \
.get = snd_hdspm_get_dolby, \
.put = snd_hdspm_put_dolby \
}
static int snd_hdspm_info_mixer(struct snd_kcontrol *kcontrol,
static int hdspm_dolby(struct hdspm * hdspm)
{
return (hdspm->control_register & HDSPM_Dolby) ? 1 : 0;
}
static int hdspm_set_dolby(struct hdspm * hdspm, int dol)
{
if (dol)
hdspm->control_register |= HDSPM_Dolby;
else
hdspm->control_register &= ~HDSPM_Dolby;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
static int snd_hdspm_info_dolby(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 3;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 65535;
uinfo->value.integer.step = 1;
uinfo->value.integer.max = 1;
return 0;
}
static int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
static int snd_hdspm_get_dolby(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int source;
int destination;
source = ucontrol->value.integer.value[0];
if (source < 0)
source = 0;
else if (source >= 2 * HDSPM_MAX_CHANNELS)
source = 2 * HDSPM_MAX_CHANNELS - 1;
destination = ucontrol->value.integer.value[1];
if (destination < 0)
destination = 0;
else if (destination >= HDSPM_MAX_CHANNELS)
destination = HDSPM_MAX_CHANNELS - 1;
spin_lock_irq(&hdspm->lock);
if (source >= HDSPM_MAX_CHANNELS)
ucontrol->value.integer.value[2] =
hdspm_read_pb_gain(hdspm, destination,
source - HDSPM_MAX_CHANNELS);
else
ucontrol->value.integer.value[2] =
hdspm_read_in_gain(hdspm, destination, source);
ucontrol->value.enumerated.item[0] = hdspm_dolby(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
int source;
int destination;
int gain;
unsigned int val;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
source = ucontrol->value.integer.value[0];
destination = ucontrol->value.integer.value[1];
if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
return -1;
if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
return -1;
gain = ucontrol->value.integer.value[2];
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
if (source >= HDSPM_MAX_CHANNELS)
change = gain != hdspm_read_pb_gain(hdspm, destination,
source -
HDSPM_MAX_CHANNELS);
else
change =
gain != hdspm_read_in_gain(hdspm, destination, source);
if (change) {
if (source >= HDSPM_MAX_CHANNELS)
hdspm_write_pb_gain(hdspm, destination,
source - HDSPM_MAX_CHANNELS,
gain);
else
hdspm_write_in_gain(hdspm, destination, source,
gain);
}
change = (int) val != hdspm_dolby(hdspm);
hdspm_set_dolby(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
/* The simple mixer control(s) provide gain control for the
basic 1:1 mappings of playback streams to output
streams.
*/
#define HDSPM_PLAYBACK_MIXER \
#define HDSPM_PROFESSIONAL(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_hdspm_info_playback_mixer, \
.get = snd_hdspm_get_playback_mixer, \
.put = snd_hdspm_put_playback_mixer \
.name = xname, \
.index = xindex, \
.info = snd_hdspm_info_professional, \
.get = snd_hdspm_get_professional, \
.put = snd_hdspm_put_professional \
}
static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
static int hdspm_professional(struct hdspm * hdspm)
{
return (hdspm->control_register & HDSPM_Professional) ? 1 : 0;
}
static int hdspm_set_professional(struct hdspm * hdspm, int dol)
{
if (dol)
hdspm->control_register |= HDSPM_Professional;
else
hdspm->control_register &= ~HDSPM_Professional;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
static int snd_hdspm_info_professional(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 65536;
uinfo->value.integer.step = 1;
uinfo->value.integer.max = 1;
return 0;
}
static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
static int snd_hdspm_get_professional(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int channel;
int mapped_channel;
channel = ucontrol->id.index - 1;
snd_assert(channel >= 0
|| channel < HDSPM_MAX_CHANNELS, return -EINVAL);
if ((mapped_channel = hdspm->channel_map[channel]) < 0)
return -EINVAL;
spin_lock_irq(&hdspm->lock);
ucontrol->value.integer.value[0] =
hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel);
ucontrol->value.enumerated.item[0] = hdspm_professional(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_put_professional(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
change = (int) val != hdspm_professional(hdspm);
hdspm_set_professional(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
#define HDSPM_INPUT_SELECT(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_hdspm_info_input_select, \
.get = snd_hdspm_get_input_select, \
.put = snd_hdspm_put_input_select \
}
static int hdspm_input_select(struct hdspm * hdspm)
{
return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
}
static int hdspm_set_input_select(struct hdspm * hdspm, int out)
{
if (out)
hdspm->control_register |= HDSPM_InputSelect0;
else
hdspm->control_register &= ~HDSPM_InputSelect0;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "optical", "coaxial" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int snd_hdspm_get_input_select(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
change = (int) val != hdspm_input_select(hdspm);
hdspm_set_input_select(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
#define HDSPM_DS_WIRE(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_hdspm_info_ds_wire, \
.get = snd_hdspm_get_ds_wire, \
.put = snd_hdspm_put_ds_wire \
}
static int hdspm_ds_wire(struct hdspm * hdspm)
{
return (hdspm->control_register & HDSPM_DS_DoubleWire) ? 1 : 0;
}
static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
{
if (ds)
hdspm->control_register |= HDSPM_DS_DoubleWire;
else
hdspm->control_register &= ~HDSPM_DS_DoubleWire;
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int snd_hdspm_get_ds_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_ds_wire(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdspm->lock);
change = (int) val != hdspm_ds_wire(hdspm);
hdspm_set_ds_wire(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
#define HDSPM_QS_WIRE(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_hdspm_info_qs_wire, \
.get = snd_hdspm_get_qs_wire, \
.put = snd_hdspm_put_qs_wire \
}
static int hdspm_qs_wire(struct hdspm * hdspm)
{
if (hdspm->control_register & HDSPM_QS_DoubleWire)
return 1;
if (hdspm->control_register & HDSPM_QS_QuadWire)
return 2;
return 0;
}
static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
{
hdspm->control_register &= ~(HDSPM_QS_DoubleWire | HDSPM_QS_QuadWire);
switch (mode) {
case 0:
break;
case 1:
hdspm->control_register |= HDSPM_QS_DoubleWire;
break;
case 2:
hdspm->control_register |= HDSPM_QS_QuadWire;
break;
}
hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
return 0;
}
static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double", "Quad" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int snd_hdspm_get_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdspm->lock);
ucontrol->value.enumerated.item[0] = hdspm_qs_wire(hdspm);
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
int val;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
val = ucontrol->value.integer.value[0];
if (val < 0)
val = 0;
if (val > 2)
val = 2;
spin_lock_irq(&hdspm->lock);
change = (int) val != hdspm_qs_wire(hdspm);
hdspm_set_qs_wire(hdspm, val);
spin_unlock_irq(&hdspm->lock);
return change;
}
/* Simple Mixer
deprecated since to much faders ???
MIXER interface says output (source, destination, value)
where source > MAX_channels are playback channels
on MADICARD
- playback mixer matrix: [channelout+64] [output] [value]
- input(thru) mixer matrix: [channelin] [output] [value]
(better do 2 kontrols for seperation ?)
*/
#define HDSPM_MIXER(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
.name = xname, \
.index = xindex, \
.device = 0, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_hdspm_info_mixer, \
.get = snd_hdspm_get_mixer, \
.put = snd_hdspm_put_mixer \
}
static int snd_hdspm_info_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 3;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 65535;
uinfo->value.integer.step = 1;
return 0;
}
static int snd_hdspm_get_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int source;
int destination;
source = ucontrol->value.integer.value[0];
if (source < 0)
source = 0;
else if (source >= 2 * HDSPM_MAX_CHANNELS)
source = 2 * HDSPM_MAX_CHANNELS - 1;
destination = ucontrol->value.integer.value[1];
if (destination < 0)
destination = 0;
else if (destination >= HDSPM_MAX_CHANNELS)
destination = HDSPM_MAX_CHANNELS - 1;
spin_lock_irq(&hdspm->lock);
if (source >= HDSPM_MAX_CHANNELS)
ucontrol->value.integer.value[2] =
hdspm_read_pb_gain(hdspm, destination,
source - HDSPM_MAX_CHANNELS);
else
ucontrol->value.integer.value[2] =
hdspm_read_in_gain(hdspm, destination, source);
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int change;
int source;
int destination;
int gain;
if (!snd_hdspm_use_is_exclusive(hdspm))
return -EBUSY;
source = ucontrol->value.integer.value[0];
destination = ucontrol->value.integer.value[1];
if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
return -1;
if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
return -1;
gain = ucontrol->value.integer.value[2];
spin_lock_irq(&hdspm->lock);
if (source >= HDSPM_MAX_CHANNELS)
change = gain != hdspm_read_pb_gain(hdspm, destination,
source -
HDSPM_MAX_CHANNELS);
else
change =
gain != hdspm_read_in_gain(hdspm, destination, source);
if (change) {
if (source >= HDSPM_MAX_CHANNELS)
hdspm_write_pb_gain(hdspm, destination,
source - HDSPM_MAX_CHANNELS,
gain);
else
hdspm_write_in_gain(hdspm, destination, source,
gain);
}
spin_unlock_irq(&hdspm->lock);
return change;
}
/* The simple mixer control(s) provide gain control for the
basic 1:1 mappings of playback streams to output
streams.
*/
#define HDSPM_PLAYBACK_MIXER \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_hdspm_info_playback_mixer, \
.get = snd_hdspm_get_playback_mixer, \
.put = snd_hdspm_put_playback_mixer \
}
static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 65536;
uinfo->value.integer.step = 1;
return 0;
}
static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
int channel;
int mapped_channel;
channel = ucontrol->id.index - 1;
snd_assert(channel >= 0
|| channel < HDSPM_MAX_CHANNELS, return -EINVAL);
if ((mapped_channel = hdspm->channel_map[channel]) < 0)
return -EINVAL;
spin_lock_irq(&hdspm->lock);
ucontrol->value.integer.value[0] =
hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel);
spin_unlock_irq(&hdspm->lock);
/* snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, value %d\n",
ucontrol->id.index, channel, mapped_channel, ucontrol->value.integer.value[0]);
*/
......@@ -2135,6 +2697,15 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
static int hdspm_wc_sync_check(struct hdspm * hdspm)
{
if (hdspm->is_aes32) {
int status = hdspm_read(hdspm, HDSPM_statusRegister);
if (status & HDSPM_AES32_wcLock) {
/* I don't know how to differenciate sync from lock.
Doing as if sync for now */
return 2;
}
return 0;
} else {
int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
if (status2 & HDSPM_wcLock) {
if (status2 & HDSPM_wcSync)
......@@ -2143,6 +2714,7 @@ static int hdspm_wc_sync_check(struct hdspm * hdspm)
return 1;
}
return 0;
}
}
static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol,
......@@ -2188,9 +2760,43 @@ static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol,
}
#define HDSPM_AES_SYNC_CHECK(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_hdspm_info_sync_check, \
.get = snd_hdspm_get_aes_sync_check \
}
static int hdspm_aes_sync_check(struct hdspm * hdspm, int idx)
{
int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
if (status2 & (HDSPM_LockAES >> idx)) {
/* I don't know how to differenciate sync from lock.
Doing as if sync for now */
return 2;
}
return 0;
}
static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int offset;
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
offset = ucontrol->id.index - 1;
if (offset < 0 || offset >= 8)
return -EINVAL;
ucontrol->value.enumerated.item[0] =
hdspm_aes_sync_check(hdspm, offset);
return 0;
}
static struct snd_kcontrol_new snd_hdspm_controls[] = {
static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
HDSPM_MIXER("Mixer", 0),
/* 'Sample Clock Source' complies with the alsa control naming scheme */
......@@ -2211,6 +2817,29 @@ static struct snd_kcontrol_new snd_hdspm_controls[] = {
HDSPM_INPUT_SELECT("Input Select", 0),
};
static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
HDSPM_MIXER("Mixer", 0),
/* 'Sample Clock Source' complies with the alsa control naming scheme */
HDSPM_CLOCK_SOURCE("Sample Clock Source", 0),
HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
/* 'External Rate' complies with the alsa control naming scheme */
HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0),
/* HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */
HDSPM_LINE_OUT("Line Out", 0),
HDSPM_EMPHASIS("Emphasis", 0),
HDSPM_DOLBY("Non Audio", 0),
HDSPM_PROFESSIONAL("Professional", 0),
HDSPM_C_TMS("Clear Track Marker", 0),
HDSPM_DS_WIRE("Double Speed Wire Mode", 0),
HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
};
static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
......@@ -2245,20 +2874,40 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm
struct snd_kcontrol *kctl;
/* add control list first */
for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls); idx++) {
if ((err =
snd_ctl_add(card, kctl =
snd_ctl_new1(&snd_hdspm_controls[idx],
hdspm))) < 0) {
if (hdspm->is_aes32) {
struct snd_kcontrol_new aes_sync_ctl =
HDSPM_AES_SYNC_CHECK("AES Lock Status", 0);
for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32);
idx++) {
err = snd_ctl_add(card,
snd_ctl_new1(&snd_hdspm_controls_aes32[idx],
hdspm));
if (err < 0)
return err;
}
for (idx = 1; idx <= 8; idx++) {
aes_sync_ctl.index = idx;
err = snd_ctl_add(card,
snd_ctl_new1(&aes_sync_ctl, hdspm));
if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi);
idx++) {
err = snd_ctl_add(card,
snd_ctl_new1(&snd_hdspm_controls_madi[idx],
hdspm));
if (err < 0)
return err;
}
}
/* Channel playback mixer as default control
Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats too big for any alsamixer
they are accesible via special IOCTL on hwdep
and the mixer 2dimensional mixer control */
Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats too big for any alsamixer
they are accesible via special IOCTL on hwdep
and the mixer 2dimensional mixer control */
snd_hdspm_playback_mixer.name = "Chn";
limit = HDSPM_MAX_CHANNELS;
......@@ -2289,7 +2938,8 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm
------------------------------------------------------------*/
static void
snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer)
snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
struct hdspm *hdspm = (struct hdspm *) entry->private_data;
unsigned int status;
......@@ -2420,11 +3070,10 @@ snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe
clock_source = "Error";
}
snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
if (!(hdspm->control_register & HDSPM_ClockModeMaster))
system_clock_mode = "Slave";
} else {
else
system_clock_mode = "Master";
}
snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
switch (hdspm_pref_sync_ref(hdspm)) {
......@@ -2484,13 +3133,213 @@ snd_hdspm_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe
snd_iprintf(buffer, "\n");
}
static void
snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
struct hdspm *hdspm = (struct hdspm *) entry->private_data;
unsigned int status;
unsigned int status2;
unsigned int timecode;
int pref_syncref;
char *autosync_ref;
char *system_clock_mode;
char *clock_source;
int x;
status = hdspm_read(hdspm, HDSPM_statusRegister);
status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n",
hdspm->card_name, hdspm->card->number + 1,
hdspm->firmware_rev);
snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
snd_iprintf(buffer, "--- System ---\n");
snd_iprintf(buffer,
"IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
status & HDSPM_audioIRQPending,
(status & HDSPM_midi0IRQPending) ? 1 : 0,
(status & HDSPM_midi1IRQPending) ? 1 : 0,
hdspm->irq_count);
snd_iprintf(buffer,
"HW pointer: id = %d, rawptr = %d (%d->%d) estimated= %ld (bytes)\n",
((status & HDSPM_BufferID) ? 1 : 0),
(status & HDSPM_BufferPositionMask),
(status & HDSPM_BufferPositionMask) % (2 *
(int)hdspm->
period_bytes),
((status & HDSPM_BufferPositionMask) -
64) % (2 * (int)hdspm->period_bytes),
(long) hdspm_hw_pointer(hdspm) * 4);
snd_iprintf(buffer,
"MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
snd_iprintf(buffer,
"Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, status2=0x%x, timecode=0x%x\n",
hdspm->control_register, hdspm->control2_register,
status, status2, timecode);
snd_iprintf(buffer, "--- Settings ---\n");
x = 1 << (6 +
hdspm_decode_latency(hdspm->
control_register &
HDSPM_LatencyMask));
snd_iprintf(buffer,
"Size (Latency): %d samples (2 periods of %lu bytes)\n",
x, (unsigned long) hdspm->period_bytes);
snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n",
(hdspm->
control_register & HDSPM_LineOut) ? "on " : "off",
(hdspm->precise_ptr) ? "on" : "off");
snd_iprintf(buffer,
"ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
(hdspm->
control_register & HDSPM_clr_tms) ? "on" : "off",
(hdspm->
control_register & HDSPM_Emphasis) ? "on" : "off",
(hdspm->
control_register & HDSPM_Dolby) ? "on" : "off");
switch (hdspm_clock_source(hdspm)) {
case HDSPM_CLOCK_SOURCE_AUTOSYNC:
clock_source = "AutoSync";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
clock_source = "Internal 32 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
clock_source = "Internal 44.1 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
clock_source = "Internal 48 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
clock_source = "Internal 64 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
clock_source = "Internal 88.2 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
clock_source = "Internal 96 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ:
clock_source = "Internal 128 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ:
clock_source = "Internal 176.4 kHz";
break;
case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ:
clock_source = "Internal 192 kHz";
break;
default:
clock_source = "Error";
}
snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
if (!(hdspm->control_register & HDSPM_ClockModeMaster))
system_clock_mode = "Slave";
else
system_clock_mode = "Master";
snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
pref_syncref = hdspm_pref_sync_ref(hdspm);
if (pref_syncref == 0)
snd_iprintf(buffer, "Preferred Sync Reference: Word Clock\n");
else
snd_iprintf(buffer, "Preferred Sync Reference: AES%d\n",
pref_syncref);
snd_iprintf(buffer, "System Clock Frequency: %d\n",
hdspm->system_sample_rate);
snd_iprintf(buffer, "Double speed: %s\n",
hdspm->control_register & HDSPM_DS_DoubleWire?
"Double wire" : "Single wire");
snd_iprintf(buffer, "Quad speed: %s\n",
hdspm->control_register & HDSPM_QS_DoubleWire?
"Double wire" :
hdspm->control_register & HDSPM_QS_QuadWire?
"Quad wire" : "Single wire");
snd_iprintf(buffer, "--- Status:\n");
snd_iprintf(buffer, "Word: %s Frequency: %d\n",
(status & HDSPM_AES32_wcLock)? "Sync " : "No Lock",
HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
for (x = 0; x < 8; x++) {
snd_iprintf(buffer, "AES%d: %s Frequency: %d\n",
x+1,
(status2 & (HDSPM_LockAES >> x))? "Sync ": "No Lock",
HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
}
switch (hdspm_autosync_ref(hdspm)) {
case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break;
case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break;
case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break;
default: autosync_ref = "---"; break;
}
snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
snd_iprintf(buffer, "\n");
}
#ifdef CONFIG_SND_DEBUG
static void
snd_hdspm_proc_read_debug(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
struct hdspm *hdspm = (struct hdspm *)entry->private_data;
int j,i;
for (i = 0; i < 256 /* 1024*64 */; i += j)
{
snd_iprintf(buffer, "0x%08X: ", i);
for (j = 0; j < 16; j += 4)
snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j));
snd_iprintf(buffer, "\n");
}
}
#endif
static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(hdspm->card, "hdspm", &entry))
snd_info_set_text_ops(entry, hdspm,
snd_hdspm_proc_read);
hdspm->is_aes32 ?
snd_hdspm_proc_read_aes32 :
snd_hdspm_proc_read_madi);
#ifdef CONFIG_SND_DEBUG
/* debug file to read all hdspm registers */
if (!snd_card_proc_new(hdspm->card, "debug", &entry))
snd_info_set_text_ops(entry, hdspm,
snd_hdspm_proc_read_debug);
#endif
}
/*------------------------------------------------------------
......@@ -2507,6 +3356,13 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
/* set defaults: */
if (hdspm->is_aes32)
hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */
hdspm_encode_latency(7) | /* latency maximum = 8192 samples */
HDSPM_SyncRef0 | /* AES1 is syncclock */
HDSPM_LineOut | /* Analog output in */
HDSPM_Professional; /* Professional mode */
else
hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */
hdspm_encode_latency(7) | /* latency maximum = 8192 samples */
HDSPM_InputCoaxial | /* Input Coax not Optical */
......@@ -2822,6 +3678,8 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
hdspm->playback_buffer =
(unsigned char *) substream->runtime->dma_area;
snd_printdd("Allocated sample buffer for playback at 0x%08X\n",
hdspm->playback_buffer);
} else {
hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn,
params_channels(params));
......@@ -2831,7 +3689,15 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
hdspm->capture_buffer =
(unsigned char *) substream->runtime->dma_area;
snd_printdd("Allocated sample buffer for capture at 0x%08X\n",
hdspm->capture_buffer);
}
/*
snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
"playback" : "capture",
snd_pcm_sgbuf_get_addr(sgbuf, 0));
*/
return 0;
}
......@@ -2982,9 +3848,10 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000),
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 ),
.rate_min = 32000,
.rate_max = 96000,
.rate_max = 192000,
.channels_min = 1,
.channels_max = HDSPM_MAX_CHANNELS,
.buffer_bytes_max =
......@@ -3006,9 +3873,10 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000),
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
.rate_min = 32000,
.rate_max = 96000,
.rate_max = 192000,
.channels_min = 1,
.channels_max = HDSPM_MAX_CHANNELS,
.buffer_bytes_max =
......@@ -3315,7 +4183,8 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
pcm = hdspm->pcm;
wanted = HDSPM_DMA_AREA_BYTES + 4096; /* dont know why, but it works */
/* wanted = HDSPM_DMA_AREA_BYTES + 4096;*/ /* dont know why, but it works */
wanted = HDSPM_DMA_AREA_BYTES;
if ((err =
snd_pcm_lib_preallocate_pages_for_all(pcm,
......@@ -3467,9 +4336,16 @@ static int __devinit snd_hdspm_create(struct snd_card *card, struct hdspm * hdsp
pci_read_config_word(hdspm->pci,
PCI_CLASS_REVISION, &hdspm->firmware_rev);
strcpy(card->driver, "HDSPM");
hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION);
strcpy(card->mixername, "Xilinx FPGA");
if (hdspm->is_aes32) {
strcpy(card->driver, "HDSPAES32");
hdspm->card_name = "RME HDSPM AES32";
} else {
strcpy(card->driver, "HDSPM");
hdspm->card_name = "RME HDSPM MADI";
}
if ((err = pci_enable_device(pci)) < 0)
return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment