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@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@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@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);