summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_geode/oss_geode.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_geode/oss_geode.c')
-rw-r--r--kernel/drv/oss_geode/oss_geode.c852
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;
+}