Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  io.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 *                   Lee Revell <rlrevell@joe-job.com>
 *                   James Courtier-Dutton <James@superbug.co.uk>
 *                   Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
 *                   Creative Labs, Inc.
 *
 *  Routines for control of EMU10K1 chips
 */


#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <linux/delay.h>
#include <linux/export.h>
#include "p17v.h"

static inline bool check_ptr_reg(struct snd_emu10k1 *emu, unsigned int reg)
{
 if (snd_BUG_ON(!emu))
  return false;
 if (snd_BUG_ON(reg & (emu->audigy ? (0xffff0000 & ~A_PTR_ADDRESS_MASK)
       : (0xffff0000 & ~PTR_ADDRESS_MASK))))
  return false;
 if (snd_BUG_ON(reg & 0x0000ffff & ~PTR_CHANNELNUM_MASK))
  return false;
 return true;
}

unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
{
 unsigned long flags;
 unsigned int regptr, val;
 unsigned int mask;

 regptr = (reg << 16) | chn;
 if (!check_ptr_reg(emu, regptr))
  return 0;

 spin_lock_irqsave(&emu->emu_lock, flags);
 outl(regptr, emu->port + PTR);
 val = inl(emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);

 if (reg & 0xff000000) {
  unsigned char size, offset;
  
  size = (reg >> 24) & 0x3f;
  offset = (reg >> 16) & 0x1f;
  mask = (1 << size) - 1;
  
  return (val >> offset) & mask;
 } else {
  return val;
 }
}

EXPORT_SYMBOL(snd_emu10k1_ptr_read);

void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
{
 unsigned int regptr;
 unsigned long flags;
 unsigned int mask;

 regptr = (reg << 16) | chn;
 if (!check_ptr_reg(emu, regptr))
  return;

 if (reg & 0xff000000) {
  unsigned char size, offset;

  size = (reg >> 24) & 0x3f;
  offset = (reg >> 16) & 0x1f;
  mask = (1 << size) - 1;
  if (snd_BUG_ON(data & ~mask))
   return;
  mask <<= offset;
  data <<= offset;

  spin_lock_irqsave(&emu->emu_lock, flags);
  outl(regptr, emu->port + PTR);
  data |= inl(emu->port + DATA) & ~mask;
 } else {
  spin_lock_irqsave(&emu->emu_lock, flags);
  outl(regptr, emu->port + PTR);
 }
 outl(data, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

EXPORT_SYMBOL(snd_emu10k1_ptr_write);

void snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 *emu, unsigned int chn, ...)
{
 va_list va;
 u32 addr_mask;
 unsigned long flags;

 if (snd_BUG_ON(!emu))
  return;
 if (snd_BUG_ON(chn & ~PTR_CHANNELNUM_MASK))
  return;
 addr_mask = ~((emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK) >> 16);

 va_start(va, chn);
 spin_lock_irqsave(&emu->emu_lock, flags);
 for (;;) {
  u32 data;
  u32 reg = va_arg(va, u32);
  if (reg == REGLIST_END)
   break;
  data = va_arg(va, u32);
  if (snd_BUG_ON(reg & addr_mask))  // Only raw registers supported here
   continue;
  outl((reg << 16) | chn, emu->port + PTR);
  outl(data, emu->port + DATA);
 }
 spin_unlock_irqrestore(&emu->emu_lock, flags);
 va_end(va);
}

EXPORT_SYMBOL(snd_emu10k1_ptr_write_multiple);

unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, 
       unsigned int reg, 
       unsigned int chn)
{
 unsigned long flags;
 unsigned int regptr, val;
  
 regptr = (reg << 16) | chn;

 spin_lock_irqsave(&emu->emu_lock, flags);
 outl(regptr, emu->port + PTR2);
 val = inl(emu->port + DATA2);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
 return val;
}

