summaryrefslogtreecommitdiff
path: root/attic/drv/oss_als4k
diff options
context:
space:
mode:
Diffstat (limited to 'attic/drv/oss_als4k')
-rw-r--r--attic/drv/oss_als4k/.config1
-rw-r--r--attic/drv/oss_als4k/.devices1
-rw-r--r--attic/drv/oss_als4k/.name1
-rw-r--r--attic/drv/oss_als4k/.params11
-rwxr-xr-xattic/drv/oss_als4k/oss_als4k.c1153
-rw-r--r--attic/drv/oss_als4k/oss_als4k.man23
6 files changed, 1190 insertions, 0 deletions
diff --git a/attic/drv/oss_als4k/.config b/attic/drv/oss_als4k/.config
new file mode 100644
index 0000000..5280084
--- /dev/null
+++ b/attic/drv/oss_als4k/.config
@@ -0,0 +1 @@
+platform=i86pc
diff --git a/attic/drv/oss_als4k/.devices b/attic/drv/oss_als4k/.devices
new file mode 100644
index 0000000..99d4af6
--- /dev/null
+++ b/attic/drv/oss_als4k/.devices
@@ -0,0 +1 @@
+oss_als4k pci4005,4000 Avance Logic ALS4000
diff --git a/attic/drv/oss_als4k/.name b/attic/drv/oss_als4k/.name
new file mode 100644
index 0000000..df310ec
--- /dev/null
+++ b/attic/drv/oss_als4k/.name
@@ -0,0 +1 @@
+Avance Logic ALS4000
diff --git a/attic/drv/oss_als4k/.params b/attic/drv/oss_als4k/.params
new file mode 100644
index 0000000..337be40
--- /dev/null
+++ b/attic/drv/oss_als4k/.params
@@ -0,0 +1,11 @@
+int als4000_mpu_ioaddr=0;
+/*
+ * MPU 401 I/O Address
+ * Values: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360, 0x370 Default: 0x330
+ */
+
+int als4000_mpu_irq=0;
+/*
+ * MPU 401 IRQ
+ * Values: 5, 7, 9, 10, 11 Default: 0
+ */
diff --git a/attic/drv/oss_als4k/oss_als4k.c b/attic/drv/oss_als4k/oss_als4k.c
new file mode 100755
index 0000000..f5f9222
--- /dev/null
+++ b/attic/drv/oss_als4k/oss_als4k.c
@@ -0,0 +1,1153 @@
+/*
+ * Purpose: Driver for ALS ALS4000 PCI audio controller.
+ */
+/*
+ *
+ * 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_als4k_cfg.h"
+#include "oss_pci.h"
+
+#define ALS_VENDOR_ID 0x4005
+#define ALS_4000 0x4000
+
+#define DSP_RESET (devc->base + 0x16)
+#define DSP_READ (devc->base + 0x1A)
+#define DSP_WRITE (devc->base + 0x1C)
+#define DSP_COMMAND (devc->base + 0x1C)
+#define DSP_STATUS (devc->base + 0x1C)
+#define DSP_DATA_AVAIL (devc->base + 0x1E)
+#define DSP_DATA_AVL16 (devc->base + 0x1F)
+#define MIXER_ADDR (devc->base + 0x14)
+#define MIXER_DATA (devc->base + 0x15)
+
+#define DSP_CMD_SPKON 0xD1
+#define DSP_CMD_SPKOFF 0xD3
+
+#define ALS_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+#define ALS_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
+ SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
+ SOUND_MASK_IGAIN | SOUND_MASK_CD | \
+ SOUND_MASK_VOLUME)
+#define ALS4000_RECORDING_DEVICES (ALS_RECORDING_DEVICES)
+#define ALS4000_MIXER_DEVICES (ALS_MIXER_DEVICES|SOUND_MASK_LINE2|SOUND_MASK_SPEAKER|SOUND_MASK_RECLEV)
+
+#define LEFT_CHN 0
+#define RIGHT_CHN 1
+
+#define VOC_VOL 0x04
+#define MIC_VOL 0x0A
+#define MIC_MIX 0x0A
+#define RECORD_SRC 0x0C
+#define IN_FILTER 0x0C
+#define OUT_FILTER 0x0E
+#define MASTER_VOL 0x22
+#define FM_VOL 0x26
+#define CD_VOL 0x28
+#define LINE_VOL 0x2E
+#define IRQ_NR 0x80
+#define DMA_NR 0x81
+#define IRQ_STAT 0x82
+#define OPSW 0x3c
+
+static int default_levels[32] = {
+ 0x5a5a, /* Master Volume */
+ 0x4b4b, /* Bass */
+ 0x4b4b, /* Treble */
+ 0x4b4b, /* FM */
+ 0x4b4b, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x2020, /* Mic */
+ 0x4b4b, /* CD */
+ 0x0000, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b, /* Recording level */
+ 0x4b4b, /* Input gain */
+ 0x4b4b, /* Output gain */
+ 0x4040, /* Line1 */
+ 0x4040, /* Line2 */
+ 0x1515 /* Line3 */
+};
+
+#define MAX_PORTC 2
+
+typedef struct als4000_portc
+{
+ int speed, bits, channels;
+ int open_mode;
+ int audio_enabled;
+ int trigger_bits;
+ int audiodev;
+}
+als4000_portc;
+
+typedef struct als4000_devc
+{
+ oss_device_t *osdev;
+ oss_native_word base, mpu_base;
+ int sb_attached, mpu_attached, fm_attached;
+ int irq, mpu_irq;
+ int play_rate_lock, rec_rate_lock;
+
+ char *chip_name;
+ oss_mutex_t mutex;
+ oss_mutex_t low_mutex;
+
+ /* Audio parameters */
+ int open_mode;
+ als4000_portc portc[MAX_PORTC];
+ oss_native_word srcode;
+
+ /* Mixer parameters */
+ int mixer_dev;
+ int *levels;
+ int recmask;
+}
+als4000_devc;
+
+extern int als4000_mpu_ioaddr;
+extern int als4000_mpu_irq;
+
+void
+als4000_gcr_writel (als4000_devc * devc, unsigned char index,
+ oss_native_word data)
+{
+ OUTB (devc->osdev, index, devc->base + 0x0c);
+ OUTL (devc->osdev, data, devc->base + 0x08);
+}
+
+
+oss_native_word
+als4000_gcr_readl (als4000_devc * devc, unsigned char index)
+{
+ oss_native_word bufl;
+
+ OUTB (devc->osdev, index, devc->base + 0x0c);
+ bufl = INL (devc->osdev, devc->base + 0x08);
+ return (bufl);
+}
+
+static void
+als4000_write (als4000_devc * devc, unsigned char val)
+{
+ int i;
+
+ for (i = 0; i < 100000; i++)
+ if ((INB (devc->osdev, DSP_STATUS) & 0x80) == 0)
+ {
+ OUTB (devc->osdev, val, DSP_COMMAND);
+ return;
+ }
+
+ cmn_err (CE_WARN, "Write Command(%x) timed out.\n", val);
+ return;
+}
+
+static unsigned char
+als4000_read (als4000_devc * devc)
+{
+ int i;
+
+ for (i = 0; i < 100000; i++)
+ if (INB (devc->osdev, DSP_DATA_AVAIL) & 0x80)
+ {
+ return INB (devc->osdev, DSP_READ);
+ }
+
+ cmn_err (CE_WARN, "Read Command timed out.\n");
+ return 0xff;
+}
+
+static void
+als4000_setmixer (als4000_devc * devc, unsigned char port,
+ unsigned char value)
+{
+ OUTB (devc->osdev, port, MIXER_ADDR);
+ oss_udelay (20);
+ OUTB (devc->osdev, value, MIXER_DATA);
+ //oss_udelay (20);
+}
+
+static unsigned int
+als4000_getmixer (als4000_devc * devc, unsigned char port)
+{
+ unsigned char val;
+
+ OUTB (devc->osdev, port, MIXER_ADDR);
+ oss_udelay (20);
+ val = INB (devc->osdev, MIXER_DATA);
+ //oss_udelay (20);
+ return val;
+}
+
+struct mixer_def
+{
+ unsigned int regno:8;
+ unsigned int bitoffs:4;
+ unsigned int nbits:4;
+};
+
+typedef struct mixer_def mixer_tab[32][2];
+typedef struct mixer_def mixer_ent;
+
+#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
+ {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
+
+static mixer_tab als4000_mix = {
+ MIX_ENT (SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
+ MIX_ENT (SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
+ MIX_ENT (SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
+ MIX_ENT (SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
+ MIX_ENT (SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
+ MIX_ENT (SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
+ MIX_ENT (SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
+ MIX_ENT (SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
+ MIX_ENT (SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
+ MIX_ENT (SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0),
+ MIX_ENT (SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+ MIX_ENT (SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2),
+ MIX_ENT (SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
+ MIX_ENT (SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
+};
+
+/*ARGSUSED*/
+static void
+change_bits (als4000_devc * devc, unsigned char *regval, int dev, int chn,
+ int newval)
+{
+ unsigned char mask;
+ int shift;
+
+ mask = (1 << als4000_mix[dev][chn].nbits) - 1;
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale */
+
+ shift =
+ als4000_mix[dev][chn].bitoffs - als4000_mix[dev][LEFT_CHN].nbits + 1;
+
+ *regval &= ~(mask << shift); /* Mask out previous value */
+ *regval |= (newval & mask) << shift; /* Set the new value */
+}
+
+static int set_recmask (als4000_devc * devc, int mask);
+
+static int
+als4000_mixer_set (als4000_devc * devc, int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+
+ int regoffs;
+ unsigned char val;
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return OSS_EINVAL;
+
+ if (!(ALS4000_MIXER_DEVICES & (1 << dev))) /*
+ * Not supported
+ */
+ return OSS_EINVAL;
+
+ regoffs = als4000_mix[dev][LEFT_CHN].regno;
+
+ if (regoffs == 0)
+ return OSS_EINVAL;
+
+ val = als4000_getmixer (devc, regoffs);
+ change_bits (devc, &val, dev, LEFT_CHN, left);
+
+ devc->levels[dev] = left | (left << 8);
+
+ if (als4000_mix[dev][RIGHT_CHN].regno != regoffs) /*
+ * Change register
+ */
+ {
+ als4000_setmixer (devc, regoffs, val); /*
+ * Save the old one
+ */
+ regoffs = als4000_mix[dev][RIGHT_CHN].regno;
+
+ if (regoffs == 0)
+ return left | (left << 8); /*
+ * Just left channel present
+ */
+
+ val = als4000_getmixer (devc, regoffs); /*
+ * Read the new one
+ */
+ }
+
+ change_bits (devc, &val, dev, RIGHT_CHN, right);
+
+ als4000_setmixer (devc, regoffs, val);
+
+ devc->levels[dev] = left | (right << 8);
+ return left | (right << 8);
+}
+
+/*ARGSUSED*/
+static int
+als4000_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
+{
+ als4000_devc *devc = mixer_devs[dev]->devc;
+ int val;
+
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (IOC_IS_OUTPUT (cmd))
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ val = *arg;
+ return *arg = set_recmask (devc, val);
+ break;
+
+ default:
+
+ val = *arg;
+ return *arg = als4000_mixer_set (devc, cmd & 0xff, val);
+ }
+ else
+ switch (cmd & 0xff)
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return *arg = devc->recmask;
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return *arg = ALS4000_MIXER_DEVICES;
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return *arg = ALS4000_MIXER_DEVICES &
+ ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return *arg = ALS4000_RECORDING_DEVICES;
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return *arg = SOUND_CAP_EXCL_INPUT;
+ break;
+
+
+ default:
+ return *arg = devc->levels[cmd & 0x1f];
+ }
+ }
+ else
+ return OSS_EINVAL;
+}
+
+
+static void
+set_recsrc (als4000_devc * devc, int src)
+{
+ als4000_setmixer (devc, RECORD_SRC,
+ (als4000_getmixer (devc, RECORD_SRC) & ~7) | (src & 0x7));
+}
+
+static int
+set_recmask (als4000_devc * devc, int mask)
+{
+ int devmask = mask & ALS4000_RECORDING_DEVICES;
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE && devmask != SOUND_MASK_CD)
+ { /*
+ * More than one devices selected. Drop the *
+ * previous selection
+ */
+ devmask &= ~devc->recmask;
+ }
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE && devmask != SOUND_MASK_CD)
+ { /*
+ * More than one devices selected. Default to
+ * * mic
+ */
+ devmask = SOUND_MASK_MIC;
+ }
+
+
+ if (devmask ^ devc->recmask) /*
+ * Input source changed
+ */
+ {
+ switch (devmask)
+ {
+
+ case SOUND_MASK_MIC:
+ set_recsrc (devc, 0);
+ break;
+
+ case SOUND_MASK_LINE:
+ set_recsrc (devc, 6);
+ break;
+
+ case SOUND_MASK_CD:
+ set_recsrc (devc, 2);
+ break;
+
+
+ default:
+ set_recsrc (devc, 0);
+ }
+ }
+
+ devc->recmask = devmask;
+ return devc->recmask;
+}
+
+static void
+als4000_mixer_reset (als4000_devc * devc)
+{
+ char name[32];
+ int i;
+
+ sprintf (name, "ALS4000");
+
+ devc->levels = load_mixer_volumes (name, default_levels, 1);
+ devc->recmask = 0;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ als4000_mixer_set (devc, i, devc->levels[i]);
+
+ set_recmask (devc, SOUND_MASK_MIC);
+}
+
+
+static mixer_driver_t als4000_mixer_driver = {
+ als4000_mixer_ioctl
+};
+
+static int
+als4000intr (oss_device_t * osdev)
+{
+ als4000_devc *devc = (als4000_devc *) osdev->devc;
+ unsigned int status, i;
+ unsigned char mxstat;
+ int serviced = 0;
+
+
+ status = INB (devc->osdev, devc->base + 0x0e);
+
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ als4000_portc *portc = &devc->portc[i];
+
+ if ((status & 0x80) && (portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ serviced = 1;
+ oss_audio_outputintr (portc->audiodev, 1);
+ }
+ if ((status & 0x40) && (portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ serviced = 1;
+ oss_audio_inputintr (portc->audiodev, 0);
+ }
+ }
+
+ OUTB (devc->osdev, status, devc->base + 0x0e); /* acknowledge interrupt */
+
+#if 0
+ if (status & 0x10)
+ {
+ serviced = 1;
+ uart401intr (INT_HANDLER_CALL (devc->mpu_irq));
+ }
+#endif
+ mxstat = als4000_getmixer (devc, 0x82);
+ switch (mxstat)
+ {
+ case 0x1:
+ INB (devc->osdev, devc->base + 0x1e);
+ break;
+ case 0x2:
+ INB (devc->osdev, devc->base + 0x1f);
+ break;
+ case 0x4:
+ INB (devc->osdev, devc->base + 0x30);
+ break;
+ case 0x20:
+ INB (devc->osdev, devc->base + 0x16);
+ break;
+ }
+ return serviced;
+}
+
+static int
+als4000_audio_set_rate (int dev, int arg)
+{
+ als4000_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
+als4000_audio_set_channels (int dev, short arg)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+
+ if ((arg != 1) && (arg != 2))
+ return portc->channels;
+ portc->channels = arg;
+
+ return portc->channels;
+}
+
+static unsigned int
+als4000_audio_set_format (int dev, unsigned int arg)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->bits;
+
+ if (!(arg & (AFMT_U8 | AFMT_S16_LE)))
+ return portc->bits;
+ portc->bits = arg;
+
+ return portc->bits;
+}
+
+static void
+als4000_speed (als4000_devc * devc, int speed)
+{
+ if (!(devc->play_rate_lock | devc->rec_rate_lock))
+ {
+ als4000_write (devc, 0x41);
+ als4000_write (devc, (speed >> 8) & 0xFF);
+ als4000_write (devc, speed & 0xFF);
+ }
+}
+
+/*ARGSUSED*/
+static int
+als4000_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static int
+als4000_reset (als4000_devc * devc)
+{
+ int i;
+ unsigned char byte;
+ DDB (cmn_err (CE_WARN, "Entered als4000_reset()\n"));
+
+ OUTB (devc->osdev, 1, DSP_RESET);
+ oss_udelay (20);
+ OUTB (devc->osdev, 0, DSP_RESET);
+
+ byte = als4000_read (devc);
+ for (i = 0; i < 10000; i++)
+ if (byte != 0xAA)
+ {
+ DDB (cmn_err (CE_WARN, "No response to RESET\n"));
+ return 0; /* Sorry */
+ }
+
+ DDB (cmn_err (CE_WARN, "als4000_reset() OK\n"));
+ return 1;
+}
+
+static void als4000_audio_trigger (int dev, int state);
+
+static void
+als4000_audio_reset (int dev)
+{
+ als4000_audio_trigger (dev, 0);
+}
+
+static void
+als4000_audio_reset_input (int dev)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+ als4000_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+als4000_audio_reset_output (int dev)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+ als4000_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+/*ARGSUSED*/
+static int
+als4000_audio_open (int dev, int mode, int open_flags)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+ als4000_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;
+ }
+
+ if (devc->open_mode & mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ devc->open_mode |= mode;
+
+ portc->open_mode = mode;
+ portc->audio_enabled &= ~mode;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+static void
+als4000_audio_close (int dev, int mode)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+ als4000_devc *devc = audio_engines[dev]->devc;
+
+ als4000_audio_reset (dev);
+ als4000_reset (devc);
+ portc->open_mode = 0;
+ devc->open_mode &= ~mode;
+ portc->audio_enabled &= ~mode;
+}
+
+/*ARGSUSED*/
+static void
+als4000_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+}
+
+/*ARGSUSED*/
+static void
+als4000_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+}
+
+static void
+als4000_audio_trigger (int dev, int state)
+{
+ als4000_portc *portc = audio_engines[dev]->portc;
+ als4000_devc *devc = audio_engines[dev]->devc;
+ 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->bits == AFMT_U8)
+ als4000_write (devc, 0xd6); /*start 8bit dma */
+ else
+ als4000_write (devc, 0xd4); /*start 16bit dma */
+ devc->play_rate_lock = 1;
+ 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->bits == AFMT_U8)
+ als4000_write (devc, 0xd0); /*exit 8bit dma */
+ else
+ als4000_write (devc, 0xd5); /*exit 16bit dma */
+ devc->play_rate_lock = 0;
+ }
+ }
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ /* start recording */
+ als4000_setmixer (devc, 0xde,
+ als4000_getmixer (devc, 0xde) | 0x80);
+ devc->rec_rate_lock = 1;
+ 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;
+ /* stop recording */
+ als4000_setmixer (devc, 0xde,
+ als4000_getmixer (devc, 0xde) & ~0x80);
+ devc->rec_rate_lock = 0;
+ }
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+als4000_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ als4000_devc *devc = audio_engines[dev]->devc;
+ als4000_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ oss_native_word flags;
+ int cnt, bits;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ /* set speed */
+ als4000_speed (devc, portc->speed);
+
+ /* set mode */
+ if (portc->bits == AFMT_S16_LE)
+ bits = 0x10; /*signed 16bit */
+ else
+ bits = 0x04; /*unsigned 8bit */
+ if (portc->channels == 2)
+ bits |= 0x20; /*stereo */
+ als4000_setmixer (devc, 0xde, bits);
+
+ /* set size and buffer address */
+ als4000_gcr_writel (devc, 0xa2, dmap->dmabuf_phys);
+ als4000_gcr_writel (devc, 0xa3, dmap->bytes_in_use - 1);
+
+ /* set tranfersize */
+ cnt = dmap->fragment_size;
+ if (portc->bits == AFMT_S16_LE)
+ cnt >>= 1;
+ cnt--;
+
+ als4000_setmixer (devc, 0xdc, cnt & 0xFF);
+ als4000_setmixer (devc, 0xdd, (cnt >> 8) & 0xFF);
+
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+als4000_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ als4000_devc *devc = audio_engines[dev]->devc;
+ als4000_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+ oss_native_word flags;
+ int cnt;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ /* set speed */
+ als4000_speed (devc, portc->speed);
+
+ /* set mode */
+ als4000_write (devc, (portc->bits == AFMT_S16_LE ? 0xb6 : 0xc6));
+ als4000_write (devc, ((portc->channels == 2 ? 0x20 : 0) +
+ (portc->bits == AFMT_S16_LE ? 0x10 : 0)));
+
+
+ /* set size and buffer address */
+ als4000_gcr_writel (devc, 0x91, dmap->dmabuf_phys);
+ als4000_gcr_writel (devc, 0x92, (dmap->bytes_in_use - 1) | 0x00180000);
+
+ /* set transfer size */
+ cnt = dmap->fragment_size;
+ if (portc->bits == AFMT_S16_LE)
+ cnt >>= 1;
+ cnt--;
+
+ als4000_write (devc, cnt & 0xff); /*length low */
+ als4000_write (devc, (cnt >> 8)); /*length high */
+
+ als4000_write (devc, 0xd1); /* turn on speaker */
+
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+static int
+als4000_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ als4000_devc *devc = audio_engines[dev]->devc;
+ unsigned int ptr;
+
+ if (direction == PCM_ENABLE_OUTPUT)
+ ptr = als4000_gcr_readl (devc, 0xa0);
+ else
+ ptr = als4000_gcr_readl (devc, 0xa4);
+
+ return (ptr - dmap->dmabuf_phys) % dmap->bytes_in_use;
+}
+
+
+static audiodrv_t als4000_audio_driver = {
+ als4000_audio_open,
+ als4000_audio_close,
+ als4000_audio_output_block,
+ als4000_audio_start_input,
+ als4000_audio_ioctl,
+ als4000_audio_prepare_for_input,
+ als4000_audio_prepare_for_output,
+ als4000_audio_reset,
+ NULL,
+ NULL,
+ als4000_audio_reset_input,
+ als4000_audio_reset_output,
+ als4000_audio_trigger,
+ als4000_audio_set_rate,
+ als4000_audio_set_format,
+ als4000_audio_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* als4000_alloc_buffer, */
+ NULL, /* als4000_free_buffer, */
+ NULL,
+ NULL,
+ als4000_get_buffer_pointer
+};
+
+#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.
+ */
+static void
+attach_fm (als4000_devc * devc)
+{
+ if (!opl3_detect (0x388, devc->osdev))
+ return;
+ opl3_init (0x388, devc->osdev);
+ devc->fm_attached = 1;
+}
+
+static void
+attach_mpu (als4000_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 = "Avance Logic ALS4000 MPU";
+ 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 (als4000_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 = "Avance Logic ALS4000 MPU";
+ 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
+
+void
+setup_als4000 (als4000_devc * devc)
+{
+ unsigned char byte, i;
+ oss_native_word dwTemp;
+
+ dwTemp = ((oss_native_word) 0x200 << 16) | (0x388);
+ dwTemp |= 0x00010000;
+ dwTemp |= 0x00000001;
+ als4000_gcr_writel (devc, 0xa8, dwTemp);
+ dwTemp = ((oss_native_word) devc->mpu_base << 16);
+ dwTemp |= 0x00010000;
+ als4000_gcr_writel (devc, 0xa9, dwTemp);
+
+ byte = als4000_getmixer (devc, 0xc0);
+ als4000_setmixer (devc, 0xc0, byte | 0x80); /*disable M80/M81 write protect */
+ als4000_setmixer (devc, 0x81, 0x01);
+ als4000_setmixer (devc, 0xc0, (byte & 0x7F)); /*set M80/M81 write protect */
+
+ dwTemp = als4000_gcr_readl (devc, 0x8c);
+ als4000_gcr_writel (devc, 0x8c, dwTemp | 0x00028000); /*enable INTA */
+
+ for (i = 0x91; i < 0xa7; i++)
+ als4000_gcr_writel (devc, i, 0x00);
+ dwTemp = als4000_gcr_readl (devc, 0x99);
+ als4000_gcr_writel (devc, 0x99, dwTemp | 0x100);
+}
+
+
+static int
+init_als4000 (als4000_devc * devc)
+{
+ int my_mixer;
+ int first_dev = 0;
+ unsigned char byte;
+ int i;
+ int adev;
+
+ devc->mpu_attached = devc->fm_attached = 0;
+
+ setup_als4000 (devc);
+
+#ifdef OBSOLETED_STUFF
+ attach_fm (devc);
+ if (devc->mpu_base > 0)
+ attach_mpu (devc);
+#endif
+
+ /* check ESP */
+ als4000_write (devc, 0xE4);
+ als4000_write (devc, 0xaa);
+ als4000_write (devc, 0xE8);
+ byte = als4000_read (devc);
+ if (byte != 0xaa)
+ {
+ DDB (cmn_err (CE_WARN, "ESP Not OK\n"));
+ return 0;
+ }
+
+ /* reset the DSP */
+ als4000_reset (devc);
+
+ if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "Avance Logic ALS4000",
+ &als4000_mixer_driver,
+ sizeof (mixer_driver_t), devc)) < 0)
+ {
+ return 0;
+ }
+ als4000_mixer_reset (devc);
+
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ char tmp_name[100];
+ als4000_portc *portc = &devc->portc[i];
+ int caps = ADEV_AUTOMODE;
+
+ if (i == 0)
+ {
+ strcpy (tmp_name, devc->chip_name);
+ caps |= ADEV_DUPLEX;
+ }
+ else
+ {
+ strcpy (tmp_name, devc->chip_name);
+ caps |= ADEV_DUPLEX | ADEV_SHADOW;
+ }
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &als4000_audio_driver,
+ sizeof (audiodrv_t),
+ caps,
+ AFMT_U8 | AFMT_S16_LE, 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]->mixer_dev = my_mixer;
+ audio_engines[adev]->min_rate = 5000;
+ audio_engines[adev]->max_rate = 48000;
+ audio_engines[adev]->dmabuf_maxaddr = MEMLIMIT_ISA;
+ portc->open_mode = 0;
+ portc->audiodev = adev;
+ portc->audio_enabled = 0;
+#ifdef CONFIG_OSS_VMIX
+ if (i == 0)
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+ }
+ }
+ return 1;
+}
+
+int
+oss_als4k_attach (oss_device_t * osdev)
+{
+
+ unsigned char pci_irq_line, pci_revision;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ int err;
+ als4000_devc *devc;
+
+ DDB (cmn_err (CE_WARN, "Entered ALS ALS4000 probe routine\n"));
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision);
+ pci_read_config_word (osdev, PCI_COMMAND, &pci_command);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+ pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line);
+ pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr);
+
+ if (vendor != ALS_VENDOR_ID || device != ALS_4000)
+ return 0;
+
+ DDB (cmn_err (CE_WARN, "ALS4000 I/O base %04x\n", 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->irq = pci_irq_line;
+ devc->chip_name = "Avance Logic ALS4000";
+ devc->mpu_base = als4000_mpu_ioaddr;
+ devc->mpu_irq = als4000_mpu_irq;
+
+
+ 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);
+
+ 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 ((err = oss_register_interrupts (devc->osdev, 0, als4000intr, NULL)) < 0)
+ {
+ cmn_err (CE_WARN, "Error installing interrupt handler: %x\n", err);
+ return 0;
+ }
+
+ return init_als4000 (devc); /* Detected */
+}
+
+
+int
+oss_als4k_detach (oss_device_t * osdev)
+{
+ als4000_devc *devc = (als4000_devc *) osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ als4000_gcr_writel (devc, 0x8c, 0x0);
+
+#ifdef OBSOLETED_STUFF
+ if (devc->mpu_attached)
+ unload_mpu (devc);
+#endif
+
+ 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;
+}
diff --git a/attic/drv/oss_als4k/oss_als4k.man b/attic/drv/oss_als4k/oss_als4k.man
new file mode 100644
index 0000000..ace289f
--- /dev/null
+++ b/attic/drv/oss_als4k/oss_als4k.man
@@ -0,0 +1,23 @@
+NAME
+oss_als4k - ALS4000 audio driver.
+
+DESCRIPTION
+Open Sound System driver for Avance Logic ALS4000 based soundcards.
+ALS4000 characteristics:
+ o 8/16 bit
+ o mono/stereo
+ o 8Khz - 48Khz sampling rates
+ o FM Synthesis
+ o MIDI UART401
+
+The ALS4000 has a Sound Blaster compatible mixer device.
+
+OPTIONS
+None
+
+FILES
+CONFIGFILEPATH/oss_als4k.conf
+
+AUTHOR
+4Front Technologies
+