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_cmpci/oss_cmpci.c

Driver for CMEDIA CM8738 PCI audio controller.

Description




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_cmpci_cfg.h"
#include "oss_pci.h"
#include "uart401.h"

#define CMEDIA_VENDOR_ID	0x13F6
#define CMEDIA_CM8738		0x0111
#define CMEDIA_CM8338A		0x0100
#define CMEDIA_CM8338B		0x0101

CM8338 registers definition


#define FUNCTRL0      (devc->base+0x00)
#define FUNCTRL1      (devc->base+0x04)
#define CHFORMAT      (devc->base+0x08)
#define INT_HLDCLR    (devc->base+0x0C)
#define INT_STATUS    (devc->base+0x10)
#define LEGACY_CTRL   (devc->base+0x14)
#define MISC_CTRL     (devc->base+0x18)
#define TDMA_POS      (devc->base+0x1C)
#define MIXER         (devc->base+0x20)
#define MIXER_DATA    (devc->base+0x22)
#define MIXER_ADDR    (devc->base+0x23)
#define MIXER1        (devc->base+0x24)
#define MIXER2        (devc->base+0x25)
#define AUX_VOL       (devc->base+0x26)
#define MIXER3        (devc->base+0x27)
#define AC97          (devc->base+0x28)

#define CH0_FRAME1    (devc->base+0x80)
#define CH0_FRAME2    (devc->base+0x84)
#define CH1_FRAME1    (devc->base+0x88)
#define CH1_FRAME2    (devc->base+0x8C)

#define SPDIF_STAT    (devc->base+0x90)
#define MISC2_CTRL    (devc->base+0x92)

#define MPU_MIRROR    (devc->base+0x40)
#define FM_MIRROR     (devc->base+0x50)
#define JOY_MIRROR    (devc->base+0x60)

#define DSP_MIX_DATARESETIDX    (0x00)
#define DSP_MIX_OUTMIXIDX	(0x3c)
#define CM_CH0_ENABLE     0x01
#define CM_CH1_ENABLE     0x02
#define CM_CH0_RESET      0x04
#define CM_CH1_RESET      0x08
#define CM_CH0_RECORD	  0x01
#define CM_CH1_RECORD	  0x02
#define CM_CH0_PLAY	  ~0x01
#define CM_CH1_PLAY	  ~0x02
#define CM_CH0_INT        1
#define CM_CH1_INT        2
#define CM_EXTENT_CODEC   0x100
#define CM_EXTENT_MIDI    0x2
#define CM_EXTENT_SYNTH   0x4
#define CM_CFMT_STEREO    0x01
#define CM_CFMT_16BIT     0x02
#define CM_CFMT_MASK      0x03
#define CM_CFMT_DACSHIFT  0
#define CM_CFMT_ADCSHIFT  2

#define MUTE_LINE	1
#define MUTE_CD		2
#define MUTE_MIC	3
#define MUTE_AUX	4
#define REAR2LINE	5
#define CEN2LINE	6
#define BASS2LINE	7
#define CEN2MIC		8
#define MODE_4SPK	9
#define DUALDAC		10
#define MICBOOST	11
#define SPDIF_PLAY	12
#define SPDIF_LOOP	13
#define SPDIF_REC	14
#define SPDIF_IMON	15
#define SPDIF_POL	16
#define SPDIF_COPY	17
#define SPDIF_OPT	18
#define SPDIF_AC3	19
#define AUX_REC		20
#define AUX_LEVEL	21

#define CHAN0		   0x1
#define CHAN1		   0x2

static struct
{
  unsigned int rate;
  unsigned int lower;
  unsigned int upper;
  unsigned char freq;
}
rate_lookup[] =
{
  {
  5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0}
  ,
  {
  8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4}
  ,
  {
  11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1}
  ,
  {
  16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5}
  ,
  {
  22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2}
  ,
  {
  32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6}
  ,
  {
  44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3}
  ,
  {
  48000, (48000 + 44100) / 2, (48000 + 96000) / 2, 7}
};

static unsigned char cmpci_recmasks_L[SOUND_MIXER_NRDEVICES] = {
  0x00,				/* SOUND_MIXER_VOLUME   */
  0x00,				/* SOUND_MIXER_BASS     */
  0x00,				/* SOUND_MIXER_TREBLE   */
  0x40,				/* SOUND_MIXER_SYNTH    */
  0x00,				/* SOUND_MIXER_PCM      */
  0x00,				/* SOUND_MIXER_SPEAKER  */
  0x10,				/* SOUND_MIXER_LINE     */
  0x01,				/* SOUND_MIXER_MIC      */
  0x04,				/* SOUND_MIXER_CD       */
  0x00,				/* SOUND_MIXER_IMIX     */
  0x00,				/* SOUND_MIXER_LINE1    */
  0x00,				/* SOUND_MIXER_RECLEV   */
  0x00,				/* SOUND_MIXER_IGAIN    */
  0x00				/* SOUND_MIXER_OGAIN    */
};

static unsigned char cmpci_recmasks_R[SOUND_MIXER_NRDEVICES] = {
  0x00,				/* SOUND_MIXER_VOLUME   */
  0x00,				/* SOUND_MIXER_BASS     */
  0x00,				/* SOUND_MIXER_TREBLE   */
  0x20,				/* SOUND_MIXER_SYNTH    */
  0x00,				/* SOUND_MIXER_PCM      */
  0x00,				/* SOUND_MIXER_SPEAKER  */
  0x08,				/* SOUND_MIXER_LINE     */
  0x01,				/* SOUND_MIXER_MIC      */
  0x02,				/* SOUND_MIXER_CD       */
  0x00,				/* SOUND_MIXER_IMIX     */
  0x00,				/* SOUND_MIXER_LINE1    */
  0x00,				/* SOUND_MIXER_RECLEV   */
  0x00,				/* SOUND_MIXER_IGAIN    */
  0x00				/* SOUND_MIXER_OGAIN    */
};

#define CMEDIA_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
				  SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1)

#define CMEDIA_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
			     SOUND_MASK_LINE | SOUND_MASK_MIC | \
			     SOUND_MASK_IGAIN | SOUND_MASK_CD | \
			     SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER |\
			     SOUND_MASK_LINE1|SOUND_MASK_RECLEV)

#define LEFT_CHN        0
#define RIGHT_CHN       1

Mixer registers of CMPCI

#define CMPCI_IMASK_L    0x3d
#define CMPCI_IMASK_R    0x3e


int default_levels[32] = {
  0x5a5a,			/* Master Volume */
  0x4b4b,			/* Bass */
  0x4b4b,			/* Treble */
  0x4b4b,			/* FM */
  0x4b4b,			/* PCM */
  0x4b4b,			/* PC Speaker */
  0x4b4b,			/* Ext Line */
  0x2020,			/* Mic */
  0x4b4b,			/* CD */
  0x0000,			/* Recording monitor */
  0x4b4b,			/* SB PCM */
  0x4b4b,			/* Recording level */
  0x4b4b,			/* Input gain */
  0x4b4b,			/* Output gain */
  0x4040,			/* Line1 */
  0x4040,			/* Line2 */
  0x1515			/* Line3 */
};

typedef struct cmpci_portc
{
  int speed, bits, channels;
  int open_mode;
  int trigger_bits;
  int audio_enabled;
  int audiodev;
  int dacfmt, adcfmt;
  int chan0_play, chan0_rec;
  int chan1_play, chan1_rec;
}
cmpci_portc;

#define MAX_PORTC 2