void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, 
       unsigned int reg, 
       unsigned int chn, 
       unsigned int data)
{
 unsigned int regptr;
 unsigned long flags;

 regptr = (reg << 16) | chn;

 spin_lock_irqsave(&emu->emu_lock, flags);
 outl(regptr, emu->port + PTR2);
 outl(data, emu->port + DATA2);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
       unsigned int data)
{
 unsigned int reset, set;
 unsigned int reg, tmp;
 int n, result;
 int err = 0;

 /* This function is not re-entrant, so protect against it. */
 spin_lock(&emu->spi_lock);
 if (emu->card_capabilities->ca0108_chip)
  reg = P17V_SPI;
 else {
  /* For other chip types the SPI register
 * is currently unknown. */

  err = 1;
  goto spi_write_exit;
 }
 if (data > 0xffff) {
  /* Only 16bit values allowed */
  err = 1;
  goto spi_write_exit;
 }

 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
 reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
 set = reset | 0x10000; /* Set xxx1xxxx */
 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
 snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
 result = 1;
 /* Wait for status bit to return to 0 */
 for (n = 0; n < 100; n++) {
  udelay(10);
  tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
  if (!(tmp & 0x10000)) {
   result = 0;
   break;
  }
 }
 if (result) {
  /* Timed out */
  err = 1;
  goto spi_write_exit;
 }
 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
 err = 0;
spi_write_exit:
 spin_unlock(&emu->spi_lock);
 return err;
}

/* The ADC does not support i2c read, so only write is implemented */
int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
    u32 reg,
    u32 value)
{
 u32 tmp;
 int timeout = 0;
 int status;
 int retry;
 int err = 0;

 if ((reg > 0x7f) || (value > 0x1ff)) {
  dev_err(emu->card->dev, "i2c_write: invalid values.\n");
  return -EINVAL;
 }

 /* This function is not re-entrant, so protect against it. */
 spin_lock(&emu->i2c_lock);

 tmp = reg << 25 | value << 16;

 /* This controls the I2C connected to the WM8775 ADC Codec */
 snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
 tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */

 for (retry = 0; retry < 10; retry++) {
  /* Send the data to i2c */
  tmp = 0;
  tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
  snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);

  /* Wait till the transaction ends */
  while (1) {
   mdelay(1);
   status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
   timeout++;
   if ((status & I2C_A_ADC_START) == 0)
    break;

   if (timeout > 1000) {
    dev_warn(emu->card->dev,
        "emu10k1:I2C:timeout status=0x%x\n",
        status);
    break;
   }
  }
  //Read back and see if the transaction is successful
  if ((status & I2C_A_ADC_ABORT) == 0)
   break;
 }

 if (retry == 10) {
  dev_err(emu->card->dev, "Writing to ADC failed!\n");
  dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
   status, reg, value);
  /* dump_stack(); */
  err = -EINVAL;
 }
    
 spin_unlock(&emu->i2c_lock);
 return err;
}

static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
 if (snd_BUG_ON(reg > 0x3f))
  return;
 reg += 0x40; /* 0x40 upwards are registers. */
 if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
  return;
 outw(reg, emu->port + A_GPIO);
 udelay(10);
 outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
 udelay(10);
 outw(value, emu->port + A_GPIO);
 udelay(10);
 outw(value | 0x80 , emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
 udelay(10);
}

void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
 if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
  return;
 snd_emu1010_fpga_write_locked(emu, reg, value);
}

void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
 snd_emu1010_fpga_lock(emu);
 snd_emu1010_fpga_write_locked(emu, reg, value);
 snd_emu1010_fpga_unlock(emu);
}

void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
{
 // The higest input pin is used as the designated interrupt trigger,
 // so it needs to be masked out.
 // But note that any other input pin change will also cause an IRQ,
 // so using this function often causes an IRQ as a side effect.
 u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;

 if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
  return;
 if (snd_BUG_ON(reg > 0x3f))
  return;
 reg += 0x40; /* 0x40 upwards are registers. */
 outw(reg, emu->port + A_GPIO);
 udelay(10);
 outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
 udelay(10);
 *value = ((inw(emu->port + A_GPIO) >> 8) & mask);
}

/* Each Destination has one and only one Source,
 * but one Source can feed any number of Destinations simultaneously.
 */

