/*
 * Purpose: Driver for RME Digi96 family
 */
/*
 * This file is part of Open Sound System
 *
 * Copyright (C) 4Front Technologies 1996-2008.
 *
 * This software is released under the BSD license.
 * See the COPYING file included in the main directory of this source
 * distribution for the license terms and conditions
 */


#include "oss_digi96_cfg.h"
#include "oss_pci.h"

#define RME_VENDOR_ID2		0x10ee
#define RME_DIGI96		0x3fc0
#define RME_DIGI96_8		0x3fc1
#define RME_DIGI96_PRO		0x3fc2
#define RME_DIGI96_PAD		0x3fc3

#define MAX_AUDIO_CHANNEL	2

/*
 * Control register pCTRL1
 */
#define CTRL1_STARTPLAY		0x00000001
#define CTRL1_STARTREC		0x00000002
#define CTRL1_GAIN		0x0000000c
#define CTRL1_MODE24_PLAY	0x00000010
#define CTRL1_MODE24_REC 	0x00000020
#define CTRL1_PLAYBM		0x00000040
#define CTRL1_RECBM		0x00000080
#define CTRL1_ADAT		0x00000100
#define CTRL1_FREQ		0x00000600
#define	CTRL1_FREQ32		0x00000200
#define	CTRL1_FREQ44		0x00000400
#define	CTRL1_FREQ48		0x00000600
#define CTRL1_DS		0x00000800
#define CTRL1_PRO		0x00001000
#define CTRL1_EMP		0x00002000
#define CTRL1_SEL		0x00004000
#define CTRL1_MASTER		0x00008000
#define CTRL1_PD		0x00010000
#define CTRL1_INPUTSEL		0x00060000
#define CTRL1_INPUTSHIFT		17
#define CTRL1_THRU		0x07f80000
#define CTRL1_AC3		0x08000000
#define CTRL1_MONITOR		0x30000000
#define CTRL1_ISEL		0x40000000
#define CTRL1_IDIS		0x80000000

/*
 * Control register pCTRL2
 */
#define CTRL2_WSEL		0x00000001
#define CTRL2_ANALOG		0x00000002	/* PAD only */
#define CTRL2_FREQAD		0x0000001c	/* PAD */
#define CTRL2_PD2		0x00000020
#
/* Next ones are only for new Digi96/8Pro (blue board) and PAD */
#define CTRL2_DAC_EN		0x00000040
#define CTRL2_CLATCH		0x00000080
#define CTRL2_CCLK		0x00000100
#define CTRL2_CDATA		0x00000200
/*
 * For reads from the pPLAYPOS and pRECPOS registers
 */

#define POS_PLAYIRQ		0x80000000
#define POS_AUTOSYNC		0x40000000
#define POS_FBITS		0x38000000
#define POS_FSHIFT			27
#define POS_ERF			0x04000000
#define POS_CONSTANT11		0x03000000
#define POS_LOCK		0x00800000
#define POS_DEVID		0x00600000
#define POS_CONSTANT000		0x008c0000
#define POS_TOUT		0x00020000
#define POS_RECIRQ		0x00010000
#define POS_ADDR		0x000fffff

typedef struct digi96_portc
{
  int open_mode;
  int audio_dev;
  int channels, bits;
  int speed, speedsel;
  int trigger_bits;
}
digi96_portc;

typedef struct digi96_devc
{
  oss_device_t *osdev;
  oss_mutex_t mutex;

  char *chip_name;
  int have_adat;
  int have_analog;
  int mode;
#define MD_SPDIF	0
#define MD_AES		1
#define MD_ADAT		2

  oss_native_word physaddr;
  char *linaddr;
  unsigned int *pPLAYBUF;
  unsigned int *pRECBUF;
  unsigned int *pCTRL1, ctrl1_bits;
  unsigned int *pCTRL2, ctrl2_bits;
  unsigned int *pPLAYACK;
  unsigned int *pRECACK;
  unsigned int *pPLAYPOS;
  unsigned int *pRECPOS;
  unsigned int *pRESETPLAY;
  unsigned int *pRESETREC;

  int irq;

  digi96_portc portc[MAX_AUDIO_CHANNEL];
  int open_mode;
#define TY_READ	0
#define TY_WRITE 1
#define TY_BOTH 2
  int mixer_dev;

  int master;
  int external_locked;
  int input_source;
  int doublespeed;
  int adatrate;
}
digi96_devc;

