summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_digi96
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_digi96')
-rw-r--r--kernel/drv/oss_digi96/.devices4
-rw-r--r--kernel/drv/oss_digi96/.name1
-rw-r--r--kernel/drv/oss_digi96/oss_digi96.c1097
-rw-r--r--kernel/drv/oss_digi96/oss_digi96.man63
4 files changed, 1165 insertions, 0 deletions
diff --git a/kernel/drv/oss_digi96/.devices b/kernel/drv/oss_digi96/.devices
new file mode 100644
index 0000000..84196cc
--- /dev/null
+++ b/kernel/drv/oss_digi96/.devices
@@ -0,0 +1,4 @@
+oss_digi96 pci10ee,3fc0 RME Digi96
+oss_digi96 pci10ee,3fc1 RME Digi96/8
+oss_digi96 pci10ee,3fc2 RME Digi96/8 PRO
+oss_digi96 pci10ee,3fc3 RME Digi96/8 PAD
diff --git a/kernel/drv/oss_digi96/.name b/kernel/drv/oss_digi96/.name
new file mode 100644
index 0000000..8b0b06c
--- /dev/null
+++ b/kernel/drv/oss_digi96/.name
@@ -0,0 +1 @@
+RME Digi96
diff --git a/kernel/drv/oss_digi96/oss_digi96.c b/kernel/drv/oss_digi96/oss_digi96.c
new file mode 100644
index 0000000..6a93285
--- /dev/null
+++ b/kernel/drv/oss_digi96/oss_digi96.c
@@ -0,0 +1,1097 @@
+/*
+ * Purpose: Driver for RME Digi96 family
+ */
+/*
+ *
+ * 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_digi96_cfg.h"
+#include "oss_pci.h"
+
+#define RME_VENDOR_ID2 0x10ee
+#define RME_DIGI96 0x3fc0
+#define RME_DIGI96_8 0x3fc1
+#define RME_DIGI96_PRO 0x3fc2
+#define RME_DIGI96_PAD 0x3fc3
+
+#define MAX_AUDIO_CHANNEL 2
+
+/*
+ * Control register pCTRL1
+ */
+#define CTRL1_STARTPLAY 0x00000001
+#define CTRL1_STARTREC 0x00000002
+#define CTRL1_GAIN 0x0000000c
+#define CTRL1_MODE24_PLAY 0x00000010
+#define CTRL1_MODE24_REC 0x00000020
+#define CTRL1_PLAYBM 0x00000040
+#define CTRL1_RECBM 0x00000080
+#define CTRL1_ADAT 0x00000100
+#define CTRL1_FREQ 0x00000600
+#define CTRL1_FREQ32 0x00000200
+#define CTRL1_FREQ44 0x00000400
+#define CTRL1_FREQ48 0x00000600
+#define CTRL1_DS 0x00000800
+#define CTRL1_PRO 0x00001000
+#define CTRL1_EMP 0x00002000
+#define CTRL1_SEL 0x00004000
+#define CTRL1_MASTER 0x00008000
+#define CTRL1_PD 0x00010000
+#define CTRL1_INPUTSEL 0x00060000
+#define CTRL1_INPUTSHIFT 17
+#define CTRL1_THRU 0x07f80000
+#define CTRL1_AC3 0x08000000
+#define CTRL1_MONITOR 0x30000000
+#define CTRL1_ISEL 0x40000000
+#define CTRL1_IDIS 0x80000000
+
+/*
+ * Control register pCTRL2
+ */
+#define CTRL2_WSEL 0x00000001
+#define CTRL2_ANALOG 0x00000002 /* PAD only */
+#define CTRL2_FREQAD 0x0000001c /* PAD */
+#define CTRL2_PD2 0x00000020
+#
+/* Next ones are only for new Digi96/8Pro (blue board) and PAD */
+#define CTRL2_DAC_EN 0x00000040
+#define CTRL2_CLATCH 0x00000080
+#define CTRL2_CCLK 0x00000100
+#define CTRL2_CDATA 0x00000200
+/*
+ * For reads from the pPLAYPOS and pRECPOS registers
+ */
+
+#define POS_PLAYIRQ 0x80000000
+#define POS_AUTOSYNC 0x40000000
+#define POS_FBITS 0x38000000
+#define POS_FSHIFT 27
+#define POS_ERF 0x04000000
+#define POS_CONSTANT11 0x03000000
+#define POS_LOCK 0x00800000
+#define POS_DEVID 0x00600000
+#define POS_CONSTANT000 0x008c0000
+#define POS_TOUT 0x00020000
+#define POS_RECIRQ 0x00010000
+#define POS_ADDR 0x000fffff
+
+typedef struct digi96_portc
+{
+ int open_mode;
+ int audio_dev;
+ int channels, bits;
+ int speed, speedsel;
+ int trigger_bits;
+}
+digi96_portc;
+
+typedef struct digi96_devc
+{
+ oss_device_t *osdev;
+ oss_mutex_t mutex;
+
+ char *chip_name;
+ int have_adat;
+ int have_analog;
+ int mode;
+#define MD_SPDIF 0
+#define MD_AES 1
+#define MD_ADAT 2
+
+ oss_native_word physaddr;
+ char *linaddr;
+ unsigned int *pPLAYBUF;
+ unsigned int *pRECBUF;
+ unsigned int *pCTRL1, ctrl1_bits;
+ unsigned int *pCTRL2, ctrl2_bits;
+ unsigned int *pPLAYACK;
+ unsigned int *pRECACK;
+ unsigned int *pPLAYPOS;
+ unsigned int *pRECPOS;
+ unsigned int *pRESETPLAY;
+ unsigned int *pRESETREC;
+
+ int irq;
+
+ digi96_portc portc[MAX_AUDIO_CHANNEL];
+ int open_mode;
+#define TY_READ 0
+#define TY_WRITE 1
+#define TY_BOTH 2
+ int mixer_dev;
+
+ int master;
+ int external_locked;
+ int input_source;
+ int doublespeed;
+ int adatrate;
+}
+digi96_devc;
+
+static int fbit_tab[8] = {
+ 0, 0, 0, 96000, 88200, 48000, 44100, 32000
+};
+
+
+static void
+write_ctrl1 (digi96_devc * devc, unsigned int ctrl)
+{
+ devc->ctrl1_bits = ctrl;
+
+ PCI_WRITEL (devc->osdev, devc->pCTRL1, ctrl);
+}
+
+static void
+write_ctrl2 (digi96_devc * devc, unsigned int ctrl)
+{
+ devc->ctrl2_bits = ctrl;
+
+ PCI_WRITEL (devc->osdev, devc->pCTRL2, ctrl);
+}
+
+static int
+digi96intr (oss_device_t * osdev)
+{
+ int i, serviced = 0;
+ unsigned int playstat, recstat;
+ digi96_devc *devc = (digi96_devc *) osdev->devc;
+ digi96_portc *portc;
+
+ playstat = PCI_READL (devc->osdev, devc->pPLAYPOS);
+ recstat = PCI_READL (devc->osdev, devc->pRECPOS);
+
+ for (i = 0; i < 2; i++)
+ {
+ portc = &devc->portc[i];
+
+ if (playstat & POS_PLAYIRQ)
+ {
+ serviced = 1;
+ PCI_WRITEL (devc->osdev, devc->pPLAYACK, 0);
+ oss_audio_outputintr (portc->audio_dev, 0);
+ }
+
+ if (recstat & POS_RECIRQ)
+ {
+ serviced = 1;
+ PCI_WRITEL (devc->osdev, devc->pRECACK, 0);
+ oss_audio_inputintr (portc->audio_dev, 0);
+ }
+ }
+ return serviced;
+}
+
+
+/***********************************
+ * Audio routines
+ ***********************************/
+
+static int
+digi96_set_rate (int dev, int arg)
+{
+ digi96_devc *devc = audio_engines[dev]->devc;
+ digi96_portc *portc = audio_engines[dev]->portc;
+
+ static int speed_table[6] = { 32000, 44100, 48000, 64000, 88200, 96000 };
+ int i, best = 2, dif, bestdif = 0x7fffffff;
+
+ if (arg)
+ {
+ if (devc->external_locked || (portc->open_mode & OPEN_READ))
+ arg = portc->speed; /* Speed locked to input */
+
+ for (i = 0; i < 6; i++)
+ {
+ if (arg == speed_table[i]) /* Exact match */
+ {
+ portc->speed = arg;
+ portc->speedsel = i;
+ return portc->speed;
+ }
+
+ dif = arg - speed_table[i];
+ if (dif < 0)
+ dif *= -1;
+ if (dif <= bestdif)
+ {
+ best = i;
+ bestdif = dif;
+ }
+
+ }
+
+ portc->speed = speed_table[best];
+ portc->speedsel = best;
+ }
+
+ return portc->speed;
+}
+
+static short
+digi96_set_channels (int dev, short arg)
+{
+ digi96_portc *portc = audio_engines[dev]->portc;
+ digi96_devc *devc = audio_engines[dev]->devc;
+
+ if (arg == 0)
+ return portc->channels;
+
+ if (devc->mode == MD_ADAT)
+ arg = 8;
+ else
+ arg = 2;
+ return portc->channels = arg;
+}
+
+static unsigned int
+digi96_set_format (int dev, unsigned int arg)
+{
+ digi96_portc *portc = audio_engines[dev]->portc;
+
+ if (arg != AFMT_S16_LE && arg != AFMT_AC3 && arg != AFMT_S32_LE)
+ return portc->bits;
+
+ return portc->bits = arg;
+}
+
+/*ARGSUSED*/
+static int
+digi96_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void digi96_trigger (int dev, int state);
+
+static void
+digi96_reset (int dev)
+{
+ digi96_trigger (dev, 0);
+}
+
+static void
+digi96_reset_input (int dev)
+{
+ digi96_portc *portc = audio_engines[dev]->portc;
+ digi96_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+digi96_reset_output (int dev)
+{
+ digi96_portc *portc = audio_engines[dev]->portc;
+ digi96_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+static int
+verify_input (digi96_devc * devc, digi96_portc * portc)
+{
+ int i, status, savedstatus;
+ int tmp;
+ oss_native_word flags;
+
+ tmp = devc->ctrl1_bits & ~CTRL1_INPUTSEL;
+ tmp |= (devc->input_source & 0x3) << CTRL1_INPUTSHIFT;
+ write_ctrl1 (devc, tmp);
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ i = 0;
+ status = PCI_READL (devc->osdev, devc->pRECPOS);
+
+ if (status & POS_LOCK) /* ADAT input */
+ {
+ devc->mode = MD_ADAT;
+ portc->channels = 8;
+
+ switch (devc->adatrate)
+ {
+ case 0: /* Autodetect */
+ if (status & POS_AUTOSYNC)
+ portc->speed = 48000;
+ else
+ portc->speed = 44100;
+ DDB (cmn_err
+ (CE_WARN, "ADAT input detected, sr=%d Hz\n", portc->speed));
+ break;
+
+ case 1:
+ portc->speed = 44100;
+ break;
+
+ case 2:
+ portc->speed = 48000;
+ break;
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 1;
+ }
+
+ while (i++ < 100 && status & POS_ERF)
+ {
+ oss_udelay (10);
+ status = PCI_READL (devc->osdev, devc->pRECPOS);
+ }
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ savedstatus = status;
+
+ status &= POS_ERF;
+
+ if (status)
+ cmn_err (CE_WARN, "Cannot sync with the input signal\n");
+ else
+ {
+ int fbits = (savedstatus & POS_FBITS) >> POS_FSHIFT;
+ portc->speed = fbit_tab[fbits];
+ DDB (cmn_err
+ (CE_WARN, "digi96: Measured input sampling rate is %d\n",
+ portc->speed));
+ }
+ return devc->external_locked = !status;
+}
+
+/*ARGSUSED*/
+static int
+digi96_open (int dev, int mode, int open_flags)
+{
+ unsigned int tmp;
+ digi96_portc *portc = audio_engines[dev]->portc;
+ digi96_devc *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (portc->open_mode != 0 || (devc->open_mode & mode)) /* Busy? */
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+ devc->open_mode |= mode;
+ portc->audio_dev = dev;
+ portc->trigger_bits = 0;
+
+ tmp = devc->ctrl1_bits;
+ devc->external_locked = 0;
+ if (devc->master)
+ tmp |= CTRL1_MASTER;
+ else
+ {
+ tmp &= ~CTRL1_MASTER;
+ devc->external_locked = 1;
+ }
+ write_ctrl1 (devc, tmp);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ if (devc->external_locked || (portc->open_mode & OPEN_READ))
+ verify_input (devc, portc);
+
+ if (devc->mode == MD_ADAT)
+ {
+ audio_engines[dev]->min_block = 8 * 1024;
+ audio_engines[dev]->max_block = 8 * 1024;
+ audio_engines[dev]->caps &= ~DSP_CH_MASK;
+ audio_engines[dev]->caps |= DSP_CH_MULTI;
+ audio_engines[dev]->min_channels = 8;
+ audio_engines[dev]->max_channels = 8;
+ write_ctrl1 (devc, devc->ctrl1_bits & ~CTRL1_ISEL);
+ }
+ else
+ {
+ audio_engines[dev]->min_block = 2 * 1024;
+ audio_engines[dev]->max_block = 2 * 1024;
+ audio_engines[dev]->caps &= ~DSP_CH_MASK;
+ audio_engines[dev]->caps |= DSP_CH_STEREO;
+ audio_engines[dev]->min_channels = 2;
+ audio_engines[dev]->max_channels = 2;
+ write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_ISEL);
+ }
+
+ return 0;
+}
+
+static void
+digi96_close (int dev, int mode)
+{
+ digi96_devc *devc = audio_engines[dev]->devc;
+ digi96_portc *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+
+ digi96_reset (dev);
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->open_mode = 0;
+ devc->open_mode &= ~mode;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static void
+digi96_output_block (int dev, oss_native_word ptr, int count, int fragsize,
+ int intrflag)
+{
+}
+
+/*ARGSUSED*/
+static void
+digi96_start_input (int dev, oss_native_word ptr, int count, int fragsize,
+ int intrflag)
+{
+}
+
+static void
+digi96_trigger (int dev, int state)
+{
+ digi96_devc *devc = audio_engines[dev]->devc;
+ digi96_portc *portc = audio_engines[dev]->portc;
+ unsigned int tmp = devc->ctrl1_bits;
+
+ state &= portc->open_mode;
+
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if (state & PCM_ENABLE_OUTPUT)
+ tmp |= CTRL1_STARTPLAY;
+ else
+ tmp &= ~CTRL1_STARTPLAY;
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ tmp |= CTRL1_STARTREC;
+ else
+ tmp &= ~CTRL1_STARTREC;
+ }
+
+ portc->trigger_bits = state;
+ write_ctrl1 (devc, tmp);
+}
+
+/*ARGSUSED*/
+static int
+digi96_prepare_for_output (int dev, int bsize, int bcount)
+{
+ digi96_devc *devc = audio_engines[dev]->devc;
+ digi96_portc *portc = audio_engines[dev]->portc;
+
+ int cmd = devc->ctrl1_bits;
+ int doublespeed;
+
+ PCI_WRITEL (devc->osdev, devc->pRESETPLAY, 0);
+ if (devc->master)
+ cmd |= CTRL1_MASTER;
+ else
+ cmd &= ~CTRL1_MASTER;
+ if (portc->channels == 8)
+ cmd |= CTRL1_ADAT;
+ else
+ cmd &= ~CTRL1_ADAT;
+ write_ctrl1 (devc, cmd);
+
+ if (!devc->master)
+ {
+ if (!verify_input (devc, portc))
+ return OSS_EIO;
+ }
+
+ cmd = devc->ctrl1_bits;
+ doublespeed = (portc->speedsel > 3);
+
+ cmd &= ~(CTRL1_DS | CTRL1_FREQ);
+ if (doublespeed)
+ cmd |= CTRL1_DS;
+
+ cmd |= ((portc->speedsel % 3) + 1) << 9;
+ write_ctrl1 (devc, cmd);
+
+ if (doublespeed != devc->doublespeed)
+ {
+ devc->doublespeed = doublespeed;
+ write_ctrl1 (devc, cmd | CTRL1_PD); /* Reset the DAC */
+ }
+
+ if (portc->bits == AFMT_AC3)
+ write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_AC3 | CTRL1_PD);
+ else
+ {
+ if (portc->bits == AFMT_S32_LE)
+ {
+ write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_MODE24_PLAY);
+ }
+ else
+ write_ctrl1 (devc, devc->ctrl1_bits & ~(CTRL1_MODE24_PLAY));
+
+ write_ctrl1 (devc, devc->ctrl1_bits & ~(CTRL1_AC3 | CTRL1_PD)); /* Unmute DAC */
+ }
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+digi96_prepare_for_input (int dev, int bsize, int bcount)
+{
+ digi96_devc *devc = audio_engines[dev]->devc;
+ digi96_portc *portc = audio_engines[dev]->portc;
+
+ int cmd = devc->ctrl1_bits;
+
+ if (portc->channels == 8)
+ cmd |= CTRL1_ADAT;
+ else
+ cmd &= ~CTRL1_ADAT;
+
+ if (portc->bits == AFMT_S32_LE)
+ cmd |= CTRL1_MODE24_REC;
+ else
+ cmd &= ~CTRL1_MODE24_REC;
+ write_ctrl1 (devc, cmd);
+
+ if (!verify_input (devc, portc))
+ return OSS_EIO;
+ PCI_WRITEL (devc->osdev, devc->pRESETREC, 0);
+
+ return 0;
+}
+
+static int
+digi96_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+ digi96_devc *devc = audio_engines[dev]->devc;
+
+ if (direction == OPEN_WRITE)
+ {
+ dmap->dmabuf = (void *) devc->pPLAYBUF;
+ dmap->dmabuf_phys = (oss_native_word) devc->pPLAYBUF;
+ }
+ else
+ {
+ dmap->dmabuf = (void *) devc->pRECBUF;
+ dmap->dmabuf_phys = (oss_native_word) devc->pRECBUF;
+ }
+ dmap->buffsize = 64 * 1024;
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+digi96_free_buffer (int dev, dmap_t * dmap, int direction)
+{
+ dmap->dmabuf = NULL;
+ dmap->dmabuf_phys = 0;
+ return 0;
+}
+
+/*ARGSUSED*/
+static void
+digi96_setup_fragments (int dev, dmap_p dmap, int direction)
+{
+ /* Make sure the whole sample RAM is covered by the buffer */
+ dmap->nfrags = dmap->buffsize / dmap->fragment_size;
+}
+
+static audiodrv_t digi96_driver = {
+ digi96_open,
+ digi96_close,
+ digi96_output_block,
+ digi96_start_input,
+ digi96_ioctl,
+ digi96_prepare_for_input,
+ digi96_prepare_for_output,
+ digi96_reset,
+ NULL,
+ NULL,
+ digi96_reset_input,
+ digi96_reset_output,
+ digi96_trigger,
+ digi96_set_rate,
+ digi96_set_format,
+ digi96_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ digi96_alloc_buffer,
+ digi96_free_buffer,
+ NULL,
+ NULL,
+ NULL, /* digi96_get_buffer_pointer */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ digi96_setup_fragments
+};
+
+/*ARGSUSED*/
+static int
+digi96_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
+{
+ if (cmd == SOUND_MIXER_PRIVATE1) /* Bogus testing */
+ {
+ int val;
+
+ val = *arg;
+ return *arg = val;
+ }
+ 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);
+ return OSS_EINVAL;
+}
+
+static mixer_driver_t digi96_mixer_driver = {
+ digi96_mixer_ioctl
+};
+
+static int
+digi96_set_control (int dev, int ctrl, unsigned int cmd, int value)
+{
+ digi96_devc *devc = mixer_devs[dev]->devc;
+ unsigned int tmp;
+
+ if (ctrl < 0)
+ return OSS_EINVAL;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ switch (ctrl)
+ {
+ case 1:
+ return !!devc->master;
+ break;
+
+ case 2:
+ return devc->input_source;
+ break;
+
+ case 3:
+ return !!(devc->ctrl1_bits & CTRL1_SEL);
+ break;
+
+ case 4:
+ return devc->mode;
+ break;
+
+ case 5:
+ return !!(devc->ctrl2_bits & CTRL2_WSEL);
+ break;
+
+ case 6:
+ return !!(devc->ctrl1_bits & CTRL1_EMP);
+ break;
+
+ case 7:
+ return !!(devc->ctrl1_bits & CTRL1_AC3);
+ break;
+
+ case 8:
+ return devc->adatrate;
+ break;
+ }
+
+ return OSS_EINVAL;
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ switch (ctrl)
+ {
+ case 1:
+ devc->master = !!value;
+ return !!value;
+ break;
+
+ case 2:
+ if (value < 0 || value > 3)
+ return OSS_EINVAL;
+
+ devc->input_source = value;
+ return value;
+ break;
+
+ case 3:
+ tmp = devc->ctrl1_bits;
+ if (value)
+ tmp |= CTRL1_SEL;
+ else
+ {
+ tmp &= ~(CTRL1_SEL | CTRL1_MASTER);
+ devc->master = 0;
+ }
+ write_ctrl1 (devc, tmp);
+ return value;
+ break;
+
+ case 4:
+ if (value > 2 || (!devc->have_adat && value > 1))
+ return OSS_EINVAL;
+
+ devc->mode = value;
+ return value;
+ break;
+
+ case 5:
+ if (value)
+ {
+ write_ctrl2 (devc, devc->ctrl2_bits | CTRL2_WSEL);
+ return 1;
+ }
+
+ write_ctrl2 (devc, devc->ctrl2_bits & ~CTRL2_WSEL);
+ return 0;
+ break;
+
+ case 6:
+ if (value)
+ {
+ write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_EMP);
+ return 1;
+ }
+
+ write_ctrl1 (devc, devc->ctrl1_bits & ~CTRL1_EMP);
+ return 0;
+ break;
+
+ case 7:
+ if (value)
+ {
+ write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_AC3);
+ return 1;
+ }
+
+ write_ctrl1 (devc, devc->ctrl1_bits & ~CTRL1_AC3);
+ return 0;
+ break;
+
+ case 8:
+ if (value > 2)
+ value = 2;
+ if (value < 0)
+ value = 0;
+
+ return devc->adatrate = value;
+ break;
+
+ }
+
+ return OSS_EINVAL;
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+digi96_mix_init (int dev)
+{
+ /* digi96_devc *devc = mixer_devs[dev]->devc; */
+ int group, err;
+
+ if ((group = mixer_ext_create_group (dev, 0, "DIGI96")) < 0)
+ return group;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 4, digi96_set_control,
+ MIXT_ENUM,
+ "DIGI96_MODE", 3,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 1, digi96_set_control,
+ MIXT_ENUM,
+ "DIGI96_SYNC", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 2, digi96_set_control,
+ MIXT_ENUM,
+ "DIGI96_INPUT", 4,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 3, digi96_set_control,
+ MIXT_ENUM,
+ "DIGI96_SEL", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 5, digi96_set_control,
+ MIXT_ONOFF,
+ "DIGI96_WORLDCLK", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 6, digi96_set_control,
+ MIXT_ONOFF,
+ "DIGI96_EMPH", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+#if 0
+ if ((err = mixer_ext_create_control (dev, group,
+ 7, digi96_set_control,
+ MIXT_ENUM,
+ "DIGI96_DATA", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+#endif
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 8, digi96_set_control,
+ MIXT_ENUM,
+ "DIGI96_ADATRATE", 3,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ mixer_ext_set_strings (dev, err, "AUTO 44100 48000", 0);
+
+ return 0;
+}
+
+static int
+attach_channel (digi96_devc * devc, int chnum, char *name,
+ audiodrv_t * drv, int type)
+{
+ int adev;
+ digi96_portc *portc;
+ int opts = ADEV_DUPLEX | ADEV_COLD | ADEV_AUTOMODE | ADEV_NOVIRTUAL;
+
+ if (chnum < 0 || chnum >= MAX_AUDIO_CHANNEL)
+ return 0;
+
+ if (type == TY_BOTH)
+ opts = ADEV_SHADOW;
+
+ portc = &devc->portc[chnum];
+
+ devc->master = 1;
+ devc->input_source = 0;
+ devc->doublespeed = 0;
+ devc->external_locked = 0;
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ name,
+ drv,
+ sizeof (audiodrv_t),
+ opts,
+ AFMT_AC3 | AFMT_S16_LE | AFMT_S32_LE,
+ devc, -1)) < 0)
+ {
+ return 0;
+ }
+
+ audio_engines[adev]->mixer_dev = devc->mixer_dev;
+ audio_engines[adev]->portc = portc;
+ audio_engines[adev]->caps |= DSP_CH_STEREO;
+ audio_engines[adev]->min_block = 2 * 1024;
+ audio_engines[adev]->max_block = 2 * 1024;
+ audio_engines[adev]->min_rate = 32000;
+ audio_engines[adev]->max_rate = 96000;
+ portc->open_mode = 0;
+ devc->open_mode = 0;
+ portc->audio_dev = adev;
+
+ portc->speed = 48000;
+ portc->speedsel = 3;
+ portc->bits = AFMT_S16_LE;
+ portc->channels = 2;
+ return 1;
+}
+
+int
+init_digi96 (digi96_devc * devc)
+{
+ int my_mixer;
+ int ret;
+
+ if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "RME Digi96 Control panel",
+ &digi96_mixer_driver,
+ sizeof (mixer_driver_t), devc)) < 0)
+ {
+
+ devc->mixer_dev = -1;
+ return 0;
+ }
+ else
+
+ {
+ mixer_devs[my_mixer]->priority = -1; /* Don't use as the default mixer */
+ devc->mixer_dev = my_mixer;
+ mixer_ext_set_init_fn (my_mixer, digi96_mix_init, 20);
+ }
+
+ ret = attach_channel (devc, 0, devc->chip_name, &digi96_driver, 0);
+ if (ret > 0)
+ {
+ ret =
+ attach_channel (devc, 1, devc->chip_name, &digi96_driver, TY_BOTH);
+ }
+
+/*
+ * Set some defaults
+ */
+ write_ctrl2 (devc, 0);
+ write_ctrl1 (devc, CTRL1_ISEL | CTRL1_FREQ48 | CTRL1_PD); /* Reset DAC */
+ write_ctrl1 (devc, CTRL1_ISEL | CTRL1_FREQ48 | CTRL1_MASTER | CTRL1_SEL);
+ write_ctrl2 (devc, CTRL2_DAC_EN); /* Soft unmute the DAC (blue PRO boards) */
+ return ret;
+}
+
+
+int
+oss_digi96_attach (oss_device_t * osdev)
+{
+ digi96_devc *devc;
+ unsigned char pci_irq_line, pci_revision /*, pci_latency */ ;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ int err;
+
+ DDB (cmn_err (CE_WARN, "Entered Digi96 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_ID2 ||
+ (device != RME_DIGI96_PRO &&
+ device != RME_DIGI96_PAD &&
+ device != RME_DIGI96 && device != RME_DIGI96_8))
+
+ 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 (osdev, sizeof (*devc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ return 0;
+ }
+
+ devc->osdev = osdev;
+ osdev->devc = devc;
+ devc->physaddr = pci_ioaddr;
+
+ devc->have_adat = 0;
+ devc->have_analog = 0;
+ devc->mode = MD_SPDIF;
+
+ switch (device)
+ {
+ case RME_DIGI96:
+ devc->chip_name = "RME Digi96";
+ break;
+
+ case RME_DIGI96_8:
+ devc->chip_name = "RME Digi96/8";
+ devc->have_adat = 1;
+ break;
+
+ case RME_DIGI96_PRO:
+ devc->have_adat = 1;
+ if (pci_revision < 2)
+ {
+ devc->chip_name = "RME Digi96/8 PRO (green)";
+ }
+ else
+ {
+ devc->chip_name = "RME Digi96/8 PRO (blue)";
+ }
+ break;
+
+ case RME_DIGI96_PAD:
+ devc->have_adat = 1;
+ devc->have_analog = 1;
+ devc->chip_name = "RME Digi96/8 PAD";
+ break;
+ }
+
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+
+ oss_register_device (osdev, devc->chip_name);
+
+ devc->linaddr =
+ (char *) MAP_PCI_MEM (devc->osdev, 0, devc->physaddr, 0x60000);
+ if (devc->linaddr == NULL)
+ {
+ cmn_err (CE_WARN, "Can't map PCI registers (0x%08lx)\n",
+ devc->physaddr);
+ return 0;
+ }
+
+ if ((err = oss_register_interrupts (devc->osdev, 0, digi96intr, NULL)) < 0)
+ {
+ cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err);
+ return 0;
+ }
+
+ devc->pPLAYBUF = (unsigned int *) devc->linaddr;
+ devc->pRECBUF = (unsigned int *) (devc->linaddr + 0x10000);
+ devc->pCTRL1 = (unsigned int *) (devc->linaddr + 0x20000);
+ devc->pCTRL2 = (unsigned int *) (devc->linaddr + 0x20004);
+ devc->pPLAYACK = (unsigned int *) (devc->linaddr + 0x20008);
+ devc->pRECACK = (unsigned int *) (devc->linaddr + 0x2000c);
+ devc->pPLAYPOS = (unsigned int *) (devc->linaddr + 0x20000);
+ devc->pRECPOS = (unsigned int *) (devc->linaddr + 0x30000);
+ devc->pRESETPLAY = (unsigned int *) (devc->linaddr + 0x4fffc);
+ devc->pRESETREC = (unsigned int *) (devc->linaddr + 0x5fffc);
+
+ return init_digi96 (devc); /* Detected */
+}
+
+int
+oss_digi96_detach (oss_device_t * osdev)
+{
+ digi96_devc *devc = (digi96_devc *) osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ write_ctrl2 (devc, 0); /* Soft mute */
+
+ oss_unregister_interrupts (devc->osdev);
+
+ MUTEX_CLEANUP (devc->mutex);
+
+ UNMAP_PCI_MEM (devc->osdev, 0, devc->physaddr, devc->linaddr, 0x60000);
+
+ oss_unregister_device (devc->osdev);
+
+ return 1;
+
+}
diff --git a/kernel/drv/oss_digi96/oss_digi96.man b/kernel/drv/oss_digi96/oss_digi96.man
new file mode 100644
index 0000000..f13fb66
--- /dev/null
+++ b/kernel/drv/oss_digi96/oss_digi96.man
@@ -0,0 +1,63 @@
+NAME
+oss_digi96 - RME Digi96 professional audio driver.
+
+DESCRIPTION
+Audio driver for the RME Digi96 family of profressional audio controllers.
+- Only 16 and 24/32 bit audio formats are supported.
+- All Digi96 family members support 32kHz, 44.1kHz, 48kHz, 64kHz, 88.2kHz and
+96kHz. sampling rates.
+
+
+MIXER PANEL
+
+Note! For recording you need to set digi96.sync to INTERNAL and the values
+ of digi96.mode and digi96.input to match your studio setup. Otherwise
+ recordings will fail with I/O error.
+
+There are several settings that can be changed using the ossmix program
+shipped with OSS. Note that some features don't work with all Digi96
+family members. For example ADAT mode is not supported by the base
+model.
+
+o digi96.mode <SPDIF|AESEBU|ADAT>:
+This setting controls the output mode which can be S/PDIF (consumer),
+AES/EBU (professional) or ADAT. The input mode is detected automatically.
+If ADAT input is detected the output mode will be switched to ADAT
+automatically (this doesn't work in the other direction).
+
+o digi96.sync <EXTERNAL|INTERNAL>:
+This setting tells if the playback sampling rate is based on the internal
+oscillator or the sample rate detected in the input port. See also the
+definition of the digi96.worldclk setting.
+
+o digi96.input <OPTICAL|COAXIAL|INTERNAL|XLR>: Selects the active input.
+
+o digi96.sel <BYPASS|NORMAL>:
+When set to BYPASS the input signal will be routed directly to the
+output (also sets digi96.sync automatically to EXTERNAL). In this mode
+audio data written to /dev/dsp will be muted.
+
+o digi96.worldclk ON|OFF:
+Setting this control to ON will enable the optional worldclock input as
+the sample rate source (overrides the digi96.sync setting).
+
+o digi96.emph ON|OFF:
+Enables/disables the de-emphasis option on the analog (monitor) output
+connector.
+
+o digi96.data <AUDIO|AC3>:
+Specifies if the output signal is audio or AC3 data (sets the non-audio
+bit in the channel status data).
+
+
+OPTIONS
+None
+
+FILES
+CONFIGFILEPATH/oss_digi96.conf Device configuration file
+
+AUTHOR
+4Front Technologies
+
+
+