summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_trident
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_trident')
-rw-r--r--kernel/drv/oss_trident/.devices5
-rw-r--r--kernel/drv/oss_trident/.name1
-rw-r--r--kernel/drv/oss_trident/.params6
-rw-r--r--kernel/drv/oss_trident/oss_trident.c1580
-rw-r--r--kernel/drv/oss_trident/oss_trident.man27
5 files changed, 1619 insertions, 0 deletions
diff --git a/kernel/drv/oss_trident/.devices b/kernel/drv/oss_trident/.devices
new file mode 100644
index 0000000..4d766ef
--- /dev/null
+++ b/kernel/drv/oss_trident/.devices
@@ -0,0 +1,5 @@
+oss_trident pci1023,2000 Trident 4DWave-DX
+oss_trident pci1023,2001 Trident 4DWave-NX
+oss_trident pci1023,2002 Trident 4DWave-CX
+oss_trident pci1039,7018 SiS 7018
+oss_trident pci10b9,5451 ALI M5451
diff --git a/kernel/drv/oss_trident/.name b/kernel/drv/oss_trident/.name
new file mode 100644
index 0000000..f3df66b
--- /dev/null
+++ b/kernel/drv/oss_trident/.name
@@ -0,0 +1 @@
+Trident 4DWave, SiS7018, ALI M5451
diff --git a/kernel/drv/oss_trident/.params b/kernel/drv/oss_trident/.params
new file mode 100644
index 0000000..70d8ef5
--- /dev/null
+++ b/kernel/drv/oss_trident/.params
@@ -0,0 +1,6 @@
+int trident_mpu_ioaddr=0;
+/*
+ * Trident MPU 401 I/O Address
+ * Values: 0x300, 0x330 Default: 0x330
+ */
+
diff --git a/kernel/drv/oss_trident/oss_trident.c b/kernel/drv/oss_trident/oss_trident.c
new file mode 100644
index 0000000..13d8872
--- /dev/null
+++ b/kernel/drv/oss_trident/oss_trident.c
@@ -0,0 +1,1580 @@
+/*
+ * Purpose: Driver for Trident 4DWAVE, ALI 5451 and SiS 7918 audio chips
+ */
+/*
+ *
+ * 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_trident_cfg.h"
+#include "oss_pci.h"
+#include "ac97.h"
+
+#if defined(sparc)
+#define MEM_MAPPED_REGISTERS
+#else
+#undef MEM_MAPPED_REGISTERS
+#endif
+
+#define TRIDENT_VENDOR_ID 0x1023
+#define TRIDENT_4DWAVEDX_ID 0x2000
+#define TRIDENT_4DWAVENX_ID 0x2001
+#define TRIDENT_4DWAVECX_ID 0x2002
+
+#define ALI_VENDOR_ID 0x10b9
+#define ALI_5451_ID 0x5451
+
+#define SIS_VENDOR_ID 0x1039
+#define SIS_7018_ID 0x7018
+
+/* DX/NX/CX IO Registers */
+#define SP_CSO 0x24
+#define SP_LBA 0x28
+#define SP_ESO 0x2C
+#define ACR0 0x40
+#define SP_STAT 0x64
+#define TLBC 0x6C
+#define START_A 0x80
+#define STOP_A 0x84
+#define INT_A 0x98
+#define INTEN_A 0xA4
+#define VOL 0xA8
+#define DELTA 0xAC
+#define MISCINT 0xB0
+#define START_B 0xB4
+#define STOP_B 0xB8
+#define INT_B 0xD8
+#define INTEN_B 0xDC
+#define CIR 0xA0
+#define CSO 0xE0
+#define LBA 0xE4
+#define ESO 0xE8
+#define FMC 0xEC
+#define TCTRL 0xF0
+#define EBUF1 0xF4
+#define EBUF2 0xF8
+
+#define DMAR0 0x00
+#define DMAR4 0x04
+#define DMAR6 0x06
+#define DMAR11 0x0b
+#define DMAR15 0x0f
+#define SBDELTA 0xac
+#define SBBL 0xc0
+#define SBCTL 0xc4
+
+/* SIS 7018 Define */
+#define SECONDARY_ID 0x00004000
+#define PCMOUT 0x00010000
+#define SURROUT 0x00020000
+#define CENTEROUT 0x00040000
+#define LFEOUT 0x00080000
+#define LINE1OUT 0x00100000
+#define LINE2OUT 0x00200000
+#define GPIOOUT 0x00400000
+#define CHANNEL_PB 0x0000
+#define CHANNEL_SPC_PB 0x4000
+#define CHANNEL_REC 0x8000
+#define CHANNEL_REC_PB 0xc000
+#define MODEM_LINE1 0x0000
+#define MODEM_LINE2 0x0400
+#define PCM_LR 0x0800
+#define HSET 0x0c00
+#define I2S_LR 0x1000
+#define CENTER_LFE 0x1400
+#define SURR_LR 0x1800
+#define SPDIF_LR 0x1c00
+#define MIC 0x1400
+#define MONO_LEFT 0x0000
+#define MONO_RIGHT 0x0100
+#define MONO_MIX 0x0200
+#define SRC_ENABLE 0x0080
+
+#define INTR(bank) ((bank==1)?INT_A:INT_B)
+#define INTEN(bank) ((bank==1)?INTEN_A:INTEN_B)
+#define STOP(bank) ((bank==1)?STOP_A:STOP_B)
+#define START(bank) ((bank==1)?START_A:START_B)
+
+/* ALI5451 definitions */
+#define ALI_5451_V02 0x2
+#define SIS_7018_V02 0x2
+
+
+extern int trident_mpu_ioaddr;
+
+#define MAX_PORTC 8
+
+typedef struct trident_portc
+{
+ int speed, bits, channels;
+ int open_mode;
+ int audio_enabled;
+ int trigger_bits;
+ int audiodev;
+ int port_type;
+#define DF_PCM 0
+#define DF_SPDIF 1
+ unsigned char play_chan, play_intr_chan;
+ unsigned char rec_chan, rec_intr_chan;
+ int pbank, rbank;
+#define BANK_A 1
+#define BANK_B 0
+} trident_portc;
+
+
+typedef struct trident_devc
+{
+ oss_device_t *osdev;
+ char *chip_name;
+ int chip_type;
+ int revision;
+ int irq;
+
+#ifdef MEM_MAPPED_REGISTERS
+ unsigned int bar1addr;
+ char *bar1virt;
+#else /* */
+ oss_native_word base;
+#endif /* */
+ volatile unsigned char intr_mask;
+ oss_mutex_t mutex;
+ oss_mutex_t low_mutex;
+
+ /* Legacy */
+ int mpu_base, mpu_irq, mpu_attached;
+
+ /* Mixer parameters */
+ ac97_devc ac97devc;
+ int mixer_dev;
+
+ /* Audio parameters */
+ trident_portc portc[MAX_PORTC];
+ int audio_opened;
+ unsigned char sb_dma_flags;
+} trident_devc;
+
+#ifndef MEM_MAPPED_REGISTERS
+/* I/O mapped register access */
+#define READL(o,a) INL(o, devc->base+(a))
+#define READW(o,a) INW(o, devc->base+(a))
+#define READB(o,a) INB(o, devc->base+(a))
+#define WRITEL(o,d,a) OUTL(o, d, devc->base+(a))
+#define WRITEW(o,d,a) OUTW(o, d, devc->base+(a))
+#define WRITEB(o,d,a) OUTB(o, d, devc->base+(a))
+#else
+/* Mem mapped I/O registers */
+#define READL(o,a) *(volatile unsigned int*)(devc->bar1virt+(a))
+#define READW(o,a) *(volatile unsigned short*)(devc->bar1virt+(a))
+#define READB(o,a) *(volatile unsigned char*)(devc->bar1virt+(a))
+#define WRITEL(o,d,a) *(volatile unsigned int*)(devc->bar1virt+(a))=d
+#define WRITEW(o,d,a) *(volatile unsigned short*)(devc->bar1virt+(a))=d
+#define WRITEB(o,d,a) *(volatile unsigned char*)(devc->bar1virt+(a))=d
+#endif
+
+static int
+ac97_read (void *devc_, int addr)
+{
+ trident_devc *devc = devc_;
+ int t, ret;
+ oss_native_word data, reg = 0;
+ oss_native_word rmask = 0;
+ oss_native_word dmask = 0;
+ oss_native_word wport = 0;
+ oss_native_word rport = 0;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+
+ t = 20000;
+ switch (devc->chip_type)
+ {
+ case TRIDENT_4DWAVEDX_ID:
+ case SIS_7018_ID:
+ wport = 0x40;
+ rport = 0x44;
+ if (devc->revision == SIS_7018_V02)
+ {
+ rport = 0x40;
+ }
+ rmask = 0x00008000;
+ dmask = 0x00008000;
+ break;
+ case TRIDENT_4DWAVENX_ID:
+ wport = 0x44;
+ rport = 0x48;
+ rmask = 0x00000800;
+ dmask = 0x00000400;
+ break;
+ case ALI_5451_ID:
+ wport = 0x40;
+ rport = 0x44;
+ rmask = 0x00008000;
+ dmask = 0x00008000;
+ if (devc->revision == ALI_5451_V02)
+ {
+ rport = 0x40;
+ }
+ break;
+ default:
+ {
+ static int already_done = 0;
+ if (!already_done)
+ cmn_err (CE_WARN, "Unknown codec interface\n");
+ already_done = 1;
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return OSS_EIO;
+ }
+ }
+
+ /* Check to make sure No Writes are pending */
+ reg = READL (devc->osdev, wport);
+ while ((reg & dmask) && --t)
+ reg = READL (devc->osdev, wport); /* re-read status/data */
+
+ /* Check to make sure No reads are pending */
+ reg = READL (devc->osdev, rport);
+ while ((reg & dmask) && --t)
+ reg = READL (devc->osdev, rport); /* re-read status/data */
+ data = addr | rmask;
+ WRITEL (devc->osdev, data, rport); /* select register for reading */
+ reg = READL (devc->osdev, rport); /* read status/data */
+
+ t = 20000;
+
+ while ((reg & dmask) && --t)
+ { /* busy reading */
+ reg = READL (devc->osdev, rport); /* re-read status/data */
+ }
+ if (t)
+ ret = (reg >> 16);
+ else
+ {
+ DDB (cmn_err (CE_WARN, "AC97 mixer read timed out\n"));
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return OSS_EIO;
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return ret;
+}
+
+static int
+ac97_write (void *devc_, int addr, int data)
+{
+ trident_devc *devc = devc_;
+ int t;
+ oss_native_word wport = 0;
+ oss_native_word reg = 0;
+ unsigned int wmask = 0;
+ unsigned int dmask = 0;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+
+ t = 2000;
+
+ switch (devc->chip_type)
+ {
+ case TRIDENT_4DWAVEDX_ID:
+ case SIS_7018_ID:
+ wport = 0x40;
+ wmask = 0x00008000;
+ dmask = 0x00008000;
+ break;
+ case TRIDENT_4DWAVENX_ID:
+ wport = 0x44;
+ wmask = 0x00000800;
+ dmask = 0x00000800;
+ break;
+ case ALI_5451_ID:
+ wport = 0x40;
+ wmask = 0x00008000;
+ dmask = 0x00008000;
+ if (devc->revision == 2)
+ dmask = 0x100;
+ break;
+ default:
+ {
+ static int already_done = 0;
+ if (!already_done)
+ cmn_err (CE_WARN, "Unknown codec interface\n");
+ already_done = 1;
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return OSS_EIO;
+ }
+ }
+
+ /* Check to make sure No Writes are pending */
+ reg = READL (devc->osdev, wport);
+ while ((reg & wmask) && --t)
+ {
+ reg = READL (devc->osdev, wport); /* re-read status/data */
+ }
+ if (t)
+ {
+ reg = data << 16;
+ reg |= (addr & 0xff);
+ reg |= wmask | dmask;
+ WRITEL (devc->osdev, reg, wport);
+ }
+ else
+ DDB (cmn_err (CE_WARN, "AC97 mixer write timed out\n"));
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+}
+
+static int
+tridentintr (oss_device_t * osdev)
+{
+ trident_devc *devc = (trident_devc *) osdev->devc;
+ int i;
+ oss_native_word status, intstat;
+ int serviced = 0;
+ dmap_t *dmapin;
+ oss_native_word flag;
+
+
+ /* check the interrupt status register MISCINT */
+ status = READL (devc->osdev, MISCINT);
+ serviced = 1;
+
+ /* If it is a not 4DWave Playback or MIDI interrupt then return */
+ if (!(status & (0x00000020 | 0x00000008 | 0x4)))
+ {
+ WRITEL (devc->osdev, status, MISCINT);
+ return 1;
+ }
+
+#ifdef OBSOLETED_STUFF
+/*
+ * This device has "ISA style" MIDI and FM subsystems. Such devices don't
+ * use PCI config space for the I/O ports and interrupts. Instead the driver
+ * needs to allocate proper resources itself. This functionality is no longer
+ * possible. For this reason the MIDI and FM parts are not accessible.
+ */
+ /* Check if this is a MIDI interrupt */
+ if (status & 0x00000008)
+ {
+ uart401intr (INT_HANDLER_CALL (devc->irq));
+ serviced = 1;
+ }
+#endif
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flag);
+
+ /* Check to see if this is our channel */
+ if (status & 0x20)
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ trident_portc *portc = &devc->portc[i];
+ serviced = 1;
+
+ /* Handle Playback Interrupts */
+ intstat = READL (devc->osdev, INTR (portc->pbank));
+
+ if (intstat & 1L << portc->play_intr_chan)
+ {
+ WRITEL (devc->osdev, READL (devc->osdev, INTR (portc->pbank))
+ | 1L << portc->play_intr_chan, INTR (portc->pbank));
+ oss_audio_outputintr (portc->audiodev, 1);
+ }
+
+ /* Handle Record Interrupts */
+ intstat = READL (devc->osdev, INTR (portc->rbank));
+
+ if (intstat & 1L << portc->rec_intr_chan)
+ {
+ WRITEL (devc->osdev, READL (devc->osdev, INTR (portc->rbank))
+ | 1L << portc->rec_intr_chan, INTR (portc->rbank));
+ if ((devc->chip_type == ALI_5451_ID)
+ || (devc->chip_type == SIS_7018_ID))
+ {
+ unsigned int ptr;
+ int i;
+ int chan;
+ dmapin = audio_engines[portc->audiodev]->dmap_in;
+
+ if (portc->rbank == BANK_A)
+ chan = portc->rec_chan;
+
+ else
+ chan = portc->rec_chan + 32;
+ WRITEL (devc->osdev,
+ (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR);
+ ptr = READW (devc->osdev, CSO + 2);
+ ptr++;
+ ptr *= portc->channels * (portc->bits / 8);
+
+ if (dmapin->bytes_in_use == 0 || dmapin->fragment_size == 0)
+ {
+ cmn_err(CE_WARN, "bytes_in_use=%d, fragment_size=%d\n", dmapin->bytes_in_use, dmapin->fragment_size);
+ continue;
+ }
+ ptr %= dmapin->bytes_in_use;
+ ptr /= dmapin->fragment_size;
+ i = 0;
+ while (dmap_get_qtail (dmapin) != ptr && i++ < dmapin->nfrags)
+ oss_audio_inputintr (portc->audiodev, 0);
+ }
+ else
+ oss_audio_inputintr (portc->audiodev, 0);
+ }
+ }
+
+ if (status & 0x4)
+ {
+ serviced = 1;
+ READB (devc->osdev, 0x1E); /*SB ESP ack */
+ READB (devc->osdev, 0x1F); /*SB IRQ ack */
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flag);
+ return serviced;
+}
+
+
+/***************************************************************************/
+static int
+trident_audio_set_rate (int dev, int arg)
+{
+ trident_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
+trident_audio_set_channels (int dev, short arg)
+{
+ trident_portc *portc = audio_engines[dev]->portc;
+ if ((arg != 1) && (arg != 2))
+ return portc->channels;
+ portc->channels = arg;
+ return portc->channels;
+}
+
+static unsigned int
+trident_audio_set_format (int dev, unsigned int arg)
+{
+ trident_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
+trident_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void trident_audio_trigger (int dev, int state);
+
+static void
+trident_audio_reset (int dev)
+{
+ trident_audio_trigger (dev, 0);
+}
+
+static void
+trident_audio_reset_input (int dev)
+{
+ trident_portc *portc = audio_engines[dev]->portc;
+ trident_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+trident_audio_reset_output (int dev)
+{
+ trident_portc *portc = audio_engines[dev]->portc;
+ trident_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+/*ARGSUSED*/
+static int
+trident_audio_open (int dev, int mode, int open_flags)
+{
+ trident_portc *portc = audio_engines[dev]->portc;
+ trident_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
+trident_audio_close (int dev, int mode)
+{
+ trident_portc *portc = audio_engines[dev]->portc;
+ trident_audio_reset (dev);
+ portc->open_mode = 0;
+ portc->audio_enabled &= ~mode;
+}
+
+/*ARGSUSED*/
+static void
+trident_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ trident_portc *portc = audio_engines[dev]->portc;
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+}
+
+/*ARGSUSED*/
+static void
+trident_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ trident_portc *portc = audio_engines[dev]->portc;
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+}
+
+static void
+trident_audio_trigger (int dev, int state)
+{
+ trident_devc *devc = audio_engines[dev]->devc;
+ trident_portc *portc = audio_engines[dev]->portc;
+ oss_native_word ainten = 0, playstop = 0, recstop = 0;
+ 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))
+ {
+
+ /* Enable the channel interrupt */
+ WRITEL (devc->osdev, READL (devc->osdev, INTEN (portc->pbank))
+ | (1L << portc->play_intr_chan), INTEN (portc->pbank));
+
+ /* start playback and playback interrupt channels */
+ WRITEL (devc->osdev, READL (devc->osdev, START (portc->pbank))
+ | (1L << portc->play_chan)
+ | (1L << portc->play_intr_chan), START (portc->pbank));
+ 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;
+
+ /* disable playback interrupt channel */
+ ainten = READL (devc->osdev, INTEN (portc->pbank));
+ ainten = ainten & ~(1L << portc->play_intr_chan);
+ WRITEL (devc->osdev, ainten, INTEN (portc->pbank));
+
+ /* stop playback and playback interrupt channels */
+ playstop = ((1L << portc->play_chan) |
+ (1L << portc->play_intr_chan));
+ WRITEL (devc->osdev, playstop, STOP (portc->pbank));
+ }
+ }
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT)
+ && !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ /* Enable the channel interrupts */
+ WRITEL (devc->osdev, READL (devc->osdev, INTEN (portc->rbank))
+ | (1L << portc->rec_intr_chan), INTEN (portc->rbank));
+
+ if ((devc->chip_type != ALI_5451_ID)
+ && (devc->chip_type != SIS_7018_ID))
+ {
+ /* set recording flags */
+ WRITEB (devc->osdev, devc->sb_dma_flags, SBCTL);
+
+ /* start record interrupt channel */
+ WRITEL (devc->osdev, 1L << portc->rec_intr_chan,
+ START (portc->rbank));
+ }
+ else
+ {
+ WRITEL (devc->osdev,
+ READL (devc->osdev,
+ START (portc->rbank)) | (1L << portc->
+ rec_chan) | (1L <<
+ portc->
+ rec_intr_chan),
+ START (portc->rbank));
+ }
+ 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;
+ if ((devc->chip_type != ALI_5451_ID)
+ && (devc->chip_type != SIS_7018_ID))
+ {
+ /* disable the record interrrupt enable channel */
+ ainten = READL (devc->osdev, INTEN (portc->rbank));
+ ainten = ainten & ~(1L << portc->rec_intr_chan);
+ WRITEL (devc->osdev, ainten, INTEN (portc->rbank));
+
+ /* stop DMA controller */
+ WRITEB (devc->osdev, 0x00, SBCTL);
+
+ /* stop record interrupt channel */
+ recstop |= 1L << portc->rec_intr_chan;
+ WRITEL (devc->osdev, recstop, STOP (portc->rbank));
+ }
+ else
+ {
+ /* disable rec interrupt channel */
+ ainten = READL (devc->osdev, INTEN (portc->rbank));
+ ainten = ainten & ~(1L << portc->rec_intr_chan);
+ WRITEL (devc->osdev, ainten, INTEN (portc->rbank));
+
+ /* stop playback and playback interrupt channels */
+ recstop = ((1L << portc->rec_chan) |
+ (1L << portc->rec_intr_chan));
+ WRITEL (devc->osdev, recstop, STOP (portc->rbank));
+ }
+
+ /* ack any interrupts on INT_A */
+ recstop = READL (devc->osdev, INTR (portc->rbank));
+ recstop |= 1L << portc->rec_intr_chan;
+ WRITEL (devc->osdev, recstop, INTR (portc->rbank));
+ }
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+trident_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ trident_devc *devc = audio_engines[dev]->devc;
+ trident_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ oss_native_word bufsize, delta, eso;
+ oss_native_word temp;
+ unsigned int attribute = 0;
+ unsigned char bValue;
+ unsigned short wValue;
+ oss_native_word dwValue;
+ unsigned short wRecCodecSamples;
+ int chan;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ {
+ bValue = READB (devc->osdev, 0x48); /*enable AC97 ADC */
+ WRITEB (devc->osdev, bValue | 0x48, 0x48); /*disable rec intr */
+ }
+
+ if (devc->chip_type == TRIDENT_4DWAVENX_ID)
+ {
+ wValue = READW (devc->osdev, MISCINT);
+ WRITEW (devc->osdev, wValue | 0x1000, MISCINT);
+ }
+
+ if ((devc->chip_type != ALI_5451_ID) && (devc->chip_type != SIS_7018_ID))
+ {
+ /* Initialize legacy recording */
+ WRITEB (devc->osdev, 0, DMAR15);
+ bValue = READB (devc->osdev, DMAR11) & 0x03;
+ WRITEB (devc->osdev, bValue | 0x54, DMAR11);
+
+ /* Set base address in DMAR0-DMAR3 */
+ WRITEL (devc->osdev, dmap->dmabuf_phys, DMAR0);
+
+ /* Set count in DMAR4/DMAR5 */
+ eso = dmap->bytes_in_use;
+ WRITEW (devc->osdev, eso - 1, DMAR4);
+
+ /* Set speed in SBDelta */
+ dwValue = (48000 << 12) / portc->speed;
+ WRITEW (devc->osdev, (unsigned short) dwValue, SBDELTA);
+
+ /*Set channel interrupt blk length */
+ if ((portc->bits == 16) || (portc->channels == 2))
+ wRecCodecSamples = (unsigned short) ((eso >> 1) - 1);
+ else
+ wRecCodecSamples = (unsigned short) (eso - 1);
+
+ dwValue = wRecCodecSamples << 16;
+ dwValue |= wRecCodecSamples & 0x0000ffff;
+ WRITEL (devc->osdev, dwValue, SBBL);
+
+ /*set format */
+ devc->sb_dma_flags |= 0x19;
+ if (portc->bits == 16)
+ devc->sb_dma_flags |= 0xA0;
+ if (portc->channels == 2)
+ devc->sb_dma_flags |= 0x40;
+ }
+ else
+ {
+#if !defined(sparc)
+ if (devc->chip_type == ALI_5451_ID)
+ WRITEL (devc->osdev,
+ READL (devc->osdev, 0xd4) | ((unsigned int) 1 << 31), 0xd4);
+#endif
+
+ /* for SiS 7018: Rec with PCM_LR, MONO_MIX, SRC_EN */
+ if (devc->chip_type == SIS_7018_ID)
+ attribute = 0x8880; /* PCM->IN */
+
+ delta = (48000 << 12) / portc->speed;
+ bufsize = dmap->bytes_in_use;
+
+ if (portc->bits == 16)
+ bufsize /= 2;
+ if (portc->channels == 2)
+ bufsize /= 2;
+ bufsize--;
+
+ if (portc->rbank == BANK_A)
+ chan = portc->rec_chan;
+ else
+ chan = portc->rec_chan + 32;
+
+ WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); /*select current chan */
+
+ /* Now set the buffer address pointer */
+ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA);
+
+ /* Set the Size and Sampling Rate */
+ eso = (bufsize << 16) | (delta & 0x0000FFFF);
+ WRITEL (devc->osdev, eso, ESO);
+ temp = 0x80000000; /*enable gvsel */
+ if (portc->channels == 2)
+ temp |= 0x00004000; /*stereo/mono */
+ if (portc->bits == 16)
+ temp |= 0x0000A000; /*unsigned 8/signed 16bit */
+ temp |= 0x00001000; /*enable loop */
+ temp |= 0x003F0000; /*set pan to off */
+ temp |= 0x00000FFF; /*set vol to off */
+ WRITEL (devc->osdev, temp, TCTRL); /*set format */
+ WRITEL (devc->osdev, attribute << 16, FMC);
+ WRITEL (devc->osdev, 0, EBUF1);
+ WRITEL (devc->osdev, 0, EBUF2);
+ /* WRITEL (devc->osdev, 0xFFFF, VOL); *//*Music/WaveVol set to max */
+ WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */
+ }
+
+/* Now prepare the Record Interrupt Channel */
+ delta = (48000 << 12) / portc->speed;
+ bufsize = dmap->fragment_size;
+ if (portc->bits == 16)
+ bufsize /= 2;
+ if (portc->channels == 2)
+ bufsize /= 2;
+ bufsize--;
+
+ if (portc->rbank == BANK_A)
+ chan = portc->rec_intr_chan;
+ else
+ chan = portc->rec_intr_chan + 32;
+
+ WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR);
+
+ /* Now set the buffer address pointer */
+ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA);
+
+ /* Set the Size and Sampling Rate */
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID))
+ {
+ eso = (bufsize << 16) | (delta & 0x0000FFFF);
+ WRITEL (devc->osdev, eso, ESO);
+ }
+ else
+ {
+ eso = ((delta << 16) & 0xff000000) | (bufsize & 0x00ffffff);
+ WRITEL (devc->osdev, eso, ESO);
+ }
+
+ temp = 0x80000000; /*enable gvsel */
+ if (portc->channels == 2)
+ temp |= 0x00004000; /*stereo/mono */
+ if (portc->bits == 16)
+ temp |= 0x0000A000; /*8 unsigned/16bit signed data */
+ temp |= 0x00001000; /*enable loop */
+ temp |= 0x003F0000; /*set pan to off */
+ temp |= 0x00000FFF; /*set vol to off */
+ WRITEL (devc->osdev, temp, TCTRL); /*set format */
+
+ /* for the record interrupt channel note that no attribute should be
+ * set for SIS7018
+ */
+ WRITEL (devc->osdev, 0, FMC);
+ WRITEL (devc->osdev, 0, EBUF1);
+ WRITEL (devc->osdev, 0, EBUF2);
+ /* WRITEL (devc->osdev, 0xFFFF, VOL); *//*Music/WaveVol set to min */
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID))
+ {
+ WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */
+ }
+ else
+ {
+ WRITEL (devc->osdev, (delta << 24) | (0 & 0x00ffffff), CSO);
+ }
+
+ /* Now set the mode on portc to record */
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+trident_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ trident_devc *devc = audio_engines[dev]->devc;
+ trident_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+ oss_native_word bufsize, delta, eso;
+ oss_native_word temp;
+ int chan, spdif_rate, spdif_chan;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ /* prepare the main playback channel */
+ delta = (portc->speed * 4096) / 48000;
+ bufsize = dmap->bytes_in_use;
+
+ if (portc->bits == 16)
+ bufsize /= 2;
+ if (portc->channels == 2)
+ bufsize /= 2;
+ bufsize--;
+
+ if ((devc->chip_type == ALI_5451_ID) && (devc->revision == ALI_5451_V02)
+ && (portc->port_type == DF_SPDIF))
+
+ {
+ ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_AUDIO, SNDCTL_MIX_WRITE,
+ 0);
+ portc->play_chan = 15;
+ portc->play_intr_chan = 14;
+
+ if (portc->bits == AFMT_AC3)
+
+ {
+ ac97_spdif_setup (devc->mixer_dev, portc->speed, portc->bits);
+ portc->bits = 16;
+ portc->channels = 2;
+ portc->speed = 48000;
+ /* set non-pcm (AC3) mode on SPDIF control */
+ WRITEW (devc->osdev, (READW (devc->osdev, 0x70) & ~0x2) | 0x2,
+ 0x70);
+ }
+ else
+ /* set pcm mode on SPDIF control */
+ WRITEW (devc->osdev, READW (devc->osdev, 0x70) & ~0x2, 0x70);
+
+ /*set up the sampling rate */
+ switch (portc->speed)
+ {
+ case 44100:
+ spdif_rate = 0;
+ break;
+ case 32000:
+ spdif_rate = 0x300;
+ break;
+ case 48000:
+ default:
+ spdif_rate = 0x200;
+ break;
+ }
+ spdif_chan = READB (devc->osdev, 0x74) & 0xbf; /*select spdif_out */
+ spdif_chan |= 0x80; /*select right */
+ WRITEB (devc->osdev, spdif_chan, 0x74);
+ WRITEB (devc->osdev, spdif_rate | 0x20, 0x72);
+ spdif_chan &= (~0x80); /*select left */
+ WRITEB (devc->osdev, spdif_chan, 0x74);
+ WRITEB (devc->osdev, spdif_rate | 0x10, 0x72);
+ }
+
+ if (portc->pbank == BANK_A)
+ chan = portc->play_chan;
+ else
+ chan = portc->play_chan + 32;
+
+ WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); /*select current chan */
+
+ /* Now set the buffer address pointer */
+ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA);
+
+ /* Set the Size and Sampling Rate */
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID))
+ {
+ eso = (bufsize << 16) | (delta & 0x0000FFFF);
+ WRITEL (devc->osdev, eso, ESO);
+ }
+ else
+ {
+ eso = ((delta << 16) & 0xff000000) | (bufsize & 0x00ffffff);
+ WRITEL (devc->osdev, eso, ESO);
+ }
+ temp = 0x80000000; /*enable gvsel */
+ if (portc->channels == 2)
+ temp |= 0x00004000; /*stereo/mono */
+ if (portc->bits == 16)
+ temp |= 0x0000A000; /*8 unsigned/16bit signed data */
+ temp |= 0x00001000; /*enable loop */
+ WRITEL (devc->osdev, temp, TCTRL); /*set format */
+ WRITEL (devc->osdev, 0, FMC);
+ WRITEL (devc->osdev, 0, EBUF1);
+ WRITEL (devc->osdev, 0, EBUF2);
+ WRITEL (devc->osdev, 0, VOL); /*Music/WaveVol set to max */
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID))
+ {
+ WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */
+ }
+ else
+ {
+ WRITEL (devc->osdev, (delta << 24) | (0 & 0x00ffffff), CSO);
+ }
+
+ /* Now prepare the playback interrupt channel */
+ delta = (portc->speed * 4096) / 48000;
+ bufsize = dmap->fragment_size;
+ if (portc->bits == 16)
+ bufsize /= 2;
+ if (portc->channels == 2)
+ bufsize /= 2;
+ bufsize--;
+
+ if (portc->pbank == BANK_A)
+ chan = portc->play_intr_chan;
+ else
+ chan = portc->play_intr_chan + 32;
+
+ WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); /*select current chan */
+
+ /* Now set the buffer address pointer */
+ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA);
+
+ /* Set the Size and Sampling Rate */
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID))
+ {
+ eso = (bufsize << 16) | (delta & 0x0000FFFF);
+ WRITEL (devc->osdev, eso, ESO);
+ }
+ else
+ {
+ eso = ((delta << 16) & 0xff000000) | (bufsize & 0x00ffffff);
+ WRITEL (devc->osdev, eso, ESO);
+ }
+
+ temp = 0x80000000; /*enable gvsel */
+ if (portc->channels == 2)
+ temp |= 0x00004000; /*stereo/mono */
+ if (portc->bits == 16)
+ temp |= 0x0000A000; /*8 unsigned/16bit signed data */
+ temp |= 0x00001000; /*enable loop */
+ temp |= 0xFFF; /* no vol */
+ temp |= 0x003F0000; /*set volume to off */
+ WRITEL (devc->osdev, temp, TCTRL); /*set format */
+ WRITEL (devc->osdev, 0, FMC);
+ WRITEL (devc->osdev, 0, EBUF1);
+ WRITEL (devc->osdev, 0, EBUF2);
+ WRITEL (devc->osdev, 0, VOL); /*Music/WaveVol set to max */
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID))
+ {
+ WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */
+ }
+ else
+ {
+ WRITEL (devc->osdev, (delta << 24) | (0 & 0x00ffffff), CSO);
+ }
+
+ /* Now set the mode on portc to playback */
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+#if 0
+static int
+trident_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ trident_devc *devc = audio_engines[dev]->devc;
+ trident_portc *portc = audio_engines[dev]->portc;
+ oss_native_word flags;
+ int ptr = 0;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (direction == PCM_ENABLE_INPUT)
+ {
+ WRITEB (devc->osdev,
+ (READB (devc->osdev, CIR) & ~0x7F) | portc->rec_chan, CIR);
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID)
+ || (devc->chip_type == ALI_5451_ID))
+ ptr = READW (devc->osdev, CSO + 2);
+ else /* ID_4DWAVE_NX */
+ {
+ ptr = READW (devc->osdev, SBBL) & 0xFFFF;
+ if (portc->channels > 1)
+ ptr >>= 1;
+ if (ptr > 0)
+ ptr = dmap->bytes_in_use - ptr;
+ }
+ }
+
+ if (direction == PCM_ENABLE_OUTPUT)
+ {
+ WRITEB (devc->osdev,
+ (READB (devc->osdev, CIR) & ~0x7F) | portc->play_chan, CIR);
+ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ || (devc->chip_type == SIS_7018_ID)
+ || (devc->chip_type == ALI_5451_ID))
+ {
+ ptr = READW (devc->osdev, CSO + 2);
+ }
+ else
+ {
+ ptr = READL (devc->osdev, CSO) & 0x00ffffff;
+ }
+ if (ptr > dmap->bytes_in_use)
+ ptr = 0;
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return ptr;
+}
+#endif
+
+#if !defined(sparc)
+static int
+trident_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+ int err;
+
+ if (dmap->dmabuf != NULL)
+ return 0;
+
+ if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0)
+ {
+ cmn_err (CE_WARN, "Failed to allocate a DMA buffer.\n");
+ return err;
+ }
+ if (dmap->dmabuf_phys & 0x80000000)
+
+ {
+ cmn_err (CE_WARN, "Got DMA buffer address beyond 2G limit.\n");
+ oss_free_dmabuf (dev, dmap);
+ dmap->dmabuf = NULL;
+ return OSS_ENOSPC;
+ }
+ return 0;
+}
+#endif
+
+static audiodrv_t trident_audio_driver = {
+ trident_audio_open,
+ trident_audio_close,
+ trident_audio_output_block,
+ trident_audio_start_input,
+ trident_audio_ioctl,
+ trident_audio_prepare_for_input,
+ trident_audio_prepare_for_output,
+ trident_audio_reset,
+ NULL,
+ NULL,
+ trident_audio_reset_input,
+ trident_audio_reset_output,
+ trident_audio_trigger,
+ trident_audio_set_rate,
+ trident_audio_set_format,
+ trident_audio_set_channels,
+ NULL,
+ NULL,
+ NULL, /* trident_check_input, */
+ NULL, /* trident_check_output, */
+#if !defined(sparc)
+ trident_alloc_buffer,
+#else
+ NULL,
+#endif
+ NULL, /* trident_free_buffer, */
+ NULL,
+ NULL,
+ NULL /* trident_get_buffer_pointer */
+};
+
+
+#ifdef OBSOLETED_STUFF
+static void
+attach_mpu (trident_devc * devc)
+{
+ struct address_info hw_config;
+ hw_config.io_base = devc->mpu_base;
+ hw_config.irq = -devc->irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = devc->chip_name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osdev = devc->osdev;
+#ifdef CREATE_OSP
+ CREATE_OSP (hw_config.osdev);
+#endif
+ hw_config.card_subtype = 0;
+ if (!probe_uart401 (&hw_config))
+ {
+ cmn_err (CE_WARN, "MPU-401 was not detected\n");
+ return;
+ }
+ devc->mpu_attached = 1;
+ attach_uart401 (&hw_config);
+}
+
+static void
+unload_mpu (trident_devc * devc)
+{
+ struct address_info hw_config;
+ hw_config.io_base = devc->mpu_base;
+ hw_config.irq = devc->mpu_irq;
+ hw_config.dma = -1;
+ hw_config.dma2 = -1;
+ hw_config.always_detect = 0;
+ hw_config.name = devc->chip_name;
+ hw_config.driver_use_1 = 0;
+ hw_config.driver_use_2 = 0;
+ hw_config.osdev = devc->osdev;
+#ifdef CREATE_OSP
+ CREATE_OSP (hw_config.osdev);
+#endif /* */
+ hw_config.card_subtype = 0;
+ devc->mpu_attached = 0;
+ unload_uart401 (&hw_config);
+}
+#endif
+
+static int
+init_trident (trident_devc * devc)
+{
+ int my_mixer, i;
+ oss_device_t *osdev = devc->osdev;
+ unsigned char legacy;
+ oss_native_word global_control;
+ int adev;
+ unsigned int dwVal;
+ int first_dev = 0;
+
+/*
+ * Legacy I/O setup
+ */
+ devc->mpu_attached = 0;
+ legacy = 0xA0; /* Enable MPU, GP, FM */
+ switch (devc->mpu_base)
+ {
+ case 0x330:
+ legacy |= 0x00;
+ break;
+ case 0x300:
+ legacy |= 0x40;
+ break;
+ default:
+ devc->mpu_base = 0;
+ }
+ pci_write_config_byte (osdev, 0x44, legacy); /*setup legacy devs */
+
+ /* pci_write_config_byte (osdev, 0x45, 0x12); *//*setup DDMA */
+ /* pci_write_config_byte (osdev, 0x46, 0x02); *//*setup device r/w */
+
+ /* Now reset AC97 and unmute the codec channels */
+ if (devc->chip_type == TRIDENT_4DWAVEDX_ID)
+ {
+ WRITEB (devc->osdev, 0x09, 0x40); /*set ddma */
+ WRITEL (devc->osdev, 0x2, 0x48); /*enable the AC97 */
+ }
+
+ if (devc->chip_type == SIS_7018_ID)
+ {
+ WRITEL (devc->osdev, 0, 0x4c);
+ WRITEL (devc->osdev, 0xF0000, 0x48); /* enable ac97 link */
+ }
+
+ if (devc->chip_type == TRIDENT_4DWAVENX_ID)
+ {
+ WRITEL (devc->osdev, 0x2, 0x40);
+ /* Setup 48KHz S/PDIF output on 4DWave NX */
+ WRITEL (devc->osdev, INL (devc->osdev, SP_STAT) | 4, SP_STAT);
+ WRITEB (devc->osdev, 0x38, SP_CSO + 3);
+ }
+
+ if (devc->chip_type == ALI_5451_ID)
+ {
+#ifndef sparc
+ pci_read_config_dword (osdev, 0x7c, &dwVal);
+ pci_write_config_dword (osdev, 0x7c, dwVal | 0x08000000);
+ oss_udelay (5000);
+ pci_read_config_dword (osdev, 0x7c, &dwVal);
+ pci_write_config_dword (osdev, 0x7c, dwVal & 0xf7ffffff);
+ oss_udelay (5000);
+ pci_read_config_dword (osdev, 0x44, &dwVal);
+ pci_write_config_dword (osdev, 0x44, dwVal | 0x000c0000);
+ oss_udelay (500);
+ pci_read_config_dword (osdev, 0x44, &dwVal);
+ pci_write_config_dword (osdev, 0x44, dwVal & 0xfffbffff);
+#endif
+ /* enable full 32bit and disable DDMA */
+ pci_write_config_dword (osdev, 0x40, dwVal & 0x00000008);
+ oss_udelay (5000);
+ }
+
+ my_mixer = ac97_install (&devc->ac97devc, devc->chip_name, ac97_read,
+ ac97_write, devc, devc->osdev);
+ if (my_mixer >= 0)
+ {
+ devc->mixer_dev = my_mixer;
+ mixer_devs[my_mixer]->priority = 9;
+ }
+ else
+ return 0;
+
+#ifdef OBSOLETED_STUFF
+ if (devc->mpu_base > 0)
+ attach_mpu (devc);
+#endif
+
+ for (i = 0; i < 2; i++)
+ {
+ char tmp_name[1024];
+ trident_portc *portc = &devc->portc[i];
+ int caps = ADEV_AUTOMODE /* | ADEV_HWMIX */ ;
+ int porttype = DF_PCM;
+
+ sprintf (tmp_name, "%s (rev %d)", devc->chip_name, devc->revision);
+ if (i == 0)
+ {
+ caps |= ADEV_DUPLEX;
+ }
+ else
+ {
+ caps |= ADEV_NOINPUT;
+ if (i > 1)
+ caps |= ADEV_SHADOW;
+ }
+
+#if 0
+ /* This will not work any more */
+ if ((devc->chip_type == ALI_5451_ID)
+ && (devc->revision == ALI_5451_V02) && (i == 7))
+ {
+ /* need to set SPDIF output in PCI southbridge chip 0x1533 */
+ while ((osdev = (pci_find_class (0x601 << 8, osdev))))
+ {
+ unsigned short vendorid, deviceid;
+ unsigned char temp;
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendorid);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &deviceid);
+ if (vendorid != 0x10b9 || deviceid != 0x1533)
+ continue;
+ pci_read_config_byte (osdev, 0x61, &temp);
+ temp |= 0x40;
+ pci_write_config_byte (osdev, 0x61, temp);
+ pci_read_config_byte (osdev, 0x7d, &temp);
+ temp |= 0x01;
+ pci_write_config_byte (osdev, 0x7d, temp);
+ pci_read_config_byte (osdev, 0x7e, &temp);
+ temp &= (~0x20);
+ temp |= 0x10;
+ pci_write_config_byte (osdev, 0x7e, temp);
+
+ /* SPDIF Magic!!! */
+ pci_read_config_byte (osdev, 0x63, &temp);
+ temp |= 0x3; /* enable SPDIF OUT bit0=out bit1=in */
+ pci_write_config_byte (osdev, 0x63, temp);
+
+ /* SPDIF Output in SERIAL Config reg */
+ temp = READL (devc->osdev, 0x48);
+ WRITEB (devc->osdev, temp | 0x20, 0x48);
+ temp = READL (devc->osdev, 0x74);
+ WRITEB (devc->osdev, temp & 0xbf, 0x74);
+
+ /* Enable SPDIF on play_channel 15 and setup as SPDIFOUT */
+ WRITEW (devc->osdev, READW (devc->osdev, 0xd4) | 0x8000, 0xd4);
+ WRITEW (devc->osdev, READW (devc->osdev, 0xd4) & ~0x0400, 0xd4);
+
+ porttype = DF_SPDIF;
+ caps |= ADEV_SPECIAL | ADEV_FIXEDRATE;
+ sprintf (tmp_name, "%s (S/PDIF Output)", devc->chip_name);
+ }
+ }
+#endif
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &trident_audio_driver,
+ sizeof (audiodrv_t),
+ caps,
+ AFMT_U8 | AFMT_S16_LE | AFMT_AC3,
+ devc, -1)) < 0)
+
+ {
+ adev = -1;
+ return 0;
+ }
+ else
+ {
+ if (i == 0)
+ first_dev = adev;
+ audio_engines[adev]->portc = portc;
+ audio_engines[adev]->mixer_dev = my_mixer;
+ audio_engines[adev]->rate_source = first_dev;
+ audio_engines[adev]->min_rate = 8000;
+ audio_engines[adev]->max_rate = 48000;
+ audio_engines[adev]->caps |= PCM_CAP_FREERATE;
+
+#if !defined(sparc)
+ /*
+ * Only 31 bit memory addresses are supported except under Sparc
+ * where the on-board audio chip supports the top half of 32 bit PCI
+ * address space.
+ */
+ audio_engines[adev]->dmabuf_maxaddr = MEMLIMIT_31BITS;
+#endif
+ portc->audiodev = adev;
+ portc->open_mode = 0;
+ portc->audio_enabled = 0;
+
+ if (audio_engines[adev]->flags & ADEV_FIXEDRATE)
+ {
+ audio_engines[adev]->fixed_rate = 48000;
+ audio_engines[adev]->min_rate = 48000;
+ audio_engines[adev]->max_rate = 48000;
+ }
+
+ portc->port_type = porttype;
+ portc->play_chan = 3 + (2 * i);
+ portc->play_intr_chan = 2 + (2 * i);
+ portc->pbank = BANK_A;
+ portc->rec_chan = 1;
+ portc->rec_intr_chan = 0;
+ portc->rbank = BANK_A;
+
+ if (devc->chip_type == ALI_5451_ID)
+ {
+ portc->rec_chan = 31;
+ portc->rec_intr_chan = 30;
+ portc->rbank = BANK_A;
+ }
+ if (devc->chip_type == SIS_7018_ID)
+ {
+ portc->pbank = BANK_B;
+ portc->rec_chan = 1;
+ portc->rec_intr_chan = 0;
+ portc->rbank = BANK_B;
+ WRITEL (devc->osdev, READL (devc->osdev, CIR) | 0x10000, CIR);
+ }
+#ifdef CONFIG_OSS_VMIX
+ if (i == 0)
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+ }
+
+ devc->audio_opened = 0;
+ /* set the global control and enable end interrupt condition */
+ global_control = READL (devc->osdev, CIR);
+ global_control |= (1 << 12); /* control end intr */
+ WRITEL (devc->osdev, global_control, CIR);
+ }
+ return 1;
+}
+
+int
+oss_trident_attach (oss_device_t * osdev)
+{
+ unsigned char pci_irq_line, pci_revision /*, pci_latency */ ;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ trident_devc *devc;
+
+ DDB (cmn_err (CE_WARN, "Entered Trident 4DWAVE probe routine\n"));
+
+ oss_pci_byteswap (osdev, 1);
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+ if ((vendor != TRIDENT_VENDOR_ID && vendor != ALI_VENDOR_ID
+ && vendor != SIS_VENDOR_ID) ||
+ (device != TRIDENT_4DWAVEDX_ID && device != TRIDENT_4DWAVENX_ID &&
+ device != TRIDENT_4DWAVECX_ID && device != ALI_5451_ID &&
+ device != SIS_7018_ID))
+
+ 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_ioaddr == 0)
+ {
+ cmn_err (CE_WARN, "I/O address not assigned by BIOS.\n");
+ return 0;
+ }
+
+ if (pci_irq_line == 0)
+ {
+ cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d).\n", pci_irq_line);
+ 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->revision = pci_revision;
+ devc->irq = pci_irq_line;
+
+ switch (device)
+ {
+ case TRIDENT_4DWAVEDX_ID:
+ devc->chip_name = "Trident 4DWAVEDX";
+ devc->chip_type = TRIDENT_4DWAVEDX_ID;
+ break;
+ case TRIDENT_4DWAVENX_ID:
+ devc->chip_name = "Trident 4DWAVENX";
+ devc->chip_type = TRIDENT_4DWAVENX_ID;
+ break;
+ case TRIDENT_4DWAVECX_ID:
+ devc->chip_name = "Trident 4DWAVECX";
+ devc->chip_type = TRIDENT_4DWAVECX_ID;
+ break;
+ case ALI_5451_ID:
+ devc->chip_name = "ALI M5451";
+ devc->chip_type = ALI_5451_ID;
+ break;
+ case SIS_7018_ID:
+ devc->chip_name = "SiS 7018";
+ devc->chip_type = SIS_7018_ID;
+ break;
+ default:
+ devc->chip_name = "Trident 4DWAVE";
+ devc->chip_type = TRIDENT_4DWAVEDX_ID;
+ }
+
+#ifdef MEM_MAPPED_REGISTERS
+ pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_1, &devc->bar1addr);
+ devc->bar1virt =
+ (char *) MAP_PCI_MEM (devc->osdev, 1, devc->bar1addr, 1024 * 1024);
+ pci_command |= PCI_COMMAND_MEMORY;
+#else
+ devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr);
+
+ /* Remove I/O space marker in bit 0. */
+ devc->base &= ~0x03;
+ pci_command |= PCI_COMMAND_IO;
+#endif
+ devc->mpu_base = trident_mpu_ioaddr;
+ devc->mpu_irq = devc->irq;
+
+ /* activate the device */
+ pci_command |= PCI_COMMAND_MASTER;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+ MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1);
+
+ oss_register_device (osdev, devc->chip_name);
+
+ if (oss_register_interrupts (devc->osdev, 0, tridentintr, NULL) < 0)
+ {
+ cmn_err (CE_WARN, "Can't allocate IRQ%d\n", pci_irq_line);
+ return 0;
+ }
+
+ return init_trident (devc); /*Detected */
+}
+
+
+
+int
+oss_trident_detach (oss_device_t * osdev)
+{
+ trident_devc *devc = (trident_devc *) osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ WRITEL (devc->osdev, 0, CIR);
+ if (devc->chip_type == TRIDENT_4DWAVENX_ID)
+ WRITEL (devc->osdev, 0x0, SP_CSO); /*disable S/PDIF on NX */
+
+#ifdef OBSOLETED_STUFF
+ if (devc->mpu_attached)
+ {
+ unload_mpu (devc);
+ devc->mpu_attached = 0;
+ }
+#endif
+
+ oss_unregister_interrupts (devc->osdev);
+
+ MUTEX_CLEANUP (devc->mutex);
+ MUTEX_CLEANUP (devc->low_mutex);
+
+#ifdef MEM_MAPPED_REGISTERS
+ UNMAP_PCI_MEM(osdev, 1, devc->bar1addr, devc->bar1virt, 1024x1024);
+#else
+ UNMAP_PCI_IOADDR (devc->osdev, 0);
+#endif
+
+ oss_unregister_device (devc->osdev);
+ return 1;
+}
diff --git a/kernel/drv/oss_trident/oss_trident.man b/kernel/drv/oss_trident/oss_trident.man
new file mode 100644
index 0000000..d70929d
--- /dev/null
+++ b/kernel/drv/oss_trident/oss_trident.man
@@ -0,0 +1,27 @@
+NAME
+oss_trident - SiS7018, 4Dwave, ALIM5451 audio driver.
+
+DESCRIPTION
+Open Sound System driver for Trident 4DWave DX/NX, SiS7018 and ALI5451 audio
+controllers.
+
+Trident device characteristics:
+ o 8/16 bit playback/record
+ o mono/stereo playback/recording
+ o 8KHz to 48Khz sample rate.
+ o Upto 8 hardware channels to mixing audio streams
+
+OPTIONS
+trident_mpu_ioaddr=<xxx>
+Set the MPU I/O address. Refer to the driver.conf file for valid addresses.
+
+LIMITATIONS
+o Due to PCI addressing limitations any add-on cards based on these chips
+will not work under Sparc. The only exception is the ALI5451 chip that is
+used on the main boards on many Sparc based systems.
+
+FILES
+CONFIGFILEPATH/oss_trident.conf Device configuration file
+
+AUTHOR
+4Front Technologies