typedef struct cmpci_devc
{
  oss_device_t *osdev;
  oss_native_word base;
  int fm_attached;
  int irq;
  int max_channels;
  volatile unsigned char intr_mask;
  int model;
#define MDL_CM8738		1
#define MDL_CM8338A		2
#define MDL_CM8338B		3
#define MDL_CM8768		4
  char *chip_name;
  int chiprev;
  int mode_4spk;
  int dev_mode;
#define DEFAULT_MODE		1
#define DUALDAC_MODE		2
#define SPDIFIN_MODE		4
  unsigned char spdif_control_bits[24];
  /* Audio parameters */
  oss_mutex_t mutex;
  oss_mutex_t low_mutex;
  int open_mode;
  int audio_initialized;
  cmpci_portc portc[MAX_PORTC];

  /* spdif/ac3 stuff */
  int spdif_enabled;
  int can_ac3;

  /* Mixer parameters */
  int mixer_dev;
  int *levels;
  int recmask;

  /* uart401 */
  int uart401_attached;
  uart401_devc uart401devc;
}
cmpci_devc;


static void
set_spdif_rate (cmpci_devc * devc, cmpci_portc * portc)
{
  if (portc->speed == 48000)
    {
      /* setup MISC control reg */
      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 24) | (1 << 15),
	    MISC_CTRL);
    }
  else
    {
      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 15),
	    MISC_CTRL);
      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 24),
	    MISC_CTRL);
    }
}

static void
setup_ac3 (cmpci_devc * devc, cmpci_portc * portc, int value)
{
  if (value && (portc->speed == 48000 || portc->speed == 44100))
    {
      if (devc->chiprev == 37)
	{
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | (1 << 20),
		CHFORMAT);
	}
      else
	{
	  /* Enable AC3 */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 18),
		MISC_CTRL);
	}
    }
  else
    {
      if (devc->chiprev == 37)
	{
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 20),
		CHFORMAT);
	}
      else
	{
	  /* Disable AC3 */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 18),
		MISC_CTRL);
	}
    }
}


static void
cmpci_setmixer (cmpci_devc * devc, unsigned int port, unsigned int value)
{
  oss_native_word flags;
  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  OUTB (devc->osdev, (unsigned char) (port & 0xff), MIXER_ADDR);
  oss_udelay (20);
  OUTB (devc->osdev, (unsigned char) (value & 0xff), MIXER_DATA);
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  oss_udelay (20);
}

static unsigned int
cmpci_getmixer (cmpci_devc * devc, unsigned int port)
{
  unsigned int val;

  oss_native_word flags;
  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  OUTB (devc->osdev, (unsigned char) (port & 0xff), MIXER_ADDR);


  oss_udelay (20);
  val = INB (devc->osdev, MIXER_DATA);
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  oss_udelay (20);
  return val;
}

struct mixer_def
{
  unsigned int regno:8;
  unsigned int bitoffs:4;
  unsigned int nbits:4;
};

typedef struct mixer_def mixer_tab[32][2];
typedef struct mixer_def mixer_ent;

#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
        {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}

