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_cs461x/oss_cs461x.c

Driver for Crystal cs461x and cs461x PCI audio controllers

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_cs461x_cfg.h"
#include "midi_core.h"
#include "ac97.h"
#include "oss_pci.h"
#include "cs461x.h"

extern int cs461x_clkrun_fix;

#define CRYSTAL_VENDOR_ID	0x1013
#define CRYSTAL_CS4610_ID	0x6001
#define CRYSTAL_CS461x_ID	0x6003
#define CRYSTAL_CS4615_ID	0x6004

#define USE_SG

#define WRITEB(a,d) devc->bRegister0[a] = d
#define READB(a) devc->bRegister0[a]
#define WRITEW(a,d) devc->wRegister0[a>>1] = d
#define READW(a) devc->wRegister0[a>>1]

#define READ0L(a) (devc->dwRegister0[a>>2])
#define WRITE0L(a, d) (devc->dwRegister0[a>>2] = d)
#define READ1L(a) (devc->dwRegister1[a>>2])
#define WRITE1L(a,d) (devc->dwRegister1[a>>2] = d)

#define READ1L(a) (devc->dwRegister1[a>>2])

#ifdef OSS_BIG_ENDIAN
static unsigned int
ymf_swap (unsigned int x)
{
  return ((x & 0x000000ff) << 24) |
    ((x & 0x0000ff00) << 8) |
    ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24);
}

#define LSWAP(x) ymf_swap(x)
#else
#define LSWAP(x) 	x
#endif

#define MAX_PORTC 2

typedef struct cs461x_portc
{
  int speed, bits, channels;
  int open_mode;
  int audio_enabled;
  int trigger_bits;
  int audiodev;
}
cs461x_portc;

typedef struct cs461x_devc
{
  oss_device_t *osdev;
  char *chip_name;
  unsigned short subsysid;

  oss_native_word bar0addr, bar1addr;
  unsigned int *bar0virt, *bar1virt;
  volatile unsigned int *dwRegister0, *dwRegister1;
  volatile unsigned short *wRegister0, *wRegister1;
  volatile unsigned char *bRegister0, *bRegister1;
  int irq;
  int dual_codec;
  unsigned int *play_sgbuf;
  unsigned int play_sgbuf_phys;
  oss_dma_handle_t play_sgbuf_dma_handle;

  /* Mutex */
  oss_mutex_t mutex;
  oss_mutex_t low_mutex;

  /* MIDI */
  int midi_opened;
  int midi_dev;
  oss_midi_inputbyte_t midi_input_intr;

  /* Mixer parameters */
  ac97_devc ac97devc, ac97devc2;
  int mixer_dev, mixer2_dev;

  /* Audio parameters */
  cs461x_portc portc[MAX_PORTC];
  int open_mode;
  oss_native_word PCTL;		/*Play Control Register */
  oss_native_word CCTL;		/*Record Control Register */
  int processor_started;
}
cs461x_devc;

#define MAX_CS461x 6

static int ac97_write (void *devc, int addr, int data);
static int ac97_read (void *devc, int addr);

static void
PokeBA0 (cs461x_devc * devc, unsigned int offset, unsigned int value)
{
  WRITE0L (offset, value);
}

static unsigned int
PeekBA0 (cs461x_devc * devc, unsigned int offset)
{
  unsigned int value;

  value = READ0L (offset);
  return (value);

}

static int
ac97_read (void *devc_, int offset)
{
  cs461x_devc *devc = devc_;
  int count;
  unsigned int status, value;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);

Make sure that there is not data sitting around from a previous uncompleted access. ACSDA = Status Data Register = 47Ch

  status = PeekBA0 (devc, BA0_ACSDA);
  /* Get the actual AC97 register from the offset */
  PokeBA0 (devc, BA0_ACCAD, offset - BA0_AC97_RESET);
  PokeBA0 (devc, BA0_ACCDA, 0);
  PokeBA0 (devc, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM |
	   ACCTL_ESYN | ACCTL_RSTN);

  /* Wait for the read to occur. */
  for (count = 0; count < 10; count++)
    {
      /* First, we want to wait for a short time. */
      oss_udelay (100);

Now, check to see if the read has completed. ACCTL = 460h, DCV should be reset by now and 460h = 17h

      status = PeekBA0 (devc, BA0_ACCTL);
      if (!(status & ACCTL_DCV))
	{
	  break;
	}
    }

  /* Make sure the read completed. */
  if (status & ACCTL_DCV)
    {
      cmn_err (CE_WARN, "AC97 Read Timedout\n");
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return (-1);
    }

  /* Wait for the valid status bit to go active. */

  for (count = 0; count < 10; count++)
    {

Read the AC97 status register. ACSTS = Status Register = 464h

      status = PeekBA0 (devc, BA0_ACSTS);

See if we have valid status. VSTS - Valid Status

      if (status & ACSTS_VSTS)
	break;


Wait for a short while.

      oss_udelay (100);
    }
  /* Make sure we got valid status. */
  if (!(status & ACSTS_VSTS))
    {
      cmn_err (CE_WARN, "AC97 Read Timedout(2)\n");
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return (-1);
    }


Read the data returned from the AC97 register. ACSDA = Status Data Register = 474h

  value = PeekBA0 (devc, BA0_ACSDA);
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return (value);
}

static int
ac97_write (void *devc_, int offset, int data)
{
  cs461x_devc *devc = devc_;
  int count;
  unsigned int status = 0;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  PokeBA0 (devc, BA0_ACCAD, offset);
  PokeBA0 (devc, BA0_ACCDA, data);
  PokeBA0 (devc, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
  for (count = 0; count < 10; count++)
    {
      /* First, we want to wait for a short time. */
      oss_udelay (100);
      /* Now, check to see if the write has completed. */
      /* ACCTL = 460h, DCV should be reset by now and 460h = 07h */
      status = PeekBA0 (devc, BA0_ACCTL);
      if (!(status & ACCTL_DCV))
	break;
    }

  /* write didn't completed. */
  if (status & ACCTL_DCV)
    {
      cmn_err (CE_WARN, "AC97 Write timeout\n");
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return (-1);
    }
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return 0;
}

static int
ac97_read2 (void *devc_, int offset)
{
  cs461x_devc *devc = devc_;
  int count;
  unsigned int status, value;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);

Make sure that there is not data sitting around from a previous uncompleted access. ACSDA = Status Data Register = 47Ch

  status = PeekBA0 (devc, BA0_ACSDA2);

  /* Get the actual AC97 register from the offset */
  PokeBA0 (devc, BA0_ACCAD, offset);
  PokeBA0 (devc, BA0_ACCDA, 0);
  PokeBA0 (devc, BA0_ACCTL,
	   ACCTL_DCV | ACCTL_TC | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN |
	   ACCTL_RSTN);


  /* Wait for the read to occur. */
  for (count = 0; count < 10; count++)
    {
      /* First, we want to wait for a short time. */
      oss_udelay (1000);

Now, check to see if the read has completed. ACCTL = 460h, DCV should be reset by now and 460h = 17h

      status = PeekBA0 (devc, BA0_ACCTL);
      if (!(status & ACCTL_DCV))
	break;
    }

  /* Make sure the read completed. */
  if (PeekBA0 (devc, BA0_ACCTL) & ACCTL_DCV)
    {
      cmn_err (CE_WARN, "Secondary AC97 Read Timedout\n");
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return (-1);
    }

  /* Wait for the valid status bit to go active. */

  for (count = 0; count < 10; count++)
    {

Read the AC97 status register. ACSTS = Status Register = 464h

      status = PeekBA0 (devc, BA0_ACSTS2);

See if we have valid status. VSTS - Valid Status

      if (status & ACSTS_VSTS)
	break;


Wait for a short while.

      oss_udelay (1000);
    }
  /* Make sure we got valid status */
  if (!(status & ACSTS_VSTS))
    {
      cmn_err (CE_WARN, "Secondary AC97 Read Timedout(2)\n");
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return (-1);
    }


Read the data returned from the AC97 register. ACSDA = Status Data Register = 474h

  value = PeekBA0 (devc, BA0_ACSDA2);
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return (value);
}

