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_envy24/envy24_6fire.c

Card specific routines for Terratec DMX6fire.

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_config.h"
#include "ac97.h"
#include "envy24.h"

#if 0
# define PRT_STATUS(v) outb(v&0xff, 0x378)
#else
# define PRT_STATUS(v)
#endif

#define DEFAULT_FRONTBOX_SETUP	0xfd

extern int envy24_gain_sliders;
extern int envy24_virtualout;

#define EWX_DIG_ADDR 0x24	/* IIC address of digital traceiver chip of 6fire */


Function : IIC_GetSDA ------------------------------------------------------------------------------- Description : Returns : unsigned char -> Parameters : unsigned long dwPortAddr -> ------------------------------------------------------------------------------- Notes : =============================================================================

static unsigned char
IIC_GetSDA (envy24_devc * devc)
{

  unsigned char bReg, bSDA;

  /* FMB TEST: RW line */
  /* set write mask */
  bReg = ~(0x08);		/* writeable */
  envy24_write_cci (devc, 0x21, bReg);

  /* set RW line LOW */
  bReg = 0;			/* writeable */
  envy24_write_cci (devc, 0x20, bReg);

  /* Set direction: SDA to input and SCL to output */
  bReg = envy24_read_cci (devc, 0x22);
  bReg |= 0x20;			/* SCL output = 1 */
  bReg &= ~0x10;		/* SDA input  = 0 */
  envy24_write_cci (devc, 0x22, bReg);

  /* Get SDA line state */
  bSDA = envy24_read_cci (devc, 0x20);


  /* set RW line HIGH */
  bReg = 0x08;			/* writeable */
  envy24_write_cci (devc, 0x20, bReg);

  return 1;
  /* return ((bSDA & 0x10) == 0x10); */

}


Function : IIC_SetIic ------------------------------------------------------------------------------- Description : Returns : void -> Parameters : unsigned int dwPortAddr -> : unsigned char fSDA -> : unsigned char fSCL -> ------------------------------------------------------------------------------- Notes : =============================================================================

static void
IIC_SetIic (envy24_devc * devc, unsigned char fSDA, unsigned char fSCL)
{
  unsigned char bReg;

  /* Set direction: SDA and SCL to output */
  bReg = envy24_read_cci (devc, 0x22);
  bReg |= (0x20 | 0x10);	/* 1 -> output */
  envy24_write_cci (devc, 0x22, bReg);

  /* set write mask */
  bReg = ~(0x20 | 0x10);	/* writeable */
  envy24_write_cci (devc, 0x21, bReg);

  /* Set line state */
  /* FMB TEST: RW line */
  bReg = 0x08;
  if (fSDA)
    bReg += 0x10;
  if (fSCL)
    bReg += 0x20;
  envy24_write_cci (devc, 0x20, bReg);

}


Function : IIC_Start ------------------------------------------------------------------------------- Description : Returns : void -> Parameters : unsigned int dwPortAddr -> ------------------------------------------------------------------------------- Notes : =============================================================================

/*__inline  */
static void
IIC_Start (envy24_devc * devc)
{
  /* falling edge of SDA while SCL is HIGH */
  IIC_SetIic (devc, 1, 1);
  oss_udelay (30);
  IIC_SetIic (devc, 0, 1);
}


Function : IIC_Stop ------------------------------------------------------------------------------- Description : Returns : void -> Parameters : unsigned int dwPortAddr -> ------------------------------------------------------------------------------- Notes : =============================================================================

/*__inline  */

static void
IIC_Stop (envy24_devc * devc)
{
  /* rising edge of SDA while SCL is HIGH */
  IIC_SetIic (devc, 0, 1);
  IIC_SetIic (devc, 1, 1);
}



Function : IIC_SendByte ------------------------------------------------------------------------------- Description : Returns : unsigned char -> Parameters : unsigned int dwPortAddr -> : unsigned char bByte -> ------------------------------------------------------------------------------- Notes : =============================================================================

