diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/drv/oss_madi/oss_madi.c | |
download | oss4-upstream.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/drv/oss_madi/oss_madi.c')
-rw-r--r-- | kernel/drv/oss_madi/oss_madi.c | 975 |
1 files changed, 975 insertions, 0 deletions
diff --git a/kernel/drv/oss_madi/oss_madi.c b/kernel/drv/oss_madi/oss_madi.c new file mode 100644 index 0000000..8f0f2fa --- /dev/null +++ b/kernel/drv/oss_madi/oss_madi.c @@ -0,0 +1,975 @@ +/* + * Purpose: Driver for RME MADI and AES32 audio interfaces + * + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_madi_cfg.h" +#include "oss_pci.h" +#include "madi.h" + +#define RME_VENDOR_ID 0x10ee /* Xilinx actually */ +#define RME_MADI 0x3fc6 + +#define HWINFO_SIZE 128 + +extern int madi_devsize; +extern int madi_maxchannels; + +#define LSWAP(x) x + +static void +set_output_enable (madi_devc_t * devc, unsigned int chn, int enabled) +{ + madi_write (devc, MADI_outputEnableStart + 4 * chn, enabled); +} + +static void +set_input_enable (madi_devc_t * devc, unsigned int chn, int enabled) +{ + madi_write (devc, MADI_inputEnableStart + 4 * chn, enabled); +} + +static void +start_audio (madi_devc_t * devc) +{ + madi_control (devc, devc->cmd | (MADI_AudioInterruptEnable | MADI_Start)); +} + +static void +stop_audio (madi_devc_t * devc) +{ + madi_control (devc, devc->cmd & ~(MADI_AudioInterruptEnable | MADI_Start)); +} + +static void +audio_interrupt (madi_devc_t * devc, unsigned int status) +{ + int i; + + for (i = 0; i < devc->num_outputs; i++) + { + madi_portc_t *portc = devc->out_portc[i]; + + if (!(portc->trigger_bits & PCM_ENABLE_OUTPUT)) + continue; + + oss_audio_outputintr (portc->audio_dev, 1); + } + + for (i = 0; i < devc->num_inputs; i++) + { + madi_portc_t *portc = devc->in_portc[i]; + + if (!(portc->trigger_bits & PCM_ENABLE_INPUT)) + continue; + + oss_audio_inputintr (portc->audio_dev, 1); + } +} + +static int +madiintr (oss_device_t * osdev) +{ + madi_devc_t *devc = osdev->devc; + unsigned int status; + + status = madi_read (devc, MADI_status); + + if (status & MADI_audioIntPending) + audio_interrupt (devc, status); + + madi_write (devc, MADI_interruptAck, 0); + + return !!(status & + (MADI_audioIntPending | MADI_midi0IRQStatus | MADI_midi1IRQStatus)) + != 0; +} + +static void +madi_change_hw_rate (madi_devc_t * devc) +{ + unsigned int rate_bits = 0; + unsigned long long v; + + devc->rate = devc->next_rate; + /* + * Compute and set the sample rate in control2 register. + */ + + rate_bits = MADI_Freq1; // TODO + + devc->cmd &= ~MADI_FreqMask; + devc->cmd |= rate_bits; + + madi_control (devc, devc->cmd); + + /* + * Compute and set the DDS value + */ + v = 110100480000000ULL / (unsigned long long) devc->rate; + madi_write (devc, MADI_freq, (unsigned int) v); +} + +static void +initialize_hardware (madi_devc_t * devc) +{ + int chn, src; + +/* + * Intialize the control register + */ + devc->cmd = MADI_ClockModeMaster | MADI_LatencyMask | MADI_LineOut; + + if (devc->model == MDL_AES32) + devc->cmd |= MADI_SyncSrc0 | MADI_ProBit; + else + devc->cmd |= + MADI_InputCoax | MADI_SyncRef_MADI | MADI_TX_64ch_mode | MADI_AutoInput; + + madi_control (devc, devc->cmd); + +/* + * Set all input and output engines to stoped + */ + for (chn = 0; chn < MAX_CHANNELS; chn++) + { + set_output_enable (devc, chn, 0); + set_input_enable (devc, chn, 0); + } + +/* + * Init control2 register + */ + if (devc->model == MDL_MADI) +#ifdef OSS_BIG_ENDIAN + madi_control2 (devc, MADI_BIGENDIAN_MODE); +#else + madi_control2 (devc, 0x0); +#endif + + madi_change_hw_rate (devc); + +/* + * Write all HW mixer registers + */ + + for (chn = 0; chn < MAX_CHANNELS; chn++) + for (src = 0; src < 2 * MAX_CHANNELS; src++) + madi_write_gain (devc, chn, src, devc->mixer_values[chn][src]); +} + +static int +madi_set_rate (int dev, int arg) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + + return devc->rate; +} + +static __inline__ void +reserve_rec_channels(madi_devc_t *devc, int c, int nc) +{ + int ch; + + for (ch=c;ch < c+nc;ch++) + devc->busy_rec_channels |= (1ULL << ch); +} + +static __inline__ void +free_rec_channels(madi_devc_t *devc, int c, int nc) +{ + int ch; + + for (ch=c;ch < c+nc;ch++) + devc->busy_rec_channels &= ~(1ULL << ch); +} + +static __inline__ int +rec_channels_avail(madi_devc_t *devc, int c, int nc) +{ + int ch; + + for (ch=c;ch < c+nc;ch++) + if (devc->busy_rec_channels & (1ULL << ch)) + return 0; + + return 1; +} + +static __inline__ void +reserve_play_channels(madi_devc_t *devc, int c, int nc) +{ + int ch; + + for (ch=c;ch < c+nc;ch++) + devc->busy_play_channels |= (1ULL << ch); +} + +static __inline__ void +free_play_channels(madi_devc_t *devc, int c, int nc) +{ + int ch; + + for (ch=c;ch < c+nc;ch++) + devc->busy_play_channels &= ~(1ULL << ch); +} + +static __inline__ int +play_channels_avail(madi_devc_t *devc, int c, int nc) +{ + int ch; + + for (ch=c;ch < c+nc;ch++) + if (devc->busy_play_channels & (1ULL << ch)) + { + return 0; + } + + return 1; +} + +static short +madi_set_channels (int dev, short arg) +{ + madi_portc_t *portc = audio_engines[dev]->portc; + madi_devc_t *devc = audio_engines[dev]->devc; + oss_native_word flags; + int ok=1; + + if (arg < 1) + return portc->channels; + + if (arg != 1 && arg != 2 && arg != 4 && arg != 8 && arg != 16 && arg != 32 + && arg != 64) + return portc->channels; + + if (arg > portc->max_channels) + return portc->channels; + + if (arg == portc->channels) + return portc->channels; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + if (arg > portc->channels) /* Needs more channels */ + { + if (portc->direction == DIR_OUT) + { + if (!play_channels_avail(devc, portc->channel+portc->channels, arg - portc->channels)) + ok=0; + } + else + { + if (!rec_channels_avail(devc, portc->channel+portc->channels, arg - portc->channels)) + ok=0; + } + } + else + { + if (portc->direction == DIR_OUT) + { + free_play_channels(devc, portc->channel+arg, portc->channels-arg); + } + else + { + free_rec_channels(devc, portc->channel+arg, portc->channels-arg); + } + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + if (!ok) + return portc->channels; + + return portc->channels = arg; +} + + /*ARGSUSED*/ static unsigned int +madi_set_format (int dev, unsigned int arg) +{ + return AFMT_S32_LE; +} + +static int +madi_ioctl (int dev, unsigned int cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +static void +madi_trigger (int dev, int state) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + int ch; + + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + if (portc->open_mode & OPEN_WRITE) + { + if ((state & PCM_ENABLE_OUTPUT) + && !(portc->trigger_bits & PCM_ENABLE_OUTPUT)) + { + for (ch = 0; ch < portc->channels; ch++) + set_output_enable (devc, portc->channel + ch, 1); + portc->trigger_bits |= PCM_ENABLE_OUTPUT; + devc->active_outputs |= (1ULL << portc->channel); + } + else if (portc->trigger_bits & PCM_ENABLE_OUTPUT) + { + for (ch = 0; ch < portc->channels; ch++) + set_output_enable (devc, portc->channel + ch, 0); + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; + devc->active_outputs &= ~(1ULL << portc->channel); + } + } + + if ((portc->open_mode & OPEN_READ) + && !(portc->trigger_bits & PCM_ENABLE_INPUT)) + { + if (state & PCM_ENABLE_INPUT) + { + for (ch = 0; ch < portc->channels; ch++) + set_input_enable (devc, portc->channel + ch, 1); + portc->trigger_bits |= PCM_ENABLE_INPUT; + devc->active_inputs |= (1ULL << portc->channel); + } + else if (portc->trigger_bits & PCM_ENABLE_INPUT) + { + for (ch = 0; ch < portc->channels; ch++) + set_input_enable (devc, portc->channel + ch, 0); + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + devc->active_inputs &= ~(1ULL << portc->channel); + } + } + + if (devc->active_inputs || devc->active_outputs) + start_audio (devc); + else + stop_audio (devc); + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + +static int +madi_sync_control (int dev, int event, int mode) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + oss_native_word flags; + int ch; + + if (event == SYNC_PREPARE) + { + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + if ((mode & PCM_ENABLE_OUTPUT) && portc->direction == DIR_OUT) + { + for (ch = 0; ch < portc->channels; ch++) + set_output_enable (devc, portc->channel + ch, 1); + portc->trigger_bits |= PCM_ENABLE_OUTPUT; + devc->active_outputs |= (1ULL << portc->channel); + } + + if ((mode & PCM_ENABLE_INPUT) && portc->direction == DIR_IN) + { + for (ch = 0; ch < portc->channels; ch++) + set_input_enable (devc, portc->channel + ch, 1); + portc->trigger_bits |= PCM_ENABLE_INPUT; + devc->active_inputs |= (1ULL << portc->channel); + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; + } + + if (event == SYNC_TRIGGER) + { + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + if (devc->active_inputs || devc->active_outputs) + start_audio (devc); + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + return 0; + } + + return OSS_EIO; +} + +static void +madi_halt (int dev) +{ + madi_trigger (dev, 0); +} + + /*ARGSUSED*/ static int +madi_open (int dev, int mode, int open_flags) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + oss_native_word flags; + int ok=1; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + if (portc->open_mode != 0) + ok=0; + else + if (portc->direction == DIR_OUT) + { + if (!play_channels_avail(devc, portc->channel, 2)) + { + ok=0; + } + } + else + { + if (!rec_channels_avail(devc, portc->channel, 2)) + { + ok=0; + } + } + + if (!ok) + { + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return OSS_EBUSY; + } + + portc->open_mode = mode; + devc->open_audiodevs++; + + portc->channels = 2; + if (portc->direction == DIR_OUT) + reserve_play_channels(devc, portc->channel, 2); + else + reserve_rec_channels(devc, portc->channel, 2); + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + return 0; +} + + /*ARGSUSED*/ static void +madi_close (int dev, int mode) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + portc->open_mode = 0; + devc->open_audiodevs--; + if (portc->direction == DIR_OUT) + { + free_play_channels(devc, portc->channel, portc->channels); + } + else + { + free_rec_channels(devc, portc->channel, portc->channels); + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + + /*ARGSUSED*/ static void +madi_output_block (int dev, oss_native_word buf, int count, + int fragsize, int intrflag) +{ +} + + /*ARGSUSED*/ static void +madi_start_input (int dev, oss_native_word buf, int count, int fragsize, + int intrflag) +{ +} + + /*ARGSUSED*/ static int +madi_prepare_for_input (int dev, int bsize, int bcount) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_in; + int i; + int offs = portc->channel * CHBUF_PAGES; + + for (i = 0; i < CHBUF_PAGES * portc->channels; i++) + madi_write (devc, MADI_RecPageTable + 4 * (offs + i), + LSWAP(dmap->dmabuf_phys + i * 4096)); + + return 0; +} + + /*ARGSUSED*/ static int +madi_prepare_for_output (int dev, int bsize, int bcount) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_out; + int i; + int offs = portc->channel * CHBUF_PAGES; + + for (i = 0; i < CHBUF_PAGES * portc->channels; i++) + madi_write (devc, MADI_PlayPageTable + 4 * (offs + i), + LSWAP(dmap->dmabuf_phys + i * 4096)); + return 0; +} + + /*ARGSUSED*/ static int +madi_alloc_buffer (int dev, dmap_t * dmap, int direction) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + + if (direction == OPEN_READ) + { + dmap->dmabuf = devc->recbuf + portc->channel * CHBUF_SIZE; + dmap->dmabuf_phys = devc->recbuf_phys + portc->channel * CHBUF_SIZE; + dmap->buffsize = CHBUF_SIZE; + } + else + { + dmap->dmabuf = devc->playbuf + portc->channel * CHBUF_SIZE; + dmap->dmabuf_phys = devc->playbuf_phys + portc->channel * CHBUF_SIZE; + dmap->buffsize = CHBUF_SIZE; + } + + return 0; +} + + /*ARGSUSED*/ static int +madi_free_buffer (int dev, dmap_t * dmap, int direction) +{ + dmap->dmabuf = NULL; + dmap->dmabuf_phys = 0; + dmap->buffsize = 0; + + return 0; +} + + /*ARGSUSED*/ static int +madi_get_buffer_pointer (int dev, dmap_t * dmap, int direction) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + unsigned int status; +#if 1 + /* + * Use only full/half buffer resolution. + */ + int bytes; + bytes = ((devc->cmd & MADI_LatencyMask) >> 1) + 8; + bytes = 1 << bytes; + bytes *= portc->channels; + + status = madi_read (devc, MADI_status); + return (status & MADI_BufferHalf) ? bytes : 0; +#else + /* + * Use full resolution + */ + status = madi_read (devc, MADI_status) & MADI_BufferPosMask; + + return status * portc->channels; +#endif +} + +/*ARGSUSED*/ +static void +madi_setup_fragments (int dev, dmap_t * dmap, int direction) +{ + madi_devc_t *devc = audio_engines[dev]->devc; + madi_portc_t *portc = audio_engines[dev]->portc; + int bytes; + + bytes = ((devc->cmd & MADI_LatencyMask) >> 1) + 8; + bytes = 1 << bytes; + + dmap->buffsize = portc->channels * CHBUF_SIZE; + +#if 1 + /* + * Use two fragment (ping-pong) buffer that is also used by the hardware. + */ + dmap->fragment_size = bytes * portc->channels; + dmap->nfrags = 2; +#else +/* + * Use 4 fragments instead of 2 to avoid clicking caused by accessing the + * DMA buffer too close to the active DMA position. + * + * This means that the DMA pointer will jump by 2 fregments every time there + * is a half/full bufferinterrupt. + */ + dmap->fragment_size = (bytes * portc->channels) / 2; + dmap->nfrags = 4; +#endif + + dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size; +} + +static audiodrv_t madi_driver = { + madi_open, + madi_close, + madi_output_block, + madi_start_input, + madi_ioctl, + madi_prepare_for_input, + madi_prepare_for_output, + madi_halt, + NULL, // madi_local_qlen, + NULL, + NULL, // madi_halt_input, + NULL, // madi_halt_output, + madi_trigger, + madi_set_rate, + madi_set_format, + madi_set_channels, + NULL, + NULL, + NULL, + NULL, + madi_alloc_buffer, + madi_free_buffer, + NULL, + NULL, + madi_get_buffer_pointer, + NULL, + madi_sync_control, + NULL, + NULL, + NULL, + NULL, + madi_setup_fragments +}; + +static int +create_output_device (madi_devc_t * devc, char *name, int chn) +{ + madi_portc_t *portc; + adev_t *adev; + int n; + + if ((portc = PMALLOC (devc->osdev, sizeof (*portc))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate portc structure\n"); + return OSS_ENOMEM; + } + memset (portc, 0, sizeof (*portc)); + + n = devc->num_outputs++;; + portc->channel = chn; + portc->channels = 2; + portc->max_channels = 64 - chn; + + devc->out_portc[n] = portc; + + if ((portc->audio_dev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + name, + &madi_driver, + sizeof (audiodrv_t), + ADEV_NOINPUT | + ADEV_NONINTERLEAVED | + ADEV_NOMMAP, AFMT_S32_LE, + devc, -1)) < 0) + { + return portc->audio_dev; + } + + adev = audio_engines[portc->audio_dev]; + + if (devc->first_audiodev == -1) + devc->first_audiodev = portc->audio_dev; + adev->rate_source = devc->first_audiodev; + + adev->mixer_dev = devc->mixer_dev; + adev->portc = portc; + adev->min_rate = devc->rate; + adev->max_rate = devc->rate; + adev->min_channels = 1; + adev->max_channels = portc->max_channels; + adev->min_block = CHBUF_SIZE / 2; + adev->max_block = CHBUF_SIZE / 2; + + portc->direction = DIR_OUT; + + return portc->audio_dev; +} + +static int +create_input_device (madi_devc_t * devc, char *name, int chn) +{ + madi_portc_t *portc; + adev_t *adev; + int n; + + if ((portc = PMALLOC (devc->osdev, sizeof (*portc))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate portc structure\n"); + return OSS_ENOMEM; + } + memset (portc, 0, sizeof (*portc)); + + n = devc->num_inputs++;; + portc->channel = chn; + portc->channels = 2; + portc->max_channels = 64 - chn; + + devc->in_portc[n] = portc; + + if ((portc->audio_dev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + name, + &madi_driver, + sizeof (audiodrv_t), + ADEV_NOOUTPUT | + ADEV_NONINTERLEAVED | + ADEV_NOMMAP, AFMT_S32_LE, + devc, -1)) < 0) + { + return portc->audio_dev; + } + + adev = audio_engines[portc->audio_dev]; + + if (devc->first_audiodev == -1) + devc->first_audiodev = portc->audio_dev; + adev->rate_source = devc->first_audiodev; + + adev->mixer_dev = devc->mixer_dev; + adev->portc = portc; + adev->min_rate = devc->rate; + adev->max_rate = devc->rate; + adev->min_channels = 1; + adev->max_channels = portc->max_channels; + adev->min_block = CHBUF_SIZE / 2; + adev->max_block = CHBUF_SIZE / 2; + + portc->direction = DIR_IN; + + return portc->audio_dev; +} + +int +oss_madi_attach (oss_device_t * osdev) +{ + madi_devc_t *devc; + unsigned char pci_irq_line, pci_revision /*, pci_latency */ ; + unsigned short pci_command, vendor, device; + unsigned int pci_ioaddr; + int i; + int err; + int my_mixer; + + int chn, src; + + DDB (cmn_err (CE_NOTE, "Entered RME MADI detect routine\n")); + + pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); + pci_read_config_word (osdev, PCI_DEVICE_ID, &device); + + if (vendor != RME_VENDOR_ID || device != RME_MADI) + return 0; + + pci_read_config_word (osdev, PCI_COMMAND, &pci_command); + pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); + pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); + pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &pci_ioaddr); + + pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config_word (osdev, PCI_COMMAND, pci_command); + + if (pci_ioaddr == 0) + { + cmn_err (CE_WARN, "BAR0 not initialized by BIOS\n"); + return 0; + } + + if ((devc = PMALLOC (osp, sizeof (*devc))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate devc\n"); + return 0; + } + memset (devc, 0, sizeof (*devc)); + devc->osdev = osdev; + osdev->devc = devc; + devc->first_audiodev = -1; + + MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); + + DDB (cmn_err (CE_CONT, "RME HDSPM revision %d\n", pci_revision)); + + if (pci_revision < 230) + { + devc->name = "RME MADI"; + devc->model = MDL_MADI; + } + else + { + devc->name = "RME AES32"; + devc->model = MDL_AES32; + } + + DDB (cmn_err (CE_CONT, "Card model is %s\n", devc->name)); + + devc->rate = devc->next_rate = 48000; // TODO: Also set the other rate control fields */ + + osdev->hw_info = PMALLOC (osdev, HWINFO_SIZE); /* Text buffer for additional device info */ + sprintf (osdev->hw_info, "PCI revision %d", pci_revision); + + oss_register_device (osdev, devc->name); + + devc->physaddr = pci_ioaddr; + devc->registers = MAP_PCI_MEM (devc->osdev, 0, devc->physaddr, 65536); + + if (devc->registers == NULL) + { + cmn_err (CE_WARN, "Can't map PCI registers (0x%08x)\n", devc->physaddr); + return 0; + } + + devc->playbuf = CONTIG_MALLOC (devc->osdev, DMABUF_SIZE, MEMLIMIT_32BITS, + &devc->playbuf_phys, devc->play_dma_handle); + if (devc->playbuf == NULL) + { + cmn_err (CE_WARN, "Cannot allocate play DMA buffers\n"); + return 0; + } + + devc->recbuf = CONTIG_MALLOC (devc->osdev, DMABUF_SIZE, MEMLIMIT_32BITS, + &devc->recbuf_phys, devc->rec_dma_handle); + if (devc->recbuf == NULL) + { + cmn_err (CE_WARN, "Cannot allocate rec DMA buffers\n"); + return 0; + } + + if ((err = oss_register_interrupts (devc->osdev, 0, madiintr, NULL)) < 0) + { + cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err); + return 0; + } + + if (madi_maxchannels < 2) madi_maxchannels=2; + if (madi_maxchannels > MAX_CHANNELS)madi_maxchannels=MAX_CHANNELS; + + if (madi_devsize < 1) madi_devsize = 1; + + if (madi_devsize != 1 && madi_devsize != 2 && madi_devsize != 4) + madi_devsize = 2; + + /* + * Set the hw mixer shadow values to sane levels so that + * initialize_hardware() can write them to the actual registers. + */ + + for (chn = 0; chn < MAX_CHANNELS; chn++) + for (src = 0; src < 2 * MAX_CHANNELS; src++) + devc->mixer_values[chn][src] = MUTE_GAIN; /* Set everything off */ + +#if 0 + for (src = 0; src < MAX_CHANNELS; src += 2) + { + /* Setup playback monitoring to analog out */ + devc->mixer_values[MONITOR_CH][SRC_PLAY | src] = UNITY_GAIN / 10; + devc->mixer_values[MONITOR_CH + 1][SRC_PLAY | (src + 1)] = + UNITY_GAIN / 10; + } +#endif + + for (chn = 0; chn < MAX_CHANNELS; chn++) + devc->mixer_values[chn][SRC_PLAY | chn] = UNITY_GAIN; /* N->N outputs on */ + + initialize_hardware (devc); + + if ((my_mixer = madi_install_mixer (devc)) < 0) + { + devc->mixer_dev = -1; + return 0; + } + else + { + devc->mixer_dev = my_mixer; + } + + if (madi_devsize == 1) + { + for (i = 0; i < madi_maxchannels; i++) + { + char tmp[32]; + sprintf (tmp, "%s out%d", devc->name, i+1); + create_output_device (devc, tmp, i); + } + + for (i = 0; i < madi_maxchannels; i++) + { + char tmp[32]; + sprintf (tmp, "%s in%d", devc->name, i+1); + create_input_device (devc, tmp, i); + } + } + else + { + for (i = 0; i < madi_maxchannels; i += madi_devsize) + { + char tmp[32]; + sprintf (tmp, "%s out%d-%d", devc->name, i+1, i + madi_devsize); + create_output_device (devc, tmp, i); + } + + for (i = 0; i < madi_maxchannels; i += madi_devsize) + { + char tmp[32]; + sprintf (tmp, "%s in%d-%d", devc->name, i+1, i + madi_devsize); + create_input_device (devc, tmp, i); + } + } + + madi_activate_mixer (devc); + + return 1; +} + +int +oss_madi_detach (oss_device_t * osdev) +{ + madi_devc_t *devc = (madi_devc_t *) osdev->devc; + + if (oss_disable_device (osdev) < 0) + return 0; + + stop_audio (devc); + + /* + * Shut up the hardware + */ + devc->cmd &= ~MADI_FreqMask; + madi_control (devc, devc->cmd); + + oss_unregister_interrupts (devc->osdev); + + MUTEX_CLEANUP (devc->mutex); + + if (devc->registers != NULL) + UNMAP_PCI_MEM (osdev, 0, devc->physaddr, devc->registers, 65536); + CONTIG_FREE (devc->osdev, devc->playbuf, DMABUF_SIZE, + devc->play_dma_handle); + CONTIG_FREE (devc->osdev, devc->recbuf, DMABUF_SIZE, devc->rec_dma_handle); + + oss_unregister_device (devc->osdev); + + return 1; +} |