static int
ac97_write2 (void *devc_, int offset, int data)
{
  cs461x_devc *devc = devc_;
  int count;
  unsigned int status = 0;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  PokeBA0 (devc, BA0_ACCAD, offset);
  PokeBA0 (devc, BA0_ACCDA, data);
  PokeBA0 (devc, BA0_ACCTL,
	   ACCTL_DCV | ACCTL_TC | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
  for (count = 0; count < 10; count++)
    {
      /* First, we want to wait for a short time. */
      oss_udelay (1000);
      /* Now, check to see if the write has completed. */
      /* ACCTL = 460h, DCV should be reset by now and 460h = 07h */
      status = PeekBA0 (devc, BA0_ACCTL);
      if (!(status & ACCTL_DCV))
	break;
    }

  /* write didn't completed. */
  if (status & ACCTL_DCV)
    {
      cmn_err (CE_WARN, "Secondary AC97 Write timeout\n");
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return (-1);
    }
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return 0;
}


this is 3*1024 for parameter, 3.5*1024 for sample and 2*3.5*1024 for code since each instruction is 40 bits and takes two dwords

#define INKY_BA1_DWORD_SIZE (13 * 1024 + 512)
/* this is parameter, sample, and code */
#define INKY_MEMORY_COUNT 3
struct cs461x_firmware_struct
{
  struct
  {
    unsigned int ulDestAddr;
    unsigned int ulSourceSize;
  }
  MemoryStat[INKY_MEMORY_COUNT];
  unsigned int BA1Array[INKY_BA1_DWORD_SIZE];
};
#include "cs461x_dsp.h"

static void
DoTransfer (cs461x_devc * devc, unsigned int *fpulSrc,
	    unsigned int ulByteDestOffset, unsigned int ulByteLength)
{
  int dwByteCounter;

  if (ulByteDestOffset & 0x3)
    {
      cmn_err (CE_WARN, "invalid DMA address\n");
      return;
    }

  for (dwByteCounter = 0; dwByteCounter < ulByteLength; dwByteCounter += 4)
    {
      WRITE1L ((ulByteDestOffset + dwByteCounter),
	       fpulSrc[dwByteCounter / 4]);
    }
}

static void
install_ucode (cs461x_devc * devc)
{
  unsigned int i, count;

  count = 0;
  for (i = 0; i < INKY_MEMORY_COUNT; i++)
    {

      DoTransfer (devc, (unsigned int *) (cs461x_firmware.BA1Array + count),
		  cs461x_firmware.MemoryStat[i].ulDestAddr,
		  cs461x_firmware.MemoryStat[i].ulSourceSize);
      count += cs461x_firmware.MemoryStat[i].ulSourceSize / 4;
    }
}

void
clear_serial_fifo (cs461x_devc * devc)
{
  unsigned int ulIdx, ulLoop;
  unsigned int ulStatus = 0;
  unsigned int ulCLKCR1;


  ulCLKCR1 = PeekBA0 (devc, BA0_CLKCR1);

  if (!(ulCLKCR1 & CLKCR1_SWCE))
    {
      PokeBA0 (devc, BA0_CLKCR1, ulCLKCR1 | CLKCR1_SWCE);
    }
  PokeBA0 (devc, BA0_SERBWP, 0);

  for (ulIdx = 0; ulIdx < 256; ulIdx++)
    {

Make sure the previous FIFO write operation has completed.

      for (ulLoop = 0; ulLoop < 5; ulLoop++)
	{
	  oss_udelay (100);
	  ulStatus = PeekBA0 (devc, BA0_SERBST);
	  if (!(ulStatus & SERBST_WBSY))
	    {
	      break;
	    }
	}

      if (ulStatus & SERBST_WBSY)
	{
	  if (!(ulCLKCR1 & CLKCR1_SWCE))
	    {
	      PokeBA0 (devc, BA0_CLKCR1, ulCLKCR1);
	    }
	}

      /* Write the serial port FIFO index. */

      PokeBA0 (devc, BA0_SERBAD, ulIdx);


Tell the serial port to load the new value into the FIFO location.

      PokeBA0 (devc, BA0_SERBCM, SERBCM_WRC);
    }


Now, if we powered up the devices, then power them back down again. This is kinda ugly, but should never happen.

  if (!(ulCLKCR1 & CLKCR1_SWCE))
    {
      PokeBA0 (devc, BA0_CLKCR1, ulCLKCR1);
    }

}

static int
cs461x_reset_processor (cs461x_devc * devc)
{
  unsigned int ulIdx;

  /* Write the reset bit of the SP control register. */
  WRITE1L (BA1_SPCR, SPCR_RSTSP);
  WRITE1L (BA1_SPCR, SPCR_DRQEN);

  /* Clear the trap registers. */
  for (ulIdx = 0; ulIdx < 8; ulIdx++)
    {
      WRITE1L (BA1_DREG, DREG_REGID_TRAP_SELECT + ulIdx);
      WRITE1L (BA1_TWPR, 0xFFFF);
    }

  WRITE1L (BA1_DREG, 0);
  WRITE1L (BA1_FRMT, 0xadf);
  return (0);
}

static int
cs461x_start_processor (cs461x_devc * devc)
{

  unsigned int ulCount;
  int ulTemp = 0;

  /* reset the processor first */
  cs461x_reset_processor (devc);

  /* reload the ucode */
  install_ucode (devc);

  /* Set the frame timer to reflect the number of cycles per frame. */
  WRITE1L (BA1_FRMT, 0xADF);


Turn on the run, run at frame, and DMA enable bits in the local copy of the SP control register.

  WRITE1L (BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);


Wait until the run at frame bit resets itself in the SP control register.

  for (ulCount = 0; ulCount < 25; ulCount++)
    {
      /* Wait a little bit, so we don't issue PCI reads too frequently. */
      oss_udelay (100);

      /* Fetch the current value of the SP status register. */
      ulTemp = READ1L (BA1_SPCR);

      /* If the run at frame bit has reset, then stop waiting. */
      if (!(ulTemp & SPCR_RUNFR))
	break;
    }
  /* If the run at frame bit never reset, then return an error. */
  if (ulTemp & SPCR_RUNFR)
    {
      cmn_err (CE_WARN, "Start(): SPCR_RUNFR never reset.\n");
    }
  devc->processor_started = 1;
  return 0;
}

static int
cs461xintr (oss_device_t * osdev)
{
  cs461x_devc *devc = (cs461x_devc *) osdev->devc;
  cs461x_portc *portc;
  unsigned int status;
  int i;
  unsigned int uart_stat;
  int serviced = 0;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);

/* Read the Interrupt Status Register */
  status = PeekBA0 (devc, BA0_HISR);


This is the MIDI read interrupt service. First check to see if the MIDI interrupt flag is set in the HISR register. Next read the MIDI status register. See if Receive Buffer Empty is empty (0=FIFO Not empty, 1=FIFO is empty

  if ((devc->midi_opened & OPEN_READ) && (status & HISR_MIDI))
    {
      uart_stat = PeekBA0 (devc, BA0_MIDSR);

read one byte of MIDI data and hand it off the the sequencer module to decode this. Keep checking to see if the data is available. Stop when no more data is there in the FIFO.

      while (!(uart_stat & MIDSR_RBE))
	{
	  unsigned char d;
	  d = PeekBA0 (devc, BA0_MIDRP);

	  if (devc->midi_opened & OPEN_READ && devc->midi_input_intr)
	    devc->midi_input_intr (devc->midi_dev, d);
	  uart_stat = PeekBA0 (devc, BA0_MIDSR);
	}
    }

/* Audio interrupt handling */
  if (status & 0x3)
    {
      for (i = 0; i < MAX_PORTC; i++)
	{
	  portc = &devc->portc[i];
	  if (status & 0x1)
	    if (portc->trigger_bits & PCM_ENABLE_OUTPUT)
	      {
		dmap_t *dmap = audio_engines[portc->audiodev]->dmap_out;
		int ptr, n;

		ptr = READ1L (BA1_PBA) - dmap->dmabuf_phys;
		ptr = ptr / dmap->fragment_size;
		if (ptr < 0 || ptr >= dmap->nfrags)
		  ptr = 0;
		n = 0;
		while (dmap_get_qhead (dmap) != ptr && n++ < dmap->nfrags)
		  oss_audio_outputintr (portc->audiodev, 0);
	      }

	  if (status & 0x2)
	    if (portc->trigger_bits & PCM_ENABLE_INPUT)
	      {

		dmap_t *dmap = audio_engines[portc->audiodev]->dmap_in;
		int ptr, n;

		ptr = READ1L (BA1_CBA) - dmap->dmabuf_phys;
		ptr = ptr / (dmap->fragment_size);

		if (ptr < 0 || ptr >= dmap->nfrags)
		  ptr = 0;
		n = 0;
		while (dmap_get_qtail (dmap) != ptr && n++ < dmap->nfrags)
		  oss_audio_inputintr (portc->audiodev, 0);
	      }
	}
    }

  serviced = 1;
  PokeBA0 (devc, BA0_HICR, 0x03);

  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);

  return serviced;
}

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

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

  if (audio_engines[dev]->flags & ADEV_FIXEDRATE)
    {
      audio_engines[dev]->fixed_rate = 48000;
      arg = 48000;
    }
  else
    audio_engines[dev]->fixed_rate = 0;

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