/*__inline  */
static unsigned char
IIC_SendByte (envy24_devc * devc, unsigned char bByte)
{
  unsigned char bDataBit, bAck;
  int i;

  for (i = 7; i >= 0; i--)	/* send byte (MSB first) */
    {
      bDataBit = (bByte >> i) & 0x01;

      IIC_SetIic (devc, bDataBit, 0);
      IIC_SetIic (devc, bDataBit, 1);
      IIC_SetIic (devc, bDataBit, 0);
    }				/* end for i */

  IIC_SetIic (devc, 1, 0);

  /* Get acknowledge */
  IIC_SetIic (devc, 1, 1);
  bAck = IIC_GetSDA (devc);
  /* FMB this is a start condition but never mind */
  IIC_SetIic (devc, 0, 0);
#if 0
  if (bAck)
    cmn_err (CE_CONT, "SendByte failed %02x\n", bByte);
  else
    cmn_err (CE_CONT, "SendByte OK %02x\n", bByte);
#endif
  return 1;
  /* return (!bAck); *//* bAck = 0 --> success */
}

#if 0

Function : IIC_WriteByte ------------------------------------------------------------------------------- Description : Returns : unsigned char -> Parameters : unsigned int dwPortAddr -> : unsigned char bIicAddress -> : unsigned char bByte -> ------------------------------------------------------------------------------- Notes : =============================================================================

static unsigned char
IIC_WriteByte (envy24_devc * devc, unsigned char bIicAddress,
	       unsigned char bByte)
{
  IIC_Start (devc);

  /* send IIC address and data byte */
  if (!IIC_SendByte (devc, bIicAddress))
    {
      cmn_err (CE_CONT, "IIC_SendByte 1 failed\n");
      goto FAILED;
    }
  if (!IIC_SendByte (devc, bByte))
    {
      cmn_err (CE_CONT, "IIC_SendByte 2 failed\n");
      goto FAILED;
    }

  IIC_Stop (devc);
  return 1;

FAILED:
  IIC_Stop (devc);
  return 0;
}
#endif

static unsigned char
IIC_WriteWord (envy24_devc * devc, unsigned char bIicAddress,
	       unsigned char reg, unsigned char bByte)
{
  IIC_Start (devc);

  /* send IIC address and data byte */
  if (!IIC_SendByte (devc, bIicAddress))
    {
      cmn_err (CE_CONT, "IIC_SendByte 1 failed\n");
      goto FAILED;
    }
  if (!IIC_SendByte (devc, reg))
    {
      cmn_err (CE_CONT, "IIC_SendByte 2 failed\n");
      goto FAILED;
    }
  if (!IIC_SendByte (devc, bByte))
    {
      cmn_err (CE_CONT, "IIC_SendByte 3 failed\n");
      goto FAILED;
    }

  IIC_Stop (devc);
  return 1;

FAILED:
  IIC_Stop (devc);
  return 0;
}

static void
FrontboxControl (envy24_devc * devc, int b)
{
  IIC_WriteWord (devc, 0x40, 0x01, b);	/* Output register */
  devc->gpio_tmp = b;
}

static int
HW_AK4525_SetChipSelect (envy24_devc * devc, unsigned char bCsMask)
{
  unsigned char b;
  /* check validity */
  if (bCsMask > 0x07)
    {
      cmn_err (CE_CONT, "Invalid bCsMask %02x\n", bCsMask);
      return 0;			/* invalid  */
    }

  envy24_write_cci (devc, 0x21, 0x00);
  envy24_write_cci (devc, 0x22, 0xff);
  b = envy24_read_cci (devc, 0x20);
  b &= ~0x07;

  b |= bCsMask & 0x07;

  envy24_write_cci (devc, 0x20, b);

  return 1;
}

#define HW_AK4525_SetLines IIC_SetIic