static mixer_tab cmpci_mix = {
  MIX_ENT (SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
  MIX_ENT (SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
  MIX_ENT (SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
  MIX_ENT (SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
  MIX_ENT (SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
  MIX_ENT (SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
  MIX_ENT (SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
  MIX_ENT (SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
  MIX_ENT (SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
  MIX_ENT (SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0),
  MIX_ENT (SOUND_MIXER_LINE1, 0, 0, 0, 0, 0, 0),
  MIX_ENT (SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2),
  MIX_ENT (SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
  MIX_ENT (SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
};

/*ARGSUSED*/
static void
change_bits (cmpci_devc * devc, unsigned char *regval, int dev, int chn,
	     int newval)
{
  unsigned char mask;
  int shift;

  mask = (1 << cmpci_mix[dev][chn].nbits) - 1;
  newval = (int) ((newval * mask) + 50) / 100;	/* Scale */

  shift = cmpci_mix[dev][chn].bitoffs - cmpci_mix[dev][LEFT_CHN].nbits + 1;

  *regval &= ~(mask << shift);	/* Mask out previous value */
  *regval |= (newval & mask) << shift;	/* Set the new value */
}

static int set_recmask (int dev, int mask);

static int
cmpci_mixer_set (cmpci_devc * devc, int chan, int value)
{
  int left = value & 0x000000ff;
  int right = (value & 0x0000ff00) >> 8;

  int regoffs;
  unsigned char val;

  if (left > 100)
    left = 100;
  if (right > 100)
    right = 100;

  if (chan > 31)
    return OSS_EINVAL;


Not supported

    return OSS_EINVAL;

  regoffs = cmpci_mix[chan][LEFT_CHN].regno;

  if (regoffs == 0)
    return OSS_EINVAL;

  val = cmpci_getmixer (devc, regoffs);
  change_bits (devc, &val, chan, LEFT_CHN, left);

  devc->levels[chan] = left | (left << 8);


Change register

    {

Save the old one

      regoffs = cmpci_mix[chan][RIGHT_CHN].regno;

      if (regoffs == 0)

Just left channel present



Read the new one

    }

  change_bits (devc, &val, chan, RIGHT_CHN, right);

  cmpci_setmixer (devc, regoffs, val);

  devc->levels[chan] = left | (right << 8);
  return left | (right << 8);
}

static int cmpci_outsw (int dev, int ctrl, unsigned int cmd, int value);

/*ARGSUSED*/
static int
cmpci_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
{
  cmpci_devc *devc = mixer_devs[dev]->devc;
  int val;

  if (((cmd >> 8) & 0xff) == 'M')
    {
      if (IOC_IS_OUTPUT (cmd))
	switch (cmd & 0xff)
	  {
	  case SOUND_MIXER_RECSRC:
	    val = *arg;
	    if (val == SOUND_MASK_LINE1)
	      *arg = cmpci_outsw (dev, AUX_REC, SNDCTL_MIX_WRITE, 1);
	    else
	      *arg = cmpci_outsw (dev, AUX_REC, SNDCTL_MIX_WRITE, 0);
	    return *arg = set_recmask (dev, val);
	    break;

	  case SOUND_MIXER_LINE1:
	    val = *arg;
	    return *arg = cmpci_outsw (dev, AUX_LEVEL, SNDCTL_MIX_WRITE, val);
	    break;

	  default:
	    val = *arg;
	    return *arg = cmpci_mixer_set (devc, cmd & 0xff, val);
	  }
      else
	switch (cmd & 0xff)
	  {

	  case SOUND_MIXER_RECSRC:
	    return *arg = devc->recmask;
	    break;

	  case SOUND_MIXER_DEVMASK:
	    return *arg = CMEDIA_MIXER_DEVICES;
	    break;

	  case SOUND_MIXER_STEREODEVS:
	    return *arg = CMEDIA_MIXER_DEVICES &
	      ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
	    break;

	  case SOUND_MIXER_RECMASK:
	    return *arg = CMEDIA_RECORDING_DEVICES;
	    break;

	  case SOUND_MIXER_LINE1:
	    val = cmpci_outsw (dev, AUX_LEVEL, SNDCTL_MIX_READ, 0);
	    return *arg = val;
	    break;

	  case SOUND_MIXER_CAPS:
	    return *arg = SOUND_CAP_EXCL_INPUT;
	    break;

	  default:
	    return *arg = devc->levels[cmd & 0x1f];
	  }
    }
  else
    return OSS_EINVAL;
}

static int
set_recmask (int dev, int mask)
{
  cmpci_devc *devc = mixer_devs[dev]->devc;
  int devmask = mask & CMEDIA_RECORDING_DEVICES;
  int i;
  unsigned char regimageL, regimageR;

  if (!devmask)
    devmask = SOUND_MASK_MIC;

  regimageL = regimageR = 0;
  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    if ((1 << i) & devmask)
      {
	regimageL |= cmpci_recmasks_L[i];
	regimageR |= cmpci_recmasks_R[i];
      }
  cmpci_setmixer (devc, CMPCI_IMASK_L, regimageL);
  cmpci_setmixer (devc, CMPCI_IMASK_R, regimageR);
  devc->recmask = devmask;
  return devc->recmask;
}

static int
cmpci_outsw (int dev, int ctrl, unsigned int cmd, int value)
{

Access function for CMPCI mixer extension bits

  cmpci_devc *devc = mixer_devs[dev]->devc;
  int left, right, tmp;

  if (cmd == SNDCTL_MIX_READ)
    {
      value = 0;
      switch (ctrl)
	{
	case MUTE_LINE:	/* Left line in to output connection */
	  value = (cmpci_getmixer (devc, 0x3c) & 0x18) ? 0 : 1;
	  break;

	case MUTE_CD:		/* Cd in to output connection */
	  value = (cmpci_getmixer (devc, 0x3c) & 0x06) ? 0 : 1;
	  break;

	case MUTE_MIC:		/* Mic in to output connection */
	  value = (cmpci_getmixer (devc, 0x3c) & 0x01) ? 0 : 1;
	  break;

	case MODE_4SPK:	/* 4Speaker out */
	  value = INL (devc->osdev, MISC_CTRL) & (1 << 26) ? 1 : 0;
	  break;

	case DUALDAC:		/* dual dac */
	  value = devc->dev_mode & DUALDAC_MODE ? 1 : 0;
	  break;

	case REAR2LINE:	/* rear to line in */
	  value = INB (devc->osdev, MIXER1) & (1 << 5) ? 1 : 0;
	  break;

	case CEN2LINE:		/* center to line in */
	  value = INL (devc->osdev, LEGACY_CTRL) & (1 << 14) ? 1 : 0;
	  break;

	case BASS2LINE:	/* basss to line in */
	  value = INL (devc->osdev, LEGACY_CTRL) & (1 << 13) ? 1 : 0;
	  break;

	case SPDIF_PLAY:	/* spdif out */
	  value = INL (devc->osdev, LEGACY_CTRL) & (1 << 23) ? 1 : 0;
	  break;

	case SPDIF_LOOP:	/* S/PDIF I/O Loop */
	  value = (INL (devc->osdev, FUNCTRL1) & (1 << 7)) ? 1 : 0;
	  break;

	case SPDIF_REC:	/* spdif record mode */
	  value = devc->dev_mode & SPDIFIN_MODE ? 1 : 0;
	  break;

	case SPDIF_IMON:	/* spdif input monitor */
	  value = INB (devc->osdev, MIXER1) & 0x1 ? 1 : 0;
	  break;

	case SPDIF_POL:	/* spdif input reverse */
	  if (devc->chiprev < 39)
	    value = INB (devc->osdev, MIXER3) & 0x06 ? 1 : 0;
	  else
	    value = INL (devc->osdev, CHFORMAT) & 0x80 ? 1 : 0;
	  break;

	case SPDIF_AC3:	/* ac3 */
	  value = INL (devc->osdev, MISC_CTRL) & (1 << 18) ? 1 : 0;
	  break;

	case SPDIF_COPY:	/* copy protect (indirect) */
	  value = INL (devc->osdev, LEGACY_CTRL) & (1 << 22) ? 1 : 0;
	  break;

	case SPDIF_OPT:	/* Coax/Optical Select */
	  value = INL (devc->osdev, MISC_CTRL) & (1 << 25) ? 1 : 0;
	  break;

	case CEN2MIC:		/* Center2MIC */
	  if (devc->chiprev >= 39)
	    value = INB (devc->osdev, MIXER3) & 0x4 ? 1 : 0;
	  break;

	case MICBOOST:		/* MIC Boost */
	  value = INB (devc->osdev, MIXER2) & 0x1 ? 0 : 1;
	  break;

	case AUX_LEVEL:
	  value = devc->levels[SOUND_MIXER_LINE1];
	  break;

	case AUX_REC:		/* set LINE1 as rec source - handled by set_recmask */
	  break;

	case MUTE_AUX:		/* AUX mute */
	  value = INB (devc->osdev, MIXER2) & 0x30 ? 0 : 1;
	  break;

	default:
	  return OSS_EINVAL;
	}

      return value;
    }

  if (cmd == SNDCTL_MIX_WRITE)
    {
      switch (ctrl)
	{
	case MUTE_LINE:	/* L/R line in to output connection */
	  if (!value)
	    cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) | 0x18);
	  else
	    cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) & ~0x18);
	  break;

	case MUTE_CD:		/* Cd in to output connection */
	  if (!value)
	    cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) | 0x06);
	  else
	    cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) & ~0x06);
	  break;

	case MUTE_MIC:		/* Mic in to output connection */
	  if (!value)
	    cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) | 0x01);
	  else
	    cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) & ~0x01);
	  break;

	case MODE_4SPK:	/* 4Speaker out */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 26),
		    MISC_CTRL);
	      devc->mode_4spk = 1;
	    }
	  else
	    OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26),
		  MISC_CTRL);
	  devc->mode_4spk = 0;
	  break;

	case DUALDAC:		/* DUAL DAC mode */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 23),
		    MISC_CTRL);
	      /* Disable 4Speaker mode */
	      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26),
		    MISC_CTRL);
	      devc->dev_mode = DUALDAC_MODE;
	    }
	  else
	    {
	      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 23),
		    MISC_CTRL);

	      /* enable back the 4Speaker mode */
	      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | 1 << 26,
		    MISC_CTRL);

	      devc->dev_mode = DEFAULT_MODE;
	    }
	  break;

	case REAR2LINE:	/* REAR TO LINEIN */
	  if (value)
	    {
	      OUTB (devc->osdev, (INB (devc->osdev, MIXER1) | (1 << 5)),
		    MIXER1);
	    }
	  else
	    OUTB (devc->osdev, (INB (devc->osdev, MIXER1) & ~(1 << 5)),
		  MIXER1);
	  break;

	case CEN2LINE:		/* CENTER TO LINEIN */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 14),
		    LEGACY_CTRL);
	    }
	  else
	    OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 14),
		  LEGACY_CTRL);
	  break;

	case BASS2LINE:	/* BASS TO LINEIN */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 13),
		    LEGACY_CTRL);
	    }
	  else
	    OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 13),
		  LEGACY_CTRL);
	  break;

	case SPDIF_PLAY:	/* SPDIF ENABLE */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 23),
		    LEGACY_CTRL);

	      /* enable wave/fm/midi to spdif OUT DAC2SPDO on rev 33/37 */
	      if (devc->chiprev < 39)
		OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 21),
		      LEGACY_CTRL);
	      devc->spdif_enabled = 1;
	    }
	  else
	    {
	      OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 23),
		    LEGACY_CTRL);
	      /* Disable wave/fm/midi to spdif OUT (DAC2SPDO) */
	      if (devc->chiprev < 39)
		OUTL (devc->osdev,
		      INL (devc->osdev, LEGACY_CTRL) & ~(1 << 21),
		      LEGACY_CTRL);
	      devc->spdif_enabled = 0;
	    }
	  break;

	case SPDIF_LOOP:	/* S/PDIF I/O Loop */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 7),
		    FUNCTRL1);
	    }
	  else
	    OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 7),
		  FUNCTRL1);
	  break;

	case SPDIF_REC:	/* SPDIF Record Mode */
	  if (value)
	    {
	      devc->dev_mode = SPDIFIN_MODE;
	    }
	  else
	    devc->dev_mode = DEFAULT_MODE;
	  break;

	case SPDIF_IMON:	/*  spdif monitor */
	  if (value)
	    {
	      OUTB (devc->osdev, INB (devc->osdev, MIXER1) | 0x0D, MIXER1);
	    }
	  else
	    OUTB (devc->osdev, INB (devc->osdev, MIXER1) & ~0xD, MIXER1);
	  break;

	case SPDIF_POL:	/* spdif reverse */
	  if (value)
	    {
	      if (devc->chiprev < 39)
		{
		  OUTB (devc->osdev, INB (devc->osdev, MIXER3) | 0x06,
			MIXER3);
		}
	      else
		OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | 0x80,
		      CHFORMAT);
	    }
	  else
	    {
	      if (devc->chiprev < 39)
		{
		  OUTB (devc->osdev, INB (devc->osdev, MIXER3) & ~0x06,
			MIXER3);
		}
	      else
		OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~0x80,
		      CHFORMAT);
	    }
	  break;

	case SPDIF_AC3:	/* AC3 enabled on S/PDIF */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 18),
		    MISC_CTRL);
	    }
	  else
	    OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 18),
		  MISC_CTRL);
	  break;

	case SPDIF_COPY:	/* Copy protect */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 22),
		    LEGACY_CTRL);
	    }
	  else
	    OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 22),
		  LEGACY_CTRL);
	  break;

	case SPDIF_OPT:	/* Coax/Optical */
	  if (value)
	    {
	      OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 25),
		    MISC_CTRL);
	    }
	  else
	    OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 25),
		  MISC_CTRL);
	  break;

	case CEN2MIC:		/* Center -> Mic OUT */
	  if (value)
	    {
	      if (devc->chiprev >= 39)
		{
		  OUTB (devc->osdev, INB (devc->osdev, MIXER3) | 0x4, MIXER3);
		}
	    }
	  else if (devc->chiprev >= 39)
	    {
	      OUTB (devc->osdev, INB (devc->osdev, MIXER3) & ~0x4, MIXER3);
	    }
	  break;

	case MICBOOST:		/* Mic Boost */
	  if (!value)
	    {
	      OUTB (devc->osdev, INB (devc->osdev, MIXER2) | 0x1, MIXER2);
	    }
	  else
	    OUTB (devc->osdev, INB (devc->osdev, MIXER2) & ~0x1, MIXER2);
	  break;

	case AUX_LEVEL:	/* Aux levels */
	  left = value & 0xff;
	  right = (value >> 8) & 0xff;
	  if (left > 100)
	    left = 100;
	  if (right > 100)
	    right = 100;
	  value = left | (right << 8);
	  left = mix_cvt[left];
	  right = mix_cvt[right];

	  tmp = ((right * ((1 << 4) - 1) / 100) << 4) |
	    (left * ((1 << 4) - 1) / 100);

	  OUTB (devc->osdev, tmp, AUX_VOL);
	  devc->levels[SOUND_MIXER_LINE1] = value;
	  break;

	case AUX_REC:		/* line1 record select */
	  if (value)
	    {
	      OUTB (devc->osdev, INB (devc->osdev, MIXER2) | 0xc0, MIXER2);
	    }
	  else
	    OUTB (devc->osdev, INB (devc->osdev, MIXER2) & ~0xc0, MIXER2);
	  break;

	case MUTE_AUX:		/* line1 mute control */
	  if (!value)
	    {
	      OUTB (devc->osdev, INB (devc->osdev, MIXER2) | 0x30, MIXER2);
	    }
	  else
	    OUTB (devc->osdev, INB (devc->osdev, MIXER2) & ~0x30, MIXER2);
	  break;

	default:
	  return OSS_EINVAL;
	}

      return (value);
    }
  return OSS_EINVAL;
}