static int fbit_tab[8] = {
  0, 0, 0, 96000, 88200, 48000, 44100, 32000
};


static void
write_ctrl1 (digi96_devc * devc, unsigned int ctrl)
{
  devc->ctrl1_bits = ctrl;

  PCI_WRITEL (devc->osdev, devc->pCTRL1, ctrl);
}

static void
write_ctrl2 (digi96_devc * devc, unsigned int ctrl)
{
  devc->ctrl2_bits = ctrl;

  PCI_WRITEL (devc->osdev, devc->pCTRL2, ctrl);
}

static int
digi96intr (oss_device_t * osdev)
{
  int i, serviced = 0;
  unsigned int playstat, recstat;
  digi96_devc *devc = (digi96_devc *) osdev->devc;
  digi96_portc *portc;

  playstat = PCI_READL (devc->osdev, devc->pPLAYPOS);
  recstat = PCI_READL (devc->osdev, devc->pRECPOS);

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

      if (playstat & POS_PLAYIRQ)
	{
	  serviced = 1;
	  PCI_WRITEL (devc->osdev, devc->pPLAYACK, 0);
	  oss_audio_outputintr (portc->audio_dev, 0);
	}

      if (recstat & POS_RECIRQ)
	{
	  serviced = 1;
	  PCI_WRITEL (devc->osdev, devc->pRECACK, 0);
	  oss_audio_inputintr (portc->audio_dev, 0);
	}
    }
  return serviced;
}


/***********************************
 * Audio routines 
 ***********************************/

static int
digi96_set_rate (int dev, int arg)
{
  digi96_devc *devc = audio_engines[dev]->devc;
  digi96_portc *portc = audio_engines[dev]->portc;

  static int speed_table[6] = { 32000, 44100, 48000, 64000, 88200, 96000 };
  int i, best = 2, dif, bestdif = 0x7fffffff;

  if (arg)
    {
      if (devc->external_locked || (portc->open_mode & OPEN_READ))
	arg = portc->speed;	/* Speed locked to input */

      for (i = 0; i < 6; i++)
	{
	  if (arg == speed_table[i])	/* Exact match */
	    {
	      portc->speed = arg;
	      portc->speedsel = i;
	      return portc->speed;
	    }

	  dif = arg - speed_table[i];
	  if (dif < 0)
	    dif *= -1;
	  if (dif <= bestdif)
	    {
	      best = i;
	      bestdif = dif;
	    }

	}

      portc->speed = speed_table[best];
      portc->speedsel = best;
    }

  return portc->speed;
}

static short
digi96_set_channels (int dev, short arg)
{
  digi96_portc *portc = audio_engines[dev]->portc;
  digi96_devc *devc = audio_engines[dev]->devc;

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

  if (devc->mode == MD_ADAT)
    arg = 8;
  else
    arg = 2;
  return portc->channels = arg;
}

static unsigned int
digi96_set_format (int dev, unsigned int arg)
{
  digi96_portc *portc = audio_engines[dev]->portc;

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

  return portc->bits = arg;
}

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

static void digi96_trigger (int dev, int state);

static void
digi96_reset (int dev)
{
  digi96_trigger (dev, 0);
}

static void
digi96_reset_input (int dev)
{
  digi96_portc *portc = audio_engines[dev]->portc;
  digi96_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
}

static void
digi96_reset_output (int dev)
{
  digi96_portc *portc = audio_engines[dev]->portc;
  digi96_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
}

static int
verify_input (digi96_devc * devc, digi96_portc * portc)
{
  int i, status, savedstatus;
  int tmp;
  oss_native_word flags;

  tmp = devc->ctrl1_bits & ~CTRL1_INPUTSEL;
  tmp |= (devc->input_source & 0x3) << CTRL1_INPUTSHIFT;
  write_ctrl1 (devc, tmp);

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);

  i = 0;
  status = PCI_READL (devc->osdev, devc->pRECPOS);

  if (status & POS_LOCK)	/* ADAT input */
    {
      devc->mode = MD_ADAT;
      portc->channels = 8;

      switch (devc->adatrate)
	{
	case 0:		/* Autodetect */
	  if (status & POS_AUTOSYNC)
	    portc->speed = 48000;
	  else
	    portc->speed = 44100;
	  DDB (cmn_err
	       (CE_WARN, "ADAT input detected, sr=%d Hz\n", portc->speed));
	  break;

	case 1:
	  portc->speed = 44100;
	  break;

	case 2:
	  portc->speed = 48000;
	  break;
	}
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return 1;
    }

  while (i++ < 100 && status & POS_ERF)
    {
      oss_udelay (10);
      status = PCI_READL (devc->osdev, devc->pRECPOS);
    }

  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  savedstatus = status;

  status &= POS_ERF;

  if (status)
    cmn_err (CE_WARN, "Cannot sync with the input signal\n");
  else
    {
      int fbits = (savedstatus & POS_FBITS) >> POS_FSHIFT;
      portc->speed = fbit_tab[fbits];
      DDB (cmn_err
	   (CE_WARN, "digi96: Measured input sampling rate is %d\n",
	    portc->speed));
    }
  return devc->external_locked = !status;
}

/*ARGSUSED*/
static int
digi96_open (int dev, int mode, int open_flags)
{
  unsigned int tmp;
  digi96_portc *portc = audio_engines[dev]->portc;
  digi96_devc *devc = audio_engines[dev]->devc;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  if (portc->open_mode != 0 || (devc->open_mode & mode))	/* Busy? */
    {
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return OSS_EBUSY;
    }

  portc->open_mode = mode;
  devc->open_mode |= mode;
  portc->audio_dev = dev;
  portc->trigger_bits = 0;

  tmp = devc->ctrl1_bits;
  devc->external_locked = 0;
  if (devc->master)
    tmp |= CTRL1_MASTER;
  else
    {
      tmp &= ~CTRL1_MASTER;
      devc->external_locked = 1;
    }
  write_ctrl1 (devc, tmp);
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);

  if (devc->external_locked || (portc->open_mode & OPEN_READ))
    verify_input (devc, portc);

  if (devc->mode == MD_ADAT)
    {
      audio_engines[dev]->min_block = 8 * 1024;
      audio_engines[dev]->max_block = 8 * 1024;
      audio_engines[dev]->caps &= ~DSP_CH_MASK;
      audio_engines[dev]->caps |= DSP_CH_MULTI;
      audio_engines[dev]->min_channels = 8;
      audio_engines[dev]->max_channels = 8;
      write_ctrl1 (devc, devc->ctrl1_bits & ~CTRL1_ISEL);
    }
  else
    {
      audio_engines[dev]->min_block = 2 * 1024;
      audio_engines[dev]->max_block = 2 * 1024;
      audio_engines[dev]->caps &= ~DSP_CH_MASK;
      audio_engines[dev]->caps |= DSP_CH_STEREO;
      audio_engines[dev]->min_channels = 2;
      audio_engines[dev]->max_channels = 2;
      write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_ISEL);
    }

  return 0;
}

static void
digi96_close (int dev, int mode)
{
  digi96_devc *devc = audio_engines[dev]->devc;
  digi96_portc *portc = audio_engines[dev]->portc;
  oss_native_word flags;

  digi96_reset (dev);
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  portc->open_mode = 0;
  devc->open_mode &= ~mode;
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
}

/*ARGSUSED*/
static void
digi96_output_block (int dev, oss_native_word ptr, int count, int fragsize,
		     int intrflag)
{
}

/*ARGSUSED*/
static void
digi96_start_input (int dev, oss_native_word ptr, int count, int fragsize,
		    int intrflag)
{
}

static void
digi96_trigger (int dev, int state)
{
  digi96_devc *devc = audio_engines[dev]->devc;
  digi96_portc *portc = audio_engines[dev]->portc;
  unsigned int tmp = devc->ctrl1_bits;

  state &= portc->open_mode;

  if (portc->open_mode & OPEN_WRITE)
    {
      if (state & PCM_ENABLE_OUTPUT)
	tmp |= CTRL1_STARTPLAY;
      else
	tmp &= ~CTRL1_STARTPLAY;
    }

  if (portc->open_mode & OPEN_READ)
    {
      if (state & PCM_ENABLE_INPUT)
	tmp |= CTRL1_STARTREC;
      else
	tmp &= ~CTRL1_STARTREC;
    }

  portc->trigger_bits = state;
  write_ctrl1 (devc, tmp);
}

/*ARGSUSED*/
static int
digi96_prepare_for_output (int dev, int bsize, int bcount)
{
  digi96_devc *devc = audio_engines[dev]->devc;
  digi96_portc *portc = audio_engines[dev]->portc;

  int cmd = devc->ctrl1_bits;
  int doublespeed;

  PCI_WRITEL (devc->osdev, devc->pRESETPLAY, 0);
  if (devc->master)
    cmd |= CTRL1_MASTER;
  else
    cmd &= ~CTRL1_MASTER;
  if (portc->channels == 8)
    cmd |= CTRL1_ADAT;
  else
    cmd &= ~CTRL1_ADAT;
  write_ctrl1 (devc, cmd);

  if (!devc->master)
    {
      if (!verify_input (devc, portc))
	return OSS_EIO;
    }

  cmd = devc->ctrl1_bits;
  doublespeed = (portc->speedsel > 3);

  cmd &= ~(CTRL1_DS | CTRL1_FREQ);
  if (doublespeed)
    cmd |= CTRL1_DS;

  cmd |= ((portc->speedsel % 3) + 1) << 9;
  write_ctrl1 (devc, cmd);

  if (doublespeed != devc->doublespeed)
    {
      devc->doublespeed = doublespeed;
      write_ctrl1 (devc, cmd | CTRL1_PD);	/* Reset the DAC */
    }

  if (portc->bits == AFMT_AC3)
    write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_AC3 | CTRL1_PD);
  else
    {
      if (portc->bits == AFMT_S32_LE)
	{
	  write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_MODE24_PLAY);
	}
      else
	write_ctrl1 (devc, devc->ctrl1_bits & ~(CTRL1_MODE24_PLAY));

      write_ctrl1 (devc, devc->ctrl1_bits & ~(CTRL1_AC3 | CTRL1_PD));	/* Unmute DAC */
    }
  return 0;
}

/*ARGSUSED*/
static int
digi96_prepare_for_input (int dev, int bsize, int bcount)
{
  digi96_devc *devc = audio_engines[dev]->devc;
  digi96_portc *portc = audio_engines[dev]->portc;

  int cmd = devc->ctrl1_bits;

  if (portc->channels == 8)
    cmd |= CTRL1_ADAT;
  else
    cmd &= ~CTRL1_ADAT;

  if (portc->bits == AFMT_S32_LE)
    cmd |= CTRL1_MODE24_REC;
  else
    cmd &= ~CTRL1_MODE24_REC;
  write_ctrl1 (devc, cmd);

  if (!verify_input (devc, portc))
    return OSS_EIO;
  PCI_WRITEL (devc->osdev, devc->pRESETREC, 0);

  return 0;
}

static int
digi96_alloc_buffer (int dev, dmap_t * dmap, int direction)
{
  digi96_devc *devc = audio_engines[dev]->devc;

  if (direction == OPEN_WRITE)
    {
      dmap->dmabuf = (void *) devc->pPLAYBUF;
      dmap->dmabuf_phys = (oss_native_word) devc->pPLAYBUF;
    }
  else
    {
      dmap->dmabuf = (void *) devc->pRECBUF;
      dmap->dmabuf_phys = (oss_native_word) devc->pRECBUF;
    }
  dmap->buffsize = 64 * 1024;

  return 0;
}

/*ARGSUSED*/
static int
digi96_free_buffer (int dev, dmap_t * dmap, int direction)
{
  dmap->dmabuf = NULL;
  dmap->dmabuf_phys = 0;
  return 0;
}

/*ARGSUSED*/
static void
digi96_setup_fragments (int dev, dmap_p dmap, int direction)
{
  /* Make sure the whole sample RAM is covered by the buffer */
  dmap->nfrags = dmap->buffsize / dmap->fragment_size;
}

static audiodrv_t digi96_driver = {
  digi96_open,
  digi96_close,
  digi96_output_block,
  digi96_start_input,
  digi96_ioctl,
  digi96_prepare_for_input,
  digi96_prepare_for_output,
  digi96_reset,
  NULL,
  NULL,
  digi96_reset_input,
  digi96_reset_output,
  digi96_trigger,
  digi96_set_rate,
  digi96_set_format,
  digi96_set_channels,
  NULL,
  NULL,
  NULL,
  NULL,
  digi96_alloc_buffer,
  digi96_free_buffer,
  NULL,
  NULL,
  NULL,				/* digi96_get_buffer_pointer */
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  digi96_setup_fragments
};

/*ARGSUSED*/
static int
digi96_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
{
  if (cmd == SOUND_MIXER_PRIVATE1)	/* Bogus testing */
    {
      int val;

      val = *arg;
      return *arg = val;
    }
  if (cmd == SOUND_MIXER_READ_DEVMASK ||
      cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC)
    return *arg = 0;

  if (cmd == SOUND_MIXER_READ_VOLUME || cmd == SOUND_MIXER_READ_PCM)
    return *arg = 100 | (100 << 8);
  if (cmd == SOUND_MIXER_WRITE_VOLUME || cmd == SOUND_MIXER_WRITE_PCM)
    return *arg = 100 | (100 << 8);
  return OSS_EINVAL;
}

static mixer_driver_t digi96_mixer_driver = {
  digi96_mixer_ioctl
};

static int
digi96_set_control (int dev, int ctrl, unsigned int cmd, int value)
{
  digi96_devc *devc = mixer_devs[dev]->devc;
  unsigned int tmp;

  if (ctrl < 0)
    return OSS_EINVAL;

  if (cmd == SNDCTL_MIX_READ)
    {
      switch (ctrl)
	{
	case 1:
	  return !!devc->master;
	  break;

	case 2:
	  return devc->input_source;
	  break;

	case 3:
	  return !!(devc->ctrl1_bits & CTRL1_SEL);
	  break;

	case 4:
	  return devc->mode;
	  break;

	case 5:
	  return !!(devc->ctrl2_bits & CTRL2_WSEL);
	  break;

	case 6:
	  return !!(devc->ctrl1_bits & CTRL1_EMP);
	  break;

	case 7:
	  return !!(devc->ctrl1_bits & CTRL1_AC3);
	  break;

	case 8:
	  return devc->adatrate;
	  break;
	}

      return OSS_EINVAL;
    }

  if (cmd == SNDCTL_MIX_WRITE)
    {
      switch (ctrl)
	{
	case 1:
	  devc->master = !!value;
	  return !!value;
	  break;

	case 2:
	  if (value < 0 || value > 3)
	    return OSS_EINVAL;

	  devc->input_source = value;
	  return value;
	  break;

	case 3:
	  tmp = devc->ctrl1_bits;
	  if (value)
	    tmp |= CTRL1_SEL;
	  else
	    {
	      tmp &= ~(CTRL1_SEL | CTRL1_MASTER);
	      devc->master = 0;
	    }
	  write_ctrl1 (devc, tmp);
	  return value;
	  break;

	case 4:
	  if (value > 2 || (!devc->have_adat && value > 1))
	    return OSS_EINVAL;

	  devc->mode = value;
	  return value;
	  break;

	case 5:
	  if (value)
	    {
	      write_ctrl2 (devc, devc->ctrl2_bits | CTRL2_WSEL);
	      return 1;
	    }

	  write_ctrl2 (devc, devc->ctrl2_bits & ~CTRL2_WSEL);
	  return 0;
	  break;

	case 6:
	  if (value)
	    {
	      write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_EMP);
	      return 1;
	    }

	  write_ctrl1 (devc, devc->ctrl1_bits & ~CTRL1_EMP);
	  return 0;
	  break;

	case 7:
	  if (value)
	    {
	      write_ctrl1 (devc, devc->ctrl1_bits | CTRL1_AC3);
	      return 1;
	    }

	  write_ctrl1 (devc, devc->ctrl1_bits & ~CTRL1_AC3);
	  return 0;
	  break;

	case 8:
	  if (value > 2)
	    value = 2;
	  if (value < 0)
	    value = 0;

	  return devc->adatrate = value;
	  break;

	}

      return OSS_EINVAL;
    }

  return OSS_EINVAL;
}

