diff options
author | Remy Bruno <remy.bruno@trinnov.com> | 2006-10-16 12:46:32 +0200 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2007-02-09 09:00:57 +0100 |
commit | 3cee5a60ce18034a63f70ba2bdd54f85018ce960 (patch) | |
tree | 9054f39eadc635debe335723bd5aab31d9938cd9 /sound | |
parent | b3b9c1cbb35125f7e43a323ebe89e7a74e3c1ac2 (diff) |
[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: Remy Bruno <remy.bruno@trinnov.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/rme9652/hdspm.c | 1242 |
1 files changed, 1059 insertions, 183 deletions
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 0547f6f04bd..3d3a4ce3a35 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -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,86 +634,105 @@ 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) { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int rate_bits; - int rate = 0; + 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; + int rate = 0; - /* if wordclock has synced freq and wordclock is valid */ - if ((status2 & HDSPM_wcLock) != 0 && - (status & HDSPM_SelSyncRef0) == 0) { + /* if wordclock has synced freq and wordclock is valid */ + if ((status2 & HDSPM_wcLock) != 0 && + (status & HDSPM_SelSyncRef0) == 0) { - rate_bits = status2 & HDSPM_wcFreqMask; + rate_bits = status2 & HDSPM_wcFreqMask; - switch (rate_bits) { - case HDSPM_wcFreq32: - rate = 32000; - break; - case HDSPM_wcFreq44_1: - rate = 44100; - break; - case HDSPM_wcFreq48: - rate = 48000; - break; - case HDSPM_wcFreq64: - rate = 64000; - break; - case HDSPM_wcFreq88_2: - rate = 88200; - break; - case HDSPM_wcFreq96: - rate = 96000; - break; - /* Quadspeed Bit missing ???? */ - default: - rate = 0; - break; + switch (rate_bits) { + case HDSPM_wcFreq32: + rate = 32000; + break; + case HDSPM_wcFreq44_1: + rate = 44100; + break; + case HDSPM_wcFreq48: + rate = 48000; + break; + case HDSPM_wcFreq64: + rate = 64000; + break; + case HDSPM_wcFreq88_2: + rate = 88200; + break; + case HDSPM_wcFreq96: + rate = 96000; + break; + /* Quadspeed Bit missing ???? */ + default: + rate = 0; + break; + } } - } - /* if rate detected and Syncref is Word than have it, word has priority to MADI */ - if (rate != 0 - && (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) - return rate; + /* if rate detected and Syncref is Word than have it, word has priority to MADI */ + if (rate != 0 && + (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) + return rate; - /* maby a madi input (which is taken if sel sync is madi) */ - if (status & HDSPM_madiLock) { - rate_bits = status & HDSPM_madiFreqMask; + /* maby a madi input (which is taken if sel sync is madi) */ + if (status & HDSPM_madiLock) { + rate_bits = status & HDSPM_madiFreqMask; - switch (rate_bits) { - case HDSPM_madiFreq32: - rate = 32000; - break; - case HDSPM_madiFreq44_1: - rate = 44100; - break; - case HDSPM_madiFreq48: - rate = 48000; - break; - case HDSPM_madiFreq64: - rate = 64000; - break; - case HDSPM_madiFreq88_2: - rate = 88200; - break; - case HDSPM_madiFreq96: - rate = 96000; - break; - case HDSPM_madiFreq128: - rate = 128000; - break; - case HDSPM_madiFreq176_4: - rate = 176400; - break; - case HDSPM_madiFreq192: - rate = 192000; - break; - default: - rate = 0; - break; + switch (rate_bits) { + case HDSPM_madiFreq32: + rate = 32000; + break; + case HDSPM_madiFreq44_1: + rate = 44100; + break; + case HDSPM_madiFreq48: + rate = 48000; + break; + case HDSPM_madiFreq64: + rate = 64000; + break; + case HDSPM_madiFreq88_2: + rate = 88200; + break; + case HDSPM_madiFreq96: + rate = 96000; + break; + case HDSPM_madiFreq128: + rate = 128000; + break; + case HDSPM_madiFreq176_4: + rate = 176400; + break; + case HDSPM_madiFreq192: + rate = 192000; + break; + default: + rate = 0; + break; + } } + return rate; } - 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,11 +1579,27 @@ static int hdspm_pref_sync_ref(struct hdspm * hdspm) /* Notice that this looks at the requested sync source, not the one actually in use. */ - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - return HDSPM_SYNC_FROM_WORD; - case HDSPM_SyncRef_MADI: - return HDSPM_SYNC_FROM_MADI; + 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,15 +1609,49 @@ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) { hdspm->control_register &= ~HDSPM_SyncRefMask; - switch (pref) { - case HDSPM_SYNC_FROM_MADI: - hdspm->control_register |= HDSPM_SyncRef_MADI; - break; - case HDSPM_SYNC_FROM_WORD: - hdspm->control_register |= HDSPM_SyncRef_Word; - break; - default: - return -1; + 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; + break; + case HDSPM_SYNC_FROM_WORD: + hdspm->control_register |= HDSPM_SyncRef_Word; + break; + default: + return -1; + } } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); return 0; @@ -1486,18 +1660,36 @@ 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) { - static char *texts[] = { "Word", "MADI" }; + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + if (hdspm->is_aes32) { + static char *texts[] = { "Word", "AES1", "AES2", "AES3", + "AES4", "AES5", "AES6", "AES7", "AES8" }; - uinfo->value.enumerated.items = 2; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; - 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.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; + 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; } @@ -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,40 +1734,64 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, static int hdspm_autosync_ref(struct hdspm * hdspm) { - /* 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; + 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; + } - default: return 0; } - - return 0; } static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "WordClock", "MADI", "None" }; + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - 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]); + 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; + 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; } @@ -1841,6 +2057,195 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol, return change; } +#define HDSPM_EMPHASIS(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_emphasis, \ + .get = snd_hdspm_get_emphasis, \ + .put = snd_hdspm_put_emphasis \ +} + +static int hdspm_emphasis(struct hdspm * hdspm) +{ + return (hdspm->control_register & HDSPM_Emphasis) ? 1 : 0; +} + +static int hdspm_set_emphasis(struct hdspm * hdspm, int emp) +{ + if (emp) + hdspm->control_register |= HDSPM_Emphasis; + else + hdspm->control_register &= ~HDSPM_Emphasis; + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + return 0; +} + +static int snd_hdspm_info_emphasis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +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_emphasis(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_emphasis(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_emphasis(hdspm); + hdspm_set_emphasis(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_DOLBY(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_dolby, \ + .get = snd_hdspm_get_dolby, \ + .put = snd_hdspm_put_dolby \ +} + +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_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdspm_get_dolby(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_dolby(hdspm); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +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; + 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_dolby(hdspm); + hdspm_set_dolby(hdspm, val); + spin_unlock_irq(&hdspm->lock); + return change; +} + +#define HDSPM_PROFESSIONAL(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_professional, \ + .get = snd_hdspm_get_professional, \ + .put = snd_hdspm_put_professional \ +} + +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_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdspm_get_professional(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_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, \ @@ -1912,6 +2317,163 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol, 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) @@ -2135,14 +2697,24 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, static int hdspm_wc_sync_check(struct hdspm * hdspm) { - int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - if (status2 & HDSPM_wcLock) { - if (status2 & HDSPM_wcSync) + 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; - else - return 1; + } + return 0; + } else { + int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + if (status2 & HDSPM_wcLock) { + if (status2 & HDSPM_wcSync) + return 2; + else + return 1; + } + return 0; } - 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) { - return err; + 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,13 +3356,20 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) /* set defaults: */ - hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = 8192 samples */ - HDSPM_InputCoaxial | /* Input Coax not Optical */ - HDSPM_SyncRef_MADI | /* Madi is syncclock */ - HDSPM_LineOut | /* Analog output in */ - HDSPM_TX_64ch | /* transmit in 64ch mode */ - HDSPM_AutoInp; /* AutoInput chossing (takeover) */ + 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 */ + HDSPM_SyncRef_MADI | /* Madi is syncclock */ + HDSPM_LineOut | /* Analog output in */ + HDSPM_TX_64ch | /* transmit in 64ch mode */ + HDSPM_AutoInp; /* AutoInput chossing (takeover) */ /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */ /* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */ @@ -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"); - hdspm->card_name = "RME HDSPM MADI"; + 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; |