Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/sound/pci/trident/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 117 kB image not shown  

Quelle  trident_main.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Maintained by Jaroslav Kysela <perex@perex.cz>
 *  Originated by audio@tridentmicro.com
 *  Fri Feb 19 15:55:28 MST 1999
 *  Routines for control of Trident 4DWave (DX and NX) chip
 *
 *  BUGS:
 *
 *  TODO:
 *    ---
 *
 *  SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
 */


#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/gameport.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/io.h>

#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include "trident.h"
#include <sound/asoundef.h>

static int snd_trident_pcm_mixer_build(struct snd_trident *trident,
           struct snd_trident_voice * voice,
           struct snd_pcm_substream *substream);
static int snd_trident_pcm_mixer_free(struct snd_trident *trident,
          struct snd_trident_voice * voice,
          struct snd_pcm_substream *substream);
static irqreturn_t snd_trident_interrupt(int irq, void *dev_id);
static int snd_trident_sis_reset(struct snd_trident *trident);

static void snd_trident_clear_voices(struct snd_trident * trident,
         unsigned short v_min, unsigned short v_max);
static void snd_trident_free(struct snd_card *card);

/*
 *  common I/O routines
 */



#if 0
static void snd_trident_print_voice_regs(struct snd_trident *trident, int voice)
{
 unsigned int val, tmp;

 dev_dbg(trident->card->dev, "Trident voice %i:\n", voice);
 outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
 val = inl(TRID_REG(trident, CH_LBA));
 dev_dbg(trident->card->dev, "LBA: 0x%x\n", val);
 val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
 dev_dbg(trident->card->dev, "GVSel: %i\n", val >> 31);
 dev_dbg(trident->card->dev, "Pan: 0x%x\n", (val >> 24) & 0x7f);
 dev_dbg(trident->card->dev, "Vol: 0x%x\n", (val >> 16) & 0xff);
 dev_dbg(trident->card->dev, "CTRL: 0x%x\n", (val >> 12) & 0x0f);
 dev_dbg(trident->card->dev, "EC: 0x%x\n", val & 0x0fff);
 if (trident->device != TRIDENT_DEVICE_ID_NX) {
  val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
  dev_dbg(trident->card->dev, "CSO: 0x%x\n", val >> 16);
  dev_dbg(trident->card->dev, "Alpha: 0x%x\n", (val >> 4) & 0x0fff);
  dev_dbg(trident->card->dev, "FMS: 0x%x\n", val & 0x0f);
  val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
  dev_dbg(trident->card->dev, "ESO: 0x%x\n", val >> 16);
  dev_dbg(trident->card->dev, "Delta: 0x%x\n", val & 0xffff);
  val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
 } else {  // TRIDENT_DEVICE_ID_NX
  val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
  tmp = (val >> 24) & 0xff;
  dev_dbg(trident->card->dev, "CSO: 0x%x\n", val & 0x00ffffff);
  val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
  tmp |= (val >> 16) & 0xff00;
  dev_dbg(trident->card->dev, "Delta: 0x%x\n", tmp);
  dev_dbg(trident->card->dev, "ESO: 0x%x\n", val & 0x00ffffff);
  val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
  dev_dbg(trident->card->dev, "Alpha: 0x%x\n", val >> 20);
  dev_dbg(trident->card->dev, "FMS: 0x%x\n", (val >> 16) & 0x0f);
 }
 dev_dbg(trident->card->dev, "FMC: 0x%x\n", (val >> 14) & 3);
 dev_dbg(trident->card->dev, "RVol: 0x%x\n", (val >> 7) & 0x7f);
 dev_dbg(trident->card->dev, "CVol: 0x%x\n", val & 0x7f);
}
#endif

/*---------------------------------------------------------------------------
   unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
  
   Description: This routine will do all of the reading from the external
                CODEC (AC97).
  
   Parameters:  ac97 - ac97 codec structure
                reg - CODEC register index, from AC97 Hal.
 
   returns:     16 bit value read from the AC97.
  
  ---------------------------------------------------------------------------*/

static unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
{
 unsigned int data = 0, treg;
 unsigned short count = 0xffff;
 unsigned long flags;
 struct snd_trident *trident = ac97->private_data;

 spin_lock_irqsave(&trident->reg_lock, flags);
 if (trident->device == TRIDENT_DEVICE_ID_DX) {
  data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
  outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
  do {
   data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
   if ((data & DX_AC97_BUSY_READ) == 0)
    break;
  } while (--count);
 } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
  data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
  treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
  outl(data, TRID_REG(trident, treg));
  do {
   data = inl(TRID_REG(trident, treg));
   if ((data & 0x00000C00) == 0)
    break;
  } while (--count);
 } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
  data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
  if (ac97->num == 1)
   data |= SI_AC97_SECONDARY;
  outl(data, TRID_REG(trident, SI_AC97_READ));
  do {
   data = inl(TRID_REG(trident, SI_AC97_READ));
   if ((data & (SI_AC97_BUSY_READ)) == 0)
    break;
  } while (--count);
 }

 if (count == 0 && !trident->ac97_detect) {
  dev_err(trident->card->dev,
   "ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n",
      reg, data);
  data = 0;
 }

 spin_unlock_irqrestore(&trident->reg_lock, flags);
 return ((unsigned short) (data >> 16));
}

/*---------------------------------------------------------------------------
   void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
   unsigned short wdata)
  
   Description: This routine will do all of the writing to the external
                CODEC (AC97).
  
   Parameters: ac97 - ac97 codec structure
            reg - CODEC register index, from AC97 Hal.
                data  - Lower 16 bits are the data to write to CODEC.
  
   returns:     TRUE if everything went ok, else FALSE.
  
  ---------------------------------------------------------------------------*/

static void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
        unsigned short wdata)
{
 unsigned int address, data;
 unsigned short count = 0xffff;
 unsigned long flags;
 struct snd_trident *trident = ac97->private_data;

 data = ((unsigned long) wdata) << 16;

 spin_lock_irqsave(&trident->reg_lock, flags);
 if (trident->device == TRIDENT_DEVICE_ID_DX) {
  address = DX_ACR0_AC97_W;

  /* read AC-97 write register status */
  do {
   if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
    break;
  } while (--count);

  data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
 } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
  address = NX_ACR1_AC97_W;

  /* read AC-97 write register status */
  do {
   if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
    break;
  } while (--count);

  data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
 } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
  address = SI_AC97_WRITE;

  /* read AC-97 write register status */
  do {
   if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
    break;
  } while (--count);

  data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
  if (ac97->num == 1)
   data |= SI_AC97_SECONDARY;
 } else {
  address = 0; /* keep GCC happy */
  count = 0; /* return */
 }

 if (count == 0) {
  spin_unlock_irqrestore(&trident->reg_lock, flags);
  return;
 }
 outl(data, TRID_REG(trident, address));
 spin_unlock_irqrestore(&trident->reg_lock, flags);
}

