summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_madi
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_madi')
-rw-r--r--kernel/drv/oss_madi/.devices1
-rw-r--r--kernel/drv/oss_madi/.name1
-rw-r--r--kernel/drv/oss_madi/.params12
-rw-r--r--kernel/drv/oss_madi/madi.h246
-rw-r--r--kernel/drv/oss_madi/madi_mixer.c459
-rw-r--r--kernel/drv/oss_madi/oss_madi.c975
-rw-r--r--kernel/drv/oss_madi/oss_madi.man70
7 files changed, 1764 insertions, 0 deletions
diff --git a/kernel/drv/oss_madi/.devices b/kernel/drv/oss_madi/.devices
new file mode 100644
index 0000000..c282a96
--- /dev/null
+++ b/kernel/drv/oss_madi/.devices
@@ -0,0 +1 @@
+oss_madi pci10ee,3fc6 RME MADI (not supported yet)
diff --git a/kernel/drv/oss_madi/.name b/kernel/drv/oss_madi/.name
new file mode 100644
index 0000000..a776e0b
--- /dev/null
+++ b/kernel/drv/oss_madi/.name
@@ -0,0 +1 @@
+RME MADI Digital Interface
diff --git a/kernel/drv/oss_madi/.params b/kernel/drv/oss_madi/.params
new file mode 100644
index 0000000..9a9907e
--- /dev/null
+++ b/kernel/drv/oss_madi/.params
@@ -0,0 +1,12 @@
+int madi_maxchannels=64;
+/*
+ * By default OSS will create device files for all 64 channels (32 stereo
+ * pairs). Number of channels can be decreased by changing this parameter.
+ */
+int madi_devsize=2;
+/*
+ * This parameter tells how the device files should be created for MADI
+ * channels. By default (2) a device file will be created for each stereo
+ * channel pair. Value of 1 means that separate device file will be
+ * created for each channel.
+ */
diff --git a/kernel/drv/oss_madi/madi.h b/kernel/drv/oss_madi/madi.h
new file mode 100644
index 0000000..3cdd172
--- /dev/null
+++ b/kernel/drv/oss_madi/madi.h
@@ -0,0 +1,246 @@
+/*
+ * Purpose: Internal definitions for RME MADI and AES32 audio interfaces
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+
+/*
+ * DMA buffer size (for one direction)
+ */
+#define MAX_CHANNELS 64
+#define CHBUF_SIZE (64*1024) // 64k / channel
+#define CHBUF_PAGES (CHBUF_SIZE/4096)
+#define DMABUF_SIZE (MAX_CHANNELS*CHBUF_SIZE) // 4 Mbytes (total)
+
+
+/*
+ * Monitor mixer channel
+ */
+#define MONITOR_CH 64 // ???
+
+/*
+ * Registers
+ */
+#define MADI_control 64
+#define MADI_interruptAck 96
+#define MADI_control2 256
+#define MADI_freq 256 /* AES32 only */
+#define MADI_midiOut0 352
+#define MADI_midiOut1 356
+#define MADI_eeprom 384 /* AES32 only */
+#define MADI_outputEnableStart 512
+#define MADI_inputEnableStart 768
+#define MADI_PlayPageTable 8192
+#define MADI_RecPageTable 12288 /* 12k */
+#define MADI_MATRIX_MIXER_SIZE 8192
+#define MADI_mixerStart 32768
+
+#define MADI_status 0
+#define MADI_status2 192
+#define MADI_timecode 128
+#define MADI_midiIn0 360
+#define MADI_midiIn1 364
+#define MADI_midiStatusOut0 384
+#define MADI_midiStatusOut1 388
+#define MADI_midiStatusIn0 392
+#define MADI_midiStatusIn1 396
+#define MADI_peakrmsStart 4096
+#define MADI_inpeaks (1024*4)
+#define MADI_playpeaks (MADI_inpeaks+64*4)
+#define MADI_outpeaks (MADI_playpeaks+64*4)
+
+/*
+ * Control register bits
+ */
+#define MADI_Start (1<<0)
+#define MADI_Latency0 (1<<1)
+#define MADI_Latency1 (1<<2)
+#define MADI_Latency2 (1<<3)
+#define MADI_ClockModeMaster (1<<4)
+#define MADI_AudioInterruptEnable (1<<5)
+
+#define MADI_Freq0 (1<<6)
+#define MADI_Freq1 (1<<7)
+#define MADI_DblSpeed (1<<8)
+#define MADI_QuadSpeed (1U<<31)
+
+#define MADI_ProBit (1<<9) // AES32
+
+#define MADI_TX_64ch_mode (1<<10) // MADI
+#define MADI_Emphasis (1<<10) // AES32
+
+#define MADI_AutoInput (1<<11) // MADI
+#define MADI_DataBit (1<<11) // AES32
+
+#define MADI_InputSrc0 (1<<14) // MADI
+#define MADI_InputSrc1 (1<<15)
+#define MADI_SyncSrc0 (1<<16)
+#define MADI_SyncSrc1 (1<<17) // AES32
+#define MADI_SyncSrc2 (1<<13) // AES32
+#define MADI_SyncSrc3 (1<<25) // AES32
+#define MADI_SMUX (1<<18) // MADI
+#define MADI_clr_tms (1<<19)
+
+#define MADI_taxi_reset (1<<20) // MADI
+#define MADI_WCK48 (1<<20) // AES32
+
+#define MADI_Midi0IntrEna (1<<22)
+#define MADI_Midi1IntrEna (1<<23)
+
+#define MADI_LineOut (1<<24)
+
+#define MADI_DS_2Wire (1<<26) // AES32
+#define MADI_QS_2Wire (1<<27) // AES32
+#define MADI_QS_4Wire (1<<28) // AES32
+
+#define MADI_wclk_sel (1<<30)
+
+/*
+ * Control2 register bits
+ */
+#define MADI_BIGENDIAN_MODE (1<<9)
+
+/*
+ * Helper macros for the command register
+ */
+#define MADI_FreqMask (MADI_Freq0|MADI_Freq1|\
+ MADI_DblSpeed|MADI_QuadSpeed)
+
+#define MADI_LatencyMask (MADI_Latency0|MADI_Latency1|MADI_Latency2)
+
+#define MADI_InputMask (MADI_InputSrc0|MADI_InputSrc1)
+#define MADI_InputOptical 0
+#define MADI_InputCoax (MADI_InputSrc0)
+#define MADI_SyncRefMask (MADI_SyncSrc0|MADI_SyncSrc1|\
+ MADI_SyncSrc2|MADI_SyncSrc3)
+#define MADI_SyncRef_Word 0
+#define MADI_SyncRef_MADI (MADI_SyncSrc0)
+
+/*
+ * Status register bits
+ */
+#define MADI_audioIntPending (1<<0)
+#define MADI_RX_64ch_mode (1<<1)
+#define MADI_AB_int (1<<2)
+#define MADI_LockStatus (1<<3)
+#define MADI_BufferPosMask 0x000FFC0
+#define MADI_madiSync (1<<18)
+#define MADI_DblSpeedStatus (1<<19)
+
+#define MADI_Freq0_status (1<<22)
+#define MADI_Freq1_status (1<<23)
+#define MADI_Freq2_status (1<<24)
+#define MADI_Freq3_status (1<<25)
+
+#define MADI_BufferHalf (1<<26)
+
+#define MADI_midi0IRQStatus (1<<30)
+#define MADI_midi1IRQStatus (1U<<31)
+
+#define UNITY_GAIN 32768
+#define MUTE_GAIN 0
+
+typedef struct
+{
+ int open_mode;
+ unsigned int trigger_bits;
+ int direction;
+#define DIR_IN PCM_ENABLE_INPUT
+#define DIR_OUT PCM_ENABLE_OUTPUT
+ int channel; /* Index to the first channel */
+
+ int audio_dev;
+
+ int max_channels;
+ int channels; /* Number of channels */
+} madi_portc_t;
+
+typedef struct
+{
+ oss_device_t *osdev;
+ oss_mutex_t mutex;
+ char *name;
+
+ int model;
+#define MDL_MADI 0
+#define MDL_AES32 1
+
+ char *registers;
+ oss_native_word physaddr;
+
+ unsigned int cmd, cmd2; // Cached control/control2 register values
+
+ /* Sample rate, etc. */
+ unsigned int rate, next_rate;
+ unsigned long long active_inputs, active_outputs; /* Bitmasks indexed by ch# */
+
+ /* Mixer */
+ int mixer_dev;
+
+ /* Shadow of the hw mixer gain registers */
+ unsigned short mixer_values[MAX_CHANNELS][2 * MAX_CHANNELS];
+
+ /* Playback */
+ unsigned char *playbuf;
+ oss_native_word playbuf_phys;
+ oss_dma_handle_t play_dma_handle;
+ int num_outputs;
+ madi_portc_t *out_portc[MAX_CHANNELS];
+ unsigned long long busy_play_channels;
+
+ /* Recording */
+ unsigned char *recbuf;
+ oss_native_word recbuf_phys;
+ oss_dma_handle_t rec_dma_handle;
+ int num_inputs;
+ madi_portc_t *in_portc[MAX_CHANNELS];
+ unsigned long long busy_rec_channels;
+
+ volatile int open_audiodevs; /* Number of audio input/output devices currently open */
+ int first_audiodev;
+} madi_devc_t;
+
+static __inline__ void
+madi_write (madi_devc_t * devc, int reg, unsigned int value)
+{
+ PCI_WRITEL (devc->osdev, devc->registers + reg, value);
+}
+
+static __inline__ unsigned int
+madi_read (madi_devc_t * devc, int reg)
+{
+ return PCI_READL (devc->osdev, devc->registers + reg);
+}
+
+static __inline__ void
+madi_control (madi_devc_t * devc, unsigned int value)
+{
+ madi_write (devc, MADI_control, value);
+ devc->cmd = value;
+}
+
+static __inline__ void
+madi_control2 (madi_devc_t * devc, unsigned int value)
+{
+ madi_write (devc, MADI_control2, value);
+ devc->cmd2 = value;
+}
+
+#define SRC_IN 0
+#define SRC_PLAY 64
+extern void madi_write_gain (madi_devc_t * devc, unsigned int chn,
+ unsigned int src, unsigned short value);
+extern int madi_read_gain (madi_devc_t * devc, unsigned int chn,
+ unsigned int src);
+extern int madi_install_mixer (madi_devc_t * devc);
+extern void madi_activate_mixer (madi_devc_t * devc);
diff --git a/kernel/drv/oss_madi/madi_mixer.c b/kernel/drv/oss_madi/madi_mixer.c
new file mode 100644
index 0000000..8882fa7
--- /dev/null
+++ b/kernel/drv/oss_madi/madi_mixer.c
@@ -0,0 +1,459 @@
+/*
+ * Purpose: Mixer/control panel for RME MADI and AES32 audio interfaces
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_madi_cfg.h"
+#include "oss_pci.h"
+#include "madi.h"
+
+extern int madi_maxchannels;
+
+static const unsigned char peak_cnv[256] = {
+ 0, 18, 29, 36, 42, 47, 51, 54, 57, 60, 62, 65, 67, 69, 71, 72,
+ 74, 75, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 89, 90,
+ 91, 92, 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 100, 101,
+ 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108,
+ 108,
+ 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 113, 113, 113, 114, 114,
+ 114,
+ 115, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119,
+ 119,
+ 119, 119, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123,
+ 123,
+ 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126,
+ 126,
+ 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129,
+ 130,
+ 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 132, 132, 132, 132,
+ 132,
+ 132, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 135,
+ 135,
+ 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137,
+ 137,
+ 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139,
+ 139,
+ 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141,
+ 141,
+ 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143,
+ 143,
+ 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144,
+};
+
+void
+madi_write_gain (madi_devc_t * devc, unsigned int chn, unsigned int src,
+ unsigned short value)
+{
+/*
+ * Write gain value to a mixer register. Parameter chn selects the output
+ * channel. Parameter src is the signal source. If (src & SRC_PLAY) then set
+ * the audio (DMA) playback volume (pcm[src]->out[chn]). Otherwise set
+ * the input[src]->out[chn] volume
+ */
+
+ if (chn >= MAX_CHANNELS || src >= (SRC_PLAY + MAX_CHANNELS))
+ return;
+
+ madi_write (devc, MADI_mixerStart + (src + 128 * chn) * 4, value);
+ devc->mixer_values[chn][src] = value;
+}
+
+int
+madi_read_gain (madi_devc_t * devc, unsigned int chn, unsigned int src)
+{
+ if (chn >= MAX_CHANNELS || src >= (SRC_PLAY + MAX_CHANNELS))
+ return 0;
+
+ return devc->mixer_values[chn][src];
+}
+
+static int
+madi_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
+{
+#if 0
+/*
+ * Dummy mixer. Some broken applications require this.
+ */
+
+ if (cmd == SOUND_MIXER_READ_CAPS)
+ return *arg = SOUND_CAP_NOLEGACY;
+
+ if (cmd == SOUND_MIXER_READ_DEVMASK ||
+ cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC)
+ return *arg = 0;
+
+ if (cmd == SOUND_MIXER_READ_VOLUME || cmd == SOUND_MIXER_READ_PCM)
+ return *arg = 100 | (100 << 8);
+ if (cmd == SOUND_MIXER_WRITE_VOLUME || cmd == SOUND_MIXER_WRITE_PCM)
+ return *arg = 100 | (100 << 8);
+#endif
+ return OSS_EINVAL;
+}
+
+ /*ARGSUSED*/ static int
+get_peak (int dev, int ctrl, unsigned int cmd, int value)
+{
+ madi_devc_t *devc = mixer_devs[dev]->devc;
+ unsigned int v;
+
+ if (cmd != SNDCTL_MIX_READ)
+ return OSS_EINVAL;
+
+ v = madi_read (devc, ctrl) >> 24; // Left channel
+ value = peak_cnv[v & 0xff];
+
+ v = madi_read (devc, ctrl + 4) >> 24; // Right channel
+ value |= peak_cnv[v & 0xff] << 8;
+
+ return value;
+}
+
+ /*ARGSUSED*/ static int
+vol_slider (int dev, int ctrl, unsigned int cmd, int value)
+{
+ madi_devc_t *devc = mixer_devs[dev]->devc;
+ unsigned int v;
+ int chn, src;
+
+ chn = (ctrl >> 8) & 0xff;
+ src = ctrl & 0xff;
+
+ if (chn >= MAX_CHANNELS || src >= 2 * MAX_CHANNELS)
+ return OSS_EINVAL;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ // Left channel
+ v = devc->mixer_values[chn][src];
+ if (v > 0)
+ v--;
+ value = v & 0xffff;
+
+ // Right channel
+ v = devc->mixer_values[chn + 1][src + 1];
+ if (v > 0)
+ v--;
+ value |= (v & 0xffff) << 16;
+
+ return value;
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ // Left channel
+ v = value & 0xffff;
+ if (v > UNITY_GAIN)
+ v = UNITY_GAIN;
+ if (v > 0)
+ v++;
+ madi_write_gain (devc, chn, src, v);
+
+ // Right channel
+ v = (value >> 16) & 0xffff;
+ if (v > UNITY_GAIN)
+ v = UNITY_GAIN;
+ if (v > 0)
+ v++;
+ madi_write_gain (devc, chn + 1, src + 1, v);
+
+ return value;
+ }
+
+ return OSS_EINVAL;
+}
+
+ /*ARGSUSED*/ static int
+madi_set_ctl (int dev, int ctrl, unsigned int cmd, int value)
+{
+ madi_devc_t *devc = mixer_devs[dev]->devc;
+ unsigned int status;
+
+ if (cmd == SNDCTL_MIX_READ)
+ switch (ctrl)
+ {
+ case 0: /* Buffer size */
+ return (devc->cmd & MADI_LatencyMask) >> 1;
+ break;
+
+ case 1: /* Sync locked status */
+ status = madi_read (devc, MADI_status);
+ return !!(status & MADI_LockStatus);
+ break;
+
+ case 2: /* Input source (Optical/Coax) */
+ return !!(devc->cmd & MADI_InputSrc0);
+ break;
+
+ case 3: /* Clock source */
+ return !(devc->cmd & MADI_ClockModeMaster);
+ break;
+
+ case 4: /* Preferred sync reference */
+ return !!(devc->cmd & MADI_SyncRef_MADI);
+ break;
+
+ case 5: /* Safe Mode (auto input) */
+ return !!(devc->cmd & MADI_AutoInput);
+ break;
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ switch (ctrl)
+ {
+ case 0: /* Buffer size */
+ if (devc->busy_play_channels || devc->busy_rec_channels) /* Device busy */
+ return (devc->cmd & MADI_LatencyMask) >> 1;
+
+ if (value < 0 || value > 7)
+ return OSS_EINVAL;
+
+ devc->cmd &= ~MADI_LatencyMask;
+ devc->cmd |= value << 1;
+ madi_control (devc, devc->cmd);
+ return value;
+ break;
+
+ case 2: /* Input source (Optical/Coax) */
+ value = !!value;
+ if (value)
+ madi_control (devc, devc->cmd | MADI_InputSrc0);
+ else
+ madi_control (devc, devc->cmd & ~MADI_InputSrc0);
+ return value;
+ break;
+
+ case 3: /* Clock source */
+ value = !!value;
+ if (!value)
+ madi_control (devc, devc->cmd | MADI_ClockModeMaster);
+ else
+ madi_control (devc, devc->cmd & ~MADI_ClockModeMaster);
+ return value;
+ break;
+
+ case 4: /* Preferred sync reference */
+ value = !!value;
+ if (value)
+ madi_control (devc, devc->cmd | MADI_SyncRef_MADI);
+ else
+ madi_control (devc, devc->cmd & ~MADI_SyncRef_MADI);
+ return value;
+ break;
+
+ case 5: /* Safe Mode (auto input) */
+ if (value)
+ madi_control (devc, devc->cmd | MADI_AutoInput);
+ else
+ madi_control (devc, devc->cmd & ~MADI_AutoInput);
+ return value;
+ break;
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+madi_mix_init (int dev)
+{
+ int i;
+ madi_devc_t *devc = mixer_devs[dev]->devc;
+ char tmp[16];
+ int err, ctl;
+ int group = 0;
+
+/*
+ * Common controls
+ */
+
+ if ((ctl = mixer_ext_create_control (dev, 0,
+ 3,
+ madi_set_ctl,
+ MIXT_ENUM, "clock", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ mixer_ext_set_strings (dev, ctl, "master autosync", 0);
+
+ if (devc->model == MDL_MADI)
+ {
+ if ((ctl = mixer_ext_create_control (dev, 0,
+ 4,
+ madi_set_ctl,
+ MIXT_ENUM, "syncref", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ mixer_ext_set_strings (dev, ctl, "WordClock MADI-in", 0);
+ }
+
+ if (devc->model == MDL_MADI)
+ {
+ if ((ctl = mixer_ext_create_control (dev, 0,
+ 2,
+ madi_set_ctl,
+ MIXT_ENUM, "input", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ mixer_ext_set_strings (dev, ctl, "optical coax", 0);
+ }
+
+ if (devc->model == MDL_MADI)
+ {
+ if ((ctl = mixer_ext_create_control (dev, 0,
+ 5,
+ madi_set_ctl,
+ MIXT_ONOFF, "safe-input", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ }
+
+ if ((ctl = mixer_ext_create_control (dev, 0,
+ 0,
+ madi_set_ctl,
+ MIXT_ENUM, "buffer", 8,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ mixer_ext_set_strings (dev, ctl, "64 128 256 512 1k 2k 4k 8k", 0);
+
+ if ((ctl = mixer_ext_create_control (dev, 0,
+ 1,
+ madi_set_ctl,
+ MIXT_VALUE, "lock", 2,
+ MIXF_OKFAIL | MIXF_READABLE |
+ MIXF_POLL)) < 0)
+ return ctl;
+
+ /*
+ * Input mixer
+ */
+
+ for (i = 0; i < madi_maxchannels; i += 2)
+ {
+ int offs = MADI_inpeaks + i * 4;
+ int ctl;
+
+ if (!(i % 32))
+ if ((group = mixer_ext_create_group (dev, 0, "in")) < 0)
+ return group;
+
+ ctl = (i << 8) | (SRC_IN | i);
+
+ sprintf (tmp, "%d-%d", i + 1, i + 2);
+ if ((err =
+ mixer_ext_create_control (dev, group, ctl, vol_slider,
+ MIXT_STEREOSLIDER16, tmp, UNITY_GAIN - 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ if ((err =
+ mixer_ext_create_control (dev, group, offs, get_peak,
+ MIXT_STEREOPEAK, "-", 144,
+ MIXF_READABLE | MIXF_DECIBEL)) < 0)
+ return err;
+ }
+
+ /*
+ * Playback (pcm) mixer
+ */
+ for (i = 0; i < devc->num_outputs; i++)
+ {
+ int offs = MADI_playpeaks;
+ int ctl, ch;
+ madi_portc_t *portc = devc->out_portc[i];
+
+ ch = portc->channel;
+ offs += ch * 4;
+
+ if (!(i % 16))
+ if ((group = mixer_ext_create_group (dev, 0, "play")) < 0)
+ return group;
+
+ ctl = (ch << 8) | (SRC_PLAY | ch);
+
+ if ((err =
+ mixer_ext_create_control (dev, group, offs, get_peak,
+ MIXT_STEREOPEAK, "-", 144,
+ MIXF_READABLE | MIXF_DECIBEL)) < 0)
+ return err;
+
+ sprintf (tmp, "@pcm%d", portc->audio_dev);
+ if ((err =
+ mixer_ext_create_control (dev, group, ctl, vol_slider,
+ MIXT_STEREOSLIDER16, tmp, UNITY_GAIN - 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ }
+
+ /*
+ * Output mixer
+ */
+ for (i = 0; i < madi_maxchannels; i += 2)
+ {
+ int offs = MADI_outpeaks + i * 4;
+
+ if (!(i % 64))
+ if ((group = mixer_ext_create_group (dev, 0, "out")) < 0)
+ return group;
+
+ sprintf (tmp, "%d-%d", i + 1, i + 2);
+ if ((err =
+ mixer_ext_create_control (dev, group, offs, get_peak,
+ MIXT_STEREOPEAK, tmp, 144,
+ MIXF_READABLE | MIXF_DECIBEL)) < 0)
+ return err;
+ }
+
+#if 0
+ /*
+ * Analog monitor
+ */
+ {
+ int offs = MADI_outpeaks + MONITOR_CH * 4;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, offs, get_peak,
+ MIXT_STEREOPEAK, "mon", 144,
+ MIXF_READABLE | MIXF_DECIBEL)) < 0)
+ return err;
+ }
+#endif
+
+ return 0;
+}
+
+static mixer_driver_t madi_mixer_driver = {
+ madi_mixer_ioctl
+};
+
+int
+madi_install_mixer (madi_devc_t * devc)
+{
+ int my_mixer;
+
+ if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ devc->name,
+ &madi_mixer_driver,
+ sizeof (mixer_driver_t), devc)) < 0)
+ return my_mixer;
+
+ mixer_devs[my_mixer]->priority = -1; /* Don't use as the default mixer */
+
+ return my_mixer;
+}
+
+void
+madi_activate_mixer (madi_devc_t * devc)
+{
+ mixer_ext_set_init_fn (devc->mixer_dev, madi_mix_init, madi_maxchannels*5 + 30);
+ touch_mixer (devc->mixer_dev);
+}
diff --git a/kernel/drv/oss_madi/oss_madi.c b/kernel/drv/oss_madi/oss_madi.c
new file mode 100644
index 0000000..8f0f2fa
--- /dev/null
+++ b/kernel/drv/oss_madi/oss_madi.c
@@ -0,0 +1,975 @@
+/*
+ * Purpose: Driver for RME MADI and AES32 audio interfaces
+ *
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_madi_cfg.h"
+#include "oss_pci.h"
+#include "madi.h"
+
+#define RME_VENDOR_ID 0x10ee /* Xilinx actually */
+#define RME_MADI 0x3fc6
+
+#define HWINFO_SIZE 128
+
+extern int madi_devsize;
+extern int madi_maxchannels;
+
+#define LSWAP(x) x
+
+static void
+set_output_enable (madi_devc_t * devc, unsigned int chn, int enabled)
+{
+ madi_write (devc, MADI_outputEnableStart + 4 * chn, enabled);
+}
+
+static void
+set_input_enable (madi_devc_t * devc, unsigned int chn, int enabled)
+{
+ madi_write (devc, MADI_inputEnableStart + 4 * chn, enabled);
+}
+
+static void
+start_audio (madi_devc_t * devc)
+{
+ madi_control (devc, devc->cmd | (MADI_AudioInterruptEnable | MADI_Start));
+}
+
+static void
+stop_audio (madi_devc_t * devc)
+{
+ madi_control (devc, devc->cmd & ~(MADI_AudioInterruptEnable | MADI_Start));
+}
+
+static void
+audio_interrupt (madi_devc_t * devc, unsigned int status)
+{
+ int i;
+
+ for (i = 0; i < devc->num_outputs; i++)
+ {
+ madi_portc_t *portc = devc->out_portc[i];
+
+ if (!(portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ continue;
+
+ oss_audio_outputintr (portc->audio_dev, 1);
+ }
+
+ for (i = 0; i < devc->num_inputs; i++)
+ {
+ madi_portc_t *portc = devc->in_portc[i];
+
+ if (!(portc->trigger_bits & PCM_ENABLE_INPUT))
+ continue;
+
+ oss_audio_inputintr (portc->audio_dev, 1);
+ }
+}
+
+static int
+madiintr (oss_device_t * osdev)
+{
+ madi_devc_t *devc = osdev->devc;
+ unsigned int status;
+
+ status = madi_read (devc, MADI_status);
+
+ if (status & MADI_audioIntPending)
+ audio_interrupt (devc, status);
+
+ madi_write (devc, MADI_interruptAck, 0);
+
+ return !!(status &
+ (MADI_audioIntPending | MADI_midi0IRQStatus | MADI_midi1IRQStatus))
+ != 0;
+}
+
+static void
+madi_change_hw_rate (madi_devc_t * devc)
+{
+ unsigned int rate_bits = 0;
+ unsigned long long v;
+
+ devc->rate = devc->next_rate;
+ /*
+ * Compute and set the sample rate in control2 register.
+ */
+
+ rate_bits = MADI_Freq1; // TODO
+
+ devc->cmd &= ~MADI_FreqMask;
+ devc->cmd |= rate_bits;
+
+ madi_control (devc, devc->cmd);
+
+ /*
+ * Compute and set the DDS value
+ */
+ v = 110100480000000ULL / (unsigned long long) devc->rate;
+ madi_write (devc, MADI_freq, (unsigned int) v);
+}
+
+static void
+initialize_hardware (madi_devc_t * devc)
+{
+ int chn, src;
+
+/*
+ * Intialize the control register
+ */
+ devc->cmd = MADI_ClockModeMaster | MADI_LatencyMask | MADI_LineOut;
+
+ if (devc->model == MDL_AES32)
+ devc->cmd |= MADI_SyncSrc0 | MADI_ProBit;
+ else
+ devc->cmd |=
+ MADI_InputCoax | MADI_SyncRef_MADI | MADI_TX_64ch_mode | MADI_AutoInput;
+
+ madi_control (devc, devc->cmd);
+
+/*
+ * Set all input and output engines to stoped
+ */
+ for (chn = 0; chn < MAX_CHANNELS; chn++)
+ {
+ set_output_enable (devc, chn, 0);
+ set_input_enable (devc, chn, 0);
+ }
+
+/*
+ * Init control2 register
+ */
+ if (devc->model == MDL_MADI)
+#ifdef OSS_BIG_ENDIAN
+ madi_control2 (devc, MADI_BIGENDIAN_MODE);
+#else
+ madi_control2 (devc, 0x0);
+#endif
+
+ madi_change_hw_rate (devc);
+
+/*
+ * Write all HW mixer registers
+ */
+
+ for (chn = 0; chn < MAX_CHANNELS; chn++)
+ for (src = 0; src < 2 * MAX_CHANNELS; src++)
+ madi_write_gain (devc, chn, src, devc->mixer_values[chn][src]);
+}
+
+static int
+madi_set_rate (int dev, int arg)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+
+ return devc->rate;
+}
+
+static __inline__ void
+reserve_rec_channels(madi_devc_t *devc, int c, int nc)
+{
+ int ch;
+
+ for (ch=c;ch < c+nc;ch++)
+ devc->busy_rec_channels |= (1ULL << ch);
+}
+
+static __inline__ void
+free_rec_channels(madi_devc_t *devc, int c, int nc)
+{
+ int ch;
+
+ for (ch=c;ch < c+nc;ch++)
+ devc->busy_rec_channels &= ~(1ULL << ch);
+}
+
+static __inline__ int
+rec_channels_avail(madi_devc_t *devc, int c, int nc)
+{
+ int ch;
+
+ for (ch=c;ch < c+nc;ch++)
+ if (devc->busy_rec_channels & (1ULL << ch))
+ return 0;
+
+ return 1;
+}
+
+static __inline__ void
+reserve_play_channels(madi_devc_t *devc, int c, int nc)
+{
+ int ch;
+
+ for (ch=c;ch < c+nc;ch++)
+ devc->busy_play_channels |= (1ULL << ch);
+}
+
+static __inline__ void
+free_play_channels(madi_devc_t *devc, int c, int nc)
+{
+ int ch;
+
+ for (ch=c;ch < c+nc;ch++)
+ devc->busy_play_channels &= ~(1ULL << ch);
+}
+
+static __inline__ int
+play_channels_avail(madi_devc_t *devc, int c, int nc)
+{
+ int ch;
+
+ for (ch=c;ch < c+nc;ch++)
+ if (devc->busy_play_channels & (1ULL << ch))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static short
+madi_set_channels (int dev, short arg)
+{
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+ int ok=1;
+
+ if (arg < 1)
+ return portc->channels;
+
+ if (arg != 1 && arg != 2 && arg != 4 && arg != 8 && arg != 16 && arg != 32
+ && arg != 64)
+ return portc->channels;
+
+ if (arg > portc->max_channels)
+ return portc->channels;
+
+ if (arg == portc->channels)
+ return portc->channels;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (arg > portc->channels) /* Needs more channels */
+ {
+ if (portc->direction == DIR_OUT)
+ {
+ if (!play_channels_avail(devc, portc->channel+portc->channels, arg - portc->channels))
+ ok=0;
+ }
+ else
+ {
+ if (!rec_channels_avail(devc, portc->channel+portc->channels, arg - portc->channels))
+ ok=0;
+ }
+ }
+ else
+ {
+ if (portc->direction == DIR_OUT)
+ {
+ free_play_channels(devc, portc->channel+arg, portc->channels-arg);
+ }
+ else
+ {
+ free_rec_channels(devc, portc->channel+arg, portc->channels-arg);
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ if (!ok)
+ return portc->channels;
+
+ return portc->channels = arg;
+}
+
+ /*ARGSUSED*/ static unsigned int
+madi_set_format (int dev, unsigned int arg)
+{
+ return AFMT_S32_LE;
+}
+
+static int
+madi_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void
+madi_trigger (int dev, int state)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ int ch;
+
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if ((state & PCM_ENABLE_OUTPUT)
+ && !(portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ for (ch = 0; ch < portc->channels; ch++)
+ set_output_enable (devc, portc->channel + ch, 1);
+ portc->trigger_bits |= PCM_ENABLE_OUTPUT;
+ devc->active_outputs |= (1ULL << portc->channel);
+ }
+ else if (portc->trigger_bits & PCM_ENABLE_OUTPUT)
+ {
+ for (ch = 0; ch < portc->channels; ch++)
+ set_output_enable (devc, portc->channel + ch, 0);
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ devc->active_outputs &= ~(1ULL << portc->channel);
+ }
+ }
+
+ if ((portc->open_mode & OPEN_READ)
+ && !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ for (ch = 0; ch < portc->channels; ch++)
+ set_input_enable (devc, portc->channel + ch, 1);
+ portc->trigger_bits |= PCM_ENABLE_INPUT;
+ devc->active_inputs |= (1ULL << portc->channel);
+ }
+ else if (portc->trigger_bits & PCM_ENABLE_INPUT)
+ {
+ for (ch = 0; ch < portc->channels; ch++)
+ set_input_enable (devc, portc->channel + ch, 0);
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ devc->active_inputs &= ~(1ULL << portc->channel);
+ }
+ }
+
+ if (devc->active_inputs || devc->active_outputs)
+ start_audio (devc);
+ else
+ stop_audio (devc);
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+static int
+madi_sync_control (int dev, int event, int mode)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+ int ch;
+
+ if (event == SYNC_PREPARE)
+ {
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if ((mode & PCM_ENABLE_OUTPUT) && portc->direction == DIR_OUT)
+ {
+ for (ch = 0; ch < portc->channels; ch++)
+ set_output_enable (devc, portc->channel + ch, 1);
+ portc->trigger_bits |= PCM_ENABLE_OUTPUT;
+ devc->active_outputs |= (1ULL << portc->channel);
+ }
+
+ if ((mode & PCM_ENABLE_INPUT) && portc->direction == DIR_IN)
+ {
+ for (ch = 0; ch < portc->channels; ch++)
+ set_input_enable (devc, portc->channel + ch, 1);
+ portc->trigger_bits |= PCM_ENABLE_INPUT;
+ devc->active_inputs |= (1ULL << portc->channel);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+ }
+
+ if (event == SYNC_TRIGGER)
+ {
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (devc->active_inputs || devc->active_outputs)
+ start_audio (devc);
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+ }
+
+ return OSS_EIO;
+}
+
+static void
+madi_halt (int dev)
+{
+ madi_trigger (dev, 0);
+}
+
+ /*ARGSUSED*/ static int
+madi_open (int dev, int mode, int open_flags)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+ int ok=1;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->open_mode != 0)
+ ok=0;
+ else
+ if (portc->direction == DIR_OUT)
+ {
+ if (!play_channels_avail(devc, portc->channel, 2))
+ {
+ ok=0;
+ }
+ }
+ else
+ {
+ if (!rec_channels_avail(devc, portc->channel, 2))
+ {
+ ok=0;
+ }
+ }
+
+ if (!ok)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+ devc->open_audiodevs++;
+
+ portc->channels = 2;
+ if (portc->direction == DIR_OUT)
+ reserve_play_channels(devc, portc->channel, 2);
+ else
+ reserve_rec_channels(devc, portc->channel, 2);
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+ /*ARGSUSED*/ static void
+madi_close (int dev, int mode)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->open_mode = 0;
+ devc->open_audiodevs--;
+ if (portc->direction == DIR_OUT)
+ {
+ free_play_channels(devc, portc->channel, portc->channels);
+ }
+ else
+ {
+ free_rec_channels(devc, portc->channel, portc->channels);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+ /*ARGSUSED*/ static void
+madi_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+}
+
+ /*ARGSUSED*/ static void
+madi_start_input (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+}
+
+ /*ARGSUSED*/ static int
+madi_prepare_for_input (int dev, int bsize, int bcount)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ int i;
+ int offs = portc->channel * CHBUF_PAGES;
+
+ for (i = 0; i < CHBUF_PAGES * portc->channels; i++)
+ madi_write (devc, MADI_RecPageTable + 4 * (offs + i),
+ LSWAP(dmap->dmabuf_phys + i * 4096));
+
+ return 0;
+}
+
+ /*ARGSUSED*/ static int
+madi_prepare_for_output (int dev, int bsize, int bcount)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+ int i;
+ int offs = portc->channel * CHBUF_PAGES;
+
+ for (i = 0; i < CHBUF_PAGES * portc->channels; i++)
+ madi_write (devc, MADI_PlayPageTable + 4 * (offs + i),
+ LSWAP(dmap->dmabuf_phys + i * 4096));
+ return 0;
+}
+
+ /*ARGSUSED*/ static int
+madi_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+
+ if (direction == OPEN_READ)
+ {
+ dmap->dmabuf = devc->recbuf + portc->channel * CHBUF_SIZE;
+ dmap->dmabuf_phys = devc->recbuf_phys + portc->channel * CHBUF_SIZE;
+ dmap->buffsize = CHBUF_SIZE;
+ }
+ else
+ {
+ dmap->dmabuf = devc->playbuf + portc->channel * CHBUF_SIZE;
+ dmap->dmabuf_phys = devc->playbuf_phys + portc->channel * CHBUF_SIZE;
+ dmap->buffsize = CHBUF_SIZE;
+ }
+
+ return 0;
+}
+
+ /*ARGSUSED*/ static int
+madi_free_buffer (int dev, dmap_t * dmap, int direction)
+{
+ dmap->dmabuf = NULL;
+ dmap->dmabuf_phys = 0;
+ dmap->buffsize = 0;
+
+ return 0;
+}
+
+ /*ARGSUSED*/ static int
+madi_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ unsigned int status;
+#if 1
+ /*
+ * Use only full/half buffer resolution.
+ */
+ int bytes;
+ bytes = ((devc->cmd & MADI_LatencyMask) >> 1) + 8;
+ bytes = 1 << bytes;
+ bytes *= portc->channels;
+
+ status = madi_read (devc, MADI_status);
+ return (status & MADI_BufferHalf) ? bytes : 0;
+#else
+ /*
+ * Use full resolution
+ */
+ status = madi_read (devc, MADI_status) & MADI_BufferPosMask;
+
+ return status * portc->channels;
+#endif
+}
+
+/*ARGSUSED*/
+static void
+madi_setup_fragments (int dev, dmap_t * dmap, int direction)
+{
+ madi_devc_t *devc = audio_engines[dev]->devc;
+ madi_portc_t *portc = audio_engines[dev]->portc;
+ int bytes;
+
+ bytes = ((devc->cmd & MADI_LatencyMask) >> 1) + 8;
+ bytes = 1 << bytes;
+
+ dmap->buffsize = portc->channels * CHBUF_SIZE;
+
+#if 1
+ /*
+ * Use two fragment (ping-pong) buffer that is also used by the hardware.
+ */
+ dmap->fragment_size = bytes * portc->channels;
+ dmap->nfrags = 2;
+#else
+/*
+ * Use 4 fragments instead of 2 to avoid clicking caused by accessing the
+ * DMA buffer too close to the active DMA position.
+ *
+ * This means that the DMA pointer will jump by 2 fregments every time there
+ * is a half/full bufferinterrupt.
+ */
+ dmap->fragment_size = (bytes * portc->channels) / 2;
+ dmap->nfrags = 4;
+#endif
+
+ dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;
+}
+
+static audiodrv_t madi_driver = {
+ madi_open,
+ madi_close,
+ madi_output_block,
+ madi_start_input,
+ madi_ioctl,
+ madi_prepare_for_input,
+ madi_prepare_for_output,
+ madi_halt,
+ NULL, // madi_local_qlen,
+ NULL,
+ NULL, // madi_halt_input,
+ NULL, // madi_halt_output,
+ madi_trigger,
+ madi_set_rate,
+ madi_set_format,
+ madi_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ madi_alloc_buffer,
+ madi_free_buffer,
+ NULL,
+ NULL,
+ madi_get_buffer_pointer,
+ NULL,
+ madi_sync_control,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ madi_setup_fragments
+};
+
+static int
+create_output_device (madi_devc_t * devc, char *name, int chn)
+{
+ madi_portc_t *portc;
+ adev_t *adev;
+ int n;
+
+ if ((portc = PMALLOC (devc->osdev, sizeof (*portc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate portc structure\n");
+ return OSS_ENOMEM;
+ }
+ memset (portc, 0, sizeof (*portc));
+
+ n = devc->num_outputs++;;
+ portc->channel = chn;
+ portc->channels = 2;
+ portc->max_channels = 64 - chn;
+
+ devc->out_portc[n] = portc;
+
+ if ((portc->audio_dev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ name,
+ &madi_driver,
+ sizeof (audiodrv_t),
+ ADEV_NOINPUT |
+ ADEV_NONINTERLEAVED |
+ ADEV_NOMMAP, AFMT_S32_LE,
+ devc, -1)) < 0)
+ {
+ return portc->audio_dev;
+ }
+
+ adev = audio_engines[portc->audio_dev];
+
+ if (devc->first_audiodev == -1)
+ devc->first_audiodev = portc->audio_dev;
+ adev->rate_source = devc->first_audiodev;
+
+ adev->mixer_dev = devc->mixer_dev;
+ adev->portc = portc;
+ adev->min_rate = devc->rate;
+ adev->max_rate = devc->rate;
+ adev->min_channels = 1;
+ adev->max_channels = portc->max_channels;
+ adev->min_block = CHBUF_SIZE / 2;
+ adev->max_block = CHBUF_SIZE / 2;
+
+ portc->direction = DIR_OUT;
+
+ return portc->audio_dev;
+}
+
+static int
+create_input_device (madi_devc_t * devc, char *name, int chn)
+{
+ madi_portc_t *portc;
+ adev_t *adev;
+ int n;
+
+ if ((portc = PMALLOC (devc->osdev, sizeof (*portc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate portc structure\n");
+ return OSS_ENOMEM;
+ }
+ memset (portc, 0, sizeof (*portc));
+
+ n = devc->num_inputs++;;
+ portc->channel = chn;
+ portc->channels = 2;
+ portc->max_channels = 64 - chn;
+
+ devc->in_portc[n] = portc;
+
+ if ((portc->audio_dev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ name,
+ &madi_driver,
+ sizeof (audiodrv_t),
+ ADEV_NOOUTPUT |
+ ADEV_NONINTERLEAVED |
+ ADEV_NOMMAP, AFMT_S32_LE,
+ devc, -1)) < 0)
+ {
+ return portc->audio_dev;
+ }
+
+ adev = audio_engines[portc->audio_dev];
+
+ if (devc->first_audiodev == -1)
+ devc->first_audiodev = portc->audio_dev;
+ adev->rate_source = devc->first_audiodev;
+
+ adev->mixer_dev = devc->mixer_dev;
+ adev->portc = portc;
+ adev->min_rate = devc->rate;
+ adev->max_rate = devc->rate;
+ adev->min_channels = 1;
+ adev->max_channels = portc->max_channels;
+ adev->min_block = CHBUF_SIZE / 2;
+ adev->max_block = CHBUF_SIZE / 2;
+
+ portc->direction = DIR_IN;
+
+ return portc->audio_dev;
+}
+
+int
+oss_madi_attach (oss_device_t * osdev)
+{
+ madi_devc_t *devc;
+ unsigned char pci_irq_line, pci_revision /*, pci_latency */ ;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ int i;
+ int err;
+ int my_mixer;
+
+ int chn, src;
+
+ DDB (cmn_err (CE_NOTE, "Entered RME MADI detect routine\n"));
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+ if (vendor != RME_VENDOR_ID || device != RME_MADI)
+ return 0;
+
+ pci_read_config_word (osdev, PCI_COMMAND, &pci_command);
+ pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision);
+ pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line);
+ pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &pci_ioaddr);
+
+ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+ if (pci_ioaddr == 0)
+ {
+ cmn_err (CE_WARN, "BAR0 not initialized by BIOS\n");
+ return 0;
+ }
+
+ if ((devc = PMALLOC (osp, sizeof (*devc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate devc\n");
+ return 0;
+ }
+ memset (devc, 0, sizeof (*devc));
+ devc->osdev = osdev;
+ osdev->devc = devc;
+ devc->first_audiodev = -1;
+
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+
+ DDB (cmn_err (CE_CONT, "RME HDSPM revision %d\n", pci_revision));
+
+ if (pci_revision < 230)
+ {
+ devc->name = "RME MADI";
+ devc->model = MDL_MADI;
+ }
+ else
+ {
+ devc->name = "RME AES32";
+ devc->model = MDL_AES32;
+ }
+
+ DDB (cmn_err (CE_CONT, "Card model is %s\n", devc->name));
+
+ devc->rate = devc->next_rate = 48000; // TODO: Also set the other rate control fields */
+
+ osdev->hw_info = PMALLOC (osdev, HWINFO_SIZE); /* Text buffer for additional device info */
+ sprintf (osdev->hw_info, "PCI revision %d", pci_revision);
+
+ oss_register_device (osdev, devc->name);
+
+ devc->physaddr = pci_ioaddr;
+ devc->registers = MAP_PCI_MEM (devc->osdev, 0, devc->physaddr, 65536);
+
+ if (devc->registers == NULL)
+ {
+ cmn_err (CE_WARN, "Can't map PCI registers (0x%08x)\n", devc->physaddr);
+ return 0;
+ }
+
+ devc->playbuf = CONTIG_MALLOC (devc->osdev, DMABUF_SIZE, MEMLIMIT_32BITS,
+ &devc->playbuf_phys, devc->play_dma_handle);
+ if (devc->playbuf == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate play DMA buffers\n");
+ return 0;
+ }
+
+ devc->recbuf = CONTIG_MALLOC (devc->osdev, DMABUF_SIZE, MEMLIMIT_32BITS,
+ &devc->recbuf_phys, devc->rec_dma_handle);
+ if (devc->recbuf == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate rec DMA buffers\n");
+ return 0;
+ }
+
+ if ((err = oss_register_interrupts (devc->osdev, 0, madiintr, NULL)) < 0)
+ {
+ cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err);
+ return 0;
+ }
+
+ if (madi_maxchannels < 2) madi_maxchannels=2;
+ if (madi_maxchannels > MAX_CHANNELS)madi_maxchannels=MAX_CHANNELS;
+
+ if (madi_devsize < 1) madi_devsize = 1;
+
+ if (madi_devsize != 1 && madi_devsize != 2 && madi_devsize != 4)
+ madi_devsize = 2;
+
+ /*
+ * Set the hw mixer shadow values to sane levels so that
+ * initialize_hardware() can write them to the actual registers.
+ */
+
+ for (chn = 0; chn < MAX_CHANNELS; chn++)
+ for (src = 0; src < 2 * MAX_CHANNELS; src++)
+ devc->mixer_values[chn][src] = MUTE_GAIN; /* Set everything off */
+
+#if 0
+ for (src = 0; src < MAX_CHANNELS; src += 2)
+ {
+ /* Setup playback monitoring to analog out */
+ devc->mixer_values[MONITOR_CH][SRC_PLAY | src] = UNITY_GAIN / 10;
+ devc->mixer_values[MONITOR_CH + 1][SRC_PLAY | (src + 1)] =
+ UNITY_GAIN / 10;
+ }
+#endif
+
+ for (chn = 0; chn < MAX_CHANNELS; chn++)
+ devc->mixer_values[chn][SRC_PLAY | chn] = UNITY_GAIN; /* N->N outputs on */
+
+ initialize_hardware (devc);
+
+ if ((my_mixer = madi_install_mixer (devc)) < 0)
+ {
+ devc->mixer_dev = -1;
+ return 0;
+ }
+ else
+ {
+ devc->mixer_dev = my_mixer;
+ }
+
+ if (madi_devsize == 1)
+ {
+ for (i = 0; i < madi_maxchannels; i++)
+ {
+ char tmp[32];
+ sprintf (tmp, "%s out%d", devc->name, i+1);
+ create_output_device (devc, tmp, i);
+ }
+
+ for (i = 0; i < madi_maxchannels; i++)
+ {
+ char tmp[32];
+ sprintf (tmp, "%s in%d", devc->name, i+1);
+ create_input_device (devc, tmp, i);
+ }
+ }
+ else
+ {
+ for (i = 0; i < madi_maxchannels; i += madi_devsize)
+ {
+ char tmp[32];
+ sprintf (tmp, "%s out%d-%d", devc->name, i+1, i + madi_devsize);
+ create_output_device (devc, tmp, i);
+ }
+
+ for (i = 0; i < madi_maxchannels; i += madi_devsize)
+ {
+ char tmp[32];
+ sprintf (tmp, "%s in%d-%d", devc->name, i+1, i + madi_devsize);
+ create_input_device (devc, tmp, i);
+ }
+ }
+
+ madi_activate_mixer (devc);
+
+ return 1;
+}
+
+int
+oss_madi_detach (oss_device_t * osdev)
+{
+ madi_devc_t *devc = (madi_devc_t *) osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ stop_audio (devc);
+
+ /*
+ * Shut up the hardware
+ */
+ devc->cmd &= ~MADI_FreqMask;
+ madi_control (devc, devc->cmd);
+
+ oss_unregister_interrupts (devc->osdev);
+
+ MUTEX_CLEANUP (devc->mutex);
+
+ if (devc->registers != NULL)
+ UNMAP_PCI_MEM (osdev, 0, devc->physaddr, devc->registers, 65536);
+ CONTIG_FREE (devc->osdev, devc->playbuf, DMABUF_SIZE,
+ devc->play_dma_handle);
+ CONTIG_FREE (devc->osdev, devc->recbuf, DMABUF_SIZE, devc->rec_dma_handle);
+
+ oss_unregister_device (devc->osdev);
+
+ return 1;
+}
diff --git a/kernel/drv/oss_madi/oss_madi.man b/kernel/drv/oss_madi/oss_madi.man
new file mode 100644
index 0000000..70239fc
--- /dev/null
+++ b/kernel/drv/oss_madi/oss_madi.man
@@ -0,0 +1,70 @@
+NAME
+oss_madi - RME HDSP MADI and AES32 audio driver
+
+DESCRIPTION
+Open Sound System driver for RME HDSP MADI and AES32 audio interfaces.
+
+This driver has been developed for RME HDSP MADI but it also supports
+HDSP AES32. The driver has been tested under Linux and Solaris under x86
+and Sparc.
+
+The internal engine of the card supports 64 playback channels and 64 recording
+channels. Only the first channels are connected to the actual input and
+output ports of the card.
+
+The mixer section of the card has recording level settings for all inputs.
+There is also an output mixer. Each output channel has mixer sliders for
+corresponding input channel and corresponding audio playback channel.
+
+There is a full 64x(64+64) mixing matrix supported by the hardware. For
+the time being this mixer matrix cannot be accessed directly by the
+applications. However support for the SNDCTL_MIX_MATRIX_WRITE and
+SNDCTL_MIX_MATRIX_READ ioctl calls can be added in the future.
+
+DEVICE FILES
+
+By default the driver will create input and output device files for
+each 32 stereo pairs. This can be changed by editing the settings in
+oss_madi.conf (see below).
+
+The application can set the devices to use 1, 2, 4, 8, 16, 32 or 64 channels.
+The device file being used selects the first channel slot within the available
+channels. For example pcm0 and pcmin0 sill select channel 0. Equally well
+pcm1 and pcmin1 will select channel slot 2 (assuming that madi_devsize option
+is set to 2). The first device (pcm0 and pcmin0) can be set to use up to 64
+channels. The last devices (pcm31 and pcmin31) only support 1 or 2 channel mode.
+The other device files support channel configurations where the last channel
+doesn't exceed the number of total channels (madi_maxchannels). Also the driver
+will not let two device files to share any of the channels with some other
+open device file.
+
+This channel allocation mechanism gives maximum flexibility to the user. It is
+possible to use some output channels by multi channel application while the
+others are available for other applications. This works as long the channel
+allocations by different applications don't overlap.
+
+OPTIONS
+
+o madi_maxchannels Number of channels supported by the driver. The default
+ is 64 which is also the maximum. This parameter can
+ be set to a lower value if full 64 channels are not
+ required. With less channels the mixer/control panel
+ interface will require less space on screen.
+o madi_devsize By default this parameter is set to 2 which means that
+ a device file will be created for each stereo channel
+ pair. Possible values are 1, 2, 4, 8, 16, 32 or 64.
+
+LIMITATIONS
+
+o The current driver doesn't support all control panel features of the card.
+For example sampling rate is fixed to 48 kHz. More features will be added
+on contract.
+
+o Use of mmap() is and will not be supported.
+o Virtual mixer is not supported (yet).
+
+FILES
+CONFIGFILEPATH/oss_madi.conf Device configuration file.
+
+AUTHOR
+4Front Technologies