void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
{
 if (snd_BUG_ON(dst & ~0x71f))
  return;
 if (snd_BUG_ON(src & ~0x71f))
  return;
 snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
 snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
 snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8);
 snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f);
}

u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
{
 u32 hi, lo;

 if (snd_BUG_ON(dst & ~0x71f))
  return 0;
 snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
 snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
 snd_emu1010_fpga_read(emu, EMU_HANA_SRCHI, &hi);
 snd_emu1010_fpga_read(emu, EMU_HANA_SRCLO, &lo);
 return (hi << 8) | lo;
}

int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src)
{
 u32 reg_lo, reg_hi, value, value2;

 switch (src) {
 case EMU_HANA_WCLOCK_HANA_SPDIF_IN:
  snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
  if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
   return 0;
  reg_lo = EMU_HANA_WC_SPDIF_LO;
  reg_hi = EMU_HANA_WC_SPDIF_HI;
  break;
 case EMU_HANA_WCLOCK_HANA_ADAT_IN:
  reg_lo = EMU_HANA_WC_ADAT_LO;
  reg_hi = EMU_HANA_WC_ADAT_HI;
  break;
 case EMU_HANA_WCLOCK_SYNC_BNC:
  reg_lo = EMU_HANA_WC_BNC_LO;
  reg_hi = EMU_HANA_WC_BNC_HI;
  break;
 case EMU_HANA_WCLOCK_2ND_HANA:
  reg_lo = EMU_HANA2_WC_SPDIF_LO;
  reg_hi = EMU_HANA2_WC_SPDIF_HI;
  break;
 default:
  return 0;
 }
 snd_emu1010_fpga_read(emu, reg_hi, &value);
 snd_emu1010_fpga_read(emu, reg_lo, &value2);
 // FIXME: The /4 is valid for 0404b, but contradicts all other info.
 return 0x1770000 / 4 / (((value << 5) | value2) + 1);
}

void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
{
 int clock;
 u32 leds;

 switch (emu->emu1010.wclock) {
 case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X:
  clock = 44100;
  leds = EMU_HANA_DOCK_LEDS_2_44K;
  break;
 case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X:
  clock = 48000;
  leds = EMU_HANA_DOCK_LEDS_2_48K;
  break;
 default:
  clock = snd_emu1010_get_raw_rate(
    emu, emu->emu1010.wclock & EMU_HANA_WCLOCK_SRC_MASK);
  // The raw rate reading is rather coarse (it cannot accurately
  // represent 44.1 kHz) and fluctuates slightly. Luckily, the
  // clock comes from digital inputs, which use standardized rates.
  // So we round to the closest standard rate and ignore discrepancies.
  if (clock < 46000) {
   clock = 44100;
   leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K;
  } else {
   clock = 48000;
   leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K;
  }
  break;
 }
 emu->emu1010.word_clock = clock;

 // FIXME: this should probably represent the AND of all currently
 // used sources' lock status. But we don't know how to get that ...
 leds |= EMU_HANA_DOCK_LEDS_2_LOCK;

 snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
}

