summaryrefslogtreecommitdiff
path: root/attic/drv/oss_digi32/oss_digi32.c
diff options
context:
space:
mode:
Diffstat (limited to 'attic/drv/oss_digi32/oss_digi32.c')
-rw-r--r--attic/drv/oss_digi32/oss_digi32.c997
1 files changed, 997 insertions, 0 deletions
diff --git a/attic/drv/oss_digi32/oss_digi32.c b/attic/drv/oss_digi32/oss_digi32.c
new file mode 100644
index 0000000..6bb1169
--- /dev/null
+++ b/attic/drv/oss_digi32/oss_digi32.c
@@ -0,0 +1,997 @@
+/*
+ * Purpose: Driver for RME Digi32 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_digi32_cfg.h"
+#include "oss_pci.h"
+
+#define RME_VENDOR_ID 0xea60
+#define RME_DIGI32 0x9896
+#define RME_DIGI32_PRO 0x9897
+#define RME_DIGI32_8 0x9898
+
+#define RME_VENDOR_ID2 0x10ee
+#define RME_DIGI96_8_PRO 0x3fc2
+
+#define MAX_AUDIO_CHANNEL 4
+
+/*
+ * Control register bits
+ */
+#define C_EMP 0x8000
+#define C_PD 0x4000
+#define C_AUTOSYNC 0x2000
+#define C_ADAT 0x1000
+#define C_DS 0x0800
+#define C_BM 0x0800
+#define C_PRO 0x0400
+#define C_MUTE 0x0200
+#define C_RESET 0x0100
+#define C_INP_1 0x0080
+#define C_INP_0 0x0040
+#define C_FREQ_1 0x0020
+#define C_FREQ_0 0x0010
+#define C_SEL 0x0008
+#define C_MODE24 0x0004
+#define C_MONO 0x0002
+#define C_START 0x0001
+
+/*
+ * Status register bits
+ */
+#define S_INTR 0x80000000
+#define S_KMODE 0x40000000 /* 0=Quartz mode, 1=PLL mode */
+#define S_FBITS 0x38000000 /* F bits of CS8412/14 */
+#define S_FSHIFT 27
+#define S_ERF 0x04000000 /* ERF of the CS8412. 1=error */
+#define S_REV 0x0x300000 /* Revision bits */
+#define S_ADATLOCK 0x00080000 /* ADAT PLL locked */
+
+typedef struct digi32_portc
+{
+ int open_mode;
+ int audio_dev;
+ int voice_chn;
+ int type;
+#define TY_IN 1
+#define TY_OUT 2
+#define TY_BOTH 3
+
+ int active;
+ int channels;
+ int speed;
+ int speedsel;
+ int bits;
+ int inptr, outptr;
+ int tmp_autosync;
+ int bytes_per_sample;
+ int trigger_bits;
+}
+digi32_portc;
+
+typedef struct digi32_devc
+{
+ oss_device_t *osdev;
+ oss_mutex_t mutex;
+ char *chip_name;
+ int model;
+#define MDL_BASIC 0
+#define MDL_8 1
+#define MDL_PRO 2
+#define MDL_96_8_PRO 3
+
+ int cs841x_part;
+#define CS8412 0
+#define CS8414 1
+
+ unsigned int cmd;
+
+ oss_native_word physaddr;
+ char *linaddr;
+ unsigned int *pFIFO;
+ unsigned int *pCTRL;
+
+ int irq;
+
+ digi32_portc portc[MAX_AUDIO_CHANNEL];
+ int open_count;
+ int mixer_dev;
+
+ int speed_locked;
+ int active_device_count;
+}
+digi32_devc;
+
+static int fbit_tab[2][8] = {
+ {0, 48000, 44100, 32000, 48000, 44100, 44056, 32000},
+ {0, 0, 0, 96000, 88200, 48000, 44100, 32000}
+};
+
+
+static void
+write_command (digi32_devc * devc, unsigned int ctrl)
+{
+ devc->cmd = ctrl;
+
+ PCI_WRITEL (devc->osdev, devc->pCTRL, ctrl);
+}
+
+/*ARGSUSED*/
+static void
+handle_recording (int dev)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+ digi32_devc *devc = audio_engines[dev]->devc;
+
+ int i, p;
+ unsigned int *buf;
+
+ p = portc->inptr / 4; /* Dword addressing */
+ buf =
+ (unsigned int *) &audio_engines[dev]->dmap_in->
+ dmabuf[dmap_get_qtail (audio_engines[dev]->dmap_in) *
+ audio_engines[dev]->dmap_in->fragment_size];
+
+ /* Perform copy with channel swapping */
+ if (portc->bytes_per_sample == 4)
+ {
+ /* 32 bit mode */
+ for (i = 0; i < (8 * 1024) / 4; i += 2)
+ {
+ buf[i] = PCI_READL (devc->osdev, devc->pFIFO + i + 1 + p);
+ buf[i + 1] = PCI_READL (devc->osdev, devc->pFIFO + i + p);
+ }
+ }
+ else
+ {
+ /* 16 bit mode */
+ unsigned int tmp;
+
+ for (i = 0; i < (8 * 1024) / 4; i++)
+ {
+ tmp = PCI_READL (devc->osdev, devc->pFIFO + i + p);
+ buf[i] = ((tmp >> 16) & 0xffff) | ((tmp & 0xffff) << 16);
+ }
+ }
+
+ if (!(portc->trigger_bits & PCM_ENABLE_OUTPUT)) /* Clean the samples */
+ for (i = 0; i < (8 * 1024) / 4; i++)
+ {
+ PCI_WRITEL (devc->osdev, devc->pFIFO + i + p, 0);
+ }
+
+ portc->inptr = (portc->inptr + (8 * 1024)) % (128 * 1024); /* Increment the pointer */
+}
+
+static int
+digi32intr (oss_device_t * osdev)
+{
+ unsigned int status;
+ digi32_devc *devc = (digi32_devc *) osdev->devc;
+ digi32_portc *portc;
+ int i;
+
+ status = PCI_READL (devc->osdev, devc->pCTRL);
+ if (!(status & S_INTR))
+ return 0;
+
+ for (i = 0; i < 2; i++)
+ {
+ portc = &devc->portc[i];
+
+ if (portc->trigger_bits & PCM_ENABLE_INPUT)
+ {
+ handle_recording (portc->audio_dev);
+ if (status & S_ERF && !(devc->cmd & C_AUTOSYNC))
+ {
+ cmn_err (CE_WARN, "External sync dropped\n");
+#if 0
+ devc->cmd |= C_AUTOSYNC;
+ write_command (devc, devc->cmd);
+#endif
+ }
+ else
+ oss_audio_inputintr (portc->audio_dev, 0);
+ }
+
+ if (portc->trigger_bits & PCM_ENABLE_OUTPUT)
+ {
+ oss_audio_outputintr (portc->audio_dev, 1);
+ }
+ }
+ write_command (devc, devc->cmd); /* Interrupt acknowledge */
+
+ return 1;
+}
+
+
+/***********************************
+ * Audio routines
+ ***********************************/
+
+static int
+digi32_set_rate (int dev, int arg)
+{
+ digi32_devc *devc = audio_engines[dev]->devc;
+ digi32_portc *portc = audio_engines[dev]->portc;
+
+ static int speed_table[6] = { 32000, 44100, 48000, 64000, 88200, 96000 };
+ int n, i, best = 2, dif, bestdif = 0x7fffffff;
+
+ if (arg == 0 || devc->active_device_count > 0 || devc->speed_locked)
+ arg = portc->speed;
+
+ n = (devc->model == MDL_PRO) ? 6 : 3;
+
+ for (i = 0; i < n; 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
+digi32_set_channels (int dev, short arg)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+
+ if ((arg == 0))
+ return portc->channels;
+ arg = 2;
+ portc->channels = arg;
+
+ return portc->channels;
+}
+
+static unsigned int
+digi32_set_format (int dev, unsigned int arg)
+{
+ digi32_devc *devc = audio_engines[dev]->devc;
+ digi32_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0 || devc->active_device_count > 0)
+ return portc->bits;
+
+ if (arg != AFMT_S16_NE && arg != AFMT_S32_NE && arg != AFMT_AC3)
+ arg = AFMT_S16_NE;
+
+ portc->bytes_per_sample = (arg & (AFMT_S32_LE | AFMT_S32_BE)) ? 4 : 2;
+ portc->bits = arg;
+
+ return portc->bits;
+}
+
+/*ARGSUSED*/
+static int
+digi32_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void digi32_trigger (int dev, int state);
+
+static void
+digi32_reset (int dev)
+{
+ digi32_trigger (dev, 0);
+}
+
+static void
+digi32_reset_input (int dev)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+ digi32_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+digi32_reset_output (int dev)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+ digi32_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+static void digi32_close (int dev, int mode);
+
+/*ARGSUSED*/
+static int
+digi32_open (int dev, int mode, int open_flags)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+ digi32_devc *devc = audio_engines[dev]->devc;
+ int i;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (portc->open_mode != 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+ portc->audio_dev = dev;
+ devc->speed_locked = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ for (i = 0; i < 32 * 1024; i++)
+ PCI_WRITEL (devc->osdev, devc->pFIFO + i, 0); /* Clean the buffer */
+ return 0;
+}
+
+/*ARGSUSED*/
+static void
+digi32_close (int dev, int mode)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+ digi32_devc *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ digi32_reset (dev);
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->open_mode = 0;
+ portc->active = 0;
+ if (portc->tmp_autosync)
+ {
+ portc->tmp_autosync = 0;
+ devc->cmd |= C_AUTOSYNC;
+ write_command (devc, devc->cmd);
+ }
+
+/*
+ * Inactivate all auxiliary channels allocated for this device
+ */
+
+ if (--devc->open_count <= 0)
+ {
+ devc->open_count = 0;
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static void
+digi32_output_block (int dev, oss_native_word ptr, int count, int fragsize,
+ int intrflag)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+ digi32_devc *devc = audio_engines[dev]->devc;
+
+ int i, p;
+ unsigned int *buf;
+
+ p = portc->outptr / 4; /* Dword addressing */
+ buf = (unsigned int *) &audio_engines[dev]->dmap_out->dmabuf[ptr];
+
+
+ /* Perform copy with channel swapping */
+ if (portc->bytes_per_sample == 4)
+ {
+ /* 32 bit mode */
+ for (i = 0; i < (8 * 1024) / 4; i += 2)
+ {
+ PCI_WRITEL (devc->osdev, devc->pFIFO + i + p, buf[i + 1]);
+ PCI_WRITEL (devc->osdev, devc->pFIFO + i + 1 + p, buf[i]);
+ }
+ }
+ else
+ {
+ /* 16 bit mode */
+ for (i = 0; i < (8 * 1024) / 4; i++)
+ {
+ unsigned int tmp = buf[i];
+ tmp = ((tmp >> 16) & 0xffff) | ((tmp & 0xffff) << 16);
+ PCI_WRITEL (devc->osdev, devc->pFIFO + i + p, tmp);
+ }
+ }
+
+ PCI_READL (devc->osdev, devc->pFIFO); /* Dummy read to flust cache on some archs */
+
+ portc->outptr = (portc->outptr + (8 * 1024)) % (128 * 1024); /* Increment the pointer */
+
+#if 1
+ if (!intrflag)
+ oss_audio_outputintr (dev, 0);
+#endif
+}
+
+/*ARGSUSED*/
+static void
+digi32_start_input (int dev, oss_native_word ptr, int count, int fragsize,
+ int intrflag)
+{
+}
+
+static void
+digi32_trigger (int dev, int state)
+{
+ digi32_portc *portc = audio_engines[dev]->portc;
+ digi32_devc *devc = audio_engines[dev]->devc;
+ int cmd = devc->cmd;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ state &= portc->open_mode;
+
+ if (portc->active != state)
+ {
+ if (state)
+ {
+ if (devc->active_device_count == 0)
+ {
+ cmd |= C_START;
+ cmd &= ~C_RESET;
+ write_command (devc, cmd | C_SEL);
+ if ((state & OPEN_WRITE) && (portc->open_mode & OPEN_WRITE))
+ portc->trigger_bits |= PCM_ENABLE_OUTPUT;
+ if ((state & OPEN_READ) && (portc->open_mode & OPEN_READ))
+ portc->trigger_bits |= PCM_ENABLE_INPUT;
+ devc->active_device_count = 1;
+ }
+ }
+ else
+ {
+ if (devc->active_device_count != 0)
+ {
+ cmd &= ~C_START;
+ write_command (devc, cmd);
+ if (!(state & OPEN_WRITE) && (portc->open_mode & OPEN_WRITE))
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ if (!(state & OPEN_READ) && (portc->open_mode & OPEN_READ))
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ devc->active_device_count = 0;
+ }
+ }
+ }
+
+ portc->active = state;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+digi32_prepare_for_output (int dev, int bsize, int bcount)
+{
+/* NOTE! This routine is used for input too */
+
+ digi32_devc *devc = audio_engines[dev]->devc;
+ digi32_portc *portc = audio_engines[dev]->portc;
+
+ int cmd = devc->cmd;
+ int dblbit = 0, prev_dblbit, speedsel;
+
+ if (devc->active_device_count > 0)
+ return 0;
+
+ if (bsize != 8 * 1024)
+ {
+ cmn_err (CE_CONT, "Illegal fragment size %d\n", bsize);
+ return OSS_EIO;
+ }
+
+ prev_dblbit = cmd & C_DS;
+ cmd &= C_EMP | C_PRO | C_SEL | C_MUTE | C_INP_0 | C_INP_1 | C_AUTOSYNC;
+ cmd |= C_RESET;
+
+ if (portc->bits & (AFMT_S32_BE | AFMT_S32_LE))
+ cmd |= C_MODE24;
+
+ if (portc->speedsel > 2)
+ dblbit = C_DS;
+
+ speedsel = (portc->speedsel % 3) + 1;
+ cmd |= (speedsel << 4) | dblbit;
+
+ if (dblbit != prev_dblbit)
+ {
+ write_command (devc, cmd | C_PD);
+ oss_udelay (10);
+ }
+ write_command (devc, cmd);
+ portc->outptr = 0;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+digi32_prepare_for_input (int dev, int bsize, int bcount)
+{
+ int i, status, fbits;
+ digi32_devc *devc = audio_engines[dev]->devc;
+ digi32_portc *portc = audio_engines[dev]->portc;
+ int cmd = devc->cmd;
+ int dblbit = 0, prev_dblbit, speedsel;
+
+ if (devc->cmd & C_AUTOSYNC)
+ {
+ devc->cmd &= ~C_AUTOSYNC; /* Autosync must be used for input */
+ portc->tmp_autosync = 1;
+ }
+
+ write_command (devc, devc->cmd);
+ oss_udelay (100);
+
+ for (i = 0; i < 1000; i++)
+ {
+ oss_udelay (10);
+ status = PCI_READL (devc->osdev, devc->pCTRL);
+
+ if (!(status & S_ERF))
+ break;
+ }
+
+ if (i >= 1000)
+ {
+ cmn_err (CE_WARN, "No input signal detected\n");
+ return OSS_EIO;
+ }
+
+ devc->speed_locked = 1;
+ fbits = (status & S_FBITS) >> S_FSHIFT;
+ portc->speed = fbit_tab[devc->cs841x_part][fbits];
+ DDB (cmn_err
+ (CE_WARN, "digi32: Measured input sampling rate is %d\n",
+ portc->speed));
+
+ if (devc->active_device_count > 0)
+ return 0;
+
+ if (bsize != 8 * 1024)
+ {
+ cmn_err (CE_CONT, "Illegal fragment size %d\n", bsize);
+ return OSS_EIO;
+ }
+
+ prev_dblbit = cmd & C_DS;
+ cmd &= C_EMP | C_PRO | C_SEL | C_MUTE | C_INP_0 | C_INP_1 | C_AUTOSYNC;
+ cmd |= C_RESET;
+
+ if (portc->bits & (AFMT_S32_BE | AFMT_S32_LE))
+ cmd |= C_MODE24;
+
+ if (portc->speedsel > 2)
+ dblbit = C_DS;
+
+ speedsel = (portc->speedsel % 3) + 1;
+ cmd |= (speedsel << 4) | dblbit;
+
+ if (dblbit != prev_dblbit)
+ {
+ write_command (devc, cmd | C_PD);
+ oss_udelay (10);
+ }
+ write_command (devc, cmd);
+ portc->inptr = 0;
+
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+digi32_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+ extern int digi32_buffsize;
+
+ if (digi32_buffsize < 512) /* Given in kilobytes */
+ digi32_buffsize *= 1024;
+
+ if (digi32_buffsize < 4096) /* Smaller ones not acceptable */
+ digi32_buffsize = 64 * 1024;
+
+ if (dmap->dmabuf != NULL)
+ return 0;
+ dmap->dmabuf_phys = 0; /* Not mmap() capable */
+ dmap->dmabuf = KERNEL_MALLOC (digi32_buffsize);
+ if (dmap->dmabuf == NULL)
+ return OSS_ENOSPC;
+ dmap->buffsize = digi32_buffsize;
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+digi32_free_buffer (int dev, dmap_t * dmap, int direction)
+{
+ if (dmap->dmabuf == NULL)
+ return 0;
+ KERNEL_FREE (dmap->dmabuf);
+
+ dmap->dmabuf = NULL;
+ return 0;
+}
+
+#if 0
+static int
+digi32_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+}
+#endif
+
+/*ARGSUSED*/
+static void
+digi32_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 digi32_output_driver = {
+ digi32_open,
+ digi32_close,
+ digi32_output_block,
+ digi32_start_input,
+ digi32_ioctl,
+ digi32_prepare_for_input,
+ digi32_prepare_for_output,
+ digi32_reset,
+ NULL,
+ NULL,
+ digi32_reset_input,
+ digi32_reset_output,
+ digi32_trigger,
+ digi32_set_rate,
+ digi32_set_format,
+ digi32_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ digi32_alloc_buffer,
+ digi32_free_buffer,
+ NULL,
+ NULL,
+ NULL, /* digi32_get_buffer_pointer */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ digi32_setup_fragments
+};
+
+static int
+attach_channel (digi32_devc * devc, int chnum, char *name,
+ audiodrv_t * drv, int type)
+{
+ int adev, opts;
+ digi32_portc *portc;
+
+ if (chnum < 0 || chnum >= MAX_AUDIO_CHANNEL)
+ return 0;
+
+ portc = &devc->portc[chnum];
+ opts =
+ ADEV_DUPLEX | ADEV_STEREOONLY | ADEV_COLD | ADEV_NOMMAP | ADEV_NOVIRTUAL;
+
+ if (chnum != 0)
+ opts |= ADEV_SHADOW;
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ name, drv, sizeof (audiodrv_t),
+ opts,
+ AFMT_S16_NE | AFMT_S32_NE | AFMT_AC3,
+ 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 = 8 * 1024;
+ audio_engines[adev]->max_block = 8 * 1024;
+ audio_engines[adev]->min_rate = 32000;
+ audio_engines[adev]->max_rate = (devc->model == MDL_PRO) ? 96000 : 48000;
+ portc->open_mode = 0;
+ portc->voice_chn = chnum;
+ portc->speed = 48000;
+ portc->bits = AFMT_S16_NE;
+ portc->bytes_per_sample = 2;
+ portc->type = type;
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+digi32_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
+{
+ 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 digi32_mixer_driver = {
+ digi32_mixer_ioctl
+};
+
+static int
+digi32_set_control (int dev, int ctrl, unsigned int cmd, int value)
+{
+ digi32_devc *devc = mixer_devs[dev]->devc;
+ int offs, nbits;
+ unsigned int data;
+
+ if (ctrl < 0)
+ return OSS_EINVAL;
+
+ offs = (ctrl >> 4) & 0x0f; /* # of bits in the field */
+ nbits = ctrl & 0x0f; /* Shift amount */
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ data = devc->cmd;
+ return (data >> offs) & ((1 << nbits) - 1);
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ if (value < 0 || value >= (1 << nbits))
+ return OSS_EINVAL;
+ data = devc->cmd & ~(((1 << nbits) - 1) << offs);
+ data |= (value & ((1 << nbits) - 1)) << offs;
+ devc->cmd = data;
+ return value;
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+digi32_mix_init (int dev)
+{
+ digi32_devc *devc = mixer_devs[dev]->devc;
+ int group, err;
+
+ if ((group = mixer_ext_create_group (dev, 0, "DIGI32")) < 0)
+ return group;
+
+ if (devc->model == MDL_PRO)
+ if ((err = mixer_ext_create_control (dev, group,
+ 0xf1, digi32_set_control,
+ MIXT_ONOFF,
+ "DIGI32_DEEMPH", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 0xd1, digi32_set_control,
+ MIXT_ENUM,
+ "DIGI32_SYNC", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if (devc->model != MDL_8)
+ if ((err = mixer_ext_create_control (dev, group,
+ 0xa1, digi32_set_control,
+ MIXT_ENUM,
+ "DIGI32_AESMODE", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 0x91, digi32_set_control,
+ MIXT_ONOFF,
+ "DIGI32_MUTE", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group,
+ 0x62, digi32_set_control,
+ MIXT_ENUM,
+ "DIGI32_INPUT", 3,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+
+ return 0;
+}
+
+int
+init_digi32 (digi32_devc * devc)
+{
+ int my_mixer;
+ char tmp[128];
+ int ret;
+
+ devc->mixer_dev = 0;
+
+ if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "RME Digi32 Control panel",
+ &digi32_mixer_driver,
+ sizeof (mixer_driver_t), devc)) < 0)
+ {
+
+ devc->mixer_dev = -1;
+ return 0;
+ }
+ else
+ {
+ devc->mixer_dev = my_mixer;
+ mixer_devs[my_mixer]->priority = -1; /* Don't use as the default mixer */
+ mixer_ext_set_init_fn (my_mixer, digi32_mix_init, 20);
+ }
+
+ ret =
+ attach_channel (devc, 0, devc->chip_name, &digi32_output_driver, TY_BOTH);
+ if (ret > 0)
+ {
+ sprintf (tmp, "%s (shadow)", devc->chip_name);
+ ret = attach_channel (devc, 1, tmp, &digi32_output_driver, TY_BOTH);
+ }
+ /*setup the command */
+ if (devc->model == MDL_PRO)
+ write_command (devc, C_PD); /* Reset DAC */
+
+ write_command (devc, C_AUTOSYNC);
+ return ret;
+}
+
+
+int
+oss_digi32_attach (oss_device_t * osdev)
+{
+ digi32_devc *devc;
+ unsigned char pci_irq_line, pci_revision;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ int err;
+
+ DDB (cmn_err (CE_WARN, "Entered Digi32 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 && vendor != RME_VENDOR_ID2) ||
+ (device != RME_DIGI32_PRO &&
+ device != RME_DIGI32 && device != RME_DIGI32_8))
+
+ return 0;
+
+ pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision);
+ pci_read_config_word (osdev, PCI_COMMAND, &pci_command);
+ pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line);
+ pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &pci_ioaddr);
+
+ 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;
+
+ switch (device)
+ {
+ case RME_DIGI32_PRO:
+ devc->model = MDL_PRO;
+ if (pci_revision == 150)
+ {
+ devc->chip_name = "RME Digi 32 Pro (CS8414)";
+ devc->cs841x_part = CS8414;
+ }
+ else
+ {
+ devc->chip_name = "RME Digi 32 Pro (CS8412)";
+ devc->cs841x_part = CS8412;
+ }
+ break;
+
+ case RME_DIGI32_8:
+ devc->chip_name = "RME Digi 32/8";
+ devc->model = MDL_8;
+ devc->cs841x_part = CS8412;
+ break;
+
+ case RME_DIGI32:
+ devc->chip_name = "RME Digi32";
+ devc->model = MDL_BASIC;
+ devc->cs841x_part = CS8412;
+ break;
+
+ case RME_DIGI96_8_PRO:
+ devc->chip_name = "RME Digi 96/8 Pro";
+ devc->model = MDL_96_8_PRO;
+ devc->cs841x_part = CS8414;
+ break;
+ }
+
+ DDB (cmn_err (CE_WARN, "Found Digi32 at 0x%x\n", pci_ioaddr));
+
+ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+
+ oss_register_device (osdev, devc->chip_name);
+
+ if ((err = oss_register_interrupts (devc->osdev, 0, digi32intr, NULL)) < 0)
+ {
+ cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err);
+ return 0;
+ }
+
+ devc->linaddr = (char *) MAP_PCI_MEM (devc->osdev, 0, devc->physaddr,
+ 128 * 1024 + 4);
+ if (devc->linaddr == NULL)
+ {
+ cmn_err (CE_WARN, "Can't ioremap PCI registers\n");
+ return 0;
+ }
+
+ devc->pFIFO = (unsigned int *) devc->linaddr;
+ devc->pCTRL = (unsigned int *) (devc->linaddr + 0x20000);
+
+ return init_digi32 (devc); /* Detected */
+}
+
+int
+oss_digi32_detach (oss_device_t * osdev)
+{
+ digi32_devc *devc = (digi32_devc *) osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ oss_unregister_interrupts (devc->osdev);
+
+ MUTEX_CLEANUP (devc->mutex);
+
+ UNMAP_PCI_MEM (devc->osdev, 0, devc->physaddr, devc->linaddr,
+ 128 * 1024 + 4);
+
+ oss_unregister_device (devc->osdev);
+
+ return 1;
+}