/*---------------------------------------------------------------------------
   void snd_trident_enable_eso(struct snd_trident *trident)
  
   Description: This routine will enable end of loop interrupts.
                End of loop interrupts will occur when a running
                channel reaches ESO.
                Also enables middle of loop interrupts.
  
   Parameters:  trident - pointer to target device class for 4DWave.
  
  ---------------------------------------------------------------------------*/


static void snd_trident_enable_eso(struct snd_trident * trident)
{
 unsigned int val;

 val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
 val |= ENDLP_IE;
 val |= MIDLP_IE;
 if (trident->device == TRIDENT_DEVICE_ID_SI7018)
  val |= BANK_B_EN;
 outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
}

/*---------------------------------------------------------------------------
   void snd_trident_disable_eso(struct snd_trident *trident)
  
   Description: This routine will disable end of loop interrupts.
                End of loop interrupts will occur when a running
                channel reaches ESO.
                Also disables middle of loop interrupts.
  
   Parameters:  
                trident - pointer to target device class for 4DWave.
  
   returns:     TRUE if everything went ok, else FALSE.
  
  ---------------------------------------------------------------------------*/


static void snd_trident_disable_eso(struct snd_trident * trident)
{
 unsigned int tmp;

 tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
 tmp &= ~ENDLP_IE;
 tmp &= ~MIDLP_IE;
 outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
}

/*---------------------------------------------------------------------------
   void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)

    Description: Start a voice, any channel 0 thru 63.
                 This routine automatically handles the fact that there are
                 more than 32 channels available.

    Parameters : voice - Voice number 0 thru n.
                 trident - pointer to target device class for 4DWave.

    Return Value: None.

  ---------------------------------------------------------------------------*/


void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
{
 unsigned int mask = 1 << (voice & 0x1f);
 unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;

 outl(mask, TRID_REG(trident, reg));
}

EXPORT_SYMBOL(snd_trident_start_voice);

/*---------------------------------------------------------------------------
   void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)

    Description: Stop a voice, any channel 0 thru 63.
                 This routine automatically handles the fact that there are
                 more than 32 channels available.

    Parameters : voice - Voice number 0 thru n.
                 trident - pointer to target device class for 4DWave.

    Return Value: None.

  ---------------------------------------------------------------------------*/


void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
{
 unsigned int mask = 1 << (voice & 0x1f);
 unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;

 outl(mask, TRID_REG(trident, reg));
}

EXPORT_SYMBOL(snd_trident_stop_voice);

/*---------------------------------------------------------------------------
    int snd_trident_allocate_pcm_channel(struct snd_trident *trident)
  
    Description: Allocate hardware channel in Bank B (32-63).
  
    Parameters :  trident - pointer to target device class for 4DWave.
  
    Return Value: hardware channel - 32-63 or -1 when no channel is available
  
  ---------------------------------------------------------------------------*/


static int snd_trident_allocate_pcm_channel(struct snd_trident * trident)
{
 int idx;

 if (trident->ChanPCMcnt >= trident->ChanPCM)
  return -1;
 for (idx = 31; idx >= 0; idx--) {
  if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
   trident->ChanMap[T4D_BANK_B] |= 1 << idx;
   trident->ChanPCMcnt++;
   return idx + 32;
  }
 }
 return -1;
}

/*---------------------------------------------------------------------------
    void snd_trident_free_pcm_channel(int channel)
  
    Description: Free hardware channel in Bank B (32-63)
  
    Parameters :  trident - pointer to target device class for 4DWave.
          channel - hardware channel number 0-63
  
    Return Value: none
  
  ---------------------------------------------------------------------------*/


static void snd_trident_free_pcm_channel(struct snd_trident *trident, int channel)
{
 if (channel < 32 || channel > 63)
  return;
 channel &= 0x1f;
 if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
  trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
  trident->ChanPCMcnt--;
 }
}

/*---------------------------------------------------------------------------
    unsigned int snd_trident_allocate_synth_channel(void)
  
    Description: Allocate hardware channel in Bank A (0-31).
  
    Parameters :  trident - pointer to target device class for 4DWave.
  
    Return Value: hardware channel - 0-31 or -1 when no channel is available
  
  ---------------------------------------------------------------------------*/


static int snd_trident_allocate_synth_channel(struct snd_trident * trident)
{
 int idx;

 for (idx = 31; idx >= 0; idx--) {
  if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
   trident->ChanMap[T4D_BANK_A] |= 1 << idx;
   trident->synth.ChanSynthCount++;
   return idx;
  }
 }
 return -1;
}

/*---------------------------------------------------------------------------
    void snd_trident_free_synth_channel( int channel )
  
    Description: Free hardware channel in Bank B (0-31).
  
    Parameters :  trident - pointer to target device class for 4DWave.
          channel - hardware channel number 0-63
  
    Return Value: none
  
  ---------------------------------------------------------------------------*/


static void snd_trident_free_synth_channel(struct snd_trident *trident, int channel)
{
 if (channel < 0 || channel > 31)
  return;
 channel &= 0x1f;
 if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) {
  trident->ChanMap[T4D_BANK_A] &= ~(1 << channel);
  trident->synth.ChanSynthCount--;
 }
}

/*---------------------------------------------------------------------------
   snd_trident_write_voice_regs
  
   Description: This routine will complete and write the 5 hardware channel
                registers to hardware.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                voice - synthesizer voice structure
                Each register field.
  
  ---------------------------------------------------------------------------*/


void snd_trident_write_voice_regs(struct snd_trident * trident,
      struct snd_trident_voice * voice)
{
 unsigned int FmcRvolCvol;
 unsigned int regs[5];

 regs[1] = voice->LBA;
 regs[4] = (voice->GVSel << 31) |
    ((voice->Pan & 0x0000007f) << 24) |
    ((voice->CTRL & 0x0000000f) << 12);
 FmcRvolCvol = ((voice->FMC & 3) << 14) |
               ((voice->RVol & 0x7f) << 7) |
               (voice->CVol & 0x7f);