static int
cmpci_mix_init (int dev)
{
  int group, err;

  if ((group = mixer_ext_create_group (dev, 0, "CMPCI_MUTECTL")) < 0)
    return group;

  if ((err =
       mixer_ext_create_control (dev, group, MUTE_LINE, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_LINEMUTE", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, MUTE_CD, cmpci_outsw, MIXT_ONOFF,
				 "CMPCI_CDMUTE", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, MUTE_MIC, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_MICMUTE", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, MUTE_AUX, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_LINE1MUTE", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((group = mixer_ext_create_group (dev, 0, "CMPCI_JACKCTL")) < 0)
    return group;
  if ((err =
       mixer_ext_create_control (dev, group, REAR2LINE, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_REAR2LINE", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;
  if ((err =
       mixer_ext_create_control (dev, group, CEN2LINE, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_CEN2LINE", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;
  if ((err =
       mixer_ext_create_control (dev, group, BASS2LINE, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_BASS2LINE", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, CEN2MIC, cmpci_outsw, MIXT_ONOFF,
				 "CMPCI_CEN2MIC", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;


  if ((group = mixer_ext_create_group (dev, 0, "CMPCI_MIXEXT")) < 0)
    return group;

  if ((err =
       mixer_ext_create_control (dev, group, MODE_4SPK, cmpci_outsw,
				 MIXT_ENUM, "CMPCI_SPKMODE", 2,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, DUALDAC, cmpci_outsw, MIXT_ONOFF,
				 "CMPCI_DUALDAC", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, MICBOOST, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_MICBOOST", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  /* Create a new SPDIF group */
  if ((group = mixer_ext_create_group (dev, 0, "CMPCI_SPDIF")) < 0)
    return group;

  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_PLAY, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_Play", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_LOOP, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_LOOP", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  /* Having this in mixer doesn't make any sense */
  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_REC, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_RECORD", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_IMON, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_IMON", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_POL, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_POLREV", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

#if 0
  /* Having this in mixer doesn't make any sense */
  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_AC3, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_AC3", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;
#endif

  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_COPY, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_COPYPROT", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err =
       mixer_ext_create_control (dev, group, SPDIF_OPT, cmpci_outsw,
				 MIXT_ONOFF, "CMPCI_OPTICAL", 1,
				 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  return 0;
}

static void
cmpci_mixer_reset (int dev)
{
  int i;
  cmpci_devc *devc = mixer_devs[dev]->devc;

  devc->levels = load_mixer_volumes ("CM8738_Mixer", default_levels, 1);
  devc->recmask = 0;

  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    cmpci_mixer_set (devc, i, devc->levels[i]);

  set_recmask (dev, SOUND_MASK_MIC);
}


static mixer_driver_t cmpci_mixer_driver = {
  cmpci_mixer_ioctl
};


static int
cmpciintr (oss_device_t * osdev)
{
  cmpci_devc *devc = (cmpci_devc *) osdev->devc;
  unsigned int intstat, intsrc;
  int i;
  int serviced = 0;

  /* see if this is our interrupt */
  intsrc = INL (devc->osdev, INT_STATUS);
  if (intsrc & (CM_CH0_INT | CM_CH1_INT))
    {
      /* Handle playback */
      serviced = 1;

      intstat = INB (devc->osdev, INT_HLDCLR + 2);

      for (i = 0; i < MAX_PORTC; i++)
	{
	  cmpci_portc *portc = &devc->portc[i];

	  if (intstat & CM_CH1_INT)
	    {
	      /* do chan1 playback */
	      if ((portc->chan1_play) &&
		  (portc->trigger_bits & PCM_ENABLE_OUTPUT))
		{
		  OUTB (devc->osdev, intstat & ~CM_CH1_INT, INT_HLDCLR + 2);
		  OUTB (devc->osdev, intstat | CM_CH1_INT, INT_HLDCLR + 2);
		  oss_audio_outputintr (portc->audiodev, 0);
		}

	      /* do chan1 record */
	      if ((portc->chan1_rec) &&
		  (portc->trigger_bits & PCM_ENABLE_INPUT))
		{
		  OUTB (devc->osdev, intstat & ~CM_CH1_INT, INT_HLDCLR + 2);
		  OUTB (devc->osdev, intstat | CM_CH1_INT, INT_HLDCLR + 2);
		  oss_audio_inputintr (portc->audiodev, 0);
		}
	    }

	  if (intstat & CM_CH0_INT)
	    {
	      /* do chan0 playback */
	      if ((portc->chan0_play) &&
		  (portc->trigger_bits & PCM_ENABLE_OUTPUT))
		{
		  OUTB (devc->osdev, intstat & ~CM_CH0_INT, INT_HLDCLR + 2);
		  OUTB (devc->osdev, intstat | CM_CH0_INT, INT_HLDCLR + 2);
		  oss_audio_outputintr (portc->audiodev, 0);
		}

	      /* do chan0 record */
	      if ((portc->chan0_rec) &&
		  (portc->trigger_bits & PCM_ENABLE_INPUT))
		{
		  OUTB (devc->osdev, intstat & ~CM_CH0_INT, INT_HLDCLR + 2);
		  OUTB (devc->osdev, intstat | CM_CH0_INT, INT_HLDCLR + 2);
		  oss_audio_inputintr (portc->audiodev, 0);
		}
	    }
	}
    }

  if (intsrc & 0x10000)
    {
      serviced = 1;
      uart401_irq (&devc->uart401devc);
    }

  return serviced;
}

static int
cmpci_audio_set_rate (int dev, int arg)
{
  cmpci_portc *portc = audio_engines[dev]->portc;

  if (arg == 0)
    return portc->speed;

  if (arg > 48000)
    arg = 48000;
  if (arg < 5000)
    arg = 5000;
  portc->speed = arg;
  return portc->speed;
}

static short
cmpci_audio_set_channels (int dev, short arg)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;

  if (devc->model == MDL_CM8768)
    {
      if (arg>8)
	 arg=8;

      if ((arg != 1) && (arg != 2) && (arg != 4) && (arg != 6) && (arg != 8))
	return portc->channels;
    }
  else
    {
      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
cmpci_audio_set_format (int dev, unsigned int arg)
{
  cmpci_portc *portc = audio_engines[dev]->portc;

  if (arg == 0)
    return portc->bits;

  if (!(arg & (AFMT_U8 | AFMT_S16_LE | AFMT_AC3)))
    return portc->bits;
  portc->bits = arg;

  return portc->bits;
}

/*ARGSUSED*/
static int
cmpci_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
{
  return OSS_EINVAL;
}

static void cmpci_audio_trigger (int dev, int state);

static void
cmpci_audio_reset (int dev)
{
  cmpci_audio_trigger (dev, 0);
}

static void
cmpci_audio_reset_input (int dev)
{
  cmpci_portc *portc = audio_engines[dev]->portc;
  cmpci_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
}

static void
cmpci_audio_reset_output (int dev)
{
  cmpci_portc *portc = audio_engines[dev]->portc;
  cmpci_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
}

/*ARGSUSED*/
static int
cmpci_audio_open (int dev, int mode, int open_flags)
{
  cmpci_portc *portc = audio_engines[dev]->portc;
  cmpci_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->dev_mode & DUALDAC_MODE))
    {
      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
cmpci_audio_close (int dev, int mode)
{
  cmpci_portc *portc = audio_engines[dev]->portc;
  cmpci_devc *devc = audio_engines[dev]->devc;

  cmpci_audio_reset (dev);
  portc->open_mode = 0;
  devc->open_mode &= ~mode;
  portc->audio_enabled &= ~mode;

  if ((devc->spdif_enabled) || (devc->dev_mode & SPDIFIN_MODE))
    OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 9), FUNCTRL1);

  if (portc->chan0_play)
    portc->chan0_play = 0;

  if (portc->chan1_play)
    portc->chan1_play = 0;

  if (portc->chan0_rec)
    portc->chan0_rec = 0;

  if (portc->chan1_rec)
    portc->chan1_rec = 0;
}

/*ARGSUSED*/
static void
cmpci_audio_output_block (int dev, oss_native_word buf, int count,
			  int fragsize, int intrflag)
{
  cmpci_portc *portc = audio_engines[dev]->portc;

  portc->audio_enabled |= PCM_ENABLE_OUTPUT;
  portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
}

/*ARGSUSED*/
static void
cmpci_audio_start_input (int dev, oss_native_word buf, int count,
			 int fragsize, int intrflag)
{
  cmpci_portc *portc = audio_engines[dev]->portc;

  portc->audio_enabled |= PCM_ENABLE_INPUT;
  portc->trigger_bits &= ~PCM_ENABLE_INPUT;
}

static void
cmpci_audio_trigger (int dev, int state)
{
  cmpci_portc *portc = audio_engines[dev]->portc;
  cmpci_devc *devc = audio_engines[dev]->devc;
  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->chan0_play)
		{
		  /* enable the channel0 */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_ENABLE,
			FUNCTRL0 + 2);
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) | CM_CH0_INT,
			INT_HLDCLR + 2);
		}

	      if (portc->chan1_play)
		{
		  /* enable the channel1 */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_ENABLE,
			FUNCTRL0 + 2);
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) | CM_CH1_INT,
			INT_HLDCLR + 2);
		}
	      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->chan0_play)
		{
		  /* disable interrupt */
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH0_INT,
			INT_HLDCLR + 2);

		  /* disable channel0 */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_ENABLE,
			FUNCTRL0 + 2);
		}
	      if (portc->chan1_play)
		{
		  /* disable interrupt */
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH1_INT,
			INT_HLDCLR + 2);

		  /* disable channel */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_ENABLE,
			FUNCTRL0 + 2);
		}
	    }
	}
    }

  if (portc->open_mode & OPEN_READ)
    {
      if (state & PCM_ENABLE_INPUT)
	{
	  if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
	      !(portc->trigger_bits & PCM_ENABLE_INPUT))
	    {
	      if (portc->chan1_rec)
		{
		  /* enable the channel1 */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_ENABLE,
			FUNCTRL0 + 2);
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) | CM_CH1_INT,
			INT_HLDCLR + 2);
		}
	      if (portc->chan0_rec)
		{
		  /* enable the channel0 */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_ENABLE,
			FUNCTRL0 + 2);
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) | CM_CH0_INT,
			INT_HLDCLR + 2);
		}
	      portc->trigger_bits |= PCM_ENABLE_INPUT;
	    }
	}
      else
	{
	  if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
	      (portc->trigger_bits & PCM_ENABLE_INPUT))
	    {
	      portc->trigger_bits &= ~PCM_ENABLE_INPUT;
	      portc->audio_enabled &= ~PCM_ENABLE_INPUT;
	      if (portc->chan1_rec)
		{
		  /* disable interrupt */
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH1_INT,
			INT_HLDCLR + 2);

		  /* disable channel 1 */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_ENABLE,
			FUNCTRL0 + 2);
		}
	      if (portc->chan0_rec)
		{
		  /* disable interrupt */
		  OUTB (devc->osdev,
			INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH0_INT,
			INT_HLDCLR + 2);

		  /* disable channel 0 */
		  OUTB (devc->osdev,
			INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_ENABLE,
			FUNCTRL0 + 2);

		}
	    }
	}
    }
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
}

static void
set_dac_rate (int dev, int chan_type)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  unsigned char freq = 4, val;
  int i;
  int rate = portc->speed;

  if (rate > 48000)
    rate = 48000;
  if (rate < 5512)
    rate = 5512;

  for (i = 0; i < sizeof (rate_lookup) / sizeof (rate_lookup[0]); i++)
    {
      if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper)
	{
	  rate = rate_lookup[i].rate;
	  freq = rate_lookup[i].freq;
	  break;
	}
    }
  if (chan_type == CHAN0)
    {
      val = INB (devc->osdev, FUNCTRL1 + 1) & ~0x1c;
      OUTB (devc->osdev, val | freq << 2, FUNCTRL1 + 1);
    }
  else
    {
      val = INB (devc->osdev, FUNCTRL1 + 1) & ~0xe0;
      OUTB (devc->osdev, val | freq << 5, FUNCTRL1 + 1);
    }
  if (devc->spdif_enabled)
    set_spdif_rate (devc, portc);
}

static void
set_dac_fmt (int dev, int chan_type)
{
  unsigned char val;
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  int channels = portc->channels;
  int bits = portc->bits;

  if (chan_type == CHAN0)
    {
      /* Set the format on Channl 0 */
      val = INB (devc->osdev, CHFORMAT) & ~0x3;

      if ((channels == 1) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x00 | val, CHFORMAT);
	  portc->dacfmt = 0;
	}

      if ((channels == 2) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x01 | val, CHFORMAT);
	  portc->dacfmt = 1;
	}

      if ((channels == 1) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x02 | val, CHFORMAT);
	  portc->dacfmt = 1;
	}

      if ((channels > 1) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x03 | val, CHFORMAT);
	  portc->dacfmt = 2;
	}
    }
  else
    {
      /* Set the format on Channel 1 */
      val = INB (devc->osdev, CHFORMAT) & ~0xC;

      if ((channels == 1) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x00 | val, CHFORMAT);
	  portc->dacfmt = 0;
	}

      if ((channels == 2) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x04 | val, CHFORMAT);
	  portc->dacfmt = 1;
	}

      if ((channels == 1) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x08 | val, CHFORMAT);
	  portc->dacfmt = 1;
	}

      if ((channels > 1) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x0C | val, CHFORMAT);
	  portc->dacfmt = 2;
	}
    }
}


