diff options
Diffstat (limited to 'attic/drv/oss_digi32/oss_digi32.c')
-rw-r--r-- | attic/drv/oss_digi32/oss_digi32.c | 997 |
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; +} |