[alsa-devel] [PATCH RFC 0/4] Driver for RME MADI FX
Hi!
I've been developing an out-of-tree driver for the RME MADI FX. History is available at
https://github.com/adiknoth/madifx
but doesn't follow any kernel convention regarding commit isolation, it was basically a backup/scratchpad during development.
I don't have too much feedback, yet, but I guess it makes sense to mainline it earlier than later, increase the driver's exposure and fix any problems when the card is in more widespread use.
Note that this patch series is essentially RFC right now to gather developer feedback.
Cheers
Adrian Knoth (4): ALSA: madifx - Makefile entries for RME MADI FX ALSA: Add RME MADI FX Kconfig entries ALSA: madifx - Expose preliminary userspace interface ALSA: madifx - Add support for RME MADI FX
include/uapi/sound/madifx.h | 173 ++ sound/pci/Kconfig | 21 + sound/pci/rme9652/Makefile | 2 + sound/pci/rme9652/madifx.c | 3986 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 4182 insertions(+) create mode 100644 include/uapi/sound/madifx.h create mode 100644 sound/pci/rme9652/madifx.c
Add Makefile entries for RME MADI FX cards.
Signed-off-by: Adrian Knoth adi@drcomp.erfurt.thur.de
diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile index dcba560..e483dce 100644 --- a/sound/pci/rme9652/Makefile +++ b/sound/pci/rme9652/Makefile @@ -6,8 +6,10 @@ snd-rme9652-objs := rme9652.o snd-hdsp-objs := hdsp.o snd-hdspm-objs := hdspm.o +snd-madifx-objs := madifx.o
# Toplevel Module Dependency obj-$(CONFIG_SND_RME9652) += snd-rme9652.o obj-$(CONFIG_SND_HDSP) += snd-hdsp.o obj-$(CONFIG_SND_HDSPM) +=snd-hdspm.o +obj-$(CONFIG_SND_MADIFX) +=snd-madifx.o
Signed-off-by: Adrian Knoth adi@drcomp.erfurt.thur.de
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 947cfb4..d1fe845 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -598,6 +598,27 @@ config SND_HDSPM To compile this driver as a module, choose M here: the module will be called snd-hdspm.
+config SND_MADIFX + tristate "RME Hammerfall DSP MADI FX" + select SND_HWDEP + select SND_RAWMIDI + select SND_PCM + help + Say Y here to include support for RME Hammerfall DSP MADI FX + soundcards. + + To compile this driver as a module, choose M here: the module + will be called snd-hdspm. + +if SND_MADIFX +config SND_MADIFX_BROKEN + bool "Include broken WIP code" + help + This option is intended for developers, most people should say N + here. It enables work-in-progress code that's not yet usable from + userspace. +endif # SND_MADIFX_BROKEN + config SND_ICE1712 tristate "ICEnsemble ICE1712 (Envy24)" select SND_MPU401_UART
Despite its name "HDSPe", the MADI FX is a new card with a similar but different design requiring new userspace tools.
These tools don't exist, yet, but to facilitate their development, expose a preliminary userspace API. Note that the stable bits are always enabled while the volatile parts require CONFIG_SND_MADIFX_BROKEN to be set.
Signed-off-by: Adrian Knoth adi@drcomp.erfurt.thur.de
diff --git a/include/uapi/sound/madifx.h b/include/uapi/sound/madifx.h new file mode 100644 index 0000000..3b07322 --- /dev/null +++ b/include/uapi/sound/madifx.h @@ -0,0 +1,173 @@ +#ifndef __SOUND_MADIFX_H +#define __SOUND_MADIFX_H +/* + * Copyright (C) 2003 Winfried Ritsch (IEM) + * based on hdsp.h from Thomas Charbonnel (thomas@undata.org) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */ +#define HDSPM_MAX_CHANNELS 196 + +enum madifx_io_type { + MADIFX +}; + +enum madifx_speed { + ss = 0, + ds = 1, + qs = 2 +}; + +/* -------------------- IOCTL Peak/RMS Meters -------------------- */ + +#ifdef CONFIG_SND_MADIFX_BROKEN +struct madifx_level_buffer { + uint32_t rms_out_pre[2 * 256]; + uint32_t peak_out_pre[256]; + + uint32_t rms_in[2 * 256]; + uint32_t peak_in[256]; + + uint32_t rms_play[2 * 256]; + uint32_t peak_play[256]; + + uint32_t rms_out[2 * 256]; + uint32_t peak_out[256]; + + uint32_t rms_in_pre[2 * 256]; + uint32_t peak_in_pre[256]; + + uint8_t speed; /* enum {ss, ds, qs} */ +}; + +#define SNDRV_MADIFX_IOCTL_GET_LEVEL \ + _IOR('H', 0x42, struct madifx_level_buffer) + +#endif /* CONFIG_SND_MADIFX_BROKEN */ + +/* ------------ CONFIG block IOCTL ---------------------- */ + +struct madifx_config { + uint8_t madi_tx_64[3]; + uint8_t madi_smux[3]; + uint8_t wcterm; + uint8_t wck48; + uint8_t aespro; + uint8_t redundancy_mode; + uint8_t mirror_madi_out; +}; + +#define SNDRV_MADIFX_IOCTL_GET_CONFIG \ + _IOR('H', 0x41, struct madifx_config) + + +/** + * The status data reflects the device's current state + * as determined by the card's configuration and + * connection status. + **/ + +enum madifx_sync { + madifx_sync_no_lock = 0, + madifx_sync_lock = 1, + madifx_sync_sync = 2 +}; + +enum madifx_madi_channel_format { + madifx_format_ch_64 = 0, + madifx_format_ch_56 = 1, + madifx_format_ch_32 = 2, + madifx_format_ch_28 = 3, + madifx_format_ch_16 = 4, + madifx_format_ch_14 = 5, + madifx_format_ch_nolock = 6 +}; + +enum madifx_madi_frame_format { + madifx_frame_48 = 0, + madifx_frame_96 = 1 +}; + +enum madifx_syncsource { + syncsource_madi1 = 0, + syncsource_madi2 = 1, + syncsource_madi3 = 2, + syncsource_aes = 3, + syncsource_wc = 4, + syncsource_syncin = 5, + syncsource_none = 6 +}; + +enum madifx_clocksource { + clock_internal = 0, + clock_aes = 1, + clock_wc = 2, + clock_madi1 = 3, + clock_madi2 = 4, + clock_madi3 = 5 +}; + + +struct madifx_status { + uint8_t card_type; /* enum madifx_io_type */ + uint8_t clock_selection; /* enum madi_clocksource */ + uint32_t system_sample_rate; + uint8_t madi_channelcount[3]; /* enum madifx_madi_channel_format */ + uint32_t external_sample_rates[6]; /* enum madifx_syncsource */ + uint8_t sync_check[6]; /* enum madifx_sync, array idx: enum madifx_syncsource */ +}; + +#define SNDRV_MADIFX_IOCTL_GET_STATUS \ + _IOR('H', 0x47, struct madifx_status) + + +/* ------------- get Matrix Mixer IOCTL --------------- */ + +/* We don't know too much about the new mixer, yet. See madifx.c for the bits + * we already have. + */ +#define HDSPM_MIXER_CHANNELS HDSPM_MAX_CHANNELS +#define MADIFX_LIST_LENGTH 4096 +#define MADIFX_NUM_OUTPUT_GAINS 198 +#define MADIFX_NUM_LEVEL_PAGES 5 +#define MADIFX_LEVEL_BUFFER_SIZE (MADIFX_NUM_LEVEL_PAGES * 4096) + +/* FIXME: maybe move to .c file */ +struct madifx_newmixer { + uint32_t listVol[MADIFX_LIST_LENGTH]; + uint32_t listCh[MADIFX_LIST_LENGTH]; + uint32_t output_gain[MADIFX_NUM_OUTPUT_GAINS]; +}; + +struct madifx_mixer_ioctl { + struct madifx_newmixer *mixer; +}; + +#ifdef CONFIG_SND_MADIFX_BROKEN +/* use indirect access due to the limit of ioctl bit size */ +#define SNDRV_HDSPM_IOCTL_GET_MIXER _IOR('H', 0x44, struct madifx_mixer_ioctl) +#endif + +/* typedefs for compatibility to user-space */ +typedef struct madifx_peak_rms madifx_peak_rms_t; +typedef struct madifx_config_info madifx_config_info_t; +typedef struct madifx_channelfader snd_madifx_channelfader_t; +typedef struct madifx_newmixer madifx_mixer_t; + + +#endif
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);
On Fri, 1 Feb 2013 13:48:39 +0100 Adrian Knoth adi@drcomp.erfurt.thur.de wrote:
Hi.
Signed-off-by: Adrian Knoth adi@drcomp.erfurt.thur.de
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 947cfb4..d1fe845 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -598,6 +598,27 @@ config SND_HDSPM To compile this driver as a module, choose M here: the module will be called snd-hdspm.
+config SND_MADIFX
- tristate "RME Hammerfall DSP MADI FX"
- select SND_HWDEP
- select SND_RAWMIDI
- select SND_PCM
- help
Say Y here to include support for RME Hammerfall DSP MADI
FX
soundcards.
To compile this driver as a module, choose M here: the
module
will be called snd-hdspm.
Should this be "snd-madifx"? Also, the the mail on patch 4/4 seems to be lost - can you resend it?
Cheers, Andre
On Sat, Feb 02, 2013 at 04:16:36PM +0100, Andre Schramm wrote:
Hi!
+config SND_MADIFX
- tristate "RME Hammerfall DSP MADI FX"
- select SND_HWDEP
- select SND_RAWMIDI
- select SND_PCM
- help
Say Y here to include support for RME Hammerfall DSP MADI
FX
soundcards.
To compile this driver as a module, choose M here: the
module
will be called snd-hdspm.
Should this be "snd-madifx"?
Of course. Copy&paste error, thanks for spotting. Fixed.
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.
I've resent it. Let's see.
Cheers
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);
participants (2)
-
Adrian Knoth
-
Andre Schramm