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_envy24ht/envy24ht_ap192.c

Low level routines for M Audio Audiophile 192

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_envy24ht_cfg.h"

#include "spdif.h"
#include "envy24ht.h"

static char channel_names[4][10] = {
  "main",
  "unused1",
  "unused2",
  "unused3"
};

#define BIT(x) (1<<(x))
#define BIT3 BIT(3)

/*----- SPI bus for CODEC communication. */
/* */
#define SPI_CLK 		1	/* Clock output to CODEC's, rising edge clocks data. */
#define SPI_DIN 		2	/* Data input from the CODEC. */
#define SPI_DOUT 		3	/* Data output to the CODEC. */
#define SPI_CS0n 		(1<<4)	/* Selects first chip. */
#define SPI_CS1n 		(1<<5)	/* Selects second chip. */

#define SPI_CC_AK4358 		0x02	/* C1:C0 for ak4358. */
#define SPI_CC_AK4114 		0x02	/* C1:C0 for ak4114. */
#define WRITEMASK 		0xffff
/*----- Revolution defines. */
/* */
#define ap192_AK4114 (1)	/* iDevice value for AK4114 DIR. */
#define ap192_AK4358 (2)	/* iDevice value for AK4358 D/A. */

static unsigned int
GpioReadAll (envy24ht_devc * devc)
{
  return INW (devc->osdev, devc->ccs_base + 0x14);
}

static void
GPIOWriteAll (envy24ht_devc * devc, unsigned int data)
{
  OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x18);	/* GPIO direction */
  OUTW (devc->osdev, 0x0000, devc->ccs_base + 0x16);	/* GPIO write mask */

  OUTW (devc->osdev, data, devc->ccs_base + 0x14);
}

static void
GPIOWrite (envy24ht_devc * devc, int pos, int bit)
{
  int data = GpioReadAll (devc);

  bit = (bit != 0);

  data &= ~(1 << pos);
  data |= (bit << pos);

  GPIOWriteAll (devc, data);
}

void
ap192_Assert_CS (envy24ht_devc * devc, int iDevice)


Assert chip select to specified GPIO-connected device. iDevice: ap192_AK4114=DIG, ap192_AK4358=DAC.


{
  unsigned int dwGPIO;		/* Current GPIO's. */
  dwGPIO = GpioReadAll (devc);	/* Read current GPIO's. */
  dwGPIO |= (SPI_CS0n | SPI_CS1n);	/* Reset CS bits. */
  switch (iDevice)		/* Select CS#. */
    {
    case ap192_AK4358:
      dwGPIO &= ~SPI_CS0n;
      break;			/* DAC */
    case ap192_AK4114:
      dwGPIO &= ~SPI_CS1n;
      break;			/* DIG */
    default:
      break;
    }
  GPIOWriteAll (devc, dwGPIO);	/* Write hardware. */
}

void
ap192_DeAssert_CS (envy24ht_devc * devc)


De-Assert all chip selects.


{
  unsigned int dwGPIO = GpioReadAll (devc);	/* Current GPIO's. */
  dwGPIO |= (SPI_CS0n | SPI_CS1n);	/* Clear CS bits. */
  GPIOWriteAll (devc, dwGPIO);	/* Write back to hardware. */
}

/*#define _delay()	oss_udelay(1) */
#define _delay()	{}

void
ap192_WriteSpiAddr (envy24ht_devc * devc, int iDevice, unsigned char bReg)


Write the address byte part of the SPI serial stream. iDevice: ap192_AK4358=DAC, ap192_AK4114=DIG, etc.


{
  unsigned char bHdr;
  unsigned char bNum;
/* Built 8-bit packet header: C1,C0,R/W,A4,A3,A2,A1,A0. */
/* */
  switch (iDevice)
    {
    case ap192_AK4358:
      bHdr = SPI_CC_AK4358 << 6;
      break;
    case ap192_AK4114:
      bHdr = SPI_CC_AK4114 << 6;
      break;
    default:
      bHdr = 0;
      break;
    }
  bHdr = bHdr | 0x20 | (bReg & 0x1F);	/* "write" + address. */
/* Write header to SPI. */
/* */
  for (bNum = 0; bNum < 8; bNum++)
    {
      GPIOWrite (devc, SPI_CLK, 0);	/* Drop clock low. */
      _delay ();
      GPIOWrite (devc, SPI_DOUT, 0x080 & bHdr);	/* Write data bit. */
      _delay ();
      GPIOWrite (devc, SPI_CLK, 1);	/* Raise clock. */
      _delay ();
      bHdr <<= 1;		/* Next bit. */
    }
}