void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock,
         const struct firmware *fw_entry)
{
 __always_unused u16 write_post;

 // On E-MU 1010 rev1 the FPGA is a Xilinx Spartan IIE XC2S50E.
 // On E-MU 0404b it is a Xilinx Spartan III XC3S50.
 // The wiring is as follows:
 // GPO7 -> FPGA input & 1K resistor -> FPGA /PGMN <- FPGA output
 //   In normal operation, the active low reset line is held up by
 //   an FPGA output, while the GPO pin performs its duty as control
 //   register access strobe signal. Writing the respective bit to
 //   EMU_HANA_FPGA_CONFIG puts the FPGA output into high-Z mode, at
 //   which point the GPO pin can control the reset line through the
 //   resistor.
 // GPO6 -> FPGA CCLK & FPGA input
 // GPO5 -> FPGA DIN (dual function)

 // If the FPGA is already programmed, return it to programming mode
 snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
          dock ? EMU_HANA_FPGA_CONFIG_AUDIODOCK :
          EMU_HANA_FPGA_CONFIG_HANA);

 // Assert reset line for 100uS
 outw(0x00, emu->port + A_GPIO);
 write_post = inw(emu->port + A_GPIO);
 udelay(100);
 outw(0x80, emu->port + A_GPIO);
 write_post = inw(emu->port + A_GPIO);
 udelay(100);  // Allow FPGA memory to clean

 // Upload the netlist. Keep reset line high!
 for (int n = 0; n < fw_entry->size; n++) {
  u8 value = fw_entry->data[n];
  for (int i = 0; i < 8; i++) {
   u16 reg = 0x80;
   if (value & 1)
    reg |= 0x20;
   value >>= 1;
   outw(reg, emu->port + A_GPIO);
   write_post = inw(emu->port + A_GPIO);
   outw(reg | 0x40, emu->port + A_GPIO);
   write_post = inw(emu->port + A_GPIO);
  }
 }

 // After programming, set GPIO bit 4 high again.
 // This appears to be a config word that the rev1 Hana
 // firmware reads; weird things happen without this.
 outw(0x10, emu->port + A_GPIO);
 write_post = inw(emu->port + A_GPIO);
}

