[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>
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 0547f6f..3d3a4ce 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_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 @@
 /* --- 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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
    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 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 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 @@
 /* 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);
 
-	/* if wordclock has synced freq and wordclock is valid */
-	if ((status2 & HDSPM_wcLock) != 0 &&
-	    (status & HDSPM_SelSyncRef0) == 0) {
+		int syncref = hdspm_autosync_ref(hdspm);
 
-		rate_bits = status2 & HDSPM_wcFreqMask;
+		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;
 
-		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 wordclock has synced freq and wordclock is valid */
+		if ((status2 & HDSPM_wcLock) != 0 &&
+				(status & HDSPM_SelSyncRef0) == 0) {
+
+			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;
+			}
 		}
-	}
 
-	/* 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 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;
+
+			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;
-
-	/* 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;
-		}
 	}
-	return rate;
 }
 
 /* Latency function */
@@ -676,7 +785,8 @@
 	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 @@
 	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 @@
 	   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 @@
 	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 @@
 	/* 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 @@
 {
 	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 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 @@
 	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 hdspm_autosync_ref(struct hdspm * hdspm)
 {
-	/* This looks at the autosync selected sync reference */
-	unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+	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) {
+		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;
+		}
 
-	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)
 {
-	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 @@
 	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 @@
 	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 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 @@
 }
 
 
+#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 @@
 	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 @@
 	struct snd_kcontrol *kctl;
 
 	/* add control list first */
+	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); idx++) {
-		if ((err =
-		     snd_ctl_add(card, kctl =
-				 snd_ctl_new1(&snd_hdspm_controls[idx],
-					      hdspm))) < 0) {
-			return err;
+		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 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 @@
 		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_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 @@
 
 	/* 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 @@
 
 		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 @@
 
 		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 @@
 		  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 @@
 		  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 @@
 
 	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 @@
 	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;