FUNCTION: HW_AK4525_WriteRegister ------------------------------------------------------------------------------- PURPOSE: PARAMETERS: unsigned int port - port address for IIC port unsigned char bCsMask - CoDec bitmask (bits 0..3) or 0xFF for all codecs together unsigned char bRegIdx - offset to desired register unsigned char bReg - new register value RETURNS: ------------------------------------------------------------------------------- NOTES: The Chip Selects must have been set prior to this call PCF8574_CODEC_IIC_ADDRESS

+-----------------------------------------------------------------+ | B15 B14 B13 B12 B11 B10 B09 B08|B07 B06 B05 B04 B03 B02 B01 B00 | +--------+---+-------------------+--------------------------------+ | C1 C0 |R/W|A4 A3 A2 A1 A0 |D7 D6 D5 D4 D3 D2 D1 D0 | | 1 0 |1 | <= 7 | data | +--------+---+-------------------+--------------------------------+

=============================================================================

static unsigned char
HW_AK4525_WriteRegister (envy24_devc * devc, unsigned char bCsMask,
			 unsigned char bRegIdx, unsigned char bRegData)
{

  unsigned short wCmd;
  unsigned char i;
  unsigned char fData;

  /* format buffer */
  wCmd = 0xA000 +		/* chip address + R/W */
    (((unsigned short) bRegIdx) << 8) +	/* register address */
    bRegData;

  HW_AK4525_SetLines (devc, 0, 0);

  /* start write cycle */
  if (!HW_AK4525_SetChipSelect (devc, bCsMask))
    {
      cmn_err (CE_CONT, "HW_AK4525_SetChipSelect failed\n");
      return 0;			/* = CS to LOW -> data latched */
    }


  for (i = 0; i < 16; i++)
    {
      fData = (wCmd & 0x8000) ? 1 : 0;
      HW_AK4525_SetLines (devc, fData, 0);
      HW_AK4525_SetLines (devc, fData, 1);	/* data is clocked in on rising edge of CCLK */
      HW_AK4525_SetLines (devc, fData, 0);
      wCmd <<= 1;
    }

  /* leave data line HIGH (= default for IIC) */
  HW_AK4525_SetLines (devc, fData, 0);	/* data is clocked in on rising edge of CCLK */

  /* end write cycle */
  if (!HW_AK4525_SetChipSelect (devc, 0x00))
    {
      cmn_err (CE_CONT, "HW_AK4525_SetChipSelect 2 failed\n");
      return 0;			/* = all CS to HIGH -> CS to default */
    }

  /* default */
  HW_AK4525_SetLines (devc, 1, 1);	/* data is clocked in on rising edge of CCLK */

  return 1;
}


/* Register offsets */
enum
{
  AK4525_REG_POWER,
  AK4525_REG_RESET,
  AK4525_REG_FORMAT,
  AK4525_REG_DEEMPHASIS,
  AK4525_REG_LEFT_INPUT,
  AK4525_REG_RIGHT_INPUT,
  AK4525_REG_LEFT_OUTPUT,
  AK4525_REG_RIGHT_OUTPUT
};



Function : HW_AK4525_Reset ------------------------------------------------------------------------------- Description : Returns : unsigned char Parameters : PCUST_HW_INSTANCE_DATA devc - int nCodecIdx -> 0..3 index of Codec ,-1 -> all ------------------------------------------------------------------------------- Notes : =============================================================================

