Google
 

Open Sound System
The Hitchhiker's Guide to OSS 4.1 Internals

Do you have problems with sound/audio application development? Don't panic! Click here for help!

oss_ich/oss_ich.c

Driver for the Intel ICH AC97 audio controller

Description



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;
	}

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)

buffer

	  else

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 */

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)

buffer

      else

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

Copyright (C) 4Front Technologies, 2007. All rights reserved.

Back to index OSS web site


Copyright (C) 4Front Technologies, 2007. All rights reserved.
Back to index OSS web site