Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
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_ali5455_cfg.h" #include <oss_pci.h> #include <ac97.h> #define ALI_VENDOR_ID 0x10b9 #define ALI_DEVICE_5455 0x5455 #define MAX_ALI5455 1 #define MAX_PORTC 3 #define BDL_SIZE 32 #ifdef OSS_BIG_ENDIAN static __inline__ unsigned int swap32 (unsigned int x) { return ((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) | ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24); } static __inline__ unsigned short swap16 (unsigned short x) { return ((x >> 8) & 0xff) | ((x & 0xff) << 8); } #define SWAP32(x) swap32(x) #define SWAP16(x) swap16(x) #else #define SWAP32(x) x #define SWAP16(x) x #endif typedef struct { int open_mode; int speed, bits, channels; int audio_enabled; int trigger_bits; int audiodev; int port_type; #define DF_PCM 0 #define DF_SPDIF 1 } ALI_portc; typedef struct { unsigned int addr; unsigned short size; unsigned short flags; } bdl_t; typedef struct ALI_devc { oss_device_t *osdev; oss_native_word base; int irq; oss_mutex_t mutex; oss_mutex_t low_mutex; /* Mixer */ ac97_devc ac97devc; int mixer_dev; /* Audio parameters */ int open_mode; /* Buffer Descriptor List */ char *bdlBuffer; bdl_t *playBDL, *recBDL, *spdifBDL; oss_native_word playBDL_phys, recBDL_phys, spdifBDL_phys; oss_dma_handle_t bdl_dma_handle; int play_currbuf, play_currfrag; int spdif_currbuf, spdif_currfrag; int rec_currbuf, rec_currfrag; char *chip_name; ALI_portc portc[MAX_PORTC]; int play_frag_index[BDL_SIZE]; int rec_frag_index[BDL_SIZE]; int spdif_frag_index[BDL_SIZE]; } ALI_devc; static int ac97_read (void *devc_, int reg) { ALI_devc *devc = devc_; int i = 100; unsigned int status; unsigned int data = 0; unsigned short read_reg = 0; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); status = INB (devc->osdev, devc->base + 0x34); /* wait for the Codec Access Semaphore bit to be set */ while (i-- && (INL (devc->osdev, devc->base + 0x3c) & 0x80000000)) oss_udelay (1); for (i = 0; i < 100; i++) { status = INB (devc->osdev, devc->base + 0x38); if (status & 0x08) break; } if (i == 100) { cmn_err (CE_WARN, "AC97 not ready for read\n"); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } OUTW (devc->osdev, reg | 0x80, devc->base + 0x22); for (i = 0; i < 100; i++) { status = INB (devc->osdev, devc->base + 0x38); if (status & 0x02) { data = INW (devc->osdev, devc->base + 0x24); read_reg = INW (devc->osdev, devc->base + 0x26); break; } } if (i == 100) { cmn_err (CE_WARN, "AC97 read timed out \n"); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } if (read_reg != reg) { cmn_err (CE_WARN, "AC97 invalid reg read %x (%x)\n", read_reg, reg); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return data; } static int ac97_write (void *devc_, int reg, int data) { ALI_devc *devc = devc_; int i = 100, status; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); /* wait for the Codec Access Semaphore bit to be set */ while (i-- && (INL (devc->osdev, devc->base + 0x3c) & 0x80000000)) oss_udelay (1); /* wait until command port is ready for write */ for (i = 0; i < 100; i++) { status = INB (devc->osdev, devc->base + 0x38); if (status & 0x01) break; } if (i == 100) { cmn_err (CE_WARN, "AC97 timed out for write\n"); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } OUTL (devc->osdev, reg << 16 | data, devc->base + 0x20); for (i = 0; i < 100; i++) { status = INB (devc->osdev, devc->base + 0x38); if (status & 0x01) break; } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 1; } static int ALIintr (oss_device_t * osdev) { int status, global_status, p, f, i; int serviced = 0; ALI_devc *devc = (ALI_devc *) osdev->devc; ALI_portc *portc; /* oss_native_word flags; */ /* Handle playback */
TODO: Fix mutexes and move the inputintr/outputintr calls outside the mutex block.
/* MUTEX_ENTER (devc->mutex, flags); */ /* Handle Global Interrupts */ global_status = INL (devc->osdev, devc->base + 0x18); OUTL (devc->osdev, global_status, devc->base + 0x18); if (!(global_status & (0x10000 | 0x20000 | 0x80000 | 0x100000 | 0x200000))) { /* MUTEX_EXIT (devc->mutex, flags); */ return serviced; } /* Handle Playback Interrupts */ status = INB (devc->osdev, devc->base + 0x56); if ((status & 0x08) && (global_status & 0x20000)) for (i = 0; i < MAX_PORTC - 1; i++) { portc = &devc->portc[i]; serviced = 1; if ((portc->trigger_bits & PCM_ENABLE_OUTPUT)) /* IOC interrupt */ { dmap_t *dmap = audio_engines[portc->audiodev]->dmap_out; p = INB (devc->osdev, devc->base + 0x54); if (p != devc->play_currbuf) { p = devc->play_currbuf; f = devc->play_currfrag; devc->playBDL[p].addr = SWAP32 (dmap->dmabuf_phys + (f * dmap->fragment_size)); devc->playBDL[p].size = SWAP16 (dmap->fragment_size / 2); devc->playBDL[p].flags = SWAP16 (0xc000); /* IOC interrupts */ OUTB (devc->osdev, p, devc->base + 0x55); /* Set LVD */ devc->play_frag_index[p] = f; devc->play_currbuf = (p + 1) % BDL_SIZE; devc->play_currfrag = (f + 1) % dmap->nfrags; } oss_audio_outputintr (portc->audiodev, 1); } } OUTB (devc->osdev, status, devc->base + 0x56); /* Clear interrupts */ /*--------------------------------------------------------------------------*/ /* handle SPDIF interrupts */ status = INB (devc->osdev, devc->base + 0x76); if ((status & 0x08) && (global_status & 0x80000)) { portc = &devc->portc[2]; serviced = 1; if ((portc->trigger_bits & PCM_ENABLE_OUTPUT)) /* IOC interrupt */ { dmap_t *dmap = audio_engines[portc->audiodev]->dmap_out; p = INB (devc->osdev, devc->base + 0x74); if (p != devc->spdif_currbuf) { p = devc->spdif_currbuf; f = devc->spdif_currfrag; devc->spdifBDL[p].addr = SWAP32 (dmap->dmabuf_phys + (f * dmap->fragment_size)); devc->spdifBDL[p].size = SWAP16 (dmap->fragment_size / 2); devc->spdifBDL[p].flags = SWAP16 (0xc000); /* IOC interrupts */ OUTB (devc->osdev, p, devc->base + 0x75); /* Set LVD */ devc->spdif_frag_index[p] = f; devc->spdif_currbuf = (p + 1) % BDL_SIZE; devc->spdif_currfrag = (f + 1) % dmap->nfrags; } oss_audio_outputintr (portc->audiodev, 1); } } OUTB (devc->osdev, status, devc->base + 0x76); /* Clear interrupts */ /*---------------------------------------------------------------------------*/ /* Handle Recording Interrupts */ status = INB (devc->osdev, devc->base + 0x46); if ((status & 0x08) && (global_status & 0x10000)) for (i = 0; i < MAX_PORTC - 1; i++) { portc = &devc->portc[i]; serviced = 1; if ((portc->trigger_bits & PCM_ENABLE_INPUT)) /* IOC interrupt */ { dmap_t *dmap = audio_engines[portc->audiodev]->dmap_in; p = INB (devc->osdev, devc->base + 0x44); if (p != devc->rec_currbuf) { p = devc->rec_currbuf; f = devc->rec_currfrag; devc->recBDL[p].addr = SWAP32 (dmap->dmabuf_phys + (f * dmap->fragment_size)); /* SIS uses bytes, ali5455 uses samples */ devc->recBDL[p].size = SWAP16 (dmap->fragment_size / 2); devc->recBDL[p].flags = SWAP16 (0xc000); /* IOC interrupts */ OUTB (devc->osdev, p, devc->base + 0x45); /* Set LVD */ devc->rec_frag_index[p] = f; devc->rec_currbuf = (p + 1) % BDL_SIZE; devc->rec_currfrag = (f + 1) % dmap->nfrags; } oss_audio_inputintr (portc->audiodev, 0); } } OUTB (devc->osdev, status, devc->base + 0x46); /* Clear int */ /* MUTEX_EXIT (devc->mutex, flags); */ return serviced; }
Audio routines
static int ALI_audio_set_rate (int dev, int arg) { ALI_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 ALI_audio_set_channels (int dev, short arg) { ALI_portc *portc = audio_engines[dev]->portc; if ((arg == 1) || (arg == 2)) { audio_engines[dev]->flags |= ADEV_STEREOONLY; arg = 2; } else audio_engines[dev]->flags &= ~ADEV_STEREOONLY; 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 ALI_audio_set_format (int dev, unsigned int arg) { ALI_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 ALI_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { return OSS_EINVAL; } static void ALI_audio_trigger (int dev, int state); static void ALI_audio_reset (int dev) { ALI_audio_trigger (dev, 0); } static void ALI_audio_reset_input (int dev) { ALI_portc *portc = audio_engines[dev]->portc; ALI_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); } static void ALI_audio_reset_output (int dev) { ALI_portc *portc = audio_engines[dev]->portc; ALI_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); } /*ARGSUSED*/ static int ALI_audio_open (int dev, int mode, int openflags) { ALI_portc *portc = audio_engines[dev]->portc; ALI_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 (portc->port_type == DF_SPDIF) { if (mode & OPEN_READ) { cmn_err (CE_WARN, "ICH: The S/PDIF device supports only playback\n"); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EIO; } } else { 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 ALI_audio_close (int dev, int mode) { ALI_portc *portc = audio_engines[dev]->portc; ALI_devc *devc = audio_engines[dev]->devc; ALI_audio_reset (dev); portc->open_mode = 0; if (portc->port_type != DF_SPDIF) devc->open_mode &= ~mode; portc->audio_enabled &= ~mode; } /*ARGSUSED*/ static void ALI_audio_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { ALI_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; } /*ARGSUSED*/ static void ALI_audio_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { ALI_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; } static void ALI_audio_trigger (int dev, int state) { ALI_devc *devc = audio_engines[dev]->devc; ALI_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)) { if (portc->port_type == DF_SPDIF) { OUTB (devc->osdev, 0x1d, devc->base + 0x7b); /* Setup intr */ OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x08) | 0x08, devc->base + 0x08); /* start DMA */ } if (portc->port_type == DF_PCM) { OUTB (devc->osdev, 0x1d, devc->base + 0x5b); /* setup intr */ OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x08) | 0x02, devc->base + 0x08); /* start DMA */ } 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->port_type == DF_SPDIF) { OUTB (devc->osdev, 0x00, devc->base + 0x7b); /* reset */ OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x08) & ~0x08, devc->base + 0x08); /* stop DMA */ } if (portc->port_type == DF_PCM) { OUTB (devc->osdev, 0x00, devc->base + 0x5b); /* reset */ OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x08) & ~0x02, devc->base + 0x08); /* stop DMA */ } } } } 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, 0x1d, devc->base + 0x4b); /* Kickstart */ OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x08) | 0x01, devc->base + 0x08); /* stop DMA */ 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, 0x00, devc->base + 0x4b); /* reset */ OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x08) & ~0x01, devc->base + 0x08); /* stop DMA */ } } } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int ALI_audio_prepare_for_input (int dev, int bsize, int bcount) { ALI_devc *devc = audio_engines[dev]->devc; ALI_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_in; int i, n; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); OUTB (devc->osdev, 0x02, devc->base + 0x4b); /* Reset */ OUTL (devc->osdev, devc->recBDL_phys, devc->base + 0x40); /* BDL base */ ac97_recrate (&devc->ac97devc, portc->speed); n = bcount; if (n > BDL_SIZE) n = BDL_SIZE; for (i = 0; i < n; i++) { devc->recBDL[i].addr = SWAP32 (dmap->dmabuf_phys + (i * dmap->fragment_size)); devc->recBDL[i].size = SWAP16 (dmap->fragment_size / 2); devc->recBDL[i].flags = SWAP16 (0xc000); /* IOC interrupts */ devc->rec_frag_index[i] = i; } OUTB (devc->osdev, n - 1, devc->base + 0x45); /* Set last valid descriptor */ devc->rec_currbuf = n % BDL_SIZE; devc->rec_currfrag = n; if (devc->rec_currfrag >= dmap->nfrags) devc->rec_currfrag = 0; portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int ALI_audio_prepare_for_output (int dev, int bsize, int bcount) { ALI_devc *devc = audio_engines[dev]->devc; ALI_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_out; int i, n; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_AUDIO, SNDCTL_MIX_WRITE, 0); ac97_spdif_setup (devc->mixer_dev, portc->speed, portc->bits); if (portc->bits == AFMT_AC3) { portc->channels = 2; portc->bits = 16; } /* do SPDIF out */ if (portc->port_type == DF_SPDIF) { ac97_playrate (&devc->ac97devc, portc->speed); OUTB (devc->osdev, 0x02, devc->base + 0x7b); /* Reset */ OUTL (devc->osdev, devc->spdifBDL_phys, devc->base + 0x70); n = bcount; if (n > BDL_SIZE) n = BDL_SIZE; for (i = 0; i < n; i++) { devc->spdifBDL[i].addr = SWAP32 (dmap->dmabuf_phys + (i * dmap->fragment_size)); devc->spdifBDL[i].size = SWAP16 (dmap->fragment_size / 2); devc->spdifBDL[i].flags = SWAP16 (0xc000); /* IOC interrupts */ devc->spdif_frag_index[i] = i; } OUTB (devc->osdev, n - 1, devc->base + 0x75); /* Set LVI descriptor */ devc->spdif_currbuf = n % BDL_SIZE; devc->spdif_currfrag = n; if (devc->spdif_currfrag >= dmap->nfrags) devc->spdif_currfrag = 0; portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /* else do PCM */ OUTB (devc->osdev, 0x02, devc->base + 0x5b); /* Reset */ OUTL (devc->osdev, devc->playBDL_phys, devc->base + 0x50); ac97_playrate (&devc->ac97devc, portc->speed); /* set default to 2 channel mode */ if (portc->channels == 2) OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x00) & ~0x300, devc->base + 0x00); if (portc->channels == 4) OUTW (devc->osdev, (INW (devc->osdev, devc->base + 0x00) & ~0x300) | 0x100, devc->base + 0x00); if (portc->channels == 6) OUTW (devc->osdev, (INW (devc->osdev, devc->base + 0x00) & ~0x300) | 0x200, devc->base + 0x00); n = bcount; if (n > BDL_SIZE) n = BDL_SIZE; for (i = 0; i < n; i++) { devc->playBDL[i].addr = SWAP32 (dmap->dmabuf_phys + (i * dmap->fragment_size)); devc->playBDL[i].size = SWAP16 (dmap->fragment_size / 2); devc->playBDL[i].flags = SWAP16 (0xc000); /* IOC interrupts */ devc->play_frag_index[i] = i; } OUTB (devc->osdev, n - 1, devc->base + 0x55); /* Set last valid descriptor */ devc->play_currbuf = n % BDL_SIZE; devc->play_currfrag = n; if (devc->play_currfrag >= dmap->nfrags) devc->play_currfrag = 0; portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static const audiodrv_t ALI_audio_driver = { ALI_audio_open, ALI_audio_close, ALI_audio_output_block, ALI_audio_start_input, ALI_audio_ioctl, ALI_audio_prepare_for_input, ALI_audio_prepare_for_output, ALI_audio_reset, NULL, NULL, ALI_audio_reset_input, ALI_audio_reset_output, ALI_audio_trigger, ALI_audio_set_rate, ALI_audio_set_format, ALI_audio_set_channels }; static int init_ALI (ALI_devc * devc) { int my_mixer, my_dev, opts; oss_native_word phaddr; int i; /* ACLink on, warm reset */ OUTL (devc->osdev, 0x80008003, devc->base + 0x00); /*reset SCR */ OUTL (devc->osdev, 0x83838383, devc->base + 0x0c); /*reset pcm in/out FIFO */ OUTL (devc->osdev, 0x83838383, devc->base + 0x1c); /*reset SPDIF/LFEr FIFO */ OUTL (devc->osdev, 0x0028000a, devc->base + 0x10); /*set spdif/pcm in/out */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0xFC) | 0x3, devc->base + 0xFC); /* set up Codec SPDIFOUT slot to 10/11 */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x00) | 0x300000, devc->base + 0x00); /* disable interrupts */ OUTL (devc->osdev, 0x00, devc->base + 0x14); OUTL (devc->osdev, 0x00, devc->base + 0x18); devc->bdlBuffer = CONTIG_MALLOC (devc->osdev, 4 * 32 * 32, MEMLIMIT_32BITS, &phaddr, devc->bdl_dma_handle); if (devc->bdlBuffer == NULL) { cmn_err (CE_WARN, "Failed to allocate BDL\n"); return 0; } devc->playBDL = (bdl_t *) devc->bdlBuffer; devc->playBDL_phys = phaddr; devc->recBDL = (bdl_t *) (devc->bdlBuffer + (1 * 32 * 32)); devc->recBDL_phys = phaddr + (1 * 32 * 32); devc->spdifBDL = (bdl_t *) (devc->bdlBuffer + (2 * 32 * 32)); devc->spdifBDL_phys = phaddr + (2 * 32 * 32);
Init mixer
my_mixer = ac97_install (&devc->ac97devc, "AC97 Mixer", ac97_read, ac97_write, devc, devc->osdev); if (my_mixer == -1) return 0; /* No mixer */ devc->mixer_dev = my_mixer; /* enable S/PDIF */ devc->ac97devc.spdif_slot = SPDIF_SLOT1011; ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_ENABLE, SNDCTL_MIX_WRITE, 1); #if 0 /* enable variable rate mode */ ac97_write (devc, 0x2a, ac97_read (devc, 0x2a) | 9); if (!(ac97_read (devc, 0x2a) & 1)) DDB (cmn_err (CE_WARN, "VRA not supported...using GRC\n")); #endif for (i = 0; i < MAX_PORTC; i++) { ALI_portc *portc = &devc->portc[i]; char tmp_name[100]; int port_fmt = DF_PCM; int formats = AFMT_S16_LE | AFMT_AC3; strcpy (tmp_name, devc->chip_name); opts = ADEV_AUTOMODE | ADEV_16BITONLY | ADEV_STEREOONLY; portc->port_type = DF_PCM; if (!ac97_varrate (&devc->ac97devc)) { opts |= ADEV_FIXEDRATE; } if (i == 0) { opts |= ADEV_DUPLEX; strcpy (tmp_name, devc->chip_name); } if (i == 1) { opts |= ADEV_DUPLEX | ADEV_SHADOW; strcpy (tmp_name, devc->chip_name); } if (i == 2) { sprintf (tmp_name, "%s (S/PDIF)", devc->chip_name); opts |= ADEV_NOINPUT | ADEV_SPECIAL | ADEV_FIXEDRATE; port_fmt = DF_SPDIF; } if ((my_dev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, tmp_name, &ALI_audio_driver, sizeof (audiodrv_t), opts, formats, devc, -1)) < 0) { my_dev = -1; return 0; } else { audio_engines[my_dev]->portc = portc; audio_engines[my_dev]->mixer_dev = my_mixer; audio_engines[my_dev]->min_rate = (opts & ADEV_FIXEDRATE) ? 48000 : 5000; audio_engines[my_dev]->max_rate = 48000; audio_engines[my_dev]->caps |= PCM_CAP_FREERATE; /*audio_engines[my_dev]->min_block = 4096; */ /*audio_engines[my_dev]->max_block = 4096; */ audio_engines[my_dev]->min_channels = 2; audio_engines[my_dev]->max_channels = 6; portc->open_mode = 0; portc->audio_enabled = 0; portc->audiodev = my_dev; portc->port_type = port_fmt; if (audio_engines[my_dev]->flags & ADEV_FIXEDRATE) audio_engines[my_dev]->fixed_rate = 48000; #ifdef CONFIG_OSS_VMIX if (i == 0) vmix_attach_audiodev(devc->osdev, my_dev, -1, 0); #endif } } return 1; } int oss_ali5455_attach (oss_device_t * osdev) { unsigned char pci_irq_line, pci_revision /*, pci_latency */ ; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr0; ALI_devc *devc; DDB (cmn_err (CE_WARN, "Entered ALI AC97 probe routine\n")); pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); if ((vendor != ALI_VENDOR_ID) || (device != ALI_DEVICE_5455)) 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_ioaddr0); if (pci_ioaddr0 == 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; /* Remove I/O space marker in bit 0. */ devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr0); devc->base &= ~0xF; pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; pci_write_config_word (osdev, PCI_COMMAND, pci_command); devc->chip_name = "ALI M5455"; 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, ALIintr, NULL) < 0) { cmn_err (CE_WARN, "Unable to install interrupt handler\n"); return 0; } return init_ALI (devc); /* Detected */ } int oss_ali5455_detach (oss_device_t * osdev) { ALI_devc *devc = (ALI_devc *) osdev->devc; if (oss_disable_device (osdev) < 0) return 0; /* disable S/PDIF */ if (devc->mixer_dev) ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_ENABLE, SNDCTL_MIX_WRITE, 0); /* disable interrupts */ OUTL (devc->osdev, 0x00, devc->base + 0x14); OUTL (devc->osdev, 0x00, devc->base + 0x18); oss_unregister_interrupts (devc->osdev); if (devc->bdlBuffer) { CONTIG_FREE (devc->osdev, devc->bdlBuffer, 4 * 32 * 32, devc->bdl_dma_handle); devc->bdlBuffer = NULL; } MUTEX_CLEANUP (devc->mutex); MUTEX_CLEANUP (devc->low_mutex); UNMAP_PCI_IOADDR (devc->osdev, 0); oss_unregister_device (osdev); return 1; }