 switch (trident->device) {
 case TRIDENT_DEVICE_ID_SI7018:
  regs[4] |= voice->number > 31 ?
    (voice->Vol & 0x000003ff) :
    ((voice->Vol & 0x00003fc) << (16-2)) |
    (voice->EC & 0x00000fff);
  regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
   (voice->FMS & 0x0000000f);
  regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
  regs[3] = (voice->Attribute << 16) | FmcRvolCvol;
  break;
 case TRIDENT_DEVICE_ID_DX:
  regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
      (voice->EC & 0x00000fff);
  regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
   (voice->FMS & 0x0000000f);
  regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
  regs[3] = FmcRvolCvol;
  break;
 case TRIDENT_DEVICE_ID_NX:
  regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
      (voice->EC & 0x00000fff);
  regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff);
  regs[2] = ((voice->Delta << 16) & 0xff000000) |
   (voice->ESO & 0x00ffffff);
  regs[3] = (voice->Alpha << 20) |
   ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol;
  break;
 default:
  snd_BUG();
  return;
 }

 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
 outl(regs[0], TRID_REG(trident, CH_START + 0));
 outl(regs[1], TRID_REG(trident, CH_START + 4));
 outl(regs[2], TRID_REG(trident, CH_START + 8));
 outl(regs[3], TRID_REG(trident, CH_START + 12));
 outl(regs[4], TRID_REG(trident, CH_START + 16));

#if 0
 dev_dbg(trident->card->dev, "written %i channel:\n", voice->number);
 dev_dbg(trident->card->dev, " regs[0] = 0x%x/0x%x\n",
        regs[0], inl(TRID_REG(trident, CH_START + 0)));
 dev_dbg(trident->card->dev, " regs[1] = 0x%x/0x%x\n",
        regs[1], inl(TRID_REG(trident, CH_START + 4)));
 dev_dbg(trident->card->dev, " regs[2] = 0x%x/0x%x\n",
        regs[2], inl(TRID_REG(trident, CH_START + 8)));
 dev_dbg(trident->card->dev, " regs[3] = 0x%x/0x%x\n",
        regs[3], inl(TRID_REG(trident, CH_START + 12)));
 dev_dbg(trident->card->dev, " regs[4] = 0x%x/0x%x\n",
        regs[4], inl(TRID_REG(trident, CH_START + 16)));
#endif
}

EXPORT_SYMBOL(snd_trident_write_voice_regs);

/*---------------------------------------------------------------------------
   snd_trident_write_cso_reg
  
   Description: This routine will write the new CSO offset
                register to hardware.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                voice - synthesizer voice structure
                CSO - new CSO value
  
  ---------------------------------------------------------------------------*/


static void snd_trident_write_cso_reg(struct snd_trident * trident,
          struct snd_trident_voice * voice,
          unsigned int CSO)
{
 voice->CSO = CSO;
 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
 if (trident->device != TRIDENT_DEVICE_ID_NX) {
  outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
 } else {
  outl((voice->Delta << 24) |
       (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO));
 }
}

/*---------------------------------------------------------------------------
   snd_trident_write_eso_reg
  
   Description: This routine will write the new ESO offset
                register to hardware.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                voice - synthesizer voice structure
                ESO - new ESO value
  
  ---------------------------------------------------------------------------*/


static void snd_trident_write_eso_reg(struct snd_trident * trident,
          struct snd_trident_voice * voice,
          unsigned int ESO)
{
 voice->ESO = ESO;
 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
 if (trident->device != TRIDENT_DEVICE_ID_NX) {
  outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2);
 } else {
  outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff),
       TRID_REG(trident, CH_NX_DELTA_ESO));
 }
}

/*---------------------------------------------------------------------------
   snd_trident_write_vol_reg
  
   Description: This routine will write the new voice volume
                register to hardware.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                voice - synthesizer voice structure
                Vol - new voice volume
  
  ---------------------------------------------------------------------------*/


static void snd_trident_write_vol_reg(struct snd_trident * trident,
          struct snd_trident_voice * voice,
          unsigned int Vol)
{
 voice->Vol = Vol;
 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
 switch (trident->device) {
 case TRIDENT_DEVICE_ID_DX:
 case TRIDENT_DEVICE_ID_NX:
  outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
  break;
 case TRIDENT_DEVICE_ID_SI7018:
  /* dev_dbg(trident->card->dev, "voice->Vol = 0x%x\n", voice->Vol); */
  outw((voice->CTRL << 12) | voice->Vol,
       TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
  break;
 }
}

/*---------------------------------------------------------------------------
   snd_trident_write_pan_reg
  
   Description: This routine will write the new voice pan
                register to hardware.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                voice - synthesizer voice structure
                Pan - new pan value
  
  ---------------------------------------------------------------------------*/


static void snd_trident_write_pan_reg(struct snd_trident * trident,
          struct snd_trident_voice * voice,
          unsigned int Pan)
{
 voice->Pan = Pan;
 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
 outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f),
      TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3));
}

/*---------------------------------------------------------------------------
   snd_trident_write_rvol_reg
  
   Description: This routine will write the new reverb volume
                register to hardware.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                voice - synthesizer voice structure
                RVol - new reverb volume
  
  ---------------------------------------------------------------------------*/


static void snd_trident_write_rvol_reg(struct snd_trident * trident,
           struct snd_trident_voice * voice,
           unsigned int RVol)
{
 voice->RVol = RVol;
 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
 outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
      (voice->CVol & 0x007f),
      TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
        CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
}

/*---------------------------------------------------------------------------
   snd_trident_write_cvol_reg
  
   Description: This routine will write the new chorus volume
                register to hardware.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                voice - synthesizer voice structure
                CVol - new chorus volume
  
  ---------------------------------------------------------------------------*/


static void snd_trident_write_cvol_reg(struct snd_trident * trident,
           struct snd_trident_voice * voice,
           unsigned int CVol)
{
 voice->CVol = CVol;
 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
 outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
      (voice->CVol & 0x007f),
      TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
        CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
}

/*---------------------------------------------------------------------------
   snd_trident_convert_rate

   Description: This routine converts rate in HZ to hardware delta value.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                rate - Real or Virtual channel number.
  
   Returns:     Delta value.
  
  ---------------------------------------------------------------------------*/

static unsigned int snd_trident_convert_rate(unsigned int rate)
{
 unsigned int delta;

 // We special case 44100 and 8000 since rounding with the equation
 // does not give us an accurate enough value. For 11025 and 22050
 // the equation gives us the best answer. All other frequencies will
 // also use the equation. JDW
 if (rate == 44100)
  delta = 0xeb3;
 else if (rate == 8000)
  delta = 0x2ab;
 else if (rate == 48000)
  delta = 0x1000;
 else
  delta = DIV_ROUND_CLOSEST(rate << 12, 48000) & 0x0000ffff;
 return delta;
}

/*---------------------------------------------------------------------------
   snd_trident_convert_adc_rate

   Description: This routine converts rate in HZ to hardware delta value.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                rate - Real or Virtual channel number.
  
   Returns:     Delta value.
  
  ---------------------------------------------------------------------------*/