void
ap192_WriteSpiReg (envy24ht_devc * devc, int iDevice, unsigned char bReg,
		   unsigned char bData)


Writes one register in specified CHIP. devc = PCI slot code of specific board. iDevice: ap192_AK4358=DAC, ap192_AK4114=DIG, etc.


{
  unsigned char bNum;
  GPIOWrite (devc, SPI_DOUT, 0);	/* Init SPI signals. */
  GPIOWrite (devc, SPI_CLK, 1);	/* */
/* Drop the chip select low. */
/* Wait at least 150 nS. */
/* */
  ap192_Assert_CS (devc, iDevice);
  _delay ();
/* Write the address byte. */
/* */
  ap192_WriteSpiAddr (devc, iDevice, bReg);
/* Write the data byte. */
/* */
  for (bNum = 0; bNum < 8; bNum++)
    {
      GPIOWrite (devc, SPI_CLK, 0);	/* Drop clock low. */
      _delay ();
      GPIOWrite (devc, SPI_DOUT, 0x080 & bData);	/* Write data bit. */
      _delay ();
      GPIOWrite (devc, SPI_CLK, 1);	/* Raise clock. */
      _delay ();
      bData <<= 1;		/* Next bit. */
    }
/* De-assert chip selects. */
/* */
  ap192_DeAssert_CS (devc);
  _delay ();
}


#define GPIO_MUTEn 22		/* Converter mute signal. */
void
ap192_Mute (envy24ht_devc * devc, int bMute)


Mutes all outputs if bMute=TRUE.


{
  if (bMute)
    {
/* Soft-mute. Delay currently unspecified, try ½ second. */
      ap192_WriteSpiReg (devc, ap192_AK4358, 1, 0x03);
      _delay ();
/* Switch mute transistors on. */
      GPIOWrite (devc, GPIO_MUTEn, 0);
    }
  else
    {
/* Switch mute transistors off. Delay currently unspecified, try ½ second. */
      GPIOWrite (devc, GPIO_MUTEn, 1);
      _delay ();
/* Release Soft-mute. */
      ap192_WriteSpiReg (devc, ap192_AK4358, 1, 0x01);
    }

  devc->mute = bMute;
}


void
ap192_Set_OutAttn (envy24ht_devc * devc, unsigned char bChan, int iAttn)


Sets the attenuation on one output channel. bChan = Channel number (0..7). Channel 0:1 = front, 2:3 = center/sub, 4:5 = rear, 6:7 = headphones. Registers are 0x04, 05, 06, 07, 08, 09, 0B, 0C respectively iAttn = Number of steps to attenuate CODEC. Each step equals .5dB (-127..0)


{
  unsigned char bIndex;
  unsigned char bAttn;
  if (bChan > 7 || iAttn > 0 || iAttn < -127)	/* parameter test */
    {
      cmn_err (CE_CONT, "Dnvalid data! %d=bChan, %d=iAttn", bChan, iAttn);
      return;
    }
  if (bChan < 6)
    bIndex = 0x04 + bChan;	/* for registers 0x04..0x09 */
  else
    bIndex = 0x05 + bChan;	/* for registers 0x0B..0x0C */
  bAttn = (0x80 + (unsigned char) (iAttn + 127));	/* 7F is max volume. */
/* MSB enables attenuation. */
  ap192_WriteSpiReg (devc, ap192_AK4358, bIndex, bAttn);
}

static void
ap192_Set_48K_Mode (envy24ht_devc * devc)


Sets Chip and Envy24 for 8kHz-48kHz sample rates.


{
/* ICE MCLK = 256x. */
  OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) & ~BIT3,
	devc->mt_base + 2);
/* DFS=normal, RESET. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4E);
/* DFS=normal, NORMAL OPERATION. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4F);

  /* Set ADC modes */
  GPIOWrite (devc, 8, 0);	/* CKS0 = 0. MCLK = 256x */
  GPIOWrite (devc, 9, 0);	/* DFS0 = 0. */
  GPIOWrite (devc, 10, 0);	/* DFS1 = 0. Single speed mode. */

  /* Reset ADC timing */
  GPIOWrite (devc, 11, 0);
  _delay ();
  GPIOWrite (devc, 11, 1);
}

static void
ap192_Set_96K_Mode (envy24ht_devc * devc)


Sets CODEC and Envy24 for 60kHz-96kHz sample rates.


{
/* ICE MCLK = 256x. */
  OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) & ~BIT3,
	devc->mt_base + 2);
