summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_sbpci/oss_sbpci.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
commit1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch)
tree4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/drv/oss_sbpci/oss_sbpci.c
downloadoss4-upstream.tar.gz
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/drv/oss_sbpci/oss_sbpci.c')
-rw-r--r--kernel/drv/oss_sbpci/oss_sbpci.c1404
1 files changed, 1404 insertions, 0 deletions
diff --git a/kernel/drv/oss_sbpci/oss_sbpci.c b/kernel/drv/oss_sbpci/oss_sbpci.c
new file mode 100644
index 0000000..c4bea9f
--- /dev/null
+++ b/kernel/drv/oss_sbpci/oss_sbpci.c
@@ -0,0 +1,1404 @@
+/*
+ * Purpose: Creative/Ensoniq AudioPCI97 driver (ES1371/ES1373)
+ *
+ * This driver is used with the original Ensoniq AudioPCI97 card and many
+ * PCI based Sound Blaster cards by Creative Technologies. For example
+ * Sound Blaster PCI128 and Creative/Ectiva EV1938.
+ */
+/*
+ *
+ * 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_sbpci_cfg.h"
+#include "midi_core.h"
+#include "sbpci.h"
+#include "ac97.h"
+#include "oss_pci.h"
+
+
+extern int apci_latency;
+extern int apci_spdif;
+
+#define ENSONIQ_VENDOR_ID 0x1274
+#define ECTIVA_VENDOR_ID 0x1102
+#define ENSONIQ_ES1371 0x1371
+#define ENSONIQ_ES5880 0x8001
+#define ENSONIQ_ES5880A 0x8002
+#define ENSONIQ_ES5880B 0x5880
+#define ECTIVA_ES1938 0x8938
+
+#define MAX_PORTC 2
+
+typedef struct apci97_portc
+{
+
+ /* Audio parameters */
+ int audiodev;
+ int open_mode;
+ int trigger_bits;
+ int audio_enabled;
+ int speed, bits, channels;
+ int atype; /* 0=DAC/ADC, 1=Synth */
+}
+apci97_portc;
+
+typedef struct apci97_devc
+{
+ oss_device_t *osdev;
+ oss_mutex_t mutex, low_mutex;
+ oss_native_word base;
+ int irq;
+ char *chip_name;
+ int revision;
+
+ apci97_portc portc[MAX_PORTC];
+/*
+ * Mixer
+ */
+ ac97_devc ac97devc;
+
+/*
+ * MIDI
+ */
+ int midi_opened;
+ int midi_dev;
+ oss_midi_inputbyte_t midi_input_intr;
+}
+apci97_devc;
+
+
+void SRCRegWrite (apci97_devc * devc, unsigned short reg, unsigned short val);
+void SRCSetRate (apci97_devc * devc, unsigned char base, unsigned short rate);
+
+
+static int
+ac97_read (void *devc_, int wAddr)
+{
+ apci97_devc *devc = devc_;
+ int i, dtemp, dinit;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ dtemp = INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF);
+ /* wait for WIP to go away saving the current state for later */
+ for (i = 0; i < 0x100UL; ++i)
+ if (!(INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF) & (1UL << 30)))
+ break;
+
+ /* write addr w/data=0 and assert read request ... */
+
+ /* save the current state for later */
+ dinit = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF);
+
+ /* enable SRC state data in SRC mux */
+ for (i = 0; i < 0x100UL; ++i)
+ if (!
+ ((dtemp =
+ INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev, (dtemp & SRC_CTLMASK) | 0x00010000UL,
+ devc->base + CONC_dSRCIO_OFF);
+
+ /* wait for a SAFE time to write a read request and then do it, dammit */
+
+ for (i = 0; i < 0x100UL; ++i)
+ {
+ if ((INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & 0x00070000UL) ==
+ 0x00010000UL)
+ break;
+ }
+
+ OUTL (devc->osdev,
+ ((int) wAddr << 16) | (1UL << 23), devc->base + CONC_dCODECCTL_OFF);
+
+ /* restore SRC reg */
+ for (i = 0; i < 0x100UL; ++i)
+ if (!
+ ((dtemp =
+ INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev, dinit, devc->base + CONC_dSRCIO_OFF);
+
+ /* now wait for the stinkin' data (RDY) */
+ for (i = 0; i < 0x100UL; ++i)
+ if (INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF) & (1UL << 31))
+ break;
+ dtemp = INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF);
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+
+ return dtemp & 0xffff;
+}
+
+static int
+ac97_write (void *devc_, int wAddr, int wData)
+{
+ apci97_devc *devc = devc_;
+ int i, dtemp, dinit;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ /* wait for WIP to go away */
+ for (i = 0; i < 0x100UL; ++i)
+ if (!(INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF) & (1UL << 30)))
+ break;
+
+ /* save the current state for later */
+ dinit = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF);
+
+ dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF);
+ /* enable SRC state data in SRC mux */
+ for (i = 0; i < 0x100UL; ++i)
+ if (!
+ ((dtemp =
+ INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev, (dtemp & SRC_CTLMASK) | 0x00010000UL,
+ devc->base + CONC_dSRCIO_OFF);
+
+ /* wait for a SAFE time to write addr/data and then do it, dammit */
+ for (i = 0; i < 0x100UL; ++i)
+ {
+ if ((INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & 0x00070000UL) ==
+ 0x00010000UL)
+ break;
+ }
+
+ OUTL (devc->osdev, ((int) wAddr << 16) | wData,
+ devc->base + CONC_dCODECCTL_OFF);
+
+ /* restore SRC reg */
+ for (i = 0; i < 0x100UL; ++i)
+ if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev, dinit, devc->base + CONC_dSRCIO_OFF);
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+
+ return 0;
+}
+
+
+void
+SRCInit (apci97_devc * devc)
+{
+ int i;
+
+ /* Clear all SRC RAM then init - keep SRC disabled until done */
+ for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
+ if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev, SRC_DISABLE, devc->base + CONC_dSRCIO_OFF);
+
+ for (i = 0; i < 0x80; ++i)
+ SRCRegWrite (devc, (unsigned short) i, 0U);
+
+ SRCRegWrite (devc, SRC_SYNTH_BASE + SRC_TRUNC_N_OFF, 16 << 4);
+ SRCRegWrite (devc, SRC_SYNTH_BASE + SRC_INT_REGS_OFF, 16 << 10);
+ SRCRegWrite (devc, SRC_DAC_BASE + SRC_TRUNC_N_OFF, 16 << 4);
+ SRCRegWrite (devc, SRC_DAC_BASE + SRC_INT_REGS_OFF, 16 << 10);
+ SRCRegWrite (devc, SRC_SYNTH_VOL_L, 1 << 12);
+ SRCRegWrite (devc, SRC_SYNTH_VOL_R, 1 << 12);
+ SRCRegWrite (devc, SRC_DAC_VOL_L, 1 << 12);
+ SRCRegWrite (devc, SRC_DAC_VOL_R, 1 << 12);
+ SRCRegWrite (devc, SRC_ADC_VOL_L, 1 << 12);
+ SRCRegWrite (devc, SRC_ADC_VOL_R, 1 << 12);
+
+ /* default some rates */
+ SRCSetRate (devc, SRC_SYNTH_BASE, 8000);
+ SRCSetRate (devc, SRC_DAC_BASE, 8000);
+ SRCSetRate (devc, SRC_ADC_BASE, 8000);
+
+ /* now enable the whole deal */
+ for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
+ if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev, 0, devc->base + CONC_dSRCIO_OFF);
+
+ return;
+}
+
+unsigned short
+SRCRegRead (apci97_devc * devc, unsigned short reg)
+{
+ int i, dtemp;
+
+ dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF);
+ /* wait for ready */
+ for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
+ if (!
+ ((dtemp =
+ INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY))
+ break;
+
+ /* assert a read request */
+ OUTL (devc->osdev,
+ (dtemp & SRC_CTLMASK) | ((int) reg << 25),
+ devc->base + CONC_dSRCIO_OFF);
+
+ /* now wait for the data */
+ for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
+ if (!
+ ((dtemp =
+ INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY))
+ break;
+
+ return (unsigned short) dtemp;
+}
+
+
+void
+SRCRegWrite (apci97_devc * devc, unsigned short reg, unsigned short val)
+{
+ int i, dtemp;
+ int writeval;
+
+ dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF);
+ /* wait for ready */
+ for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
+ if (!
+ ((dtemp =
+ INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY))
+ break;
+
+ /* assert the write request */
+ writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE | ((int) reg << 25) | val;
+ OUTL (devc->osdev, writeval, devc->base + CONC_dSRCIO_OFF);
+
+ return;
+}
+
+typedef struct
+{
+ unsigned char base;
+ unsigned short rate;
+}
+SRC_RATE_REC;
+
+#define SRC_RATE_RECS (3)
+SRC_RATE_REC theSRCRates[SRC_RATE_RECS] = {
+ {SRC_SYNTH_BASE, 0},
+ {SRC_DAC_BASE, 0},
+ {SRC_ADC_BASE, 0}
+};
+
+/*ARGSUSED*/
+unsigned short
+SRCGetRate (apci97_devc * devc, unsigned char base)
+{
+ unsigned short i;
+
+ for (i = 0; i < SRC_RATE_RECS; i++)
+ if (theSRCRates[i].base == base)
+ return theSRCRates[i].rate;
+
+ return 0;
+}
+
+void
+SRCSetRate (apci97_devc * devc, unsigned char base, unsigned short rate)
+{
+ int i, freq, dtemp;
+ unsigned short N, truncM, truncStart;
+
+
+ for (i = 0; i < SRC_RATE_RECS; i++)
+ if (theSRCRates[i].base == base)
+ {
+ theSRCRates[i].rate = rate;
+ break;
+ }
+
+ if (base != SRC_ADC_BASE)
+ {
+ /* freeze the channel */
+ dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE;
+ for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
+ if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev,
+ (INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_CTLMASK) |
+ dtemp, devc->base + CONC_dSRCIO_OFF);
+
+ /* calculate new frequency and write it - preserve accum */
+ freq = ((int) rate << 16) / 3000U;
+ SRCRegWrite (devc, (unsigned short) base + SRC_INT_REGS_OFF,
+ (SRCRegRead
+ (devc,
+ (unsigned short) base +
+ SRC_INT_REGS_OFF) & 0x00ffU) | ((unsigned short) (freq >>
+ 6) &
+ 0xfc00));
+ SRCRegWrite (devc, (unsigned short) base + SRC_VFREQ_FRAC_OFF,
+ (unsigned short) freq >> 1);
+
+ /* un-freeze the channel */
+ for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
+ if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY))
+ break;
+ OUTL (devc->osdev,
+ (INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_CTLMASK) &
+ ~dtemp, devc->base + CONC_dSRCIO_OFF);
+ }
+ else
+ {
+ /* derive oversample ratio */
+ N = rate / 3000U;
+ if (N == 15 || N == 13 || N == 11 || N == 9)
+ --N;
+
+ /* truncate the filter and write n/trunc_start */
+ truncM = (21 * N - 1) | 1;
+ if (rate >= 24000U)
+ {
+ if (truncM > 239)
+ truncM = 239;
+ truncStart = (239 - truncM) >> 1;
+
+ SRCRegWrite (devc, base + SRC_TRUNC_N_OFF,
+ (truncStart << 9) | (N << 4));
+ }
+ else
+ {
+ if (truncM > 119)
+ truncM = 119;
+ truncStart = (119 - truncM) >> 1;
+
+ SRCRegWrite (devc, base + SRC_TRUNC_N_OFF,
+ 0x8000U | (truncStart << 9) | (N << 4));
+ }
+
+ /* calculate new frequency and write it - preserve accum */
+ freq = ((48000UL << 16) / rate) * N;
+ SRCRegWrite (devc, base + SRC_INT_REGS_OFF,
+ (SRCRegRead
+ (devc,
+ (unsigned short) base +
+ SRC_INT_REGS_OFF) & 0x00ff) | ((unsigned short) (freq >>
+ 6) &
+ 0xfc00));
+ SRCRegWrite (devc, base + SRC_VFREQ_FRAC_OFF,
+ (unsigned short) freq >> 1);
+
+ SRCRegWrite (devc, SRC_ADC_VOL_L, N << 8);
+ SRCRegWrite (devc, SRC_ADC_VOL_R, N << 8);
+
+ }
+
+ return;
+}
+
+static void
+apci97_writemem (apci97_devc * devc, int page, int offs, int data)
+{
+ int tmp;
+
+ tmp = INL (devc->osdev, devc->base + 0xc);
+ OUTL (devc->osdev, page, devc->base + 0xc); /* Select memory page */
+ OUTL (devc->osdev, data, devc->base + offs);
+ OUTL (devc->osdev, tmp, devc->base + 0xc); /* Select the original memory page */
+}
+
+static unsigned int
+apci97_readmem (apci97_devc * devc, int page, int offs)
+{
+ unsigned int val;
+
+ OUTL (devc->osdev, page, devc->base + 0xc); /* Select memory page */
+ val = INL (devc->osdev, devc->base + offs);
+ return val;
+}
+
+static int
+apci97intr (oss_device_t * osdev)
+{
+ int stats, i;
+ int tmp;
+ unsigned char ackbits = 0;
+ unsigned char uart_stat;
+ apci97_devc *devc = (apci97_devc *) osdev->devc;
+ apci97_portc *portc;
+ int served = 0;
+
+ stats = INL (devc->osdev, devc->base + 0x04);
+ /*cmn_err (CE_WARN, "AudioPCI97 intr status %08x\n", stats); */
+
+ if (!(stats & 0x80000000)) /* No interrupt pending */
+ return served;
+
+ served = 1;
+
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ portc = &devc->portc[i];
+
+ if (stats & 0x00000010) /* CCB interrupt */
+ {
+ cmn_err (CE_WARN, "CCB interrupt\n");
+ }
+
+ if ((stats & 0x00000004) && (portc->atype)) /* DAC1 (synth) interrupt */
+ {
+ ackbits |= CONC_SERCTL_DAC1IE;
+ if (portc->trigger_bits & PCM_ENABLE_OUTPUT)
+ oss_audio_outputintr (portc->audiodev, 0);
+
+ }
+
+ if ((stats & 0x00000002) && (!portc->atype)) /* DAC2 interrupt */
+ {
+ ackbits |= CONC_SERCTL_DAC2IE;
+ if (portc->trigger_bits & PCM_ENABLE_OUTPUT)
+ oss_audio_outputintr (portc->audiodev, 0);
+ }
+
+ if ((stats & 0x00000001) && (!portc->atype)) /* ADC interrupt */
+ {
+ ackbits |= CONC_SERCTL_ADCIE;
+ if (portc->trigger_bits & PCM_ENABLE_INPUT)
+ {
+ oss_audio_inputintr (portc->audiodev, 0);
+ }
+ }
+
+ if (stats & 0x00000008) /* UART interrupt */
+ {
+ uart_stat = INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF);
+
+ while (uart_stat & CONC_UART_RXRDY)
+ {
+ unsigned char d;
+ d = INB (devc->osdev, devc->base + CONC_bUARTDATA_OFF);
+
+ if (devc->midi_opened & OPEN_READ && devc->midi_input_intr)
+ devc->midi_input_intr (devc->midi_dev, d);
+ uart_stat = INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF);
+ }
+
+ }
+ /* Ack the interrupt */
+ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF);
+ OUTB (devc->osdev, (tmp & ~ackbits), devc->base + CONC_bSERCTL_OFF); /* Clear bits */
+ OUTB (devc->osdev, tmp | ackbits, devc->base + CONC_bSERCTL_OFF); /* Return them back on */
+ }
+
+ return served;
+}
+
+/*
+ * Audio routines
+ */
+
+static int
+apci97_audio_set_rate (int dev, int arg)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->speed;
+
+ if (arg > 48000)
+ arg = 48000;
+ if (arg < 5000)
+ arg = 5000;
+ portc->speed = arg;
+ return portc->speed;
+}
+
+static short
+apci97_audio_set_channels (int dev, short arg)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+
+ if ((arg != 1) && (arg != 2))
+ return portc->channels;
+ portc->channels = arg;
+
+ return portc->channels;
+}
+
+static unsigned int
+apci97_audio_set_format (int dev, unsigned int arg)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->bits;
+
+
+ if (!(arg & (AFMT_U8 | AFMT_S16_LE | AFMT_AC3)))
+ return portc->bits;
+ portc->bits = arg;
+
+ return portc->bits;
+}
+
+/*ARGSUSED*/
+static int
+apci97_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void apci97_audio_trigger (int dev, int state);
+
+static void
+apci97_audio_reset (int dev)
+{
+ apci97_audio_trigger (dev, 0);
+}
+
+static void
+apci97_audio_reset_input (int dev)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+ apci97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+apci97_audio_reset_output (int dev)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+ apci97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+/*ARGSUSED*/
+static int
+apci97_audio_open (int dev, int mode, int open_flags)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+ apci97_devc *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (portc->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+ portc->open_mode = mode;
+ portc->audio_enabled = ~mode;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+static void
+apci97_audio_close (int dev, int mode)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+
+ apci97_audio_reset (dev);
+ portc->open_mode = 0;
+ portc->audio_enabled &= ~mode;
+}
+
+/*ARGSUSED*/
+static void
+apci97_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+
+}
+
+/*ARGSUSED*/
+static void
+apci97_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ apci97_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+}
+
+static void
+apci97_audio_trigger (int dev, int state)
+{
+ apci97_devc *devc = audio_engines[dev]->devc;
+ apci97_portc *portc = audio_engines[dev]->portc;
+ int tmp;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if (state & PCM_ENABLE_OUTPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ if (portc->atype)
+ {
+ tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF);
+ tmp |= CONC_DEVCTL_DAC1_EN;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+ }
+ else
+ {
+ tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF);
+ tmp |= CONC_DEVCTL_DAC2_EN;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+ }
+ portc->trigger_bits |= PCM_ENABLE_OUTPUT;
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ if (portc->atype)
+ {
+ tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF);
+ tmp &= ~CONC_DEVCTL_DAC1_EN;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+
+ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF);
+ tmp &= ~CONC_SERCTL_DAC1IE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+ }
+ else
+ {
+ tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF);
+ tmp &= ~CONC_DEVCTL_DAC2_EN;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+
+ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF);
+ tmp &= ~CONC_SERCTL_DAC2IE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+
+ }
+ }
+ }
+ }
+
+ if ((portc->open_mode & OPEN_READ)
+ && !(audio_engines[dev]->flags & ADEV_NOINPUT))
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF);
+ tmp |= CONC_DEVCTL_ADC_EN;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+ portc->trigger_bits |= PCM_ENABLE_INPUT;
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+
+ tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF);
+ tmp &= ~CONC_DEVCTL_ADC_EN;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+
+ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF);
+ tmp &= ~CONC_SERCTL_ADCIE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+ }
+ }
+ }
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+apci97_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ apci97_devc *devc = audio_engines[dev]->devc;
+ apci97_portc *portc = audio_engines[dev]->portc;
+ int tmp = 0x00;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ /* Set physical address of the DMA buffer */
+
+ apci97_writemem (devc, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF,
+ dmap->dmabuf_phys);
+
+ /* Set ADC rate */
+ SRCSetRate (devc, SRC_ADC_BASE, portc->speed);
+
+ /* Set format */
+ tmp = INB (devc->osdev, devc->base + CONC_bSERFMT_OFF);
+ tmp &= ~(CONC_PCM_ADC_STEREO | CONC_PCM_ADC_16BIT);
+ if (portc->channels == 2)
+ tmp |= CONC_PCM_ADC_STEREO;
+ if (portc->bits == 16)
+ {
+ tmp |= CONC_PCM_ADC_16BIT;
+ OUTB (devc->osdev, 0x10, devc->base + CONC_bSKIPC_OFF); /* Skip count register */
+ }
+ else
+ {
+ OUTB (devc->osdev, 0x08, devc->base + CONC_bSKIPC_OFF); /* Skip count register */
+ }
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERFMT_OFF);
+
+ /* Set the frame count */
+ apci97_writemem (devc, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF,
+ (dmap->bytes_in_use / 4) - 1);
+
+ /* Set # of samples between interrupts */
+ OUTW (devc->osdev,
+ (dmap->fragment_size / ((portc->channels * portc->bits) / 8)) - 1,
+ devc->base + CONC_wADCIC_OFF);
+
+ /* Enable the wave interrupt */
+ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF) & ~CONC_SERCTL_ADCIE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+ tmp |= CONC_SERCTL_ADCIE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+apci97_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+ apci97_devc *devc = audio_engines[dev]->devc;
+ apci97_portc *portc = audio_engines[dev]->portc;
+ int tmp;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (devc->revision >= 4)
+ {
+ /* set SPDIF to PCM mode */
+ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x1c) & ~0x2,
+ devc->base + 0x1c);
+ if (portc->bits & AFMT_AC3)
+ {
+ portc->channels = 2;
+ portc->bits = 16;
+ portc->speed = 48000;
+ /* set S/PDIF to AC3 Mode */
+ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x1c) | 0x2,
+ devc->base + 0x1c);
+ }
+ }
+
+ if (portc->atype)
+ {
+ /* Set physical address of the DMA buffer */
+ apci97_writemem (devc, CONC_SYNCTL_PAGE, CONC_dSYNPADDR_OFF,
+ dmap->dmabuf_phys);
+
+ /* Set DAC1 rate */
+ SRCSetRate (devc, SRC_SYNTH_BASE, portc->speed);
+
+ /* Set format */
+ tmp = INB (devc->osdev, devc->base + CONC_bSERFMT_OFF);
+ tmp &= ~((CONC_PCM_DAC_STEREO | CONC_PCM_DAC_16BIT) >> 2);
+ if (portc->channels == 2)
+ tmp |= (CONC_PCM_DAC_STEREO >> 2);
+ if (portc->bits == 16)
+ {
+ tmp |= (CONC_PCM_DAC_16BIT >> 2);
+ }
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERFMT_OFF);
+
+ /* Set the frame count */
+ apci97_writemem (devc, CONC_SYNCTL_PAGE, CONC_wSYNFC_OFF,
+ (dmap->bytes_in_use / 4) - 1);
+
+ /* Set # of samples between interrupts */
+ OUTW (devc->osdev,
+ (dmap->fragment_size / ((portc->channels * portc->bits) / 8)) - 1,
+ devc->base + CONC_wSYNIC_OFF);
+
+ /* Enable the wave interrupt */
+ tmp =
+ INB (devc->osdev,
+ devc->base + CONC_bSERCTL_OFF) & ~CONC_SERCTL_DAC1IE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+ tmp |= CONC_SERCTL_DAC1IE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+
+ }
+ else
+ {
+ /* Set physical address of the DMA buffer */
+ apci97_writemem (devc, CONC_DACCTL_PAGE, CONC_dDACPADDR_OFF,
+ dmap->dmabuf_phys);
+
+ /* Set DAC rate */
+ SRCSetRate (devc, SRC_DAC_BASE, portc->speed);
+
+ /* Set format */
+ tmp = INB (devc->osdev, devc->base + CONC_bSERFMT_OFF);
+ tmp &= ~(CONC_PCM_DAC_STEREO | CONC_PCM_DAC_16BIT);
+ if (portc->channels == 2)
+ tmp |= CONC_PCM_DAC_STEREO;
+ if (portc->bits == 16)
+ {
+ tmp |= CONC_PCM_DAC_16BIT;
+ OUTB (devc->osdev, 0x10, devc->base + CONC_bSKIPC_OFF); /* Skip count register */
+ }
+ else
+ {
+ OUTB (devc->osdev, 0x08, devc->base + CONC_bSKIPC_OFF); /* Skip count register */
+ }
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERFMT_OFF);
+
+
+ /* Set the frame count */
+ apci97_writemem (devc, CONC_DACCTL_PAGE, CONC_wDACFC_OFF,
+ (dmap->bytes_in_use / 4) - 1);
+
+ /* Set # of samples between interrupts */
+ OUTW (devc->osdev,
+ (dmap->fragment_size / ((portc->channels * portc->bits) / 8)) - 1,
+ devc->base + CONC_wDACIC_OFF);
+
+ /* Enable the wave interrupt */
+ tmp =
+ INB (devc->osdev,
+ devc->base + CONC_bSERCTL_OFF) & ~CONC_SERCTL_DAC2IE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+ tmp |= CONC_SERCTL_DAC2IE;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF);
+ }
+
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+apci97_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ apci97_devc *devc = audio_engines[dev]->devc;
+ apci97_portc *portc = audio_engines[dev]->portc;
+ int ptr = 0, port = 0, page = 0;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ if (direction == PCM_ENABLE_OUTPUT)
+ {
+ if (portc->atype)
+ {
+ port = CONC_wSYNFC_OFF;
+ page = CONC_SYNCTL_PAGE;
+ }
+ else
+ {
+ port = CONC_wDACFC_OFF;
+ page = CONC_DACCTL_PAGE;
+ }
+ }
+
+ if (direction == PCM_ENABLE_INPUT)
+ {
+ port = CONC_wADCFC_OFF;
+ page = CONC_ADCCTL_PAGE;
+ }
+
+ ptr = apci97_readmem (devc, page, port);
+ ptr >>= 16;
+ ptr <<= 2; /* count is in dwords */
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+
+ return ptr;
+}
+
+audiodrv_t apci97_audio_driver = {
+ apci97_audio_open,
+ apci97_audio_close,
+ apci97_audio_output_block,
+ apci97_audio_start_input,
+ apci97_audio_ioctl,
+ apci97_audio_prepare_for_input,
+ apci97_audio_prepare_for_output,
+ apci97_audio_reset,
+ NULL,
+ NULL,
+ apci97_audio_reset_input,
+ apci97_audio_reset_output,
+ apci97_audio_trigger,
+ apci97_audio_set_rate,
+ apci97_audio_set_format,
+ apci97_audio_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* apci97_alloc_buffer */
+ NULL, /* apci97_free_buffer */
+ NULL,
+ NULL,
+ apci97_get_buffer_pointer
+};
+
+/*ARGSUSED*/
+static int
+apci97_midi_open (int dev, int mode, oss_midi_inputbyte_t inputbyte,
+ oss_midi_inputbuf_t inputbuf,
+ oss_midi_outputintr_t outputintr)
+{
+ apci97_devc *devc = (apci97_devc *) midi_devs[dev]->devc;
+
+ if (devc->midi_opened)
+ {
+ return OSS_EBUSY;
+ }
+
+ devc->midi_input_intr = inputbyte;
+ devc->midi_opened = mode;
+
+ if (mode & OPEN_READ)
+ {
+ OUTB (devc->osdev, CONC_UART_RXINTEN, devc->base + CONC_bUARTCSTAT_OFF);
+ }
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static void
+apci97_midi_close (int dev, int mode)
+{
+ apci97_devc *devc = (apci97_devc *) midi_devs[dev]->devc;
+
+ OUTB (devc->osdev, 0x00, devc->base + CONC_bUARTCSTAT_OFF);
+ devc->midi_opened = 0;
+}
+
+static int
+apci97_midi_out (int dev, unsigned char midi_byte)
+{
+ apci97_devc *devc = (apci97_devc *) midi_devs[dev]->devc;
+ int i;
+
+ unsigned char uart_stat =
+ INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF);
+
+ i = 0;
+ while (i < 1000000 && !(uart_stat & CONC_UART_TXRDY))
+ {
+ uart_stat = INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF);
+ i++;
+ }
+
+ if (!(uart_stat & CONC_UART_TXRDY))
+ return 0;
+
+
+ OUTB (devc->osdev, midi_byte, devc->base + CONC_bUARTDATA_OFF);
+
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+apci97_midi_ioctl (int dev, unsigned cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static midi_driver_t apci97_midi_driver = {
+ apci97_midi_open,
+ apci97_midi_close,
+ apci97_midi_ioctl,
+ apci97_midi_out
+};
+
+static int
+apci97_control (int dev, int ctrl, unsigned int cmd, int value)
+{
+ apci97_devc *devc = mixer_devs[dev]->hw_devc;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ value = 0;
+ switch (ctrl)
+ {
+ case 1: /* Speaker Mode */
+ value = (INL (devc->osdev, devc->base + 4) & (1 << 26) ? 1 : 0);
+ break;
+
+ case 2: /* Dual Dac Mode */
+ value = INL (devc->osdev, devc->base + 4) & (1 << 27) ? 1 : 0;
+ break;
+ }
+ }
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ switch (ctrl)
+ {
+ case 1: /* Front/Rear Mirror */
+ if (value)
+ {
+ OUTL (devc->osdev,
+ INL (devc->osdev, devc->base + 4) | (1 << 26),
+ devc->base + 4);
+ }
+ else
+ {
+ OUTL (devc->osdev,
+ INL (devc->osdev, devc->base + 4) & ~(1 << 26),
+ devc->base + 4);
+ }
+ break;
+
+ case 2: /* DAC1->Front DAC2->REAR */
+ if (value)
+ {
+ /* disable front/rear mirroring */
+ OUTL (devc->osdev,
+ INL (devc->osdev, devc->base + 4) & ~(1 << 26),
+ devc->base + 4);
+ /* Enable Dual Dac mode */
+ OUTL (devc->osdev,
+ INL (devc->osdev, devc->base + 4) | (1 << 27) | (1 << 24),
+ devc->base + 4);
+ }
+ else
+ {
+ /* enable mirror */
+ OUTL (devc->osdev,
+ INL (devc->osdev, devc->base + 4) | (1 << 26),
+ devc->base + 4);
+ /* disable dual dac */
+ OUTL (devc->osdev,
+ INL (devc->osdev,
+ devc->base + 4) & ~((1 << 27) | (1 << 24)),
+ devc->base + 4);
+ }
+ break;
+ }
+ }
+ return value;
+}
+
+static int
+apci97_mix_init (int dev)
+{
+ int group, err;
+
+ if ((group = mixer_ext_create_group (dev, 0, "MIXEXT")) < 0)
+ return group;
+
+ if ((err = mixer_ext_create_control (dev, group, 1, apci97_control,
+ MIXT_ENUM, "SPKMODE", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group, 2, apci97_control,
+ MIXT_ONOFF, "DUALDAC", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ return 0;
+
+}
+
+static int
+init_apci97 (apci97_devc * devc, int device_id)
+{
+ int my_mixer;
+ int tmp, i;
+ int first_dev = 0;
+
+ if ((device_id == ENSONIQ_ES5880) || (device_id == ENSONIQ_ES5880A) ||
+ (device_id == ENSONIQ_ES5880B) ||
+ (device_id == 0x1371 && devc->revision == 7) ||
+ (device_id == 0x1371 && devc->revision >= 9))
+ {
+ int i;
+
+ /* Have a ES5880 so enable the codec manually */
+ tmp = INB (devc->osdev, devc->base + CONC_bINTSUMM_OFF) & 0xff;
+ tmp |= 0x20;
+ OUTB (devc->osdev, tmp, devc->base + CONC_bINTSUMM_OFF); /* OUTB? */
+ for (i = 0; i < 2000; i++)
+ oss_udelay (10);
+ }
+
+ SRCInit (devc);
+#if 0
+ OUTB (devc->osdev, 0x00, devc->base + CONC_bSERCTL_OFF);
+ OUTB (devc->osdev, 0x00, devc->base + CONC_bNMIENA_OFF); /* NMI off */
+ OUTB (devc->osdev, 0x00, devc->base + CONC_wNMISTAT_OFF); /* OUTB? */
+#endif
+/*
+ * Turn on UART and CODEC
+ */
+ tmp = INL (devc->osdev, devc->base + CONC_bDEVCTL_OFF) & 0xff;
+ tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS);
+ OUTB (devc->osdev, tmp | CONC_DEVCTL_UART_EN | CONC_DEVCTL_JSTICK_EN,
+ devc->base + CONC_bDEVCTL_OFF);
+ OUTB (devc->osdev, 0x00, devc->base + CONC_bUARTCSTAT_OFF);
+
+ /* Perform AC97 codec warm reset */
+ tmp = INB (devc->osdev, devc->base + CONC_bMISCCTL_OFF) & 0xff;
+ OUTB (devc->osdev, tmp | CONC_MISCCTL_SYNC_RES,
+ devc->base + CONC_bMISCCTL_OFF);
+ oss_udelay (200);
+ OUTB (devc->osdev, tmp, devc->base + CONC_bMISCCTL_OFF);
+ oss_udelay (200);
+
+/*
+ * Enable S/PDIF
+ */
+ if (devc->revision >= 4)
+ {
+ if (apci_spdif)
+ {
+ /* enable SPDIF */
+ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x04) | (1 << 18),
+ devc->base + 0x04);
+ /* SPDIF out = data from DAC */
+ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x00) | (1 << 26),
+ devc->base + 0x00);
+ }
+ else
+ {
+ /* disable spdif out */
+ OUTL (devc->osdev,
+ INL (devc->osdev, devc->base + 0x04) & ~(1 << 18),
+ devc->base + 0x04);
+ OUTL (devc->osdev,
+ INL (devc->osdev, devc->base + 0x00) & ~(1 << 26),
+ devc->base + 0x00);
+ }
+ }
+
+/*
+ * Init mixer
+ */
+ my_mixer =
+ ac97_install (&devc->ac97devc, "AC97 Mixer", ac97_read, ac97_write, devc,
+ devc->osdev);
+
+ if (my_mixer < 0)
+ return 0;
+
+ if (devc->revision >= 4)
+ {
+ /* enable 4 speaker mode */
+ OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) | (1 << 26),
+ devc->base + 4);
+ mixer_ext_set_init_fn (my_mixer, apci97_mix_init, 5);
+ }
+
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+
+ int adev;
+ char tmp_name[100];
+ apci97_portc *portc = &devc->portc[i];
+ int caps = ADEV_AUTOMODE;
+ int fmts = AFMT_U8 | AFMT_S16_LE;
+
+ if (devc->revision >= 4)
+ fmts |= AFMT_AC3;
+
+ if (i == 0)
+ {
+ sprintf (tmp_name, "%s (rev %d)", devc->chip_name, devc->revision);
+ caps |= ADEV_DUPLEX;
+ }
+ else
+ {
+ sprintf (tmp_name, "%s (playback only)", devc->chip_name);
+ caps |= ADEV_NOINPUT;
+ }
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &apci97_audio_driver,
+ sizeof (audiodrv_t),
+ caps, fmts, devc, -1)) < 0)
+ {
+ adev = -1;
+ return 0;
+ }
+ else
+ {
+ if (i == 0)
+ first_dev = adev;
+ audio_engines[adev]->portc = portc;
+ audio_engines[adev]->rate_source = first_dev;
+ audio_engines[adev]->min_rate = 5000;
+ audio_engines[adev]->max_rate = 48000;
+ audio_engines[adev]->caps |= PCM_CAP_FREERATE;
+ portc->open_mode = 0;
+ portc->audiodev = adev;
+ portc->atype = i;
+#ifdef CONFIG_OSS_VMIX
+ if (i == 0)
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+ }
+
+ audio_engines[adev]->mixer_dev = my_mixer;
+ }
+
+ if ((devc->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "APCI97", "APCI97 UART", &apci97_midi_driver, sizeof (midi_driver_t),
+ 0, devc, devc->osdev)) < 0)
+ {
+ cmn_err (CE_WARN, "Couldn't install MIDI device\n");
+ return 0;
+ }
+
+ devc->midi_opened = 0;
+ return 1;
+}
+
+int
+oss_sbpci_attach (oss_device_t * osdev)
+{
+ unsigned char pci_irq_line, pci_revision;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ apci97_devc *devc;
+ int err;
+
+ DDB (cmn_err (CE_WARN, "Entered AudioPCI97 probe routine\n"));
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+ if ((vendor != ENSONIQ_VENDOR_ID && vendor != ECTIVA_VENDOR_ID) ||
+ (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 &&
+ device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 &&
+ device != ENSONIQ_ES5880B))
+
+ 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_BASE_ADDRESS_0, &pci_ioaddr);
+
+
+ if (pci_irq_line == 0)
+ {
+ cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d). Can't continue\n",
+ pci_irq_line);
+ return 0;
+ }
+
+ if (pci_ioaddr == 0)
+ {
+ cmn_err (CE_WARN, "I/O address not assigned 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;
+
+ switch (device)
+ {
+ case ENSONIQ_ES1371:
+ devc->chip_name = "Creative AudioPCI97 (ES1371)";
+ break;
+ case ECTIVA_ES1938:
+ devc->chip_name = "Ectiva AudioPCI";
+ break;
+ case ENSONIQ_ES5880:
+ case ENSONIQ_ES5880A:
+ case ENSONIQ_ES5880B:
+ devc->chip_name = "Sound Blaster PCI128";
+ break;
+ default:
+ devc->chip_name = "AudioPCI97";
+ }
+
+ devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ devc->base &= ~3;
+
+ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+ /* set the PCI latency to 32 */
+ if ((apci_latency == 32) || (apci_latency == 64) || (apci_latency == 96) ||
+ (apci_latency == 128))
+ pci_write_config_byte (osdev, 0x0d, apci_latency);
+
+
+
+ oss_register_device (osdev, devc->chip_name);
+
+ if ((err = oss_register_interrupts (osdev, 0, apci97intr, NULL)) < 0)
+ {
+ cmn_err (CE_WARN, "Can't allocate IRQ%d, err=%d\n", pci_irq_line, err);
+ return 0;
+ }
+
+
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+ MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1);
+
+
+ devc->revision = pci_revision;
+ return init_apci97 (devc, device); /* Detected */
+}
+
+int
+oss_sbpci_detach (oss_device_t * osdev)
+{
+ apci97_devc *devc = (apci97_devc *) osdev->devc;
+ int tmp;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF) &
+ ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+ OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF);
+
+ oss_unregister_interrupts (devc->osdev);
+
+ MUTEX_CLEANUP (devc->mutex);
+ MUTEX_CLEANUP (devc->low_mutex);
+ UNMAP_PCI_IOADDR (devc->osdev, 0);
+
+ oss_unregister_device (devc->osdev);
+ return 1;
+}