static int
digi96_mix_init (int dev)
{
  /* digi96_devc *devc = mixer_devs[dev]->devc; */
  int group, err;

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

  if ((err = mixer_ext_create_control (dev, group,
				       4, digi96_set_control,
				       MIXT_ENUM,
				       "DIGI96_MODE", 3,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       1, digi96_set_control,
				       MIXT_ENUM,
				       "DIGI96_SYNC", 2,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       2, digi96_set_control,
				       MIXT_ENUM,
				       "DIGI96_INPUT", 4,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       3, digi96_set_control,
				       MIXT_ENUM,
				       "DIGI96_SEL", 2,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       5, digi96_set_control,
				       MIXT_ONOFF,
				       "DIGI96_WORLDCLK", 1,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       6, digi96_set_control,
				       MIXT_ONOFF,
				       "DIGI96_EMPH", 1,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

#if 0
  if ((err = mixer_ext_create_control (dev, group,
				       7, digi96_set_control,
				       MIXT_ENUM,
				       "DIGI96_DATA", 2,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;
#endif

  if ((err = mixer_ext_create_control (dev, group,
				       8, digi96_set_control,
				       MIXT_ENUM,
				       "DIGI96_ADATRATE", 3,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;
  mixer_ext_set_strings (dev, err, "AUTO 44100 48000", 0);

  return 0;
}

static int
attach_channel (digi96_devc * devc, int chnum, char *name,
		audiodrv_t * drv, int type)
{
  int adev;
  digi96_portc *portc;
  int opts = ADEV_DUPLEX | ADEV_COLD | ADEV_AUTOMODE | ADEV_NOVIRTUAL;

  if (chnum < 0 || chnum >= MAX_AUDIO_CHANNEL)
    return 0;

  if (type == TY_BOTH)
    opts = ADEV_SHADOW;

  portc = &devc->portc[chnum];

  devc->master = 1;
  devc->input_source = 0;
  devc->doublespeed = 0;
  devc->external_locked = 0;

  if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
				    devc->osdev,
				    devc->osdev,
				    name,
				    drv,
				    sizeof (audiodrv_t),
				    opts,
				    AFMT_AC3 | AFMT_S16_LE | AFMT_S32_LE,
				    devc, -1)) < 0)
    {
      return 0;
    }

  audio_engines[adev]->mixer_dev = devc->mixer_dev;
  audio_engines[adev]->portc = portc;
  audio_engines[adev]->caps |= DSP_CH_STEREO;
  audio_engines[adev]->min_block = 2 * 1024;
  audio_engines[adev]->max_block = 2 * 1024;
  audio_engines[adev]->min_rate = 32000;
  audio_engines[adev]->max_rate = 96000;
  portc->open_mode = 0;
  devc->open_mode = 0;
  portc->audio_dev = adev;

  portc->speed = 48000;
  portc->speedsel = 3;
  portc->bits = AFMT_S16_LE;
  portc->channels = 2;
  return 1;
}

int
init_digi96 (digi96_devc * devc)
{
  int my_mixer;
  int ret;

  if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
				     devc->osdev,
				     devc->osdev,
				     "RME Digi96 Control panel",
				     &digi96_mixer_driver,
				     sizeof (mixer_driver_t), devc)) < 0)
    {

      devc->mixer_dev = -1;
      return 0;
    }
  else

    {
      mixer_devs[my_mixer]->priority = -1;	/* Don't use as the default mixer */
      devc->mixer_dev = my_mixer;
      mixer_ext_set_init_fn (my_mixer, digi96_mix_init, 20);
    }

  ret = attach_channel (devc, 0, devc->chip_name, &digi96_driver, 0);
  if (ret > 0)
    {
      ret =
	attach_channel (devc, 1, devc->chip_name, &digi96_driver, TY_BOTH);
    }

/*
 * Set some defaults
 */
  write_ctrl2 (devc, 0);
  write_ctrl1 (devc, CTRL1_ISEL | CTRL1_FREQ48 | CTRL1_PD);	/* Reset DAC */
  write_ctrl1 (devc, CTRL1_ISEL | CTRL1_FREQ48 | CTRL1_MASTER | CTRL1_SEL);
  write_ctrl2 (devc, CTRL2_DAC_EN);	/* Soft unmute the DAC (blue PRO boards) */
  return ret;
}


int
oss_digi96_attach (oss_device_t * osdev)
{
  digi96_devc *devc;
  unsigned char pci_irq_line, pci_revision /*, pci_latency */ ;
  unsigned short pci_command, vendor, device;
  unsigned int pci_ioaddr;
  int err;

  DDB (cmn_err (CE_WARN, "Entered Digi96 detect routine\n"));

  pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
  pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
  if (vendor != RME_VENDOR_ID2 ||
      (device != RME_DIGI96_PRO &&
       device != RME_DIGI96_PAD &&
       device != RME_DIGI96 && device != RME_DIGI96_8))

    return 0;

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

  pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
  pci_write_config_word (osdev, PCI_COMMAND, pci_command);

  if (pci_ioaddr == 0)
    {
      cmn_err (CE_WARN, "BAR0 not initialized by BIOS\n");
      return 0;
    }

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

  devc->osdev = osdev;
  osdev->devc = devc;
  devc->physaddr = pci_ioaddr;

  devc->have_adat = 0;
  devc->have_analog = 0;
  devc->mode = MD_SPDIF;

  switch (device)
    {
    case RME_DIGI96:
      devc->chip_name = "RME Digi96";
      break;

    case RME_DIGI96_8:
      devc->chip_name = "RME Digi96/8";
      devc->have_adat = 1;
      break;

    case RME_DIGI96_PRO:
      devc->have_adat = 1;
      if (pci_revision < 2)
	{
	  devc->chip_name = "RME Digi96/8 PRO (green)";
	}
      else
	{
	  devc->chip_name = "RME Digi96/8 PRO (blue)";
	}
      break;

    case RME_DIGI96_PAD:
      devc->have_adat = 1;
      devc->have_analog = 1;
      devc->chip_name = "RME Digi96/8 PAD";
      break;
    }

  MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);

  oss_register_device (osdev, devc->chip_name);

  devc->linaddr =
    (char *) MAP_PCI_MEM (devc->osdev, 0, devc->physaddr, 0x60000);
  if (devc->linaddr == NULL)
    {
      cmn_err (CE_WARN, "Can't map PCI registers (0x%08lx)\n",
	       devc->physaddr);
      return 0;
    }

  if ((err = oss_register_interrupts (devc->osdev, 0, digi96intr, NULL)) < 0)
    {
      cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err);
      return 0;
    }

  devc->pPLAYBUF = (unsigned int *) devc->linaddr;
  devc->pRECBUF = (unsigned int *) (devc->linaddr + 0x10000);
  devc->pCTRL1 = (unsigned int *) (devc->linaddr + 0x20000);
  devc->pCTRL2 = (unsigned int *) (devc->linaddr + 0x20004);
  devc->pPLAYACK = (unsigned int *) (devc->linaddr + 0x20008);
  devc->pRECACK = (unsigned int *) (devc->linaddr + 0x2000c);
  devc->pPLAYPOS = (unsigned int *) (devc->linaddr + 0x20000);
  devc->pRECPOS = (unsigned int *) (devc->linaddr + 0x30000);
  devc->pRESETPLAY = (unsigned int *) (devc->linaddr + 0x4fffc);
  devc->pRESETREC = (unsigned int *) (devc->linaddr + 0x5fffc);

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

int
oss_digi96_detach (oss_device_t * osdev)
{
  digi96_devc *devc = (digi96_devc *) osdev->devc;

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

  write_ctrl2 (devc, 0);	/* Soft mute */

  oss_unregister_interrupts (devc->osdev);

  MUTEX_CLEANUP (devc->mutex);

  UNMAP_PCI_MEM (devc->osdev, 0, devc->physaddr, devc->linaddr, 0x60000);

  oss_unregister_device (devc->osdev);

  return 1;

}
