[alsa-devel] [PATCH RFC 2/4] ALSA: Add RME MADI FX Kconfig entries
Adrian Knoth
adi at drcomp.erfurt.thur.de
Mon Feb 4 17:24:57 CET 2013
On 02/03/2013 12:52 PM, Adrian Knoth wrote:
>> Also, the the mail on patch 4/4 seems to be lost - can you resend it?
> Weird, it was in my backup (cause I was automatically CCed), but neither
> gmane nor you received it.
It still doesn't show up. OK, let's include it here:
>From 3e779e85c63fb8746db2ce23a553ebc259776e3d Mon Sep 17 00:00:00 2001
From: Adrian Knoth <adi at drcomp.erfurt.thur.de>
Date: Fri, 1 Feb 2013 13:11:42 +0100
Subject: [PATCH RFC 4/4] ALSA: madifx - Add support for RME MADI FX
Early support for the new 390 channel RME MADI FX audio interface.
Current status:
* PCM playback/capture working (SS and DS tested, QS untested)
* MIDI probably working (untested)
* All card settings working (e.g. TX64, SMUX, AESpro, WC-Term,
WC-singlespeed...)
* Mirror-MADI1-to-Out2+3 maybe working (untested)
* Redundancy mode maybe working (untested)
* some ioctls implemented
* Static mixer working (fixed 1:1 mapping)
* DSP **NOT** working. RME doesn't intend to release any information
regarding the DSP.
* Adjustable mixer **NOT** working (needs new userspace tools)
* Levelmetering **NOT** working (maybe wrong, needs new userspace
tools)
See https://github.com/adiknoth/madifx/ for additional information and
helper tools.
Signed-off-by: Adrian Knoth <adi at drcomp.erfurt.thur.de>
diff --git a/sound/pci/rme9652/madifx.c b/sound/pci/rme9652/madifx.c
new file mode 100644
index 0000000..b02b59e
--- /dev/null
+++ b/sound/pci/rme9652/madifx.c
@@ -0,0 +1,3986 @@
+/*
+ * ALSA driver for RME Hammerfall DSP MADI FX audio interface(s)
+ *
+ * Based on hdspm.c
+ * Copyright (c) 2003 Winfried Ritsch (IEM)
+ * code based on hdsp.c Paul Davis
+ * Marcus Andersson
+ * Thomas Charbonnel
+ * Florian Faber
+ * Adrian Knoth
+ *
+ * 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 <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/math64.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#include <sound/rawmidi.h>
+#include <sound/hwdep.h>
+#include <sound/initval.h>
+
+#include <sound/madifx.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 bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable
this card */
+
+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_AUTHOR
+(
+ "Adrian Knoth <adi at drcomp.erfurt.thur.de>"
+);
+MODULE_DESCRIPTION("RME MADIFX");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADIFX}}");
+
+/* New mixer support is still work in progress. All references to the old
+ * mixer are disabled but shipped nevertheless to ease hacking.
+ */
+#define OLD_MIXER 0
+
+/* --- Write registers. ---
+ These are defined as byte-offsets from the iobase value. */
+
+#define MADIFX_CONTROL_REG (0*4)
+#define MADIFX_IRQ_ACK (3*4)
+#define MADIFX_FREQ_REG (1*4)
+#define MADIFX_SETTINGS_REG (2*4)
+#define MADIFX_START_LEVEL (6*4)
+#define MADIFX_midi_out0_data (8*4)
+#define MADIFX_midi_out1_data (9*4)
+#define MADIFX_midi_out2_data (10*4)
+#define MADIFX_midi_out3_data (11*4)
+#define MADIFX_ENABLE_OUTPUT (64*4)
+#define MADIFX_ENABLE_INPUT (96*4)
+#define MADIFX_MIXER_LIST_VOL (16384*4)
+#define MADIFX_MIXER_LIST_CH (20480*4)
+#define MADIFX_WR_OUTPUT_GAIN ((24576+256)*4)
+
+#define MADIFX_SAMPLE_FRAMES_PER_BUFFER 8192
+
+#define MADIFX_PAGE_ADDRESS_LIST (8192*4)
+
+/* page table size in entries, multiply by 4 to get byte offset */
+#define MADIFX_MAX_PAGE_TABLE_SIZE 4096
+#define MADIFX_LPTI_HMFX (MADIFX_MAX_PAGE_TABLE_SIZE/2+25*32768*8/4096)
+#define MADIFX_LPTI_MFXT (MADIFX_MAX_PAGE_TABLE_SIZE/2+26*32768*8/4096)
+
+#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 MADIFX_RD_STATUS (0*4)
+#define MADIFX_RD_INP_STATUS (1*4)
+#define MADIFX_RD_INP_FREQ (2*4)
+#define MADIFX_RD_PLL_FREQ (3*4)
+#define MADIFX_RD_VERSION (4*4) /* card_type(7..0) & "0000" &
build(19..0) */
+#define MADIFX_RD_FLASH (5*4)
+#define MADIFX_RD_BARCODE0 (6*4)
+#define MADIFX_RD_BARCODE1 (7*4)
+#define MADIFX_RD_DSP_DATA (8*4)
+#define MADIFX_RD_DSP_STATUS (9*4)
+#define MADIFX_midi_in0_data (12*4)
+#define MADIFX_midi_in1_data (13*4)
+#define MADIFX_midi_in2_data (14*4)
+#define MADIFX_midi_in3_data (15*4)
+#define MADIFX_midi_out0_status (16*4)
+#define MADIFX_midi_out1_status (17*4)
+#define MADIFX_midi_out2_status (18*4)
+#define MADIFX_midi_out3_status (19*4)
+#define MADIFX_midi_in0_status (20*4)
+#define MADIFX_midi_in1_status (21*4)
+#define MADIFX_midi_in2_status (22*4)
+#define MADIFX_midi_in3_status (23*4)
+
+/* input status */
+
+#define MADIFX_madi1_lock 0x0001
+#define MADIFX_madi2_lock 0x0002
+#define MADIFX_madi3_lock 0x0004
+#define MADIFX_aes_lock 0x0008
+#define MADIFX_word_lock 0x0010
+#define MADIFX_madi1_sync 0x0020
+#define MADIFX_madi2_sync 0x0040
+#define MADIFX_madi3_sync 0x0080
+#define MADIFX_word_sync 0x0100
+#define MADIFX_aes_sync 0x0200
+#define MADIFX_madi1_rx_64ch 0x0400
+#define MADIFX_madi2_rx_64ch 0x0800
+#define MADIFX_madi3_rx_64ch 0x1000
+#define MADIFX_SelSyncRef0 0x2000
+#define MADIFX_SelSyncRef1 0x4000
+#define MADIFX_SelSyncRef2 0x8000
+#define MADIFX_MADIInput0 0x10000
+#define MADIFX_MADIInput1 0x20000
+#define MADIFX_redundancy_rb 0x40000
+#define MADIFX_mirror_out_rb 0x80000
+#define MADIFX_sync_in_lock 0x100000
+#define MADIFX_sync_in_sync 0x200000
+
+/* control register bits */
+
+#define MADIFX_START 0x00000001
+#define MADIFX_freq0 0x00000002
+#define MADIFX_freq1 0x00000004
+#define MADIFX_freq2 0x00000008
+#define MADIFX_freq3 0x00000010
+#define MADIFX_BUF_SIZ_0 0x00000020
+#define MADIFX_BUF_SIZ_1 0x00000040
+#define MADIFX_BUF_SIZ_2 0x00000080
+#define MADIFX_LAT_0 0x00000100
+#define MADIFX_LAT_1 0x00000200
+#define MADIFX_LAT_2 0x00000400
+#define MADIFX_LAT_3 0x00000800
+#define MADIFX_IE_AUDIO 0x00001000
+#define MADIFX_IEN0 0x00002000
+#define MADIFX_IEN1 0x00004000
+#define MADIFX_IEN2 0x00008000
+#define MADIFX_IEN3 0x00010000
+#define MADIFX_float_format 0x00020000
+#define MADIFX_CLR_TMS 0x00040000
+#define MADIFX_Dolby 0x00080000
+
+#define MADIFX_kFrequencyMask (MADIFX_freq0 + MADIFX_freq1 +
MADIFX_freq2 + MADIFX_freq3)
+#define MADIFX_kBufferPositionMask 0xFFF0
+
+enum {
+ MADIFX_kFrequency32kHz = 0,
+ MADIFX_kFrequency44_1kHz = MADIFX_freq0,
+ MADIFX_kFrequency48kHz = MADIFX_freq1,
+ MADIFX_kFrequency64kHz = MADIFX_freq2 + 0,
+ MADIFX_kFrequency88_2kHz = MADIFX_freq2 + MADIFX_freq0,
+ MADIFX_kFrequency96kHz = MADIFX_freq2 + MADIFX_freq1,
+ MADIFX_kFrequency128kHz = MADIFX_freq3 + MADIFX_freq2 + 0,
+ MADIFX_kFrequency176_4kHz = MADIFX_freq3 + MADIFX_freq2 + MADIFX_freq0,
+ MADIFX_kFrequency192kHz = MADIFX_freq3 + MADIFX_freq2 + MADIFX_freq1
+};
+
+/* settings register bits */
+
+#define MADIFX_SyncRef0 0x00000001
+#define MADIFX_SyncRef1 0x00000002
+#define MADIFX_SyncRef2 0x00000004
+#define MADIFX_PRO 0x00000008
+#define MADIFX_DSP_EN 0x00000010
+#define MADIFX_WCK_TERM 0x00000020
+#define MADIFX_WCK48 0x00000040
+#define MADIFX_madi1_tx_64ch 0x00000080
+#define MADIFX_madi2_tx_64ch 0x00000100
+#define MADIFX_madi3_tx_64ch 0x00000200
+#define MADIFX_madi1_smux 0x00000400
+#define MADIFX_madi2_smux 0x00000800
+#define MADIFX_madi3_smux 0x00001000
+#define MADIFX_redundancy_mode 0x00002000
+#define MADIFX_mirror_madi_out 0x00004000
+
+#define MADIFX_SyncRefMask (MADIFX_SyncRef0 | MADIFX_SyncRef1 |
MADIFX_SyncRef2)
+
+/* input freq register bits */
+
+#define MADIFX_madi1_freq0 0x00001
+#define MADIFX_madi1_freq1 0x00002
+#define MADIFX_madi1_freq2 0x00004
+#define MADIFX_madi1_freq3 0x00008
+#define MADIFX_madi2_freq0 0x00010
+#define MADIFX_madi2_freq1 0x00020
+#define MADIFX_madi2_freq2 0x00040
+#define MADIFX_madi2_freq3 0x00080
+#define MADIFX_madi3_freq0 0x00100
+#define MADIFX_madi3_freq1 0x00200
+#define MADIFX_madi3_freq2 0x00400
+#define MADIFX_madi3_freq3 0x00800
+#define MADIFX_aes_freq0 0x01000
+#define MADIFX_aes_freq1 0x02000
+#define MADIFX_aes_freq2 0x04000
+#define MADIFX_aes_freq3 0x08000
+#define MADIFX_word_freq0 0x10000
+#define MADIFX_word_freq1 0x20000
+#define MADIFX_word_freq2 0x40000
+#define MADIFX_word_freq3 0x80000
+#define MADIFX_sync_in_freq0 0x100000
+#define MADIFX_sync_in_freq1 0x200000
+#define MADIFX_sync_in_freq2 0x400000
+#define MADIFX_sync_in_freq3 0x800000
+
+/* Index to DMA level buffer in uint32_t units */
+#define MADIFX_RD_RMS_IN (0*1)
+#define MADIFX_RD_PEAK_IN (512*1)
+#define MADIFX_RD_RMS_PLAY (1024*1)
+#define MADIFX_RD_PEAK_PLAY (1536*1)
+#define MADIFX_RD_RMS_OUT (2048*1)
+#define MADIFX_RD_PEAK_OUT (2560*1)
+#define MADIFX_RD_RMS_IN_PRE (3072*1)
+#define MADIFX_RD_PEAK_IN_PRE (3584*1)
+#define MADIFX_RD_RMS_OUT_PRE (4096*1)
+#define MADIFX_RD_PEAK_OUT_PRE (4608*1)
+
+/* MADIFX MIDI Interrupt enable */
+#define MADIFX_IEN0 0x00002000
+#define MADIFX_IEN1 0x00004000
+#define MADIFX_IEN2 0x00008000
+#define MADIFX_IEN3 0x00010000
+
+/* status register, MIDI IRQ Pending */
+#define MADIFX_mIRQ0 0x10000000
+#define MADIFX_mIRQ1 0x20000000
+#define MADIFX_mIRQ2 0x40000000
+#define MADIFX_mIRQ3 0x80000000
+
+/* --- bit helper defines */
+#define MADIFX_LatencyMask
(MADIFX_LAT_0|MADIFX_LAT_1|MADIFX_LAT_2|MADIFX_LAT_3)
+
+#define madifx_encode_latency(x) (((x)<<8) & MADIFX_LatencyMask)
+#define madifx_decode_latency(x) ((((x) & MADIFX_LatencyMask)>>8))
+
+/* speemode is enum 0,1,2 for ss/ds/qs, so (1<<speedmode) returns 1, 2,
4. */
+#define madifx_speed_multiplier(x) (1<<(x)->speedmode)
+
+
+#define HDSPM_audioIRQPending (1<<0) /* IRQ is high and pending */
+
+
+
+/* Mixer Values */
+#define UNITY_GAIN 32768 /* = 65536/2 */
+#define MINUS_INFINITY_GAIN 0
+
+/* Number of channels for different Speed Modes */
+#define MADIFX_SS_IN_CHANNELS 194
+#define MADIFX_DS_IN_CHANNELS 98
+#define MADIFX_QS_IN_CHANNELS 50
+
+#define MADIFX_SS_OUT_CHANNELS 196
+#define MADIFX_DS_OUT_CHANNELS 100
+#define MADIFX_QS_OUT_CHANNELS 52
+
+
+#define HDSPM_MADIFX_REV 213
+
+/* speed factor modes */
+#define HDSPM_SPEED_SINGLE 0
+#define HDSPM_SPEED_DOUBLE 1
+#define HDSPM_SPEED_QUAD 2
+
+/* DMA buffers in byte; 8192 samples per channel, each 4 bytes wide */
+#define NUM_INPUTS_S_MFXT (64*3+4)
+#define NUM_OUTPUTS_S_MFXT (64*3+6)
+#define INPUT_DMA_BUFFER_SIZE (NUM_INPUTS_S_MFXT*32768)
+#define OUTPUT_DMA_BUFFER_SIZE (NUM_OUTPUTS_S_MFXT*32768)
+
+
+
+/* names for speed modes */
+static char *madifx_speed_names[] = { "single", "double", "quad" };
+
+static char *texts_madifx_clock_source[] = {
+ "Internal",
+ "AES In",
+ "Word Clock",
+ "MADI 1 In",
+ "MADI 2 In",
+ "MADI 3 In",
+ "Sync In"
+};
+
+
+static char *texts_freq[] = {
+ "No Lock",
+ "32 kHz",
+ "44.1 kHz",
+ "48 kHz",
+ "64 kHz",
+ "88.2 kHz",
+ "96 kHz",
+ "128 kHz",
+ "176.4 kHz",
+ "192 kHz"
+};
+
+
+struct madifx_midi {
+ struct hdspm *hdspm;
+ int id;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *input;
+ struct snd_rawmidi_substream *output;
+ char istimer; /* timer in use */
+ struct timer_list timer;
+ spinlock_t lock;
+ int pending;
+ int dataIn;
+ int statusIn;
+ int dataOut;
+ int statusOut;
+ int ie;
+ int irq;
+};
+
+
+struct hdspm {
+ spinlock_t lock;
+ /* only one playback and/or capture stream */
+ struct snd_pcm_substream *capture_substream;
+ struct snd_pcm_substream *playback_substream;
+
+ char *card_name; /* for procinfo */
+ unsigned short firmware_rev;
+
+ uint8_t io_type;
+
+ int monitor_outs; /* set up monitoring outs init flag */
+
+ u32 control_register; /* cached value */
+ u32 control2_register; /* cached value */
+ u32 settings_register;
+
+ struct madifx_midi midi[4];
+ struct tasklet_struct midi_tasklet;
+
+ size_t period_bytes;
+ unsigned char ss_in_channels;
+ unsigned char ds_in_channels;
+ unsigned char qs_in_channels;
+ unsigned char ss_out_channels;
+ unsigned char ds_out_channels;
+ unsigned char qs_out_channels;
+
+ unsigned char max_channels_in;
+ unsigned char max_channels_out;
+
+ char **port_names_in;
+ char **port_names_out;
+
+ char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs;
+ char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs;
+
+ unsigned char *playback_buffer; /* suitably aligned address */
+ unsigned char *capture_buffer; /* suitably aligned address */
+ u32 *level_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;
+
+ int dev; /* Hardware vars... */
+ int irq;
+ unsigned long port;
+ void __iomem *iobase;
+
+ int irq_count; /* for debug */
+ int midiPorts;
+
+ struct snd_card *card; /* one card */
+ struct snd_pcm *pcm; /* has one pcm */
+ struct snd_hwdep *hwdep; /* and a hwdep for additional ioctl */
+ struct pci_dev *pci; /* and an pci info */
+
+ /* Mixer vars */
+#if OLD_MIXER
+ /* fast alsa mixer */
+ struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS];
+ /* but input to much, so not used */
+ struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS];
+#endif /* OLD_MIXER */
+ /* full mixer accessible over mixer ioctl or hwdep-device */
+ struct madifx_newmixer *newmixer;
+ dma_addr_t *dmaPageTable;
+ struct snd_dma_buffer dmaLevelBuffer;
+
+ char **texts_clocksource;
+ int texts_clocksource_items;
+
+ cycles_t last_interrupt;
+
+ unsigned int serial;
+
+ int speedmode;
+
+#ifdef CONFIG_SND_MADIFX_BROKEN
+ struct madifx_level_buffer peak_rms;
+#endif
+
+};
+
+
+static DEFINE_PCI_DEVICE_TABLE(snd_madifx_ids) = {
+ {
+ .vendor = PCI_VENDOR_ID_XILINX,
+ .device = 0x3fc7,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_madifx_ids);
+
+/* prototypes */
+static int snd_madifx_create_alsa_devices(struct snd_card *card,
+ struct hdspm *hdspm);
+static int snd_madifx_create_pcm(struct snd_card *card,
+ struct hdspm *hdspm);
+
+static inline void snd_madifx_initialize_midi_flush(struct hdspm *hdspm);
+static int madifx_external_freq_index(struct hdspm *hdspm, enum
madifx_syncsource port);
+static int madifx_get_clock_select(struct hdspm *hdspm);
+static int snd_madifx_set_defaults(struct hdspm *hdspm);
+static int madifx_system_clock_mode(struct hdspm *hdspm);
+
+static inline int HDSPM_bit2freq(int n)
+{
+ static const 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 */
+
+static inline void madifx_write(struct hdspm *hdspm, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, hdspm->iobase + reg);
+}
+
+static inline unsigned int madifx_read(struct hdspm *hdspm, unsigned
int reg)
+{
+ return readl(hdspm->iobase + reg);
+}
+
+#if OLD_MIXER
+/* 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 madifx_read_in_gain(struct hdspm *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 madifx_read_pb_gain(struct hdspm *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 int madifx_write_in_gain(struct hdspm *hdspm, unsigned int chan,
+ unsigned int in, unsigned short data)
+{
+ if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
+ return -1;
+
+ madifx_write(hdspm,
+ HDSPM_MADI_mixerBase +
+ ((in + 128 * chan) * sizeof(u32)),
+ (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF));
+ return 0;
+}
+
+static int madifx_write_pb_gain(struct hdspm *hdspm, unsigned int chan,
+ unsigned int pb, unsigned short data)
+{
+ if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
+ return -1;
+
+ madifx_write(hdspm,
+ HDSPM_MADI_mixerBase +
+ ((64 + pb + 128 * chan) * sizeof(u32)),
+ (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF));
+ return 0;
+}
+#endif /* OLD_MIXER */
+
+
+/* enable DMA for specific channels, now available for DSP-MADI */
+static inline void snd_madifx_enable_in(struct hdspm *hdspm, int i, int v)
+{
+ madifx_write(hdspm, MADIFX_ENABLE_INPUT + (4 * i), v);
+}
+
+static inline void snd_madifx_enable_out(struct hdspm *hdspm, int i, int v)
+{
+ madifx_write(hdspm, MADIFX_ENABLE_OUTPUT + (4 * i), v);
+}
+
+/* check if same process is writing and reading */
+static int snd_madifx_use_is_exclusive(struct hdspm *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;
+}
+
+
+/* return latency in samples per period */
+static int madifx_get_latency(struct hdspm *hdspm)
+{
+ int n;
+
+ n = madifx_decode_latency(hdspm->control_register);
+
+ return 1 << (n + 5);
+}
+
+/* Latency function */
+static inline void madifx_compute_period_size(struct hdspm *hdspm)
+{
+ hdspm->period_bytes = 4 * madifx_get_latency(hdspm);
+}
+
+
+/* position of the hardware pointer in the buffer */
+static snd_pcm_uframes_t madifx_hw_pointer(struct hdspm *hdspm)
+{
+ u32 position;
+
+ position = madifx_read(hdspm, MADIFX_RD_STATUS);
+
+ position &= MADIFX_kBufferPositionMask;
+ position >>= 4;
+ position *= 4;
+#if 0
+ position -= 4; /* safety offset */
+#endif
+ position &= (MADIFX_SAMPLE_FRAMES_PER_BUFFER-1);
+
+ return position;
+}
+
+static inline void madifx_start_audio(struct hdspm *s)
+{
+ s->control_register |= (MADIFX_IE_AUDIO | MADIFX_START);
+ madifx_write(s, MADIFX_CONTROL_REG, s->control_register);
+}
+
+static inline void madifx_stop_audio(struct hdspm *s)
+{
+ s->control_register &= ~(MADIFX_START | MADIFX_IE_AUDIO);
+ madifx_write(s, MADIFX_CONTROL_REG, s->control_register);
+}
+
+static void madifx_silence_playback(struct hdspm *hdspm)
+{
+ void *buf = hdspm->playback_buffer;
+
+ if (buf == NULL)
+ return;
+
+ memset(buf, 0, OUTPUT_DMA_BUFFER_SIZE);
+}
+
+static int madifx_set_interrupt_interval(struct hdspm *s, unsigned int
frames)
+{
+ int n;
+
+ spin_lock_irq(&s->lock);
+
+ /* FIXME: We have four bits, but we don't know the mapping to frames,
+ * yet.
+ * LAT_3 is 2048
+ * LAT_2 is 128
+ * LAT_1 is 32
+ * LAT_0 is 16
+ *
+ * 2^(n+4), encode n to 4 bits
+ */
+ n = 0;
+ frames >>= 6;
+ while (frames) {
+ n++;
+ frames >>= 1;
+ }
+
+ s->control_register &= ~MADIFX_LatencyMask;
+ s->control_register |= madifx_encode_latency(n);
+
+ madifx_write(s, MADIFX_CONTROL_REG, s->control_register);
+
+ madifx_compute_period_size(s);
+
+ spin_unlock_irq(&s->lock);
+
+ return 0;
+}
+
+static u64 madifx_calc_dds_value(struct hdspm *hdspm, u64 period)
+{
+ u64 freq_const;
+
+ if (period == 0)
+ return 0;
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ freq_const = 131072000000000ULL;
+ break;
+ default:
+ snd_BUG();
+ return 0;
+ }
+
+ return div_u64(freq_const, period);
+}
+
+
+static void madifx_set_dds_value(struct hdspm *hdspm, int rate)
+{
+ u64 n;
+
+ if (rate >= 112000)
+ rate /= 4;
+ else if (rate >= 56000)
+ rate /= 2;
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ n = 131072000000000ULL; /* 125 MHz */
+ break;
+ default:
+ snd_BUG();
+ return;
+ }
+
+ n = div_u64(n, rate);
+ /* n should be less than 2^32 for being written to FREQ register */
+ snd_BUG_ON(n >> 32);
+ madifx_write(hdspm, MADIFX_FREQ_REG, (u32)n);
+}
+
+static int madifx_get_external_rate(struct hdspm *hdspm)
+{
+ int current_clock = madifx_get_clock_select(hdspm);
+
+ switch (current_clock) {
+ case 0:
+ case 6:
+ /* Master or Sync-In */
+ break;
+ case 1:
+ case 2:
+ /* 1 == AES, 2 == WC; map to enum madifx_syncsource,
+ * so that 3 == AES, 4 == WC */
+ current_clock += 2;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ /* MADI1 == 3, MADI2 == 4, MADI3 == 5 map to
+ * MADI1 == 0, MADI2 = 1, MADI3 == 2
+ */
+ current_clock -= 3;
+ break;
+ default:
+ snd_printk(KERN_ERR "MADIFX: Unknown clock source\n");
+ return 0;
+ }
+
+
+
+ return HDSPM_bit2freq(madifx_external_freq_index(hdspm, current_clock));
+}
+
+/* dummy set rate lets see what happens */
+static int madifx_set_rate(struct hdspm *hdspm, int rate, int
called_internally)
+{
+ int current_rate;
+ int rate_bits;
+ int not_set = 0;
+ int current_speed, target_speed;
+
+ /* ASSUMPTION: hdspm->lock is either set, or there is no need for
+ it (e.g. during module initialization).
+ */
+
+ if (1 == madifx_system_clock_mode(hdspm)) {
+
+ /* 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
+ "MADIFX: Warning: device is not running as a clock master.\n");
+ not_set = 1;
+ } else {
+ int external_freq = madifx_get_external_rate(hdspm);
+
+
+
+ if (rate != external_freq) {
+ snd_printk(KERN_WARNING
+ "MADIFX: 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.
+ */
+
+ if (current_rate <= 56000)
+ current_speed = HDSPM_SPEED_SINGLE;
+ else if (current_rate <= 96000)
+ current_speed = HDSPM_SPEED_DOUBLE;
+ else
+ current_speed = HDSPM_SPEED_QUAD;
+
+ if (rate <= 48000)
+ target_speed = HDSPM_SPEED_SINGLE;
+ else if (rate <= 112000)
+ target_speed = HDSPM_SPEED_DOUBLE;
+ else
+ target_speed = HDSPM_SPEED_QUAD;
+
+ switch (rate) {
+ case 32000:
+ rate_bits = MADIFX_kFrequency32kHz;
+ break;
+ case 44100:
+ rate_bits = MADIFX_kFrequency44_1kHz;
+ break;
+ case 48000:
+ rate_bits = MADIFX_kFrequency48kHz;
+ break;
+ case 64000:
+ rate_bits = MADIFX_kFrequency64kHz;
+ break;
+ case 88200:
+ rate_bits = MADIFX_kFrequency88_2kHz;
+ break;
+ case 96000:
+ rate_bits = MADIFX_kFrequency96kHz;
+ break;
+ case 128000:
+ rate_bits = MADIFX_kFrequency128kHz;
+ break;
+ case 176400:
+ rate_bits = MADIFX_kFrequency176_4kHz;
+ break;
+ case 192000:
+ rate_bits = MADIFX_kFrequency192kHz;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (current_speed != target_speed
+ && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
+ snd_printk
+ (KERN_ERR "HDSPM: "
+ "cannot change from %s speed to %s speed mode "
+ "(capture PID = %d, playback PID = %d)\n",
+ madifx_speed_names[current_speed],
+ madifx_speed_names[target_speed],
+ hdspm->capture_pid, hdspm->playback_pid);
+ return -EBUSY;
+ }
+
+ madifx_set_dds_value(hdspm, rate);
+
+ hdspm->control_register &= ~MADIFX_kFrequencyMask;
+ hdspm->control_register |= rate_bits;
+ madifx_write(hdspm, MADIFX_CONTROL_REG, hdspm->control_register);
+
+
+
+ hdspm->system_sample_rate = rate;
+
+ if (rate <= 56000) {
+ hdspm->max_channels_in = hdspm->ss_in_channels;
+ hdspm->max_channels_out = hdspm->ss_out_channels;
+ hdspm->port_names_in = hdspm->port_names_in_ss;
+ hdspm->port_names_out = hdspm->port_names_out_ss;
+ hdspm->speedmode = ss;
+ } else if (rate <= 112000) {
+ hdspm->max_channels_in = hdspm->ds_in_channels;
+ hdspm->max_channels_out = hdspm->ds_out_channels;
+ hdspm->port_names_in = hdspm->port_names_in_ds;
+ hdspm->port_names_out = hdspm->port_names_out_ds;
+ hdspm->speedmode = ds;
+ } else {
+ hdspm->max_channels_in = hdspm->qs_in_channels;
+ hdspm->max_channels_out = hdspm->qs_out_channels;
+ hdspm->port_names_in = hdspm->port_names_in_qs;
+ hdspm->port_names_out = hdspm->port_names_out_qs;
+ hdspm->speedmode = qs;
+ }
+
+ if (not_set != 0)
+ return -1;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+ MIDI
+
----------------------------------------------------------------------------*/
+
+static inline unsigned char snd_madifx_midi_read_byte(struct hdspm *hdspm,
+ int id)
+{
+ /* the hardware already does the relevant bit-mask with 0xff */
+ return madifx_read(hdspm, hdspm->midi[id].dataIn);
+}
+
+static inline void snd_madifx_midi_write_byte(struct hdspm *hdspm, int id,
+ int val)
+{
+ /* the hardware already does the relevant bit-mask with 0xff */
+ return madifx_write(hdspm, hdspm->midi[id].dataOut, val);
+}
+
+static inline int snd_madifx_midi_input_available(struct hdspm *hdspm,
int id)
+{
+ return madifx_read(hdspm, hdspm->midi[id].statusIn) & 0xFF;
+}
+
+static inline int snd_madifx_midi_output_possible(struct hdspm *hdspm,
int id)
+{
+ int fifo_bytes_used;
+
+ fifo_bytes_used = madifx_read(hdspm, hdspm->midi[id].statusOut) & 0xFF;
+
+ if (fifo_bytes_used < 128)
+ return 128 - fifo_bytes_used;
+ else
+ return 0;
+}
+
+static void snd_madifx_flush_midi_input(struct hdspm *hdspm, int id)
+{
+ while (snd_madifx_midi_input_available(hdspm, id))
+ snd_madifx_midi_read_byte(hdspm, id);
+}
+
+static int snd_madifx_midi_output_write(struct madifx_midi *hmidi)
+{
+ unsigned long flags;
+ int n_pending;
+ int to_write;
+ int i;
+ unsigned char buf[128];
+
+ /* Output is not interrupt driven */
+
+ spin_lock_irqsave(&hmidi->lock, flags);
+ if (hmidi->output &&
+ !snd_rawmidi_transmit_empty(hmidi->output)) {
+ n_pending = snd_madifx_midi_output_possible(hmidi->hdspm,
+ hmidi->id);
+ if (n_pending > 0) {
+ if (n_pending > (int)sizeof(buf))
+ n_pending = sizeof(buf);
+
+ to_write = snd_rawmidi_transmit(hmidi->output, buf,
+ n_pending);
+ if (to_write > 0) {
+ for (i = 0; i < to_write; ++i)
+ snd_madifx_midi_write_byte(hmidi->hdspm,
+ hmidi->id,
+ buf[i]);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&hmidi->lock, flags);
+ return 0;
+}
+
+static int snd_madifx_midi_input_read(struct madifx_midi *hmidi)
+{
+ unsigned char buf[128]; /* this buffer is designed to match the MIDI
+ * input FIFO size
+ */
+ unsigned long flags;
+ int n_pending;
+ int i;
+
+ spin_lock_irqsave(&hmidi->lock, flags);
+ n_pending = snd_madifx_midi_input_available(hmidi->hdspm, hmidi->id);
+ if (n_pending > 0) {
+ if (hmidi->input) {
+ if (n_pending > (int)sizeof(buf))
+ n_pending = sizeof(buf);
+ for (i = 0; i < n_pending; ++i)
+ buf[i] = snd_madifx_midi_read_byte(hmidi->hdspm,
+ hmidi->id);
+ if (n_pending)
+ snd_rawmidi_receive(hmidi->input, buf,
+ n_pending);
+ } else {
+ /* flush the MIDI input FIFO */
+ while (n_pending--)
+ snd_madifx_midi_read_byte(hmidi->hdspm,
+ hmidi->id);
+ }
+ }
+ hmidi->pending = 0;
+ spin_unlock_irqrestore(&hmidi->lock, flags);
+
+ spin_lock_irqsave(&hmidi->hdspm->lock, flags);
+ hmidi->hdspm->control_register |= hmidi->ie;
+ madifx_write(hmidi->hdspm, MADIFX_CONTROL_REG,
+ hmidi->hdspm->control_register);
+ spin_unlock_irqrestore(&hmidi->hdspm->lock, flags);
+
+ return snd_madifx_midi_output_write(hmidi);
+}
+
+static void
+snd_madifx_midi_input_trigger(struct snd_rawmidi_substream *substream,
int up)
+{
+ struct hdspm *hdspm;
+ struct madifx_midi *hmidi;
+ unsigned long flags;
+
+ hmidi = substream->rmidi->private_data;
+ hdspm = hmidi->hdspm;
+
+ spin_lock_irqsave(&hdspm->lock, flags);
+ if (up) {
+ if (!(hdspm->control_register & hmidi->ie)) {
+ snd_madifx_flush_midi_input(hdspm, hmidi->id);
+ hdspm->control_register |= hmidi->ie;
+ }
+ } else {
+ hdspm->control_register &= ~hmidi->ie;
+ }
+
+ madifx_write(hdspm, MADIFX_CONTROL_REG, hdspm->control_register);
+ spin_unlock_irqrestore(&hdspm->lock, flags);
+}
+
+static void snd_madifx_midi_output_timer(unsigned long data)
+{
+ struct madifx_midi *hmidi = (struct madifx_midi *) data;
+ unsigned long flags;
+
+ snd_madifx_midi_output_write(hmidi);
+ spin_lock_irqsave(&hmidi->lock, flags);
+
+ /* this does not bump hmidi->istimer, because the
+ kernel automatically removed the timer when it
+ expired, and we are now adding it back, thus
+ leaving istimer wherever it was set before.
+ */
+
+ if (hmidi->istimer) {
+ hmidi->timer.expires = 1 + jiffies;
+ add_timer(&hmidi->timer);
+ }
+
+ spin_unlock_irqrestore(&hmidi->lock, flags);
+}
+
+static void
+snd_madifx_midi_output_trigger(struct snd_rawmidi_substream *substream,
int up)
+{
+ struct madifx_midi *hmidi;
+ unsigned long flags;
+
+ hmidi = substream->rmidi->private_data;
+ spin_lock_irqsave(&hmidi->lock, flags);
+ if (up) {
+ if (!hmidi->istimer) {
+ init_timer(&hmidi->timer);
+ hmidi->timer.function = snd_madifx_midi_output_timer;
+ hmidi->timer.data = (unsigned long) hmidi;
+ hmidi->timer.expires = 1 + jiffies;
+ add_timer(&hmidi->timer);
+ hmidi->istimer++;
+ }
+ } else {
+ if (hmidi->istimer && --hmidi->istimer <= 0)
+ del_timer(&hmidi->timer);
+ }
+ spin_unlock_irqrestore(&hmidi->lock, flags);
+ if (up)
+ snd_madifx_midi_output_write(hmidi);
+}
+
+static int snd_madifx_midi_input_open(struct snd_rawmidi_substream
*substream)
+{
+ struct madifx_midi *hmidi;
+
+ hmidi = substream->rmidi->private_data;
+ spin_lock_irq(&hmidi->lock);
+ snd_madifx_flush_midi_input(hmidi->hdspm, hmidi->id);
+ hmidi->input = substream;
+ spin_unlock_irq(&hmidi->lock);
+
+ return 0;
+}
+
+static int snd_madifx_midi_output_open(struct snd_rawmidi_substream
*substream)
+{
+ struct madifx_midi *hmidi;
+
+ hmidi = substream->rmidi->private_data;
+ spin_lock_irq(&hmidi->lock);
+ hmidi->output = substream;
+ spin_unlock_irq(&hmidi->lock);
+
+ return 0;
+}
+
+static int snd_madifx_midi_input_close(struct snd_rawmidi_substream
*substream)
+{
+ struct madifx_midi *hmidi;
+
+ snd_madifx_midi_input_trigger(substream, 0);
+
+ hmidi = substream->rmidi->private_data;
+ spin_lock_irq(&hmidi->lock);
+ hmidi->input = NULL;
+ spin_unlock_irq(&hmidi->lock);
+
+ return 0;
+}
+
+static int snd_madifx_midi_output_close(struct snd_rawmidi_substream
*substream)
+{
+ struct madifx_midi *hmidi;
+
+ snd_madifx_midi_output_trigger(substream, 0);
+
+ hmidi = substream->rmidi->private_data;
+ spin_lock_irq(&hmidi->lock);
+ hmidi->output = NULL;
+ spin_unlock_irq(&hmidi->lock);
+
+ return 0;
+}
+
+static struct snd_rawmidi_ops snd_madifx_midi_output = {
+ .open = snd_madifx_midi_output_open,
+ .close = snd_madifx_midi_output_close,
+ .trigger = snd_madifx_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_madifx_midi_input = {
+ .open = snd_madifx_midi_input_open,
+ .close = snd_madifx_midi_input_close,
+ .trigger = snd_madifx_midi_input_trigger,
+};
+
+static int snd_madifx_create_midi(struct snd_card *card,
+ struct hdspm *hdspm, int id)
+{
+ int err;
+ char buf[32];
+
+ hdspm->midi[id].id = id;
+ hdspm->midi[id].hdspm = hdspm;
+ spin_lock_init(&hdspm->midi[id].lock);
+
+ switch (id) {
+ case 0:
+ hdspm->midi[0].dataIn = MADIFX_midi_in0_data;
+ hdspm->midi[0].statusIn = MADIFX_midi_in0_status;
+ hdspm->midi[0].dataOut = MADIFX_midi_out0_data;
+ hdspm->midi[0].statusOut = MADIFX_midi_out0_status;
+ hdspm->midi[0].ie = MADIFX_IEN0;
+ hdspm->midi[0].irq = MADIFX_mIRQ0;
+ break;
+
+ case 1:
+ hdspm->midi[1].dataIn = MADIFX_midi_in1_data;
+ hdspm->midi[1].statusIn = MADIFX_midi_in1_status;
+ hdspm->midi[1].dataOut = MADIFX_midi_out1_data;
+ hdspm->midi[1].statusOut = MADIFX_midi_out1_status;
+ hdspm->midi[1].ie = MADIFX_IEN1;
+ hdspm->midi[1].irq = MADIFX_mIRQ1;
+ break;
+
+ case 2:
+ hdspm->midi[2].dataIn = MADIFX_midi_in2_data;
+ hdspm->midi[2].statusIn = MADIFX_midi_in2_status;
+ hdspm->midi[2].dataOut = MADIFX_midi_out2_data;
+ hdspm->midi[2].statusOut = MADIFX_midi_out2_status;
+ hdspm->midi[2].ie = MADIFX_IEN2;
+ hdspm->midi[2].irq = MADIFX_mIRQ2;
+ break;
+
+ case 3:
+ hdspm->midi[3].dataIn = MADIFX_midi_in3_data;
+ hdspm->midi[3].statusIn = MADIFX_midi_in3_status;
+ hdspm->midi[3].dataOut = MADIFX_midi_out3_data;
+ hdspm->midi[3].statusOut = MADIFX_midi_out3_status;
+ hdspm->midi[3].ie = MADIFX_IEN3;
+ hdspm->midi[3].irq = MADIFX_mIRQ3;
+ break;
+
+ default:
+ snd_printk(KERN_ERR "MADIFX: Unknown MIDI port %i\n", id);
+ return -EINVAL;
+
+ }
+
+ sprintf(buf, "%s MIDIoverMADI %d", card->shortname, id+1);
+
+ err = snd_rawmidi_new(card, buf, id, 1, 1,
+ &hdspm->midi[id].rmidi);
+ if (err < 0)
+ return err;
+
+ sprintf(hdspm->midi[id].rmidi->name, "%s MIDI %d",
+ card->id, id+1);
+ hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
+
+ snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+ SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_madifx_midi_output);
+ snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_madifx_midi_input);
+
+ hdspm->midi[id].rmidi->info_flags |=
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
+
+
+static void madifx_midi_tasklet(unsigned long arg)
+{
+ struct hdspm *hdspm = (struct hdspm *)arg;
+ int i = 0;
+
+ while (i < hdspm->midiPorts) {
+ if (hdspm->midi[i].pending)
+ snd_madifx_midi_input_read(&hdspm->midi[i]);
+
+ i++;
+ }
+}
+
+
+/*-----------------------------------------------------------------------------
+ Status Interface
+
----------------------------------------------------------------------------*/
+
+/* get the system sample rate which is set */
+
+
+/**
+ * Calculate the real sample rate from the
+ * current DDS value.
+ **/
+static int madifx_get_system_sample_rate(struct hdspm *hdspm)
+{
+ unsigned int period, rate;
+
+ period = madifx_read(hdspm, MADIFX_RD_PLL_FREQ);
+ rate = madifx_calc_dds_value(hdspm, period) *
+ madifx_speed_multiplier(hdspm);
+
+ return rate;
+}
+
+
+#define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_madifx_info_system_sample_rate, \
+ .put = snd_madifx_put_system_sample_rate, \
+ .get = snd_madifx_get_system_sample_rate \
+}
+
+static int snd_madifx_info_system_sample_rate(struct snd_kcontrol
*kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 27000;
+ uinfo->value.integer.max = 207000;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+
+static int snd_madifx_get_system_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *
+ ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = madifx_get_system_sample_rate(hdspm);
+ return 0;
+}
+
+static int snd_madifx_put_system_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *
+ ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ madifx_set_dds_value(hdspm, ucontrol->value.enumerated.item[0]);
+ return 0;
+}
+
+
+
+static int madifx_external_freq_index(struct hdspm *hdspm, enum
madifx_syncsource port)
+{
+ int i = 0;
+ int freq0_bit;
+ int inp_freq;
+ int lock_bit;
+ int inp_status;
+
+ inp_status = madifx_read(hdspm, MADIFX_RD_INP_STATUS);
+ inp_freq = madifx_read(hdspm, MADIFX_RD_INP_FREQ);
+
+ switch (port) {
+ case syncsource_madi1:
+ lock_bit = MADIFX_madi1_lock;
+ freq0_bit = MADIFX_madi1_freq0;
+ break;
+ case syncsource_madi2:
+ lock_bit = MADIFX_madi2_lock;
+ freq0_bit = MADIFX_madi2_freq0;
+ break;
+ case syncsource_madi3:
+ lock_bit = MADIFX_madi3_lock;
+ freq0_bit = MADIFX_madi3_freq0;
+ break;
+ case syncsource_aes:
+ lock_bit = MADIFX_aes_lock;
+ freq0_bit = MADIFX_aes_freq0;
+ break;
+ case syncsource_wc:
+ lock_bit = MADIFX_word_lock;
+ freq0_bit = MADIFX_word_freq0;
+ break;
+ case syncsource_syncin:
+ lock_bit = MADIFX_sync_in_lock;
+ freq0_bit = MADIFX_sync_in_freq0;
+ break;
+ default:
+ snd_printk(KERN_ERR "MADIFX: Unknown external port ID %i\n", port);
+ return 0;
+ }
+
+ if (!(inp_status & lock_bit)) {
+ i = 0;
+ } else {
+ int freq_bits = inp_freq & (freq0_bit*15);
+ if (freq_bits == freq0_bit * 1)
+ i = 1;
+ else if (freq_bits == freq0_bit * 2)
+ i = 2;
+ else if (freq_bits == freq0_bit * 3)
+ i = 3;
+ else if (freq_bits == freq0_bit * 4)
+ i = 4;
+ else if (freq_bits == freq0_bit * 5)
+ i = 5;
+ else if (freq_bits == freq0_bit * 6)
+ i = 6;
+ else if (freq_bits == freq0_bit * 7)
+ i = 7;
+ else if (freq_bits == freq0_bit * 8)
+ i = 8;
+ else if (freq_bits == freq0_bit * 9)
+ i = 9;
+ else
+ i = 0;
+ }
+ return i;
+}
+
+
+#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_madifx_info_autosync_sample_rate, \
+ .get = snd_madifx_get_autosync_sample_rate \
+}
+
+
+static int snd_madifx_info_autosync_sample_rate(struct snd_kcontrol
*kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ 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_freq[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+
+static int snd_madifx_get_autosync_sample_rate(struct snd_kcontrol
*kcontrol,
+ struct snd_ctl_elem_value *
+ ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ ucontrol->value.enumerated.item[0] =
+ madifx_external_freq_index(hdspm, kcontrol->private_value);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#define MADIFX_MADI_CHANNELCOUNT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+ .info = snd_madifx_info_channelcount, \
+ .get = snd_madifx_get_channelcount \
+}
+
+
+static int snd_madifx_info_channelcount(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "64", "56", "32", "28", "16", "14", "No lock" };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 7;
+ 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 madifx_get_madichannelcount(struct hdspm *hdspm, int idx)
+{
+ int rate_index;
+ int i = 0;
+ int inp_status;
+ int rx_64ch_bit = (MADIFX_madi1_rx_64ch << idx);
+
+ inp_status = madifx_read(hdspm, MADIFX_RD_INP_STATUS);
+
+
+ /* Check for speed. If rate_index is zero, there's no lock */
+ rate_index = madifx_external_freq_index(hdspm, idx);
+ if (0 == rate_index)
+ i = 6;
+ else {
+ if (inp_status & rx_64ch_bit)
+ i = 0;
+ else
+ i = 1;
+
+ /* I know this is ugly */
+ if (rate_index > 3)
+ i += 2; /* Double speed */
+ if (rate_index > 6)
+ i += 2; /* Quad speed */
+ }
+
+ return i;
+
+}
+
+
+static int snd_madifx_get_channelcount(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *
+ ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+
+
+ ucontrol->value.enumerated.item[0] =
+ madifx_get_madichannelcount(hdspm, idx);
+
+ return 0;
+}
+
+
+
+/**
+ * Returns the system clock mode for the given card.
+ * @returns 0 - master, 1 - slave
+ **/
+static int madifx_system_clock_mode(struct hdspm *hdspm)
+{
+ u32 status;
+
+ status = madifx_read(hdspm, MADIFX_RD_INP_STATUS);
+ if ((status & (MADIFX_SelSyncRef0 * 7)) == (MADIFX_SelSyncRef0 * 7))
+ return 0;
+
+ return 1;
+}
+
+
+#define HDSPM_INTERNAL_CLOCK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_madifx_info_clock_source, \
+ .get = snd_madifx_get_clock_source, \
+ .put = snd_madifx_put_clock_source \
+}
+
+
+static int madifx_clock_source(struct hdspm *hdspm)
+{
+ switch (hdspm->system_sample_rate) {
+ case 32000: return 0;
+ case 44100: return 1;
+ case 48000: return 2;
+ case 64000: return 3;
+ case 88200: return 4;
+ case 96000: return 5;
+ case 128000: return 6;
+ case 176400: return 7;
+ case 192000: return 8;
+ }
+
+ return -1;
+}
+
+static int madifx_set_clock_source(struct hdspm *hdspm, int mode)
+{
+ int rate;
+ switch (mode) {
+ case 0:
+ rate = 32000; break;
+ case 1:
+ rate = 44100; break;
+ case 2:
+ rate = 48000; break;
+ case 3:
+ rate = 64000; break;
+ case 4:
+ rate = 88200; break;
+ case 5:
+ rate = 96000; break;
+ case 6:
+ rate = 128000; break;
+ case 7:
+ rate = 176400; break;
+ case 8:
+ rate = 192000; break;
+ default:
+ rate = 48000;
+ }
+ madifx_set_rate(hdspm, rate, 1);
+ return 0;
+}
+
+static int snd_madifx_info_clock_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 9;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+
+ strcpy(uinfo->value.enumerated.name,
+ texts_freq[uinfo->value.enumerated.item+1]);
+
+ return 0;
+}
+
+static int snd_madifx_get_clock_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = madifx_clock_source(hdspm);
+ return 0;
+}
+
+static int snd_madifx_put_clock_source(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_madifx_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.enumerated.item[0];
+ if (val < 0)
+ val = 0;
+ if (val > 9)
+ val = 9;
+ spin_lock_irq(&hdspm->lock);
+ if (val != madifx_clock_source(hdspm))
+ change = (madifx_set_clock_source(hdspm, val) == 0) ? 1 : 0;
+ else
+ change = 0;
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
+
+static int madifx_get_clock_select(struct hdspm *hdspm)
+{
+ switch (hdspm->io_type) {
+ case MADIFX:
+ {
+ int i;
+ u32 status;
+
+ status = madifx_read(hdspm, MADIFX_RD_INP_STATUS);
+
+ switch (status & (MADIFX_SelSyncRef0 * 7)) {
+ case MADIFX_SelSyncRef0 * 0:
+ i = 0; break;
+ case MADIFX_SelSyncRef0 * 1:
+ i = 1; break;
+ case MADIFX_SelSyncRef0 * 2:
+ i = 2; break;
+ case MADIFX_SelSyncRef0 * 3:
+ i = 3; break;
+ case MADIFX_SelSyncRef0 * 4:
+ i = 4; break;
+ case MADIFX_SelSyncRef0 * 5:
+ i = 5; break;
+ case MADIFX_SelSyncRef0 * 6:
+ i = 6; break;
+ default:
+ /* We are master */
+ i = 0;
+ break;
+ }
+ return i;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static int madifx_set_clock_select(struct hdspm *hdspm, int val)
+{
+ hdspm->settings_register &= ~MADIFX_SyncRefMask;
+ hdspm->settings_register |= MADIFX_SyncRef0 * val;
+ madifx_write(hdspm, MADIFX_SETTINGS_REG, hdspm->settings_register);
+
+ if (val > 0) {
+ /* switched to slave mode */
+ hdspm->system_sample_rate = madifx_get_external_rate(hdspm);
+ }
+
+ return 0;
+}
+
+
+
+#define MADIFX_CLOCK_SELECT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_madifx_info_clock_select, \
+ .get = snd_madifx_get_clock_select, \
+ .put = snd_madifx_put_clock_select \
+}
+
+
+static int snd_madifx_info_clock_select(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = hdspm->texts_clocksource_items;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+
+ strcpy(uinfo->value.enumerated.name,
+ hdspm->texts_clocksource[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int snd_madifx_get_clock_select(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int psf = madifx_get_clock_select(hdspm);
+
+ if (psf >= 0) {
+ ucontrol->value.enumerated.item[0] = psf;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int snd_madifx_put_clock_select(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int val, change = 0;
+
+ if (!snd_madifx_use_is_exclusive(hdspm))
+ return -EBUSY;
+
+ val = ucontrol->value.enumerated.item[0];
+
+ if (val < 0)
+ val = 0;
+ else if (val >= hdspm->texts_clocksource_items)
+ val = hdspm->texts_clocksource_items-1;
+
+ spin_lock_irq(&hdspm->lock);
+ if (val != madifx_get_clock_select(hdspm))
+ change = (0 == madifx_set_clock_select(hdspm, val)) ? 1 : 0;
+
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
+
+
+#define MADIFX_TOGGLE_SETTING(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .info = snd_madifx_info_toggle_setting, \
+ .get = snd_madifx_get_toggle_setting, \
+ .put = snd_madifx_put_toggle_setting \
+}
+
+static int madifx_read_toggle_setting(struct hdspm *hdspm, u32 reg)
+{
+ return (hdspm->settings_register & (reg)) ? 1 : 0;
+}
+
+static int madifx_set_toggle_setting(struct hdspm *hdspm, u32 reg, int out)
+{
+ if (out)
+ hdspm->settings_register |= (reg);
+ else
+ hdspm->settings_register &= ~(reg);
+ madifx_write(hdspm, MADIFX_SETTINGS_REG, hdspm->settings_register);
+
+ return 0;
+}
+
+#define snd_madifx_info_toggle_setting snd_ctl_boolean_mono_info
+
+static int snd_madifx_get_toggle_setting(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.integer.value[0] = madifx_read_toggle_setting(hdspm,
+ kcontrol->private_value);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_madifx_put_toggle_setting(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ u32 reg = kcontrol->private_value;
+ unsigned int val;
+
+ if (!snd_madifx_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdspm->lock);
+ change = (int) val != madifx_read_toggle_setting(hdspm, reg);
+ madifx_set_toggle_setting(hdspm, reg, val);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+
+
+
+#define HDSPM_MIXER(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+ .name = xname, \
+ .index = xindex, \
+ .device = 0, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_madifx_info_mixer, \
+ .get = snd_madifx_get_mixer, \
+ .put = snd_madifx_put_mixer \
+}
+
+#if OLD_MIXER
+static int snd_madifx_info_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 65535;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int snd_madifx_get_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int source;
+ int destination;
+
+ source = ucontrol->value.integer.value[0];
+ if (source < 0)
+ source = 0;
+ else if (source >= 2 * HDSPM_MAX_CHANNELS)
+ source = 2 * HDSPM_MAX_CHANNELS - 1;
+
+ destination = ucontrol->value.integer.value[1];
+ if (destination < 0)
+ destination = 0;
+ else if (destination >= HDSPM_MAX_CHANNELS)
+ destination = HDSPM_MAX_CHANNELS - 1;
+
+ spin_lock_irq(&hdspm->lock);
+ if (source >= HDSPM_MAX_CHANNELS)
+ ucontrol->value.integer.value[2] =
+ madifx_read_pb_gain(hdspm, destination,
+ source - HDSPM_MAX_CHANNELS);
+ else
+ ucontrol->value.integer.value[2] =
+ madifx_read_in_gain(hdspm, destination, source);
+
+ spin_unlock_irq(&hdspm->lock);
+
+ return 0;
+}
+
+static int snd_madifx_put_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ int source;
+ int destination;
+ int gain;
+
+ if (!snd_madifx_use_is_exclusive(hdspm))
+ return -EBUSY;
+
+ source = ucontrol->value.integer.value[0];
+ destination = ucontrol->value.integer.value[1];
+
+ if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
+ return -1;
+ if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
+ return -1;
+
+ gain = ucontrol->value.integer.value[2];
+
+ spin_lock_irq(&hdspm->lock);
+
+ if (source >= HDSPM_MAX_CHANNELS)
+ change = gain != madifx_read_pb_gain(hdspm, destination,
+ source -
+ HDSPM_MAX_CHANNELS);
+ else
+ change = gain != madifx_read_in_gain(hdspm, destination,
+ source);
+
+ if (change) {
+ if (source >= HDSPM_MAX_CHANNELS)
+ madifx_write_pb_gain(hdspm, destination,
+ source - HDSPM_MAX_CHANNELS,
+ gain);
+ else
+ madifx_write_in_gain(hdspm, destination, source,
+ gain);
+ }
+ spin_unlock_irq(&hdspm->lock);
+
+ return change;
+}
+
+/* The simple mixer control(s) provide gain control for the
+ basic 1:1 mappings of playback streams to output
+ streams.
+*/
+
+#define HDSPM_PLAYBACK_MIXER \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_madifx_info_playback_mixer, \
+ .get = snd_madifx_get_playback_mixer, \
+ .put = snd_madifx_put_playback_mixer \
+}
+
+static int snd_madifx_info_playback_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 64;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int snd_madifx_get_playback_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int channel;
+
+ channel = ucontrol->id.index - 1;
+
+ if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+ return -EINVAL;
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.integer.value[0] =
+ (madifx_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
+ spin_unlock_irq(&hdspm->lock);
+
+ return 0;
+}
+
+static int snd_madifx_put_playback_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ int channel;
+ int gain;
+
+ if (!snd_madifx_use_is_exclusive(hdspm))
+ return -EBUSY;
+
+ channel = ucontrol->id.index - 1;
+
+ if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+ return -EINVAL;
+
+ gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
+
+ spin_lock_irq(&hdspm->lock);
+ change =
+ gain != madifx_read_pb_gain(hdspm, channel,
+ channel);
+ if (change)
+ madifx_write_pb_gain(hdspm, channel, channel,
+ gain);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
+#endif /* OLD_MIXER */
+
+#define HDSPM_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_madifx_info_sync_check, \
+ .get = snd_madifx_get_sync_check \
+}
+
+
+static int snd_madifx_info_sync_check(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ 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 madifx_sync_check(struct hdspm *hdspm, int idx)
+{
+ u32 status, lockmask, syncmask;
+ int lock, sync;
+
+ status = madifx_read(hdspm, MADIFX_RD_INP_STATUS);
+
+ if (5 == idx) {
+ /* Sync-In was added later, so it's further up in the register */
+ lockmask = MADIFX_sync_in_lock;
+ syncmask = MADIFX_sync_in_sync;
+ } else {
+ lockmask = (MADIFX_madi1_lock << idx);
+ syncmask = (MADIFX_madi1_sync << idx);
+ }
+
+ lock = (status & lockmask) ? 1 : 0;
+ sync = (status & syncmask) ? 1 : 0;
+
+ if (lock && sync)
+ return 2;
+ else if (lock)
+ return 1;
+ return 0;
+}
+
+
+
+
+static int snd_madifx_get_sync_check(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int val = -1;
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ val = madifx_sync_check(hdspm, kcontrol->private_value);
+ break;
+ }
+
+ if (-1 == val)
+ val = 3;
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+
+
+static struct snd_kcontrol_new snd_madifx_controls_madi[] = {
+ HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+ HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
+ HDSPM_SYNC_CHECK("MADI 1 SyncCheck", 0),
+ HDSPM_SYNC_CHECK("MADI 2 SyncCheck", 1),
+ HDSPM_SYNC_CHECK("MADI 3 SyncCheck", 2),
+ HDSPM_SYNC_CHECK("WC SyncCheck", 3),
+ HDSPM_SYNC_CHECK("AES SyncCheck", 4),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("MADI 1 Frequency", 0),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("MADI 2 Frequency", 1),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("MADI 3 Frequency", 2),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 3),
+ HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 4),
+ MADIFX_MADI_CHANNELCOUNT("MADI 1 RX #ch", 0),
+ MADIFX_MADI_CHANNELCOUNT("MADI 2 RX #ch", 1),
+ MADIFX_MADI_CHANNELCOUNT("MADI 3 RX #ch", 2),
+ MADIFX_TOGGLE_SETTING("MADI 1 TX 64ch mode", MADIFX_madi1_tx_64ch),
+ MADIFX_TOGGLE_SETTING("MADI 2 TX 64ch mode", MADIFX_madi2_tx_64ch),
+ MADIFX_TOGGLE_SETTING("MADI 3 TX 64ch mode", MADIFX_madi3_tx_64ch),
+ MADIFX_TOGGLE_SETTING("MADI 1 SMUX mode", MADIFX_madi1_smux),
+ MADIFX_TOGGLE_SETTING("MADI 2 SMUX mode", MADIFX_madi2_smux),
+ MADIFX_TOGGLE_SETTING("MADI 3 SMUX mode", MADIFX_madi3_smux),
+ MADIFX_TOGGLE_SETTING("WC Term", MADIFX_WCK_TERM),
+ MADIFX_TOGGLE_SETTING("WC single speed", MADIFX_WCK48),
+ MADIFX_TOGGLE_SETTING("AES professional", MADIFX_PRO),
+ MADIFX_TOGGLE_SETTING("Redundancy mode", MADIFX_redundancy_mode),
+ MADIFX_TOGGLE_SETTING("Mirror MADI out", MADIFX_mirror_madi_out),
+ MADIFX_CLOCK_SELECT("Clock Selection", 0)
+};
+
+
+#if OLD_MIXER
+static struct snd_kcontrol_new snd_madifx_playback_mixer =
HDSPM_PLAYBACK_MIXER;
+
+
+static int madifx_update_simple_mixer_controls(struct hdspm *hdspm)
+{
+ int i;
+ snd_printk(KERN_WARNING "MADIFX: updating broken mixer\n");
+
+ for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) {
+ if (hdspm->system_sample_rate > 48000) {
+ hdspm->playback_mixer_ctls[i]->vd[0].access =
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+ SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+ } else {
+ hdspm->playback_mixer_ctls[i]->vd[0].access =
+ SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+ }
+ snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &hdspm->playback_mixer_ctls[i]->id);
+ }
+
+ return 0;
+}
+#endif
+
+
+static int snd_madifx_create_controls(struct snd_card *card,
+ struct hdspm *hdspm)
+{
+ unsigned int idx, limit;
+ int err;
+ struct snd_kcontrol_new *list = NULL;
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ list = snd_madifx_controls_madi;
+ limit = ARRAY_SIZE(snd_madifx_controls_madi);
+ break;
+ }
+
+ if (NULL != list) {
+ for (idx = 0; idx < limit; idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&list[idx], hdspm));
+ if (err < 0)
+ return err;
+ }
+ }
+
+
+ /* FIXME: MADI FX, we don't know how to mix, yet */
+#if 0
+ /* create simple 1:1 playback mixer controls */
+ snd_madifx_playback_mixer.name = "Chn";
+ if (hdspm->system_sample_rate >= 128000) {
+ limit = hdspm->qs_out_channels;
+ } else if (hdspm->system_sample_rate >= 64000) {
+ limit = hdspm->ds_out_channels;
+ } else {
+ limit = hdspm->ss_out_channels;
+ }
+ for (idx = 0; idx < limit; ++idx) {
+ struct snd_kcontrol *kctl;
+ snd_madifx_playback_mixer.index = idx + 1;
+ kctl = snd_ctl_new1(&snd_madifx_playback_mixer, hdspm);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ hdspm->playback_mixer_ctls[idx] = kctl;
+ }
+#endif
+
+
+ return 0;
+}
+
+/*------------------------------------------------------------
+ /proc interface
+ ------------------------------------------------------------*/
+
+static void
+snd_madifx_proc_read_madifx(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ u32 status, inp_status, control, freq;
+
+ char *system_clock_mode;
+ int x;
+
+ unsigned int period;
+ u64 freq_const = 0;
+ u32 rate;
+
+ status = madifx_read(hdspm, MADIFX_RD_STATUS);
+ inp_status = madifx_read(hdspm, MADIFX_RD_INP_STATUS);
+ control = hdspm->control_register;
+ freq = madifx_read(hdspm, MADIFX_RD_INP_FREQ);
+
+#if 0
+ snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
+ hdspm->card_name, hdspm->card->number + 1,
+ hdspm->firmware_rev,
+ (status2 & HDSPM_version0) |
+ (status2 & HDSPM_version1) | (status2 &
+ HDSPM_version2));
+#endif
+ snd_iprintf(buffer, "HW Serial: 0x%x\n", madifx_read(hdspm,
+ MADIFX_RD_VERSION));
+
+ 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, MIDI2=%d MIDI3=%d
IRQcount=%d\n",
+ status & HDSPM_audioIRQPending,
+ (status & MADIFX_mIRQ0) ? 1 : 0,
+ (status & MADIFX_mIRQ1) ? 1 : 0,
+ (status & MADIFX_mIRQ2) ? 1 : 0,
+ (status & MADIFX_mIRQ3) ? 1 : 0,
+ hdspm->irq_count);
+
+ snd_iprintf(buffer,
+ "MIDI FIFO: Out0=0x%x, Out1=0x%x, Out2=0x%x, Out3=0x%x\n",
+ madifx_read(hdspm, MADIFX_midi_out0_status) & 0xFF,
+ madifx_read(hdspm, MADIFX_midi_out1_status) & 0xFF,
+ madifx_read(hdspm, MADIFX_midi_out2_status) & 0xFF,
+ madifx_read(hdspm, MADIFX_midi_out3_status) & 0xFF);
+ snd_iprintf(buffer,
+ "MIDI FIFO: in0=0x%x, in1=0x%x, in2=0x%x, in3=0x%x\n",
+ madifx_read(hdspm, MADIFX_midi_in0_status) & 0xFF,
+ madifx_read(hdspm, MADIFX_midi_in1_status) & 0xFF,
+ madifx_read(hdspm, MADIFX_midi_in2_status) & 0xFF,
+ madifx_read(hdspm, MADIFX_midi_in3_status) & 0xFF);
+ snd_iprintf(buffer,
+ "Register:\ncontrol=0x%x, settings=0x%x, status=0x%x, "
+ "input=0x%x inp_freq=0x%x\n",
+ hdspm->control_register, hdspm->settings_register,
+ status, inp_status, freq);
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ freq_const = 131072000000000ULL;
+ break;
+ }
+
+ period = madifx_read(hdspm, MADIFX_RD_PLL_FREQ);
+ snd_iprintf(buffer, " period: %u\n", period);
+
+
+ /* rate = freq_const/period; */
+ rate = div_u64(freq_const, period);
+
+ rate *= madifx_speed_multiplier(hdspm);
+
+
+ snd_iprintf(buffer, " Frequency: %u Hz\n",
+ (unsigned int) rate);
+
+
+ snd_iprintf(buffer, "--- Settings ---\n");
+
+ x = madifx_get_latency(hdspm);
+
+ snd_iprintf(buffer,
+ "Size (Latency): %d samples\n", x);
+
+#if 0
+ snd_iprintf(buffer, "Line out: %s\n",
+ (hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
+
+ switch (hdspm->control_register & HDSPM_InputMask) {
+ case HDSPM_InputOptical:
+ insel = "Optical";
+ break;
+ case HDSPM_InputCoaxial:
+ insel = "Coaxial";
+ break;
+ default:
+ insel = "Unknown";
+ }
+
+ snd_iprintf(buffer,
+ "ClearTrackMarker = %s, Transmit in %s Channel Mode, "
+ "Auto Input %s\n",
+ (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
+ (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
+ (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
+#endif
+
+
+ if (1 == madifx_system_clock_mode(hdspm))
+ system_clock_mode = "Slave";
+ else
+ system_clock_mode = "Master";
+ snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode);
+
+ snd_iprintf(buffer, "Selected clock source: %s\n",
+ hdspm->texts_clocksource[madifx_get_clock_select(hdspm)]);
+
+ snd_iprintf(buffer, "System Clock Frequency: %d\n",
+ hdspm->system_sample_rate);
+
+
+#if 0
+ snd_iprintf(buffer, "--- Status:\n");
+
+ x = status & HDSPM_madiSync;
+ x2 = status2 & HDSPM_wcSync;
+
+ snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
+ (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
+ "NoLock",
+ (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
+ "NoLock");
+
+ switch (madifx_autosync_ref(hdspm)) {
+ case HDSPM_AUTOSYNC_FROM_SYNC_IN:
+ autosync_ref = "Sync In";
+ break;
+ case HDSPM_AUTOSYNC_FROM_TCO:
+ autosync_ref = "TCO";
+ break;
+ case HDSPM_AUTOSYNC_FROM_WORD:
+ autosync_ref = "Word Clock";
+ break;
+ case HDSPM_AUTOSYNC_FROM_MADI:
+ autosync_ref = "MADI Sync";
+ break;
+ case HDSPM_AUTOSYNC_FROM_NONE:
+ autosync_ref = "Input not valid";
+ break;
+ default:
+ autosync_ref = "---";
+ break;
+ }
+ snd_iprintf(buffer,
+ "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
+ autosync_ref, madifx_external_sample_rate(hdspm),
+ (status & HDSPM_madiFreqMask) >> 22,
+ (status2 & HDSPM_wcFreqMask) >> 5);
+
+ snd_iprintf(buffer, "Input: %s, Mode=%s\n",
+ (status & HDSPM_AB_int) ? "Coax" : "Optical",
+ (status & HDSPM_RX_64ch) ? "64 channels" :
+ "56 channels");
+#endif
+
+ snd_iprintf(buffer, "\n");
+}
+
+
+#ifdef CONFIG_SND_DEBUG
+static void
+snd_madifx_proc_read_debug(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *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 ", madifx_read(hdspm, i + j));
+ snd_iprintf(buffer, "\n");
+ }
+}
+#endif
+
+
+#if 0
+/* FIXME: Portnames not implemented, yet. hdspm->port_names_in and _out are
+ * set to NULL, so don't derefence them for the time being.
+ */
+static void snd_madifx_proc_ports_in(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "# generated by hdspm\n");
+
+ for (i = 0; i < hdspm->max_channels_in; i++)
+ snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]);
+}
+
+static void snd_madifx_proc_ports_out(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdspm *hdspm = entry->private_data;
+ int i;
+
+ snd_iprintf(buffer, "# generated by hdspm\n");
+
+ for (i = 0; i < hdspm->max_channels_out; i++)
+ snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]);
+}
+#endif
+
+
+static void snd_madifx_proc_init(struct hdspm *hdspm)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(hdspm->card, "madifx", &entry)) {
+ switch (hdspm->io_type) {
+ case MADIFX:
+ snd_info_set_text_ops(entry, hdspm,
+ snd_madifx_proc_read_madifx);
+ break;
+ }
+ }
+
+#if 0
+ /* FIXME: port names still missing for MADIFX */
+ if (!snd_card_proc_new(hdspm->card, "ports.in", &entry))
+ snd_info_set_text_ops(entry, hdspm, snd_madifx_proc_ports_in);
+
+ if (!snd_card_proc_new(hdspm->card, "ports.out", &entry))
+ snd_info_set_text_ops(entry, hdspm, snd_madifx_proc_ports_out);
+#endif
+
+#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_madifx_proc_read_debug);
+#endif
+}
+
+/*------------------------------------------------------------
+ hdspm intitialize
+ ------------------------------------------------------------*/
+
+static int snd_madifx_set_defaults(struct hdspm *hdspm)
+{
+ /* ASSUMPTION: hdspm->lock is either held, or there is no need to
+ hold it (e.g. during module initialization).
+ */
+
+ /* set defaults: */
+
+ hdspm->settings_register = 0;
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ /* OSX: LAT_3+BUF_SIZ_1+BUF_SIZ_2+freq1; */
+ hdspm->control_register = MADIFX_LAT_3 + MADIFX_BUF_SIZ_1 +
+ MADIFX_BUF_SIZ_2 + MADIFX_freq1;
+ /* PRO+madi1_tx_64ch+madi2_tx_64ch+madi3_tx_64ch; */
+ hdspm->settings_register = 0x8 + 0x80 + 0x100 + 0x200;
+ break;
+ }
+
+ madifx_write(hdspm, MADIFX_CONTROL_REG, hdspm->control_register);
+
+ madifx_compute_period_size(hdspm);
+
+ madifx_write(hdspm, MADIFX_SETTINGS_REG, hdspm->settings_register);
+
+ /* set a default rate so that the channel map is set up. */
+ madifx_set_rate(hdspm, 48000, 1);
+
+ return 0;
+}
+
+
+/*------------------------------------------------------------
+ interrupt
+ ------------------------------------------------------------*/
+
+static irqreturn_t snd_madifx_interrupt(int irq, void *dev_id)
+{
+ struct hdspm *hdspm = (struct hdspm *) dev_id;
+ unsigned int status;
+ int i, audio, midi, schedule = 0;
+ /* cycles_t now; */
+
+ status = madifx_read(hdspm, MADIFX_RD_STATUS);
+
+ audio = status & HDSPM_audioIRQPending;
+ midi = status & (MADIFX_mIRQ0 | MADIFX_mIRQ1 |
+ MADIFX_mIRQ2 | MADIFX_mIRQ3);
+
+ /* now = get_cycles(); */
+ /**
+ * LAT_2..LAT_0 period counter (win) counter (mac)
+ * 6 4096 ~256053425 ~514672358
+ * 5 2048 ~128024983 ~257373821
+ * 4 1024 ~64023706 ~128718089
+ * 3 512 ~32005945 ~64385999
+ * 2 256 ~16003039 ~32260176
+ * 1 128 ~7998738 ~16194507
+ * 0 64 ~3998231 ~8191558
+ **/
+ /*
+ snd_printk(KERN_INFO "snd_madifx_interrupt %llu @ %llx\n",
+ now-hdspm->last_interrupt, status & 0xFFC0);
+ hdspm->last_interrupt = now;
+ */
+
+ if (!audio && !midi)
+ return IRQ_NONE;
+
+ madifx_write(hdspm, MADIFX_IRQ_ACK, 0);
+ hdspm->irq_count++;
+
+
+ if (audio) {
+ if (hdspm->capture_substream)
+ snd_pcm_period_elapsed(hdspm->capture_substream);
+
+ if (hdspm->playback_substream)
+ snd_pcm_period_elapsed(hdspm->playback_substream);
+ }
+
+ if (midi) {
+ i = 0;
+ while (i < hdspm->midiPorts) {
+ if ((madifx_read(hdspm,
+ hdspm->midi[i].statusIn) & 0xff) &&
+ (status & hdspm->midi[i].irq)) {
+ /* we disable interrupts for this input until
+ * processing is done
+ */
+ hdspm->control_register &= ~hdspm->midi[i].ie;
+ madifx_write(hdspm, MADIFX_CONTROL_REG,
+ hdspm->control_register);
+ hdspm->midi[i].pending = 1;
+ schedule = 1;
+ }
+
+ i++;
+ }
+
+ if (schedule)
+ tasklet_hi_schedule(&hdspm->midi_tasklet);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*------------------------------------------------------------
+ pcm interface
+ ------------------------------------------------------------*/
+
+
+static snd_pcm_uframes_t snd_madifx_hw_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ return madifx_hw_pointer(hdspm);
+}
+
+
+static int snd_madifx_reset(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *other;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ other = hdspm->capture_substream;
+ else
+ other = hdspm->playback_substream;
+
+ if (hdspm->running)
+ runtime->status->hw_ptr = madifx_hw_pointer(hdspm);
+ else
+ runtime->status->hw_ptr = 0;
+ if (other) {
+ struct snd_pcm_substream *s;
+ struct snd_pcm_runtime *oruntime = other->runtime;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s == other) {
+ oruntime->status->hw_ptr =
+ runtime->status->hw_ptr;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int snd_madifx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ int err;
+ int i;
+ pid_t this_pid;
+ pid_t other_pid;
+
+ spin_lock_irq(&hdspm->lock);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ this_pid = hdspm->playback_pid;
+ other_pid = hdspm->capture_pid;
+ } else {
+ this_pid = hdspm->capture_pid;
+ other_pid = hdspm->playback_pid;
+ }
+
+ if (other_pid > 0 && this_pid != other_pid) {
+
+ /* The other stream is open, and not by the same
+ task as this one. Make sure that the parameters
+ that matter are the same.
+ */
+
+ if (params_rate(params) != hdspm->system_sample_rate) {
+ spin_unlock_irq(&hdspm->lock);
+ _snd_pcm_hw_param_setempty(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ return -EBUSY;
+ }
+
+ if (params_period_size(params) != hdspm->period_bytes / 4) {
+ spin_unlock_irq(&hdspm->lock);
+ _snd_pcm_hw_param_setempty(params,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return -EBUSY;
+ }
+
+ }
+ /* We're fine. */
+ spin_unlock_irq(&hdspm->lock);
+
+ /* how to make sure that the rate matches an externally-set one ? */
+
+ spin_lock_irq(&hdspm->lock);
+ err = madifx_set_rate(hdspm, params_rate(params), 0);
+ if (err < 0) {
+ snd_printk(KERN_INFO "err on madifx_set_rate: %d\n", err);
+ spin_unlock_irq(&hdspm->lock);
+ _snd_pcm_hw_param_setempty(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ return err;
+ }
+ spin_unlock_irq(&hdspm->lock);
+
+ err = madifx_set_interrupt_interval(hdspm,
+ params_period_size(params));
+ if (err < 0) {
+ snd_printk(KERN_INFO "err on madifx_set_interrupt_interval: %d\n", err);
+ _snd_pcm_hw_param_setempty(params,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ return err;
+ }
+
+ /* Memory allocation */
+#define NUM_AES_PAGES (32768*2/4096)
+#define NUM_MADI_PAGES (32768*192/4096)
+#define NUM_DMA_CH_PAGES (32768*8/4096)
+#define MADIFX_HARDWARE_PAGE_SIZE 4096
+
+ {
+ int wanted;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ wanted = OUTPUT_DMA_BUFFER_SIZE;
+ else
+ wanted = INPUT_DMA_BUFFER_SIZE;
+
+ err = snd_pcm_lib_malloc_pages(substream, wanted);
+ if (err < 0) {
+ snd_printk(KERN_INFO "err on snd_pcm_lib_malloc_pages: %d\n", err);
+ return err;
+ }
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+ /* initialise default DMA table. Will be
+ * overwritten in a second. */
+ for (i = 0; i < MADIFX_MAX_PAGE_TABLE_SIZE/2; i++)
+ hdspm->dmaPageTable[i] = snd_pcm_sgbuf_get_addr(substream, 0);
+
+ /* AES Out, stereo */
+ for (i = 0; i < NUM_AES_PAGES; i++)
+ hdspm->dmaPageTable[i] = snd_pcm_sgbuf_get_addr(substream,
+ i * MADIFX_HARDWARE_PAGE_SIZE);
+
+ /* Phones Out, stereo */
+ for (i = 0; i < NUM_AES_PAGES; i++)
+ hdspm->dmaPageTable[i+1*NUM_DMA_CH_PAGES] =
+ snd_pcm_sgbuf_get_addr(substream,
+ (i+1*NUM_AES_PAGES) * MADIFX_HARDWARE_PAGE_SIZE);
+
+ /* MADI Out, 192 channels */
+ for (i = 0; i < NUM_MADI_PAGES; i++)
+ hdspm->dmaPageTable[i+2*NUM_DMA_CH_PAGES] =
+ snd_pcm_sgbuf_get_addr(substream,
+ (i+2*NUM_AES_PAGES) * MADIFX_HARDWARE_PAGE_SIZE);
+
+ for (i = 0; i < MADIFX_MAX_PAGE_TABLE_SIZE/2; i++)
+ madifx_write(hdspm, MADIFX_PAGE_ADDRESS_LIST + (4 * i),
+ hdspm->dmaPageTable[i]);
+
+ for (i = 0; i < 32; ++i)
+ snd_madifx_enable_out(hdspm, i, 1);
+
+ hdspm->playback_buffer =
+ (unsigned char *) substream->runtime->dma_area;
+ snd_printdd("Allocated sample buffer for playback at %p\n",
+ hdspm->playback_buffer);
+ } else {
+ /* initialise default DMA table. Will be
+ * overwritten in a second. */
+ for (i = MADIFX_MAX_PAGE_TABLE_SIZE/2;
+ i < MADIFX_MAX_PAGE_TABLE_SIZE; i++) {
+ hdspm->dmaPageTable[i] = snd_pcm_sgbuf_get_addr(substream, 0);
+ }
+
+ /* setup DMA page table */
+ /* AES In, stereo */
+ for (i = 0; i < NUM_AES_PAGES; i++) {
+ hdspm->dmaPageTable[i+MADIFX_MAX_PAGE_TABLE_SIZE/2] =
+ snd_pcm_sgbuf_get_addr(substream,
+ i * MADIFX_HARDWARE_PAGE_SIZE);
+ }
+
+ /* MADI In, 192 channels */
+ for (i = 0; i < NUM_MADI_PAGES; i++) {
+ hdspm->dmaPageTable[i + MADIFX_MAX_PAGE_TABLE_SIZE / 2 +
NUM_DMA_CH_PAGES] =
+ snd_pcm_sgbuf_get_addr(substream,
+ (i + NUM_AES_PAGES) * MADIFX_HARDWARE_PAGE_SIZE);
+ }
+
+ for (i = MADIFX_MAX_PAGE_TABLE_SIZE/2;
+ i < MADIFX_MAX_PAGE_TABLE_SIZE; i++) {
+ madifx_write(hdspm, MADIFX_PAGE_ADDRESS_LIST + (4 * i),
+ hdspm->dmaPageTable[i]);
+ }
+
+ for (i = 0; i < 32; ++i)
+ snd_madifx_enable_in(hdspm, i, 1);
+
+ hdspm->capture_buffer =
+ (unsigned char *) substream->runtime->dma_area;
+ snd_printdd("Allocated sample buffer for capture at %p\n",
+ hdspm->capture_buffer);
+ }
+
+ /* Switch to native float format if requested */
+ if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
+ if (!(hdspm->control_register & MADIFX_float_format))
+ snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE float
format.\n");
+
+ hdspm->control_register |= MADIFX_float_format;
+ } else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) {
+ if (hdspm->control_register & MADIFX_float_format)
+ snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE integer
format.\n");
+
+ hdspm->control_register &= ~MADIFX_float_format;
+ }
+ madifx_write(hdspm, MADIFX_CONTROL_REG, hdspm->control_register);
+
+ return 0;
+}
+
+static int snd_madifx_hw_free(struct snd_pcm_substream *substream)
+{
+ int i;
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+ /* params_channels(params) should be enough,
+ but to get sure in case of error */
+ for (i = 0; i < 32; ++i)
+ snd_madifx_enable_out(hdspm, i, 0);
+
+ hdspm->playback_buffer = NULL;
+ } else {
+ for (i = 0; i < 32; ++i)
+ snd_madifx_enable_in(hdspm, i, 0);
+
+ hdspm->capture_buffer = NULL;
+
+ }
+
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+
+static int snd_madifx_channel_info(struct snd_pcm_substream *substream,
+ struct snd_pcm_channel_info *info)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ int last_madi_channel = 193;
+
+ if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) {
+ snd_printk(KERN_INFO
+ "snd_madifx_channel_info: output channel out of range (%d)\n",
+ info->channel);
+ return -EINVAL;
+ }
+
+ switch (hdspm->speedmode) {
+ case ss:
+ /* MADI FX Playback channel map
+ Outputstream 0 with 2 channels at byte offset 0 AES
+ Outputstream 1 with 8 channels at byte offset 131072 MADI
+ Outputstream 2 with 8 channels at byte offset 393216
+ Outputstream 3 with 8 channels at byte offset 655360
+ Outputstream 4 with 8 channels at byte offset 917504
+ Outputstream 5 with 8 channels at byte offset 1179648
+ Outputstream 6 with 8 channels at byte offset 1441792
+ Outputstream 7 with 8 channels at byte offset 1703936
+ Outputstream 8 with 8 channels at byte offset 1966080
+ Outputstream 9 with 8 channels at byte offset 2228224
+ Outputstream 10 with 8 channels at byte offset 2490368
+ Outputstream 11 with 8 channels at byte offset 2752512
+ Outputstream 12 with 8 channels at byte offset 3014656
+ Outputstream 13 with 8 channels at byte offset 3276800
+ Outputstream 14 with 8 channels at byte offset 3538944
+ Outputstream 15 with 8 channels at byte offset 3801088
+ Outputstream 16 with 8 channels at byte offset 4063232
+ Outputstream 17 with 8 channels at byte offset 4325376
+ Outputstream 18 with 8 channels at byte offset 4587520
+ Outputstream 19 with 8 channels at byte offset 4849664
+ Outputstream 20 with 8 channels at byte offset 5111808
+ Outputstream 21 with 8 channels at byte offset 5373952
+ Outputstream 22 with 8 channels at byte offset 5636096
+ Outputstream 23 with 8 channels at byte offset 5898240
+ Outputstream 24 with 8 channels at byte offset 6160384
+ Outputstream 25 with 2 channels at byte offset 65536 Phones
+ */
+
+ /* Note: channels start at zero */
+ last_madi_channel = 193;
+ break;
+ case ds:
+ last_madi_channel = 97;
+ break;
+ case qs:
+ last_madi_channel = 49;
+ break;
+ }
+ info->offset = (info->channel < 2) ?
+ 0 : ((info->channel > last_madi_channel) ? 65536 :
+ 131072 + 8 * 4 * 8192 * ((info->channel-2)/8));
+ info->first = (info->channel < 2 || info->channel > last_madi_channel) ?
+ 32 * info->channel : 32 * ((info->channel-2) % 8);
+ info->step = (info->channel < 2 || info->channel > last_madi_channel) ?
+ 64 : 256;
+ } else {
+ if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) {
+ snd_printk(KERN_INFO
+ "snd_madifx_channel_info: input channel out of range (%d)\n",
+ info->channel);
+ return -EINVAL;
+ }
+
+ switch (hdspm->speedmode) {
+ /* MADI FX Input channel map
+ Inputstream 0 with 2 channels at byte offset 0 AES
+ Inputstream 1 with 8 channels at byte offset 65536 MADI
+ Inputstream 2 with 8 channels at byte offset 327680
+ Inputstream 3 with 8 channels at byte offset 589824
+ Inputstream 4 with 8 channels at byte offset 851968
+ Inputstream 5 with 8 channels at byte offset 1114112
+ Inputstream 6 with 8 channels at byte offset 1376256
+ Inputstream 7 with 8 channels at byte offset 1638400
+ Inputstream 8 with 8 channels at byte offset 1900544
+ Inputstream 9 with 8 channels at byte offset 2162688
+ Inputstream 10 with 8 channels at byte offset 2424832
+ Inputstream 11 with 8 channels at byte offset 2686976
+ Inputstream 12 with 8 channels at byte offset 2949120
+ Inputstream 13 with 8 channels at byte offset 3211264
+ Inputstream 14 with 8 channels at byte offset 3473408
+ Inputstream 15 with 8 channels at byte offset 3735552
+ Inputstream 16 with 8 channels at byte offset 3997696
+ Inputstream 17 with 8 channels at byte offset 4259840
+ Inputstream 18 with 8 channels at byte offset 4521984
+ Inputstream 19 with 8 channels at byte offset 4784128
+ Inputstream 20 with 8 channels at byte offset 5046272
+ Inputstream 21 with 8 channels at byte offset 5308416
+ Inputstream 22 with 8 channels at byte offset 5570560
+ Inputstream 23 with 8 channels at byte offset 5832704
+ Inputstream 24 with 8 channels at byte offset 6094848
+ */
+ case ss:
+ case ds:
+ case qs:
+ info->offset = (info->channel < 2) ? 0 : 65536 + 8 * 4 * 8192 *
+ ((info->channel-2)/8);
+ info->first = (info->channel < 2) ? 32 * info->channel :
+ 32 * ((info->channel-2) % 8);
+ info->step = (info->channel < 2) ? 64 : 256;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int snd_madifx_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL1_RESET:
+ return snd_madifx_reset(substream);
+
+ case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+ {
+ struct snd_pcm_channel_info *info = arg;
+ return snd_madifx_channel_info(substream, info);
+ }
+ default:
+ break;
+ }
+
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_madifx_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *other;
+ int running;
+
+ spin_lock(&hdspm->lock);
+ running = hdspm->running;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ running |= 1 << substream->stream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ running &= ~(1 << substream->stream);
+ break;
+ default:
+ snd_BUG();
+ spin_unlock(&hdspm->lock);
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ other = hdspm->capture_substream;
+ else
+ other = hdspm->playback_substream;
+
+ if (other) {
+ struct snd_pcm_substream *s;
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s == other) {
+ snd_pcm_trigger_done(s, substream);
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ running |= 1 << s->stream;
+ else
+ running &= ~(1 << s->stream);
+ goto _ok;
+ }
+ }
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
+ && substream->stream ==
+ SNDRV_PCM_STREAM_CAPTURE)
+ madifx_silence_playback(hdspm);
+ } else {
+ if (running &&
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ madifx_silence_playback(hdspm);
+ }
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ madifx_silence_playback(hdspm);
+ }
+_ok:
+ snd_pcm_trigger_done(substream, substream);
+ if (!hdspm->running && running)
+ madifx_start_audio(hdspm);
+ else if (hdspm->running && !running)
+ madifx_stop_audio(hdspm);
+ hdspm->running = running;
+ spin_unlock(&hdspm->lock);
+
+ return 0;
+}
+
+static int snd_madifx_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static struct snd_pcm_hardware snd_madifx_playback_subinfo = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_COMPLEX |
+ SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_DOUBLE),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT_LE,
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
+ .rate_min = 32000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 196,
+ .buffer_bytes_max = OUTPUT_DMA_BUFFER_SIZE,
+ .period_bytes_min = (32 * 4),
+ .period_bytes_max = OUTPUT_DMA_BUFFER_SIZE,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .fifo_size = 0
+};
+
+static struct snd_pcm_hardware snd_madifx_capture_subinfo = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_COMPLEX |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT_LE,
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000),
+ .rate_min = 32000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 194,
+ .buffer_bytes_max = INPUT_DMA_BUFFER_SIZE,
+ .period_bytes_min = (32 * 4),
+ .period_bytes_max = INPUT_DMA_BUFFER_SIZE,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .fifo_size = 0
+};
+
+static int snd_madifx_hw_rule_in_channels_rate(struct snd_pcm_hw_params
*params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ if (r->min > 96000 && r->max <= 192000) {
+ struct snd_interval t = {
+ .min = hdspm->qs_in_channels,
+ .max = hdspm->qs_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->min > 48000 && r->max <= 96000) {
+ struct snd_interval t = {
+ .min = hdspm->ds_in_channels,
+ .max = hdspm->ds_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->max < 64000) {
+ struct snd_interval t = {
+ .min = hdspm->ss_in_channels,
+ .max = hdspm->ss_in_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ }
+
+ return 0;
+}
+
+static int snd_madifx_hw_rule_out_channels_rate(struct
snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ if (r->min > 96000 && r->max <= 192000) {
+ struct snd_interval t = {
+ .min = hdspm->qs_out_channels,
+ .max = hdspm->qs_out_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->min > 48000 && r->max <= 96000) {
+ struct snd_interval t = {
+ .min = hdspm->ds_out_channels,
+ .max = hdspm->ds_out_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else if (r->max < 64000) {
+ struct snd_interval t = {
+ .min = hdspm->ss_out_channels,
+ .max = hdspm->ss_out_channels,
+ .integer = 1,
+ };
+ return snd_interval_refine(c, &t);
+ } else {
+ }
+ return 0;
+}
+
+static int snd_madifx_hw_rule_rate_in_channels(struct snd_pcm_hw_params
*params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ if (c->min >= hdspm->ss_in_channels) {
+ struct snd_interval t = {
+ .min = 32000,
+ .max = 48000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdspm->qs_in_channels) {
+ struct snd_interval t = {
+ .min = 128000,
+ .max = 192000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdspm->ds_in_channels) {
+ struct snd_interval t = {
+ .min = 64000,
+ .max = 96000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ }
+
+ return 0;
+}
+static int snd_madifx_hw_rule_rate_out_channels(struct
snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ if (c->min >= hdspm->ss_out_channels) {
+ struct snd_interval t = {
+ .min = 32000,
+ .max = 48000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdspm->qs_out_channels) {
+ struct snd_interval t = {
+ .min = 128000,
+ .max = 192000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ } else if (c->max <= hdspm->ds_out_channels) {
+ struct snd_interval t = {
+ .min = 64000,
+ .max = 96000,
+ .integer = 1,
+ };
+ return snd_interval_refine(r, &t);
+ }
+
+ return 0;
+}
+
+static int snd_madifx_hw_rule_in_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ unsigned int list[3];
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ list[0] = hdspm->qs_in_channels;
+ list[1] = hdspm->ds_in_channels;
+ list[2] = hdspm->ss_in_channels;
+ return snd_interval_list(c, 3, list, 0);
+}
+
+static int snd_madifx_hw_rule_out_channels(struct snd_pcm_hw_params
*params,
+ struct snd_pcm_hw_rule *rule)
+{
+ unsigned int list[3];
+ struct hdspm *hdspm = rule->private;
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ list[0] = hdspm->qs_out_channels;
+ list[1] = hdspm->ds_out_channels;
+ list[2] = hdspm->ss_out_channels;
+ return snd_interval_list(c, 3, list, 0);
+}
+
+
+static int snd_madifx_playback_open(struct snd_pcm_substream *substream)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ spin_lock_irq(&hdspm->lock);
+
+ snd_pcm_set_sync(substream);
+
+
+ runtime->hw = snd_madifx_playback_subinfo;
+
+ if (hdspm->capture_substream == NULL)
+ madifx_stop_audio(hdspm);
+
+ hdspm->playback_pid = current->pid;
+ hdspm->playback_substream = substream;
+
+ spin_unlock_irq(&hdspm->lock);
+
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 32, 4096);
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ 8192, 8192);
+ break;
+
+ default:
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 64, 8192);
+ break;
+ }
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_madifx_hw_rule_rate_out_channels, hdspm,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_madifx_hw_rule_out_channels, hdspm,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_madifx_hw_rule_out_channels_rate, hdspm,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
+ return 0;
+}
+
+static int snd_madifx_playback_release(struct snd_pcm_substream *substream)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&hdspm->lock);
+
+ hdspm->playback_pid = -1;
+ hdspm->playback_substream = NULL;
+
+ spin_unlock_irq(&hdspm->lock);
+
+ return 0;
+}
+
+
+static int snd_madifx_capture_open(struct snd_pcm_substream *substream)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ spin_lock_irq(&hdspm->lock);
+ snd_pcm_set_sync(substream);
+ runtime->hw = snd_madifx_capture_subinfo;
+
+ if (hdspm->playback_substream == NULL)
+ madifx_stop_audio(hdspm);
+
+ hdspm->capture_pid = current->pid;
+ hdspm->capture_substream = substream;
+
+ spin_unlock_irq(&hdspm->lock);
+
+ snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 32, 4096);
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ 8192, 8192);
+ break;
+
+ default:
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 64, 8192);
+ break;
+ }
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_madifx_hw_rule_rate_in_channels, hdspm,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_madifx_hw_rule_in_channels, hdspm,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_madifx_hw_rule_in_channels_rate, hdspm,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
+ return 0;
+}
+
+static int snd_madifx_capture_release(struct snd_pcm_substream *substream)
+{
+ struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&hdspm->lock);
+
+ hdspm->capture_pid = -1;
+ hdspm->capture_substream = NULL;
+
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_madifx_hwdep_dummy_op(struct snd_hwdep *hw, struct file
*file)
+{
+ /* we have nothing to initialize but the call is required */
+ return 0;
+}
+
+static inline int copy_u32_le(void __user *dest, void __iomem *src)
+{
+ u32 val = readl(src);
+ return copy_to_user(dest, &val, 4);
+}
+
+static int snd_madifx_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct hdspm *hdspm = hw->private_data;
+ struct madifx_config info;
+ struct madifx_status status;
+#ifdef CONFIG_SND_MADIFX_BROKEN
+ struct madifx_level_buffer *levels;
+ struct madifx_mixer_ioctl mixer;
+ long unsigned int s;
+#endif /* CONFIG_SND_MADIFX_BROKEN */
+ int i = 0;
+
+ switch (cmd) {
+
+#ifdef CONFIG_SND_MADIFX_BROKEN
+ case SNDRV_MADIFX_IOCTL_GET_LEVEL:
+ {
+ int row;
+
+ levels = &(hdspm->peak_rms);
+ for (row = 1; row <= 5 ; row++) {
+ int rms_index, peak_index;
+ u32 *target_rms, *target_peak;
+
+ switch (row) {
+ case 1:
+ rms_index = MADIFX_RD_RMS_IN;
+ peak_index = MADIFX_RD_PEAK_IN;
+ target_rms = levels->rms_in;
+ target_peak = levels->peak_in;
+ break;
+ case 2:
+ rms_index = MADIFX_RD_RMS_PLAY;
+ peak_index = MADIFX_RD_PEAK_PLAY;
+ target_rms = levels->rms_play;
+ target_peak = levels->peak_play;
+ break;
+ case 3:
+ rms_index = MADIFX_RD_RMS_OUT;
+ peak_index = MADIFX_RD_PEAK_OUT;
+ target_rms = levels->rms_out;
+ target_peak = levels->peak_out;
+ break;
+ case 4:
+ rms_index = MADIFX_RD_RMS_IN_PRE;
+ peak_index = MADIFX_RD_PEAK_IN_PRE;
+ target_rms = levels->rms_in_pre;
+ target_peak = levels->peak_in_pre;
+ break;
+ default:
+ rms_index = MADIFX_RD_RMS_OUT_PRE;
+ peak_index = MADIFX_RD_PEAK_OUT_PRE;
+ target_rms = levels->rms_out_pre;
+ target_peak = levels->peak_out_pre;
+ break;
+ }
+
+ for (i = 0; i < 2 * 256; i++)
+ *(target_rms + i) = hdspm->level_buffer[rms_index + i];
+
+ for (i = 0; i < 256; i++)
+ *(target_peak + i) = hdspm->level_buffer[peak_index + i];
+ }
+
+ }
+
+
+ levels->speed = hdspm->speedmode;
+
+ s = copy_to_user(argp, levels, sizeof(struct madifx_level_buffer));
+ if (0 != s) {
+ /* snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu
+ [Levels]\n", sizeof(struct madifx_peak_rms), s);
+ */
+ return -EFAULT;
+ }
+
+ madifx_write(hdspm, MADIFX_START_LEVEL, 0);
+
+ break;
+#endif /* CONFIG_SND_MADIFX_BROKEN */
+
+
+ case SNDRV_MADIFX_IOCTL_GET_CONFIG:
+
+ memset(&info, 0, sizeof(info));
+ spin_lock_irq(&hdspm->lock);
+
+ for (i = 0; i < ARRAY_SIZE(info.madi_tx_64); i++) {
+ info.madi_tx_64[i] = madifx_read_toggle_setting(hdspm,
+ (MADIFX_madi1_tx_64ch << i));
+
+ info.madi_smux[i] = madifx_read_toggle_setting(hdspm,
+ (MADIFX_madi1_smux << i));
+ }
+
+ info.wcterm = madifx_read_toggle_setting(hdspm,
+ MADIFX_WCK_TERM);
+
+ info.wck48 = madifx_read_toggle_setting(hdspm, MADIFX_WCK48);
+
+ info.aespro = madifx_read_toggle_setting(hdspm, MADIFX_PRO);
+
+ info.redundancy_mode = madifx_read_toggle_setting(hdspm,
+ MADIFX_redundancy_mode);
+
+ info.mirror_madi_out = madifx_read_toggle_setting(hdspm,
+ MADIFX_mirror_madi_out);
+
+
+ spin_unlock_irq(&hdspm->lock);
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+
+ case SNDRV_MADIFX_IOCTL_GET_STATUS:
+ memset(&status, 0, sizeof(status));
+
+ status.card_type = hdspm->io_type;
+
+ status.clock_selection = madifx_get_clock_select(hdspm);
+
+ status.system_sample_rate =
+ madifx_get_system_sample_rate(hdspm);
+
+
+ for (i = 0; i < ARRAY_SIZE(status.sync_check); i++) {
+ status.sync_check[i] = madifx_sync_check(hdspm, i);
+ status.external_sample_rates[i] =
+ HDSPM_bit2freq(madifx_external_freq_index(hdspm, i));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(status.madi_channelcount); i++) {
+ status.madi_channelcount[i] =
+ madifx_get_madichannelcount(hdspm, i);
+ }
+
+
+ if (copy_to_user(argp, &status, sizeof(status)))
+ return -EFAULT;
+
+
+ break;
+
+#ifdef CONFIG_SND_MADIFX_BROKEN
+ case SNDRV_HDSPM_IOCTL_GET_MIXER:
+ if (copy_from_user(&mixer, argp, sizeof(mixer)))
+ return -EFAULT;
+ if (copy_to_user((void __user *)mixer.mixer, hdspm->newmixer,
+ sizeof(struct madifx_newmixer)))
+ return -EFAULT;
+ break;
+#endif /* CONFIG_SND_MADIFX_BROKEN */
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct snd_pcm_ops snd_madifx_playback_ops = {
+ .open = snd_madifx_playback_open,
+ .close = snd_madifx_playback_release,
+ .ioctl = snd_madifx_ioctl,
+ .hw_params = snd_madifx_hw_params,
+ .hw_free = snd_madifx_hw_free,
+ .prepare = snd_madifx_prepare,
+ .trigger = snd_madifx_trigger,
+ .pointer = snd_madifx_hw_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static struct snd_pcm_ops snd_madifx_capture_ops = {
+ .open = snd_madifx_capture_open,
+ .close = snd_madifx_capture_release,
+ .ioctl = snd_madifx_ioctl,
+ .hw_params = snd_madifx_hw_params,
+ .hw_free = snd_madifx_hw_free,
+ .prepare = snd_madifx_prepare,
+ .trigger = snd_madifx_trigger,
+ .pointer = snd_madifx_hw_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static int snd_madifx_create_hwdep(struct snd_card *card,
+ struct hdspm *hdspm)
+{
+ struct snd_hwdep *hw;
+ int err;
+
+ err = snd_hwdep_new(card, "MADIFX hwdep", 0, &hw);
+ if (err < 0)
+ return err;
+
+ hdspm->hwdep = hw;
+ hw->private_data = hdspm;
+ strcpy(hw->name, "MADIFX hwdep interface");
+
+ hw->ops.open = snd_madifx_hwdep_dummy_op;
+ hw->ops.ioctl = snd_madifx_hwdep_ioctl;
+ hw->ops.ioctl_compat = snd_madifx_hwdep_ioctl;
+ hw->ops.release = snd_madifx_hwdep_dummy_op;
+
+ return 0;
+}
+
+
+/*------------------------------------------------------------
+ memory interface
+ ------------------------------------------------------------*/
+static int snd_madifx_preallocate_memory(struct hdspm *hdspm)
+{
+ int err;
+ int i;
+ int lpti; /* level page table index */
+ struct snd_pcm *pcm;
+ size_t wanted;
+ dma_addr_t levelPageTable[MADIFX_NUM_LEVEL_PAGES];
+
+ pcm = hdspm->pcm;
+
+
+ wanted = max(INPUT_DMA_BUFFER_SIZE, OUTPUT_DMA_BUFFER_SIZE);
+
+ hdspm->dmaPageTable = kzalloc(sizeof(dma_addr_t) *
+ MADIFX_MAX_PAGE_TABLE_SIZE, GFP_KERNEL);
+
+ if (!hdspm->dmaPageTable) {
+ snd_printk(KERN_ERR
+ "MADIFX: unable to kmalloc dmaPageTable memory\n");
+ return -ENOMEM;
+ }
+
+ err =
+ snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(hdspm->pci),
+ wanted,
+ wanted);
+ if (err < 0) {
+ snd_printdd("Could not preallocate %zd Bytes\n", wanted);
+
+ return err;
+ } else
+ snd_printdd(" Preallocated %zd Bytes\n", wanted);
+
+ /* allocate level buffer */
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(hdspm->pci),
+ MADIFX_LEVEL_BUFFER_SIZE, &hdspm->dmaLevelBuffer);
+ if (err < 0) {
+ snd_printk(KERN_ERR
+ "MADIFX: Unable to allocate DMA level buffer\n");
+ return -ENOMEM;
+ }
+
+ /* Fill level page table */
+ for (i = 0; i < MADIFX_NUM_LEVEL_PAGES; i++) {
+ levelPageTable[i] = snd_sgbuf_get_addr(&(hdspm->dmaLevelBuffer),
+ i * MADIFX_HARDWARE_PAGE_SIZE);
+
+ }
+
+ /* Write level page table to device */
+ lpti = (MADIFX == hdspm->io_type) ? MADIFX_LPTI_HMFX :
+ MADIFX_LPTI_MFXT;
+
+ for (i = 0; i < MADIFX_NUM_LEVEL_PAGES; i++) {
+ madifx_write(hdspm, MADIFX_PAGE_ADDRESS_LIST + (4 * (lpti + i)),
+ levelPageTable[i]);
+ }
+
+ hdspm->level_buffer = snd_sgbuf_get_ptr(&(hdspm->dmaLevelBuffer), 0);
+
+ memset(hdspm->level_buffer, 0, MADIFX_LEVEL_BUFFER_SIZE);
+
+
+ return 0;
+}
+
+
+/* ------------- ALSA Devices ---------------------------- */
+static int snd_madifx_create_pcm(struct snd_card *card,
+ struct hdspm *hdspm)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ hdspm->pcm = pcm;
+ pcm->private_data = hdspm;
+ strcpy(pcm->name, hdspm->card_name);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_madifx_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_madifx_capture_ops);
+
+ pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+ err = snd_madifx_preallocate_memory(hdspm);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static inline void snd_madifx_initialize_midi_flush(struct hdspm *hdspm)
+{
+ int i;
+
+ for (i = 0; i < hdspm->midiPorts; i++)
+ snd_madifx_flush_midi_input(hdspm, i);
+}
+
+static int snd_madifx_create_alsa_devices(struct snd_card *card,
+ struct hdspm *hdspm)
+{
+ int err, i;
+
+ snd_printdd("Create card...\n");
+ err = snd_madifx_create_pcm(card, hdspm);
+ if (err < 0)
+ return err;
+
+ i = 0;
+ while (i < hdspm->midiPorts) {
+ err = snd_madifx_create_midi(card, hdspm, i);
+ if (err < 0)
+ return err;
+ i++;
+ }
+
+ err = snd_madifx_create_controls(card, hdspm);
+ if (err < 0)
+ return err;
+
+ err = snd_madifx_create_hwdep(card, hdspm);
+ if (err < 0)
+ return err;
+
+ snd_printdd("proc init...\n");
+ snd_madifx_proc_init(hdspm);
+
+ hdspm->system_sample_rate = -1;
+ hdspm->last_external_sample_rate = -1;
+ hdspm->last_internal_sample_rate = -1;
+ hdspm->playback_pid = -1;
+ hdspm->capture_pid = -1;
+ hdspm->capture_substream = NULL;
+ hdspm->playback_substream = NULL;
+
+ snd_printdd("Set defaults...\n");
+ err = snd_madifx_set_defaults(hdspm);
+ if (err < 0)
+ return err;
+
+ snd_printdd("Update mixer controls...\n");
+#if 0
+ /* FIXME: MADI FX disable, old mixer is broken */
+ madifx_update_simple_mixer_controls(hdspm);
+#endif
+
+ snd_printdd("Initializeing complete ???\n");
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_printk(KERN_ERR "MADIFX: error registering card\n");
+ return err;
+ }
+
+ snd_printdd("... yes now\n");
+
+ return 0;
+}
+
+static int snd_madifx_create(struct snd_card *card,
+ struct hdspm *hdspm) {
+
+ struct pci_dev *pci = hdspm->pci;
+ int err;
+ unsigned long io_extent;
+
+ hdspm->irq = -1;
+ hdspm->card = card;
+
+ spin_lock_init(&hdspm->lock);
+
+ pci_read_config_word(hdspm->pci,
+ PCI_CLASS_REVISION, &hdspm->firmware_rev);
+
+ strcpy(card->mixername, "Xilinx FPGA");
+ strcpy(card->driver, "MADIFX");
+
+ switch (hdspm->firmware_rev) {
+ case HDSPM_MADIFX_REV:
+ hdspm->io_type = MADIFX;
+ hdspm->card_name = "RME MADI FX";
+ hdspm->midiPorts = 4;
+ break;
+ default:
+ snd_printk(KERN_ERR
+ "MADIFX: unknown firmware revision %x\n",
+ hdspm->firmware_rev);
+ return -ENODEV;
+ }
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ pci_set_master(hdspm->pci);
+
+ err = pci_request_regions(pci, "hdspm");
+ if (err < 0)
+ return err;
+
+ hdspm->port = pci_resource_start(pci, 0);
+ io_extent = pci_resource_len(pci, 0);
+
+ snd_printdd("grabbed memory region 0x%lx-0x%lx\n",
+ hdspm->port, hdspm->port + io_extent - 1);
+
+ hdspm->iobase = ioremap_nocache(hdspm->port, io_extent);
+ if (!hdspm->iobase) {
+ snd_printk(KERN_ERR
+ "HDSPM: unable to remap region 0x%lx-0x%lx\n",
+ hdspm->port, hdspm->port + io_extent - 1);
+ return -EBUSY;
+ }
+ snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n",
+ (unsigned long)hdspm->iobase, hdspm->port,
+ hdspm->port + io_extent - 1);
+
+ if (request_irq(pci->irq, snd_madifx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, hdspm)) {
+ snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+
+ snd_printdd("use IRQ %d\n", pci->irq);
+
+ hdspm->irq = pci->irq;
+
+ snd_printdd("kmalloc Mixer memory of %zd Bytes\n",
+ sizeof(struct madifx_mixer));
+
+ hdspm->newmixer = kzalloc(sizeof(struct madifx_newmixer), GFP_KERNEL);
+ if (!hdspm->newmixer) {
+ snd_printk(KERN_ERR
+ "HDSPM: unable to kmalloc Mixer memory of %d Bytes\n",
+ (int)sizeof(struct madifx_newmixer));
+ return -ENOMEM;
+ }
+
+ {
+ /* This somehow initialises the mixer. I have no idea what it
+ * does exactly.
+ */
+ int i;
+ for (i = 0; i < MADIFX_NUM_OUTPUT_GAINS; i++)
+ hdspm->newmixer->output_gain[i] = 0x9000;
+
+ for (i = 0; i < MADIFX_LIST_LENGTH; i++) {
+ hdspm->newmixer->listCh[i] = 0;
+ hdspm->newmixer->listVol[i] = 0;
+ }
+
+ for (i = 0; i < 196; i++) {
+ hdspm->newmixer->listCh[i] = (256 + i) | (i << 9);
+ hdspm->newmixer->listVol[i] = 32768+(32768>>3);
+ }
+
+ /* Of course, the data has to be written to the device before
+ * something can happen.
+ */
+ for (i = 0; i < MADIFX_LIST_LENGTH; i++) {
+ madifx_write(hdspm, MADIFX_MIXER_LIST_CH + (4 * i),
+ hdspm->newmixer->listCh[i]);
+ madifx_write(hdspm, MADIFX_MIXER_LIST_VOL + (4 * i),
+ hdspm->newmixer->listVol[i]);
+ }
+
+ for (i = 0; i < MADIFX_NUM_OUTPUT_GAINS; i++) {
+ madifx_write(hdspm, MADIFX_WR_OUTPUT_GAIN + (4 * i),
+ hdspm->newmixer->output_gain[i]);
+ }
+ }
+
+
+ hdspm->port_names_in = NULL;
+ hdspm->port_names_out = NULL;
+
+ switch (hdspm->io_type) {
+ case MADIFX:
+ hdspm->ss_in_channels = MADIFX_SS_IN_CHANNELS;
+ hdspm->ds_in_channels = MADIFX_DS_IN_CHANNELS;
+ hdspm->qs_in_channels = MADIFX_QS_IN_CHANNELS;
+ hdspm->ss_out_channels = MADIFX_SS_OUT_CHANNELS;
+ hdspm->ds_out_channels = MADIFX_DS_OUT_CHANNELS;
+ hdspm->qs_out_channels = MADIFX_QS_OUT_CHANNELS;
+ /* FIXME: portnames and stuff missing */
+ break;
+ }
+
+
+ /* texts */
+ switch (hdspm->io_type) {
+ /* Keep the switch if MFXT will be different */
+ case MADIFX:
+ hdspm->texts_clocksource = texts_madifx_clock_source;
+ hdspm->texts_clocksource_items =
+ ARRAY_SIZE(texts_madifx_clock_source);
+ break;
+ }
+
+ tasklet_init(&hdspm->midi_tasklet,
+ madifx_midi_tasklet, (unsigned long) hdspm);
+
+
+ sprintf(card->id, "MADIFXtest");
+ snd_card_set_id(card, card->id);
+
+ snd_printdd("create alsa devices.\n");
+ err = snd_madifx_create_alsa_devices(card, hdspm);
+ if (err < 0)
+ return err;
+
+ snd_madifx_initialize_midi_flush(hdspm);
+
+ return 0;
+}
+
+
+static int snd_madifx_free(struct hdspm *hdspm)
+{
+
+ if (hdspm->port) {
+
+ /* stop th audio, and cancel all interrupts */
+ hdspm->control_register &=
+ ~(MADIFX_START | MADIFX_IE_AUDIO |
+ MADIFX_IEN0 | MADIFX_IEN1 |
+ MADIFX_IEN2 | MADIFX_IEN3);
+ madifx_write(hdspm, MADIFX_CONTROL_REG,
+ hdspm->control_register);
+ madifx_write(hdspm, MADIFX_START_LEVEL, 0);
+ }
+
+ if (hdspm->irq >= 0)
+ free_irq(hdspm->irq, (void *) hdspm);
+
+ kfree(hdspm->newmixer);
+ kfree(hdspm->dmaPageTable);
+ snd_dma_free_pages(&(hdspm->dmaLevelBuffer));
+
+ if (hdspm->iobase)
+ iounmap(hdspm->iobase);
+
+ if (hdspm->port)
+ pci_release_regions(hdspm->pci);
+
+ pci_disable_device(hdspm->pci);
+ return 0;
+}
+
+
+static void snd_madifx_card_free(struct snd_card *card)
+{
+ struct hdspm *hdspm = card->private_data;
+
+ if (hdspm)
+ snd_madifx_free(hdspm);
+}
+
+
+static int snd_madifx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct hdspm *hdspm;
+ struct snd_card *card;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ err = snd_card_create(index[dev], id[dev],
+ THIS_MODULE, sizeof(struct hdspm), &card);
+ if (err < 0)
+ return err;
+
+ hdspm = card->private_data;
+ card->private_free = snd_madifx_card_free;
+ hdspm->dev = dev;
+ hdspm->pci = pci;
+
+ snd_card_set_dev(card, &pci->dev);
+
+ err = snd_madifx_create(card, hdspm);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ sprintf(card->shortname, "%s_%x",
+ hdspm->card_name,
+ hdspm->serial);
+ sprintf(card->longname, "%s S/N 0x%x at 0x%lx, irq %d",
+ hdspm->card_name,
+ hdspm->serial,
+ hdspm->port, hdspm->irq);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ pci_set_drvdata(pci, card);
+
+ madifx_write(hdspm, MADIFX_START_LEVEL, 1);
+
+ dev++;
+ return 0;
+}
+
+static void snd_madifx_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver madifx_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_madifx_ids,
+ .probe = snd_madifx_probe,
+ .remove = snd_madifx_remove,
+};
+
+module_pci_driver(madifx_driver);
--
1.7.10.4
More information about the Alsa-devel
mailing list