diff options
Diffstat (limited to 'kernel/drv/oss_geode/oss_geode.c')
-rw-r--r-- | kernel/drv/oss_geode/oss_geode.c | 852 |
1 files changed, 852 insertions, 0 deletions
diff --git a/kernel/drv/oss_geode/oss_geode.c b/kernel/drv/oss_geode/oss_geode.c new file mode 100644 index 0000000..284aba3 --- /dev/null +++ b/kernel/drv/oss_geode/oss_geode.c @@ -0,0 +1,852 @@ +/* + * Purpose: Driver for the NS/Cyrix/AMD Geode 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_geode_cfg.h" +#include "oss_pci.h" +#include "ac97.h" + +#define CYRIX_VENDOR_ID 0x1078 +#define CYRIX_GEODE 0x0103 +#define AMD_VENDOR_ID 0x1022 +#define AMD_CS5536_ID 0x2093 +#define NATIONAL_VENDOR_ID 0x100b +#define NATIONAL_SC1200 0x0503 + +#define CS_READL(devc,addr) INL((devc)->osdev, (unsigned int)((devc)->physaddr) + (addr)) +#define CS_READW(devc,addr) INW((devc)->osdev, (unsigned int)((devc)->physaddr) + (addr)) +#define CS_READB(devc,addr) INB((devc)->osdev, (unsigned int)((devc)->physaddr) + (addr)) +#define CS_WRITEL(devc,addr,val) OUTL((devc)->osdev, (val), (unsigned int)((devc)->physaddr) + (addr)) +#define CS_WRITEB(devc,addr,val) OUTB((devc)->osdev, (val), (unsigned int)((devc)->physaddr) + (addr)) + +#define MAX_PORTC 2 + +typedef struct +{ + unsigned int ptr; + unsigned int size; +#define PRD_EOT 0x80000000 +#define PRD_EOP 0x40000000 +#define PRD_JMP 0x20000000 +} +PRD_rec; + +typedef struct +{ + int open_mode; + int speed; + int bits; + int channels; + int trigger_bits; + int audio_enabled; + int audiodev; +} +geode_portc; + +typedef struct +{ + oss_device_t *osdev; + int physaddr; + void *linaddr; + int irq; + char *chip_name; + oss_mutex_t mutex; + oss_mutex_t low_mutex; + + int mixer_dev; + ac97_devc ac97devc; + + geode_portc portc[MAX_PORTC]; + int open_mode; + + PRD_rec *prdin, *prdout; + unsigned long prdin_phys, prdout_phys; + oss_dma_handle_t prdin_dma_handle, prdout_dma_handle; + + unsigned int chip; +} +geode_devc; + +static int +geodeintr_5530 (oss_device_t * osdev) +{ + + geode_devc *devc = osdev->devc; + geode_portc *portc; + int i, n, irqstat; + int serviced = 0; + unsigned int pos; + int ptr; + + irqstat = CS_READW (devc, 0x12); + if (irqstat & 3) /* either gpio or gpio wakeup */ + { + CS_READL (devc, 0x00); + serviced = 1; + } + + for (i = 0; i < MAX_PORTC; i++) + { + portc = &devc->portc[i]; + + if ((portc->trigger_bits & PCM_ENABLE_OUTPUT) && (irqstat & 4)) + { + dmap_t *dmap; + dmap = audio_engines[portc->audiodev]->dmap_out; + CS_READB (devc, 0x21); /* ack interrupt */ + pos = CS_READL (devc, 0x24); + pos = (pos - devc->prdout_phys) / 8; + ptr = pos; + ptr--; + + if (ptr < 0) + ptr = 0; + ptr %= dmap->nfrags; + + n = 0; + while (ptr != dmap_get_qhead (dmap) && n++ < dmap->nfrags) + oss_audio_outputintr (portc->audiodev, 0); + serviced = 1; + } + + + if ((portc->trigger_bits & PCM_ENABLE_INPUT) && (irqstat & 8)) + { + dmap_t *dmap; + + dmap = audio_engines[portc->audiodev]->dmap_in; + + pos = CS_READL (devc, 0x2c); + pos = (pos - devc->prdin_phys) / 8; + ptr = pos; + ptr--; + + if (ptr < 0) + ptr = 0; + ptr %= dmap->nfrags; + + n = 0; + while (ptr != dmap_get_qtail (dmap) && n++ < dmap->nfrags) + oss_audio_inputintr (portc->audiodev, 0); + + serviced = 1; + } + } + return serviced; +} + +static int +geodeintr_5536 (oss_device_t * osdev) +{ + + geode_devc *devc = osdev->devc; + geode_portc *portc; + int i; + int serviced = 0; + int irqstat; + + irqstat = CS_READW (devc, 0x12); + + if (irqstat & 3) /* either gpio or gpio wakeup */ + { + CS_READL (devc, 0x00); + serviced = 1; + } + + for (i = 0; i < MAX_PORTC; i++) + { + portc = &devc->portc[i]; + + if ((portc->trigger_bits & PCM_ENABLE_OUTPUT) && (irqstat & 4)) + if (CS_READB (devc, 0x21) & 1) + { + oss_audio_outputintr (portc->audiodev, 0); + serviced = 1; + + } + + + if ((portc->trigger_bits & PCM_ENABLE_INPUT) && (irqstat & 8)) + if (CS_READB (devc, 0x29) & 1) + { + oss_audio_inputintr (portc->audiodev, 0); + serviced = 1; + } + } + return serviced; +} + +static int +codec_valid_data (geode_devc * devc, int command, unsigned int *data) +{ + int y; + for (y = 0; y < 1000; y++) + { + *data = CS_READL (devc, 0x08); + + if ((*data & 0x7F000000) != (command & 0x7F000000)) + continue; + if (devc->chip == AMD_CS5536_ID) + { + if ((*data & 0x00020000) == 0x00020000) + return 1; + } + else + { + if ((*data & 0x00030000) == 0x00030000) + return 1; + } + } + return 0; +} + +static int +ac97_read (void *devc_, int wAddr) +{ + geode_devc *devc = devc_; + int i; + unsigned int x, y; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + + for (i = 0; i < 10000; i++) + if (!(CS_READL (devc, 0x0c) & 0x10000)) + break; + + for (i = 0; i < 10000; i++) + { + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + x = (wAddr << 24) | 0x80000000; + if (devc->chip == AMD_CS5536_ID) + x = x | 0x10000; /* this chip also need the NEW flag set */ + CS_WRITEL (devc, 0x0c, x); + + if (codec_valid_data (devc, x, &y)) + { + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + return y & 0xffff; + } + } + + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + return OSS_EIO; +} + +static int +ac97_write (void *devc_, int wAddr, int wData) +{ + geode_devc *devc = devc_; + unsigned int tmp, i; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + + tmp = (wAddr << 24) | wData; + if (devc->chip == AMD_CS5536_ID) + tmp = (tmp | 0x10000) & ~0x80000000; + CS_WRITEL (devc, 0x0c, tmp); + + /* wait for codec to be ready */ + for (i = 0; i <= 10000; i++) + if (!(CS_READL (devc, 0x0c) & 0x10000)) + break; + + if (i >= 10000) + { + cmn_err (CE_WARN, "AC97 write timeout\n"); + } + + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + return 0; +} + +static int +geode_audio_set_rate (int dev, int arg) +{ + geode_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 < 8000) + arg = 8000; + portc->speed = arg; + return portc->speed; +} + +static short +geode_audio_set_channels (int dev, short arg) +{ + geode_portc *portc = audio_engines[dev]->portc; + + if (arg == 0) + return portc->channels; + + if (audio_engines[dev]->flags & ADEV_STEREOONLY) + arg = 2; + + if ((arg != 1) && (arg != 2)) + return portc->channels; + portc->channels = arg; + + return portc->channels; +} + +static unsigned int +geode_audio_set_format (int dev, unsigned int arg) +{ + geode_portc *portc = audio_engines[dev]->portc; + + if (arg == 0) + return portc->bits; + + if (audio_engines[dev]->flags & ADEV_16BITONLY) + arg = 16; + + if (!(arg & (AFMT_U8 | AFMT_S16_LE))) + return portc->bits; + portc->bits = arg; + return portc->bits; +} + +/*ARGSUSED*/ +static int +geode_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +static void geode_audio_trigger (int dev, int state); + +static void +geode_audio_reset (int dev) +{ + geode_audio_trigger (dev, 0); +} + +static void +geode_audio_reset_input (int dev) +{ + geode_portc *portc = audio_engines[dev]->portc; + geode_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); +} + +static void +geode_audio_reset_output (int dev) +{ + geode_portc *portc = audio_engines[dev]->portc; + geode_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); +} + +/*ARGSUSED*/ +static int +geode_audio_open (int dev, int mode, int open_flags) +{ + geode_portc *portc = audio_engines[dev]->portc; + geode_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 +geode_audio_close (int dev, int mode) +{ + geode_portc *portc = audio_engines[dev]->portc; + geode_devc *devc = audio_engines[dev]->devc; + + geode_audio_reset (dev); + portc->open_mode = 0; + devc->open_mode &= ~mode; + portc->audio_enabled &= ~mode; +} + +/*ARGSUSED*/ +static void +geode_audio_output_block (int dev, oss_native_word buf, int count, + int fragsize, int intrflag) +{ + geode_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; +} + +/*ARGSUSED*/ +static void +geode_audio_start_input (int dev, oss_native_word buf, int count, + int fragsize, int intrflag) +{ + geode_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; +} + +static void +geode_audio_trigger (int dev, int state) +{ + geode_devc *devc = audio_engines[dev]->devc; + geode_portc *portc = audio_engines[dev]->portc; + int i; + 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)) + { + CS_WRITEB (devc, 0x20, 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; + CS_WRITEB (devc, 0x20, 0x00); + for (i = 0; i < 512; i++) + { + devc->prdout[i].size = PRD_EOT; /* Stop */ + } + } + } + } + + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + { + if ((portc->audio_enabled & PCM_ENABLE_INPUT) && + !(portc->trigger_bits & PCM_ENABLE_INPUT)) + { + CS_WRITEB (devc, 0x28, 0x09); + 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; + + CS_WRITEB (devc, 0x28, 0x00); + for (i = 0; i < 512; i++) + { + devc->prdin[i].size = PRD_EOT; /* Stop */ + } + } + } + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + +/*ARGSUSED*/ +static int +geode_audio_prepare_for_input (int dev, int bsize, int bcount) +{ + geode_devc *devc = audio_engines[dev]->devc; + geode_portc *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_in; + int i; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + ac97_recrate (&devc->ac97devc, portc->speed); + + + /* clear out the prd table */ + memset (devc->prdin, 0, 512 * sizeof (PRD_rec)); + + /* Initialize PRD entries */ + for (i = 0; i < dmap->nfrags; i++) + { + devc->prdin[i].ptr = dmap->dmabuf_phys + (i * dmap->fragment_size); + devc->prdin[i].size = dmap->fragment_size | PRD_EOP; + } + + /* Initialize the JMP entry back to the beginning */ + devc->prdin[dmap->nfrags].ptr = devc->prdin_phys; + devc->prdin[dmap->nfrags].size = PRD_JMP | PRD_EOP; + + CS_WRITEL (devc, 0x2c, devc->prdin_phys); + + portc->audio_enabled &= ~PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; +} + +/*ARGSUSED*/ +static int +geode_audio_prepare_for_output (int dev, int bsize, int bcount) +{ + geode_devc *devc = audio_engines[dev]->devc; + geode_portc *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_out; + int i; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + ac97_playrate (&devc->ac97devc, portc->speed); + + /* clear out the PRD table */ + memset (devc->prdout, 0, 512 * sizeof (PRD_rec)); + + + /* Initialize PRD entries */ + for (i = 0; i < dmap->nfrags; i++) + { + devc->prdout[i].ptr = dmap->dmabuf_phys + (i * dmap->fragment_size); + devc->prdout[i].size = dmap->fragment_size | PRD_EOP; + } + + /* Initialize the JMP entry back to the beginning */ + devc->prdout[dmap->nfrags].ptr = devc->prdout_phys; + devc->prdout[dmap->nfrags].size = PRD_JMP | PRD_EOP; + + CS_WRITEL (devc, 0x24, devc->prdout_phys); + + portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + return 0; +} + +static int +geode_get_buffer_pointer (int dev, dmap_t * dmap, int direction) +{ + geode_devc *devc = audio_engines[dev]->devc; + int ptr = 0; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + if (direction == PCM_ENABLE_OUTPUT) + { + if (devc->chip == AMD_CS5536_ID) + ptr = CS_READL (devc, 0x60); + else + ptr = CS_READL (devc, 0x24); + } + if (direction == PCM_ENABLE_INPUT) + { + if (devc->chip == AMD_CS5536_ID) + ptr = CS_READL (devc, 0x64); + else + ptr = CS_READL (devc, 0x2c); + } + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + return ptr - dmap->dmabuf_phys; +} + +static const audiodrv_t geode_audio_driver = { + geode_audio_open, + geode_audio_close, + geode_audio_output_block, + geode_audio_start_input, + geode_audio_ioctl, + geode_audio_prepare_for_input, + geode_audio_prepare_for_output, + geode_audio_reset, + NULL, + NULL, + geode_audio_reset_input, + geode_audio_reset_output, + geode_audio_trigger, + geode_audio_set_rate, + geode_audio_set_format, + geode_audio_set_channels, + NULL, + NULL, + NULL, + NULL, + NULL, /* geode_alloc_buffer */ + NULL, /* geode_free_buffer */ + NULL, + NULL, + geode_get_buffer_pointer +}; + +static int +init_geode (geode_devc * devc) +{ + int i, caps, opts; + int first_dev = 0; + oss_native_word phaddr; + + /* Allocate the buffers for the prdin/prdout tables */ + devc->prdin = + (PRD_rec *) CONTIG_MALLOC (devc->osdev, 512 * sizeof (PRD_rec), + MEMLIMIT_32BITS, &phaddr, devc->prdin_dma_handle); + if (devc->prdin == NULL) + { + cmn_err (CE_WARN, "Can't allocate memory for PRD input tables\n"); + return 0; + } + + devc->prdin_phys = phaddr; + + devc->prdout = + (PRD_rec *) CONTIG_MALLOC (devc->osdev, 512 * sizeof (PRD_rec), + MEMLIMIT_32BITS, &phaddr, devc->prdout_dma_handle); + if (devc->prdout == NULL) + { + cmn_err (CE_WARN, "Can't allocate memory for PRD output tables\n"); + return 0; + } + + devc->prdout_phys = phaddr; + + if (devc->chip != AMD_CS5536_ID) + { + /* VSA2 IRQ config method */ + OUTW (devc->osdev, 0xFC53, 0xAC1C); + OUTW (devc->osdev, 0x108, 0xAC1C); + OUTW (devc->osdev, devc->irq, 0xAC1E); + + /* VSA1 IRQ config method */ + OUTL (devc->osdev, 0x800090D0, 0x0CF8); + OUTL (devc->osdev, (devc->irq << 16) | 0xA00A, 0x0CFC); + oss_udelay (10000); + } + /* Now configure the OSS devices */ + + devc->mixer_dev = + ac97_install (&devc->ac97devc, "AC97 Mixer", ac97_read, ac97_write, devc, + devc->osdev); + + if (devc->mixer_dev < 0) + return 0; + + if (devc->chip == AMD_CS5536_ID) + { + /* remove controls that are backed by devices unsupported by the + chip, like s/pdif (cs5536 databook, page 89) */ + ac97_remove_control (&devc->ac97devc, + SOUND_MASK_VIDEO | SOUND_MASK_MONO | SOUND_MASK_CD | SOUND_MASK_PHONE, + 0); + } + + opts = ADEV_AUTOMODE | ADEV_STEREOONLY | ADEV_16BITONLY; + + if (!ac97_varrate (&devc->ac97devc)) + { + opts |= ADEV_FIXEDRATE; + } + + for (i = 0; i < MAX_PORTC; i++) + { + int adev; + geode_portc *portc = &devc->portc[i]; + char tmp_name[100]; + + if (i == 0) + { + if (devc->chip == AMD_CS5536_ID) + sprintf (tmp_name, "%s", "Geode CS5536"); + else + sprintf (tmp_name, "%s", "Geode CS5530"); + + caps = opts | ADEV_DUPLEX; + } + else + { + if (devc->chip == AMD_CS5536_ID) + sprintf (tmp_name, "%s", "Geode CS5536 (playback)"); + else + sprintf (tmp_name, "%s", "Geode CS5530 (playback"); + + caps = opts | ADEV_DUPLEX | ADEV_SHADOW; + } + + if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + tmp_name, + &geode_audio_driver, + sizeof (audiodrv_t), + caps, 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 = devc->mixer_dev; + audio_engines[adev]->min_rate = 8000; + audio_engines[adev]->max_rate = 48000; + audio_engines[adev]->caps |= PCM_CAP_FREERATE; + portc->open_mode = 0; + portc->audiodev = adev; + portc->audio_enabled = 0; + if (caps & ADEV_FIXEDRATE) + { + audio_engines[adev]->fixed_rate = 48000; + audio_engines[adev]->min_rate = 48000; + } +#ifdef CONFIG_OSS_VMIX + if (i == 0) + vmix_attach_audiodev(devc->osdev, adev, -1, 0); +#endif + } + + } + return 1; +} + +int +oss_geode_attach (oss_device_t * osdev) +{ + unsigned char pci_revision, pci_irq_line /*, pci_latency */ ; + unsigned short pci_command, vendor, device; + unsigned int pci_ioaddr; + int err; + geode_devc *devc; + + DDB (cmn_err (CE_WARN, "Entered Geode probe routine\n")); + + pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); + pci_read_config_word (osdev, PCI_DEVICE_ID, &device); + if (vendor != CYRIX_VENDOR_ID || device != CYRIX_GEODE) + if (vendor != NATIONAL_VENDOR_ID || device != NATIONAL_SC1200) + if (vendor != AMD_VENDOR_ID || device != AMD_CS5536_ID) + return 0; + pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); + pci_read_config_word (osdev, PCI_COMMAND, &pci_command); + pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &pci_ioaddr); + pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); + + pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_write_config_word (osdev, PCI_COMMAND, pci_command); + + if (pci_ioaddr == 0) + { + cmn_err (CE_WARN, "BAR0 not initialized by BIOS\n"); + 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->physaddr = pci_ioaddr & ~1UL; + devc->irq = pci_irq_line; + devc->chip = device; + + MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); + MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1); + if (devc->chip == AMD_CS5536_ID) + { + devc->chip_name = "AMD CS5536 AC97 Controller"; + + oss_register_device (osdev, devc->chip_name); + + if ((err = oss_register_interrupts (devc->osdev, 0, geodeintr_5536, NULL)) < 0) + { + cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err); + return 0; + } + } + else + { + devc->chip_name = "Geode/NS5530 AC97 Controller"; + + oss_register_device (osdev, devc->chip_name); + + if ((err = oss_register_interrupts (devc->osdev, 0, geodeintr_5530, NULL)) < 0) + { + cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err); + return 0; + } + } + + return init_geode (devc); /* Detected */ +} + + +int +oss_geode_detach (oss_device_t * osdev) +{ + geode_devc *devc = (geode_devc *) osdev->devc; + + if (oss_disable_device (osdev) < 0) + return 0; + + CS_WRITEB (devc, 0x20, 0); /* Disable output */ + CS_WRITEB (devc, 0x28, 0); /* Disable input */ + CS_WRITEL (devc, 0x24, 0); + CS_WRITEL (devc, 0x2c, 0); + + oss_unregister_interrupts (devc->osdev); + + MUTEX_CLEANUP (devc->mutex); + MUTEX_CLEANUP (devc->low_mutex); + + if (devc->prdin != NULL) + CONTIG_FREE (devc->osdev, devc->prdin, 512 * sizeof (PRD_rec), devc->prdin_dma_handle); + if (devc->prdout != NULL) + CONTIG_FREE (devc->osdev, devc->prdout, 512 * sizeof (PRD_rec), devc->prdout_dma_handle); + + devc->prdin = devc->prdout = NULL; + oss_unregister_device (devc->osdev); + + return 1; +} |