static void
set_adc_rate (int dev, int chan_type)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  unsigned char freq = 4, val;
  int i;
  int rate = portc->speed;

  if (rate > 48000)
    rate = 48000;
  if (rate < 5512)
    rate = 5512;
  for (i = 0; i < sizeof (rate_lookup) / sizeof (rate_lookup[0]); i++)
    {
      if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper)
	{
	  rate = rate_lookup[i].rate;
	  freq = rate_lookup[i].freq;
	  break;
	}
    }
  if (chan_type == CHAN1)
    {
      val = INB (devc->osdev, FUNCTRL1 + 1) & ~0xe0;
      OUTB (devc->osdev, val | freq << 5, FUNCTRL1 + 1);
    }
  else
    {
      val = INB (devc->osdev, FUNCTRL1 + 1) & ~0x1c;
      OUTB (devc->osdev, val | freq << 2, FUNCTRL1 + 1);
    }

  if (devc->dev_mode & SPDIFIN_MODE)
    set_spdif_rate (devc, portc);
}

static void
set_adc_fmt (int dev, int chan_type)
{
  unsigned char val;
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  int channels = portc->channels;
  int bits = portc->bits;

  if (chan_type == CHAN1)
    {
      /* Set the format on Channel 1 */
      val = INB (devc->osdev, CHFORMAT) & ~0xC;

      if ((channels == 1) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x00 | val, CHFORMAT);
	  portc->adcfmt = 0;
	}

      if ((channels == 2) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x04 | val, CHFORMAT);
	  portc->adcfmt = 1;
	}

      if ((channels == 1) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x08 | val, CHFORMAT);
	  portc->adcfmt = 1;
	}

      if ((channels > 1) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x0C | val, CHFORMAT);
	  portc->adcfmt = 2;
	}
    }
  else
    {
      /* Set the format on Channl 0 */
      val = INB (devc->osdev, CHFORMAT) & ~0x3;

      if ((channels == 1) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x00 | val, CHFORMAT);
	  portc->adcfmt = 0;
	}

      if ((channels == 2) && (bits == 8))
	{
	  OUTB (devc->osdev, 0x01 | val, CHFORMAT);
	  portc->adcfmt = 1;
	}

      if ((channels == 1) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x02 | val, CHFORMAT);
	  portc->adcfmt = 1;
	}

      if ((channels == 2) && (bits == 16))
	{
	  OUTB (devc->osdev, 0x03 | val, CHFORMAT);
	  portc->adcfmt = 2;
	}
    }
}

static void
setup_record (int dev, int chan_type)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  dmap_t *dmap = audio_engines[dev]->dmap_in;

  if (chan_type == CHAN1)	/* SPDIF Record can only occur on CHAN1 */
    {
      /* reset and disable channel */
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_RESET, FUNCTRL0 + 2);
      oss_udelay (10);
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_RESET, FUNCTRL0 + 2);

      cmpci_outsw (devc->mixer_dev, SPDIF_PLAY, SNDCTL_MIX_WRITE, 0);

      OUTL (devc->osdev, dmap->dmabuf_phys, CH1_FRAME1);
      OUTW (devc->osdev, (dmap->bytes_in_use >> portc->adcfmt) - 1,
	    CH1_FRAME2);
      OUTW (devc->osdev, (dmap->fragment_size >> portc->adcfmt),
	    CH1_FRAME2 + 2);

      /* set channel 1 to record mode */
      OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) | CM_CH1_RECORD,
	    FUNCTRL0);
      portc->chan1_rec = 1;

      /* setup SPDIF in on CHAN A */
      OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 9), FUNCTRL1);
    }
  else				/* Normal PCM record on Channel 0 */
    {
      /* reset and disable channel */
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_RESET, FUNCTRL0 + 2);
      oss_udelay (10);
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_RESET, FUNCTRL0 + 2);

      OUTL (devc->osdev, dmap->dmabuf_phys, CH0_FRAME1);
      OUTW (devc->osdev, (dmap->bytes_in_use >> portc->adcfmt) - 1,
	    CH0_FRAME2);
      OUTW (devc->osdev, (dmap->fragment_size >> portc->adcfmt),
	    CH0_FRAME2 + 2);

      /* set channel 0 to record mode */
      OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) | CM_CH0_RECORD,
	    FUNCTRL0);
      portc->chan0_rec = 1;
    }
}

/*ARGSUSED*/
static int
cmpci_audio_prepare_for_input (int dev, int bsize, int bcount)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);

  switch (devc->dev_mode)
    {
    case DEFAULT_MODE:
      set_adc_rate (dev, CHAN0);
      set_adc_fmt (dev, CHAN0);
      setup_record (dev, CHAN0);
      break;

    case DUALDAC_MODE:
      cmn_err (CE_WARN, "Cannot record because DUALDAC mode is ON.\n");
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return OSS_EIO;

    case SPDIFIN_MODE:
      if (portc->speed < 44100)
	{
	  cmn_err (CE_WARN,
		   "Cannot record spdif at sampling rate less than 44.1Khz.\n");
	  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
	  return OSS_EIO;
	}
      set_adc_rate (dev, CHAN1);
      set_adc_fmt (dev, CHAN1);
      setup_record (dev, CHAN1);
      break;
    }
  portc->audio_enabled &= ~PCM_ENABLE_INPUT;
  portc->trigger_bits &= ~PCM_ENABLE_INPUT;
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  return 0;
}

