summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_ich/oss_ich.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_ich/oss_ich.c')
-rw-r--r--kernel/drv/oss_ich/oss_ich.c1470
1 files changed, 1470 insertions, 0 deletions
diff --git a/kernel/drv/oss_ich/oss_ich.c b/kernel/drv/oss_ich/oss_ich.c
new file mode 100644
index 0000000..fc23e01
--- /dev/null
+++ b/kernel/drv/oss_ich/oss_ich.c
@@ -0,0 +1,1470 @@
+/*
+ * Purpose: Driver for the Intel ICH AC97 audio controller
+ *
+ * The same design is also used in many PC chipsets by nVidia, AMD and SiS for
+ * audio functionality.
+ */
+
+/*
+ *
+ * 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_ich_cfg.h"
+#include <oss_pci.h>
+
+#include <ac97.h>
+extern int intelpci_force_mmio;
+
+#define INTEL_VENDOR_ID 0x8086
+#define SIS_VENDOR_ID 0x1039
+#define NVIDIA_VENDOR_ID 0x10de
+#define AMD_VENDOR_ID 0x1022
+#define SIS_DEVICE_7012 0x7012
+#define INTEL_DEVICE_ICH1 0x2415
+#define INTEL_DEVICE_ICH1R1 0x2425
+#define INTEL_DEVICE_ICH1R2 0x7195
+#define INTEL_DEVICE_ICH2 0x2445
+#define INTEL_DEVICE_ICH3 0x2485
+#define INTEL_DEVICE_ICH4 0x24c5
+#define INTEL_DEVICE_ICH5 0x24d5
+#define INTEL_DEVICE_ESB 0x25a6
+#define INTEL_DEVICE_ICH6 0x266e
+#define INTEL_DEVICE_ICH7 0x27de
+#define NVIDIA_DEVICE_NFORCE 0x01b1
+#define NVIDIA_DEVICE_MCP4 0x003a
+#define NVIDIA_DEVICE_NFORCE2 0x006a
+#define NVIDIA_DEVICE_CK8 0x008a
+#define NVIDIA_DEVICE_NFORCE3 0x00da
+#define NVIDIA_DEVICE_CK8S 0x00ea
+#define NVIDIA_DEVICE_NFORCE4 0x0059
+#define NVIDIA_DEVICE_MCP51 0x026b
+#define AMD_DEVICE_768 0x7445
+#define AMD_DEVICE_8111 0x746d
+
+extern int intelpci_rate_tuning;
+
+#define MAX_PORTC 3
+#define BDL_SIZE 32
+
+extern int ich_jacksense;
+
+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
+}
+ich_portc;
+
+typedef struct
+{
+ unsigned int addr;
+ unsigned short size;
+ unsigned short flags;
+}
+bdl_t;
+
+typedef struct ich_devc
+{
+ oss_device_t *osdev;
+ oss_native_word base, ac97_base;
+ oss_native_word membar_addr, ac97_membar_addr;
+ char *membar_virt, *ac97_membar_virt;
+#define CTL_BASE 0 /* addressing controller regs */
+#define MIXER_BASE 1 /* addressing mixer regs */
+ int mem_mode;
+#define MMAP_MODE 0 /* ICH4/ICH5 uses MEM BARS */
+#define IO_MODE 1 /* ICH1/2/3/4/5 uses IO BARS */
+
+ int irq;
+ oss_mutex_t mutex;
+ oss_mutex_t low_mutex;
+
+ /* Mixer */
+ ac97_devc ac97devc;
+ int mixer_dev;
+ int inverted_amplifier;
+
+ /* Audio parameters */
+ int open_mode;
+ int fifo_errors;
+
+ /* Buffer Descriptor List */
+ char *bdlBuffer;
+ bdl_t *playBDL, *recBDL, *spdifBDL;
+ oss_native_word playBDL_phys, recBDL_phys, spdifBDL_phys;
+ oss_dma_handle_t bldbuf_dma_handle;
+
+ int play_currbuf, play_currfrag;
+ int spdif_currbuf, spdif_currfrag;
+ int rec_currbuf, rec_currfrag;
+#define INTEL_ICH1 0
+#define INTEL_ICH3 1
+#define INTEL_ICH4 2
+#define SIS_7012 3
+#define AMD_768 4
+#define AMD_8111 5
+#define NVIDIA_NFORCE 6
+#define NVIDIA_NFORCE2 7
+ int model;
+ char *chip_name;
+ ich_portc portc[MAX_PORTC];
+ int play_frag_index[BDL_SIZE];
+ int rec_frag_index[BDL_SIZE];
+ int spdif_frag_index[BDL_SIZE];
+}
+ich_devc;
+
+static unsigned int
+ich_INL (ich_devc * devc, int base, unsigned int a)
+{
+ if (devc->mem_mode == MMAP_MODE)
+ {
+ if (base == CTL_BASE)
+ return *(volatile unsigned int *) (devc->membar_virt + (a));
+ else
+ return *(volatile unsigned int *) (devc->ac97_membar_virt + (a));
+ }
+ else
+ {
+ if (base == CTL_BASE)
+ return INL (devc->osdev, devc->base + a);
+ else
+ return INL (devc->osdev, devc->ac97_base + a);
+ }
+}
+
+static unsigned short
+ich_INW (ich_devc * devc, int base, unsigned short a)
+{
+ if (devc->mem_mode == MMAP_MODE)
+ {
+ if (base == CTL_BASE)
+ return *(volatile unsigned short *) (devc->membar_virt + (a));
+ else
+ return *(volatile unsigned short *) (devc->ac97_membar_virt + (a));
+ }
+ else
+ {
+ if (base == CTL_BASE)
+ return INW (devc->osdev, devc->base + a);
+ else
+ return INW (devc->osdev, devc->ac97_base + a);
+ }
+}
+
+static unsigned char
+ich_INB (ich_devc * devc, int base, unsigned char a)
+{
+ if (devc->mem_mode == MMAP_MODE)
+ {
+ if (base == CTL_BASE)
+ return *(volatile unsigned char *) (devc->membar_virt + (a));
+ else
+ return *(volatile unsigned char *) (devc->ac97_membar_virt + (a));
+ }
+ else
+ {
+ if (base == CTL_BASE)
+ return INB (devc->osdev, devc->base + a);
+ else
+ return INB (devc->osdev, devc->ac97_base + a);
+ }
+}
+
+static void
+ich_OUTL (ich_devc * devc, unsigned int d, int base, unsigned int a)
+{
+ if (devc->mem_mode == MMAP_MODE)
+ {
+ if (base == CTL_BASE)
+ *(volatile unsigned int *) (devc->membar_virt + (a)) = d;
+ else
+ *(volatile unsigned int *) (devc->ac97_membar_virt + (a)) = d;
+ }
+ else
+ {
+ if (base == CTL_BASE)
+ OUTL (devc->osdev, d, devc->base + a);
+ else
+ OUTL (devc->osdev, d, devc->ac97_base + a);
+ }
+}
+
+static void
+ich_OUTW (ich_devc * devc, unsigned short d, int base, unsigned short a)
+{
+ if (devc->mem_mode == MMAP_MODE)
+ {
+ if (base == CTL_BASE)
+ *(volatile unsigned short *) (devc->membar_virt + (a)) = d;
+ else
+ *(volatile unsigned short *) (devc->ac97_membar_virt + (a)) = d;
+ }
+ else
+ {
+ if (base == CTL_BASE)
+ OUTW (devc->osdev, d, devc->base + a);
+ else
+ OUTW (devc->osdev, d, devc->ac97_base + a);
+ }
+}
+
+static void
+ich_OUTB (ich_devc * devc, unsigned char d, int base, unsigned char a)
+{
+ if (devc->mem_mode == MMAP_MODE)
+ {
+ if (base == CTL_BASE)
+ *(volatile unsigned char *) (devc->membar_virt + (a)) = d;
+ else
+ *(volatile unsigned char *) (devc->ac97_membar_virt + (a)) = d;
+ }
+ else
+ {
+ if (base == CTL_BASE)
+ OUTB (devc->osdev, d, devc->base + a);
+ else
+ OUTB (devc->osdev, d, devc->ac97_base + a);
+ }
+}
+
+static int
+ac97_read (void *devc_, int reg)
+{
+ ich_devc *devc = devc_;
+ int i = 100, status;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ status = ich_INB (devc, CTL_BASE, 0x34);
+
+ while (status & 0x01 && i-- > 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ oss_udelay (10);
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ status = ich_INB (devc, CTL_BASE, 0x34);
+ }
+
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return ich_INW (devc, MIXER_BASE, reg);
+}
+
+static int
+ac97_write (void *devc_, int reg, int data)
+{
+ ich_devc *devc = devc_;
+ int i = 100, status;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ status = ich_INB (devc, CTL_BASE, 0x34);
+
+ while (status & 0x01 && i-- > 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ oss_udelay (10);
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ status = ich_INB (devc, CTL_BASE, 0x34);
+ }
+
+ ich_OUTW (devc, data, MIXER_BASE, reg);
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 1;
+}
+
+/*
+ * The top half interrupt handler
+ */
+static int
+ichintr (oss_device_t * osdev)
+{
+ int serviced = 0;
+ ich_devc *devc = osdev->devc;
+ ich_portc *portc;
+ unsigned int glob_status, status, p, f;
+ oss_native_word flags;
+ int i;
+
+ flags = 0; /* To prevent compiler warnings */
+ MUTEX_ENTER (devc->mutex, flags);
+ /* Get pending interrupts and acknowledge them */
+ glob_status = ich_INL (devc, CTL_BASE, 0x30);
+ ich_OUTL (devc, glob_status, CTL_BASE, 0x30);
+
+ /*
+ * Check the interrupt bits of the status register
+ */
+ if (!(glob_status & 0x0cf7))
+ {
+ /* Not for me */
+ MUTEX_EXIT (devc->mutex, flags);
+ return 0;
+ }
+
+ /*-------------------- Handle PCM -------------------*/
+ if (devc->model == SIS_7012)
+ status = ich_INB (devc, CTL_BASE, 0x18);
+ else
+ status = ich_INB (devc, CTL_BASE, 0x16);
+
+ if (status & 0x10) /* FIFO error */
+ devc->fifo_errors++;
+
+ if (status & 0x08)
+ {
+ 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 = ich_INB (devc, CTL_BASE, 0x14);
+
+ if (p != devc->play_currbuf)
+ {
+ p = devc->play_currbuf;
+ f = devc->play_currfrag;
+ devc->playBDL[p].addr =
+ dmap->dmabuf_phys + (f * dmap->fragment_size);
+
+ /* SIS uses bytes, intelpci uses samples */
+ if (devc->model == SIS_7012)
+ devc->playBDL[p].size = (dmap->fragment_size);
+ else
+ devc->playBDL[p].size = (dmap->fragment_size / 2);
+ devc->playBDL[p].flags = 0xc000; /* IOC interrupts */
+
+ ich_OUTB (devc, p, CTL_BASE, 0x15); /* 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);
+ }
+ }
+ if (devc->model == SIS_7012)
+ ich_OUTB (devc, status, CTL_BASE, 0x18); /* Clear interrupts */
+ else
+ ich_OUTB (devc, status, CTL_BASE, 0x16); /* Clear interrupts */
+ }
+
+ /*------------------- handle SPDIF interrupts -------------------------*/
+
+ if (devc->model == NVIDIA_NFORCE2)
+ {
+ status = ich_INB (devc, CTL_BASE, 0x76);
+ if (status & 0x08)
+ {
+ 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 = ich_INB (devc, CTL_BASE, 0x74);
+
+ if (p != devc->spdif_currbuf)
+ {
+ p = devc->spdif_currbuf;
+ f = devc->spdif_currfrag;
+ devc->spdifBDL[p].addr =
+ dmap->dmabuf_phys + (f * dmap->fragment_size);
+ /* SIS uses bytes, intelpci uses samples */
+ devc->spdifBDL[p].size = (dmap->fragment_size / 2);
+ devc->spdifBDL[p].flags = 0xc000; /* IOC interrupts */
+
+ ich_OUTB (devc, p, CTL_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);
+ }
+ ich_OUTB (devc, status, CTL_BASE, 0x76); /* Clear interrupts */
+ }
+ }
+
+ /*----------------------- Handle Recording Interrupts --------------------*/
+
+ if (devc->model == SIS_7012)
+ status = ich_INB (devc, CTL_BASE, 0x08);
+ else
+ status = ich_INB (devc, CTL_BASE, 0x06);
+
+ if (status & 0x08)
+ {
+ 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 = ich_INB (devc, CTL_BASE, 0x04);
+
+ if (p != devc->rec_currbuf)
+ {
+ p = devc->rec_currbuf;
+ f = devc->rec_currfrag;
+ devc->recBDL[p].addr =
+ dmap->dmabuf_phys + (f * dmap->fragment_size);
+
+ /* SIS uses bytes, intelpci uses samples */
+ if (devc->model == SIS_7012)
+ devc->recBDL[p].size = (dmap->fragment_size);
+ else
+ devc->recBDL[p].size = (dmap->fragment_size / 2);
+
+ devc->recBDL[p].flags = 0xc000; /* IOC interrupts */
+
+ ich_OUTB (devc, p, CTL_BASE, 0x05); /* 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);
+ }
+ }
+ if (devc->model == SIS_7012)
+ ich_OUTB (devc, status, CTL_BASE, 0x08); /* Clear int */
+ else
+ ich_OUTB (devc, status, CTL_BASE, 0x06); /* Clear int */
+ }
+ MUTEX_EXIT (devc->mutex, flags);
+ return serviced;
+}
+
+
+/*
+ * Audio routines
+ */
+
+static int
+ich_audio_set_rate (int dev, int arg)
+{
+ ich_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
+ich_audio_set_channels (int dev, short arg)
+{
+ ich_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
+ich_audio_set_format (int dev, unsigned int arg)
+{
+ ich_portc *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->bits;
+
+#if 1
+ if (portc->open_mode & OPEN_READ)
+ return portc->bits = AFMT_S16_LE;
+#endif
+ if (!(arg & (AFMT_S16_LE | AFMT_AC3)))
+ return portc->bits;
+ portc->bits = arg;
+
+ return portc->bits;
+}
+
+/*ARGSUSED*/
+static int
+ich_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void ich_audio_trigger (int dev, int state);
+
+static void
+ich_audio_reset (int dev)
+{
+ ich_audio_trigger (dev, 0);
+}
+
+static void
+ich_audio_reset_input (int dev)
+{
+ ich_portc *portc = audio_engines[dev]->portc;
+ ich_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
+}
+
+static void
+ich_audio_reset_output (int dev)
+{
+ ich_portc *portc = audio_engines[dev]->portc;
+ ich_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
+}
+
+/*ARGSUSED*/
+static int
+ich_audio_open (int dev, int mode, int openflags)
+{
+ ich_portc *portc = audio_engines[dev]->portc;
+ ich_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_NOTE, "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;
+ devc->fifo_errors = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+static void
+ich_audio_close (int dev, int mode)
+{
+ ich_portc *portc = audio_engines[dev]->portc;
+ ich_devc *devc = audio_engines[dev]->devc;
+
+ ich_audio_reset (dev);
+ portc->open_mode = 0;
+
+ if (devc->fifo_errors > 0)
+ cmn_err (CE_CONT, "%d fifo errors were detected\n", devc->fifo_errors);
+
+ if (portc->port_type != DF_SPDIF)
+ devc->open_mode &= ~mode;
+ portc->audio_enabled &= ~mode;
+}
+
+/*ARGSUSED*/
+static void
+ich_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ ich_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+}
+
+/*ARGSUSED*/
+static void
+ich_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ ich_portc *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+}
+
+static void
+ich_audio_trigger (int dev, int state)
+{
+ ich_devc *devc = audio_engines[dev]->devc;
+ ich_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)
+ ich_OUTB (devc, 0x1d, CTL_BASE, 0x7b); /* Kickstart */
+ else
+ ich_OUTB (devc, 0x1d, CTL_BASE, 0x1b); /* Kickstart */
+ 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)
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x7b); /* reset */
+ else
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x1b); /* reset */
+ }
+ }
+ }
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ ich_OUTB (devc, 0x1d, CTL_BASE, 0x0b); /* Kickstart */
+ 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;
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x0b); /* reset */
+ }
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+ich_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ ich_devc *devc = audio_engines[dev]->devc;
+ ich_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ int i, n, speed;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ ich_OUTB (devc, 0x02, CTL_BASE, 0x0b); /* Reset */
+ ich_OUTL (devc, devc->recBDL_phys, CTL_BASE, 0x00); /* BDL base */
+
+ speed = portc->speed;
+ speed = (speed * 240) / intelpci_rate_tuning;
+ ac97_recrate (&devc->ac97devc, speed);
+
+ n = bcount;
+ if (n > BDL_SIZE)
+ n = BDL_SIZE;
+
+ for (i = 0; i < n; i++)
+ {
+ devc->recBDL[i].addr = dmap->dmabuf_phys + (i * dmap->fragment_size);
+
+ /* SiS7012 uses bytes, ICH uses samples */
+ if (devc->model == SIS_7012)
+ devc->recBDL[i].size = (dmap->fragment_size);
+ else
+ devc->recBDL[i].size = (dmap->fragment_size / 2);
+
+ devc->recBDL[i].flags = 0xc000; /* IOC interrupts */
+ devc->rec_frag_index[i] = i;
+ }
+ ich_OUTB (devc, n - 1, CTL_BASE, 0x05); /* 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
+ich_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ ich_devc *devc = audio_engines[dev]->devc;
+ ich_portc *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+ int i, n, speed;
+ unsigned int tmp;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ /* We need to add ac3 pass through support */
+ if (devc->model == SIS_7012)
+ {
+ if (portc->bits == AFMT_AC3)
+ ich_OUTB (devc, 0, CTL_BASE, 0x4c);
+ else
+ ich_OUTB (devc, 1, CTL_BASE, 0x4c);
+ }
+
+ 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) && (devc->model == NVIDIA_NFORCE2))
+ {
+ ich_OUTB (devc, 0x02, CTL_BASE, 0x7b); /* Reset */
+ ich_OUTL (devc, devc->spdifBDL_phys, CTL_BASE, 0x70); /* BDL base */
+ n = bcount;
+ if (n > BDL_SIZE)
+ n = BDL_SIZE;
+
+ for (i = 0; i < n; i++)
+ {
+ devc->spdifBDL[i].addr =
+ dmap->dmabuf_phys + (i * dmap->fragment_size);
+ devc->spdifBDL[i].size = (dmap->fragment_size / 2);
+ devc->spdifBDL[i].flags = 0xc000; /* IOC interrupts */
+ devc->spdif_frag_index[i] = i;
+ }
+ ich_OUTB (devc, n - 1, CTL_BASE, 0x75); /* Set last valid
+ * 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 */
+ ich_OUTB (devc, 0x02, CTL_BASE, 0x1b); /* Reset */
+ ich_OUTL (devc, devc->playBDL_phys, CTL_BASE, 0x10); /* BDL base */
+
+ speed = portc->speed;
+ speed = (speed * 240) / intelpci_rate_tuning;
+ ac97_playrate (&devc->ac97devc, speed);
+
+ /* Handle 4/6 channel output on 7012 */
+ if (devc->model == SIS_7012)
+ {
+ tmp = ich_INL (devc, CTL_BASE, 0x30);
+
+ /* set default to 2 channel mode */
+ ich_OUTB (devc, ich_INB (devc, CTL_BASE, 0x2c) & 0x3f, CTL_BASE, 0x2c);
+
+ if ((portc->channels == 4) && (tmp & (1 << 20)))
+ ich_OUTB (devc, (ich_INB (devc, CTL_BASE, 0x2c) & 0x3f) | 0x40,
+ CTL_BASE, 0x2c);
+
+ if ((portc->channels == 6) && (tmp & (1 << 21)))
+ ich_OUTB (devc, (ich_INB (devc, CTL_BASE, 0x2c) & 0x3f) | 0x80,
+ CTL_BASE, 0x2c);
+ }
+ /* Handle 4/6 channel output on evrything other than ICH1 and SIS7012 */
+ if ((devc->model != INTEL_ICH1) && (devc->model != SIS_7012))
+ {
+ tmp = ich_INL (devc, CTL_BASE, 0x30);
+
+ /* set default to 2 channel mode */
+ ich_OUTL (devc, ich_INL (devc, CTL_BASE, 0x2c) & 0x0cfffff, CTL_BASE,
+ 0x2c);
+
+ if ((portc->channels == 4) && (tmp & (1 << 20)))
+ ich_OUTL (devc,
+ (ich_INL (devc, CTL_BASE, 0x2c) & 0x00fffff) | 0x0100000,
+ CTL_BASE, 0x2c);
+
+ if ((portc->channels == 6) && (tmp & (1 << 21)))
+ ich_OUTL (devc,
+ (ich_INL (devc, CTL_BASE, 0x2c) & 0x00fffff) | 0x0200000,
+ CTL_BASE, 0x2c);
+ }
+ n = bcount;
+ if (n > BDL_SIZE)
+ n = BDL_SIZE;
+
+ for (i = 0; i < n; i++)
+ {
+ devc->playBDL[i].addr = dmap->dmabuf_phys + (i * dmap->fragment_size);
+
+ /* SiS7012 uses bytes, ICH uses samples */
+ if (devc->model == SIS_7012)
+ devc->playBDL[i].size = (dmap->fragment_size);
+ else
+ devc->playBDL[i].size = (dmap->fragment_size / 2);
+
+ devc->playBDL[i].flags = 0xc000; /* IOC interrupts */
+ devc->play_frag_index[i] = i;
+ }
+ ich_OUTB (devc, n - 1, CTL_BASE, 0x15); /* 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 int
+ich_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ ich_devc *devc = audio_engines[dev]->devc;
+ ich_portc *portc = audio_engines[dev]->portc;
+ int p = 0, f = 0, c = 0;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+ if (direction == PCM_ENABLE_OUTPUT)
+ {
+ if (portc->port_type == DF_PCM)
+ {
+ f = ich_INB (devc, CTL_BASE, 0x14); /* Current buffer */
+
+ if (devc->model == SIS_7012)
+ p = ich_INW (devc, CTL_BASE, 0x16); /* Position in current
+ * buffer */
+ else
+ p = ich_INW (devc, CTL_BASE, 0x18); /* Position in current
+ * buffer */
+ c = devc->play_frag_index[f]; /* Current fragment */
+
+ if (devc->model == SIS_7012)
+ p = dmap->fragment_size - (p); /* Remaining bytes */
+ else
+ p = dmap->fragment_size - (p * 2); /* Remaining bytes */
+ }
+ if ((portc->port_type == DF_SPDIF) && (devc->model == NVIDIA_NFORCE2))
+ {
+ f = ich_INB (devc, CTL_BASE, 0x74); /* Current buffer */
+ p = ich_INW (devc, CTL_BASE, 0x78); /* Position in current
+ * buffer */
+ c = devc->play_frag_index[f]; /* Current fragment */
+ p = dmap->fragment_size - (p * 2); /* Remaining bytes */
+ }
+ }
+ /*
+ * Handle input
+ */
+ if (direction == PCM_ENABLE_INPUT)
+ {
+
+ f = ich_INB (devc, CTL_BASE, 0x04); /* Current buffer */
+
+ if (devc->model == SIS_7012)
+ p = ich_INW (devc, CTL_BASE, 0x06); /* Position in current
+ * buffer */
+ else
+ p = ich_INW (devc, CTL_BASE, 0x08); /* Position in current
+ * buffer */
+ c = devc->rec_frag_index[f]; /* Current fragment */
+
+ if (devc->model == SIS_7012)
+ p = dmap->fragment_size - (p); /* Remaining bytes */
+ else
+ p = dmap->fragment_size - (p * 2); /* Remaining bytes */
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return p + c * dmap->fragment_size;
+}
+
+#if 0
+static int
+ich_calibrate_speed (int dev, int nominal_speed, int true_speed)
+{
+ unsigned int fix;
+ DDB (smn_err (CE_CONT,
+ "ich_calibrate_speed(%d, %d, %d)\n", dev, nominal_speed,
+ true_speed));
+
+ fix = ((240 * true_speed) + nominal_speed / 2) / nominal_speed;
+ DDB (cmn_err (CE_NOTE, "intelpci_rate_tuning = %d\n", fix));
+ if (fix > 1)
+ intelpci_rate_tuning = fix;
+
+ return 0;
+}
+#endif
+
+static const audiodrv_t ich_audio_driver = {
+ ich_audio_open,
+ ich_audio_close,
+ ich_audio_output_block,
+ ich_audio_start_input,
+ ich_audio_ioctl,
+ ich_audio_prepare_for_input,
+ ich_audio_prepare_for_output,
+ ich_audio_reset,
+ NULL,
+ NULL,
+ ich_audio_reset_input,
+ ich_audio_reset_output,
+ ich_audio_trigger,
+ ich_audio_set_rate,
+ ich_audio_set_format,
+ ich_audio_set_channels,
+ NULL,
+ NULL,
+ NULL, /* ich_check_input, */
+ NULL, /* ich_check_output, */
+ NULL, /* ich_alloc_buffer, */
+ NULL, /* ich_free_buffer, */
+ NULL,
+ NULL,
+ ich_get_buffer_pointer,
+ NULL /* ich_calibrate_speed */
+};
+
+static int
+ich_init (ich_devc * devc)
+{
+ int my_mixer, adev, opts;
+ int i, max_port;
+ unsigned int reg;
+ int first_dev = 0;
+ oss_native_word phaddr;
+
+ /* ACLink on, warm reset */
+ reg = ich_INL (devc, CTL_BASE, 0x2c);
+ if ((reg & 0x02) == 0)
+ reg |= 2;
+ else
+ reg |= 4;
+ reg &= ~8;
+ ich_OUTL (devc, reg, CTL_BASE, 0x2c);
+ oss_udelay (500);
+ if (devc->model == SIS_7012)
+ {
+ reg |= 0x10;
+ ich_OUTL (devc, reg, CTL_BASE, 0x2c);
+ oss_udelay (500);
+ }
+ /* disable interrupts */
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x0b);
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x1b);
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x2b);
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x7b);
+
+ devc->bdlBuffer =
+ CONTIG_MALLOC (devc->osdev, 4 * 32 * 32, MEMLIMIT_32BITS, &phaddr, devc->bldbuf_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 + (32 * 32));
+ devc->recBDL_phys = phaddr + 32 * 32;
+ devc->spdifBDL = (bdl_t *) (devc->bdlBuffer + (2 * 32 * 32));
+ devc->spdifBDL_phys = phaddr + 2 * 32 * 32;
+
+ /*
+ * Init mixer
+ */
+ my_mixer =
+ ac97_install_full (&devc->ac97devc, "ICH AC97 Mixer", ac97_read, ac97_write,
+ devc, devc->osdev, devc->inverted_amplifier |
+ (ich_jacksense?AC97_FORCE_SENSE:0));
+ if (my_mixer == -1)
+ {
+ cmn_err (CE_WARN, "AC97 mixer installation failed\n");
+ return 0; /* No mixer */
+ }
+
+ devc->mixer_dev = my_mixer;
+ mixer_devs[my_mixer]->priority = 10; /* Known motherboard device */
+
+ /* enable S/PDIF */
+ devc->ac97devc.spdif_slot = SPDIF_SLOT34;
+ ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_ENABLE, SNDCTL_MIX_WRITE, 1);
+
+ /* enable variable rate mode */
+ ac97_write (devc, 0x2a, ac97_read (devc, 0x2a) | 9);
+ if (!(ac97_read (devc, 0x2a) & 1))
+ DDB (cmn_err (CE_NOTE, "VRA not supported...using GRC\n"));
+
+ /* Enable SPDIF for SiS 7012 */
+ if (devc->model == SIS_7012)
+ {
+ ich_OUTL (devc, ich_INL (devc, CTL_BASE, 0x2c) | (1 << 10), CTL_BASE,
+ 0x2c);
+ ich_OUTL (devc, ich_INL (devc, CTL_BASE, 0x4c) | 1, CTL_BASE, 0x4c);
+ }
+ if (devc->model == NVIDIA_NFORCE2)
+ max_port = 3;
+ else
+ max_port = 2;
+
+ for (i = 0; i < max_port; i++)
+ {
+ ich_portc *portc = &devc->portc[i];
+ char tmp_name[100];
+ int port_fmt = DF_PCM;
+ int formats = AFMT_S16_LE | AFMT_AC3;
+ char *devfile_name = "";
+
+ strcpy (tmp_name, devc->chip_name);
+ opts = ADEV_AUTOMODE | ADEV_16BITONLY | ADEV_STEREOONLY | ADEV_COLD;
+
+ if (!ac97_varrate (&devc->ac97devc))
+ {
+ opts |= ADEV_FIXEDRATE;
+ }
+ if (i == 0)
+ {
+ strcpy (tmp_name, devc->chip_name);
+ opts |= ADEV_DUPLEX;
+ }
+ if (i == 1)
+ {
+ strcpy (tmp_name, devc->chip_name);
+ opts |= ADEV_DUPLEX | ADEV_SHADOW;
+ }
+ if (i == 2)
+ {
+ sprintf (tmp_name, "%s S/PDIF out", devc->chip_name);
+ opts |= ADEV_NOINPUT | ADEV_SPECIAL | ADEV_FIXEDRATE;
+ port_fmt = DF_SPDIF;
+ devfile_name = "spdout";
+ }
+ if ((adev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &ich_audio_driver,
+ sizeof (audiodrv_t),
+ opts, formats, devc, -1,
+ devfile_name)) < 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]->max_block = 64 * 1024;
+
+ /* fix a timeout bug with Nforce2 */
+ if ((devc->model == NVIDIA_NFORCE) ||
+ (devc->model == NVIDIA_NFORCE2))
+ {
+ audio_engines[adev]->min_block = 4096;
+ audio_engines[adev]->max_block = 4096;
+ }
+
+ audio_engines[adev]->min_rate =
+ (opts & ADEV_FIXEDRATE) ? 48000 : 5000;
+ audio_engines[adev]->max_rate = 48000;
+ audio_engines[adev]->caps |= PCM_CAP_FREERATE;
+ audio_engines[adev]->min_channels = 2;
+ audio_engines[adev]->max_channels = 6;
+ portc->open_mode = 0;
+ portc->audio_enabled = 0;
+ portc->audiodev = adev;
+ portc->port_type = port_fmt;
+ if (audio_engines[adev]->flags & ADEV_FIXEDRATE)
+ audio_engines[adev]->fixed_rate = 48000;
+#ifdef CONFIG_OSS_VMIX
+ if (i == 0)
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+ }
+ }
+ return 1;
+}
+
+int
+oss_ich_attach (oss_device_t * osdev)
+{
+ unsigned char pci_irq_line, pci_revision /* , pci_latency */ ;
+ unsigned short pci_command, vendor, device, sub_vendor, sub_id;
+ unsigned int pci_ioaddr0, pci_ioaddr1;
+ unsigned int dw;
+
+ ich_devc *devc;
+
+ 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;
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+ DDB (cmn_err
+ (CE_CONT, "oss_ich_attach(Vendor %x, device %x)\n", vendor, device));
+
+#if 0
+ // This check is not necessary because the kernel has already checked
+ // the vendor&device ID
+
+ if ((vendor != INTEL_VENDOR_ID && vendor != SIS_VENDOR_ID &&
+ vendor != NVIDIA_VENDOR_ID && vendor != AMD_VENDOR_ID) ||
+ (device != INTEL_DEVICE_ICH1 && device != INTEL_DEVICE_ICH1R1 &&
+ device != INTEL_DEVICE_ICH1R2 && device != INTEL_DEVICE_ICH2 &&
+ device != INTEL_DEVICE_ICH3 && device != INTEL_DEVICE_ICH4 &&
+ device != INTEL_DEVICE_ICH5 && device != INTEL_DEVICE_ESB &&
+ device != INTEL_DEVICE_ICH6 && device != INTEL_DEVICE_ICH7 &&
+ device != SIS_DEVICE_7012 &&
+ device != AMD_DEVICE_768 && device != AMD_DEVICE_8111 &&
+ device != NVIDIA_DEVICE_NFORCE && device != NVIDIA_DEVICE_NFORCE2 &&
+ device != NVIDIA_DEVICE_NFORCE3 && device != NVIDIA_DEVICE_CK8S &&
+ device != NVIDIA_DEVICE_NFORCE4 && device != NVIDIA_DEVICE_CK8 &&
+ device != NVIDIA_DEVICE_MCP51 && device != NVIDIA_DEVICE_MCP4
+ ))
+ {
+ cmn_err (CE_WARN, "Hardware not recognized (vendor=%x, dev=%x)\n",
+ vendor, device);
+ return 0;
+ }
+#endif
+
+ pci_read_config_word (osdev, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor);
+ pci_read_config_word (osdev, PCI_SUBSYSTEM_ID, &sub_id);
+ dw = (sub_id << 16) | sub_vendor;
+
+ switch (dw)
+ {
+ case 0x202f161f: /* Gateway 7326GZ */
+ case 0x203a161f: /* Gateway 4028GZ or 4542GZ */
+ case 0x203e161f: /* Gateway 3520GZ/M210 */
+ case 0x204c161f: /* Kvazar-Micro Senator 3592XT */
+ case 0x8144104d: /* Sony VAIO PCG-TR* */
+ case 0x8197104d: /* Sony S1XP */
+ case 0x81c0104d: /* Sony VAIO type T */
+ case 0x81c5104d: /* Sony VAIO VGN B1VP/B1XP */
+ case 0x3089103c: /* Compaq Presario B3800 */
+ case 0x309a103c: /* HP Compaq nx4300 */
+ case 0x82131033: /* NEC VersaPro VJ10F/BH */
+ case 0x82be1033: /* NEC VersaPro VJ12F/CH */
+ devc->inverted_amplifier = AC97_INVERTED;
+ cmn_err (CE_CONT, "An inverted amplifier has been autodetected\n");
+ break;
+ default:
+ devc->inverted_amplifier = 0;
+ break;
+ }
+
+ 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);
+ pci_read_config_dword (osdev, PCI_BASE_ADDRESS_1, &pci_ioaddr1);
+
+ switch (device)
+ {
+ case INTEL_DEVICE_ICH1:
+ devc->model = INTEL_ICH1;
+ devc->chip_name = "Intel ICH (2415)";
+ break;
+ case INTEL_DEVICE_ICH1R1:
+ devc->model = INTEL_ICH1;
+ devc->chip_name = "Intel ICHR1(2425)";
+ break;
+ case INTEL_DEVICE_ICH1R2:
+ devc->model = INTEL_ICH1;
+ devc->chip_name = "Intel ICHR2 (7195)";
+ break;
+ case INTEL_DEVICE_ICH2:
+ devc->model = INTEL_ICH1;
+ devc->chip_name = "Intel ICH2 (2445)";
+ break;
+ case INTEL_DEVICE_ICH3:
+ devc->model = INTEL_ICH3;
+ devc->chip_name = "Intel ICH3 (2485)";
+ break;
+ case INTEL_DEVICE_ICH4:
+ devc->model = INTEL_ICH4;
+ devc->chip_name = "Intel ICH4 (24C5)";
+ break;
+ case INTEL_DEVICE_ICH5:
+ devc->model = INTEL_ICH4;
+ devc->chip_name = "Intel ICH5 (24D5)";
+ break;
+ case INTEL_DEVICE_ICH6:
+ devc->model = INTEL_ICH4;
+ devc->chip_name = "Intel ICH6 (266E)";
+ break;
+ case INTEL_DEVICE_ICH7:
+ devc->model = INTEL_ICH4;
+ devc->chip_name = "Intel ICH7 (27DE)";
+ break;
+ case INTEL_DEVICE_ESB:
+ devc->model = INTEL_ICH4;
+ devc->chip_name = "Intel ICH5 (25a6)";
+ break;
+ case SIS_DEVICE_7012:
+ devc->model = SIS_7012;
+ devc->chip_name = "SiS 7012";
+ break;
+ case NVIDIA_DEVICE_NFORCE:
+ devc->model = NVIDIA_NFORCE;
+ devc->chip_name = "Nvidia nForce";
+ break;
+ case NVIDIA_DEVICE_NFORCE2:
+ devc->model = NVIDIA_NFORCE2;
+ devc->chip_name = "Nvidia nForce2";
+ pci_read_config_dword (osdev, 0x4c, &dw);
+ dw |= 0x1000000;
+ pci_write_config_dword (osdev, 0x4c, dw);
+ break;
+ case NVIDIA_DEVICE_NFORCE3:
+ devc->model = NVIDIA_NFORCE2;
+ devc->chip_name = "Nvidia nForce3";
+ pci_read_config_dword (osdev, 0x4c, &dw);
+ dw |= 0x1000000;
+ pci_write_config_dword (osdev, 0x4c, dw);
+ break;
+ case NVIDIA_DEVICE_CK8S:
+ devc->model = NVIDIA_NFORCE2;
+ devc->chip_name = "Nvidia CK8S";
+ pci_read_config_dword (osdev, 0x4c, &dw);
+ dw |= 0x1000000;
+ pci_write_config_dword (osdev, 0x4c, dw);
+ break;
+ case NVIDIA_DEVICE_NFORCE4:
+ devc->model = NVIDIA_NFORCE2;
+ devc->chip_name = "Nvidia nForce4";
+ pci_read_config_dword (osdev, 0x4c, &dw);
+ dw |= 0x1000000;
+ pci_write_config_dword (osdev, 0x4c, dw);
+ break;
+ case NVIDIA_DEVICE_CK8:
+ devc->model = NVIDIA_NFORCE2;
+ devc->chip_name = "Nvidia CK8";
+ pci_read_config_dword (osdev, 0x4c, &dw);
+ dw |= 0x1000000;
+ pci_write_config_dword (osdev, 0x4c, dw);
+ break;
+ case NVIDIA_DEVICE_MCP51:
+ devc->model = NVIDIA_NFORCE2;
+ devc->chip_name = "Nvidia MCP51";
+ pci_read_config_dword (osdev, 0x4c, &dw);
+ dw |= 0x1000000;
+ pci_write_config_dword (osdev, 0x4c, dw);
+ break;
+ case NVIDIA_DEVICE_MCP4:
+ devc->model = NVIDIA_NFORCE2;
+ devc->chip_name = "Nvidia MCP4";
+ pci_read_config_dword (osdev, 0x4c, &dw);
+ dw |= 0x1000000;
+ pci_write_config_dword (osdev, 0x4c, dw);
+ break;
+ case AMD_DEVICE_768:
+ devc->model = AMD_768;
+ devc->chip_name = "AMD 768";
+ break;
+ case AMD_DEVICE_8111:
+ devc->model = AMD_8111;
+ devc->chip_name = "AMD 8111";
+ break;
+ default:
+ devc->chip_name = "Unknown ICH chip";
+ }
+
+ if (((pci_ioaddr1 == 0) || (intelpci_force_mmio)) &&
+ (devc->model == INTEL_ICH4))
+ {
+ unsigned int ioaddr;
+
+ /* read bar2 and bar3 for getting mmap address */
+ pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_2, &ioaddr);
+ devc->ac97_membar_addr = ioaddr;
+ pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_3, &ioaddr);
+ devc->membar_addr = ioaddr;
+
+ /* get virtual address */
+ devc->ac97_membar_virt =
+ (char *) MAP_PCI_MEM (devc->osdev, 2, devc->ac97_membar_addr, 512);
+ devc->membar_virt =
+ (char *) MAP_PCI_MEM (devc->osdev, 3, devc->membar_addr, 256);
+ devc->mem_mode = MMAP_MODE;
+ }
+ else
+ devc->mem_mode = IO_MODE;
+
+ if (devc->mem_mode == IO_MODE)
+ {
+ if (devc->model == INTEL_ICH4)
+ {
+ /*
+ * enable the IOSE bit in 0x41 for legacy
+ * mode for ICH4/ICH5
+ */
+ pci_write_config_byte (osdev, 0x41, 1);
+
+ /* Set the secondary codec ID */
+ pci_write_config_byte (osdev, 0x40, 0x39);
+ }
+ /* Remove I/O space marker in bit 0. */
+ devc->ac97_base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr0);
+ devc->base = MAP_PCI_IOADDR (devc->osdev, 1, pci_ioaddr1);
+ devc->ac97_base &= ~0xF;
+ devc->base &= ~0xF;
+ }
+ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+ devc->irq = pci_irq_line;
+
+ if (devc->mem_mode != IO_MODE)
+ {
+ devc->base = devc->membar_addr;
+ }
+
+ 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, ichintr, NULL) < 0)
+ {
+ cmn_err (CE_WARN, "Unable to install interrupt handler\n");
+ return 0;
+ }
+
+ return ich_init (devc); /* Detected */
+}
+
+int
+oss_ich_detach (oss_device_t * osdev)
+{
+ ich_devc *devc = (ich_devc *) osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ /* disable interrupts */
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x0b);
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x1b);
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x2b);
+ ich_OUTB (devc, 0x00, CTL_BASE, 0x7b);
+ oss_unregister_interrupts (devc->osdev);
+
+ /* disable S/PDIF */
+ if (devc->mixer_dev)
+ ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_ENABLE, SNDCTL_MIX_WRITE, 0);
+
+ if (devc->bdlBuffer)
+ CONTIG_FREE (devc->osdev, devc->bdlBuffer, 4 * 32 * 32, devc->bldbuf_dma_handle);
+ devc->bdlBuffer = NULL;
+
+ MUTEX_CLEANUP (devc->mutex);
+ MUTEX_CLEANUP (devc->low_mutex);
+
+ if ((devc->mem_mode == MMAP_MODE) && (devc->membar_addr != 0))
+ {
+ UNMAP_PCI_MEM (devc->osdev, 2, devc->ac97_membar_addr,
+ devc->ac97_membar_virt, 512);
+ UNMAP_PCI_MEM (devc->osdev, 3, devc->membar_addr, devc->membar_virt,
+ 256);
+ devc->membar_addr = 0;
+ devc->ac97_membar_addr = 0;
+ }
+ else
+ {
+ UNMAP_PCI_IOADDR (devc->osdev, 0);
+ UNMAP_PCI_IOADDR (devc->osdev, 1);
+ }
+ oss_unregister_device (osdev);
+ return 1;
+}
+
+#ifdef OSS_POWER_MANAGE
+/* Not activated in .config at this moment */
+int
+oss_ich_power (oss_device_t *osdev, int component, int level)
+{
+// cmn_err(CE_CONT, "oss_ich_power(%d, %d)\n", component, level);
+
+ return 0; /* Failed */
+}
+#endif
+
+#ifdef OSS_SUSPEND_RESUME
+int
+oss_ich_suspend(oss_device_t *osdev)
+{
+//cmn_err(CE_CONT, "oss_ich_suspend()\n");
+ return 0; /* Failed */
+}
+
+int
+oss_ich_resume(oss_device_t *osdev)
+{
+//cmn_err(CE_CONT, "oss_ich_resume()\n");
+ return 0; /* Failed */
+}
+#endif