static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
{
 unsigned int delta;

 // We special case 44100 and 8000 since rounding with the equation
 // does not give us an accurate enough value. For 11025 and 22050
 // the equation gives us the best answer. All other frequencies will
 // also use the equation. JDW
 if (rate == 44100)
  delta = 0x116a;
 else if (rate == 8000)
  delta = 0x6000;
 else if (rate == 48000)
  delta = 0x1000;
 else
  delta = ((48000 << 12) / rate) & 0x0000ffff;
 return delta;
}

/*---------------------------------------------------------------------------
   snd_trident_spurious_threshold

   Description: This routine converts rate in HZ to spurious threshold.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                rate - Real or Virtual channel number.
  
   Returns:     Delta value.
  
  ---------------------------------------------------------------------------*/

static unsigned int snd_trident_spurious_threshold(unsigned int rate,
         unsigned int period_size)
{
 unsigned int res = (rate * period_size) / 48000;
 if (res < 64)
  res = res / 2;
 else
  res -= 32;
 return res;
}

/*---------------------------------------------------------------------------
   snd_trident_control_mode

   Description: This routine returns a control mode for a PCM channel.
  
   Parameters:  trident - pointer to target device class for 4DWave.
                substream  - PCM substream
  
   Returns:     Control value.
  
  ---------------------------------------------------------------------------*/

static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream)
{
 unsigned int CTRL;
 struct snd_pcm_runtime *runtime = substream->runtime;

 /* set ctrl mode
   CTRL default: 8-bit (unsigned) mono, loop mode enabled
 */

 CTRL = 0x00000001;
 if (snd_pcm_format_width(runtime->format) == 16)
  CTRL |= 0x00000008; // 16-bit data
 if (snd_pcm_format_signed(runtime->format))
  CTRL |= 0x00000002; // signed data
 if (runtime->channels > 1)
  CTRL |= 0x00000004; // stereo data
 return CTRL;
}

/*
 *  PCM part
 */


/*---------------------------------------------------------------------------
   snd_trident_allocate_pcm_mem
  
   Description: Allocate PCM ring buffer for given substream
  
   Parameters:  substream  - PCM substream class
hw_params  - hardware parameters
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_allocate_pcm_mem(struct snd_pcm_substream *substream,
     struct snd_pcm_hw_params *hw_params)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;

 if (trident->tlb.entries) {
  if (runtime->buffer_changed) {
   if (voice->memblk)
    snd_trident_free_pages(trident, voice->memblk);
   voice->memblk = snd_trident_alloc_pages(trident, substream);
   if (voice->memblk == NULL)
    return -ENOMEM;
  }
 }
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_allocate_evoice
  
   Description: Allocate extra voice as interrupt generator
  
   Parameters:  substream  - PCM substream class
hw_params  - hardware parameters
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_allocate_evoice(struct snd_pcm_substream *substream,
           struct snd_pcm_hw_params *hw_params)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident_voice *evoice = voice->extra;

 /* voice management */

 if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
  if (evoice == NULL) {
   evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
   if (evoice == NULL)
    return -ENOMEM;
   voice->extra = evoice;
   evoice->substream = substream;
  }
 } else {
  if (evoice != NULL) {
   snd_trident_free_voice(trident, evoice);
   voice->extra = evoice = NULL;
  }
 }

 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_hw_params
  
   Description: Set the hardware parameters for the playback device.
  
   Parameters:  substream  - PCM substream class
hw_params  - hardware parameters
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_hw_params(struct snd_pcm_substream *substream,
     struct snd_pcm_hw_params *hw_params)
{
 int err;

 err = snd_trident_allocate_pcm_mem(substream, hw_params);
 if (err >= 0)
  err = snd_trident_allocate_evoice(substream, hw_params);
 return err;
}

/*---------------------------------------------------------------------------
   snd_trident_playback_hw_free
  
   Description: Release the hardware resources for the playback device.
  
   Parameters:  substream  - PCM substream class
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_hw_free(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident_voice *evoice = voice ? voice->extra : NULL;

 if (trident->tlb.entries) {
  if (voice && voice->memblk) {
   snd_trident_free_pages(trident, voice->memblk);
   voice->memblk = NULL;
  }
 }
 if (evoice != NULL) {
  snd_trident_free_voice(trident, evoice);
  voice->extra = NULL;
 }
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_playback_prepare
  
   Description: Prepare playback device for playback.
  
   Parameters:  substream  - PCM substream class
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_playback_prepare(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident_voice *evoice = voice->extra;
 struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];

 spin_lock_irq(&trident->reg_lock); 

 /* set delta (rate) value */
 voice->Delta = snd_trident_convert_rate(runtime->rate);
 voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);

 /* set Loop Begin Address */
 if (voice->memblk)
  voice->LBA = voice->memblk->offset;
 else
  voice->LBA = runtime->dma_addr;
 
 voice->CSO = 0;
 voice->ESO = runtime->buffer_size - 1; /* in samples */
 voice->CTRL = snd_trident_control_mode(substream);
 voice->FMC = 3;
 voice->GVSel = 1;
 voice->EC = 0;
 voice->Alpha = 0;
 voice->FMS = 0;
 voice->Vol = mix->vol;
 voice->RVol = mix->rvol;
 voice->CVol = mix->cvol;
 voice->Pan = mix->pan;
 voice->Attribute = 0;
#if 0
 voice->Attribute = (1<<(30-16))|(2<<(26-16))|
      (0<<(24-16))|(0x1f<<(19-16));
#else
 voice->Attribute = 0;
#endif

 snd_trident_write_voice_regs(trident, voice);

 if (evoice != NULL) {
  evoice->Delta = voice->Delta;
  evoice->spurious_threshold = voice->spurious_threshold;
  evoice->LBA = voice->LBA;
  evoice->CSO = 0;
  evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
  evoice->CTRL = voice->CTRL;
  evoice->FMC = 3;
  evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
  evoice->EC = 0;
  evoice->Alpha = 0;
  evoice->FMS = 0;
  evoice->Vol = 0x3ff;   /* mute */
  evoice->RVol = evoice->CVol = 0x7f; /* mute */
  evoice->Pan = 0x7f;   /* mute */
#if 0
  evoice->Attribute = (1<<(30-16))|(2<<(26-16))|
        (0<<(24-16))|(0x1f<<(19-16));
#else
  evoice->Attribute = 0;
#endif
  snd_trident_write_voice_regs(trident, evoice);
  evoice->isync2 = 1;
  evoice->isync_mark = runtime->period_size;
  evoice->ESO = (runtime->period_size * 2) - 1;
 }

 spin_unlock_irq(&trident->reg_lock);

 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_capture_hw_params
  
   Description: Set the hardware parameters for the capture device.
  
   Parameters:  substream  - PCM substream class