static void
setup_play (int dev, int chan_type)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  dmap_t *dmap = audio_engines[dev]->dmap_out;

  if (chan_type == CHAN0)
    {
      /* reset channel */
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_RESET, FUNCTRL0 + 2);
      oss_udelay (10);
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_RESET, FUNCTRL0 + 2);
      oss_udelay (10);

      /* Now set the buffer address/sizes */
      OUTL (devc->osdev, dmap->dmabuf_phys, CH0_FRAME1);
      OUTW (devc->osdev, (dmap->bytes_in_use >> portc->dacfmt) - 1,
	    CH0_FRAME2);
      OUTW (devc->osdev, (dmap->fragment_size >> portc->dacfmt),
	    CH0_FRAME2 + 2);

      /* set channel 0 to play mode */
      OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) & CM_CH0_PLAY, FUNCTRL0);
      portc->chan0_play = 1;

      /* setup spdif output on CHAN A , disable CHAN B spdif */
      if (devc->spdif_enabled)
	{
	  OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 8),
		FUNCTRL1);
	  OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 9),
		FUNCTRL1);
	}
    }
  else
    {
      /* reset and disable channel */
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_RESET, FUNCTRL0 + 2);
      oss_udelay (10);
      OUTB (devc->osdev,
	    INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_RESET, FUNCTRL0 + 2);
      oss_udelay (10);

      /* Handle 4/5/6 channel mode */
      if (portc->channels < 4)
	{
	  /* check if 4speaker mode is enabled from mixer or not */
	  if (devc->mode_4spk)
	    OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26),
		  MISC_CTRL);

	  /* disable 4channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 29),
		CHFORMAT);
	  /* disable 5 channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(0x80000000),
		CHFORMAT);
	  /* disable 6channel mode out CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 15),
		LEGACY_CTRL);
	  /* disable 8 channel decode on CHAN B - only for CMI8768 */
	  if (devc->model == MDL_CM8768)
	    OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) & ~0x20,
		  MISC2_CTRL);
	  /* Set NXCNG */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(0x80000000),
		LEGACY_CTRL);
	}

      if ((portc->channels == 4) && (devc->chiprev > 37))
	{
	  /* disable 4 speaker mode */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26),
		MISC_CTRL);
	  /* enable 4channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | (1 << 29),
		CHFORMAT);

	  /* disable 5 channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(0x80000000),
		CHFORMAT);
	  /* disable 6channel mode out CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 15),
		LEGACY_CTRL);
	  /* disable center/bass channel */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 7),
		MISC_CTRL);
	  /* disable bass */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 12),
		LEGACY_CTRL);

	  /* disable 8 channel decode on CHAN B - only for CMI8768 */
	  if (devc->model == MDL_CM8768)
	    OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) & ~0x20,
		  MISC2_CTRL);
	  /* Set NXCNG */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(0x80000000),
		LEGACY_CTRL);
	}

      if ((portc->channels == 6) && (devc->chiprev > 37))
	{
	  /* disable 4 speaker mode */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26),
		MISC_CTRL);
	  /* disable 4channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 29),
		CHFORMAT);

	  /* enable center channel */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 7),
		MISC_CTRL);
	  /* enable bass */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 12),
		LEGACY_CTRL);
	  /* enable 5 channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | (0x80000000),
		CHFORMAT);
	  /* enable 6 channel decode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 15),
		LEGACY_CTRL);

	  /* disable 8 channel decode on CHAN B - only for CMI8768 */
	  if (devc->model == MDL_CM8768)
	    OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) & ~0x20,
		  MISC2_CTRL);

	  /* Set NXCNG */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (0x80000000),
		LEGACY_CTRL);
	}

      if ((portc->channels == 8) && (devc->model == MDL_CM8768))
	{
	  /* disable 4 speaker mode */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26),
		MISC_CTRL);
	  /* disable 4channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 29),
		CHFORMAT);

	  /* enable center channel */
	  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 7),
		MISC_CTRL);
	  /* enable bass channel */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 12),
		LEGACY_CTRL);
	  /* disable 5 channel mode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(0x80000000),
		CHFORMAT);
	  /* disable 6 channel decode on CHAN B */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 15),
		LEGACY_CTRL);

	  /* enable 8 channel decode on CHAN B */
	  OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) | 0x20,
		MISC2_CTRL);
	  /* Set NXCNG */
	  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (0x80000000),
		LEGACY_CTRL);
	}

      /* Now set the buffer address/sizes */
      OUTL (devc->osdev, dmap->dmabuf_phys, CH1_FRAME1);
      OUTW (devc->osdev, (dmap->bytes_in_use >> portc->dacfmt) - 1,
	    CH1_FRAME2);
      OUTW (devc->osdev, (dmap->fragment_size >> portc->dacfmt),
	    CH1_FRAME2 + 2);


      /* set channel 1 to play mode */
      OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) & CM_CH1_PLAY, FUNCTRL0);
      portc->chan1_play = 1;

      /* setup spdif output on CHAN B , disable CHAN A spdif */
      if (devc->spdif_enabled)
	{
	  OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 9),
		FUNCTRL1);
	  OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 8),
		FUNCTRL1);
	}
    }
}

/*ARGSUSED*/
static int
cmpci_audio_prepare_for_output (int dev, int bsize, int bcount)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);

  if ((portc->bits == AFMT_AC3) && devc->can_ac3)
    {
      portc->bits = 16;
      portc->channels = 2;
      cmpci_outsw (devc->mixer_dev, SPDIF_PLAY, SNDCTL_MIX_WRITE, 1);
      setup_ac3 (devc, portc, 1);
    }
  else
    setup_ac3 (devc, portc, 0);

  switch (devc->dev_mode)
    {
    case DEFAULT_MODE:
      /* set speed */
      set_dac_rate (dev, CHAN1);
      /* set format */
      set_dac_fmt (dev, CHAN1);
      /* set buffer address/size and other setups */
      setup_play (dev, CHAN1);
      break;

    case DUALDAC_MODE:
      if (dev == devc->portc[0].audiodev)
	{
	  set_dac_rate (dev, CHAN1);
	  set_dac_fmt (dev, CHAN1);
	  setup_play (dev, CHAN1);
	  setup_ac3 (devc, portc, 0);
	}
      if (dev == devc->portc[1].audiodev)
	{
	  set_dac_rate (dev, CHAN0);
	  set_dac_fmt (dev, CHAN0);
	  setup_play (dev, CHAN0);
	  setup_ac3 (devc, portc, 0);
	}
      break;

    case SPDIFIN_MODE:
      set_dac_rate (dev, CHAN0);
      set_dac_fmt (dev, CHAN0);
      setup_play (dev, CHAN0);
      setup_ac3 (devc, portc, 0);
      break;
    }

  portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
  portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  return 0;
}

static int
cmpci_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
{
  cmpci_devc *devc = audio_engines[dev]->devc;
  cmpci_portc *portc = audio_engines[dev]->portc;
  unsigned int ptr = 0;
  oss_native_word flags;

  if (!(portc->open_mode & direction))
    return 0;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  if (direction == PCM_ENABLE_INPUT)
    {
      if (portc->chan0_rec)
	ptr = INW (devc->osdev, CH0_FRAME1);
      if (portc->chan1_rec)
	ptr = INW (devc->osdev, CH1_FRAME1);
    }

  if (direction == PCM_ENABLE_OUTPUT)
    {
      if (portc->chan0_play)
	ptr = INW (devc->osdev, CH0_FRAME1);
      if (portc->chan1_play)
	ptr = INW (devc->osdev, CH1_FRAME1);
    }
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return ptr % dmap->bytes_in_use;
}

static audiodrv_t cmpci_audio_driver = {
  cmpci_audio_open,
  cmpci_audio_close,
  cmpci_audio_output_block,
  cmpci_audio_start_input,
  cmpci_audio_ioctl,
  cmpci_audio_prepare_for_input,
  cmpci_audio_prepare_for_output,
  cmpci_audio_reset,
  NULL,
  NULL,
  cmpci_audio_reset_input,
  cmpci_audio_reset_output,
  cmpci_audio_trigger,
  cmpci_audio_set_rate,
  cmpci_audio_set_format,
  cmpci_audio_set_channels,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,				/* cmpci_alloc_buffer, */
  NULL,				/* cmpci_free_buffer, */
  NULL,
  NULL,
  cmpci_get_buffer_pointer
};

#ifdef OBSOLETED_STUFF
static void
attach_mpu (cmpci_devc * devc)
{
  unsigned int base;

  if (devc->chiprev == 33)
    base = 0x330;		/* Chiprev033 doen't have bas+0x40 */
  else
    base = MPU_MIRROR;		/* base+0x40 is MPU PCI mirror */

  uart401_init (&devc->uart401devc, devc->osdev, base, "Cmedia MIDI UART");
  devc->uart401_attached = 1;
}
#endif

static int
init_cmpci (cmpci_devc * devc)
{
  oss_native_word val;
  int first_dev = 0;
  int i;

  devc->fm_attached = 0;


Enable BusMasterMode and IOSpace Access

  /* Check the model number of the chip */
  val = INL (devc->osdev, INT_HLDCLR) & 0xff000000;

  if (!val)
    {
      val = INL (devc->osdev, CHFORMAT) & 0x1f000000;
      if (!val)
	{
	  devc->chiprev = 33;
	  devc->can_ac3 = 0;
	  devc->max_channels = 6;
	}
      else
	{
	  devc->chiprev = 37;
	  devc->can_ac3 = 1;
	  devc->max_channels = 6;
	}
    }
  else
    {
      if (val & 0x04000000)
	{
	  devc->chiprev = 39;
	  devc->can_ac3 = 1;
	  devc->max_channels = 6;
	}
      if (val & 0x08000000)
	{
	  devc->chiprev = 55;
	  devc->can_ac3 = 1;
	  devc->max_channels = 6;
	}
      if (val & 0x28000000)
	{
	  devc->chiprev = 68;
	  devc->can_ac3 = 1;
	  devc->model = MDL_CM8768;
	  devc->max_channels = 8;
	  devc->chip_name = "CMedia CM8768";
	}
    }

  /* enable uart, joystick in Function Control Reg1 */
  OUTB (devc->osdev, INB (devc->osdev, FUNCTRL1) | 0x06, FUNCTRL1);
  /* enable FM */
  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 19), MISC_CTRL);
  OUTB (devc->osdev, 0, INT_HLDCLR + 2);	/* disable ints */
  OUTB (devc->osdev, 0, FUNCTRL0 + 2);	/* reset channels */

#ifdef OBSOLETED_STUFF
  attach_mpu (devc);
#endif

  /* install the CMPCI mixer */
  if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
					    devc->osdev,
					    devc->osdev,
					    "CMedia CMPCI",
					    &cmpci_mixer_driver,
					    sizeof (mixer_driver_t),
					    devc)) < 0)
    {
      return 0;
    }

  mixer_devs[devc->mixer_dev]->hw_devc = devc;
  mixer_devs[devc->mixer_dev]->priority = 1;	/* Possible default mixer candidate */

  cmpci_mixer_reset (devc->mixer_dev);
  mixer_ext_set_init_fn (devc->mixer_dev, cmpci_mix_init, 25);
  OUTB (devc->osdev, 0xF, MIXER2);

  /* setup 4speaker output */
  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 26), MISC_CTRL);
  /* enable subwoofer/center channel */
  OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 12), LEGACY_CTRL);
  OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 7), MISC_CTRL);

  for (i = 0; i < MAX_PORTC; i++)
    {
      char tmp_name[100];
      cmpci_portc *portc = &devc->portc[i];
      int caps = ADEV_AUTOMODE;

      if (i == 0)
	{
	  sprintf (tmp_name, "%s (rev %0d)", devc->chip_name, devc->chiprev);
	  caps |= ADEV_DUPLEX;
	}
      else
	{
	  sprintf (tmp_name, "%s (playback only)", devc->chip_name);
	  caps |= ADEV_NOINPUT;
	}
      if ((portc->audiodev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
						   devc->osdev,
						   devc->osdev,
						   tmp_name,
						   &cmpci_audio_driver,
						   sizeof (audiodrv_t),
						   caps,
						   AFMT_U8 | AFMT_S16_LE |
						   AFMT_AC3, devc, -1)) < 0)
	{
	  return 0;
	}
      else
	{
	  if (i == 0)
	    first_dev = portc->audiodev;
	  audio_engines[portc->audiodev]->portc = portc;
	  audio_engines[portc->audiodev]->rate_source = first_dev;
	  audio_engines[portc->audiodev]->caps =
	    PCM_CAP_ANALOGOUT | PCM_CAP_ANALOGIN | PCM_CAP_DIGITALOUT |
	    PCM_CAP_DIGITALIN;
	  audio_engines[portc->audiodev]->min_rate = 5000;
	  audio_engines[portc->audiodev]->max_rate = 48000;
	  audio_engines[portc->audiodev]->caps |= PCM_CAP_FREERATE;
	  audio_engines[portc->audiodev]->min_channels = 2;
	  audio_engines[portc->audiodev]->max_channels = devc->max_channels;
	  audio_engines[portc->audiodev]->vmix_flags = VMIX_MULTIFRAG;
	  audio_engines[portc->audiodev]->dmabuf_alloc_flags |=
	    DMABUF_SIZE_16BITS;
	  portc->open_mode = 0;
	  portc->audio_enabled = 0;
          audio_engines[portc->audiodev]->mixer_dev = devc->mixer_dev;
          devc->dev_mode = DEFAULT_MODE;
          devc->spdif_enabled = 0;
#ifdef CONFIG_OSS_VMIX
	  if (i == 0)
	     vmix_attach_audiodev(devc->osdev, portc->audiodev, -1, 0);
#endif
	}
    }
  return 1;
}

int
oss_cmpci_attach (oss_device_t * osdev)
{
  unsigned char pci_irq_line, pci_revision;
  unsigned short pci_command, vendor, device;
  unsigned int pci_ioaddr;
  int err;
  cmpci_devc *devc;

  DDB (cmn_err (CE_CONT, "Entered CMEDIA CMPCI attach routine\n"));
  pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
  pci_read_config_word (osdev, PCI_DEVICE_ID, &device);

  if (vendor != CMEDIA_VENDOR_ID
      || ((device != CMEDIA_CM8738) && (device != CMEDIA_CM8338A)
	  && (device != CMEDIA_CM8338B)))
    return 0;

  pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision);
  pci_read_config_word (osdev, PCI_COMMAND, &pci_command);
  pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line);
  pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr);

  DDB (cmn_err (CE_WARN, "CMPCI I/O base %04x\n", pci_ioaddr));

  if (pci_ioaddr == 0)
    {
      cmn_err (CE_WARN, "I/O address not assigned by BIOS.\n");
      return 0;
    }

  if (pci_irq_line == 0)
    {
      cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d).\n", pci_irq_line);
      return 0;
    }

  if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL)
    {
      cmn_err (CE_WARN, "Out of memory\n");
      return 0;
    }


  devc->osdev = osdev;
  osdev->devc = devc;
  devc->irq = pci_irq_line;

  /* Map the IO Base address */
  devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr);

  /* Remove I/O space marker in bit 0. */
  devc->base &= ~3;

  /* set the PCI_COMMAND register to master mode */
  pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO;
  pci_write_config_word (osdev, PCI_COMMAND, pci_command);

  switch (device)
    {
    case CMEDIA_CM8738:
      devc->model = MDL_CM8738;
      devc->chip_name = "CMedia CM8738";
      devc->max_channels = 6;
      break;

    case CMEDIA_CM8338A:
      devc->model = MDL_CM8338A;
      devc->chip_name = "CMedia CM8338A";
      devc->max_channels = 6;
      break;

    case CMEDIA_CM8338B:
      devc->model = MDL_CM8338B;
      devc->chip_name = "CMedia CM8338B";
      devc->max_channels = 6;
      break;
    }

  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 ((err = oss_register_interrupts (devc->osdev, 0, cmpciintr, NULL)) < 0)
    {
      cmn_err (CE_WARN, "Can't allocate IRQ%d, err=%d\n", pci_irq_line, err);
      return 0;
    }

  return init_cmpci (devc);	/* Detected */
}

int
oss_cmpci_detach (oss_device_t * osdev)
{
  cmpci_devc *devc = (cmpci_devc *) osdev->devc;

  if (oss_disable_device (osdev) < 0)
    return 0;

  /* disable Interrupts */
  OUTB (devc->osdev, 0, INT_HLDCLR + 2);

  /* disable channels */
  OUTB (devc->osdev, 0, FUNCTRL0 + 2);

  /* uninstall UART401 */
  if (devc->uart401_attached)
    uart401_disable (&devc->uart401devc);

  oss_unregister_interrupts (devc->osdev);

  MUTEX_CLEANUP (devc->mutex);
  MUTEX_CLEANUP (devc->low_mutex);
  UNMAP_PCI_IOADDR (devc->osdev, 0);

  oss_unregister_device (osdev);
  return 1;
}

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