static short
cs461x_audio_set_channels (int dev, short arg)
{
  cs461x_portc *portc = audio_engines[dev]->portc;

  if (audio_engines[dev]->flags & ADEV_STEREOONLY)
    arg = 2;

  if ((arg != 1) && (arg != 2))
    return portc->channels;
  portc->channels = arg;

  return portc->channels;
}

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

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

  if (audio_engines[dev]->flags & ADEV_16BITONLY)
    arg = 16;

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

  return portc->bits;
}

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

static void cs461x_audio_trigger (int dev, int state);

static void
cs461x_audio_reset (int dev)
{
  cs461x_audio_trigger (dev, 0);
}

static void
cs461x_audio_reset_input (int dev)
{
  cs461x_portc *portc = audio_engines[dev]->portc;
  cs461x_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
}

static void
cs461x_audio_reset_output (int dev)
{
  cs461x_portc *portc = audio_engines[dev]->portc;
  cs461x_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
}

/*ARGSUSED*/
static int
cs461x_audio_open (int dev, int mode, int open_flags)
{
  cs461x_portc *portc = audio_engines[dev]->portc;
  cs461x_devc *devc = audio_engines[dev]->devc;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  if (portc->open_mode)
    {
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return OSS_EBUSY;
    }

  if (devc->open_mode & mode)
    {
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return OSS_EBUSY;
    }

  devc->open_mode |= mode;

  portc->open_mode = mode;
  portc->audio_enabled &= ~mode;
#if 0
  if (mode & OPEN_READ)
    {
      dmapin->buffsize = 4096;
      audio_engines[dev]->fixed_rate = 48000;
      audio_engines[dev]->min_rate = 48000;
      audio_engines[dev]->max_rate = 48000;
      audio_engines[dev]->flags |=
	ADEV_FIXEDRATE | ADEV_16BITONLY | ADEV_STEREOONLY;
    }
  if (mode & OPEN_WRITE)
    {
      audio_engines[dev]->min_rate = 5000;
      audio_engines[dev]->max_rate = 48000;
      audio_engines[dev]->flags &=
	~(ADEV_FIXEDRATE | ADEV_16BITONLY | ADEV_STEREOONLY);
      audio_engines[dev]->fixed_rate = 0;
    }
#endif
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  return 0;
}

