summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_via97/oss_via97.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_via97/oss_via97.c
downloadoss4-upstream/4.2-build2006.tar.gz
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/drv/oss_via97/oss_via97.c')
-rw-r--r--kernel/drv/oss_via97/oss_via97.c944
1 files changed, 944 insertions, 0 deletions
diff --git a/kernel/drv/oss_via97/oss_via97.c b/kernel/drv/oss_via97/oss_via97.c
new file mode 100644
index 0000000..ce79841
--- /dev/null
+++ b/kernel/drv/oss_via97/oss_via97.c
@@ -0,0 +1,944 @@
+/*
+ * uPurpose: Driver for the VIA VT82C686A AC97 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_via97_cfg.h"
+#include "oss_pci.h"
+#include "ac97.h"
+
+#define VIA_VENDOR_ID 0x1106
+#define VIA_82C686 0x3058
+
+#define CODEC_TIMEOUT_COUNT 500
+#define AC97CODEC 0x80 /*Access AC97 Codec */
+#define IN_CMD 0x01000000 /*busy in sending */
+#define STA_VALID 0x02000000 /*1:status data is valid */
+#define CODEC_RD 0x00800000 /*Read CODEC status */
+#define CODEC_INDEX 0x007F0000 /*Index of command register to access */
+#define CODEC_DATA 0x0000FFFF /*AC97 status register data */
+
+typedef struct
+{
+ unsigned int phaddr;
+ unsigned int flags;
+}
+SGD_entry;
+
+typedef struct
+{
+ int open_mode;
+ int speed, bits, channels;
+ int audiodev;
+ int audio_enabled;
+ int trigger_bits;
+}
+via97_portc;
+
+typedef struct via97_devc
+{
+ oss_device_t *osdev;
+ oss_native_word base;
+ int irq;
+ int open_mode;
+ char *chip_name;
+/*
+ * Mixer
+ */
+ ac97_devc ac97devc;
+
+/*
+ * MIDI
+ */
+
+ int mpu_base;
+ int mpu_attached;
+
+ /* Audio parameters */
+ via97_portc portc[2];
+ oss_mutex_t mutex; /* For normal locking */
+ oss_mutex_t low_mutex; /* For low level routines */
+
+ /* Memory allocation and scatter/gather info */
+#define BUFFER_SIZE (128*1024)
+
+/* NOTE! Check SGD_ALLOC if changing SGD_SIZE */
+#define MIN_BLOCK 64
+#define SGD_SIZE (BUFFER_SIZE/MIN_BLOCK)
+#define SGD_TOTAL_SIZE (2*SGD_SIZE)
+#define SGD_ALLOC (SGD_TOTAL_SIZE*8)
+
+ SGD_entry *SGD_table;
+ oss_native_word SGD_table_phys;
+ int play_sgd_ptr, rec_sgd_ptr;
+ oss_dma_handle_t sgd_dma_handle;
+ unsigned int play_sgd_phys, rec_sgd_phys;
+}
+via97_devc;
+
+static int
+ac97_read (void *devc_, int wIndex)
+{
+ oss_native_word dwWriteValue = 0, dwTmpValue;
+ unsigned int i = 0;
+ oss_native_word flags;
+ via97_devc *devc = devc_;
+
+ /* Index has only 7 bit */
+ if (wIndex > 0x7F)
+ return 0;
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ dwWriteValue = ((oss_native_word) wIndex << 16) + CODEC_RD;
+ OUTL (devc->osdev, dwWriteValue, devc->base + AC97CODEC);
+ oss_udelay (100);
+ /* Check AC CODEC access time out */
+ for (i = 0; i < CODEC_TIMEOUT_COUNT; i++)
+ {
+ /* if send command over, break */
+ if (INL (devc->osdev, devc->base + AC97CODEC) & STA_VALID)
+ break;
+ oss_udelay (50);
+ }
+ if (i == CODEC_TIMEOUT_COUNT)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+ }
+
+ /* Check if Index still ours? If yes, return data, else return FAIL */
+ dwTmpValue = INL (devc->osdev, devc->base + AC97CODEC);
+ OUTB (devc->osdev, 0x02, devc->base + AC97CODEC + 3);
+ if (((dwTmpValue & CODEC_INDEX) >> 16) == wIndex)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return ((int) dwTmpValue & CODEC_DATA);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+
+}
+
+static int
+ac97_write (void *devc_, int wIndex, int wData)
+{
+ oss_native_word dwWriteValue = 0;
+ unsigned int i = 0;
+ oss_native_word flags;
+ via97_devc *devc = devc_;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ dwWriteValue = ((oss_native_word) wIndex << 16) + wData;
+ OUTL (devc->osdev, dwWriteValue, devc->base + AC97CODEC);
+ oss_udelay (100);
+
+ /* Check AC CODEC access time out */
+ for (i = 0; i < CODEC_TIMEOUT_COUNT; i++)
+ {
+ /* if send command over, break */
+ if (!(INL (devc->osdev, devc->base + AC97CODEC) & IN_CMD))
+ break;
+ oss_udelay (50);
+ }
+ if (i == CODEC_TIMEOUT_COUNT)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return (-1);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 1;
+}
+
+static int
+via97intr (oss_device_t * osdev)
+{
+ via97_devc *devc = (via97_devc *) osdev->devc;
+ via97_portc *portc;
+ unsigned char status;
+ int serviced = 0;
+ int i;
+
+#ifdef OBSOLETED_STUFF
+ if (devc->mpu_attached)
+ {
+ if (uart401intr (INT_HANDLER_CALL (devc->irq)))
+ serviced = 1;
+ }
+#endif
+
+ /* Handle playback interrupt */
+ status = INB (devc->osdev, devc->base + 0x00);
+
+ if (status & 0x01)
+ {
+ serviced = 1;
+ for (i = 0; i < 2; i++)
+ {
+ portc = &devc->portc[i];
+ /* IOC Interrupt */
+ if ((portc->trigger_bits & PCM_ENABLE_OUTPUT) && (status & 0x01))
+ oss_audio_outputintr (portc->audiodev, 0);
+ }
+ }
+ OUTB (devc->osdev, status | 0x01, devc->base + 0x00);
+
+
+ /* Handle record interrupt */
+ status = INB (devc->osdev, devc->base + 0x10);
+
+ if (status & 0x01)
+ {
+ serviced = 1;
+ for (i = 0; i < 2; i++)
+ {
+ portc = &devc->portc[i];
+ /* IOC Interrupt */
+ if ((portc->trigger_bits & PCM_ENABLE_INPUT) && (status & 0x01))
+ oss_audio_inputintr (portc->audiodev, 0);
+ }
+ }
+
+ OUTB (devc->osdev, status | 0x01, devc->base + 0x10);
+ return serviced;
+}
+
+/*
+ * Audio routines
+ */
+
+static int
+via97_audio_set_rate (int dev, int arg)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->speed;
+
+ if (audio_engines[dev]->flags & ADEV_FIXEDRATE)
+ arg = 48000;
+
+ if (arg > 48000)
+ arg = 48000;
+ if (arg < 5000)
+ arg = 5000;
+ portc->speed = arg;
+ return portc->speed;
+}
+
+static short
+via97_audio_set_channels (int dev, short arg)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->channels;
+
+
+ if ((arg != 1) && (arg != 2))
+ return portc->channels = 2;
+ portc->channels = arg;
+
+ return portc->channels;
+}
+
+static unsigned int
+via97_audio_set_format (int dev, unsigned int arg)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->bits;
+
+ if (!(arg & (AFMT_U8 | AFMT_S16_LE)))
+ return portc->bits = AFMT_S16_LE;
+ portc->bits = arg;
+
+ return portc->bits;
+}
+
+/*ARGSUSED*/
+static int
+via97_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void via97_audio_trigger (int dev, int state);
+
+static void
+via97_audio_reset (int dev)
+{
+ via97_audio_trigger (dev, 0);
+}
+
+static void
+via97_audio_reset_input (int dev)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+ via97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+via97_audio_reset_output (int dev)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+ via97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+/*ARGSUSED*/
+static int
+via97_audio_open (int dev, int mode, int open_flags)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+ via97_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
+via97_audio_close (int dev, int mode)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+ via97_devc *devc = audio_engines[dev]->devc;
+
+ via97_audio_reset (dev);
+ portc->open_mode = 0;
+ devc->open_mode &= ~mode;
+ portc->audio_enabled &= ~mode;
+}
+
+/*ARGSUSED*/
+static void
+via97_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+
+}
+
+/*ARGSUSED*/
+static void
+via97_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ via97_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+
+}
+
+static void
+via97_audio_trigger (int dev, int state)
+{
+ via97_devc *devc = audio_engines[dev]->devc;
+ via97_portc *portc = audio_engines[dev]->portc;
+ 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))
+ {
+ OUTB (devc->osdev, 0x80, devc->base + 0x01);
+ 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;
+ OUTB (devc->osdev, 0x40, devc->base + 0x01);
+ }
+ }
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+
+ OUTB (devc->osdev, 0x80, devc->base + 0x11);
+ 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;
+ OUTB (devc->osdev, 0x40, devc->base + 0x11);
+ }
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+via97_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ via97_devc *devc = audio_engines[dev]->devc;
+ via97_portc *portc = audio_engines[dev]->portc;
+ int i, sgd_ptr;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ unsigned char tmp;
+ oss_native_word flags;
+
+ if (audio_engines[dev]->dmap_in->dmabuf_phys == 0)
+ return OSS_ENOSPC;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ tmp = 0x81; /* Auto start at EOL, interrupt on FLAG */
+
+ if (portc->bits != AFMT_U8)
+ tmp |= 0x20;
+ if (portc->channels != 1)
+ tmp |= 0x10;
+ OUTB (devc->osdev, tmp, devc->base + 0x12);
+
+ ac97_recrate (&devc->ac97devc, portc->speed);
+
+ sgd_ptr = devc->rec_sgd_ptr;
+ for (i = 0; i < dmap->nfrags; i++)
+ {
+ if (sgd_ptr >= SGD_TOTAL_SIZE)
+ {
+ cmn_err (CE_WARN, "Out of Record SGD entries\n");
+ return OSS_ENOSPC;
+ }
+
+ devc->SGD_table[sgd_ptr].phaddr =
+ dmap->dmabuf_phys + (i * dmap->fragment_size);
+ devc->SGD_table[sgd_ptr].flags = 0x40000000 | dmap->fragment_size;
+
+ sgd_ptr++;
+ }
+
+ devc->SGD_table[sgd_ptr - 1].flags |= 0x80000000; /* EOL */
+ devc->rec_sgd_phys =
+ devc->SGD_table_phys + devc->rec_sgd_ptr * sizeof (SGD_entry);
+ OUTL (devc->osdev, devc->rec_sgd_phys, devc->base + 0x14);
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+via97_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ via97_devc *devc = audio_engines[dev]->devc;
+ via97_portc *portc = audio_engines[dev]->portc;
+ int i, sgd_ptr;
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+ unsigned char tmp;
+ oss_native_word flags;
+
+ if (audio_engines[dev]->dmap_out->dmabuf_phys == 0)
+ return OSS_ENOSPC;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ tmp = 0x81; /* Auto start at EOL, interrupt on FLAG */
+
+ if (portc->bits != AFMT_U8)
+ tmp |= 0x20;
+ if (portc->channels != 1)
+ tmp |= 0x10;
+ OUTB (devc->osdev, tmp, devc->base + 0x02);
+
+ ac97_playrate (&devc->ac97devc, portc->speed);
+
+ sgd_ptr = devc->play_sgd_ptr;
+
+ for (i = 0; i < dmap->nfrags; i++)
+ {
+ if (sgd_ptr >= SGD_TOTAL_SIZE)
+ {
+ cmn_err (CE_WARN, "Out of Playback SGD entries\n");
+ return OSS_ENOSPC;
+ }
+
+ devc->SGD_table[sgd_ptr].phaddr =
+ dmap->dmabuf_phys + (i * dmap->fragment_size);
+ devc->SGD_table[sgd_ptr].flags = 0x40000000 | dmap->fragment_size;
+
+ sgd_ptr++;
+ }
+
+ devc->SGD_table[sgd_ptr - 1].flags |= 0x80000000; /* EOL */
+ devc->play_sgd_phys =
+ devc->SGD_table_phys + devc->play_sgd_ptr * sizeof (SGD_entry);
+ OUTL (devc->osdev, devc->play_sgd_phys, devc->base + 0x04);
+
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+static int
+via97_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)
+ return err;
+
+ if (dmap->buffsize > BUFFER_SIZE)
+ {
+ cmn_err (CE_WARN, "Too large DMA buffer (%d/%d) - truncated\n",
+ dmap->buffsize, BUFFER_SIZE);
+ dmap->buffsize = BUFFER_SIZE;
+ }
+
+ return 0;
+}
+
+static int
+via97_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+/*
+ * Unfortunately the VIA chip seems to raise interrupt about 32 bytes before
+ * the DMA pointer moves to a new fragment. This means that the bytes value
+ * returned by SNDCTL_DSP_GET[]PTR will be bogus during few samples before
+ * the pointer wraps back to the beginning of buffer.
+ *
+ * If mmap() is not being used this routine will return 0 during this period.
+ */
+ via97_devc *devc = audio_engines[dev]->devc;
+ unsigned int ptr, sgd;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+
+ if (direction == PCM_ENABLE_OUTPUT)
+ {
+ ptr = INL (devc->osdev, devc->base + 0x0c) & 0xffffff;
+ sgd =
+ ((INL (devc->osdev, devc->base + 0x04) - devc->play_sgd_phys) / 8) -
+ 1;
+
+ ptr = dmap->fragment_size - ptr;
+ ptr = ptr + (sgd * dmap->fragment_size);
+//cmn_err(CE_CONT, "%d/%d\n", sgd, ptr);
+ }
+ else
+ {
+ ptr = INL (devc->osdev, devc->base + 0x1c) & 0xffffff;
+ sgd =
+ ((INL (devc->osdev, devc->base + 0x14) - devc->rec_sgd_phys) / 8) - 1;
+
+ ptr = dmap->fragment_size - ptr;
+ ptr = ptr + (sgd * dmap->fragment_size);
+ }
+
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return ptr;
+}
+
+static const audiodrv_t via97_audio_driver = {
+ via97_audio_open,
+ via97_audio_close,
+ via97_audio_output_block,
+ via97_audio_start_input,
+ via97_audio_ioctl,
+ via97_audio_prepare_for_input,
+ via97_audio_prepare_for_output,
+ via97_audio_reset,
+ NULL,
+ NULL,
+ via97_audio_reset_input,
+ via97_audio_reset_output,
+ via97_audio_trigger,
+ via97_audio_set_rate,
+ via97_audio_set_format,
+ via97_audio_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ via97_alloc_buffer,
+ NULL, /* via97_free_buffer, */
+ NULL,
+ NULL,
+ via97_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_mpu (via97_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 = "VIA97 external MIDI";
+ 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 1
+ if (!probe_uart401 (&hw_config))
+ {
+ cmn_err (CE_WARN, "MPU-401 was not detected\n");
+ return;
+ }
+#endif
+ DDB (cmn_err (CE_WARN, "MPU-401 detected - Good\n"));
+ attach_uart401 (&hw_config);
+ devc->mpu_attached = 1;
+}
+
+static void
+unload_mpu (via97_devc * devc)
+{
+ struct address_info hw_config;
+
+ if (devc == NULL || !devc->mpu_attached)
+ return;
+
+ devc->mpu_attached = 0;
+ 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 = "VIA97";
+ 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;
+
+ unload_uart401 (&hw_config);
+
+}
+#endif
+
+static int
+init_via97 (via97_devc * devc)
+{
+ int my_mixer, adev, opts, i;
+ int first_dev = 0;
+ oss_native_word phaddr;
+
+
+/*
+ * Allocate the SGD buffers
+ */
+
+ if (devc->SGD_table == NULL)
+ {
+ devc->SGD_table =
+ CONTIG_MALLOC (devc->osdev, SGD_ALLOC, MEMLIMIT_32BITS, &phaddr, devc->sgd_dma_handle);
+
+ if (devc->SGD_table == NULL)
+ return OSS_ENOSPC;
+ devc->SGD_table_phys = phaddr;
+ }
+
+ /*
+ * Allocate SGD entries for recording and playback.
+ */
+ devc->rec_sgd_ptr = 0;
+ devc->play_sgd_ptr = SGD_SIZE;
+/*
+ * Init mixer
+ */
+ my_mixer =
+ ac97_install (&devc->ac97devc, "VIA82C686 AC97 Mixer", ac97_read,
+ ac97_write, devc, devc->osdev);
+ if (my_mixer == -1)
+ return 0; /* No mixer */
+
+ mixer_devs[my_mixer]->priority = 10; /* Known motherboard device */
+
+ /* enable variable rate */
+ ac97_write (devc, 0x2a, 0x01);
+
+#ifdef OBSOLETED_STUFF
+ DDB (cmn_err (CE_WARN, "Probing UART401 at 0x%x\n", devc->mpu_base));
+ attach_mpu (devc);
+#endif
+
+ for (i = 0; i < 2; i++)
+ {
+ via97_portc *portc = &devc->portc[i];
+ char tmp_name[100];
+
+ opts = ADEV_AUTOMODE;
+
+ if (!ac97_varrate (&devc->ac97devc))
+ {
+ opts |= ADEV_FIXEDRATE;
+ }
+
+ if (i == 0)
+ {
+ opts |= ADEV_DUPLEX;
+ strcpy (tmp_name, "VIA 82C686 AC97 Controller");
+ }
+ else
+ {
+ opts |= ADEV_DUPLEX | ADEV_SHADOW;
+ strcpy (tmp_name, "VIA 82C686 AC97 Controller");
+ }
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &via97_audio_driver,
+ sizeof (audiodrv_t),
+ opts,
+ 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 = 8000;
+ audio_engines[adev]->max_rate = 48000;
+ audio_engines[adev]->caps |= PCM_CAP_FREERATE;
+ portc->open_mode = 0;
+ portc->audio_enabled = 0;
+ portc->audiodev = adev;
+ audio_engines[adev]->min_block = MIN_BLOCK;
+ audio_engines[adev]->max_block = 4 * 1024;
+ audio_engines[adev]->max_fragments = SGD_SIZE;
+ if (opts & ADEV_FIXEDRATE)
+ {
+ audio_engines[adev]->fixed_rate = 48000;
+ audio_engines[adev]->min_rate = 48000;
+ audio_engines[adev]->max_rate = 48000;
+ }
+#ifdef CONFIG_OSS_VMIX
+ if (i == 0)
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+ }
+ }
+ return 1;
+}
+
+int
+oss_via97_attach (oss_device_t * osdev)
+{
+ unsigned char tmp, pci_irq_line, pci_revision /*, pci_latency */ ;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ int mpu_base;
+ via97_devc *devc;
+
+ DDB (cmn_err (CE_WARN, "Entered VT82C686 probe routine\n"));
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+ if (vendor != VIA_VENDOR_ID || device != VIA_82C686)
+ 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->chip_name = "VIA VT82C686";
+
+ /* Enable codec, etc */
+
+ pci_write_config_byte (osdev, 0x41, 0xc0);
+ oss_udelay (10);
+ pci_read_config_byte (osdev, 0x41, &tmp);
+ pci_write_config_byte (osdev, 0x41, tmp | 0x0c);
+ oss_udelay (10);
+
+ /* setup game port/MIDI */
+ pci_write_config_byte (osdev, 0x42, 0x2a);
+ /* disable FM io */
+ pci_write_config_byte (osdev, 0x48, 0x00);
+
+ /* Enable interrupt on FLAG and on EOL */
+ tmp = INB (devc->osdev, devc->base + 0x22);
+ OUTB (devc->osdev, tmp | 0x83, devc->base + 0x22);
+
+
+ /* Enable MPU401 */
+ pci_read_config_byte (osdev, 0x8, &tmp);
+ if ((tmp & 0xff) >= 0x20)
+ {
+ pci_read_config_byte (osdev, 0x42, &tmp);
+ pci_write_config_byte (osdev, 0x42, tmp & 0x3f);
+ }
+
+ pci_read_config_byte (osdev, 0x43, &tmp);
+ switch ((tmp & 0x0c) >> 2)
+ {
+ case 0:
+ mpu_base = 0x300;
+ break;
+ case 1:
+ mpu_base = 0x310;
+ break;
+ case 2:
+ mpu_base = 0x320;
+ break;
+ default:
+ mpu_base = 0x330;
+ break;
+ }
+
+ /* map PCI IO address space */
+ devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ devc->base &= ~0x3;
+
+ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+ devc->irq = pci_irq_line;
+ devc->mpu_base = mpu_base;
+ devc->SGD_table = NULL;
+ devc->mpu_attached = 0;
+ devc->open_mode = 0;
+
+ 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, via97intr, NULL) < 0)
+ {
+ cmn_err (CE_WARN, "Unable to register interrupts\n");
+ return 0;
+ }
+
+ return init_via97 (devc); /* Detected */
+}
+
+
+int
+oss_via97_detach (oss_device_t * osdev)
+{
+ via97_devc *devc = (via97_devc *) osdev->devc;
+
+
+ if (oss_disable_device (devc->osdev) < 0)
+ return 0;
+
+ OUTB (devc->osdev, 0x40, devc->base + 0x01);
+ OUTB (devc->osdev, 0x40, devc->base + 0x11);
+ OUTB (devc->osdev, 0, devc->base + 0x02);
+ OUTB (devc->osdev, 0, devc->base + 0x12);
+ OUTL (devc->osdev, 0, devc->base + 0x04);
+ OUTL (devc->osdev, 0, devc->base + 0x14);
+ OUTL (devc->osdev, 0, devc->base + 0x22);
+ oss_udelay (30);
+
+#ifdef OBSOLETED_STUFF
+ if (devc->mpu_attached)
+ unload_mpu (devc);
+#endif
+
+ oss_unregister_interrupts (devc->osdev);
+
+ if (devc->SGD_table != NULL)
+ {
+ CONTIG_FREE (devc->osdev, devc->SGD_table, SGD_ALLOC, devc->sgd_dma_handle);
+ devc->SGD_table = NULL;
+ }
+
+ MUTEX_CLEANUP (devc->mutex);
+ MUTEX_CLEANUP (devc->low_mutex);
+ UNMAP_PCI_IOADDR (devc->osdev, 0);
+
+ oss_unregister_device (devc->osdev);
+ return 1;
+}