diff options
Diffstat (limited to 'kernel/drv/oss_digi96')
-rw-r--r-- | kernel/drv/oss_digi96/.devices | 4 | ||||
-rw-r--r-- | kernel/drv/oss_digi96/.name | 1 | ||||
-rw-r--r-- | kernel/drv/oss_digi96/oss_digi96.c | 1097 | ||||
-rw-r--r-- | kernel/drv/oss_digi96/oss_digi96.man | 63 |
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 + + + |