diff options
Diffstat (limited to 'kernel/drv/oss_madi')
-rw-r--r-- | kernel/drv/oss_madi/.devices | 1 | ||||
-rw-r--r-- | kernel/drv/oss_madi/.name | 1 | ||||
-rw-r--r-- | kernel/drv/oss_madi/.params | 12 | ||||
-rw-r--r-- | kernel/drv/oss_madi/madi.h | 246 | ||||
-rw-r--r-- | kernel/drv/oss_madi/madi_mixer.c | 459 | ||||
-rw-r--r-- | kernel/drv/oss_madi/oss_madi.c | 975 | ||||
-rw-r--r-- | kernel/drv/oss_madi/oss_madi.man | 70 |
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 |