static void
cs461x_audio_close (int dev, int mode)
{
  cs461x_portc *portc = audio_engines[dev]->portc;
  cs461x_devc *devc = audio_engines[dev]->devc;

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

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

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

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

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

static void
cs461x_audio_trigger (int dev, int state)
{
  oss_native_word flags, tmp;
  cs461x_devc *devc = audio_engines[dev]->devc;
  cs461x_portc *portc = audio_engines[dev]->portc;

  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))
	    {
	      tmp = READ1L (BA1_PCTL);
	      tmp &= 0xFFFF;
	      tmp |= devc->PCTL;
	      WRITE1L (BA1_PCTL, tmp);
	      portc->trigger_bits |= PCM_ENABLE_OUTPUT;
	    }
	}
      else
	{
	  if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
	      (portc->trigger_bits & PCM_ENABLE_OUTPUT))
	    {
	      portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
	      portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
	      tmp = READ1L (BA1_PCTL);
	      tmp &= 0xFFFF;
	      WRITE1L (BA1_PCTL, tmp);
	    }
	}
    }

  if (portc->open_mode & OPEN_READ)
    {
      if (state & PCM_ENABLE_INPUT)
	{
	  if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
	      !(portc->trigger_bits & PCM_ENABLE_INPUT))
	    {
	      tmp = READ1L (BA1_CCTL);
	      tmp &= 0xFFFF0000;
	      tmp |= devc->CCTL;
	      WRITE1L (BA1_CCTL, tmp);
	      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;
	      tmp = READ1L (BA1_CCTL);
	      tmp &= 0xFFFF0000;
	      WRITE1L (BA1_CCTL, tmp);
	    }
	}
    }
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
}

static int
cs461x_play_rate (cs461x_devc * devc, unsigned int ulInRate)
{
/*define GOF_PER_SEC 200*/
  unsigned int ulTemp1, ulTemp2;
  unsigned int ulPhiIncr;
  unsigned int ulCorrectionPerGOF, ulCorrectionPerSec;
  unsigned int ulOutRate = 48000;

  ulTemp1 = ulInRate << 16;
  ulPhiIncr = ulTemp1 / ulOutRate;
  ulTemp1 -= ulPhiIncr * ulOutRate;
  ulTemp1 <<= 10;
  ulPhiIncr <<= 10;
  ulTemp2 = ulTemp1 / ulOutRate;
  ulPhiIncr += ulTemp2;
  ulTemp1 -= ulTemp2 * ulOutRate;
  ulCorrectionPerGOF = ulTemp1 / 200;
  ulTemp1 -= ulCorrectionPerGOF * 200;
  ulCorrectionPerSec = ulTemp1;

  /* Fill in the SampleRateConverter control block. */
  WRITE1L (BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) |
	   (ulCorrectionPerGOF & 0xFFFF));
  WRITE1L (BA1_PPI, ulPhiIncr);
  return 0;
}