void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
 unsigned long flags;
 unsigned int enable;

 spin_lock_irqsave(&emu->emu_lock, flags);
 enable = inl(emu->port + INTE) | intrenb;
 outl(enable, emu->port + INTE);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
 unsigned long flags;
 unsigned int enable;

 spin_lock_irqsave(&emu->emu_lock, flags);
 enable = inl(emu->port + INTE) & ~intrenb;
 outl(enable, emu->port + INTE);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;
 unsigned int val;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(CLIEH << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val |= 1 << (voicenum - 32);
 } else {
  outl(CLIEL << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val |= 1 << voicenum;
 }
 outl(val, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;
 unsigned int val;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(CLIEH << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val &= ~(1 << (voicenum - 32));
 } else {
  outl(CLIEL << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val &= ~(1 << voicenum);
 }
 outl(val, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(CLIPH << 16, emu->port + PTR);
  voicenum = 1 << (voicenum - 32);
 } else {
  outl(CLIPL << 16, emu->port + PTR);
  voicenum = 1 << voicenum;
 }
 outl(voicenum, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;
 unsigned int val;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(HLIEH << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val |= 1 << (voicenum - 32);
 } else {
  outl(HLIEL << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val |= 1 << voicenum;
 }
 outl(val, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;
 unsigned int val;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(HLIEH << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val &= ~(1 << (voicenum - 32));
 } else {
  outl(HLIEL << 16, emu->port + PTR);
  val = inl(emu->port + DATA);
  val &= ~(1 << voicenum);
 }
 outl(val, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(HLIPH << 16, emu->port + PTR);
  voicenum = 1 << (voicenum - 32);
 } else {
  outl(HLIPL << 16, emu->port + PTR);
  voicenum = 1 << voicenum;
 }
 outl(voicenum, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

#if 0
void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;
 unsigned int sol;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(SOLEH << 16, emu->port + PTR);
  sol = inl(emu->port + DATA);
  sol |= 1 << (voicenum - 32);
 } else {
  outl(SOLEL << 16, emu->port + PTR);
  sol = inl(emu->port + DATA);
  sol |= 1 << voicenum;
 }
 outl(sol, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
{
 unsigned long flags;
 unsigned int sol;

 spin_lock_irqsave(&emu->emu_lock, flags);
 if (voicenum >= 32) {
  outl(SOLEH << 16, emu->port + PTR);
  sol = inl(emu->port + DATA);
  sol &= ~(1 << (voicenum - 32));
 } else {
  outl(SOLEL << 16, emu->port + PTR);
  sol = inl(emu->port + DATA);
  sol &= ~(1 << voicenum);
 }
 outl(sol, emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}
#endif

void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
{
 unsigned long flags;

 spin_lock_irqsave(&emu->emu_lock, flags);
 outl(SOLEL << 16, emu->port + PTR);
 outl(inl(emu->port + DATA) | (u32)voices, emu->port + DATA);
 outl(SOLEH << 16, emu->port + PTR);
 outl(inl(emu->port + DATA) | (u32)(voices >> 32), emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
{
 unsigned long flags;

 spin_lock_irqsave(&emu->emu_lock, flags);
 outl(SOLEL << 16, emu->port + PTR);
 outl(inl(emu->port + DATA) & (u32)~voices, emu->port + DATA);
 outl(SOLEH << 16, emu->port + PTR);
 outl(inl(emu->port + DATA) & (u32)(~voices >> 32), emu->port + DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

int snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices)
{
 unsigned long flags;
 u32 soll, solh;
 int ret = -EIO;

 spin_lock_irqsave(&emu->emu_lock, flags);

 outl(SOLEL << 16, emu->port + PTR);
 soll = inl(emu->port + DATA);
 outl(SOLEH << 16, emu->port + PTR);
 solh = inl(emu->port + DATA);

 soll &= (u32)~voices;
 solh &= (u32)(~voices >> 32);

 for (int tries = 0; tries < 1000; tries++) {
  const u32 quart = 1U << (REG_SIZE(WC_CURRENTCHANNEL) - 2);
  // First we wait for the third quarter of the sample cycle ...
  u32 wc = inl(emu->port + WC);
  u32 cc = REG_VAL_GET(WC_CURRENTCHANNEL, wc);
  if (cc >= quart * 2 && cc < quart * 3) {
   // ... and release the low voices, while the high ones are serviced.
   outl(SOLEL << 16, emu->port + PTR);
   outl(soll, emu->port + DATA);
   // Then we wait for the first quarter of the next sample cycle ...
   for (; tries < 1000; tries++) {
    cc = REG_VAL_GET(WC_CURRENTCHANNEL, inl(emu->port + WC));
    if (cc < quart)
     goto good;
    // We will block for 10+ us with interrupts disabled. This is
    // not nice at all, but necessary for reasonable reliability.
    udelay(1);
   }
   break;
  good:
   // ... and release the high voices, while the low ones are serviced.
   outl(SOLEH << 16, emu->port + PTR);
   outl(solh, emu->port + DATA);
   // Finally we verify that nothing interfered in fact.
   if (REG_VAL_GET(WC_SAMPLECOUNTER, inl(emu->port + WC)) ==
       ((REG_VAL_GET(WC_SAMPLECOUNTER, wc) + 1) & REG_MASK0(WC_SAMPLECOUNTER))) {
    ret = 0;
   } else {
    ret = -EAGAIN;
   }
   break;
  }
  // Don't block for too long
  spin_unlock_irqrestore(&emu->emu_lock, flags);
  udelay(1);
  spin_lock_irqsave(&emu->emu_lock, flags);
 }

 spin_unlock_irqrestore(&emu->emu_lock, flags);
 return ret;
}

void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
{
 volatile unsigned count;
 unsigned int newtime = 0, curtime;

 curtime = inl(emu->port + WC) >> 6;
 while (wait-- > 0) {
  count = 0;
  while (count++ < 16384) {
   newtime = inl(emu->port + WC) >> 6;
   if (newtime != curtime)
    break;
  }
  if (count > 16384)
   break;
  curtime = newtime;
 }
}

unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
 struct snd_emu10k1 *emu = ac97->private_data;
 unsigned long flags;
 unsigned short val;

 spin_lock_irqsave(&emu->emu_lock, flags);
 outb(reg, emu->port + AC97ADDRESS);
 val = inw(emu->port + AC97DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
 return val;
}

void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
{
 struct snd_emu10k1 *emu = ac97->private_data;
 unsigned long flags;

 spin_lock_irqsave(&emu->emu_lock, flags);
 outb(reg, emu->port + AC97ADDRESS);
 outw(data, emu->port + AC97DATA);
 spin_unlock_irqrestore(&emu->emu_lock, flags);
}

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

¤ Dauer der Verarbeitung: 0.2 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge