/* * Purpose: Driver for the ALI 5455 (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_ali5455_cfg.h" #include #include #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; }