static int
cs461x_record_rate (cs461x_devc * devc, int ulOutRate)
{
  unsigned int ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2;
  unsigned int ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay;
  unsigned int ulInRate = 48000;


We can only decimate by up to a factor of 1/9th the hardware rate. Return an error if an attempt is made to stray outside that limit.

  if ((ulOutRate * 9) < ulInRate)
    {
      cmn_err (CE_WARN,
	       "SetCaptureSampleRate(): Requested output rate is below 1/9th of the input rate.\n");
      return (-1);
    }


We can not capture at at rate greater than the Input Rate (48000). Return an error if an attempt is made to stray outside that limit.

  if (ulOutRate > ulInRate)
    {
      cmn_err (CE_WARN,
	       "SetCaptureSampleRate(): Requested output rate is greater than the input rate.");
      return (-1);
    }
  ulTemp1 = ulOutRate << 16;
  ulCoeffIncr = ulTemp1 / ulInRate;
  ulTemp1 -= ulCoeffIncr * ulInRate;
  ulTemp1 <<= 7;
  ulCoeffIncr <<= 7;
  ulCoeffIncr += ulTemp1 / ulInRate;
  ulCoeffIncr ^= 0xFFFFFFFF;
  ulCoeffIncr++;
  ulTemp1 = ulInRate << 16;
  ulPhiIncr = ulTemp1 / ulOutRate;
  ulTemp1 -= ulPhiIncr * ulOutRate;
  ulTemp1 <<= 10;
  ulPhiIncr <<= 10;
  ulTemp2 = ulTemp1 / ulOutRate;
  ulPhiIncr += ulTemp2;
  ulTemp1 -= ulTemp2 * ulOutRate;
  ulCorrectionPerGOF = ulTemp1 / 200;
  ulTemp1 -= ulCorrectionPerGOF * 200;
  ulCorrectionPerSec = ulTemp1;
  ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate;
  /* Fill in the VariDecimate control block. */
  WRITE1L (BA1_CSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) |
	   (ulCorrectionPerGOF & 0xFFFF));
  WRITE1L (BA1_CCI, ulCoeffIncr);
  WRITE1L (BA1_CD,
	   (((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000)
	   | 0x80);
  WRITE1L (BA1_CPI, ulPhiIncr);
#if 0
  {
    unsigned int frameGroupLength, cnt;
    int rate = ulOutRate;

    frameGroupLength = 1;
    for (cnt = 2; cnt <= 64; cnt *= 2)
      {
	if (((rate / cnt) * cnt) != rate)
	  frameGroupLength *= 2;
      }
    if (((rate / 3) * 3) != rate)
      {
	frameGroupLength *= 3;
      }
    for (cnt = 5; cnt <= 125; cnt *= 5)
      {
	if (((rate / cnt) * cnt) != rate)
	  frameGroupLength *= 5;
      }

    WRITE1L (BA1_CFG1, frameGroupLength);
    WRITE1L (BA1_CFG2, (0x00800000 | frameGroupLength));
    WRITE1L (BA1_CCST, 0x0000FFFF);
    WRITE1L (BA1_CSPB, ((65536 * rate) / 24000));
    WRITE1L ((BA1_CSPB + 4), 0x0000FFFF);
  }
#endif
  return (0);
}

struct InitStruct
{
  unsigned long off;
  unsigned long val;
}
InitArray[] =
{
  {
  0x00000040, 0x3fc0000f}
  ,
  {
  0x0000004c, 0x04800000}
  ,
  {
  0x000000b3, 0x00000780}
  ,
  {
  0x000000b7, 0x00000000}
  ,
  {
  0x000000bc, 0x07800000}
  ,
  {
  0x000000cd, 0x00800000}
,};



"SetCaptureSPValues()" -- Initialize record task values before each capture startup.

void
SetCaptureSPValues (cs461x_devc * devc)
{
  unsigned i, offset;
  for (i = 0; i < sizeof (InitArray) / sizeof (struct InitStruct); i++)
    {
      offset = InitArray[i].off * 4;	/* 8bit to 32bit offset value */
      WRITE1L (offset, InitArray[i].val);
    }
}

/*ARGSUSED*/
static int
cs461x_audio_prepare_for_input (int dev, int bsize, int bcount)
{
  cs461x_devc *devc = audio_engines[dev]->devc;
  cs461x_portc *portc = audio_engines[dev]->portc;
  dmap_t *dmap = audio_engines[dev]->dmap_in;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);

  /* Start the processor */
  if (!devc->processor_started)
    cs461x_start_processor (devc);

  /* Set Capture S/P values */
  SetCaptureSPValues (devc);
  /* set the record rate */
  cs461x_record_rate (devc, portc->speed);

  /* write the buffer address */
  WRITE1L (BA1_CBA, dmap->dmabuf_phys);

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

/*ARGSUSED*/
static int
cs461x_audio_prepare_for_output (int dev, int bsize, int bcount)
{
  cs461x_devc *devc = audio_engines[dev]->devc;
  cs461x_portc *portc = audio_engines[dev]->portc;
  dmap_t *dmap = audio_engines[dev]->dmap_out;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  /* Start the processor */
  if (!devc->processor_started)
    cs461x_start_processor (devc);

#ifdef USE_SG
  {
    unsigned int sg_temp[9], sg_npages = 0;
    int i;
    oss_native_word Count, playFormat, tmp, tmp2;

    sg_npages = (dmap->bytes_in_use) / 4096;

    devc->play_sgbuf[0] = dmap->dmabuf_phys;
    devc->play_sgbuf[1] = 0x00000008;

    for (i = 0; i < sg_npages; i++)
      {
	devc->play_sgbuf[2 * i] = dmap->dmabuf_phys + 4096 * i;
	if (i == sg_npages - 1)
	  tmp2 = 0xbfff0000;
	else
	  tmp2 = 0x80000000 + 8 * (i + 1);

	devc->play_sgbuf[2 * i + 1] = tmp2;
      }
    sg_temp[0] = 0x82c0200d;
    sg_temp[1] = 0xffff0000;
    sg_temp[2] = devc->play_sgbuf[0];
    sg_temp[3] = 0x00010600;
    sg_temp[4] = devc->play_sgbuf[2];
    sg_temp[5] = 0x80000010;
    sg_temp[6] = devc->play_sgbuf[0];
    sg_temp[7] = devc->play_sgbuf[2];
    sg_temp[8] = (devc->play_sgbuf_phys & 0xffff000) | 0x10;

    for (i = 0; i < sizeof (sg_temp) / 4; i++)
      WRITE1L ((BA1_PDTC + i * 4), sg_temp[i]);

    WRITE1L (BA1_PBA, dmap->dmabuf_phys);

    cs461x_play_rate (devc, (unsigned int) portc->speed);

    Count = 4;
    playFormat = READ1L (BA1_PFIE);
    playFormat &= ~0x0000f03f;

    if ((portc->channels == 2))
      {
	playFormat &= ~DMA_RQ_C2_AC_MONO_TO_STEREO;
	Count *= 2;
      }
    else
      playFormat |= DMA_RQ_C2_AC_MONO_TO_STEREO;

    if ((portc->bits == 16))
      {
	playFormat &=
	  ~(DMA_RQ_C2_AC_8_TO_16_BIT | DMA_RQ_C2_AC_SIGNED_CONVERT);
	Count *= 2;
      }
    else
      playFormat |= (DMA_RQ_C2_AC_8_TO_16_BIT | DMA_RQ_C2_AC_SIGNED_CONVERT);

    WRITE1L (BA1_PFIE, playFormat);

    tmp = READ1L (BA1_PDTC);
    tmp &= 0xfffffe00;
    WRITE1L (BA1_PDTC, tmp | --Count);
  }
#else
  {
    unsigned int pdtc_value, pfie_value;
    /* Set the sample rate converter */
    cs461x_play_rate (devc, (unsigned int) portc->speed);

    pfie_value = READ1L (BA1_PFIE);
    pfie_value &= ~0x0000f03f;

    pdtc_value = READ1L (BA1_PDTC);
    pdtc_value &= ~0x000003ff;

    /* Now set the sample size/stereo/mono */
    if ((portc->bits == 8) && (portc->channels == 1))
      {
	pdtc_value |= 0x03;
	pfie_value |= 0xB000;	/*8bit mono */
      }
    if ((portc->bits == 8) && (portc->channels == 2))
      {
	pdtc_value |= 0x07;
	pfie_value |= 0xA000;	/*8bit stereo */
      }
    if ((portc->bits == 16) && (portc->channels == 1))
      {
	pdtc_value |= 0x07;
	pfie_value |= 0x2000;	/*16bit mono */
      }
    if ((portc->bits == 16) && (portc->channels == 2))
      {
	pdtc_value |= 0x0F;
	pfie_value |= 0x0000;	/*16bit stereo */
      }

    WRITE1L (BA1_PDTC, pdtc_value);
    WRITE1L (BA1_PFIE, pfie_value);
    WRITE1L (BA1_PBA, dmap->dmabuf_phys);
  }
#endif

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

static int
cs461x_alloc_buffer (int dev, dmap_t * dmap, int direction)
{

#ifdef USE_SG
  int err;
#else
  oss_native_word phaddr;
  cs461x_devc *devc = audio_engines[dev]->devc;
#endif

  if (dmap->dmabuf != NULL)
    return 0;

#ifdef USE_SG
  if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0)
    {
      cmn_err (CE_WARN, "Failed to allocate a DMA buffer\n");
      return err;
    }
#else
  dmap->dmabuf =
    (void *) CONTIG_MALLOC (devc->osdev, 4096, MEMLIMIT_32BITS, &phaddr, dmap->dmabuf_dma_handle);
  dmap->dmabuf_phys = phaddr;
# ifdef linux
  oss_reserve_pages (dmap->dmabuf, dmap->dmabuf + 4096 - 1);
# endif
  dmap->buffsize = 4096;
#endif
  return 0;
}

/*ARGSUSED*/
static int
cs461x_free_buffer (int dev, dmap_t * dmap, int direction)
{
#ifndef USE_SG
  cs461x_devc *devc = audio_engines[dev]->devc;
#endif
#ifdef USE_SG
  oss_free_dmabuf (dev, dmap);
  dmap->dmabuf = 0;
#else
  if (dmap->dmabuf == NULL)
    return 0;
  CONTIG_FREE (devc->osdev, dmap->dmabuf, 4096, dmap->dmabuf_dma_handle);
# ifdef linux
  oss_unreserve_pages (dmap->dmabuf, dmap->dmabuf + 4096 - 1);
# endif
  dmap->dmabuf = 0;
#endif
  return 0;

}

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

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  if (direction == PCM_ENABLE_OUTPUT)
    {
      ptr = READ1L (BA1_PBA) - dmap->dmabuf_phys;
    }
  if (direction == PCM_ENABLE_INPUT)
    {
      ptr = READ1L (BA1_CBA) - dmap->dmabuf_phys;
    }
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return ptr;
}

static audiodrv_t cs461x_audio_driver = {
  cs461x_audio_open,
  cs461x_audio_close,
  cs461x_audio_output_block,
  cs461x_audio_start_input,
  cs461x_audio_ioctl,
  cs461x_audio_prepare_for_input,
  cs461x_audio_prepare_for_output,
  cs461x_audio_reset,
  NULL,
  NULL,
  cs461x_audio_reset_input,
  cs461x_audio_reset_output,
  cs461x_audio_trigger,
  cs461x_audio_set_rate,
  cs461x_audio_set_format,
  cs461x_audio_set_channels,
  NULL,
  NULL,
  NULL,
  NULL,
  cs461x_alloc_buffer,
  cs461x_free_buffer,
  NULL,
  NULL,
  cs461x_get_buffer_pointer
};

/***********************MIDI PORT ROUTINES ****************/

/*ARGSUSED*/
static int
cs461x_midi_open (int dev, int mode, oss_midi_inputbyte_t inputbyte,
		  oss_midi_inputbuf_t inputbuf,
		  oss_midi_outputintr_t outputintr)
{
  cs461x_devc *devc = (cs461x_devc *) midi_devs[dev]->devc;
  oss_native_word flags;

  if (devc->midi_opened)
    {
      return OSS_EBUSY;
    }
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  devc->midi_input_intr = inputbyte;
  devc->midi_opened = mode;

  /* first reset the MIDI port */
  PokeBA0 (devc, BA0_MIDCR, 0x10);
  PokeBA0 (devc, BA0_MIDCR, 0x00);

  /* Now check if we're in Read or Write mode */
  if (mode & OPEN_READ)
    {
      /* enable MIDI Input intr and receive enable */
      PokeBA0 (devc, BA0_MIDCR, MIDCR_RXE | MIDCR_RIE);
    }

  if (mode & OPEN_WRITE)
    {
      /* enable MIDI transmit enable without interrupt mode */
      PokeBA0 (devc, BA0_MIDCR, MIDCR_TXE);
    }
  PokeBA0 (devc, BA0_HICR, HICR_IEV | HICR_CHGM);
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  return 0;
}

/*ARGSUSED*/
static void
cs461x_midi_close (int dev, int mode)
{
  cs461x_devc *devc = (cs461x_devc *) midi_devs[dev]->devc;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
/* Reset the device*/
  PokeBA0 (devc, BA0_MIDCR, 0x10);
  PokeBA0 (devc, BA0_MIDCR, 0x00);
  devc->midi_opened = 0;
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
}

static int
cs461x_midi_out (int dev, unsigned char midi_byte)
{
  cs461x_devc *devc = (cs461x_devc *) midi_devs[dev]->devc;
  unsigned char uart_stat;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  uart_stat = PeekBA0 (devc, BA0_MIDSR);

/* Check if Transmit buffer full flag is set - if so return */
  if ((uart_stat & MIDSR_TBF))
    {
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return 0;
    }
/* Now put the MIDI databyte in the write port */
  PokeBA0 (devc, BA0_MIDWP, midi_byte);
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  return 1;
}

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

static midi_driver_t cs461x_midi_driver = {
  cs461x_midi_open,
  cs461x_midi_close,
  cs461x_midi_ioctl,
  cs461x_midi_out
};

static int
init_cs461x (cs461x_devc * devc)
{

  int my_mixer, my_mixer2, i;
  unsigned int ulCount = 0;
  unsigned int ulStatus = 0;
  unsigned int tmp;
  int first_dev = 0;
  int adev;
  oss_native_word phaddr;

/****************BEGIN HARDWARE INIT*****************/

First, blast the clock control register to zero so that the PLL starts out in a known state, and blast the master serial port control register to zero so that the serial ports also start out in a known state.

  PokeBA0 (devc, BA0_CLKCR1, 0x0);
  PokeBA0 (devc, BA0_SERMC1, 0x0);
  oss_udelay (1000);

If we are in AC97 mode, then we must set the part to a host controlled AC-link. Otherwise, we won't be able to bring up the link.

  if (devc->dual_codec)
    PokeBA0 (devc, BA0_SERACC,
	     SERACC_HSP | SERACC_TWO_CODECS | SERACC_CODEC_TYPE_2_0);
  else
    PokeBA0 (devc, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03);	/* AC97 1.03 */


Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 spec) and then drive it high. This is done for non AC97 modes since there might be logic external to the CS461x that uses the ARST# line for a reset.

  PokeBA0 (devc, BA0_ACCTL, 1);
  oss_udelay (10000);
  PokeBA0 (devc, BA0_ACCTL, 0);
  oss_udelay (10000);
  PokeBA0 (devc, BA0_ACCTL, ACCTL_RSTN);

The first thing we do here is to enable sync generation. As soon as we start receiving bit clock, we'll start producing the SYNC signal.

  PokeBA0 (devc, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN);


Now wait for a short while to allow the AC97 part to start generating bit clock (so we don't try to start the PLL without an input clock).

  oss_udelay (500000);

Set the serial port timing configuration, so that the clock control circuit gets its clock from the correct place.

  PokeBA0 (devc, BA0_SERMC1, SERMC1_PTC_AC97);
  oss_udelay (500000);

Write the selected clock control setup to the hardware. Do not turn on SWCE yet (if requested), so that the devices clocked by the output of PLL are not clocked until the PLL is stable.

  PokeBA0 (devc, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ);
  PokeBA0 (devc, BA0_PLLM, 0x3a);
  PokeBA0 (devc, BA0_CLKCR2, CLKCR2_PDIVS_8);


Power up the PLL.

  PokeBA0 (devc, BA0_CLKCR1, CLKCR1_PLLP);


Wait until the PLL has stabilized.

  oss_udelay (100000);

Turn on clocking of the core so that we can setup the serial ports.

  tmp = PeekBA0 (devc, BA0_CLKCR1) | CLKCR1_SWCE;
  PokeBA0 (devc, BA0_CLKCR1, tmp);

  /* Clear the FIFOs */
  clear_serial_fifo (devc);
  /* PokeBA0 (devc, BA0_SERBSP, 0); */


Write the serial port configuration to the part. The master enable bit is not set until all other values have been written.

  PokeBA0 (devc, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN);
  PokeBA0 (devc, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
  PokeBA0 (devc, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);

  PokeBA0 (devc, BA0_SERC7, SERC7_ASDI2EN);
  PokeBA0 (devc, BA0_SERC3, 0);
  PokeBA0 (devc, BA0_SERC4, 0);
  PokeBA0 (devc, BA0_SERC5, 0);
  PokeBA0 (devc, BA0_SERC6, 0);

  oss_udelay (100000);


  /* Wait for the codec ready signal from the AC97 codec. */
  for (ulCount = 0; ulCount < 1000; ulCount++)
    {

First, lets wait a short while to let things settle out a bit, and to prevent retrying the read too quickly.

      oss_udelay (10000);

Read the AC97 status register to see if we've seen a CODEC READY signal from the AC97 codec.

      ulStatus = PeekBA0 (devc, BA0_ACSTS);
      if (ulStatus & ACSTS_CRDY)
	break;
    }

  if (!(ulStatus & ACSTS_CRDY))
    {
      cmn_err (CE_WARN, "Initialize() Never read Codec Ready from AC97.\n");
      return 0;
    }

  if (devc->dual_codec)
    {
      for (ulCount = 0; ulCount < 1000; ulCount++)
	{

First, lets wait a short while to let things settle out a bit, and to prevent retrying the read too quickly.

	  oss_udelay (10000);

Read the AC97 status register to see if we've seen a CODEC READY signal from the AC97 codec.

	  ulStatus = PeekBA0 (devc, BA0_ACSTS2);
	  if (ulStatus & ACSTS_CRDY)
	    break;
	}

      if (!(ulStatus & ACSTS_CRDY))
	{
	  cmn_err (CE_WARN,
		   "Initialize() Never read Codec Ready from second AC97.\n");
	}
    }

Assert the vaid frame signal so that we can start sending commands to the AC97 codec.

  PokeBA0 (devc, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);

Wait until we've sampled input slots 3 and 4 as valid, meaning that the codec is pumping ADC data across the AC-link.

  for (ulCount = 0; ulCount < 1000; ulCount++)
    {

First, lets wait a short while to let things settle out a bit, and to prevent retrying the read too quickly.

      oss_udelay (10000);

Read the input slot valid register and see if input slots 3 and 4 are valid yet.

      ulStatus = PeekBA0 (devc, BA0_ACISV);
      if ((ulStatus & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
	break;
    }

Make sure we sampled valid input slots 3 and 4. If not, then return an error.

  if ((ulStatus & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4))
    cmn_err (CE_WARN, "Initialize(%x) Never sampled ISV3 & ISV4 from AC97.\n",
	     (int) ulStatus);

Now, assert valid frame and the slot 3 and 4 valid bits. This will commense the transfer of digital audio data to the AC97 codec.

  PokeBA0 (devc, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);

  /* Reset the Processor */
  cs461x_reset_processor (devc);
  /* Now download the image */
  install_ucode (devc);

  devc->processor_started = 0;

  /* Save the play and capture parameters and write 0 to stop DMA */
  devc->PCTL = READ1L (BA1_PCTL) & 0xFFFF0000;
  WRITE1L (BA1_PCTL, devc->PCTL & 0x0000FFFF);
  devc->CCTL = READ1L (BA1_CCTL) & 0x0000FFFF;
  WRITE1L (BA1_CCTL, devc->CCTL & 0xFFFF0000);


Enable interrupts on the part.

  PokeBA0 (devc, BA0_HICR, HICR_IEV | HICR_CHGM);

  ulStatus = READ1L (BA1_PFIE);
  ulStatus &= ~0x0000f03f;
  WRITE1L (BA1_PFIE, ulStatus);	/* playback interrupt enable */

  ulStatus = READ1L (BA1_CIE);
  ulStatus &= ~0x0000003f;
  ulStatus |= 0x00000001;
  WRITE1L (BA1_CIE, ulStatus);	/* capture interrupt enable */

/****** END OF HARDWARE INIT *****/

  my_mixer =
    ac97_install (&devc->ac97devc, "CS461x AC97 Mixer", ac97_read, ac97_write,
		  devc, devc->osdev);

  if (my_mixer >= 0)
    {
      devc->mixer_dev = my_mixer;
      if (devc->subsysid == 0x5053)	/* SantaCruz has dual codec */
	{
	  my_mixer2 =
	    ac97_install (&devc->ac97devc2, "CS4630 AC97 Secondary",
			  ac97_read2, ac97_write2, devc, devc->osdev);
	  if (my_mixer2 >= 0)
	    {
	      devc->mixer2_dev = my_mixer2;
	      ac97_write2 (devc, BA0_AC97_6CH_VOL_C_LFE, 0x7FFF);
	      ac97_write2 (devc, BA0_AC97_6CH_VOL_SURROUND, 0x7FFF);
	    }
	}
    }
  else
    return 0;

  /* Turn on amp on the CS4297 on TB/Videologic */
  if (devc->subsysid == 0x5053)
    {
      unsigned short u16PinConfig, u16LogicType, u16ValidSlots, u16Idx;
      unsigned short u16Status = 0;
      int i;


      ac97_write (devc, 0x26, ac97_read (devc, 0x26) | 0x8000);
#if 0
      /* Set SPDIF */
      PokeBA0 (devc, BA0_ASER_MASTER, 1);	/*ASER_MASTER_ME */
      WRITE1L (0x8049, 0x00000001);
#endif

Set GPIO pin's 7 and 8 so that they are configured for output.

      u16PinConfig = ac97_read2 (devc, BA0_AC97_GPIO_PIN_CONFIG);
      u16PinConfig &= 0x27F;
      ac97_write2 (devc, BA0_AC97_GPIO_PIN_CONFIG, u16PinConfig);


Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic.

      u16LogicType = ac97_read2 (devc, BA0_AC97_GPIO_PIN_TYPE);
      u16LogicType &= 0x27F;
      ac97_write2 (devc, BA0_AC97_GPIO_PIN_TYPE, u16LogicType);

      u16ValidSlots = PeekBA0 (devc, BA0_ACOSV);
      u16ValidSlots |= 0x200;
      PokeBA0 (devc, BA0_ACOSV, u16ValidSlots);


Fill slots 12 with the correct value for the GPIO pins.

      for (u16Idx = 0x90; u16Idx <= 0x9F; u16Idx++)
	{


Initialize the fifo so that bits 7 and 8 are on.

Remember that the GPIO pins in bonzo are shifted by 4 bits to the left. 0x1800 corresponds to bits 7 and 8.

	  PokeBA0 (devc, BA0_SERBWP, 0x1800);


Make sure the previous FIFO write operation has completed.

	  for (i = 0; i < 5; i++)
	    {
	      u16Status = PeekBA0 (devc, BA0_SERBST);
	      if (!(u16Status & SERBST_WBSY))
		{
		  break;
		}
	      oss_udelay (10000);
	    }


Write the serial port FIFO index.

	  PokeBA0 (devc, BA0_SERBAD, u16Idx);


Tell the serial port to load the new value into the FIFO location.

	  PokeBA0 (devc, BA0_SERBCM, SERBCM_WRC);
	}
    }
  /* Hercules Game Theater Amp */
  if (devc->subsysid == 0x1681)
    {
      PokeBA0 (devc, BA0_EGPIODR, EGPIODR_GPOE2);	/* enable EGPIO2 output */
      PokeBA0 (devc, BA0_EGPIOPTR, EGPIOPTR_GPPT2);	/* open-drain on output */

    }

#ifdef USE_SG
  devc->play_sgbuf =
    (unsigned int *) CONTIG_MALLOC (devc->osdev, 4096, MEMLIMIT_32BITS,
				    &phaddr, devc->play_sgbuf_dma_handle);
  if (devc->play_sgbuf == NULL)
    {
      cmn_err (CE_WARN,
	       "cs461x: Failed to allocate play scatter/gather buffer.\n");
      return 0;
    }
  devc->play_sgbuf_phys = phaddr;
#endif

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

      if (i == 0)
	{
	  strcpy (tmp_name, devc->chip_name);
	  caps |= ADEV_DUPLEX;
	}
      else
	{
	  strcpy (tmp_name, devc->chip_name);
	  caps |= ADEV_DUPLEX | ADEV_SHADOW;
	}

      if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
					devc->osdev,
					devc->osdev,
					tmp_name,
					&cs461x_audio_driver,
					sizeof (audiodrv_t),
					caps,
					AFMT_S16_LE | AFMT_U8, devc, -1)) < 0)
	{
	  adev = -1;
	  return 0;
	}
      else
	{
	  if (i == 0)
	    first_dev = adev;
	  audio_engines[adev]->portc = portc;
	  audio_engines[adev]->mixer_dev = my_mixer;
	  audio_engines[adev]->rate_source = first_dev;
	  audio_engines[adev]->min_rate = 5000;
	  audio_engines[adev]->max_rate = 48000;
	  audio_engines[adev]->min_block = 4096;
	  audio_engines[adev]->max_block = 4096;
	  audio_engines[adev]->caps |= PCM_CAP_FREERATE;
	  portc->open_mode = 0;
	  portc->audiodev = adev;
	  portc->audio_enabled = 0;
	  portc->trigger_bits = 0;
#ifdef CONFIG_OSS_VMIX
	  if (i == 0)
	     vmix_attach_audiodev(devc->osdev, adev, -1, 0);
#endif
	}

    }

#ifndef sparc
  devc->midi_dev =
    oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "CS461x",
			 "CS461x MIDI Port", &cs461x_midi_driver,
			 sizeof (midi_driver_t),
			 0, devc, devc->osdev);
#endif
  devc->midi_opened = 0;
  return 1;
}

int
oss_cs461x_attach (oss_device_t * osdev)
{
  unsigned char pci_irq_line, pci_revision, pci_irq_inta;
  unsigned short pci_command, vendor, device;
  unsigned int ioaddr;
  int err;
  cs461x_devc *devc;

  DDB (cmn_err (CE_WARN, "Entered CS461x probe routine\n"));
#if 0
  if (cs461x_clkrun_fix)
    while ((osdev = (pci_find_class (0x680 << 8, osdev))))

      {
	unsigned char pp;
	unsigned int port, control;
	unsigned short vendor, device;

	pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
	pci_read_config_word (osdev, PCI_DEVICE_ID, &device);

	if (vendor != 0x8086 || device != 0x7113)
	  continue;

	pci_read_config_byte (osdev, 0x41, &pp);
	port = pp << 8;
	control = INW (devc->osdev, port + 0x10);
	OUTW (devc->osdev, control | 0x2000, port + 0x10);
	oss_udelay (100);
	OUTW (devc->osdev, control & ~0x2000, port + 0x10);
      }
#endif

  pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
  pci_read_config_word (osdev, PCI_DEVICE_ID, &device);

  if (vendor != CRYSTAL_VENDOR_ID || device != CRYSTAL_CS461x_ID)
    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->open_mode = 0;

  oss_pci_byteswap (osdev, 1);

  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_byte (osdev, PCI_INTERRUPT_LINE + 1, &pci_irq_inta);
  pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &ioaddr);
  devc->bar0addr = ioaddr;
  pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_1, &ioaddr);
  devc->bar1addr = ioaddr;
  pci_read_config_word (osdev, PCI_SUBSYSTEM_VENDOR_ID, &devc->subsysid);

  switch (device)
    {
    case CRYSTAL_CS461x_ID:
      devc->chip_name = "Crystal CS461x";
      break;

    case CRYSTAL_CS4610_ID:
      devc->chip_name = "Crystal CS4610";
      break;

    case CRYSTAL_CS4615_ID:
      devc->chip_name = "Crystal CS4615";
      break;

    default:
      devc->chip_name = "CrystalPCI";
    }

  if (devc->subsysid == 0x5053)
    {
      devc->chip_name = "Crystal CS4630";
      devc->dual_codec = 1;
    }

  /* activate the device enable bus master/memory space */
  pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
  pci_write_config_word (osdev, PCI_COMMAND, pci_command);

  if ((devc->bar0addr == 0) || (devc->bar1addr == 0))
    {
      cmn_err (CE_WARN, "Undefined MEMORY I/O address.\n");
      return 0;
    }

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

  /* Map the shared memory area */
  devc->bar0virt =
    (unsigned int *) MAP_PCI_MEM (devc->osdev, 0, devc->bar0addr, 0x2000);
  devc->bar1virt =
    (unsigned int *) MAP_PCI_MEM (devc->osdev, 1, devc->bar1addr, 0x40000);
  devc->dwRegister0 = (unsigned int *) devc->bar0virt;
  devc->wRegister0 = (unsigned short *) devc->bar0virt;
  devc->bRegister0 = (unsigned char *) devc->bar0virt;
  devc->dwRegister1 = (unsigned int *) devc->bar1virt;
  devc->wRegister1 = (unsigned short *) devc->bar1virt;
  devc->bRegister1 = (unsigned char *) devc->bar1virt;

  devc->irq = pci_irq_line;

  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, cs461xintr, NULL)) < 0)
    {
      cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err);
      return 0;
    }

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


int
oss_cs461x_detach (oss_device_t * osdev)
{
  cs461x_devc *devc = (cs461x_devc *) osdev->devc;


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

  PokeBA0 (devc, BA0_HICR, 0x02);	/*enable intena */
  cs461x_reset_processor (devc);
#ifdef USE_SG
  CONTIG_FREE (devc->osdev, devc->play_sgbuf, 4096, devc->play_sgbuf_dma_handle);
#endif

  oss_unregister_interrupts (devc->osdev);

  MUTEX_CLEANUP (devc->mutex);
  MUTEX_CLEANUP (devc->low_mutex);

  UNMAP_PCI_MEM (devc->osdev, 0, devc->bar0addr, devc->bar0virt, 0x2000);
  UNMAP_PCI_MEM (devc->osdev, 1, devc->bar1addr, devc->bar1virt, 0x40000);

  devc->bar0addr = 0;
  devc->bar1addr = 0;

  oss_unregister_device (devc->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