/* DFS=double-speed, RESET. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x5E);
/* DFS=double-speed, NORMAL OPERATION. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x5F);

  /* Set ADC modes */
  GPIOWrite (devc, 8, 0);	/* CKS0 = 0. MCLK = 256x */
  GPIOWrite (devc, 9, 1);	/* DFS0 = 0. */
  GPIOWrite (devc, 10, 0);	/* DFS1 = 0. Single speed mode. */

  /* Reset ADC timing */
  GPIOWrite (devc, 11, 0);
  _delay ();
  GPIOWrite (devc, 11, 1);
}

static void
ap192_Set_192K_Mode (envy24ht_devc * devc)


Sets CODEC and Envy24 for 120kHz-192kHz sample rate.


{
/* ICE MCLK = 128x. */
  OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) | BIT3,
	devc->mt_base + 2);
  _delay ();
/*----- SET THE D/A. */
/* DFS=quad-speed, RESET. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x6E);
  _delay ();
/* DFS=quad-speed, NORMAL OPERATION. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x6F);

  /* SPDIF */
  ap192_WriteSpiReg (devc, ap192_AK4114, 0x00, 0x0d);
  ap192_WriteSpiReg (devc, ap192_AK4114, 0x00, 0x0f);

  /* Set ADC modes */
  GPIOWrite (devc, 8, 1);	/* CKS0 = 0. MCLK = 256x */
  GPIOWrite (devc, 9, 0);	/* DFS0 = 0. */
  GPIOWrite (devc, 10, 1);	/* DFS1 = 0. Single speed mode. */

  /* Reset ADC timing */
  GPIOWrite (devc, 11, 0);
  _delay ();
  GPIOWrite (devc, 11, 1);
}

static int
set_dac (envy24ht_devc * devc, int reg, int level)
{
  if (level < 0)
    level = 0;
  if (level > 0x7f)
    level = 0x7f;

  ap192_WriteSpiReg (devc, ap192_AK4358, reg, level | 0x80);

  return level;
}

static void
AK4358_Init (envy24ht_devc * devc)
{
/*===== AK4358 D/A initialization. Leave soft-muted. */
/* */
/* Power down, reset, normal mode. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x00);
/* Power up, reset, normal mode */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4E);
/* Reset timing, Mode 3(I2S), disable auto clock detect, sharp roll off. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 0, 0x06);
/* Soft mute, reset timing. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 1, 0x02);
/* De-emphasis off. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 3, 0x01);
/* Max volume on all 8 channels. */
  set_dac (devc, 0x04, 0x7f);
  set_dac (devc, 0x05, 0x7f);
  set_dac (devc, 0x06, 0x7f);
  set_dac (devc, 0x07, 0x7f);
  set_dac (devc, 0x08, 0x7f);
  set_dac (devc, 0x09, 0x7f);
  set_dac (devc, 0x0b, 0x7f);
  set_dac (devc, 0x0c, 0x7f);

/* Datt mode 0, DZF non-invert, DCLK polarity 0, PCM mode, DCKS 512fs, TDM normal. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 0xA, 0x00);
/* DZF control disabled. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 0xD, 0x00);
  ap192_WriteSpiReg (devc, ap192_AK4358, 0xE, 0x00);
  ap192_WriteSpiReg (devc, ap192_AK4358, 0xF, 0x00);
/* Power up, normal operation. */
  ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4F);
}

static void
ap192_set_rate (envy24ht_devc * devc)
{
  int tmp;

  tmp = INB (devc->osdev, devc->mt_base + 0x02);
  if (devc->speed <= 48000)
    {
      ap192_Set_48K_Mode (devc);
      OUTB (devc->osdev, tmp & ~BIT (3), devc->mt_base + 0x02);
      return;
    }

  if (devc->speed <= 96000)
    {
      ap192_Set_96K_Mode (devc);

      return;
    }

  ap192_Set_192K_Mode (devc);
  OUTB (devc->osdev, tmp | BIT (3), devc->mt_base + 0x02);
}

static int
ap192_audio_ioctl (envy24ht_devc * devc, envy24ht_portc * portc, int cmd,
		   int *arg)
{
  int left, right, value;

  switch (cmd)
    {
    case SNDCTL_DSP_GETPLAYVOL:
      if (portc != &devc->play_portc[0])
	return OSS_EINVAL;
      left = (devc->gains[0] & 0xff) * 100 / 0x7f;
      right = ((devc->gains[0] >> 8) & 0xff) * 100 / 0x7f;
      return *arg = (left | (right << 8));
      break;

    case SNDCTL_DSP_SETPLAYVOL:
      if (portc != &devc->play_portc[0])
	return OSS_EINVAL;
      value = *arg;
      left = value & 0xff;
      right = (value >> 8) & 0xff;

      left = (left * 0x7f) / 100;
      right = (right * 0x7f) / 100;
      left = set_dac (devc, 0x04, left);
      right = set_dac (devc, 0x05, right);
      devc->gains[0] = left | (right << 8);
      mixer_devs[devc->mixer_dev]->modify_counter++;
      return 0;
      break;
    }
  return OSS_EINVAL;
}