static unsigned char
HW_AK4525_Reset (envy24_devc * devc, int nCodecIdx)
{
  unsigned char bCodecMask = 0;
  if (nCodecIdx == -1)
    bCodecMask = 0x07;
  else
    bCodecMask = 0x01 << (nCodecIdx);

  if (!HW_AK4525_WriteRegister (devc, bCodecMask,	/* 0x0F -> set mode for all codecs */
				AK4525_REG_RESET, 0x00))	/* DACs,ADCs -> reset */
    {
      cmn_err (CE_CONT, "REG_RESET failed\n");
      return 0;
    }

  if (!HW_AK4525_WriteRegister (devc, bCodecMask,	/* 0x0F -> set mode for all codecs */
				AK4525_REG_FORMAT, 0x60))	/* IIS, 256 fsn, normal speed */
    return 0;

  if (!HW_AK4525_WriteRegister (devc, bCodecMask,	/* 0x0F -> set mode for all codecs */
				AK4525_REG_RESET, 0x03))	/* DACs,ADCs -> normal operation */
    return 0;

  if (!HW_AK4525_WriteRegister (devc, bCodecMask,	/* 0x0F -> set mode for all codecs */
				AK4525_REG_POWER, 0x07))	/* power on */
    return 0;

  if (!HW_AK4525_WriteRegister (devc, bCodecMask,	/* soft mute timeout --> short */
				AK4525_REG_DEEMPHASIS, 0x19))
    return 0;

  if (!HW_AK4525_WriteRegister (devc,
				bCodecMask, AK4525_REG_LEFT_INPUT, 0x7f))
    return 0;

  if (!HW_AK4525_WriteRegister (devc,
				bCodecMask, AK4525_REG_RIGHT_INPUT, 0x7f))
    return 0;

  if (!HW_AK4525_WriteRegister (devc,
				bCodecMask, AK4525_REG_LEFT_OUTPUT, 0x7f))
    return 0;

  if (!HW_AK4525_WriteRegister (devc,
				bCodecMask, AK4525_REG_RIGHT_OUTPUT, 0x7f))
    return 0;

  return 1;
}

static void
set_dmx6fire_speed (envy24_devc * devc)
{
  unsigned char tmp;

  tmp = devc->speedbits;
  if (devc->syncsource != SYNC_INTERNAL)
    {
      tmp |= 0x10;		/* S/PDIF input clock select */
      if (devc->model_data->flags & MF_WCLOCK)	/* Has world clock too */
	{
	  int cmd = envy24_read_cci (devc, 0x20);
	  cmd |= 0x10;		/* S/PDIF */
	  if (devc->syncsource == SYNC_WCLOCK)
	    cmd &= ~0x10;	/* World clock */
	  envy24_write_cci (devc, 0x20, cmd);
	}
    }
  OUTB (devc->osdev, tmp, devc->mt_base + 0x01);
}

#if 0

S/PDIF register access doesn't work so this feature is not enabled.


#define EWX_CLK 0x20
#define EWX_DTA 0x10

static void
dmx6fire_iic_write_byte (envy24_devc * devc, unsigned char data,
			 unsigned char save)
{
  int i;
  unsigned char gpio;

  for (i = 0; i < 8; i++)
    {
      int fData = (data & 0x80) ? 1 : 0;

      gpio = 0x08;
      if (fData)
	gpio |= EWX_DTA;	/* DATA */
      envy24_write_cci (devc, 0x20, gpio | save);
      envy24_write_cci (devc, 0x20, gpio | EWX_CLK | save);	/*  Clock pulse */
      envy24_write_cci (devc, 0x20, gpio | save);

      data <<= 1;
    }

  envy24_write_cci (devc, 0x20, EWX_DTA | save);
  envy24_write_cci (devc, 0x20, EWX_DTA | EWX_CLK | save);	/*  Clock pulse  (ACK) */
  envy24_write_cci (devc, 0x20, EWX_DTA | save);
}

