aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2005-06-03 11:25:34 +0200
committerJaroslav Kysela <perex@suse.cz>2005-06-22 12:28:11 +0200
commit763f356cd8de9e158836d236b3fd9dd149d696f9 (patch)
tree530fdcc8603ed001d12b157e9972b5c089237c0c
parent375389288ae55754bd7d009a30f2bb0453a5b369 (diff)
[ALSA] Add HDSP MADI driver
HDSPM driver,PCI drivers,RME9652 driver Added RME Hammerfall DSP MADI driver by Winfried Ritsch. (Moved from alsa-driver tree to mainline.) Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--include/sound/hdspm.h131
-rw-r--r--sound/pci/Kconfig13
-rw-r--r--sound/pci/rme9652/Makefile2
-rw-r--r--sound/pci/rme9652/hdspm.c3671
4 files changed, 3817 insertions, 0 deletions
diff --git a/include/sound/hdspm.h b/include/sound/hdspm.h
new file mode 100644
index 00000000000..c34427ccd0b
--- /dev/null
+++ b/include/sound/hdspm.h
@@ -0,0 +1,131 @@
+#ifndef __SOUND_HDSPM_H /* -*- linux-c -*- */
+#define __SOUND_HDSPM_H
+/*
+ * Copyright (C) 2003 Winfried Ritsch (IEM)
+ * based on hdsp.h from Thomas Charbonnel (thomas@undata.org)
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */
+#define HDSPM_MAX_CHANNELS 64
+
+/* -------------------- IOCTL Peak/RMS Meters -------------------- */
+
+typedef struct _snd_hdspm_peak_rms hdspm_peak_rms_t;
+
+/* peam rms level structure like we get from hardware
+
+ maybe in future we can memory map it so I just copy it
+ to user on ioctl call now an dont change anything
+ rms are made out of low and high values
+ where (long) ????_rms = (????_rms_l >> 8) + ((????_rms_h & 0xFFFFFF00)<<24)
+ (i asume so from the code)
+*/
+
+struct _snd_hdspm_peak_rms {
+
+ unsigned int level_offset[1024];
+
+ unsigned int input_peak[64];
+ unsigned int playback_peak[64];
+ unsigned int output_peak[64];
+ unsigned int xxx_peak[64]; /* not used */
+
+ unsigned int reserved[256]; /* not used */
+
+ unsigned int input_rms_l[64];
+ unsigned int playback_rms_l[64];
+ unsigned int output_rms_l[64];
+ unsigned int xxx_rms_l[64]; /* not used */
+
+ unsigned int input_rms_h[64];
+ unsigned int playback_rms_h[64];
+ unsigned int output_rms_h[64];
+ unsigned int xxx_rms_h[64]; /* not used */
+};
+
+struct sndrv_hdspm_peak_rms_ioctl {
+ hdspm_peak_rms_t *peak;
+};
+
+/* use indirect access due to the limit of ioctl bit size */
+#define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS _IOR('H', 0x40, struct sndrv_hdspm_peak_rms_ioctl)
+
+/* ------------ CONFIG block IOCTL ---------------------- */
+
+typedef struct _snd_hdspm_config_info hdspm_config_info_t;
+
+struct _snd_hdspm_config_info {
+ unsigned char pref_sync_ref;
+ unsigned char wordclock_sync_check;
+ unsigned char madi_sync_check;
+ unsigned int system_sample_rate;
+ unsigned int autosync_sample_rate;
+ unsigned char system_clock_mode;
+ unsigned char clock_source;
+ unsigned char autosync_ref;
+ unsigned char line_out;
+ unsigned int passthru;
+ unsigned int analog_out;
+};
+
+#define SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO _IOR('H', 0x41, hdspm_config_info_t)
+
+
+/* get Soundcard Version */
+
+typedef struct _snd_hdspm_version hdspm_version_t;
+
+struct _snd_hdspm_version {
+ unsigned short firmware_rev;
+};
+
+#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x43, hdspm_version_t)
+
+
+/* ------------- get Matrix Mixer IOCTL --------------- */
+
+/* MADI mixer: 64inputs+64playback in 64outputs = 8192 => *4Byte = 32768 Bytes */
+
+/* organisation is 64 channelfader in a continous memory block */
+/* equivalent to hardware definition, maybe for future feature of mmap of them */
+/* each of 64 outputs has 64 infader and 64 outfader:
+ Ins to Outs mixer[out].in[in], Outstreams to Outs mixer[out].pb[pb] */
+
+#define HDSPM_MIXER_CHANNELS HDSPM_MAX_CHANNELS
+
+typedef struct _snd_hdspm_channelfader snd_hdspm_channelfader_t;
+
+struct _snd_hdspm_channelfader {
+ unsigned int in[HDSPM_MIXER_CHANNELS];
+ unsigned int pb[HDSPM_MIXER_CHANNELS];
+};
+
+typedef struct _snd_hdspm_mixer hdspm_mixer_t;
+
+struct _snd_hdspm_mixer {
+ snd_hdspm_channelfader_t ch[HDSPM_MIXER_CHANNELS];
+};
+
+struct sndrv_hdspm_mixer_ioctl {
+ hdspm_mixer_t *mixer;
+};
+
+/* use indirect access due to the limit of ioctl bit size */
+#define SNDRV_HDSPM_IOCTL_GET_MIXER _IOR('H', 0x44, struct sndrv_hdspm_mixer_ioctl)
+
+#endif /* __SOUND_HDSPM_H */
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 428efdbd70a..6d7a00f34d8 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -274,6 +274,19 @@ config SND_HDSP
To compile this driver as a module, choose M here: the module
will be called snd-hdsp.
+config SND_HDSPM
+ tristate "RME Hammerfall DSP MADI"
+ depends on SND
+ select SND_HWDEP
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say Y here to include support for RME Hammerfall DSP MADI
+ soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hdspm.
+
config SND_TRIDENT
tristate "Trident 4D-Wave DX/NX; SiS 7018"
depends on SND
diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile
index 917374c9cd4..d2c294e136f 100644
--- a/sound/pci/rme9652/Makefile
+++ b/sound/pci/rme9652/Makefile
@@ -5,7 +5,9 @@
snd-rme9652-objs := rme9652.o
snd-hdsp-objs := hdsp.o
+snd-hdspm-objs := hdspm.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_RME9652) += snd-rme9652.o
obj-$(CONFIG_SND_HDSP) += snd-hdsp.o
+obj-$(CONFIG_SND_HDSPM) +=snd-hdspm.o
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
new file mode 100644
index 00000000000..9e86d0eb41c
--- /dev/null
+++ b/sound/pci/rme9652/hdspm.c
@@ -0,0 +1,3671 @@
+/* -*- linux-c -*-
+ *
+ * ALSA driver for RME Hammerfall DSP MADI audio interface(s)
+ *
+ * Copyright (c) 2003 Winfried Ritsch (IEM)
+ * code based on hdsp.c Paul Davis
+ * Marcus Andersson
+ * Thomas Charbonnel
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#include <sound/rawmidi.h>
+#include <sound/hwdep.h>
+#include <sound/initval.h>
+
+#include <sound/hdspm.h>
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+
+/* Disable precise pointer at start */
+static int precise_ptr[SNDRV_CARDS];
+
+/* Send all playback to line outs */
+static int line_outs_monitor[SNDRV_CARDS];
+
+/* Enable Analog Outs on Channel 63/64 by default */
+static int enable_monitor[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
+
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
+
+module_param_array(precise_ptr, bool, NULL, 0444);
+MODULE_PARM_DESC(precise_ptr, "Enable precise pointer, or disable.");
+
+module_param_array(line_outs_monitor, bool, NULL, 0444);
+MODULE_PARM_DESC(line_outs_monitor,
+ "Send playback streams to analog outs by default.");
+
+module_param_array(enable_monitor, bool, NULL, 0444);
+MODULE_PARM_DESC(enable_monitor,
+ "Enable Analog Out on Channel 63/64 by default.");
+
+MODULE_AUTHOR
+ ("Winfried Ritsch <ritsch_AT_iem.at>, Paul Davis <paul@linuxaudiosystems.com>, "
+ "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>");
+MODULE_DESCRIPTION("RME HDSPM");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
+
+/* --- Write registers. ---
+ These are defined as byte-offsets from the iobase value. */
+
+#define HDSPM_controlRegister 64
+#define HDSPM_interruptConfirmation 96
+#define HDSPM_control2Reg 256 /* not in specs ???????? */
+#define HDSPM_midiDataOut0 352 /* just believe in old code */
+#define HDSPM_midiDataOut1 356
+
+/* DMA enable for 64 channels, only Bit 0 is relevant */
+#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */
+#define HDSPM_inputEnableBase 768 /* 768-1023 output DMA */
+
+/* 16 page addresses for each of the 64 channels DMA buffer in and out
+ (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
+#define HDSPM_pageAddressBufferOut 8192
+#define HDSPM_pageAddressBufferIn (HDSPM_pageAddressBufferOut+64*16*4)
+
+#define HDSPM_MADI_mixerBase 32768 /* 32768-65535 for 2x64x64 Fader */
+
+#define HDSPM_MATRIX_MIXER_SIZE 8192 /* = 2*64*64 * 4 Byte => 32kB */
+
+/* --- Read registers. ---
+ These are defined as byte-offsets from the iobase value */
+#define HDSPM_statusRegister 0
+#define HDSPM_statusRegister2 96
+
+#define HDSPM_midiDataIn0 360
+#define HDSPM_midiDataIn1 364
+
+/* status is data bytes in MIDI-FIFO (0-128) */
+#define HDSPM_midiStatusOut0 384
+#define HDSPM_midiStatusOut1 388
+#define HDSPM_midiStatusIn0 392
+#define HDSPM_midiStatusIn1 396
+
+
+/* the meters are regular i/o-mapped registers, but offset
+ considerably from the rest. the peak registers are reset
+ when read; the least-significant 4 bits are full-scale counters;
+ the actual peak value is in the most-significant 24 bits.
+*/
+#define HDSPM_MADI_peakrmsbase 4096 /* 4096-8191 2x64x32Bit Meters */
+
+/* --- Control Register bits --------- */
+#define HDSPM_Start (1<<0) /* start engine */
+
+#define HDSPM_Latency0 (1<<1) /* buffer size = 2^n */
+#define HDSPM_Latency1 (1<<2) /* where n is defined */
+#define HDSPM_Latency2 (1<<3) /* by Latency{2,1,0} */
+
+#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Slave/Autosync */
+
+#define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
+
+#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_TX_64ch (1<<10) /* Output 64channel MODE=1,
+ 56channelMODE=0 */
+
+#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode,
+ 0=off, 1=on */
+
+#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */
+#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_clr_tms (1<<19) /* clear track marker, do not use
+ AES additional bits in
+ lower 5 Audiodatabits ??? */
+
+#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 */
+
+
+/* --- bit helper defines */
+#define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
+#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1)
+#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_SyncRef_Word 0
+#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0)
+
+#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */
+#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */
+
+#define HDSPM_Frequency32KHz HDSPM_Frequency0
+#define HDSPM_Frequency44_1KHz HDSPM_Frequency1
+#define HDSPM_Frequency48KHz (HDSPM_Frequency1|HDSPM_Frequency0)
+#define HDSPM_Frequency64KHz (HDSPM_DoubleSpeed|HDSPM_Frequency0)
+#define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
+#define HDSPM_Frequency96KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1|HDSPM_Frequency0)
+
+/* --- for internal discrimination */
+#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */
+#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ 1
+#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ 2
+#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ 3
+#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ 4
+#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ 5
+#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ 6
+#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ 7
+#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8
+#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ 9
+
+/* Synccheck Status */
+#define HDSPM_SYNC_CHECK_NO_LOCK 0
+#define HDSPM_SYNC_CHECK_LOCK 1
+#define HDSPM_SYNC_CHECK_SYNC 2
+
+/* AutoSync References - used by "autosync_ref" control switch */
+#define HDSPM_AUTOSYNC_FROM_WORD 0
+#define HDSPM_AUTOSYNC_FROM_MADI 1
+#define HDSPM_AUTOSYNC_FROM_NONE 2
+
+/* Possible sources of MADI input */
+#define HDSPM_OPTICAL 0 /* optical */
+#define HDSPM_COAXIAL 1 /* BNC */
+
+#define hdspm_encode_latency(x) (((x)<<1) & HDSPM_LatencyMask)
+#define hdspm_decode_latency(x) (((x) & HDSPM_LatencyMask)>>1)
+
+#define hdspm_encode_in(x) (((x)&0x3)<<14)
+#define hdspm_decode_in(x) (((x)>>14)&0x3)
+
+/* --- control2 register bits --- */
+#define HDSPM_TMS (1<<0)
+#define HDSPM_TCK (1<<1)
+#define HDSPM_TDI (1<<2)
+#define HDSPM_JTAG (1<<3)
+#define HDSPM_PWDN (1<<4)
+#define HDSPM_PROGRAM (1<<5)
+#define HDSPM_CONFIG_MODE_0 (1<<6)
+#define HDSPM_CONFIG_MODE_1 (1<<7)
+/*#define HDSPM_VERSION_BIT (1<<8) not defined any more*/
+#define HDSPM_BIGENDIAN_MODE (1<<9)
+#define HDSPM_RD_MULTIPLE (1<<10)
+
+/* --- Status Register bits --- */
+#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) */
+#define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */
+
+#define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
+ /* since 64byte accurate last 6 bits
+ are not used */
+
+#define HDSPM_madiSync (1<<18) /* MADI is in sync */
+#define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
+
+#define HDSPM_madiFreq0 (1<<22) /* system freq 0=error */
+#define HDSPM_madiFreq1 (1<<23) /* 1=32, 2=44.1 3=48 */
+#define HDSPM_madiFreq2 (1<<24) /* 4=64, 5=88.2 6=96 */
+#define HDSPM_madiFreq3 (1<<25) /* 7=128, 8=176.4 9=192 */
+
+#define HDSPM_BufferID (1<<26) /* (Double)Buffer ID toggles with Interrupt */
+#define HDSPM_midi0IRQPending (1<<30) /* MIDI IRQ is pending */
+#define HDSPM_midi1IRQPending (1<<31) /* and aktiv */
+
+/* --- status bit helpers */
+#define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2|HDSPM_madiFreq3)
+#define HDSPM_madiFreq32 (HDSPM_madiFreq0)
+#define HDSPM_madiFreq44_1 (HDSPM_madiFreq1)
+#define HDSPM_madiFreq48 (HDSPM_madiFreq0|HDSPM_madiFreq1)
+#define HDSPM_madiFreq64 (HDSPM_madiFreq2)
+#define HDSPM_madiFreq88_2 (HDSPM_madiFreq0|HDSPM_madiFreq2)
+#define HDSPM_madiFreq96 (HDSPM_madiFreq1|HDSPM_madiFreq2)
+#define HDSPM_madiFreq128 (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2)
+#define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
+#define HDSPM_madiFreq192 (HDSPM_madiFreq3|HDSPM_madiFreq0)
+
+/* Status2 Register bits */
+
+#define HDSPM_version0 (1<<0) /* not realy defined but I guess */
+#define HDSPM_version1 (1<<1) /* in former cards it was ??? */
+#define HDSPM_version2 (1<<2)
+
+#define HDSPM_wcLock (1<<3) /* Wordclock is detected and locked */
+#define HDSPM_wcSync (1<<4) /* Wordclock is in sync with systemclock */
+
+#define HDSPM_wc_freq0 (1<<5) /* input freq detected via autosync */
+#define HDSPM_wc_freq1 (1<<6) /* 001=32, 010==44.1, 011=48, */
+#define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, */
+/* missing Bit for 111=128, 1000=176.4, 1001=192 */
+
+#define HDSPM_SelSyncRef0 (1<<8) /* Sync Source in slave mode */
+#define HDSPM_SelSyncRef1 (1<<9) /* 000=word, 001=MADI, */
+#define HDSPM_SelSyncRef2 (1<<10) /* 111=no valid signal */
+
+#define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
+
+#define HDSPM_wcFreqMask (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreq32 (HDSPM_wc_freq0)
+#define HDSPM_wcFreq44_1 (HDSPM_wc_freq1)
+#define HDSPM_wcFreq48 (HDSPM_wc_freq0|HDSPM_wc_freq1)
+#define HDSPM_wcFreq64 (HDSPM_wc_freq2)
+#define HDSPM_wcFreq88_2 (HDSPM_wc_freq0|HDSPM_wc_freq2)
+#define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2)
+
+
+#define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2)
+#define HDSPM_SelSyncRef_WORD 0
+#define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0)
+#define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2)
+
+/* Mixer Values */
+#define UNITY_GAIN 32768 /* = 65536/2 */
+#define MINUS_INFINITY_GAIN 0
+
+/* PCI info */
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6
+#endif
+
+
+/* Number of channels for different Speed Modes */
+#define MADI_SS_CHANNELS 64
+#define MADI_DS_CHANNELS 32
+#define MADI_QS_CHANNELS 16
+
+/* the size of a substream (1 mono data stream) */
+#define HDSPM_CHANNEL_BUFFER_SAMPLES (16*1024)
+#define HDSPM_CHANNEL_BUFFER_BYTES (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
+
+/* the size of the area we need to allocate for DMA transfers. the
+ size is the same regardless of the number of channels, and
+ also the latency to use.
+ for one direction !!!
+*/
+#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
+#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
+
+typedef struct _hdspm hdspm_t;
+typedef struct _hdspm_midi hdspm_midi_t;
+
+struct _hdspm_midi {
+ hdspm_t *hdspm;
+ int id;
+ snd_rawmidi_t *rmidi;
+ snd_rawmidi_substream_t *input;
+ snd_rawmidi_substream_t *output;
+ char istimer; /* timer in use */
+ struct timer_list timer;
+ spinlock_t lock;
+ int pending;
+};
+
+struct _hdspm {
+ spinlock_t lock;
+ snd_pcm_substream_t *capture_substream; /* only one playback */
+ snd_pcm_substream_t *playback_substream; /* and/or capture stream */
+
+ char *card_name; /* for procinfo */
+ unsigned short firmware_rev; /* dont know if relevant */
+
+ int precise_ptr; /* use precise pointers, to be tested */
+ int monitor_outs; /* set up monitoring outs init flag */
+
+ u32 control_register; /* cached value */
+ u32 control2_register; /* cached value */
+
+ hdspm_midi_t midi[2];
+ struct tasklet_struct midi_tasklet;
+
+ size_t period_bytes;
+ unsigned char ss_channels; /* channels of card in single speed */
+ unsigned char ds_channels; /* Double Speed */
+ unsigned char qs_channels; /* Quad Speed */
+
+ unsigned char *playback_buffer; /* suitably aligned address */
+ unsigned char *capture_buffer; /* suitably aligned address */
+
+ pid_t capture_pid; /* process id which uses capture */
+ pid_t playback_pid; /* process id which uses capture */
+ int running; /* running status */
+
+ int last_external_sample_rate; /* samplerate mystic ... */
+ int last_internal_sample_rate;
+ int system_sample_rate;
+
+ char *channel_map; /* channel map for DS and Quadspeed */
+
+ int dev; /* Hardware vars... */
+ int irq;
+ unsigned long port;
+ void __iomem *iobase;
+
+ int irq_count; /* for debug */
+
+ snd_card_t *card; /* one card */
+ snd_pcm_t *pcm; /* has one pcm */
+ snd_hwdep_t *hwdep; /* and a hwdep for additional ioctl */
+ struct pci_dev *pci; /* and an pci info */
+
+ /* Mixer vars */
+ snd_kcontrol_t *playback_mixer_ctls[HDSPM_MAX_CHANNELS]; /* fast alsa mixer */
+ snd_kcontrol_t *input_mixer_ctls[HDSPM_MAX_CHANNELS]; /* but input to much, so not used */
+ hdspm_mixer_t *mixer; /* full mixer accessable over mixer ioctl or hwdep-device */
+
+};
+
+/* These tables map the ALSA channels 1..N to the channels that we
+ need to use in order to find the relevant channel buffer. RME
+ refer to this kind of mapping as between "the ADAT channel and
+ the DMA channel." We index it using the logical audio channel,
+ and the value is the DMA channel (i.e. channel buffer number)
+ where the data for that channel can be read/written from/to.
+*/
+
+static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63
+};
+
+static char channel_map_madi_ds[HDSPM_MAX_CHANNELS] = {
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_madi_qs[HDSPM_MAX_CHANNELS] = {
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+
+static struct pci_device_id snd_hdspm_ids[] = {
+ {
+ .vendor = PCI_VENDOR_ID_XILINX,
+ .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_hdspm_ids);
+
+/* prototypes */
+static int __devinit snd_hdspm_create_alsa_devices(snd_card_t * card,
+ hdspm_t * hdspm);
+static int __devinit snd_hdspm_create_pcm(snd_card_t * card,
+ hdspm_t * hdspm);
+
+static inline void snd_hdspm_initialize_midi_flush(hdspm_t * hdspm);
+static int hdspm_update_simple_mixer_controls(hdspm_t * hdspm);
+static int hdspm_autosync_ref(hdspm_t * hdspm);
+static int snd_hdspm_set_defaults(hdspm_t * hdspm);
+static void hdspm_set_sgbuf(hdspm_t * hdspm, struct snd_sg_buf *sgbuf,
+ unsigned int reg, int channels);
+
+/* Write/read to/from HDSPM with Adresses in Bytes
+ not words but only 32Bit writes are allowed */
+
+static inline void hdspm_write(hdspm_t * hdspm, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, hdspm->iobase + reg);
+}
+
+static inline unsigned int hdspm_read(hdspm_t * hdspm, unsigned int reg)
+{
+ return readl(hdspm->iobase + reg);
+}
+
+/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
+ mixer is write only on hardware so we have to cache him for read
+ each fader is a u32, but uses only the first 16 bit */
+
+static inline int hdspm_read_in_gain(hdspm_t * hdspm, unsigned int chan,
+ unsigned int in)
+{
+ if (chan > HDSPM_MIXER_CHANNELS || in > HDSPM_MIXER_CHANNELS)
+ return 0;
+
+ return hdspm->mixer->ch[chan].in[in];
+}
+
+static inline int hdspm_read_pb_gain(hdspm_t * hdspm, unsigned int chan,
+ unsigned int pb)
+{
+ if (chan > HDSPM_MIXER_CHANNELS || pb > HDSPM_MIXER_CHANNELS)
+ return 0;
+ return hdspm->mixer->ch[chan].pb[pb];
+}
+
+static inline int hdspm_write_in_gain(hdspm_t * hdspm, unsigned int chan,
+ unsigned int in, unsigned short data)
+{
+ if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
+ return -1;
+
+ hdspm_write(hdspm,
+ HDSPM_MADI_mixerBase +
+ ((in + 128 * chan) * sizeof(u32)),
+ (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF));
+ return 0;
+}
+
+static inline int hdspm_write_pb_gain(hdspm_t * hdspm, unsigned int chan,
+ unsigned int pb, unsigned short data)
+{
+ if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
+ return -1;
+
+ hdspm_write(hdspm,
+ HDSPM_MADI_mixerBase +
+ ((64 + pb + 128 * chan) * sizeof(u32)),
+ (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF));
+ return 0;
+}
+
+
+/* enable DMA for specific channels, now available for DSP-MADI */
+static inline void snd_hdspm_enable_in(hdspm_t * hdspm, int i, int v)
+{
+ hdspm_write(hdspm, HDSPM_inputEnableBase + (4 * i), v);
+}
+
+static inline void snd_hdspm_enable_out(hdspm_t * hdspm, int i, int v)
+{
+ hdspm_write(hdspm, HDSPM_outputEnableBase + (4 * i), v);
+}
+
+/* check if same process is writing and reading */
+static inline int snd_hdspm_use_is_exclusive(hdspm_t * hdspm)
+{
+ unsigned long flags;
+ int ret = 1;
+
+ spin_lock_irqsave(&hdspm->lock, flags);
+ if ((hdspm->playback_pid != hdspm->capture_pid) &&
+ (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) {
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&hdspm->lock, flags);
+ return ret;
+}
+
+/* check for external sample rate */
+static inline int hdspm_external_sample_rate(hdspm_t * 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 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)
+ 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 */
+static inline void hdspm_compute_period_size(hdspm_t * hdspm)
+{
+ hdspm->period_bytes =
+ 1 << ((hdspm_decode_latency(hdspm->control_register) + 8));
+}
+
+static snd_pcm_uframes_t hdspm_hw_pointer(hdspm_t * hdspm)
+{
+ int position;
+
+ position = hdspm_read(hdspm, HDSPM_statusRegister);
+
+ if (!hdspm->precise_ptr) {
+ return (position & HDSPM_BufferID) ? (hdspm->period_bytes /
+ 4) : 0;
+ }
+
+ /* hwpointer comes in bytes and is 64Bytes accurate (by docu since PCI Burst)
+ i have experimented that it is at most 64 Byte to much for playing
+ so substraction of 64 byte should be ok for ALSA, but use it only
+ for application where you know what you do since if you come to
+ near with record pointer it can be a disaster */
+
+ position &= HDSPM_BufferPositionMask;
+ position = ((position - 64) % (2 * hdspm->period_bytes)) / 4;
+
+ return position;
+}
+
+
+static inline void hdspm_start_audio(hdspm_t * s)
+{
+ s->control_register |= (HDSPM_AudioInterruptEnable | HDSPM_Start);
+ hdspm_write(s, HDSPM_controlRegister, s->control_register);
+}
+
+static inline void hdspm_stop_audio(hdspm_t * s)
+{
+ s->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable);
+ hdspm_write(s, HDSPM_controlRegister, s->control_register);
+}
+
+/* should I silence all or only opened ones ? doit all for first even is 4MB*/
+static inline void hdspm_silence_playback(hdspm_t * hdspm)
+{
+ int i;
+ int n = hdspm->period_bytes;
+ void *buf = hdspm->playback_buffer;
+
+ snd_assert(buf != NULL, return);
+
+ for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
+ memset(buf, 0, n);
+ buf += HDSPM_CHANNEL_BUFFER_BYTES;
+ }
+}
+
+static int hdspm_set_interrupt_interval(hdspm_t * s, unsigned int frames)
+{
+ int n;
+
+ spin_lock_irq(&s->lock);
+
+ frames >>= 7;
+ n = 0;
+ while (frames) {
+ n++;
+ frames >>= 1;
+ }
+ s->control_register &= ~HDSPM_LatencyMask;
+ s->control_register |= hdspm_encode_latency(n);
+
+ hdspm_write(s, HDSPM_controlRegister, s->control_register);
+
+ hdspm_compute_period_size(s);
+
+ spin_unlock_irq(&s->lock);
+
+ return 0;
+}
+
+
+/* dummy set rate lets see what happens */
+static int hdspm_set_rate(hdspm_t * hdspm, int rate, int called_internally)
+{
+ int reject_if_open = 0;
+ int current_rate;
+ int rate_bits;
+ int not_set = 0;
+
+ /* ASSUMPTION: hdspm->lock is either set, or there is no need for
+ it (e.g. during module initialization).
+ */
+
+ if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
+
+ /* SLAVE --- */
+ if (called_internally) {
+
+ /* request from ctl or card initialization
+ just make a warning an remember setting
+ for future master mode switching */
+
+ snd_printk
+ (KERN_WARNING "HDSPM: Warning: device is not running as a clock master.\n");
+ not_set = 1;
+ } else {
+
+ /* hw_param request while in AutoSync mode */
+ int external_freq =
+ hdspm_external_sample_rate(hdspm);
+
+ if ((hdspm_autosync_ref(hdspm) ==
+ HDSPM_AUTOSYNC_FROM_NONE)) {
+
+ snd_printk(KERN_WARNING "HDSPM: Detected no Externel Sync \n");
+ not_set = 1;
+
+ } else if (rate != external_freq) {
+
+ snd_printk
+ (KERN_WARNING "HDSPM: Warning: No AutoSync source for requested rate\n");
+ not_set = 1;
+ }
+ }
+ }
+
+ current_rate = hdspm->system_sample_rate;
+
+ /* Changing between Singe, Double and Quad speed is not
+ allowed if any substreams are open. This is because such a change
+ causes a shift in the location of the DMA buffers and a reduction
+ in the number of available buffers.
+
+ Note that a similar but essentially insoluble problem exists for
+ externally-driven rate changes. All we can do is to flag rate
+ changes in the read/write routines.
+ */
+
+ switch (rate) {
+ case 32000:
+ if (current_rate > 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSPM_Frequency32KHz;
+ break;
+ case 44100:
+ if (current_rate > 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSPM_Frequency44_1KHz;
+ break;
+ case 48000:
+ if (current_rate > 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSPM_Frequency48KHz;
+ break;
+ case 64000:
+ if (current_rate <= 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSPM_Frequency64KHz;
+ break;
+ case 88200:
+ if (current_rate <= 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSPM_Frequency88_2KHz;
+ break;
+ case 96000:
+ if (current_rate <= 48000) {
+ reject_if_open = 1;
+ }
+ rate_bits = HDSPM_Frequency96KHz;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (reject_if_open
+ && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
+ snd_printk
+ (KERN_ERR "HDSPM: cannot change between single- and double-speed mode (capture PID = %d, playback PID = %d)\n",
+ hdspm->capture_pid, hdspm->playback_pid);
+ return -EBUSY;
+ }
+
+ hdspm->control_register &= ~HDSPM_FrequencyMask;
+ hdspm->control_register |= rate_bits;
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ if (rate > 64000)
+ hdspm->channel_map = channel_map_madi_qs;
+ else if (rate > 48000)
+ hdspm->channel_map = channel_map_madi_ds;
+ else
+ hdspm->channel_map = channel_map_madi_ss;
+
+ hdspm->system_sample_rate = rate;
+
+ if (not_set != 0)
+ return -1;
+
+ return 0;
+}
+
+/* mainly for init to 0 on load */
+static void all_in_all_mixer(hdspm_t * hdspm, int sgain)
+{
+ int i, j;
+ unsigned int gain =
+ (sgain > UNITY_GAIN) ? UNITY_GAIN : (sgain < 0) ? 0 : sgain;
+
+ for (i = 0; i < HDSPM_MIXER_CHANNELS; i++)
+ for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) {
+ hdspm_write_in_gain(hdspm, i, j, gain);
+ hdspm_write_pb_gain(hdspm, i, j, gain);
+ }
+}
+
+/*----------------------------------------------------------------------------
+ MIDI
+ ----------------------------------------------------------------------------*/
+
+static inline unsigned char snd_hdspm_midi_read_byte (hdspm_t *hdspm, int id)
+{
+ /* the hardware already does the relevant bit-mask with 0xff */
+ if (id)