diff options
Diffstat (limited to 'kernel/drv/oss_via823x')
-rw-r--r-- | kernel/drv/oss_via823x/.config | 1 | ||||
-rw-r--r-- | kernel/drv/oss_via823x/.devices | 5 | ||||
-rw-r--r-- | kernel/drv/oss_via823x/.name | 1 | ||||
-rw-r--r-- | kernel/drv/oss_via823x/oss_via823x.c | 1013 | ||||
-rw-r--r-- | kernel/drv/oss_via823x/oss_via823x.man | 20 | ||||
-rw-r--r-- | kernel/drv/oss_via823x/via8233.h | 97 |
6 files changed, 1137 insertions, 0 deletions
diff --git a/kernel/drv/oss_via823x/.config b/kernel/drv/oss_via823x/.config new file mode 100644 index 0000000..5280084 --- /dev/null +++ b/kernel/drv/oss_via823x/.config @@ -0,0 +1 @@ +platform=i86pc diff --git a/kernel/drv/oss_via823x/.devices b/kernel/drv/oss_via823x/.devices new file mode 100644 index 0000000..dfb8b74 --- /dev/null +++ b/kernel/drv/oss_via823x/.devices @@ -0,0 +1,5 @@ +oss_via823x pci1106,3059 VIA VT8233/8235/8237 +oss_via823x pci1106,4551 VIA VT5432B +oss_via823x pci1106,7059 VIA VT8233A +oss_via823x pcs1462,3800 MSI K7T266 +oss_via823x pcs1462,4720 MSI KT3 Ultra diff --git a/kernel/drv/oss_via823x/.name b/kernel/drv/oss_via823x/.name new file mode 100644 index 0000000..e947b9d --- /dev/null +++ b/kernel/drv/oss_via823x/.name @@ -0,0 +1 @@ +VIA VT8233/8235/8237 motherboard audio 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; +} diff --git a/kernel/drv/oss_via823x/oss_via823x.man b/kernel/drv/oss_via823x/oss_via823x.man new file mode 100644 index 0000000..90aa34c --- /dev/null +++ b/kernel/drv/oss_via823x/oss_via823x.man @@ -0,0 +1,20 @@ +NAME +oss_via823x - Open Sound System driver for VIA 8233/8235/8237 audio controllers. + +DESCRIPTION + +VIA823x device characteristics: + o 8/16 bit playback/record + o mono/stereo/4/5.1 (Dolby) playback + o mono/stereo recording + o 8KHz to 48Khz sample rate. + o SPDIF input/output capability based on AC97 codec attech to the + VIA823x controller. +OPTIONS +None + +FILES +CONFIGFILEPATH/oss_via823x.conf Device configuration + +AUTHOR +4Front Technologies diff --git a/kernel/drv/oss_via823x/via8233.h b/kernel/drv/oss_via823x/via8233.h new file mode 100644 index 0000000..7d2ad37 --- /dev/null +++ b/kernel/drv/oss_via823x/via8233.h @@ -0,0 +1,97 @@ +/* + * Purpose: Definitions for the via8233 driver + */ +/* + * + * 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. + * + */ +#define VIA_VENDOR_ID 0x1106 +#define VIA_8233_ID 0x3059 +#define VIA_8233A_ID 0x7059 + +#define PLAY_SGD_NUM 1 +#define REC_SGD_NUM 0 +#define MAX_ENGINES 2 +#define MAX_PORTC 2 + +#define SGD_SIZE 256 +#define SGD_ALLOC 4096 +#define SGD_EOL 0x80000000 +#define SGD_FLAG 0x40000000 + +#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; + +struct via8233_portc; + +typedef struct +{ + int sgd_num, base; + oss_dma_handle_t sgd_dma_handle; + int mode; + SGD_entry *sgd; + oss_native_word sgd_phys; + int sgd_ptr; + int prev_sgd; + int cfrag; + unsigned int prevpos; + short frags[SGD_SIZE]; + struct via8233_portc *portc; +} +engine_desc; + +typedef struct via8233_portc +{ + int audiodev; + int open_mode; + int mode_mask; + int speed, bits, channels; + engine_desc *play_engine, *rec_engine; + int trigger_bits; + int audio_enabled; +} +via8233_portc; + + +typedef struct via8233_devc +{ + oss_device_t *osdev; + int chip_type; +#define CHIP_8233 0 +#define CHIP_8233A 1 + char *chip_name; + int multi_channel_active; + oss_native_word base; + int irq; + int open_mode; + oss_mutex_t mutex; /* For normal locking */ + oss_mutex_t low_mutex; /* For low level routines */ +/* + * Mixer + */ + ac97_devc ac97devc; + int mixer_dev; + + via8233_portc portc[MAX_PORTC]; + engine_desc engines[MAX_ENGINES]; +} +via8233_devc; |