hw_params  - hardware parameters
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_capture_hw_params(struct snd_pcm_substream *substream,
      struct snd_pcm_hw_params *hw_params)
{
 return snd_trident_allocate_pcm_mem(substream, hw_params);
}

/*---------------------------------------------------------------------------
   snd_trident_capture_prepare
  
   Description: Prepare capture device for playback.
  
   Parameters:  substream  - PCM substream class
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 unsigned int val, ESO_bytes;

 spin_lock_irq(&trident->reg_lock);

 // Initialize the channel and set channel Mode
 outb(0, TRID_REG(trident, LEGACY_DMAR15));

 // Set DMA channel operation mode register
 outb(0x54, TRID_REG(trident, LEGACY_DMAR11));

 // Set channel buffer Address, DMAR0 expects contiguous PCI memory area
 voice->LBA = runtime->dma_addr;
 outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0));
 if (voice->memblk)
  voice->LBA = voice->memblk->offset;

 // set ESO
 ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1;
 outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6));
 outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4));
 ESO_bytes++;

 // Set channel sample rate, 4.12 format
 val = DIV_ROUND_CLOSEST(48000U << 12, runtime->rate);
 outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));

 // Set channel interrupt blk length
 if (snd_pcm_format_width(runtime->format) == 16) {
  val = (unsigned short) ((ESO_bytes >> 1) - 1);
 } else {
  val = (unsigned short) (ESO_bytes - 1);
 }

 outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL));

 // Right now, set format and start to run captureing, 
 // continuous run loop enable.
 trident->bDMAStart = 0x19; // 0001 1001b

 if (snd_pcm_format_width(runtime->format) == 16)
  trident->bDMAStart |= 0x80;
 if (snd_pcm_format_signed(runtime->format))
  trident->bDMAStart |= 0x20;
 if (runtime->channels > 1)
  trident->bDMAStart |= 0x40;

 // Prepare capture intr channel

 voice->Delta = snd_trident_convert_rate(runtime->rate);
 voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
 voice->isync = 1;
 voice->isync_mark = runtime->period_size;
 voice->isync_max = runtime->buffer_size;

 // Set voice parameters
 voice->CSO = 0;
 voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
 voice->CTRL = snd_trident_control_mode(substream);
 voice->FMC = 3;
 voice->RVol = 0x7f;
 voice->CVol = 0x7f;
 voice->GVSel = 1;
 voice->Pan = 0x7f;  /* mute */
 voice->Vol = 0x3ff;  /* mute */
 voice->EC = 0;
 voice->Alpha = 0;
 voice->FMS = 0;
 voice->Attribute = 0;

 snd_trident_write_voice_regs(trident, voice);

 spin_unlock_irq(&trident->reg_lock);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_si7018_capture_hw_params
  
   Description: Set the hardware parameters for the capture device.
  
   Parameters:  substream  - PCM substream class
hw_params  - hardware parameters
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_si7018_capture_hw_params(struct snd_pcm_substream *substream,
      struct snd_pcm_hw_params *hw_params)
{
 return snd_trident_allocate_evoice(substream, hw_params);
}

/*---------------------------------------------------------------------------
   snd_trident_si7018_capture_hw_free
  
   Description: Release the hardware resources for the capture device.
  
   Parameters:  substream  - PCM substream class
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_si7018_capture_hw_free(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident_voice *evoice = voice ? voice->extra : NULL;

 if (evoice != NULL) {
  snd_trident_free_voice(trident, evoice);
  voice->extra = NULL;
 }
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_si7018_capture_prepare
  
   Description: Prepare capture device for playback.
  
   Parameters:  substream  - PCM substream class
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident_voice *evoice = voice->extra;

 spin_lock_irq(&trident->reg_lock);

 voice->LBA = runtime->dma_addr;
 voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
 voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);

 // Set voice parameters
 voice->CSO = 0;
 voice->ESO = runtime->buffer_size - 1;  /* in samples */
 voice->CTRL = snd_trident_control_mode(substream);
 voice->FMC = 0;
 voice->RVol = 0;
 voice->CVol = 0;
 voice->GVSel = 1;
 voice->Pan = T4D_DEFAULT_PCM_PAN;
 voice->Vol = 0;
 voice->EC = 0;
 voice->Alpha = 0;
 voice->FMS = 0;

 voice->Attribute = (2 << (30-16)) |
      (2 << (26-16)) |
      (2 << (24-16)) |
      (1 << (23-16));

 snd_trident_write_voice_regs(trident, voice);

 if (evoice != NULL) {
  evoice->Delta = snd_trident_convert_rate(runtime->rate);
  evoice->spurious_threshold = voice->spurious_threshold;
  evoice->LBA = voice->LBA;
  evoice->CSO = 0;
  evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */
  evoice->CTRL = voice->CTRL;
  evoice->FMC = 3;
  evoice->GVSel = 0;
  evoice->EC = 0;
  evoice->Alpha = 0;
  evoice->FMS = 0;
  evoice->Vol = 0x3ff;   /* mute */
  evoice->RVol = evoice->CVol = 0x7f; /* mute */
  evoice->Pan = 0x7f;   /* mute */
  evoice->Attribute = 0;
  snd_trident_write_voice_regs(trident, evoice);
  evoice->isync2 = 1;
  evoice->isync_mark = runtime->period_size;
  evoice->ESO = (runtime->period_size * 2) - 1;
 }
 
 spin_unlock_irq(&trident->reg_lock);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_foldback_prepare
  
   Description: Prepare foldback capture device for playback.
  
   Parameters:  substream  - PCM substream class
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_foldback_prepare(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident_voice *evoice = voice->extra;

 spin_lock_irq(&trident->reg_lock);

 /* Set channel buffer Address */
 if (voice->memblk)
  voice->LBA = voice->memblk->offset;
 else
  voice->LBA = runtime->dma_addr;

 /* set target ESO for channel */
 voice->ESO = runtime->buffer_size - 1; /* in samples */

 /* set sample rate */
 voice->Delta = 0x1000;
 voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);

 voice->CSO = 0;
 voice->CTRL = snd_trident_control_mode(substream);
 voice->FMC = 3;
 voice->RVol = 0x7f;
 voice->CVol = 0x7f;
 voice->GVSel = 1;
 voice->Pan = 0x7f; /* mute */
 voice->Vol = 0x3ff; /* mute */
 voice->EC = 0;
 voice->Alpha = 0;
 voice->FMS = 0;
 voice->Attribute = 0;

 /* set up capture channel */
 outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan));

 snd_trident_write_voice_regs(trident, voice);

 if (evoice != NULL) {
  evoice->Delta = voice->Delta;
  evoice->spurious_threshold = voice->spurious_threshold;
  evoice->LBA = voice->LBA;
  evoice->CSO = 0;
  evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
  evoice->CTRL = voice->CTRL;
  evoice->FMC = 3;
  evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
  evoice->EC = 0;
  evoice->Alpha = 0;
  evoice->FMS = 0;
  evoice->Vol = 0x3ff;   /* mute */
  evoice->RVol = evoice->CVol = 0x7f; /* mute */
  evoice->Pan = 0x7f;   /* mute */
  evoice->Attribute = 0;
  snd_trident_write_voice_regs(trident, evoice);
  evoice->isync2 = 1;
  evoice->isync_mark = runtime->period_size;
  evoice->ESO = (runtime->period_size * 2) - 1;
 }

 spin_unlock_irq(&trident->reg_lock);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_spdif_hw_params
  
   Description: Set the hardware parameters for the spdif device.
  
   Parameters:  substream  - PCM substream class
