/* * uPurpose: Driver for the VIA VT82C686A AC97 audio controller */ /* * * This file is part of Open Sound System. * * Copyright (C) 4Front Technologies 1996-2008. * * This this source file is released under GPL v2 license (no other versions). * See the COPYING file included in the main directory of this source * distribution for the license terms and conditions. * */ #include "oss_via97_cfg.h" #include "oss_pci.h" #include "ac97.h" #define VIA_VENDOR_ID 0x1106 #define VIA_82C686 0x3058 #define CODEC_TIMEOUT_COUNT 500 #define AC97CODEC 0x80 /*Access AC97 Codec */ #define IN_CMD 0x01000000 /*busy in sending */ #define STA_VALID 0x02000000 /*1:status data is valid */ #define CODEC_RD 0x00800000 /*Read CODEC status */ #define CODEC_INDEX 0x007F0000 /*Index of command register to access */ #define CODEC_DATA 0x0000FFFF /*AC97 status register data */ typedef struct { unsigned int phaddr; unsigned int flags; } SGD_entry; typedef struct { int open_mode; int speed, bits, channels; int audiodev; int audio_enabled; int trigger_bits; } via97_portc; typedef struct via97_devc { oss_device_t *osdev; oss_native_word base; int irq; int open_mode; char *chip_name; /* * Mixer */ ac97_devc ac97devc; /* * MIDI */ int mpu_base; int mpu_attached; /* Audio parameters */ via97_portc portc[2]; oss_mutex_t mutex; /* For normal locking */ oss_mutex_t low_mutex; /* For low level routines */ /* Memory allocation and scatter/gather info */ #define BUFFER_SIZE (128*1024) /* NOTE! Check SGD_ALLOC if changing SGD_SIZE */ #define MIN_BLOCK 64 #define SGD_SIZE (BUFFER_SIZE/MIN_BLOCK) #define SGD_TOTAL_SIZE (2*SGD_SIZE) #define SGD_ALLOC (SGD_TOTAL_SIZE*8) SGD_entry *SGD_table; oss_native_word SGD_table_phys; int play_sgd_ptr, rec_sgd_ptr; oss_dma_handle_t sgd_dma_handle; unsigned int play_sgd_phys, rec_sgd_phys; } via97_devc; static int ac97_read (void *devc_, int wIndex) { oss_native_word dwWriteValue = 0, dwTmpValue; unsigned int i = 0; oss_native_word flags; via97_devc *devc = devc_; /* Index has only 7 bit */ if (wIndex > 0x7F) return 0; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); dwWriteValue = ((oss_native_word) wIndex << 16) + CODEC_RD; OUTL (devc->osdev, dwWriteValue, devc->base + AC97CODEC); oss_udelay (100); /* Check AC CODEC access time out */ for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) { /* if send command over, break */ if (INL (devc->osdev, devc->base + AC97CODEC) & STA_VALID) break; oss_udelay (50); } if (i == CODEC_TIMEOUT_COUNT) { MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } /* Check if Index still ours? If yes, return data, else return FAIL */ dwTmpValue = INL (devc->osdev, devc->base + AC97CODEC); OUTB (devc->osdev, 0x02, devc->base + AC97CODEC + 3); if (((dwTmpValue & CODEC_INDEX) >> 16) == wIndex) { MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return ((int) dwTmpValue & CODEC_DATA); } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } static int ac97_write (void *devc_, int wIndex, int wData) { oss_native_word dwWriteValue = 0; unsigned int i = 0; oss_native_word flags; via97_devc *devc = devc_; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); dwWriteValue = ((oss_native_word) wIndex << 16) + wData; OUTL (devc->osdev, dwWriteValue, devc->base + AC97CODEC); oss_udelay (100); /* Check AC CODEC access time out */ for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) { /* if send command over, break */ if (!(INL (devc->osdev, devc->base + AC97CODEC) & IN_CMD)) break; oss_udelay (50); } if (i == CODEC_TIMEOUT_COUNT) { MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return (-1); } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 1; } static int via97intr (oss_device_t * osdev) { via97_devc *devc = (via97_devc *) osdev->devc; via97_portc *portc; unsigned char status; int serviced = 0; int i; #ifdef OBSOLETED_STUFF if (devc->mpu_attached) { if (uart401intr (INT_HANDLER_CALL (devc->irq))) serviced = 1; } #endif /* Handle playback interrupt */ status = INB (devc->osdev, devc->base + 0x00); if (status & 0x01) { serviced = 1; for (i = 0; i < 2; i++) { portc = &devc->portc[i]; /* IOC Interrupt */ if ((portc->trigger_bits & PCM_ENABLE_OUTPUT) && (status & 0x01)) oss_audio_outputintr (portc->audiodev, 0); } } OUTB (devc->osdev, status | 0x01, devc->base + 0x00); /* Handle record interrupt */ status = INB (devc->osdev, devc->base + 0x10); if (status & 0x01) { serviced = 1; for (i = 0; i < 2; i++) { portc = &devc->portc[i]; /* IOC Interrupt */ if ((portc->trigger_bits & PCM_ENABLE_INPUT) && (status & 0x01)) oss_audio_inputintr (portc->audiodev, 0); } } OUTB (devc->osdev, status | 0x01, devc->base + 0x10); return serviced; } /* * Audio routines */ static int via97_audio_set_rate (int dev, int arg) { via97_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->speed; if (audio_engines[dev]->flags & ADEV_FIXEDRATE) arg = 48000; if (arg > 48000) arg = 48000; if (arg < 5000) arg = 5000; portc->speed = arg; return portc->speed; } static short via97_audio_set_channels (int dev, short arg) { via97_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->channels; if ((arg != 1) && (arg != 2)) return portc->channels = 2; portc->channels = arg; return portc->channels; } static unsigned int via97_audio_set_format (int dev, unsigned int arg) { via97_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->bits; if (!(arg & (AFMT_U8 | AFMT_S16_LE))) return portc->bits = AFMT_S16_LE; portc->bits = arg; return portc->bits; } /*ARGSUSED*/ static int via97_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { return OSS_EINVAL; } static void via97_audio_trigger (int dev, int state); static void via97_audio_reset (int dev) { via97_audio_trigger (dev, 0); } static void via97_audio_reset_input (int dev) { via97_portc *portc = audio_engines[dev]->portc; via97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); } static void via97_audio_reset_output (int dev) { via97_portc *portc = audio_engines[dev]->portc; via97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); } /*ARGSUSED*/ static int via97_audio_open (int dev, int mode, int open_flags) { via97_portc *portc = audio_engines[dev]->portc; via97_devc *devc = audio_engines[dev]->devc; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } if (devc->open_mode & mode) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } devc->open_mode |= mode; portc->open_mode = mode; portc->audio_enabled &= ~mode; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static void via97_audio_close (int dev, int mode) { via97_portc *portc = audio_engines[dev]->portc; via97_devc *devc = audio_engines[dev]->devc; via97_audio_reset (dev); portc->open_mode = 0; devc->open_mode &= ~mode; portc->audio_enabled &= ~mode; } /*ARGSUSED*/ static void via97_audio_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { via97_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; } /*ARGSUSED*/ static void via97_audio_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { via97_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; } static void via97_audio_trigger (int dev, int state) { via97_devc *devc = audio_engines[dev]->devc; via97_portc *portc = audio_engines[dev]->portc; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode & OPEN_WRITE) { if (state & PCM_ENABLE_OUTPUT) { if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) && !(portc->trigger_bits & PCM_ENABLE_OUTPUT)) { OUTB (devc->osdev, 0x80, devc->base + 0x01); portc->trigger_bits |= PCM_ENABLE_OUTPUT; } } else { if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) && (portc->trigger_bits & PCM_ENABLE_OUTPUT)) { portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; OUTB (devc->osdev, 0x40, devc->base + 0x01); } } } if (portc->open_mode & OPEN_READ) { if (state & PCM_ENABLE_INPUT) { if ((portc->audio_enabled & PCM_ENABLE_INPUT) && !(portc->trigger_bits & PCM_ENABLE_INPUT)) { OUTB (devc->osdev, 0x80, devc->base + 0x11); portc->trigger_bits |= PCM_ENABLE_INPUT; } } else { if ((portc->audio_enabled & PCM_ENABLE_INPUT) && (portc->trigger_bits & PCM_ENABLE_INPUT)) { portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; OUTB (devc->osdev, 0x40, devc->base + 0x11); } } } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int via97_audio_prepare_for_input (int dev, int bsize, int bcount) { via97_devc *devc = audio_engines[dev]->devc; via97_portc *portc = audio_engines[dev]->portc; int i, sgd_ptr; dmap_t *dmap = audio_engines[dev]->dmap_in; unsigned char tmp; oss_native_word flags; if (audio_engines[dev]->dmap_in->dmabuf_phys == 0) return OSS_ENOSPC; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); tmp = 0x81; /* Auto start at EOL, interrupt on FLAG */ if (portc->bits != AFMT_U8) tmp |= 0x20; if (portc->channels != 1) tmp |= 0x10; OUTB (devc->osdev, tmp, devc->base + 0x12); ac97_recrate (&devc->ac97devc, portc->speed); sgd_ptr = devc->rec_sgd_ptr; for (i = 0; i < dmap->nfrags; i++) { if (sgd_ptr >= SGD_TOTAL_SIZE) { cmn_err (CE_WARN, "Out of Record SGD entries\n"); return OSS_ENOSPC; } devc->SGD_table[sgd_ptr].phaddr = dmap->dmabuf_phys + (i * dmap->fragment_size); devc->SGD_table[sgd_ptr].flags = 0x40000000 | dmap->fragment_size; sgd_ptr++; } devc->SGD_table[sgd_ptr - 1].flags |= 0x80000000; /* EOL */ devc->rec_sgd_phys = devc->SGD_table_phys + devc->rec_sgd_ptr * sizeof (SGD_entry); OUTL (devc->osdev, devc->rec_sgd_phys, devc->base + 0x14); portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int via97_audio_prepare_for_output (int dev, int bsize, int bcount) { via97_devc *devc = audio_engines[dev]->devc; via97_portc *portc = audio_engines[dev]->portc; int i, sgd_ptr; dmap_t *dmap = audio_engines[dev]->dmap_out; unsigned char tmp; oss_native_word flags; if (audio_engines[dev]->dmap_out->dmabuf_phys == 0) return OSS_ENOSPC; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); tmp = 0x81; /* Auto start at EOL, interrupt on FLAG */ if (portc->bits != AFMT_U8) tmp |= 0x20; if (portc->channels != 1) tmp |= 0x10; OUTB (devc->osdev, tmp, devc->base + 0x02); ac97_playrate (&devc->ac97devc, portc->speed); sgd_ptr = devc->play_sgd_ptr; for (i = 0; i < dmap->nfrags; i++) { if (sgd_ptr >= SGD_TOTAL_SIZE) { cmn_err (CE_WARN, "Out of Playback SGD entries\n"); return OSS_ENOSPC; } devc->SGD_table[sgd_ptr].phaddr = dmap->dmabuf_phys + (i * dmap->fragment_size); devc->SGD_table[sgd_ptr].flags = 0x40000000 | dmap->fragment_size; sgd_ptr++; } devc->SGD_table[sgd_ptr - 1].flags |= 0x80000000; /* EOL */ devc->play_sgd_phys = devc->SGD_table_phys + devc->play_sgd_ptr * sizeof (SGD_entry); OUTL (devc->osdev, devc->play_sgd_phys, devc->base + 0x04); portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static int via97_alloc_buffer (int dev, dmap_t * dmap, int direction) { int err; if (dmap->dmabuf != NULL) return 0; if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0) return err; if (dmap->buffsize > BUFFER_SIZE) { cmn_err (CE_WARN, "Too large DMA buffer (%d/%d) - truncated\n", dmap->buffsize, BUFFER_SIZE); dmap->buffsize = BUFFER_SIZE; } return 0; } static int via97_get_buffer_pointer (int dev, dmap_t * dmap, int direction) { /* * Unfortunately the VIA chip seems to raise interrupt about 32 bytes before * the DMA pointer moves to a new fragment. This means that the bytes value * returned by SNDCTL_DSP_GET[]PTR will be bogus during few samples before * the pointer wraps back to the beginning of buffer. * * If mmap() is not being used this routine will return 0 during this period. */ via97_devc *devc = audio_engines[dev]->devc; unsigned int ptr, sgd; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); if (direction == PCM_ENABLE_OUTPUT) { ptr = INL (devc->osdev, devc->base + 0x0c) & 0xffffff; sgd = ((INL (devc->osdev, devc->base + 0x04) - devc->play_sgd_phys) / 8) - 1; ptr = dmap->fragment_size - ptr; ptr = ptr + (sgd * dmap->fragment_size); //cmn_err(CE_CONT, "%d/%d\n", sgd, ptr); } else { ptr = INL (devc->osdev, devc->base + 0x1c) & 0xffffff; sgd = ((INL (devc->osdev, devc->base + 0x14) - devc->rec_sgd_phys) / 8) - 1; ptr = dmap->fragment_size - ptr; ptr = ptr + (sgd * dmap->fragment_size); } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return ptr; } static const audiodrv_t via97_audio_driver = { via97_audio_open, via97_audio_close, via97_audio_output_block, via97_audio_start_input, via97_audio_ioctl, via97_audio_prepare_for_input, via97_audio_prepare_for_output, via97_audio_reset, NULL, NULL, via97_audio_reset_input, via97_audio_reset_output, via97_audio_trigger, via97_audio_set_rate, via97_audio_set_format, via97_audio_set_channels, NULL, NULL, NULL, NULL, via97_alloc_buffer, NULL, /* via97_free_buffer, */ NULL, NULL, via97_get_buffer_pointer }; #ifdef OBSOLETED_STUFF /* * This device has "ISA style" MIDI and FM subsystems. Such devices don't * use PCI config space for the I/O ports and interrupts. Instead the driver * needs to allocate proper resources itself. This functionality is no longer * possible. For this reason the MIDI and FM parts are not accessible. */ static void attach_mpu (via97_devc * devc) { struct address_info hw_config; hw_config.io_base = devc->mpu_base; hw_config.irq = -devc->irq; hw_config.dma = -1; hw_config.dma2 = -1; hw_config.always_detect = 0; hw_config.name = "VIA97 external MIDI"; hw_config.driver_use_1 = 0; hw_config.driver_use_2 = 0; hw_config.osdev = devc->osdev; #ifdef CREATE_OSP CREATE_OSP (hw_config.osdev); #endif hw_config.card_subtype = 0; #if 1 if (!probe_uart401 (&hw_config)) { cmn_err (CE_WARN, "MPU-401 was not detected\n"); return; } #endif DDB (cmn_err (CE_WARN, "MPU-401 detected - Good\n")); attach_uart401 (&hw_config); devc->mpu_attached = 1; } static void unload_mpu (via97_devc * devc) { struct address_info hw_config; if (devc == NULL || !devc->mpu_attached) return; devc->mpu_attached = 0; hw_config.io_base = devc->mpu_base; hw_config.irq = devc->irq; hw_config.dma = -1; hw_config.dma2 = -1; hw_config.always_detect = 0; hw_config.name = "VIA97"; hw_config.driver_use_1 = 0; hw_config.driver_use_2 = 0; hw_config.osdev = devc->osdev; #ifdef CREATE_OSP CREATE_OSP (hw_config.osdev); #endif hw_config.card_subtype = 0; unload_uart401 (&hw_config); } #endif static int init_via97 (via97_devc * devc) { int my_mixer, adev, opts, i; int first_dev = 0; oss_native_word phaddr; /* * Allocate the SGD buffers */ if (devc->SGD_table == NULL) { devc->SGD_table = CONTIG_MALLOC (devc->osdev, SGD_ALLOC, MEMLIMIT_32BITS, &phaddr, devc->sgd_dma_handle); if (devc->SGD_table == NULL) return OSS_ENOSPC; devc->SGD_table_phys = phaddr; } /* * Allocate SGD entries for recording and playback. */ devc->rec_sgd_ptr = 0; devc->play_sgd_ptr = SGD_SIZE; /* * Init mixer */ my_mixer = ac97_install (&devc->ac97devc, "VIA82C686 AC97 Mixer", ac97_read, ac97_write, devc, devc->osdev); if (my_mixer == -1) return 0; /* No mixer */ mixer_devs[my_mixer]->priority = 10; /* Known motherboard device */ /* enable variable rate */ ac97_write (devc, 0x2a, 0x01); #ifdef OBSOLETED_STUFF DDB (cmn_err (CE_WARN, "Probing UART401 at 0x%x\n", devc->mpu_base)); attach_mpu (devc); #endif for (i = 0; i < 2; i++) { via97_portc *portc = &devc->portc[i]; char tmp_name[100]; opts = ADEV_AUTOMODE; if (!ac97_varrate (&devc->ac97devc)) { opts |= ADEV_FIXEDRATE; } if (i == 0) { opts |= ADEV_DUPLEX; strcpy (tmp_name, "VIA 82C686 AC97 Controller"); } else { opts |= ADEV_DUPLEX | ADEV_SHADOW; strcpy (tmp_name, "VIA 82C686 AC97 Controller"); } if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, tmp_name, &via97_audio_driver, sizeof (audiodrv_t), opts, AFMT_U8 | AFMT_S16_LE, devc, -1)) < 0) { adev = -1; return 0; } else { if (i == 0) first_dev = adev; audio_engines[adev]->portc = portc; audio_engines[adev]->rate_source = first_dev; audio_engines[adev]->mixer_dev = my_mixer; audio_engines[adev]->min_rate = 8000; audio_engines[adev]->max_rate = 48000; audio_engines[adev]->caps |= PCM_CAP_FREERATE; portc->open_mode = 0; portc->audio_enabled = 0; portc->audiodev = adev; audio_engines[adev]->min_block = MIN_BLOCK; audio_engines[adev]->max_block = 4 * 1024; audio_engines[adev]->max_fragments = SGD_SIZE; if (opts & ADEV_FIXEDRATE) { audio_engines[adev]->fixed_rate = 48000; audio_engines[adev]->min_rate = 48000; audio_engines[adev]->max_rate = 48000; } #ifdef CONFIG_OSS_VMIX if (i == 0) vmix_attach_audiodev(devc->osdev, adev, -1, 0); #endif } } return 1; } int oss_via97_attach (oss_device_t * osdev) { unsigned char tmp, pci_irq_line, pci_revision /*, pci_latency */ ; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr; int mpu_base; via97_devc *devc; DDB (cmn_err (CE_WARN, "Entered VT82C686 probe routine\n")); pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); if (vendor != VIA_VENDOR_ID || device != VIA_82C686) return 0; pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); pci_read_config_word (osdev, PCI_COMMAND, &pci_command); pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr); if (pci_ioaddr == 0) { cmn_err (CE_WARN, "I/O address not assigned by BIOS.\n"); return 0; } if (pci_irq_line == 0) { cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d).\n", pci_irq_line); return 0; } if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL) { cmn_err (CE_WARN, "Out of memory\n"); return 0; } devc->osdev = osdev; osdev->devc = devc; devc->chip_name = "VIA VT82C686"; /* Enable codec, etc */ pci_write_config_byte (osdev, 0x41, 0xc0); oss_udelay (10); pci_read_config_byte (osdev, 0x41, &tmp); pci_write_config_byte (osdev, 0x41, tmp | 0x0c); oss_udelay (10); /* setup game port/MIDI */ pci_write_config_byte (osdev, 0x42, 0x2a); /* disable FM io */ pci_write_config_byte (osdev, 0x48, 0x00); /* Enable interrupt on FLAG and on EOL */ tmp = INB (devc->osdev, devc->base + 0x22); OUTB (devc->osdev, tmp | 0x83, devc->base + 0x22); /* Enable MPU401 */ pci_read_config_byte (osdev, 0x8, &tmp); if ((tmp & 0xff) >= 0x20) { pci_read_config_byte (osdev, 0x42, &tmp); pci_write_config_byte (osdev, 0x42, tmp & 0x3f); } pci_read_config_byte (osdev, 0x43, &tmp); switch ((tmp & 0x0c) >> 2) { case 0: mpu_base = 0x300; break; case 1: mpu_base = 0x310; break; case 2: mpu_base = 0x320; break; default: mpu_base = 0x330; break; } /* map PCI IO address space */ devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); /* Remove I/O space marker in bit 0. */ devc->base &= ~0x3; pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; pci_write_config_word (osdev, PCI_COMMAND, pci_command); devc->irq = pci_irq_line; devc->mpu_base = mpu_base; devc->SGD_table = NULL; devc->mpu_attached = 0; devc->open_mode = 0; MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1); oss_register_device (osdev, devc->chip_name); if (oss_register_interrupts (devc->osdev, 0, via97intr, NULL) < 0) { cmn_err (CE_WARN, "Unable to register interrupts\n"); return 0; } return init_via97 (devc); /* Detected */ } int oss_via97_detach (oss_device_t * osdev) { via97_devc *devc = (via97_devc *) osdev->devc; if (oss_disable_device (devc->osdev) < 0) return 0; OUTB (devc->osdev, 0x40, devc->base + 0x01); OUTB (devc->osdev, 0x40, devc->base + 0x11); OUTB (devc->osdev, 0, devc->base + 0x02); OUTB (devc->osdev, 0, devc->base + 0x12); OUTL (devc->osdev, 0, devc->base + 0x04); OUTL (devc->osdev, 0, devc->base + 0x14); OUTL (devc->osdev, 0, devc->base + 0x22); oss_udelay (30); #ifdef OBSOLETED_STUFF if (devc->mpu_attached) unload_mpu (devc); #endif oss_unregister_interrupts (devc->osdev); if (devc->SGD_table != NULL) { CONTIG_FREE (devc->osdev, devc->SGD_table, SGD_ALLOC, devc->sgd_dma_handle); devc->SGD_table = NULL; } MUTEX_CLEANUP (devc->mutex); MUTEX_CLEANUP (devc->low_mutex); UNMAP_PCI_IOADDR (devc->osdev, 0); oss_unregister_device (devc->osdev); return 1; }