static unsigned char
dmx6fire_iic_read_byte (envy24_devc * devc, unsigned char save)
{
  int i;
  unsigned char data = 0;
  int b;

  PRT_STATUS (0x01);
  save |= EWX_DTA;
  envy24_write_cci (devc, 0x20, save);
  oss_udelay (10);
#if 1
  save &= ~8;			/* R/W bit */
  envy24_write_cci (devc, 0x22, ~EWX_DTA);	/* GPIO direction */
  envy24_write_cci (devc, 0x21, EWX_DTA);	/* GPIO write mask */
#endif

  for (i = 0; i < 8; i++)
    {

      envy24_write_cci (devc, 0x20, EWX_CLK | save);	/*  Clock pulse */
      b = envy24_read_cci (devc, 0x20);
      if (b & EWX_DTA)
	data |= 0x80;
      envy24_write_cci (devc, 0x20, save);

      data >>= 1;
    }

  envy24_write_cci (devc, 0x22, 0xff);	/* GPIO direction */
  envy24_write_cci (devc, 0x21, 0x00);	/* GPIO write mask */
  envy24_write_cci (devc, 0x20, (save & ~EWX_DTA) | 0x08);	/* Low for the ACK bit */
  envy24_write_cci (devc, 0x20, (save & ~EWX_DTA) | 0x08 | EWX_CLK);	/* Clock for the ACK bit */
  envy24_write_cci (devc, 0x20, save | 0x08);
  oss_udelay (10);
  return data;
}

#if 0
static void
dmx6fire_iic_write (envy24_devc * devc, int addr,
		    unsigned char bRegIdx, unsigned char bRegData)
{
  unsigned char save;

  /* start write cycle */
  envy24_write_cci (devc, 0x22, 0xff);	/* GPIO direction */
  envy24_write_cci (devc, 0x21, 0x00);	/* GPIO write mask */

  save = (envy24_read_cci (devc, 0x20) & ~(EWX_CLK | EWX_DTA)) | 0x08	/* R/W bit */
    ;
  /* Send start */
  envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA);
  envy24_write_cci (devc, 0x20, save | EWX_CLK);
  envy24_write_cci (devc, 0x20, save);

  dmx6fire_iic_write_byte (devc, addr, save);
  dmx6fire_iic_write_byte (devc, bRegIdx, save);
  dmx6fire_iic_write_byte (devc, bRegData, save);

  /* Send stop */
  envy24_write_cci (devc, 0x20, save);
  envy24_write_cci (devc, 0x20, save | EWX_CLK);
  envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA);
}
#endif

static unsigned char
dmx6fire_iic_read (envy24_devc * devc, int addr, unsigned char bRegIdx)
{
  unsigned char save, data;

  /* dmx6fire_iic_write(devc, addr, bRegIdx, 0x55); */
  PRT_STATUS (0x80);

  envy24_write_cci (devc, 0x22, 0xff);	/* GPIO direction */
  envy24_write_cci (devc, 0x21, 0x00);	/* GPIO write mask */

  save = (envy24_read_cci (devc, 0x20) & ~(EWX_DTA | EWX_CLK)) | 0x08	/* R/W bit */
    ;
  PRT_STATUS (0x02);
  /* Send start */
  envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA);
  envy24_write_cci (devc, 0x20, save | EWX_CLK);
  envy24_write_cci (devc, 0x20, save);

  dmx6fire_iic_write_byte (devc, addr | 0x01, save);
  data = dmx6fire_iic_read_byte (devc, save);

  /* Send stop */
  envy24_write_cci (devc, 0x20, save);
  envy24_write_cci (devc, 0x20, save | EWX_CLK);
  envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA);
  PRT_STATUS (0x00);
  return data;
}

static int
read_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister)


Reads a byte from a specific CS8427 register.


{

  unsigned char ret;
  ret = dmx6fire_iic_read (devc, EWX_DIG_ADDR, bRegister);

  return ret;
}

static void
write_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister, int bData)
{

  /*dmx6fire_iic_write(devc, EWX_DIG_ADDR, bRegister, bData); */
  IIC_WriteWord (devc, EWX_DIG_ADDR, bRegister, bData);
}
#else
static int
read_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister)
{
  return 0;
}

static void
write_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister, int bData)
{
}
#endif