hw_params  - hardware parameters
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_spdif_hw_params(struct snd_pcm_substream *substream,
           struct snd_pcm_hw_params *hw_params)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 unsigned int old_bits = 0, change = 0;
 int err;

 err = snd_trident_allocate_pcm_mem(substream, hw_params);
 if (err < 0)
  return err;

 if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
  err = snd_trident_allocate_evoice(substream, hw_params);
  if (err < 0)
   return err;
 }

 /* prepare SPDIF channel */
 spin_lock_irq(&trident->reg_lock);
 old_bits = trident->spdif_pcm_bits;
 if (old_bits & IEC958_AES0_PROFESSIONAL)
  trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
 else
  trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
 if (params_rate(hw_params) >= 48000) {
  trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz
  trident->spdif_pcm_bits |=
   trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
    IEC958_AES0_PRO_FS_48000 :
    (IEC958_AES3_CON_FS_48000 << 24);
 }
 else if (params_rate(hw_params) >= 44100) {
  trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz
  trident->spdif_pcm_bits |=
   trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
    IEC958_AES0_PRO_FS_44100 :
    (IEC958_AES3_CON_FS_44100 << 24);
 }
 else {
  trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz
  trident->spdif_pcm_bits |=
   trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
    IEC958_AES0_PRO_FS_32000 :
    (IEC958_AES3_CON_FS_32000 << 24);
 }
 change = old_bits != trident->spdif_pcm_bits;
 spin_unlock_irq(&trident->reg_lock);

 if (change)
  snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);

 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_spdif_prepare
  
   Description: Prepare SPDIF device for playback.
  
   Parameters:  substream  - PCM substream class
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_spdif_prepare(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident_voice *evoice = voice->extra;
 struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
 unsigned int RESO, LBAO;
 unsigned int temp;

 spin_lock_irq(&trident->reg_lock);

 if (trident->device != TRIDENT_DEVICE_ID_SI7018) {

  /* set delta (rate) value */
  voice->Delta = snd_trident_convert_rate(runtime->rate);
  voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);

  /* set Loop Back Address */
  LBAO = runtime->dma_addr;
  if (voice->memblk)
   voice->LBA = voice->memblk->offset;
  else
   voice->LBA = LBAO;

  voice->isync = 1;
  voice->isync3 = 1;
  voice->isync_mark = runtime->period_size;
  voice->isync_max = runtime->buffer_size;

  /* set target ESO for channel */
  RESO = runtime->buffer_size - 1;
  voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;

  /* set ctrl mode */
  voice->CTRL = snd_trident_control_mode(substream);

  voice->FMC = 3;
  voice->RVol = 0x7f;
  voice->CVol = 0x7f;
  voice->GVSel = 1;
  voice->Pan = 0x7f;
  voice->Vol = 0x3ff;
  voice->EC = 0;
  voice->CSO = 0;
  voice->Alpha = 0;
  voice->FMS = 0;
  voice->Attribute = 0;

  /* prepare surrogate IRQ channel */
  snd_trident_write_voice_regs(trident, voice);

  outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO));
  outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2));
  outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA));
  outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO));
  outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2));

  /* set SPDIF setting */
  outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
  outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));

 } else { /* SiS */
 
  /* set delta (rate) value */
  voice->Delta = 0x800;
  voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);

  /* set Loop Begin Address */
  if (voice->memblk)
   voice->LBA = voice->memblk->offset;
  else
   voice->LBA = runtime->dma_addr;

  voice->CSO = 0;
  voice->ESO = runtime->buffer_size - 1; /* in samples */
  voice->CTRL = snd_trident_control_mode(substream);
  voice->FMC = 3;
  voice->GVSel = 1;
  voice->EC = 0;
  voice->Alpha = 0;
  voice->FMS = 0;
  voice->Vol = mix->vol;
  voice->RVol = mix->rvol;
  voice->CVol = mix->cvol;
  voice->Pan = mix->pan;
  voice->Attribute = (1<<(30-16))|(7<<(26-16))|
       (0<<(24-16))|(0<<(19-16));

  snd_trident_write_voice_regs(trident, voice);

  if (evoice != NULL) {
   evoice->Delta = voice->Delta;
   evoice->spurious_threshold = voice->spurious_threshold;
   evoice->LBA = voice->LBA;
   evoice->CSO = 0;
   evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
   evoice->CTRL = voice->CTRL;
   evoice->FMC = 3;
   evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
   evoice->EC = 0;
   evoice->Alpha = 0;
   evoice->FMS = 0;
   evoice->Vol = 0x3ff;   /* mute */
   evoice->RVol = evoice->CVol = 0x7f; /* mute */
   evoice->Pan = 0x7f;   /* mute */
   evoice->Attribute = 0;
   snd_trident_write_voice_regs(trident, evoice);
   evoice->isync2 = 1;
   evoice->isync_mark = runtime->period_size;
   evoice->ESO = (runtime->period_size * 2) - 1;
  }

  outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
  temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
  temp &= ~(1<<19);
  outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR));
  temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
  temp |= SPDIF_EN;
  outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
 }

 spin_unlock_irq(&trident->reg_lock);

 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_trigger
  
   Description: Start/stop devices
  
   Parameters:  substream  - PCM substream class
    cmd - trigger command (STOP, GO)
  
   Returns:     Error status
  
  ---------------------------------------------------------------------------*/


