summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_via823x/oss_via823x.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_via823x/oss_via823x.c')
-rw-r--r--kernel/drv/oss_via823x/oss_via823x.c1013
1 files changed, 1013 insertions, 0 deletions
diff --git a/kernel/drv/oss_via823x/oss_via823x.c b/kernel/drv/oss_via823x/oss_via823x.c
new file mode 100644
index 0000000..096a72a
--- /dev/null
+++ b/kernel/drv/oss_via823x/oss_via823x.c
@@ -0,0 +1,1013 @@
+/*
+ * Purpose: Driver for the VIA8233/8235 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_via823x_cfg.h"
+#include <oss_pci.h>
+#include <ac97.h>
+#include "via8233.h"
+
+
+static void feed_sgd (via8233_devc * devc, dmap_t * dmap, engine_desc * eng);
+
+static int
+ac97_read (void *devc_, int wIndex)
+{
+ oss_native_word flags;
+ unsigned int dwWriteValue = 0, dwTmpValue, i = 0;
+ via8233_devc *devc = devc_;
+
+
+ /* Index has only 7 bit */
+ if (wIndex > 0x7F)
+ return 0;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ dwWriteValue = ((unsigned int) 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 OSS_EIO;
+ }
+
+ /* 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 OSS_EIO;
+
+}
+
+static int
+ac97_write (void *devc_, int wIndex, int wData)
+{
+ oss_native_word flags;
+ unsigned int dwWriteValue = 0, i = 0;
+ via8233_devc *devc = devc_;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ dwWriteValue = ((unsigned int) 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 0;
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 1;
+}
+
+static int
+via8233intr (oss_device_t * osdev)
+{
+ int serviced = 0, i;
+ via8233_devc *devc = osdev->devc;
+ via8233_portc *portc;
+ engine_desc *eng;
+ unsigned int engine_stat;
+ unsigned int status = 0;
+ oss_native_word flags;
+
+ MUTEX_ENTER (devc->mutex, flags);
+ status = INL (devc->osdev, devc->base + 0x84);
+#if 0
+ // This is reported to cause hang because some status register bits
+ // may be turned on even ehen the device is not interrupting.
+ if (status == 0)
+ {
+ /*
+ * No interrupts are pending so we can stop without
+ * polling all the individual status registers.
+ */
+ MUTEX_EXIT (devc->mutex, flags);
+ return 0;
+ }
+#endif
+
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ portc = &devc->portc[i];
+ eng = portc->play_engine;
+
+ if (eng != NULL)
+ if ((eng->mode & OPEN_WRITE)
+ && (portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ engine_stat = INB (devc->osdev, eng->base + 0x00);
+ if (engine_stat & 0x01)
+ {
+ oss_audio_outputintr (portc->audiodev, 1);
+ feed_sgd (devc, audio_engines[portc->audiodev]->dmap_out,
+ eng);
+ serviced = 1;
+ }
+ OUTB (devc->osdev, engine_stat, eng->base + 0x00);
+ }
+
+ eng = portc->rec_engine;
+ if (eng != NULL)
+ if ((eng->mode & OPEN_READ)
+ && (portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ engine_stat = INB (devc->osdev, eng->base + 0x00);
+ if (engine_stat & 0x01)
+ {
+ oss_audio_inputintr (portc->audiodev, 0);
+ feed_sgd (devc, audio_engines[portc->audiodev]->dmap_in, eng);
+ serviced = 1;
+ }
+ OUTB (devc->osdev, engine_stat, eng->base + 0x00);
+ }
+ }
+ MUTEX_EXIT (devc->mutex, flags);
+
+ return serviced;
+}
+
+/*
+ * Audio routines
+ */
+
+static int
+via8233_audio_set_rate (int dev, int arg)
+{
+ via8233_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
+via8233_audio_set_channels (int dev, short arg)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+
+ if (arg > 6)
+ arg=6;
+
+ if ((arg != 1) && (arg != 2) && (arg != 4) && (arg != 6))
+ return portc->channels;
+ portc->channels = arg;
+
+ return portc->channels;
+}
+
+static unsigned int
+via8233_audio_set_format (int dev, unsigned int arg)
+{
+ via8233_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
+via8233_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void via8233_audio_trigger (int dev, int state);
+
+static void
+via8233_audio_reset (int dev)
+{
+ via8233_audio_trigger (dev, 0);
+}
+
+static void
+via8233_audio_reset_input (int dev)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+ via8233_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+via8233_audio_reset_output (int dev)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+ via8233_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+/*ARGSUSED*/
+static int
+via8233_audio_open (int dev, int mode, int open_flags)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+ via8233_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
+via8233_audio_close (int dev, int mode)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+ via8233_devc *devc = audio_engines[dev]->devc;
+
+ via8233_audio_reset (dev);
+ portc->open_mode = 0;
+ devc->open_mode &= ~mode;
+ portc->audio_enabled &= ~mode;
+}
+
+/*ARGSUSED*/
+static void
+via8233_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+
+
+}
+
+/*ARGSUSED*/
+static void
+via8233_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+
+}
+
+static void
+via8233_audio_trigger (int dev, int state)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+ via8233_devc *devc = audio_engines[dev]->devc;
+ engine_desc *eng;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if ((portc->open_mode & OPEN_WRITE) && portc->play_engine != NULL)
+ {
+ eng = portc->play_engine;
+
+ if (state & PCM_ENABLE_OUTPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ /* Start with autoinit and SGD flag interrupts enabled */
+ OUTB (devc->osdev, 0xa1, eng->base + 0x01);
+ portc->trigger_bits |= PCM_ENABLE_OUTPUT;
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ OUTB (devc->osdev, 0x40, eng->base + 0x01); /* Stop */
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ }
+ }
+ }
+
+ if ((portc->open_mode & OPEN_READ) && portc->rec_engine != NULL)
+ {
+ eng = portc->rec_engine;
+
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ /* Start with autoinit and SGD flag interrupts enabled */
+ OUTB (devc->osdev, 0xa1, eng->base + 0x01);
+ portc->trigger_bits |= PCM_ENABLE_INPUT;
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ OUTB (devc->osdev, 0x40, eng->base + 0x01); /* Stop */
+ }
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+static void
+feed_sgd (via8233_devc * devc, dmap_t * dmap, engine_desc * eng)
+{
+ unsigned int addr, p, tmp;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ addr = dmap->dmabuf_phys + eng->cfrag * dmap->fragment_size;
+ p = eng->sgd_ptr;
+
+ eng->sgd[p].phaddr = addr;
+ eng->sgd[p].flags = dmap->fragment_size | SGD_FLAG;
+ if (p == (SGD_SIZE - 1))
+ eng->sgd[p].flags |= SGD_EOL;
+
+ /* Update the last entry ptr */
+ tmp = INL (devc->osdev, eng->base + 0x08);
+ tmp &= 0x00ffffff;
+ tmp |= (p << 24);
+ OUTL (devc->osdev, tmp, eng->base + 0x08);
+
+ eng->prev_sgd = p;
+ eng->frags[p] = eng->cfrag;
+ eng->cfrag = (eng->cfrag + 1) % dmap->nfrags;
+ eng->sgd_ptr = (eng->sgd_ptr + 1) % SGD_SIZE;
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+via8233_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ via8233_devc *devc = audio_engines[dev]->devc;
+ via8233_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ engine_desc *eng;
+ int i;
+ unsigned int fmt;
+ oss_native_word flags;
+
+ if (portc->rec_engine == NULL)
+ {
+ cmn_err (CE_WARN, "No rec engine (dev=%d)\n", dev);
+ return OSS_EIO;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ eng = portc->rec_engine;
+
+ OUTB (devc->osdev, 0x40, eng->base + 0x01); /* Stop */
+
+ eng->cfrag = 0;
+ eng->sgd_ptr = 0;
+ eng->prevpos = 0xffffffff;
+
+ for (i = 0; i < 2; i++)
+ feed_sgd (devc, dmap, eng);
+
+ OUTL (devc->osdev, eng->sgd_phys, eng->base + 0x04);
+
+ fmt = 0;
+ if (portc->bits == AFMT_S16_LE)
+ fmt |= (1 << 21);
+ if (portc->channels == 2)
+ fmt |= (1 << 20);
+
+ if (devc->chip_type != CHIP_8233A)
+ {
+ if (portc->speed == 48000)
+ fmt |= (1 << 20) - 1;
+ else
+ fmt |= ((1024 * portc->speed + 24000) / 48000) * 1024;
+ }
+
+ fmt |= (eng->prev_sgd << 24);
+ OUTL (devc->osdev, fmt, eng->base + 0x08);
+ ac97_recrate (&devc->ac97devc, portc->speed);
+
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+via8233_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ via8233_devc *devc = audio_engines[dev]->devc;
+ via8233_portc *portc = audio_engines[dev]->portc;
+ dmap_p dmap = audio_engines[dev]->dmap_out;
+
+ engine_desc *eng;
+ int i, tmp;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ eng = portc->play_engine;
+
+ OUTB (devc->osdev, 0x40, eng->base + 0x01); /* Stop */
+
+ eng->cfrag = 0;
+ eng->sgd_ptr = 0;
+ eng->prevpos = 0xffffffff;
+
+ for (i = 0; i < 2; i++)
+ feed_sgd (devc, dmap, eng);
+
+ OUTL (devc->osdev, eng->sgd_phys, eng->base + 0x4);
+
+ ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_AUDIO, SNDCTL_MIX_WRITE, 0);
+
+ if (portc->bits == AFMT_AC3)
+ {
+ portc->channels = 2;
+ portc->bits = 16;
+ }
+ ac97_spdif_setup (devc->mixer_dev, portc->speed, portc->bits);
+
+ tmp = (portc->bits == AFMT_U8) ? 0 : 0x80;
+ tmp |= portc->channels << 4;
+ OUTB (devc->osdev, tmp, eng->base + 0x02);
+
+ /* Select channel assignment - not valid for 8233A */
+ tmp = 0;
+ if (devc->chip_type != CHIP_8233A)
+ {
+ switch (portc->channels)
+ {
+ case 1:
+ tmp = (1 << 0) | (1 << 4);
+ break;
+ case 2:
+ tmp = (1 << 0) | (2 << 4);
+ break;
+ case 4:
+ tmp = (1 << 0) | (2 << 4) | (3 << 8) | (4 << 12);
+ break;
+ case 6:
+ tmp =
+ (1 << 0) | (2 << 4) | (5 << 8) | (6 << 12) | (3 << 16) | (4 <<
+ 20);
+ break;
+ default:
+ tmp = 0;
+ break;
+ }
+ }
+ tmp |= 0xFF000000;
+ OUTL (devc->osdev, tmp, eng->base + 0x08);
+ /* need to set the speed twice - for some odd reason */
+ ac97_playrate (&devc->ac97devc, portc->speed);
+ ac97_playrate (&devc->ac97devc, portc->speed);
+
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+via8233_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+ int err;
+ via8233_devc *devc = audio_engines[dev]->devc;
+ via8233_portc *portc = audio_engines[dev]->portc;
+ engine_desc *eng;
+
+ if (dmap->dmabuf != NULL)
+ return 0;
+
+ if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0)
+ return err;
+
+
+ if (direction == PCM_ENABLE_INPUT)
+ {
+ eng = &devc->engines[REC_SGD_NUM];
+ eng->mode = OPEN_READ;
+ portc->rec_engine = eng;
+ }
+ else
+ {
+ eng = &devc->engines[PLAY_SGD_NUM];
+ eng->mode = OPEN_WRITE;
+ portc->play_engine = eng;
+ }
+
+ return 0;
+}
+
+
+/*ARGSUSED*/
+static int
+via8233_free_buffer (int dev, dmap_t * dmap, int direction)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+
+ if (dmap->dmabuf == NULL)
+ return 0;
+ oss_free_dmabuf (dev, dmap);
+
+ dmap->dmabuf = NULL;
+ dmap->dmabuf_phys = 0;
+
+ if (direction == PCM_ENABLE_OUTPUT)
+ portc->play_engine = NULL;
+
+ if (direction == PCM_ENABLE_INPUT)
+ portc->rec_engine = NULL;
+
+ return 0;
+}
+
+static int
+via8233_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ via8233_portc *portc = audio_engines[dev]->portc;
+ via8233_devc *devc = audio_engines[dev]->devc;
+ unsigned int ptr, pos, tmp;
+ engine_desc *eng;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ if (direction == PCM_ENABLE_OUTPUT)
+ {
+ int status;
+ /* int this_sgd, prev_sgd */ ;
+
+ if (portc->play_engine == NULL
+ || !(portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+ }
+
+ eng = portc->play_engine;
+
+ ptr = INL (devc->osdev, eng->base + 0x0c);
+ status = INB (devc->osdev, eng->base + 0x00);
+ if (!(status & 0x80)) /* SGD not triggered */
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+ }
+
+#ifdef DO_TIMINGS
+ oss_timing_printf ("rawpos=%d", ptr);
+#endif
+#if 0
+ this_sgd = ptr >> 24;
+ prev_sgd = eng->prevpos >> 24;
+ prev_sgd = (prev_sgd + 1) % SGD_SIZE; /* Increment */
+ /* Chip bug catcher */
+ if (((ptr & 0xffffff) == 0) &&
+ ((eng->prevpos & 0xffffff) == 0) && (this_sgd == prev_sgd))
+ ptr = eng->prevpos;
+ else
+ eng->prevpos = ptr;
+#endif
+
+ tmp = ptr & 0xffffff;
+ ptr >>= 24;
+
+ pos = eng->frags[ptr] * dmap->fragment_size;
+ pos += (dmap->fragment_size - tmp) & ~3;
+
+#ifdef DO_TIMINGS
+ oss_timing_printf ("Playpos=%d", pos);
+#endif
+ pos = pos % dmap->bytes_in_use;
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return pos;
+ }
+
+ if (direction == PCM_ENABLE_INPUT)
+ {
+ int status;
+ /* int this_sgd, prev_sgd; */
+
+ if (portc->rec_engine == NULL
+ || !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+ }
+
+ eng = portc->rec_engine;
+
+ ptr = INL (devc->osdev, eng->base + 0x0c);
+ status = INB (devc->osdev, eng->base + 0x00);
+ if (!(status & 0x80)) /* SGD not triggered */
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+ }
+
+#if 0
+ this_sgd = ptr >> 24;
+ prev_sgd = eng->prevpos >> 24;
+ prev_sgd = (prev_sgd + 1) % SGD_SIZE; /* Increment */
+
+ /* Chip bug catcher */
+ if (((ptr & 0xffffff) == 0) &&
+ ((eng->prevpos & 0xffffff) == 0) && (this_sgd == prev_sgd))
+ ptr = eng->prevpos;
+ else
+ eng->prevpos = ptr;
+#endif
+
+ tmp = ptr & 0xffffff;
+ ptr >>= 24;
+
+ pos = eng->frags[ptr] * dmap->fragment_size;
+ pos += (dmap->fragment_size - tmp) & ~3;
+
+#ifdef DO_TIMINGS
+ oss_timing_printf ("Recpos=%d", pos);
+#endif
+ pos = pos % dmap->bytes_in_use;
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return pos;
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+}
+
+static const audiodrv_t via8233_audio_driver = {
+ via8233_audio_open,
+ via8233_audio_close,
+ via8233_audio_output_block,
+ via8233_audio_start_input,
+ via8233_audio_ioctl,
+ via8233_audio_prepare_for_input,
+ via8233_audio_prepare_for_output,
+ via8233_audio_reset,
+ NULL,
+ NULL,
+ via8233_audio_reset_input,
+ via8233_audio_reset_output,
+ via8233_audio_trigger,
+ via8233_audio_set_rate,
+ via8233_audio_set_format,
+ via8233_audio_set_channels,
+ NULL,
+ NULL,
+ NULL, /* via8233_check_input, */
+ NULL, /* via8233_check_output, */
+ via8233_alloc_buffer,
+ via8233_free_buffer,
+ NULL,
+ NULL,
+ via8233_get_buffer_pointer
+};
+
+static int
+via8233_alloc_engines (via8233_devc * devc)
+{
+ engine_desc *eng;
+ oss_native_word phaddr;
+ int i;
+
+ for (i = 0; i < MAX_ENGINES; i++)
+ {
+ if (i)
+ {
+ eng = &devc->engines[REC_SGD_NUM];
+ eng->base = devc->base + 0x60;
+ }
+ else
+ {
+ eng = &devc->engines[PLAY_SGD_NUM];
+ eng->base = devc->base + 0x40;
+ }
+
+ if (eng->sgd == NULL)
+ {
+ eng->sgd =
+ CONTIG_MALLOC (devc->osdev, SGD_ALLOC, MEMLIMIT_32BITS, &phaddr, eng->sgd_dma_handle);
+
+ if (eng->sgd == NULL)
+ {
+ cmn_err (CE_WARN, "can't allocate SGD table\n");
+ return OSS_ENOSPC;
+ }
+ eng->sgd_phys = phaddr;
+ }
+ }
+ return 0;
+}
+
+static int
+via8233_init (via8233_devc * devc)
+{
+ int my_mixer, adev, opts;
+ via8233_portc *portc;
+ int i;
+ char tmp_name[50];
+ int first_dev = 0;
+
+ /* allocate the Scatter Gather Engine buffers */
+ if (via8233_alloc_engines (devc) < 0)
+ {
+ cmn_err (CE_WARN, "Unable to allocate engines\n");
+ return OSS_ENOSPC;
+ }
+
+ /*
+ * Init mixer
+ */
+ my_mixer =
+ ac97_install (&devc->ac97devc, "VIA823x AC97 Mixer", ac97_read,
+ ac97_write, devc, devc->osdev);
+
+ if (my_mixer == -1)
+ {
+ cmn_err (CE_WARN, "AC97 mixer installation failed\n");
+ return 0; /* No mixer */
+ }
+
+ devc->mixer_dev = my_mixer;
+ mixer_devs[my_mixer]->priority = 10; /* Known motherboard device */
+
+ /* enable S/PDIF */
+ devc->ac97devc.spdif_slot = SPDIF_SLOT34;
+ ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_ENABLE, SNDCTL_MIX_WRITE, 1);
+
+ for (i = 0; i < MAX_PORTC; i++)
+ {
+ opts = ADEV_AUTOMODE;
+
+ if (!ac97_varrate (&devc->ac97devc))
+ {
+ opts |= ADEV_FIXEDRATE;
+ }
+
+ portc = &devc->portc[i];
+ if (i == 0)
+ {
+ opts |= ADEV_DUPLEX;
+ strcpy (tmp_name, devc->chip_name);
+ }
+ else
+ {
+ opts |= ADEV_DUPLEX | ADEV_SHADOW;
+ strcpy (tmp_name, devc->chip_name);
+ }
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &via8233_audio_driver,
+ sizeof (audiodrv_t),
+ opts,
+ AFMT_U8 | AFMT_S16_LE | AFMT_AC3,
+ devc, -1)) < 0)
+ {
+ adev = -1;
+ return 1;
+ }
+ 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;
+ audio_engines[adev]->min_channels = 2;
+ audio_engines[adev]->max_channels = 6;
+ if (opts & ADEV_FIXEDRATE)
+ {
+ audio_engines[adev]->fixed_rate = 48000;
+ audio_engines[adev]->min_rate = 48000;
+ audio_engines[adev]->max_rate = 48000;
+ }
+
+ portc->open_mode = 0;
+ portc->audio_enabled = 0;
+ portc->audiodev = adev;
+#ifdef CONFIG_OSS_VMIX
+ if (i == 0)
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+ }
+ }
+
+ return 1;
+}
+
+int
+oss_via823x_attach (oss_device_t * osdev)
+{
+ unsigned char pci_irq_line, pci_revision, bTmp /*, pci_latency */ ;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+ via8233_devc *devc;
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+ DDB (cmn_err
+ (CE_CONT, "oss_via823x_attach(Vendor %x, device %x)\n", vendor, device));
+ if ((vendor != VIA_VENDOR_ID)
+ || (device != VIA_8233_ID && device != VIA_8233A_ID))
+ {
+
+ cmn_err (CE_WARN, "Hardware not recognized (vendor=%x, dev=%x)\n",
+ vendor, device);
+ 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->open_mode = 0;
+
+ devc->chip_type = CHIP_8233;
+ devc->chip_name = "VIA VT8233";
+
+ if (pci_revision == 0x50)
+ devc->chip_name = "VIA VT8235";
+
+ if (pci_revision == 0x60)
+ devc->chip_name = "VIA VT8237";
+
+ if ((device == VIA_8233A_ID) ||
+ (device == VIA_8233_ID && pci_revision == 0x40))
+ {
+ devc->chip_type = CHIP_8233A;
+ devc->chip_name = "VIA VT8233A";
+ }
+
+ pci_write_config_byte (osdev, 0x41, 0xc0); /*ENAC97 & deassert RESET */
+
+ oss_udelay (10);
+ pci_read_config_byte (osdev, 0x41, &bTmp);
+ oss_udelay (10);
+ if (devc->chip_type == CHIP_8233A)
+ bTmp |= 0x0C; /* Enable var rate support */
+ else
+ bTmp |= 0x0f; /* enable VRA,SB,DX */
+ pci_write_config_byte (osdev, 0x41, bTmp);
+ oss_udelay (10);
+
+ if (devc->chip_type == CHIP_8233A)
+ {
+ pci_read_config_byte (osdev, 0x49, &bTmp);
+ oss_udelay (10);
+ pci_write_config_byte (osdev, 0x49, 0x0);
+ }
+ else
+ {
+ /* set slot 3,4 as SPDIF on VIA8235 - AC3 passthrough magic! */
+ pci_write_config_byte (osdev, 0x49, 0x1);
+ }
+
+ 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;
+
+ 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, via8233intr, NULL) < 0)
+ {
+ cmn_err (CE_WARN, "Unable to register interrupts\n");
+ return 0;
+ }
+
+ return via8233_init (devc); /* Detected */
+}
+
+int
+oss_via823x_detach (oss_device_t * osdev)
+{
+ via8233_devc *devc = (via8233_devc *) osdev->devc;
+ engine_desc *eng;
+ int i;
+
+ if (oss_disable_device (devc->osdev) < 0)
+ return 0;
+
+
+ /* disable S/PDIF */
+ if (devc->mixer_dev > 0)
+ ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_ENABLE, SNDCTL_MIX_WRITE, 0);
+
+ oss_unregister_interrupts (devc->osdev);
+
+ for (i = 0; i < MAX_ENGINES; i++)
+ {
+ eng = &devc->engines[i];
+ if (eng->sgd != NULL)
+ {
+ CONTIG_FREE (devc->osdev, eng->sgd, SGD_ALLOC, eng->sgd_dma_handle);
+ eng->sgd = NULL;
+ }
+ }
+
+ MUTEX_CLEANUP (devc->mutex);
+ MUTEX_CLEANUP (devc->low_mutex);
+ UNMAP_PCI_IOADDR (devc->osdev, 0);
+
+ oss_unregister_device (devc->osdev);
+ return 1;
+}