static int
ap192_set_control (int dev, int ctrl, unsigned int cmd, int value)
{
  envy24ht_devc *devc = mixer_devs[dev]->hw_devc;

  if (cmd == SNDCTL_MIX_READ)
    switch (ctrl)
      {
      case 0:
	return devc->mute;

      default:
	return OSS_EINVAL;
      }

  if (cmd == SNDCTL_MIX_WRITE)
    switch (ctrl)
      {
      case 0:
	value = !!value;
	ap192_Mute (devc, value);
	return devc->mute;

      default:
	return OSS_EINVAL;
      }

  return OSS_EINVAL;
}

static int
ap192_set_ak4358 (int dev, int ctrl, unsigned int cmd, int value)
{
  envy24ht_devc *devc = mixer_devs[dev]->hw_devc;

  if (cmd == SNDCTL_MIX_READ)
    {
      if (ctrl < 0 || ctrl > 4)
	return OSS_EIO;

      return devc->gains[ctrl];
    }

  if (cmd == SNDCTL_MIX_WRITE)
    {
      int left, right;

      left = value & 0xff;
      right = (value >> 8) & 0xff;

      switch (ctrl)
	{
	case 0:		/* PCM */
	  left = set_dac (devc, 0x04, left);
	  right = set_dac (devc, 0x05, right);
	  break;

	case 1:		/* Line IN */
	  /* Line IN monitor permits panning but we don't support it */
	  left = set_dac (devc, 0x06, left);
	  set_dac (devc, 0x07, 0);
	  set_dac (devc, 0x08, 0);
	  right = set_dac (devc, 0x09, right);
	  break;

	case 2:		/* S/PDIF */
	  left = set_dac (devc, 0x0b, left);
	  left = set_dac (devc, 0x0c, right);
	  break;

	default:
	  return OSS_EINVAL;
	}

      value = left | (right << 8);
      return devc->gains[ctrl] = value;
    }

  return OSS_EINVAL;
}

#if 0
/*ARGSUSED*/ 
static int
set_mongain (envy24ht_devc * devc, int reg, int value)
{
  if (value < 0)
    value = 0;
  if (value > 79)
    value = 79;

  return value;
}
#endif

/*ARGSUSED*/ 
static int
ap192_mixer_init (envy24ht_devc * devc, int dev, int g)
{
  int group = g;
  int err;

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

  if ((err = mixer_ext_create_control (dev, group,
				       0, ap192_set_control,
				       MIXT_ONOFF,
				       "ENVY24_MUTE", 2,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       0, ap192_set_ak4358,
				       MIXT_STEREOSLIDER,
				       "ENVY24_PCM", 0x7f,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       1, ap192_set_ak4358,
				       MIXT_STEREOSLIDER,
				       "ENVY24_IN", 0x7f,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  if ((err = mixer_ext_create_control (dev, group,
				       2, ap192_set_ak4358,
				       MIXT_STEREOSLIDER,
				       "ENVY24_SPDIF", 0x7f,
				       MIXF_READABLE | MIXF_WRITEABLE)) < 0)
    return err;

  return 0;
}

static void
ap192_card_init (envy24ht_devc * devc)
{

  int i;

#if 1
  OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x18);	/* GPIO direction */
  OUTW (devc->osdev, 0x0000, devc->ccs_base + 0x16);	/* GPIO write mask */
  OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x14);	/* Initial bit state */

  OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1a);	/* GPIO direction for bits 16:22 */
  OUTB (devc->osdev, 0x00, devc->ccs_base + 0x1f);	/* GPIO mask for bits 16:22 */
  OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1e);	/* GPIO data for bits 16:22 */
#endif

  memcpy (devc->channel_names, channel_names, sizeof (channel_names));
  AK4358_Init (devc);
  ap192_Set_48K_Mode (devc);

  for (i = 0; i < 5; i++)
    devc->gains[i] = 0x7f7f;

  ap192_Mute (devc, 0);

  GPIOWrite (devc, 5, 0);	/* Select S/PDIF output mux */
  GPIOWrite (devc, 5, 0);	/* Select S/PDIF output mux */

}

envy24ht_auxdrv_t envy24ht_ap192_auxdrv = {
  ap192_card_init,
  ap192_mixer_init,
  ap192_set_rate,
  NULL,
  NULL,
  NULL,				/* ap192_private1 */
  ap192_audio_ioctl
};

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