static int snd_trident_trigger(struct snd_pcm_substream *substream,
          int cmd)
        
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_substream *s;
 unsigned int what, whati, capture_flag, spdif_flag;
 struct snd_trident_voice *voice, *evoice;
 unsigned int val, go;

 switch (cmd) {
 case SNDRV_PCM_TRIGGER_START:
 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 case SNDRV_PCM_TRIGGER_RESUME:
  go = 1;
  break;
 case SNDRV_PCM_TRIGGER_STOP:
 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 case SNDRV_PCM_TRIGGER_SUSPEND:
  go = 0;
  break;
 default:
  return -EINVAL;
 }
 what = whati = capture_flag = spdif_flag = 0;
 spin_lock(&trident->reg_lock);
 val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
 snd_pcm_group_for_each_entry(s, substream) {
  if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) {
   voice = s->runtime->private_data;
   evoice = voice->extra;
   what |= 1 << (voice->number & 0x1f);
   if (evoice == NULL) {
    whati |= 1 << (voice->number & 0x1f);
   } else {
    what |= 1 << (evoice->number & 0x1f);
    whati |= 1 << (evoice->number & 0x1f);
    if (go)
     evoice->stimer = val;
   }
   if (go) {
    voice->running = 1;
    voice->stimer = val;
   } else {
    voice->running = 0;
   }
   snd_pcm_trigger_done(s, substream);
   if (voice->capture)
    capture_flag = 1;
   if (voice->spdif)
    spdif_flag = 1;
  }
 }
 if (spdif_flag) {
  if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
   outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
   val = trident->spdif_pcm_ctrl;
   if (!go)
    val &= ~(0x28);
   outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
  } else {
   outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
   val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
   outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
  }
 }
 if (!go)
  outl(what, TRID_REG(trident, T4D_STOP_B));
 val = inl(TRID_REG(trident, T4D_AINTEN_B));
 if (go) {
  val |= whati;
 } else {
  val &= ~whati;
 }
 outl(val, TRID_REG(trident, T4D_AINTEN_B));
 if (go) {
  outl(what, TRID_REG(trident, T4D_START_B));

  if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
   outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
 } else {
  if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
   outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
 }
 spin_unlock(&trident->reg_lock);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_playback_pointer
  
   Description: This routine return the playback position
                
   Parameters: substream  - PCM substream class

   Returns:     position of buffer
  
  ---------------------------------------------------------------------------*/


static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 unsigned int cso;

 if (!voice->running)
  return 0;

 spin_lock(&trident->reg_lock);

 outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));

 if (trident->device != TRIDENT_DEVICE_ID_NX) {
  cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
 } else {  // ID_4DWAVE_NX
  cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
 }

 spin_unlock(&trident->reg_lock);

 if (cso >= runtime->buffer_size)
  cso = 0;

 return cso;
}

/*---------------------------------------------------------------------------
   snd_trident_capture_pointer
  
   Description: This routine return the capture position
                
   Parameters:   pcm1    - PCM device class

   Returns:     position of buffer
  
  ---------------------------------------------------------------------------*/


static snd_pcm_uframes_t snd_trident_capture_pointer(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 unsigned int result;

 if (!voice->running)
  return 0;

 result = inw(TRID_REG(trident, T4D_SBBL_SBCL));
 if (runtime->channels > 1)
  result >>= 1;
 if (result > 0)
  result = runtime->buffer_size - result;

 return result;
}

/*---------------------------------------------------------------------------
   snd_trident_spdif_pointer
  
   Description: This routine return the SPDIF playback position
                
   Parameters: substream  - PCM substream class

   Returns:     position of buffer
  
  ---------------------------------------------------------------------------*/


static snd_pcm_uframes_t snd_trident_spdif_pointer(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;
 unsigned int result;

 if (!voice->running)
  return 0;

 result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;

 return result;
}

/*
 *  Playback support device description
 */