static void
dmx6fire_card_init (envy24_devc * devc)
{
  envy24_write_cci (devc, 0x20, 0xff);
  envy24_write_cci (devc, 0x21, 0x00);
  envy24_write_cci (devc, 0x22, 0xff);
  envy24_write_cci (devc, 0x20, 0xff);
  HW_AK4525_SetChipSelect (devc, 0x00);
#if 1

  IIC_WriteWord (devc, 0x40, 0x02, 0x00);	/* Polarity register */
  IIC_WriteWord (devc, 0x40, 0x03, 0x00);	/* Direction register */

  FrontboxControl (devc, DEFAULT_FRONTBOX_SETUP);	/* Set default values */

  if (!HW_AK4525_Reset (devc, -1))
    cmn_err (CE_CONT, "Envy24: DMX6fire reset failed\n");;
#endif

}

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

  if (cmd == SNDCTL_MIX_READ)
    switch (ctrl)
      {
      case 1:
	if (devc->skipdevs == 2)
	  return devc->rec_portc[2].riaa_filter;
	else
	  return devc->rec_portc[4].riaa_filter;
	break;

      }

  if (cmd == SNDCTL_MIX_WRITE)
    switch (ctrl)
      {
      case 1:
	if (devc->skipdevs == 2)
	  return devc->rec_portc[2].riaa_filter = !!value;
	else
	  return devc->rec_portc[4].riaa_filter = !!value;
	break;

      }

  return OSS_EINVAL;
}

static int
dmx6fire_mix_init (envy24_devc * devc, int dev, int group)
{
  int err;
#if 0
  int i, mask = devc->outportmask, skip, codec, ports;
  int range = 3;
  int typ = MIXT_ENUM;
  if ((group = mixer_ext_create_group (dev, 0, "ENVY24_GAIN")) < 0)
    return group;

  skip = devc->skipdevs;
  if (skip != 2)
    skip = 1;

  if (envy24_gain_sliders)
    {
      typ = MIXT_MONOSLIDER;
      range = 164;

      for (i = 0; i < 0xff; i++)
	devc->akm_gains[i] = 0x7f;
    }
  else
    for (i = 0; i < 0xff; i++)
      devc->akm_gains[i] = 0;

  for (i = 0; i < 8; i += skip)
    {
      char tmp[32];

      if (!(mask & (1 << i)))
	continue;		/* Not present */

      if (devc->skipdevs == 2)
	{
	  if (i == 8)
	    strcpy (tmp, "ENVY24_SPDOUT");
	  else
	    sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2);

	  codec = (i > 1) ? AKM_B : AKM_A;
	  ports = 0x0c;		/* Both output ports */
	  if ((err = mixer_ext_create_control (dev, group,
					       codec | ports, envy24_set_tdif,
					       typ,
					       tmp, range,
					       MIXF_MAINVOL |
					       MIXF_READABLE |
					       MIXF_WRITEABLE)) < 0)
	    return err;
	}
      else
	{
	  if (i == 8)
	    strcpy (tmp, "ENVY24_SPDOUTL");
	  else if (i == 9)
	    strcpy (tmp, "ENVY24_SPDOUTR");
	  else
	    sprintf (tmp, "ENVY24_OUT%d", i + 1);

	  codec = (i > 1) ? AKM_B : AKM_A;
	  ports = (i & 1) ? 0x08 : 0x04;
	  if ((err = mixer_ext_create_control (dev, group,
					       codec | ports, envy24_set_tdif,
					       typ,
					       tmp, range,
					       MIXF_MAINVOL |
					       MIXF_READABLE |
					       MIXF_WRITEABLE)) < 0)
	    return err;
	}
    }
#endif

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

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

  return 0;
}

envy24_auxdrv_t dmx6fire_auxdrv = {
  dmx6fire_card_init,
  dmx6fire_mix_init,
  init_cs8427_spdif,
  write_cs8427_spdif,
  NULL,
  read_dmx6fire_spdif_reg,
  write_dmx6fire_spdif_reg,
  set_dmx6fire_speed,
  NULL,
  cs8427_spdif_mixer_init
};

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