static const struct snd_pcm_hardware snd_trident_playback =
{
 .info =   (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
     SNDRV_PCM_INFO_BLOCK_TRANSFER |
     SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
     SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
 .formats =  (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
     SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
 .rates =  SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
 .rate_min =  4000,
 .rate_max =  48000,
 .channels_min =  1,
 .channels_max =  2,
 .buffer_bytes_max = (256*1024),
 .period_bytes_min = 64,
 .period_bytes_max = (256*1024),
 .periods_min =  1,
 .periods_max =  1024,
 .fifo_size =  0,
};

/*
 *  Capture support device description
 */


static const struct snd_pcm_hardware snd_trident_capture =
{
 .info =   (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
     SNDRV_PCM_INFO_BLOCK_TRANSFER |
     SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
     SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
 .formats =  (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
     SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
 .rates =  SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
 .rate_min =  4000,
 .rate_max =  48000,
 .channels_min =  1,
 .channels_max =  2,
 .buffer_bytes_max = (128*1024),
 .period_bytes_min = 64,
 .period_bytes_max = (128*1024),
 .periods_min =  1,
 .periods_max =  1024,
 .fifo_size =  0,
};

/*
 *  Foldback capture support device description
 */


static const struct snd_pcm_hardware snd_trident_foldback =
{
 .info =   (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
     SNDRV_PCM_INFO_BLOCK_TRANSFER |
     SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
     SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
 .formats =  SNDRV_PCM_FMTBIT_S16_LE,
 .rates =  SNDRV_PCM_RATE_48000,
 .rate_min =  48000,
 .rate_max =  48000,
 .channels_min =  2,
 .channels_max =  2,
 .buffer_bytes_max = (128*1024),
 .period_bytes_min = 64,
 .period_bytes_max = (128*1024),
 .periods_min =  1,
 .periods_max =  1024,
 .fifo_size =  0,
};

/*
 *  SPDIF playback support device description
 */


static const struct snd_pcm_hardware snd_trident_spdif =
{
 .info =   (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
     SNDRV_PCM_INFO_BLOCK_TRANSFER |
     SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
     SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
 .formats =  SNDRV_PCM_FMTBIT_S16_LE,
 .rates =  (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
     SNDRV_PCM_RATE_48000),
 .rate_min =  32000,
 .rate_max =  48000,
 .channels_min =  2,
 .channels_max =  2,
 .buffer_bytes_max = (128*1024),
 .period_bytes_min = 64,
 .period_bytes_max = (128*1024),
 .periods_min =  1,
 .periods_max =  1024,
 .fifo_size =  0,
};

static const struct snd_pcm_hardware snd_trident_spdif_7018 =
{
 .info =   (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
     SNDRV_PCM_INFO_BLOCK_TRANSFER |
     SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
     SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
 .formats =  SNDRV_PCM_FMTBIT_S16_LE,
 .rates =  SNDRV_PCM_RATE_48000,
 .rate_min =  48000,
 .rate_max =  48000,
 .channels_min =  2,
 .channels_max =  2,
 .buffer_bytes_max = (128*1024),
 .period_bytes_min = 64,
 .period_bytes_max = (128*1024),
 .periods_min =  1,
 .periods_max =  1024,
 .fifo_size =  0,
};

static void snd_trident_pcm_free_substream(struct snd_pcm_runtime *runtime)
{
 struct snd_trident_voice *voice = runtime->private_data;
 struct snd_trident *trident;

 if (voice) {
  trident = voice->trident;
  snd_trident_free_voice(trident, voice);
 }
}

static int snd_trident_playback_open(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice;

 voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
 if (voice == NULL)
  return -EAGAIN;
 snd_trident_pcm_mixer_build(trident, voice, substream);
 voice->substream = substream;
 runtime->private_data = voice;
 runtime->private_free = snd_trident_pcm_free_substream;
 runtime->hw = snd_trident_playback;
 snd_pcm_set_sync(substream);
 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_playback_close
  
   Description: This routine will close the 4DWave playback device. For now 
                we will simply free the dma transfer buffer.
                
   Parameters: substream  - PCM substream class

  ---------------------------------------------------------------------------*/

static int snd_trident_playback_close(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_trident_voice *voice = runtime->private_data;

 snd_trident_pcm_mixer_free(trident, voice, substream);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_spdif_open
  
   Description: This routine will open the 4DWave SPDIF device.

   Parameters: substream  - PCM substream class

   Returns:     status  - success or failure flag
  
  ---------------------------------------------------------------------------*/


static int snd_trident_spdif_open(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_trident_voice *voice;
 struct snd_pcm_runtime *runtime = substream->runtime;
 
 voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
 if (voice == NULL)
  return -EAGAIN;
 voice->spdif = 1;
 voice->substream = substream;
 spin_lock_irq(&trident->reg_lock);
 trident->spdif_pcm_bits = trident->spdif_bits;
 spin_unlock_irq(&trident->reg_lock);

 runtime->private_data = voice;
 runtime->private_free = snd_trident_pcm_free_substream;
 if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
  runtime->hw = snd_trident_spdif;
 } else {
  runtime->hw = snd_trident_spdif_7018;
 }

 trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
 snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
         SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);

 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
 return 0;
}


/*---------------------------------------------------------------------------
   snd_trident_spdif_close
  
   Description: This routine will close the 4DWave SPDIF device.
                
   Parameters: substream  - PCM substream class

  ---------------------------------------------------------------------------*/


static int snd_trident_spdif_close(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 unsigned int temp;

 spin_lock_irq(&trident->reg_lock);
 // restore default SPDIF setting
 if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
  outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
  outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
 } else {
  outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
  temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
  if (trident->spdif_ctrl) {
   temp |= SPDIF_EN;
  } else {
   temp &= ~SPDIF_EN;
  }
  outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
 }
 spin_unlock_irq(&trident->reg_lock);
 trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
 snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
         SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_capture_open
  
   Description: This routine will open the 4DWave capture device.

   Parameters: substream  - PCM substream class

   Returns:     status  - success or failure flag

  ---------------------------------------------------------------------------*/


static int snd_trident_capture_open(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_trident_voice *voice;
 struct snd_pcm_runtime *runtime = substream->runtime;

 voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
 if (voice == NULL)
  return -EAGAIN;
 voice->capture = 1;
 voice->substream = substream;
 runtime->private_data = voice;
 runtime->private_free = snd_trident_pcm_free_substream;
 runtime->hw = snd_trident_capture;
 snd_pcm_set_sync(substream);
 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_capture_close
  
   Description: This routine will close the 4DWave capture device. For now 
                we will simply free the dma transfer buffer.
                
   Parameters: substream  - PCM substream class

  ---------------------------------------------------------------------------*/

static int snd_trident_capture_close(struct snd_pcm_substream *substream)
{
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_foldback_open
  
   Description: This routine will open the 4DWave foldback capture device.

   Parameters: substream  - PCM substream class

   Returns:     status  - success or failure flag

  ---------------------------------------------------------------------------*/


static int snd_trident_foldback_open(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_trident_voice *voice;
 struct snd_pcm_runtime *runtime = substream->runtime;

 voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
 if (voice == NULL)
  return -EAGAIN;
 voice->foldback_chan = substream->number;
 voice->substream = substream;
 runtime->private_data = voice;
 runtime->private_free = snd_trident_pcm_free_substream;
 runtime->hw = snd_trident_foldback;
 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
 return 0;
}

/*---------------------------------------------------------------------------
   snd_trident_foldback_close
  
   Description: This routine will close the 4DWave foldback capture device. 
For now we will simply free the dma transfer buffer.
                
   Parameters: substream  - PCM substream class

  ---------------------------------------------------------------------------*/

static int snd_trident_foldback_close(struct snd_pcm_substream *substream)
{
 struct snd_trident *trident = snd_pcm_substream_chip(substream);
 struct snd_trident_voice *voice;
 struct snd_pcm_runtime *runtime = substream->runtime;
 voice = runtime->private_data;
 
 /* stop capture channel */
 spin_lock_irq(&trident->reg_lock);
 outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
 spin_unlock_irq(&trident->reg_lock);
 return 0;
}

/*---------------------------------------------------------------------------
   PCM operations
  ---------------------------------------------------------------------------*/


static const struct snd_pcm_ops snd_trident_playback_ops = {
 .open =  snd_trident_playback_open,
 .close = snd_trident_playback_close,
 .hw_params = snd_trident_hw_params,
 .hw_free = snd_trident_hw_free,
 .prepare = snd_trident_playback_prepare,
 .trigger = snd_trident_trigger,
 .pointer = snd_trident_playback_pointer,
};

static const struct snd_pcm_ops snd_trident_nx_playback_ops = {
 .open =  snd_trident_playback_open,
 .close = snd_trident_playback_close,
 .hw_params = snd_trident_hw_params,
 .hw_free = snd_trident_hw_free,
 .prepare = snd_trident_playback_prepare,
 .trigger = snd_trident_trigger,
 .pointer = snd_trident_playback_pointer,
};

static const struct snd_pcm_ops snd_trident_capture_ops = {
 .open =  snd_trident_capture_open,
 .close = snd_trident_capture_close,
 .hw_params = snd_trident_capture_hw_params,
 .hw_free = snd_trident_hw_free,
 .prepare = snd_trident_capture_prepare,
 .trigger = snd_trident_trigger,
 .pointer = snd_trident_capture_pointer,
};

static const struct snd_pcm_ops snd_trident_si7018_capture_ops = {
 .open =  snd_trident_capture_open,
 .close = snd_trident_capture_close,
 .hw_params = snd_trident_si7018_capture_hw_params,
 .hw_free = snd_trident_si7018_capture_hw_free,
 .prepare = snd_trident_si7018_capture_prepare,
 .trigger = snd_trident_trigger,
 .pointer = snd_trident_playback_pointer,
};

static const struct snd_pcm_ops snd_trident_foldback_ops = {
 .open =  snd_trident_foldback_open,
 .close = snd_trident_foldback_close,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.