162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Maintained by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Originated by audio@tridentmicro.com 562306a36Sopenharmony_ci * Fri Feb 19 15:55:28 MST 1999 662306a36Sopenharmony_ci * Routines for control of Trident 4DWave (DX and NX) chip 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * BUGS: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * TODO: 1162306a36Sopenharmony_ci * --- 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net> 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/pci.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/vmalloc.h> 2262306a36Sopenharmony_ci#include <linux/gameport.h> 2362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2462306a36Sopenharmony_ci#include <linux/export.h> 2562306a36Sopenharmony_ci#include <linux/io.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <sound/core.h> 2862306a36Sopenharmony_ci#include <sound/info.h> 2962306a36Sopenharmony_ci#include <sound/control.h> 3062306a36Sopenharmony_ci#include <sound/tlv.h> 3162306a36Sopenharmony_ci#include "trident.h" 3262306a36Sopenharmony_ci#include <sound/asoundef.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int snd_trident_pcm_mixer_build(struct snd_trident *trident, 3562306a36Sopenharmony_ci struct snd_trident_voice * voice, 3662306a36Sopenharmony_ci struct snd_pcm_substream *substream); 3762306a36Sopenharmony_cistatic int snd_trident_pcm_mixer_free(struct snd_trident *trident, 3862306a36Sopenharmony_ci struct snd_trident_voice * voice, 3962306a36Sopenharmony_ci struct snd_pcm_substream *substream); 4062306a36Sopenharmony_cistatic irqreturn_t snd_trident_interrupt(int irq, void *dev_id); 4162306a36Sopenharmony_cistatic int snd_trident_sis_reset(struct snd_trident *trident); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void snd_trident_clear_voices(struct snd_trident * trident, 4462306a36Sopenharmony_ci unsigned short v_min, unsigned short v_max); 4562306a36Sopenharmony_cistatic void snd_trident_free(struct snd_card *card); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * common I/O routines 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#if 0 5362306a36Sopenharmony_cistatic void snd_trident_print_voice_regs(struct snd_trident *trident, int voice) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci unsigned int val, tmp; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci dev_dbg(trident->card->dev, "Trident voice %i:\n", voice); 5862306a36Sopenharmony_ci outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR)); 5962306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_LBA)); 6062306a36Sopenharmony_ci dev_dbg(trident->card->dev, "LBA: 0x%x\n", val); 6162306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); 6262306a36Sopenharmony_ci dev_dbg(trident->card->dev, "GVSel: %i\n", val >> 31); 6362306a36Sopenharmony_ci dev_dbg(trident->card->dev, "Pan: 0x%x\n", (val >> 24) & 0x7f); 6462306a36Sopenharmony_ci dev_dbg(trident->card->dev, "Vol: 0x%x\n", (val >> 16) & 0xff); 6562306a36Sopenharmony_ci dev_dbg(trident->card->dev, "CTRL: 0x%x\n", (val >> 12) & 0x0f); 6662306a36Sopenharmony_ci dev_dbg(trident->card->dev, "EC: 0x%x\n", val & 0x0fff); 6762306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_NX) { 6862306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS)); 6962306a36Sopenharmony_ci dev_dbg(trident->card->dev, "CSO: 0x%x\n", val >> 16); 7062306a36Sopenharmony_ci dev_dbg(trident->card->dev, "Alpha: 0x%x\n", (val >> 4) & 0x0fff); 7162306a36Sopenharmony_ci dev_dbg(trident->card->dev, "FMS: 0x%x\n", val & 0x0f); 7262306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_DX_ESO_DELTA)); 7362306a36Sopenharmony_ci dev_dbg(trident->card->dev, "ESO: 0x%x\n", val >> 16); 7462306a36Sopenharmony_ci dev_dbg(trident->card->dev, "Delta: 0x%x\n", val & 0xffff); 7562306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); 7662306a36Sopenharmony_ci } else { // TRIDENT_DEVICE_ID_NX 7762306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_NX_DELTA_CSO)); 7862306a36Sopenharmony_ci tmp = (val >> 24) & 0xff; 7962306a36Sopenharmony_ci dev_dbg(trident->card->dev, "CSO: 0x%x\n", val & 0x00ffffff); 8062306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_NX_DELTA_ESO)); 8162306a36Sopenharmony_ci tmp |= (val >> 16) & 0xff00; 8262306a36Sopenharmony_ci dev_dbg(trident->card->dev, "Delta: 0x%x\n", tmp); 8362306a36Sopenharmony_ci dev_dbg(trident->card->dev, "ESO: 0x%x\n", val & 0x00ffffff); 8462306a36Sopenharmony_ci val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL)); 8562306a36Sopenharmony_ci dev_dbg(trident->card->dev, "Alpha: 0x%x\n", val >> 20); 8662306a36Sopenharmony_ci dev_dbg(trident->card->dev, "FMS: 0x%x\n", (val >> 16) & 0x0f); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci dev_dbg(trident->card->dev, "FMC: 0x%x\n", (val >> 14) & 3); 8962306a36Sopenharmony_ci dev_dbg(trident->card->dev, "RVol: 0x%x\n", (val >> 7) & 0x7f); 9062306a36Sopenharmony_ci dev_dbg(trident->card->dev, "CVol: 0x%x\n", val & 0x7f); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci#endif 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 9562306a36Sopenharmony_ci unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci Description: This routine will do all of the reading from the external 9862306a36Sopenharmony_ci CODEC (AC97). 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci Parameters: ac97 - ac97 codec structure 10162306a36Sopenharmony_ci reg - CODEC register index, from AC97 Hal. 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci returns: 16 bit value read from the AC97. 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 10662306a36Sopenharmony_cistatic unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci unsigned int data = 0, treg; 10962306a36Sopenharmony_ci unsigned short count = 0xffff; 11062306a36Sopenharmony_ci unsigned long flags; 11162306a36Sopenharmony_ci struct snd_trident *trident = ac97->private_data; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci spin_lock_irqsave(&trident->reg_lock, flags); 11462306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_DX) { 11562306a36Sopenharmony_ci data = (DX_AC97_BUSY_READ | (reg & 0x000000ff)); 11662306a36Sopenharmony_ci outl(data, TRID_REG(trident, DX_ACR1_AC97_R)); 11762306a36Sopenharmony_ci do { 11862306a36Sopenharmony_ci data = inl(TRID_REG(trident, DX_ACR1_AC97_R)); 11962306a36Sopenharmony_ci if ((data & DX_AC97_BUSY_READ) == 0) 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci } while (--count); 12262306a36Sopenharmony_ci } else if (trident->device == TRIDENT_DEVICE_ID_NX) { 12362306a36Sopenharmony_ci data = (NX_AC97_BUSY_READ | (reg & 0x000000ff)); 12462306a36Sopenharmony_ci treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY; 12562306a36Sopenharmony_ci outl(data, TRID_REG(trident, treg)); 12662306a36Sopenharmony_ci do { 12762306a36Sopenharmony_ci data = inl(TRID_REG(trident, treg)); 12862306a36Sopenharmony_ci if ((data & 0x00000C00) == 0) 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci } while (--count); 13162306a36Sopenharmony_ci } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 13262306a36Sopenharmony_ci data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); 13362306a36Sopenharmony_ci if (ac97->num == 1) 13462306a36Sopenharmony_ci data |= SI_AC97_SECONDARY; 13562306a36Sopenharmony_ci outl(data, TRID_REG(trident, SI_AC97_READ)); 13662306a36Sopenharmony_ci do { 13762306a36Sopenharmony_ci data = inl(TRID_REG(trident, SI_AC97_READ)); 13862306a36Sopenharmony_ci if ((data & (SI_AC97_BUSY_READ)) == 0) 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci } while (--count); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (count == 0 && !trident->ac97_detect) { 14462306a36Sopenharmony_ci dev_err(trident->card->dev, 14562306a36Sopenharmony_ci "ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", 14662306a36Sopenharmony_ci reg, data); 14762306a36Sopenharmony_ci data = 0; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->reg_lock, flags); 15162306a36Sopenharmony_ci return ((unsigned short) (data >> 16)); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 15562306a36Sopenharmony_ci void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg, 15662306a36Sopenharmony_ci unsigned short wdata) 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci Description: This routine will do all of the writing to the external 15962306a36Sopenharmony_ci CODEC (AC97). 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci Parameters: ac97 - ac97 codec structure 16262306a36Sopenharmony_ci reg - CODEC register index, from AC97 Hal. 16362306a36Sopenharmony_ci data - Lower 16 bits are the data to write to CODEC. 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci returns: TRUE if everything went ok, else FALSE. 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 16862306a36Sopenharmony_cistatic void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg, 16962306a36Sopenharmony_ci unsigned short wdata) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned int address, data; 17262306a36Sopenharmony_ci unsigned short count = 0xffff; 17362306a36Sopenharmony_ci unsigned long flags; 17462306a36Sopenharmony_ci struct snd_trident *trident = ac97->private_data; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci data = ((unsigned long) wdata) << 16; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci spin_lock_irqsave(&trident->reg_lock, flags); 17962306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_DX) { 18062306a36Sopenharmony_ci address = DX_ACR0_AC97_W; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* read AC-97 write register status */ 18362306a36Sopenharmony_ci do { 18462306a36Sopenharmony_ci if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0) 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci } while (--count); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff)); 18962306a36Sopenharmony_ci } else if (trident->device == TRIDENT_DEVICE_ID_NX) { 19062306a36Sopenharmony_ci address = NX_ACR1_AC97_W; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* read AC-97 write register status */ 19362306a36Sopenharmony_ci do { 19462306a36Sopenharmony_ci if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0) 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } while (--count); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff)); 19962306a36Sopenharmony_ci } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 20062306a36Sopenharmony_ci address = SI_AC97_WRITE; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* read AC-97 write register status */ 20362306a36Sopenharmony_ci do { 20462306a36Sopenharmony_ci if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0) 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci } while (--count); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); 20962306a36Sopenharmony_ci if (ac97->num == 1) 21062306a36Sopenharmony_ci data |= SI_AC97_SECONDARY; 21162306a36Sopenharmony_ci } else { 21262306a36Sopenharmony_ci address = 0; /* keep GCC happy */ 21362306a36Sopenharmony_ci count = 0; /* return */ 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (count == 0) { 21762306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->reg_lock, flags); 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci outl(data, TRID_REG(trident, address)); 22162306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->reg_lock, flags); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 22562306a36Sopenharmony_ci void snd_trident_enable_eso(struct snd_trident *trident) 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci Description: This routine will enable end of loop interrupts. 22862306a36Sopenharmony_ci End of loop interrupts will occur when a running 22962306a36Sopenharmony_ci channel reaches ESO. 23062306a36Sopenharmony_ci Also enables middle of loop interrupts. 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void snd_trident_enable_eso(struct snd_trident * trident) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci unsigned int val; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci val = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); 24162306a36Sopenharmony_ci val |= ENDLP_IE; 24262306a36Sopenharmony_ci val |= MIDLP_IE; 24362306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) 24462306a36Sopenharmony_ci val |= BANK_B_EN; 24562306a36Sopenharmony_ci outl(val, TRID_REG(trident, T4D_LFO_GC_CIR)); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 24962306a36Sopenharmony_ci void snd_trident_disable_eso(struct snd_trident *trident) 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci Description: This routine will disable end of loop interrupts. 25262306a36Sopenharmony_ci End of loop interrupts will occur when a running 25362306a36Sopenharmony_ci channel reaches ESO. 25462306a36Sopenharmony_ci Also disables middle of loop interrupts. 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci Parameters: 25762306a36Sopenharmony_ci trident - pointer to target device class for 4DWave. 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci returns: TRUE if everything went ok, else FALSE. 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void snd_trident_disable_eso(struct snd_trident * trident) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned int tmp; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); 26862306a36Sopenharmony_ci tmp &= ~ENDLP_IE; 26962306a36Sopenharmony_ci tmp &= ~MIDLP_IE; 27062306a36Sopenharmony_ci outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR)); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 27462306a36Sopenharmony_ci void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice) 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci Description: Start a voice, any channel 0 thru 63. 27762306a36Sopenharmony_ci This routine automatically handles the fact that there are 27862306a36Sopenharmony_ci more than 32 channels available. 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci Parameters : voice - Voice number 0 thru n. 28162306a36Sopenharmony_ci trident - pointer to target device class for 4DWave. 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci Return Value: None. 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_civoid snd_trident_start_voice(struct snd_trident * trident, unsigned int voice) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci unsigned int mask = 1 << (voice & 0x1f); 29062306a36Sopenharmony_ci unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci outl(mask, TRID_REG(trident, reg)); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_trident_start_voice); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 29862306a36Sopenharmony_ci void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice) 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci Description: Stop a voice, any channel 0 thru 63. 30162306a36Sopenharmony_ci This routine automatically handles the fact that there are 30262306a36Sopenharmony_ci more than 32 channels available. 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci Parameters : voice - Voice number 0 thru n. 30562306a36Sopenharmony_ci trident - pointer to target device class for 4DWave. 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci Return Value: None. 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_civoid snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci unsigned int mask = 1 << (voice & 0x1f); 31462306a36Sopenharmony_ci unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci outl(mask, TRID_REG(trident, reg)); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_trident_stop_voice); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 32262306a36Sopenharmony_ci int snd_trident_allocate_pcm_channel(struct snd_trident *trident) 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci Description: Allocate hardware channel in Bank B (32-63). 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci Parameters : trident - pointer to target device class for 4DWave. 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci Return Value: hardware channel - 32-63 or -1 when no channel is available 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int snd_trident_allocate_pcm_channel(struct snd_trident * trident) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int idx; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (trident->ChanPCMcnt >= trident->ChanPCM) 33762306a36Sopenharmony_ci return -1; 33862306a36Sopenharmony_ci for (idx = 31; idx >= 0; idx--) { 33962306a36Sopenharmony_ci if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) { 34062306a36Sopenharmony_ci trident->ChanMap[T4D_BANK_B] |= 1 << idx; 34162306a36Sopenharmony_ci trident->ChanPCMcnt++; 34262306a36Sopenharmony_ci return idx + 32; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci return -1; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 34962306a36Sopenharmony_ci void snd_trident_free_pcm_channel(int channel) 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci Description: Free hardware channel in Bank B (32-63) 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci Parameters : trident - pointer to target device class for 4DWave. 35462306a36Sopenharmony_ci channel - hardware channel number 0-63 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci Return Value: none 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void snd_trident_free_pcm_channel(struct snd_trident *trident, int channel) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci if (channel < 32 || channel > 63) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci channel &= 0x1f; 36562306a36Sopenharmony_ci if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) { 36662306a36Sopenharmony_ci trident->ChanMap[T4D_BANK_B] &= ~(1 << channel); 36762306a36Sopenharmony_ci trident->ChanPCMcnt--; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 37262306a36Sopenharmony_ci unsigned int snd_trident_allocate_synth_channel(void) 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci Description: Allocate hardware channel in Bank A (0-31). 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci Parameters : trident - pointer to target device class for 4DWave. 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci Return Value: hardware channel - 0-31 or -1 when no channel is available 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int snd_trident_allocate_synth_channel(struct snd_trident * trident) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int idx; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (idx = 31; idx >= 0; idx--) { 38762306a36Sopenharmony_ci if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) { 38862306a36Sopenharmony_ci trident->ChanMap[T4D_BANK_A] |= 1 << idx; 38962306a36Sopenharmony_ci trident->synth.ChanSynthCount++; 39062306a36Sopenharmony_ci return idx; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci return -1; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 39762306a36Sopenharmony_ci void snd_trident_free_synth_channel( int channel ) 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci Description: Free hardware channel in Bank B (0-31). 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci Parameters : trident - pointer to target device class for 4DWave. 40262306a36Sopenharmony_ci channel - hardware channel number 0-63 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci Return Value: none 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic void snd_trident_free_synth_channel(struct snd_trident *trident, int channel) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci if (channel < 0 || channel > 31) 41162306a36Sopenharmony_ci return; 41262306a36Sopenharmony_ci channel &= 0x1f; 41362306a36Sopenharmony_ci if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) { 41462306a36Sopenharmony_ci trident->ChanMap[T4D_BANK_A] &= ~(1 << channel); 41562306a36Sopenharmony_ci trident->synth.ChanSynthCount--; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 42062306a36Sopenharmony_ci snd_trident_write_voice_regs 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci Description: This routine will complete and write the 5 hardware channel 42362306a36Sopenharmony_ci registers to hardware. 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 42662306a36Sopenharmony_ci voice - synthesizer voice structure 42762306a36Sopenharmony_ci Each register field. 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_civoid snd_trident_write_voice_regs(struct snd_trident * trident, 43262306a36Sopenharmony_ci struct snd_trident_voice * voice) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci unsigned int FmcRvolCvol; 43562306a36Sopenharmony_ci unsigned int regs[5]; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci regs[1] = voice->LBA; 43862306a36Sopenharmony_ci regs[4] = (voice->GVSel << 31) | 43962306a36Sopenharmony_ci ((voice->Pan & 0x0000007f) << 24) | 44062306a36Sopenharmony_ci ((voice->CTRL & 0x0000000f) << 12); 44162306a36Sopenharmony_ci FmcRvolCvol = ((voice->FMC & 3) << 14) | 44262306a36Sopenharmony_ci ((voice->RVol & 0x7f) << 7) | 44362306a36Sopenharmony_ci (voice->CVol & 0x7f); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci switch (trident->device) { 44662306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_SI7018: 44762306a36Sopenharmony_ci regs[4] |= voice->number > 31 ? 44862306a36Sopenharmony_ci (voice->Vol & 0x000003ff) : 44962306a36Sopenharmony_ci ((voice->Vol & 0x00003fc) << (16-2)) | 45062306a36Sopenharmony_ci (voice->EC & 0x00000fff); 45162306a36Sopenharmony_ci regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | 45262306a36Sopenharmony_ci (voice->FMS & 0x0000000f); 45362306a36Sopenharmony_ci regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); 45462306a36Sopenharmony_ci regs[3] = (voice->Attribute << 16) | FmcRvolCvol; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_DX: 45762306a36Sopenharmony_ci regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | 45862306a36Sopenharmony_ci (voice->EC & 0x00000fff); 45962306a36Sopenharmony_ci regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | 46062306a36Sopenharmony_ci (voice->FMS & 0x0000000f); 46162306a36Sopenharmony_ci regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); 46262306a36Sopenharmony_ci regs[3] = FmcRvolCvol; 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_NX: 46562306a36Sopenharmony_ci regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | 46662306a36Sopenharmony_ci (voice->EC & 0x00000fff); 46762306a36Sopenharmony_ci regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff); 46862306a36Sopenharmony_ci regs[2] = ((voice->Delta << 16) & 0xff000000) | 46962306a36Sopenharmony_ci (voice->ESO & 0x00ffffff); 47062306a36Sopenharmony_ci regs[3] = (voice->Alpha << 20) | 47162306a36Sopenharmony_ci ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci default: 47462306a36Sopenharmony_ci snd_BUG(); 47562306a36Sopenharmony_ci return; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 47962306a36Sopenharmony_ci outl(regs[0], TRID_REG(trident, CH_START + 0)); 48062306a36Sopenharmony_ci outl(regs[1], TRID_REG(trident, CH_START + 4)); 48162306a36Sopenharmony_ci outl(regs[2], TRID_REG(trident, CH_START + 8)); 48262306a36Sopenharmony_ci outl(regs[3], TRID_REG(trident, CH_START + 12)); 48362306a36Sopenharmony_ci outl(regs[4], TRID_REG(trident, CH_START + 16)); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci#if 0 48662306a36Sopenharmony_ci dev_dbg(trident->card->dev, "written %i channel:\n", voice->number); 48762306a36Sopenharmony_ci dev_dbg(trident->card->dev, " regs[0] = 0x%x/0x%x\n", 48862306a36Sopenharmony_ci regs[0], inl(TRID_REG(trident, CH_START + 0))); 48962306a36Sopenharmony_ci dev_dbg(trident->card->dev, " regs[1] = 0x%x/0x%x\n", 49062306a36Sopenharmony_ci regs[1], inl(TRID_REG(trident, CH_START + 4))); 49162306a36Sopenharmony_ci dev_dbg(trident->card->dev, " regs[2] = 0x%x/0x%x\n", 49262306a36Sopenharmony_ci regs[2], inl(TRID_REG(trident, CH_START + 8))); 49362306a36Sopenharmony_ci dev_dbg(trident->card->dev, " regs[3] = 0x%x/0x%x\n", 49462306a36Sopenharmony_ci regs[3], inl(TRID_REG(trident, CH_START + 12))); 49562306a36Sopenharmony_ci dev_dbg(trident->card->dev, " regs[4] = 0x%x/0x%x\n", 49662306a36Sopenharmony_ci regs[4], inl(TRID_REG(trident, CH_START + 16))); 49762306a36Sopenharmony_ci#endif 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_trident_write_voice_regs); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 50362306a36Sopenharmony_ci snd_trident_write_cso_reg 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci Description: This routine will write the new CSO offset 50662306a36Sopenharmony_ci register to hardware. 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 50962306a36Sopenharmony_ci voice - synthesizer voice structure 51062306a36Sopenharmony_ci CSO - new CSO value 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void snd_trident_write_cso_reg(struct snd_trident * trident, 51562306a36Sopenharmony_ci struct snd_trident_voice * voice, 51662306a36Sopenharmony_ci unsigned int CSO) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci voice->CSO = CSO; 51962306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 52062306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_NX) { 52162306a36Sopenharmony_ci outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci outl((voice->Delta << 24) | 52462306a36Sopenharmony_ci (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO)); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 52962306a36Sopenharmony_ci snd_trident_write_eso_reg 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci Description: This routine will write the new ESO offset 53262306a36Sopenharmony_ci register to hardware. 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 53562306a36Sopenharmony_ci voice - synthesizer voice structure 53662306a36Sopenharmony_ci ESO - new ESO value 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void snd_trident_write_eso_reg(struct snd_trident * trident, 54162306a36Sopenharmony_ci struct snd_trident_voice * voice, 54262306a36Sopenharmony_ci unsigned int ESO) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci voice->ESO = ESO; 54562306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 54662306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_NX) { 54762306a36Sopenharmony_ci outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2); 54862306a36Sopenharmony_ci } else { 54962306a36Sopenharmony_ci outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff), 55062306a36Sopenharmony_ci TRID_REG(trident, CH_NX_DELTA_ESO)); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 55562306a36Sopenharmony_ci snd_trident_write_vol_reg 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci Description: This routine will write the new voice volume 55862306a36Sopenharmony_ci register to hardware. 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 56162306a36Sopenharmony_ci voice - synthesizer voice structure 56262306a36Sopenharmony_ci Vol - new voice volume 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void snd_trident_write_vol_reg(struct snd_trident * trident, 56762306a36Sopenharmony_ci struct snd_trident_voice * voice, 56862306a36Sopenharmony_ci unsigned int Vol) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci voice->Vol = Vol; 57162306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 57262306a36Sopenharmony_ci switch (trident->device) { 57362306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_DX: 57462306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_NX: 57562306a36Sopenharmony_ci outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2)); 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_SI7018: 57862306a36Sopenharmony_ci /* dev_dbg(trident->card->dev, "voice->Vol = 0x%x\n", voice->Vol); */ 57962306a36Sopenharmony_ci outw((voice->CTRL << 12) | voice->Vol, 58062306a36Sopenharmony_ci TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 58662306a36Sopenharmony_ci snd_trident_write_pan_reg 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci Description: This routine will write the new voice pan 58962306a36Sopenharmony_ci register to hardware. 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 59262306a36Sopenharmony_ci voice - synthesizer voice structure 59362306a36Sopenharmony_ci Pan - new pan value 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void snd_trident_write_pan_reg(struct snd_trident * trident, 59862306a36Sopenharmony_ci struct snd_trident_voice * voice, 59962306a36Sopenharmony_ci unsigned int Pan) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci voice->Pan = Pan; 60262306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 60362306a36Sopenharmony_ci outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), 60462306a36Sopenharmony_ci TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3)); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 60862306a36Sopenharmony_ci snd_trident_write_rvol_reg 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci Description: This routine will write the new reverb volume 61162306a36Sopenharmony_ci register to hardware. 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 61462306a36Sopenharmony_ci voice - synthesizer voice structure 61562306a36Sopenharmony_ci RVol - new reverb volume 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic void snd_trident_write_rvol_reg(struct snd_trident * trident, 62062306a36Sopenharmony_ci struct snd_trident_voice * voice, 62162306a36Sopenharmony_ci unsigned int RVol) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci voice->RVol = RVol; 62462306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 62562306a36Sopenharmony_ci outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | 62662306a36Sopenharmony_ci (voice->CVol & 0x007f), 62762306a36Sopenharmony_ci TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? 62862306a36Sopenharmony_ci CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 63262306a36Sopenharmony_ci snd_trident_write_cvol_reg 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci Description: This routine will write the new chorus volume 63562306a36Sopenharmony_ci register to hardware. 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 63862306a36Sopenharmony_ci voice - synthesizer voice structure 63962306a36Sopenharmony_ci CVol - new chorus volume 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void snd_trident_write_cvol_reg(struct snd_trident * trident, 64462306a36Sopenharmony_ci struct snd_trident_voice * voice, 64562306a36Sopenharmony_ci unsigned int CVol) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci voice->CVol = CVol; 64862306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 64962306a36Sopenharmony_ci outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | 65062306a36Sopenharmony_ci (voice->CVol & 0x007f), 65162306a36Sopenharmony_ci TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? 65262306a36Sopenharmony_ci CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 65662306a36Sopenharmony_ci snd_trident_convert_rate 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci Description: This routine converts rate in HZ to hardware delta value. 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 66162306a36Sopenharmony_ci rate - Real or Virtual channel number. 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci Returns: Delta value. 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 66662306a36Sopenharmony_cistatic unsigned int snd_trident_convert_rate(unsigned int rate) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci unsigned int delta; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci // We special case 44100 and 8000 since rounding with the equation 67162306a36Sopenharmony_ci // does not give us an accurate enough value. For 11025 and 22050 67262306a36Sopenharmony_ci // the equation gives us the best answer. All other frequencies will 67362306a36Sopenharmony_ci // also use the equation. JDW 67462306a36Sopenharmony_ci if (rate == 44100) 67562306a36Sopenharmony_ci delta = 0xeb3; 67662306a36Sopenharmony_ci else if (rate == 8000) 67762306a36Sopenharmony_ci delta = 0x2ab; 67862306a36Sopenharmony_ci else if (rate == 48000) 67962306a36Sopenharmony_ci delta = 0x1000; 68062306a36Sopenharmony_ci else 68162306a36Sopenharmony_ci delta = DIV_ROUND_CLOSEST(rate << 12, 48000) & 0x0000ffff; 68262306a36Sopenharmony_ci return delta; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 68662306a36Sopenharmony_ci snd_trident_convert_adc_rate 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci Description: This routine converts rate in HZ to hardware delta value. 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 69162306a36Sopenharmony_ci rate - Real or Virtual channel number. 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci Returns: Delta value. 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 69662306a36Sopenharmony_cistatic unsigned int snd_trident_convert_adc_rate(unsigned int rate) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci unsigned int delta; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci // We special case 44100 and 8000 since rounding with the equation 70162306a36Sopenharmony_ci // does not give us an accurate enough value. For 11025 and 22050 70262306a36Sopenharmony_ci // the equation gives us the best answer. All other frequencies will 70362306a36Sopenharmony_ci // also use the equation. JDW 70462306a36Sopenharmony_ci if (rate == 44100) 70562306a36Sopenharmony_ci delta = 0x116a; 70662306a36Sopenharmony_ci else if (rate == 8000) 70762306a36Sopenharmony_ci delta = 0x6000; 70862306a36Sopenharmony_ci else if (rate == 48000) 70962306a36Sopenharmony_ci delta = 0x1000; 71062306a36Sopenharmony_ci else 71162306a36Sopenharmony_ci delta = ((48000 << 12) / rate) & 0x0000ffff; 71262306a36Sopenharmony_ci return delta; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 71662306a36Sopenharmony_ci snd_trident_spurious_threshold 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci Description: This routine converts rate in HZ to spurious threshold. 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 72162306a36Sopenharmony_ci rate - Real or Virtual channel number. 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci Returns: Delta value. 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 72662306a36Sopenharmony_cistatic unsigned int snd_trident_spurious_threshold(unsigned int rate, 72762306a36Sopenharmony_ci unsigned int period_size) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci unsigned int res = (rate * period_size) / 48000; 73062306a36Sopenharmony_ci if (res < 64) 73162306a36Sopenharmony_ci res = res / 2; 73262306a36Sopenharmony_ci else 73362306a36Sopenharmony_ci res -= 32; 73462306a36Sopenharmony_ci return res; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 73862306a36Sopenharmony_ci snd_trident_control_mode 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci Description: This routine returns a control mode for a PCM channel. 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 74362306a36Sopenharmony_ci substream - PCM substream 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci Returns: Control value. 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 74862306a36Sopenharmony_cistatic unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci unsigned int CTRL; 75162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* set ctrl mode 75462306a36Sopenharmony_ci CTRL default: 8-bit (unsigned) mono, loop mode enabled 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci CTRL = 0x00000001; 75762306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 75862306a36Sopenharmony_ci CTRL |= 0x00000008; // 16-bit data 75962306a36Sopenharmony_ci if (snd_pcm_format_signed(runtime->format)) 76062306a36Sopenharmony_ci CTRL |= 0x00000002; // signed data 76162306a36Sopenharmony_ci if (runtime->channels > 1) 76262306a36Sopenharmony_ci CTRL |= 0x00000004; // stereo data 76362306a36Sopenharmony_ci return CTRL; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci/* 76762306a36Sopenharmony_ci * PCM part 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 77162306a36Sopenharmony_ci snd_trident_allocate_pcm_mem 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci Description: Allocate PCM ring buffer for given substream 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci Parameters: substream - PCM substream class 77662306a36Sopenharmony_ci hw_params - hardware parameters 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci Returns: Error status 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic int snd_trident_allocate_pcm_mem(struct snd_pcm_substream *substream, 78362306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 78662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 78762306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (trident->tlb.entries) { 79062306a36Sopenharmony_ci if (runtime->buffer_changed) { 79162306a36Sopenharmony_ci if (voice->memblk) 79262306a36Sopenharmony_ci snd_trident_free_pages(trident, voice->memblk); 79362306a36Sopenharmony_ci voice->memblk = snd_trident_alloc_pages(trident, substream); 79462306a36Sopenharmony_ci if (voice->memblk == NULL) 79562306a36Sopenharmony_ci return -ENOMEM; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 80262306a36Sopenharmony_ci snd_trident_allocate_evoice 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci Description: Allocate extra voice as interrupt generator 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci Parameters: substream - PCM substream class 80762306a36Sopenharmony_ci hw_params - hardware parameters 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci Returns: Error status 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int snd_trident_allocate_evoice(struct snd_pcm_substream *substream, 81462306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 81762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 81862306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 81962306a36Sopenharmony_ci struct snd_trident_voice *evoice = voice->extra; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* voice management */ 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { 82462306a36Sopenharmony_ci if (evoice == NULL) { 82562306a36Sopenharmony_ci evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); 82662306a36Sopenharmony_ci if (evoice == NULL) 82762306a36Sopenharmony_ci return -ENOMEM; 82862306a36Sopenharmony_ci voice->extra = evoice; 82962306a36Sopenharmony_ci evoice->substream = substream; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci } else { 83262306a36Sopenharmony_ci if (evoice != NULL) { 83362306a36Sopenharmony_ci snd_trident_free_voice(trident, evoice); 83462306a36Sopenharmony_ci voice->extra = evoice = NULL; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 84262306a36Sopenharmony_ci snd_trident_hw_params 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci Description: Set the hardware parameters for the playback device. 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci Parameters: substream - PCM substream class 84762306a36Sopenharmony_ci hw_params - hardware parameters 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci Returns: Error status 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic int snd_trident_hw_params(struct snd_pcm_substream *substream, 85462306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci int err; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci err = snd_trident_allocate_pcm_mem(substream, hw_params); 85962306a36Sopenharmony_ci if (err >= 0) 86062306a36Sopenharmony_ci err = snd_trident_allocate_evoice(substream, hw_params); 86162306a36Sopenharmony_ci return err; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 86562306a36Sopenharmony_ci snd_trident_playback_hw_free 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci Description: Release the hardware resources for the playback device. 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci Parameters: substream - PCM substream class 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci Returns: Error status 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int snd_trident_hw_free(struct snd_pcm_substream *substream) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 87862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 87962306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 88062306a36Sopenharmony_ci struct snd_trident_voice *evoice = voice ? voice->extra : NULL; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (trident->tlb.entries) { 88362306a36Sopenharmony_ci if (voice && voice->memblk) { 88462306a36Sopenharmony_ci snd_trident_free_pages(trident, voice->memblk); 88562306a36Sopenharmony_ci voice->memblk = NULL; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci if (evoice != NULL) { 88962306a36Sopenharmony_ci snd_trident_free_voice(trident, evoice); 89062306a36Sopenharmony_ci voice->extra = NULL; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 89662306a36Sopenharmony_ci snd_trident_playback_prepare 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci Description: Prepare playback device for playback. 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci Parameters: substream - PCM substream class 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci Returns: Error status 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int snd_trident_playback_prepare(struct snd_pcm_substream *substream) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 90962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 91062306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 91162306a36Sopenharmony_ci struct snd_trident_voice *evoice = voice->extra; 91262306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number]; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* set delta (rate) value */ 91762306a36Sopenharmony_ci voice->Delta = snd_trident_convert_rate(runtime->rate); 91862306a36Sopenharmony_ci voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* set Loop Begin Address */ 92162306a36Sopenharmony_ci if (voice->memblk) 92262306a36Sopenharmony_ci voice->LBA = voice->memblk->offset; 92362306a36Sopenharmony_ci else 92462306a36Sopenharmony_ci voice->LBA = runtime->dma_addr; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci voice->CSO = 0; 92762306a36Sopenharmony_ci voice->ESO = runtime->buffer_size - 1; /* in samples */ 92862306a36Sopenharmony_ci voice->CTRL = snd_trident_control_mode(substream); 92962306a36Sopenharmony_ci voice->FMC = 3; 93062306a36Sopenharmony_ci voice->GVSel = 1; 93162306a36Sopenharmony_ci voice->EC = 0; 93262306a36Sopenharmony_ci voice->Alpha = 0; 93362306a36Sopenharmony_ci voice->FMS = 0; 93462306a36Sopenharmony_ci voice->Vol = mix->vol; 93562306a36Sopenharmony_ci voice->RVol = mix->rvol; 93662306a36Sopenharmony_ci voice->CVol = mix->cvol; 93762306a36Sopenharmony_ci voice->Pan = mix->pan; 93862306a36Sopenharmony_ci voice->Attribute = 0; 93962306a36Sopenharmony_ci#if 0 94062306a36Sopenharmony_ci voice->Attribute = (1<<(30-16))|(2<<(26-16))| 94162306a36Sopenharmony_ci (0<<(24-16))|(0x1f<<(19-16)); 94262306a36Sopenharmony_ci#else 94362306a36Sopenharmony_ci voice->Attribute = 0; 94462306a36Sopenharmony_ci#endif 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, voice); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (evoice != NULL) { 94962306a36Sopenharmony_ci evoice->Delta = voice->Delta; 95062306a36Sopenharmony_ci evoice->spurious_threshold = voice->spurious_threshold; 95162306a36Sopenharmony_ci evoice->LBA = voice->LBA; 95262306a36Sopenharmony_ci evoice->CSO = 0; 95362306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ 95462306a36Sopenharmony_ci evoice->CTRL = voice->CTRL; 95562306a36Sopenharmony_ci evoice->FMC = 3; 95662306a36Sopenharmony_ci evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; 95762306a36Sopenharmony_ci evoice->EC = 0; 95862306a36Sopenharmony_ci evoice->Alpha = 0; 95962306a36Sopenharmony_ci evoice->FMS = 0; 96062306a36Sopenharmony_ci evoice->Vol = 0x3ff; /* mute */ 96162306a36Sopenharmony_ci evoice->RVol = evoice->CVol = 0x7f; /* mute */ 96262306a36Sopenharmony_ci evoice->Pan = 0x7f; /* mute */ 96362306a36Sopenharmony_ci#if 0 96462306a36Sopenharmony_ci evoice->Attribute = (1<<(30-16))|(2<<(26-16))| 96562306a36Sopenharmony_ci (0<<(24-16))|(0x1f<<(19-16)); 96662306a36Sopenharmony_ci#else 96762306a36Sopenharmony_ci evoice->Attribute = 0; 96862306a36Sopenharmony_ci#endif 96962306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, evoice); 97062306a36Sopenharmony_ci evoice->isync2 = 1; 97162306a36Sopenharmony_ci evoice->isync_mark = runtime->period_size; 97262306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) - 1; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 98162306a36Sopenharmony_ci snd_trident_capture_hw_params 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci Description: Set the hardware parameters for the capture device. 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci Parameters: substream - PCM substream class 98662306a36Sopenharmony_ci hw_params - hardware parameters 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci Returns: Error status 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic int snd_trident_capture_hw_params(struct snd_pcm_substream *substream, 99362306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci return snd_trident_allocate_pcm_mem(substream, hw_params); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 99962306a36Sopenharmony_ci snd_trident_capture_prepare 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci Description: Prepare capture device for playback. 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci Parameters: substream - PCM substream class 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci Returns: Error status 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic int snd_trident_capture_prepare(struct snd_pcm_substream *substream) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 101262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 101362306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 101462306a36Sopenharmony_ci unsigned int val, ESO_bytes; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci // Initialize the channel and set channel Mode 101962306a36Sopenharmony_ci outb(0, TRID_REG(trident, LEGACY_DMAR15)); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci // Set DMA channel operation mode register 102262306a36Sopenharmony_ci outb(0x54, TRID_REG(trident, LEGACY_DMAR11)); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci // Set channel buffer Address, DMAR0 expects contiguous PCI memory area 102562306a36Sopenharmony_ci voice->LBA = runtime->dma_addr; 102662306a36Sopenharmony_ci outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); 102762306a36Sopenharmony_ci if (voice->memblk) 102862306a36Sopenharmony_ci voice->LBA = voice->memblk->offset; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci // set ESO 103162306a36Sopenharmony_ci ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1; 103262306a36Sopenharmony_ci outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6)); 103362306a36Sopenharmony_ci outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4)); 103462306a36Sopenharmony_ci ESO_bytes++; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci // Set channel sample rate, 4.12 format 103762306a36Sopenharmony_ci val = DIV_ROUND_CLOSEST(48000U << 12, runtime->rate); 103862306a36Sopenharmony_ci outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R)); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci // Set channel interrupt blk length 104162306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) { 104262306a36Sopenharmony_ci val = (unsigned short) ((ESO_bytes >> 1) - 1); 104362306a36Sopenharmony_ci } else { 104462306a36Sopenharmony_ci val = (unsigned short) (ESO_bytes - 1); 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL)); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci // Right now, set format and start to run captureing, 105062306a36Sopenharmony_ci // continuous run loop enable. 105162306a36Sopenharmony_ci trident->bDMAStart = 0x19; // 0001 1001b 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 105462306a36Sopenharmony_ci trident->bDMAStart |= 0x80; 105562306a36Sopenharmony_ci if (snd_pcm_format_signed(runtime->format)) 105662306a36Sopenharmony_ci trident->bDMAStart |= 0x20; 105762306a36Sopenharmony_ci if (runtime->channels > 1) 105862306a36Sopenharmony_ci trident->bDMAStart |= 0x40; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci // Prepare capture intr channel 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci voice->Delta = snd_trident_convert_rate(runtime->rate); 106362306a36Sopenharmony_ci voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); 106462306a36Sopenharmony_ci voice->isync = 1; 106562306a36Sopenharmony_ci voice->isync_mark = runtime->period_size; 106662306a36Sopenharmony_ci voice->isync_max = runtime->buffer_size; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci // Set voice parameters 106962306a36Sopenharmony_ci voice->CSO = 0; 107062306a36Sopenharmony_ci voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1; 107162306a36Sopenharmony_ci voice->CTRL = snd_trident_control_mode(substream); 107262306a36Sopenharmony_ci voice->FMC = 3; 107362306a36Sopenharmony_ci voice->RVol = 0x7f; 107462306a36Sopenharmony_ci voice->CVol = 0x7f; 107562306a36Sopenharmony_ci voice->GVSel = 1; 107662306a36Sopenharmony_ci voice->Pan = 0x7f; /* mute */ 107762306a36Sopenharmony_ci voice->Vol = 0x3ff; /* mute */ 107862306a36Sopenharmony_ci voice->EC = 0; 107962306a36Sopenharmony_ci voice->Alpha = 0; 108062306a36Sopenharmony_ci voice->FMS = 0; 108162306a36Sopenharmony_ci voice->Attribute = 0; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, voice); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 108662306a36Sopenharmony_ci return 0; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 109062306a36Sopenharmony_ci snd_trident_si7018_capture_hw_params 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci Description: Set the hardware parameters for the capture device. 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci Parameters: substream - PCM substream class 109562306a36Sopenharmony_ci hw_params - hardware parameters 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci Returns: Error status 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int snd_trident_si7018_capture_hw_params(struct snd_pcm_substream *substream, 110262306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci return snd_trident_allocate_evoice(substream, hw_params); 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 110862306a36Sopenharmony_ci snd_trident_si7018_capture_hw_free 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci Description: Release the hardware resources for the capture device. 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci Parameters: substream - PCM substream class 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci Returns: Error status 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int snd_trident_si7018_capture_hw_free(struct snd_pcm_substream *substream) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 112162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 112262306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 112362306a36Sopenharmony_ci struct snd_trident_voice *evoice = voice ? voice->extra : NULL; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (evoice != NULL) { 112662306a36Sopenharmony_ci snd_trident_free_voice(trident, evoice); 112762306a36Sopenharmony_ci voice->extra = NULL; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci return 0; 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 113362306a36Sopenharmony_ci snd_trident_si7018_capture_prepare 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci Description: Prepare capture device for playback. 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci Parameters: substream - PCM substream class 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci Returns: Error status 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substream) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 114662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 114762306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 114862306a36Sopenharmony_ci struct snd_trident_voice *evoice = voice->extra; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci voice->LBA = runtime->dma_addr; 115362306a36Sopenharmony_ci voice->Delta = snd_trident_convert_adc_rate(runtime->rate); 115462306a36Sopenharmony_ci voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci // Set voice parameters 115762306a36Sopenharmony_ci voice->CSO = 0; 115862306a36Sopenharmony_ci voice->ESO = runtime->buffer_size - 1; /* in samples */ 115962306a36Sopenharmony_ci voice->CTRL = snd_trident_control_mode(substream); 116062306a36Sopenharmony_ci voice->FMC = 0; 116162306a36Sopenharmony_ci voice->RVol = 0; 116262306a36Sopenharmony_ci voice->CVol = 0; 116362306a36Sopenharmony_ci voice->GVSel = 1; 116462306a36Sopenharmony_ci voice->Pan = T4D_DEFAULT_PCM_PAN; 116562306a36Sopenharmony_ci voice->Vol = 0; 116662306a36Sopenharmony_ci voice->EC = 0; 116762306a36Sopenharmony_ci voice->Alpha = 0; 116862306a36Sopenharmony_ci voice->FMS = 0; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci voice->Attribute = (2 << (30-16)) | 117162306a36Sopenharmony_ci (2 << (26-16)) | 117262306a36Sopenharmony_ci (2 << (24-16)) | 117362306a36Sopenharmony_ci (1 << (23-16)); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, voice); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (evoice != NULL) { 117862306a36Sopenharmony_ci evoice->Delta = snd_trident_convert_rate(runtime->rate); 117962306a36Sopenharmony_ci evoice->spurious_threshold = voice->spurious_threshold; 118062306a36Sopenharmony_ci evoice->LBA = voice->LBA; 118162306a36Sopenharmony_ci evoice->CSO = 0; 118262306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */ 118362306a36Sopenharmony_ci evoice->CTRL = voice->CTRL; 118462306a36Sopenharmony_ci evoice->FMC = 3; 118562306a36Sopenharmony_ci evoice->GVSel = 0; 118662306a36Sopenharmony_ci evoice->EC = 0; 118762306a36Sopenharmony_ci evoice->Alpha = 0; 118862306a36Sopenharmony_ci evoice->FMS = 0; 118962306a36Sopenharmony_ci evoice->Vol = 0x3ff; /* mute */ 119062306a36Sopenharmony_ci evoice->RVol = evoice->CVol = 0x7f; /* mute */ 119162306a36Sopenharmony_ci evoice->Pan = 0x7f; /* mute */ 119262306a36Sopenharmony_ci evoice->Attribute = 0; 119362306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, evoice); 119462306a36Sopenharmony_ci evoice->isync2 = 1; 119562306a36Sopenharmony_ci evoice->isync_mark = runtime->period_size; 119662306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) - 1; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 120062306a36Sopenharmony_ci return 0; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 120462306a36Sopenharmony_ci snd_trident_foldback_prepare 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci Description: Prepare foldback capture device for playback. 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci Parameters: substream - PCM substream class 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci Returns: Error status 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic int snd_trident_foldback_prepare(struct snd_pcm_substream *substream) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 121762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 121862306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 121962306a36Sopenharmony_ci struct snd_trident_voice *evoice = voice->extra; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci /* Set channel buffer Address */ 122462306a36Sopenharmony_ci if (voice->memblk) 122562306a36Sopenharmony_ci voice->LBA = voice->memblk->offset; 122662306a36Sopenharmony_ci else 122762306a36Sopenharmony_ci voice->LBA = runtime->dma_addr; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* set target ESO for channel */ 123062306a36Sopenharmony_ci voice->ESO = runtime->buffer_size - 1; /* in samples */ 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* set sample rate */ 123362306a36Sopenharmony_ci voice->Delta = 0x1000; 123462306a36Sopenharmony_ci voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci voice->CSO = 0; 123762306a36Sopenharmony_ci voice->CTRL = snd_trident_control_mode(substream); 123862306a36Sopenharmony_ci voice->FMC = 3; 123962306a36Sopenharmony_ci voice->RVol = 0x7f; 124062306a36Sopenharmony_ci voice->CVol = 0x7f; 124162306a36Sopenharmony_ci voice->GVSel = 1; 124262306a36Sopenharmony_ci voice->Pan = 0x7f; /* mute */ 124362306a36Sopenharmony_ci voice->Vol = 0x3ff; /* mute */ 124462306a36Sopenharmony_ci voice->EC = 0; 124562306a36Sopenharmony_ci voice->Alpha = 0; 124662306a36Sopenharmony_ci voice->FMS = 0; 124762306a36Sopenharmony_ci voice->Attribute = 0; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* set up capture channel */ 125062306a36Sopenharmony_ci outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan)); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, voice); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (evoice != NULL) { 125562306a36Sopenharmony_ci evoice->Delta = voice->Delta; 125662306a36Sopenharmony_ci evoice->spurious_threshold = voice->spurious_threshold; 125762306a36Sopenharmony_ci evoice->LBA = voice->LBA; 125862306a36Sopenharmony_ci evoice->CSO = 0; 125962306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ 126062306a36Sopenharmony_ci evoice->CTRL = voice->CTRL; 126162306a36Sopenharmony_ci evoice->FMC = 3; 126262306a36Sopenharmony_ci evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; 126362306a36Sopenharmony_ci evoice->EC = 0; 126462306a36Sopenharmony_ci evoice->Alpha = 0; 126562306a36Sopenharmony_ci evoice->FMS = 0; 126662306a36Sopenharmony_ci evoice->Vol = 0x3ff; /* mute */ 126762306a36Sopenharmony_ci evoice->RVol = evoice->CVol = 0x7f; /* mute */ 126862306a36Sopenharmony_ci evoice->Pan = 0x7f; /* mute */ 126962306a36Sopenharmony_ci evoice->Attribute = 0; 127062306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, evoice); 127162306a36Sopenharmony_ci evoice->isync2 = 1; 127262306a36Sopenharmony_ci evoice->isync_mark = runtime->period_size; 127362306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) - 1; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 127762306a36Sopenharmony_ci return 0; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 128162306a36Sopenharmony_ci snd_trident_spdif_hw_params 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci Description: Set the hardware parameters for the spdif device. 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci Parameters: substream - PCM substream class 128662306a36Sopenharmony_ci hw_params - hardware parameters 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci Returns: Error status 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_cistatic int snd_trident_spdif_hw_params(struct snd_pcm_substream *substream, 129362306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 129662306a36Sopenharmony_ci unsigned int old_bits = 0, change = 0; 129762306a36Sopenharmony_ci int err; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci err = snd_trident_allocate_pcm_mem(substream, hw_params); 130062306a36Sopenharmony_ci if (err < 0) 130162306a36Sopenharmony_ci return err; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 130462306a36Sopenharmony_ci err = snd_trident_allocate_evoice(substream, hw_params); 130562306a36Sopenharmony_ci if (err < 0) 130662306a36Sopenharmony_ci return err; 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci /* prepare SPDIF channel */ 131062306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 131162306a36Sopenharmony_ci old_bits = trident->spdif_pcm_bits; 131262306a36Sopenharmony_ci if (old_bits & IEC958_AES0_PROFESSIONAL) 131362306a36Sopenharmony_ci trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS; 131462306a36Sopenharmony_ci else 131562306a36Sopenharmony_ci trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24); 131662306a36Sopenharmony_ci if (params_rate(hw_params) >= 48000) { 131762306a36Sopenharmony_ci trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz 131862306a36Sopenharmony_ci trident->spdif_pcm_bits |= 131962306a36Sopenharmony_ci trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? 132062306a36Sopenharmony_ci IEC958_AES0_PRO_FS_48000 : 132162306a36Sopenharmony_ci (IEC958_AES3_CON_FS_48000 << 24); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci else if (params_rate(hw_params) >= 44100) { 132462306a36Sopenharmony_ci trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz 132562306a36Sopenharmony_ci trident->spdif_pcm_bits |= 132662306a36Sopenharmony_ci trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? 132762306a36Sopenharmony_ci IEC958_AES0_PRO_FS_44100 : 132862306a36Sopenharmony_ci (IEC958_AES3_CON_FS_44100 << 24); 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci else { 133162306a36Sopenharmony_ci trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz 133262306a36Sopenharmony_ci trident->spdif_pcm_bits |= 133362306a36Sopenharmony_ci trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? 133462306a36Sopenharmony_ci IEC958_AES0_PRO_FS_32000 : 133562306a36Sopenharmony_ci (IEC958_AES3_CON_FS_32000 << 24); 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci change = old_bits != trident->spdif_pcm_bits; 133862306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci if (change) 134162306a36Sopenharmony_ci snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci return 0; 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 134762306a36Sopenharmony_ci snd_trident_spdif_prepare 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci Description: Prepare SPDIF device for playback. 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci Parameters: substream - PCM substream class 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci Returns: Error status 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_cistatic int snd_trident_spdif_prepare(struct snd_pcm_substream *substream) 135862306a36Sopenharmony_ci{ 135962306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 136062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 136162306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 136262306a36Sopenharmony_ci struct snd_trident_voice *evoice = voice->extra; 136362306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number]; 136462306a36Sopenharmony_ci unsigned int RESO, LBAO; 136562306a36Sopenharmony_ci unsigned int temp; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci /* set delta (rate) value */ 137262306a36Sopenharmony_ci voice->Delta = snd_trident_convert_rate(runtime->rate); 137362306a36Sopenharmony_ci voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci /* set Loop Back Address */ 137662306a36Sopenharmony_ci LBAO = runtime->dma_addr; 137762306a36Sopenharmony_ci if (voice->memblk) 137862306a36Sopenharmony_ci voice->LBA = voice->memblk->offset; 137962306a36Sopenharmony_ci else 138062306a36Sopenharmony_ci voice->LBA = LBAO; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci voice->isync = 1; 138362306a36Sopenharmony_ci voice->isync3 = 1; 138462306a36Sopenharmony_ci voice->isync_mark = runtime->period_size; 138562306a36Sopenharmony_ci voice->isync_max = runtime->buffer_size; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci /* set target ESO for channel */ 138862306a36Sopenharmony_ci RESO = runtime->buffer_size - 1; 138962306a36Sopenharmony_ci voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci /* set ctrl mode */ 139262306a36Sopenharmony_ci voice->CTRL = snd_trident_control_mode(substream); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci voice->FMC = 3; 139562306a36Sopenharmony_ci voice->RVol = 0x7f; 139662306a36Sopenharmony_ci voice->CVol = 0x7f; 139762306a36Sopenharmony_ci voice->GVSel = 1; 139862306a36Sopenharmony_ci voice->Pan = 0x7f; 139962306a36Sopenharmony_ci voice->Vol = 0x3ff; 140062306a36Sopenharmony_ci voice->EC = 0; 140162306a36Sopenharmony_ci voice->CSO = 0; 140262306a36Sopenharmony_ci voice->Alpha = 0; 140362306a36Sopenharmony_ci voice->FMS = 0; 140462306a36Sopenharmony_ci voice->Attribute = 0; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci /* prepare surrogate IRQ channel */ 140762306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, voice); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO)); 141062306a36Sopenharmony_ci outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2)); 141162306a36Sopenharmony_ci outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA)); 141262306a36Sopenharmony_ci outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO)); 141362306a36Sopenharmony_ci outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2)); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* set SPDIF setting */ 141662306a36Sopenharmony_ci outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); 141762306a36Sopenharmony_ci outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci } else { /* SiS */ 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci /* set delta (rate) value */ 142262306a36Sopenharmony_ci voice->Delta = 0x800; 142362306a36Sopenharmony_ci voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci /* set Loop Begin Address */ 142662306a36Sopenharmony_ci if (voice->memblk) 142762306a36Sopenharmony_ci voice->LBA = voice->memblk->offset; 142862306a36Sopenharmony_ci else 142962306a36Sopenharmony_ci voice->LBA = runtime->dma_addr; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci voice->CSO = 0; 143262306a36Sopenharmony_ci voice->ESO = runtime->buffer_size - 1; /* in samples */ 143362306a36Sopenharmony_ci voice->CTRL = snd_trident_control_mode(substream); 143462306a36Sopenharmony_ci voice->FMC = 3; 143562306a36Sopenharmony_ci voice->GVSel = 1; 143662306a36Sopenharmony_ci voice->EC = 0; 143762306a36Sopenharmony_ci voice->Alpha = 0; 143862306a36Sopenharmony_ci voice->FMS = 0; 143962306a36Sopenharmony_ci voice->Vol = mix->vol; 144062306a36Sopenharmony_ci voice->RVol = mix->rvol; 144162306a36Sopenharmony_ci voice->CVol = mix->cvol; 144262306a36Sopenharmony_ci voice->Pan = mix->pan; 144362306a36Sopenharmony_ci voice->Attribute = (1<<(30-16))|(7<<(26-16))| 144462306a36Sopenharmony_ci (0<<(24-16))|(0<<(19-16)); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, voice); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (evoice != NULL) { 144962306a36Sopenharmony_ci evoice->Delta = voice->Delta; 145062306a36Sopenharmony_ci evoice->spurious_threshold = voice->spurious_threshold; 145162306a36Sopenharmony_ci evoice->LBA = voice->LBA; 145262306a36Sopenharmony_ci evoice->CSO = 0; 145362306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ 145462306a36Sopenharmony_ci evoice->CTRL = voice->CTRL; 145562306a36Sopenharmony_ci evoice->FMC = 3; 145662306a36Sopenharmony_ci evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; 145762306a36Sopenharmony_ci evoice->EC = 0; 145862306a36Sopenharmony_ci evoice->Alpha = 0; 145962306a36Sopenharmony_ci evoice->FMS = 0; 146062306a36Sopenharmony_ci evoice->Vol = 0x3ff; /* mute */ 146162306a36Sopenharmony_ci evoice->RVol = evoice->CVol = 0x7f; /* mute */ 146262306a36Sopenharmony_ci evoice->Pan = 0x7f; /* mute */ 146362306a36Sopenharmony_ci evoice->Attribute = 0; 146462306a36Sopenharmony_ci snd_trident_write_voice_regs(trident, evoice); 146562306a36Sopenharmony_ci evoice->isync2 = 1; 146662306a36Sopenharmony_ci evoice->isync_mark = runtime->period_size; 146762306a36Sopenharmony_ci evoice->ESO = (runtime->period_size * 2) - 1; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS)); 147162306a36Sopenharmony_ci temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); 147262306a36Sopenharmony_ci temp &= ~(1<<19); 147362306a36Sopenharmony_ci outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR)); 147462306a36Sopenharmony_ci temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 147562306a36Sopenharmony_ci temp |= SPDIF_EN; 147662306a36Sopenharmony_ci outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci return 0; 148262306a36Sopenharmony_ci} 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 148562306a36Sopenharmony_ci snd_trident_trigger 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci Description: Start/stop devices 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci Parameters: substream - PCM substream class 149062306a36Sopenharmony_ci cmd - trigger command (STOP, GO) 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci Returns: Error status 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic int snd_trident_trigger(struct snd_pcm_substream *substream, 149762306a36Sopenharmony_ci int cmd) 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 150162306a36Sopenharmony_ci struct snd_pcm_substream *s; 150262306a36Sopenharmony_ci unsigned int what, whati, capture_flag, spdif_flag; 150362306a36Sopenharmony_ci struct snd_trident_voice *voice, *evoice; 150462306a36Sopenharmony_ci unsigned int val, go; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci switch (cmd) { 150762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 150862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 150962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 151062306a36Sopenharmony_ci go = 1; 151162306a36Sopenharmony_ci break; 151262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 151362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 151462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 151562306a36Sopenharmony_ci go = 0; 151662306a36Sopenharmony_ci break; 151762306a36Sopenharmony_ci default: 151862306a36Sopenharmony_ci return -EINVAL; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci what = whati = capture_flag = spdif_flag = 0; 152162306a36Sopenharmony_ci spin_lock(&trident->reg_lock); 152262306a36Sopenharmony_ci val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; 152362306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 152462306a36Sopenharmony_ci if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) { 152562306a36Sopenharmony_ci voice = s->runtime->private_data; 152662306a36Sopenharmony_ci evoice = voice->extra; 152762306a36Sopenharmony_ci what |= 1 << (voice->number & 0x1f); 152862306a36Sopenharmony_ci if (evoice == NULL) { 152962306a36Sopenharmony_ci whati |= 1 << (voice->number & 0x1f); 153062306a36Sopenharmony_ci } else { 153162306a36Sopenharmony_ci what |= 1 << (evoice->number & 0x1f); 153262306a36Sopenharmony_ci whati |= 1 << (evoice->number & 0x1f); 153362306a36Sopenharmony_ci if (go) 153462306a36Sopenharmony_ci evoice->stimer = val; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci if (go) { 153762306a36Sopenharmony_ci voice->running = 1; 153862306a36Sopenharmony_ci voice->stimer = val; 153962306a36Sopenharmony_ci } else { 154062306a36Sopenharmony_ci voice->running = 0; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci snd_pcm_trigger_done(s, substream); 154362306a36Sopenharmony_ci if (voice->capture) 154462306a36Sopenharmony_ci capture_flag = 1; 154562306a36Sopenharmony_ci if (voice->spdif) 154662306a36Sopenharmony_ci spdif_flag = 1; 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci if (spdif_flag) { 155062306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 155162306a36Sopenharmony_ci outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); 155262306a36Sopenharmony_ci val = trident->spdif_pcm_ctrl; 155362306a36Sopenharmony_ci if (!go) 155462306a36Sopenharmony_ci val &= ~(0x28); 155562306a36Sopenharmony_ci outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); 155662306a36Sopenharmony_ci } else { 155762306a36Sopenharmony_ci outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS)); 155862306a36Sopenharmony_ci val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN; 155962306a36Sopenharmony_ci outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci if (!go) 156362306a36Sopenharmony_ci outl(what, TRID_REG(trident, T4D_STOP_B)); 156462306a36Sopenharmony_ci val = inl(TRID_REG(trident, T4D_AINTEN_B)); 156562306a36Sopenharmony_ci if (go) { 156662306a36Sopenharmony_ci val |= whati; 156762306a36Sopenharmony_ci } else { 156862306a36Sopenharmony_ci val &= ~whati; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci outl(val, TRID_REG(trident, T4D_AINTEN_B)); 157162306a36Sopenharmony_ci if (go) { 157262306a36Sopenharmony_ci outl(what, TRID_REG(trident, T4D_START_B)); 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) 157562306a36Sopenharmony_ci outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); 157662306a36Sopenharmony_ci } else { 157762306a36Sopenharmony_ci if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) 157862306a36Sopenharmony_ci outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci spin_unlock(&trident->reg_lock); 158162306a36Sopenharmony_ci return 0; 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 158562306a36Sopenharmony_ci snd_trident_playback_pointer 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci Description: This routine return the playback position 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci Parameters: substream - PCM substream class 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci Returns: position of buffer 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *substream) 159662306a36Sopenharmony_ci{ 159762306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 159862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 159962306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 160062306a36Sopenharmony_ci unsigned int cso; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (!voice->running) 160362306a36Sopenharmony_ci return 0; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci spin_lock(&trident->reg_lock); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_NX) { 161062306a36Sopenharmony_ci cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); 161162306a36Sopenharmony_ci } else { // ID_4DWAVE_NX 161262306a36Sopenharmony_ci cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci spin_unlock(&trident->reg_lock); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (cso >= runtime->buffer_size) 161862306a36Sopenharmony_ci cso = 0; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return cso; 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 162462306a36Sopenharmony_ci snd_trident_capture_pointer 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci Description: This routine return the capture position 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci Parameters: pcm1 - PCM device class 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci Returns: position of buffer 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_trident_capture_pointer(struct snd_pcm_substream *substream) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 163762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 163862306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 163962306a36Sopenharmony_ci unsigned int result; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci if (!voice->running) 164262306a36Sopenharmony_ci return 0; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci result = inw(TRID_REG(trident, T4D_SBBL_SBCL)); 164562306a36Sopenharmony_ci if (runtime->channels > 1) 164662306a36Sopenharmony_ci result >>= 1; 164762306a36Sopenharmony_ci if (result > 0) 164862306a36Sopenharmony_ci result = runtime->buffer_size - result; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci return result; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 165462306a36Sopenharmony_ci snd_trident_spdif_pointer 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci Description: This routine return the SPDIF playback position 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci Parameters: substream - PCM substream class 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci Returns: position of buffer 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_trident_spdif_pointer(struct snd_pcm_substream *substream) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 166762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 166862306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 166962306a36Sopenharmony_ci unsigned int result; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (!voice->running) 167262306a36Sopenharmony_ci return 0; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci return result; 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci/* 168062306a36Sopenharmony_ci * Playback support device description 168162306a36Sopenharmony_ci */ 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_playback = 168462306a36Sopenharmony_ci{ 168562306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 168662306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 168762306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | 168862306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), 168962306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 169062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), 169162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 169262306a36Sopenharmony_ci .rate_min = 4000, 169362306a36Sopenharmony_ci .rate_max = 48000, 169462306a36Sopenharmony_ci .channels_min = 1, 169562306a36Sopenharmony_ci .channels_max = 2, 169662306a36Sopenharmony_ci .buffer_bytes_max = (256*1024), 169762306a36Sopenharmony_ci .period_bytes_min = 64, 169862306a36Sopenharmony_ci .period_bytes_max = (256*1024), 169962306a36Sopenharmony_ci .periods_min = 1, 170062306a36Sopenharmony_ci .periods_max = 1024, 170162306a36Sopenharmony_ci .fifo_size = 0, 170262306a36Sopenharmony_ci}; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci/* 170562306a36Sopenharmony_ci * Capture support device description 170662306a36Sopenharmony_ci */ 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_capture = 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 171162306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 171262306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | 171362306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), 171462306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 171562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), 171662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 171762306a36Sopenharmony_ci .rate_min = 4000, 171862306a36Sopenharmony_ci .rate_max = 48000, 171962306a36Sopenharmony_ci .channels_min = 1, 172062306a36Sopenharmony_ci .channels_max = 2, 172162306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 172262306a36Sopenharmony_ci .period_bytes_min = 64, 172362306a36Sopenharmony_ci .period_bytes_max = (128*1024), 172462306a36Sopenharmony_ci .periods_min = 1, 172562306a36Sopenharmony_ci .periods_max = 1024, 172662306a36Sopenharmony_ci .fifo_size = 0, 172762306a36Sopenharmony_ci}; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci/* 173062306a36Sopenharmony_ci * Foldback capture support device description 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_foldback = 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 173662306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 173762306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | 173862306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), 173962306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 174062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 174162306a36Sopenharmony_ci .rate_min = 48000, 174262306a36Sopenharmony_ci .rate_max = 48000, 174362306a36Sopenharmony_ci .channels_min = 2, 174462306a36Sopenharmony_ci .channels_max = 2, 174562306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 174662306a36Sopenharmony_ci .period_bytes_min = 64, 174762306a36Sopenharmony_ci .period_bytes_max = (128*1024), 174862306a36Sopenharmony_ci .periods_min = 1, 174962306a36Sopenharmony_ci .periods_max = 1024, 175062306a36Sopenharmony_ci .fifo_size = 0, 175162306a36Sopenharmony_ci}; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci/* 175462306a36Sopenharmony_ci * SPDIF playback support device description 175562306a36Sopenharmony_ci */ 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_spdif = 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 176062306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 176162306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | 176262306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), 176362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 176462306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 176562306a36Sopenharmony_ci SNDRV_PCM_RATE_48000), 176662306a36Sopenharmony_ci .rate_min = 32000, 176762306a36Sopenharmony_ci .rate_max = 48000, 176862306a36Sopenharmony_ci .channels_min = 2, 176962306a36Sopenharmony_ci .channels_max = 2, 177062306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 177162306a36Sopenharmony_ci .period_bytes_min = 64, 177262306a36Sopenharmony_ci .period_bytes_max = (128*1024), 177362306a36Sopenharmony_ci .periods_min = 1, 177462306a36Sopenharmony_ci .periods_max = 1024, 177562306a36Sopenharmony_ci .fifo_size = 0, 177662306a36Sopenharmony_ci}; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_spdif_7018 = 177962306a36Sopenharmony_ci{ 178062306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 178162306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 178262306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | 178362306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), 178462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 178562306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 178662306a36Sopenharmony_ci .rate_min = 48000, 178762306a36Sopenharmony_ci .rate_max = 48000, 178862306a36Sopenharmony_ci .channels_min = 2, 178962306a36Sopenharmony_ci .channels_max = 2, 179062306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 179162306a36Sopenharmony_ci .period_bytes_min = 64, 179262306a36Sopenharmony_ci .period_bytes_max = (128*1024), 179362306a36Sopenharmony_ci .periods_min = 1, 179462306a36Sopenharmony_ci .periods_max = 1024, 179562306a36Sopenharmony_ci .fifo_size = 0, 179662306a36Sopenharmony_ci}; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_cistatic void snd_trident_pcm_free_substream(struct snd_pcm_runtime *runtime) 179962306a36Sopenharmony_ci{ 180062306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 180162306a36Sopenharmony_ci struct snd_trident *trident; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (voice) { 180462306a36Sopenharmony_ci trident = voice->trident; 180562306a36Sopenharmony_ci snd_trident_free_voice(trident, voice); 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_cistatic int snd_trident_playback_open(struct snd_pcm_substream *substream) 181062306a36Sopenharmony_ci{ 181162306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 181262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 181362306a36Sopenharmony_ci struct snd_trident_voice *voice; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); 181662306a36Sopenharmony_ci if (voice == NULL) 181762306a36Sopenharmony_ci return -EAGAIN; 181862306a36Sopenharmony_ci snd_trident_pcm_mixer_build(trident, voice, substream); 181962306a36Sopenharmony_ci voice->substream = substream; 182062306a36Sopenharmony_ci runtime->private_data = voice; 182162306a36Sopenharmony_ci runtime->private_free = snd_trident_pcm_free_substream; 182262306a36Sopenharmony_ci runtime->hw = snd_trident_playback; 182362306a36Sopenharmony_ci snd_pcm_set_sync(substream); 182462306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); 182562306a36Sopenharmony_ci return 0; 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 182962306a36Sopenharmony_ci snd_trident_playback_close 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci Description: This routine will close the 4DWave playback device. For now 183262306a36Sopenharmony_ci we will simply free the dma transfer buffer. 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci Parameters: substream - PCM substream class 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 183762306a36Sopenharmony_cistatic int snd_trident_playback_close(struct snd_pcm_substream *substream) 183862306a36Sopenharmony_ci{ 183962306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 184062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 184162306a36Sopenharmony_ci struct snd_trident_voice *voice = runtime->private_data; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci snd_trident_pcm_mixer_free(trident, voice, substream); 184462306a36Sopenharmony_ci return 0; 184562306a36Sopenharmony_ci} 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 184862306a36Sopenharmony_ci snd_trident_spdif_open 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci Description: This routine will open the 4DWave SPDIF device. 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci Parameters: substream - PCM substream class 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci Returns: status - success or failure flag 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_cistatic int snd_trident_spdif_open(struct snd_pcm_substream *substream) 185962306a36Sopenharmony_ci{ 186062306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 186162306a36Sopenharmony_ci struct snd_trident_voice *voice; 186262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); 186562306a36Sopenharmony_ci if (voice == NULL) 186662306a36Sopenharmony_ci return -EAGAIN; 186762306a36Sopenharmony_ci voice->spdif = 1; 186862306a36Sopenharmony_ci voice->substream = substream; 186962306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 187062306a36Sopenharmony_ci trident->spdif_pcm_bits = trident->spdif_bits; 187162306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci runtime->private_data = voice; 187462306a36Sopenharmony_ci runtime->private_free = snd_trident_pcm_free_substream; 187562306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 187662306a36Sopenharmony_ci runtime->hw = snd_trident_spdif; 187762306a36Sopenharmony_ci } else { 187862306a36Sopenharmony_ci runtime->hw = snd_trident_spdif_7018; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 188262306a36Sopenharmony_ci snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | 188362306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); 188662306a36Sopenharmony_ci return 0; 188762306a36Sopenharmony_ci} 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 189162306a36Sopenharmony_ci snd_trident_spdif_close 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci Description: This routine will close the 4DWave SPDIF device. 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci Parameters: substream - PCM substream class 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_cistatic int snd_trident_spdif_close(struct snd_pcm_substream *substream) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 190262306a36Sopenharmony_ci unsigned int temp; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 190562306a36Sopenharmony_ci // restore default SPDIF setting 190662306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 190762306a36Sopenharmony_ci outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); 190862306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); 190962306a36Sopenharmony_ci } else { 191062306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); 191162306a36Sopenharmony_ci temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 191262306a36Sopenharmony_ci if (trident->spdif_ctrl) { 191362306a36Sopenharmony_ci temp |= SPDIF_EN; 191462306a36Sopenharmony_ci } else { 191562306a36Sopenharmony_ci temp &= ~SPDIF_EN; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 192062306a36Sopenharmony_ci trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 192162306a36Sopenharmony_ci snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | 192262306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); 192362306a36Sopenharmony_ci return 0; 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 192762306a36Sopenharmony_ci snd_trident_capture_open 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci Description: This routine will open the 4DWave capture device. 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci Parameters: substream - PCM substream class 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci Returns: status - success or failure flag 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic int snd_trident_capture_open(struct snd_pcm_substream *substream) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 194062306a36Sopenharmony_ci struct snd_trident_voice *voice; 194162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); 194462306a36Sopenharmony_ci if (voice == NULL) 194562306a36Sopenharmony_ci return -EAGAIN; 194662306a36Sopenharmony_ci voice->capture = 1; 194762306a36Sopenharmony_ci voice->substream = substream; 194862306a36Sopenharmony_ci runtime->private_data = voice; 194962306a36Sopenharmony_ci runtime->private_free = snd_trident_pcm_free_substream; 195062306a36Sopenharmony_ci runtime->hw = snd_trident_capture; 195162306a36Sopenharmony_ci snd_pcm_set_sync(substream); 195262306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); 195362306a36Sopenharmony_ci return 0; 195462306a36Sopenharmony_ci} 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 195762306a36Sopenharmony_ci snd_trident_capture_close 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci Description: This routine will close the 4DWave capture device. For now 196062306a36Sopenharmony_ci we will simply free the dma transfer buffer. 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci Parameters: substream - PCM substream class 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 196562306a36Sopenharmony_cistatic int snd_trident_capture_close(struct snd_pcm_substream *substream) 196662306a36Sopenharmony_ci{ 196762306a36Sopenharmony_ci return 0; 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 197162306a36Sopenharmony_ci snd_trident_foldback_open 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci Description: This routine will open the 4DWave foldback capture device. 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci Parameters: substream - PCM substream class 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci Returns: status - success or failure flag 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_cistatic int snd_trident_foldback_open(struct snd_pcm_substream *substream) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 198462306a36Sopenharmony_ci struct snd_trident_voice *voice; 198562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); 198862306a36Sopenharmony_ci if (voice == NULL) 198962306a36Sopenharmony_ci return -EAGAIN; 199062306a36Sopenharmony_ci voice->foldback_chan = substream->number; 199162306a36Sopenharmony_ci voice->substream = substream; 199262306a36Sopenharmony_ci runtime->private_data = voice; 199362306a36Sopenharmony_ci runtime->private_free = snd_trident_pcm_free_substream; 199462306a36Sopenharmony_ci runtime->hw = snd_trident_foldback; 199562306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); 199662306a36Sopenharmony_ci return 0; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 200062306a36Sopenharmony_ci snd_trident_foldback_close 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci Description: This routine will close the 4DWave foldback capture device. 200362306a36Sopenharmony_ci For now we will simply free the dma transfer buffer. 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci Parameters: substream - PCM substream class 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 200862306a36Sopenharmony_cistatic int snd_trident_foldback_close(struct snd_pcm_substream *substream) 200962306a36Sopenharmony_ci{ 201062306a36Sopenharmony_ci struct snd_trident *trident = snd_pcm_substream_chip(substream); 201162306a36Sopenharmony_ci struct snd_trident_voice *voice; 201262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 201362306a36Sopenharmony_ci voice = runtime->private_data; 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci /* stop capture channel */ 201662306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 201762306a36Sopenharmony_ci outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); 201862306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 201962306a36Sopenharmony_ci return 0; 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 202362306a36Sopenharmony_ci PCM operations 202462306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_playback_ops = { 202762306a36Sopenharmony_ci .open = snd_trident_playback_open, 202862306a36Sopenharmony_ci .close = snd_trident_playback_close, 202962306a36Sopenharmony_ci .hw_params = snd_trident_hw_params, 203062306a36Sopenharmony_ci .hw_free = snd_trident_hw_free, 203162306a36Sopenharmony_ci .prepare = snd_trident_playback_prepare, 203262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 203362306a36Sopenharmony_ci .pointer = snd_trident_playback_pointer, 203462306a36Sopenharmony_ci}; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_nx_playback_ops = { 203762306a36Sopenharmony_ci .open = snd_trident_playback_open, 203862306a36Sopenharmony_ci .close = snd_trident_playback_close, 203962306a36Sopenharmony_ci .hw_params = snd_trident_hw_params, 204062306a36Sopenharmony_ci .hw_free = snd_trident_hw_free, 204162306a36Sopenharmony_ci .prepare = snd_trident_playback_prepare, 204262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 204362306a36Sopenharmony_ci .pointer = snd_trident_playback_pointer, 204462306a36Sopenharmony_ci}; 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_capture_ops = { 204762306a36Sopenharmony_ci .open = snd_trident_capture_open, 204862306a36Sopenharmony_ci .close = snd_trident_capture_close, 204962306a36Sopenharmony_ci .hw_params = snd_trident_capture_hw_params, 205062306a36Sopenharmony_ci .hw_free = snd_trident_hw_free, 205162306a36Sopenharmony_ci .prepare = snd_trident_capture_prepare, 205262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 205362306a36Sopenharmony_ci .pointer = snd_trident_capture_pointer, 205462306a36Sopenharmony_ci}; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_si7018_capture_ops = { 205762306a36Sopenharmony_ci .open = snd_trident_capture_open, 205862306a36Sopenharmony_ci .close = snd_trident_capture_close, 205962306a36Sopenharmony_ci .hw_params = snd_trident_si7018_capture_hw_params, 206062306a36Sopenharmony_ci .hw_free = snd_trident_si7018_capture_hw_free, 206162306a36Sopenharmony_ci .prepare = snd_trident_si7018_capture_prepare, 206262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 206362306a36Sopenharmony_ci .pointer = snd_trident_playback_pointer, 206462306a36Sopenharmony_ci}; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_foldback_ops = { 206762306a36Sopenharmony_ci .open = snd_trident_foldback_open, 206862306a36Sopenharmony_ci .close = snd_trident_foldback_close, 206962306a36Sopenharmony_ci .hw_params = snd_trident_hw_params, 207062306a36Sopenharmony_ci .hw_free = snd_trident_hw_free, 207162306a36Sopenharmony_ci .prepare = snd_trident_foldback_prepare, 207262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 207362306a36Sopenharmony_ci .pointer = snd_trident_playback_pointer, 207462306a36Sopenharmony_ci}; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_nx_foldback_ops = { 207762306a36Sopenharmony_ci .open = snd_trident_foldback_open, 207862306a36Sopenharmony_ci .close = snd_trident_foldback_close, 207962306a36Sopenharmony_ci .hw_params = snd_trident_hw_params, 208062306a36Sopenharmony_ci .hw_free = snd_trident_hw_free, 208162306a36Sopenharmony_ci .prepare = snd_trident_foldback_prepare, 208262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 208362306a36Sopenharmony_ci .pointer = snd_trident_playback_pointer, 208462306a36Sopenharmony_ci}; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_spdif_ops = { 208762306a36Sopenharmony_ci .open = snd_trident_spdif_open, 208862306a36Sopenharmony_ci .close = snd_trident_spdif_close, 208962306a36Sopenharmony_ci .hw_params = snd_trident_spdif_hw_params, 209062306a36Sopenharmony_ci .hw_free = snd_trident_hw_free, 209162306a36Sopenharmony_ci .prepare = snd_trident_spdif_prepare, 209262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 209362306a36Sopenharmony_ci .pointer = snd_trident_spdif_pointer, 209462306a36Sopenharmony_ci}; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_spdif_7018_ops = { 209762306a36Sopenharmony_ci .open = snd_trident_spdif_open, 209862306a36Sopenharmony_ci .close = snd_trident_spdif_close, 209962306a36Sopenharmony_ci .hw_params = snd_trident_spdif_hw_params, 210062306a36Sopenharmony_ci .hw_free = snd_trident_hw_free, 210162306a36Sopenharmony_ci .prepare = snd_trident_spdif_prepare, 210262306a36Sopenharmony_ci .trigger = snd_trident_trigger, 210362306a36Sopenharmony_ci .pointer = snd_trident_playback_pointer, 210462306a36Sopenharmony_ci}; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 210762306a36Sopenharmony_ci snd_trident_pcm 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci Description: This routine registers the 4DWave device for PCM support. 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci Returns: None 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ciint snd_trident_pcm(struct snd_trident *trident, int device) 211862306a36Sopenharmony_ci{ 211962306a36Sopenharmony_ci struct snd_pcm *pcm; 212062306a36Sopenharmony_ci int err; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm); 212362306a36Sopenharmony_ci if (err < 0) 212462306a36Sopenharmony_ci return err; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci pcm->private_data = trident; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci if (trident->tlb.entries) { 212962306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops); 213062306a36Sopenharmony_ci } else { 213162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 213462306a36Sopenharmony_ci trident->device != TRIDENT_DEVICE_ID_SI7018 ? 213562306a36Sopenharmony_ci &snd_trident_capture_ops : 213662306a36Sopenharmony_ci &snd_trident_si7018_capture_ops); 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci pcm->info_flags = 0; 213962306a36Sopenharmony_ci pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 214062306a36Sopenharmony_ci strcpy(pcm->name, "Trident 4DWave"); 214162306a36Sopenharmony_ci trident->pcm = pcm; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci if (trident->tlb.entries) { 214462306a36Sopenharmony_ci struct snd_pcm_substream *substream; 214562306a36Sopenharmony_ci for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 214662306a36Sopenharmony_ci snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_SG, 214762306a36Sopenharmony_ci &trident->pci->dev, 214862306a36Sopenharmony_ci 64*1024, 128*1024); 214962306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 215062306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV, 215162306a36Sopenharmony_ci &trident->pci->dev, 215262306a36Sopenharmony_ci 64*1024, 128*1024); 215362306a36Sopenharmony_ci } else { 215462306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 215562306a36Sopenharmony_ci &trident->pci->dev, 215662306a36Sopenharmony_ci 64*1024, 128*1024); 215762306a36Sopenharmony_ci } 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci return 0; 216062306a36Sopenharmony_ci} 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 216362306a36Sopenharmony_ci snd_trident_foldback_pcm 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci Description: This routine registers the 4DWave device for foldback PCM support. 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci Returns: None 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ciint snd_trident_foldback_pcm(struct snd_trident *trident, int device) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci struct snd_pcm *foldback; 217662306a36Sopenharmony_ci int err; 217762306a36Sopenharmony_ci int num_chan = 3; 217862306a36Sopenharmony_ci struct snd_pcm_substream *substream; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_NX) 218162306a36Sopenharmony_ci num_chan = 4; 218262306a36Sopenharmony_ci err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback); 218362306a36Sopenharmony_ci if (err < 0) 218462306a36Sopenharmony_ci return err; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci foldback->private_data = trident; 218762306a36Sopenharmony_ci if (trident->tlb.entries) 218862306a36Sopenharmony_ci snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops); 218962306a36Sopenharmony_ci else 219062306a36Sopenharmony_ci snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); 219162306a36Sopenharmony_ci foldback->info_flags = 0; 219262306a36Sopenharmony_ci strcpy(foldback->name, "Trident 4DWave"); 219362306a36Sopenharmony_ci substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 219462306a36Sopenharmony_ci strcpy(substream->name, "Front Mixer"); 219562306a36Sopenharmony_ci substream = substream->next; 219662306a36Sopenharmony_ci strcpy(substream->name, "Reverb Mixer"); 219762306a36Sopenharmony_ci substream = substream->next; 219862306a36Sopenharmony_ci strcpy(substream->name, "Chorus Mixer"); 219962306a36Sopenharmony_ci if (num_chan == 4) { 220062306a36Sopenharmony_ci substream = substream->next; 220162306a36Sopenharmony_ci strcpy(substream->name, "Second AC'97 ADC"); 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci trident->foldback = foldback; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci if (trident->tlb.entries) 220662306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV_SG, 220762306a36Sopenharmony_ci &trident->pci->dev, 220862306a36Sopenharmony_ci 0, 128*1024); 220962306a36Sopenharmony_ci else 221062306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV, 221162306a36Sopenharmony_ci &trident->pci->dev, 221262306a36Sopenharmony_ci 64*1024, 128*1024); 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci return 0; 221562306a36Sopenharmony_ci} 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 221862306a36Sopenharmony_ci snd_trident_spdif 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci Description: This routine registers the 4DWave-NX device for SPDIF support. 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave-NX. 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci Returns: None 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ciint snd_trident_spdif_pcm(struct snd_trident *trident, int device) 222962306a36Sopenharmony_ci{ 223062306a36Sopenharmony_ci struct snd_pcm *spdif; 223162306a36Sopenharmony_ci int err; 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif); 223462306a36Sopenharmony_ci if (err < 0) 223562306a36Sopenharmony_ci return err; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci spdif->private_data = trident; 223862306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 223962306a36Sopenharmony_ci snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops); 224062306a36Sopenharmony_ci } else { 224162306a36Sopenharmony_ci snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci spdif->info_flags = 0; 224462306a36Sopenharmony_ci strcpy(spdif->name, "Trident 4DWave IEC958"); 224562306a36Sopenharmony_ci trident->spdif = spdif; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV, 224862306a36Sopenharmony_ci &trident->pci->dev, 64*1024, 128*1024); 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci return 0; 225162306a36Sopenharmony_ci} 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci/* 225462306a36Sopenharmony_ci * Mixer part 225562306a36Sopenharmony_ci */ 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 225962306a36Sopenharmony_ci snd_trident_spdif_control 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci Description: enable/disable S/PDIF out from ac97 mixer 226262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci#define snd_trident_spdif_control_info snd_ctl_boolean_mono_info 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_cistatic int snd_trident_spdif_control_get(struct snd_kcontrol *kcontrol, 226762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 227062306a36Sopenharmony_ci unsigned char val; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 227362306a36Sopenharmony_ci val = trident->spdif_ctrl; 227462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = val == kcontrol->private_value; 227562306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 227662306a36Sopenharmony_ci return 0; 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_cistatic int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol, 228062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 228362306a36Sopenharmony_ci unsigned char val; 228462306a36Sopenharmony_ci int change; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00; 228762306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 228862306a36Sopenharmony_ci /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */ 228962306a36Sopenharmony_ci change = trident->spdif_ctrl != val; 229062306a36Sopenharmony_ci trident->spdif_ctrl = val; 229162306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 229262306a36Sopenharmony_ci if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) { 229362306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); 229462306a36Sopenharmony_ci outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); 229562306a36Sopenharmony_ci } 229662306a36Sopenharmony_ci } else { 229762306a36Sopenharmony_ci if (trident->spdif == NULL) { 229862306a36Sopenharmony_ci unsigned int temp; 229962306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); 230062306a36Sopenharmony_ci temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN; 230162306a36Sopenharmony_ci if (val) 230262306a36Sopenharmony_ci temp |= SPDIF_EN; 230362306a36Sopenharmony_ci outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci } 230662306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 230762306a36Sopenharmony_ci return change; 230862306a36Sopenharmony_ci} 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_control = 231162306a36Sopenharmony_ci{ 231262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 231362306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 231462306a36Sopenharmony_ci .info = snd_trident_spdif_control_info, 231562306a36Sopenharmony_ci .get = snd_trident_spdif_control_get, 231662306a36Sopenharmony_ci .put = snd_trident_spdif_control_put, 231762306a36Sopenharmony_ci .private_value = 0x28, 231862306a36Sopenharmony_ci}; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 232162306a36Sopenharmony_ci snd_trident_spdif_default 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci Description: put/get the S/PDIF default settings 232462306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_cistatic int snd_trident_spdif_default_info(struct snd_kcontrol *kcontrol, 232762306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 232862306a36Sopenharmony_ci{ 232962306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 233062306a36Sopenharmony_ci uinfo->count = 1; 233162306a36Sopenharmony_ci return 0; 233262306a36Sopenharmony_ci} 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_cistatic int snd_trident_spdif_default_get(struct snd_kcontrol *kcontrol, 233562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 234062306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff; 234162306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff; 234262306a36Sopenharmony_ci ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff; 234362306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff; 234462306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 234562306a36Sopenharmony_ci return 0; 234662306a36Sopenharmony_ci} 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_cistatic int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol, 234962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 235062306a36Sopenharmony_ci{ 235162306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 235262306a36Sopenharmony_ci unsigned int val; 235362306a36Sopenharmony_ci int change; 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci val = (ucontrol->value.iec958.status[0] << 0) | 235662306a36Sopenharmony_ci (ucontrol->value.iec958.status[1] << 8) | 235762306a36Sopenharmony_ci (ucontrol->value.iec958.status[2] << 16) | 235862306a36Sopenharmony_ci (ucontrol->value.iec958.status[3] << 24); 235962306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 236062306a36Sopenharmony_ci change = trident->spdif_bits != val; 236162306a36Sopenharmony_ci trident->spdif_bits = val; 236262306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 236362306a36Sopenharmony_ci if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) 236462306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); 236562306a36Sopenharmony_ci } else { 236662306a36Sopenharmony_ci if (trident->spdif == NULL) 236762306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 237062306a36Sopenharmony_ci return change; 237162306a36Sopenharmony_ci} 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_default = 237462306a36Sopenharmony_ci{ 237562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 237662306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 237762306a36Sopenharmony_ci .info = snd_trident_spdif_default_info, 237862306a36Sopenharmony_ci .get = snd_trident_spdif_default_get, 237962306a36Sopenharmony_ci .put = snd_trident_spdif_default_put 238062306a36Sopenharmony_ci}; 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 238362306a36Sopenharmony_ci snd_trident_spdif_mask 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci Description: put/get the S/PDIF mask 238662306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_cistatic int snd_trident_spdif_mask_info(struct snd_kcontrol *kcontrol, 238962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 239062306a36Sopenharmony_ci{ 239162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 239262306a36Sopenharmony_ci uinfo->count = 1; 239362306a36Sopenharmony_ci return 0; 239462306a36Sopenharmony_ci} 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_cistatic int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol, 239762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = 0xff; 240062306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = 0xff; 240162306a36Sopenharmony_ci ucontrol->value.iec958.status[2] = 0xff; 240262306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = 0xff; 240362306a36Sopenharmony_ci return 0; 240462306a36Sopenharmony_ci} 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_mask = 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 240962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 241062306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 241162306a36Sopenharmony_ci .info = snd_trident_spdif_mask_info, 241262306a36Sopenharmony_ci .get = snd_trident_spdif_mask_get, 241362306a36Sopenharmony_ci}; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 241662306a36Sopenharmony_ci snd_trident_spdif_stream 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci Description: put/get the S/PDIF stream settings 241962306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_cistatic int snd_trident_spdif_stream_info(struct snd_kcontrol *kcontrol, 242262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 242362306a36Sopenharmony_ci{ 242462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 242562306a36Sopenharmony_ci uinfo->count = 1; 242662306a36Sopenharmony_ci return 0; 242762306a36Sopenharmony_ci} 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_cistatic int snd_trident_spdif_stream_get(struct snd_kcontrol *kcontrol, 243062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 243162306a36Sopenharmony_ci{ 243262306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 243562306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff; 243662306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff; 243762306a36Sopenharmony_ci ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff; 243862306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff; 243962306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 244062306a36Sopenharmony_ci return 0; 244162306a36Sopenharmony_ci} 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_cistatic int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol, 244462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 244562306a36Sopenharmony_ci{ 244662306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 244762306a36Sopenharmony_ci unsigned int val; 244862306a36Sopenharmony_ci int change; 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci val = (ucontrol->value.iec958.status[0] << 0) | 245162306a36Sopenharmony_ci (ucontrol->value.iec958.status[1] << 8) | 245262306a36Sopenharmony_ci (ucontrol->value.iec958.status[2] << 16) | 245362306a36Sopenharmony_ci (ucontrol->value.iec958.status[3] << 24); 245462306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 245562306a36Sopenharmony_ci change = trident->spdif_pcm_bits != val; 245662306a36Sopenharmony_ci trident->spdif_pcm_bits = val; 245762306a36Sopenharmony_ci if (trident->spdif != NULL) { 245862306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 245962306a36Sopenharmony_ci outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); 246062306a36Sopenharmony_ci } else { 246162306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci } 246462306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 246562306a36Sopenharmony_ci return change; 246662306a36Sopenharmony_ci} 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_stream = 246962306a36Sopenharmony_ci{ 247062306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, 247162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 247262306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), 247362306a36Sopenharmony_ci .info = snd_trident_spdif_stream_info, 247462306a36Sopenharmony_ci .get = snd_trident_spdif_stream_get, 247562306a36Sopenharmony_ci .put = snd_trident_spdif_stream_put 247662306a36Sopenharmony_ci}; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 247962306a36Sopenharmony_ci snd_trident_ac97_control 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci Description: enable/disable rear path for ac97 248262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci#define snd_trident_ac97_control_info snd_ctl_boolean_mono_info 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_cistatic int snd_trident_ac97_control_get(struct snd_kcontrol *kcontrol, 248762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 248862306a36Sopenharmony_ci{ 248962306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 249062306a36Sopenharmony_ci unsigned char val; 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 249362306a36Sopenharmony_ci val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); 249462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0; 249562306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 249662306a36Sopenharmony_ci return 0; 249762306a36Sopenharmony_ci} 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_cistatic int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol, 250062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 250162306a36Sopenharmony_ci{ 250262306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 250362306a36Sopenharmony_ci unsigned char val; 250462306a36Sopenharmony_ci int change = 0; 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 250762306a36Sopenharmony_ci val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); 250862306a36Sopenharmony_ci val &= ~(1 << kcontrol->private_value); 250962306a36Sopenharmony_ci if (ucontrol->value.integer.value[0]) 251062306a36Sopenharmony_ci val |= 1 << kcontrol->private_value; 251162306a36Sopenharmony_ci change = val != trident->ac97_ctrl; 251262306a36Sopenharmony_ci trident->ac97_ctrl = val; 251362306a36Sopenharmony_ci outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); 251462306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 251562306a36Sopenharmony_ci return change; 251662306a36Sopenharmony_ci} 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_ac97_rear_control = 251962306a36Sopenharmony_ci{ 252062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 252162306a36Sopenharmony_ci .name = "Rear Path", 252262306a36Sopenharmony_ci .info = snd_trident_ac97_control_info, 252362306a36Sopenharmony_ci .get = snd_trident_ac97_control_get, 252462306a36Sopenharmony_ci .put = snd_trident_ac97_control_put, 252562306a36Sopenharmony_ci .private_value = 4, 252662306a36Sopenharmony_ci}; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 252962306a36Sopenharmony_ci snd_trident_vol_control 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci Description: wave & music volume control 253262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_cistatic int snd_trident_vol_control_info(struct snd_kcontrol *kcontrol, 253562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 253662306a36Sopenharmony_ci{ 253762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 253862306a36Sopenharmony_ci uinfo->count = 2; 253962306a36Sopenharmony_ci uinfo->value.integer.min = 0; 254062306a36Sopenharmony_ci uinfo->value.integer.max = 255; 254162306a36Sopenharmony_ci return 0; 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_cistatic int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol, 254562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 254662306a36Sopenharmony_ci{ 254762306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 254862306a36Sopenharmony_ci unsigned int val; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci val = trident->musicvol_wavevol; 255162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff); 255262306a36Sopenharmony_ci ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff); 255362306a36Sopenharmony_ci return 0; 255462306a36Sopenharmony_ci} 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0); 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_cistatic int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol, 255962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 256062306a36Sopenharmony_ci{ 256162306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 256262306a36Sopenharmony_ci unsigned int val; 256362306a36Sopenharmony_ci int change = 0; 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 256662306a36Sopenharmony_ci val = trident->musicvol_wavevol; 256762306a36Sopenharmony_ci val &= ~(0xffff << kcontrol->private_value); 256862306a36Sopenharmony_ci val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) | 256962306a36Sopenharmony_ci ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value; 257062306a36Sopenharmony_ci change = val != trident->musicvol_wavevol; 257162306a36Sopenharmony_ci outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); 257262306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 257362306a36Sopenharmony_ci return change; 257462306a36Sopenharmony_ci} 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_vol_music_control = 257762306a36Sopenharmony_ci{ 257862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 257962306a36Sopenharmony_ci .name = "Music Playback Volume", 258062306a36Sopenharmony_ci .info = snd_trident_vol_control_info, 258162306a36Sopenharmony_ci .get = snd_trident_vol_control_get, 258262306a36Sopenharmony_ci .put = snd_trident_vol_control_put, 258362306a36Sopenharmony_ci .private_value = 16, 258462306a36Sopenharmony_ci .tlv = { .p = db_scale_gvol }, 258562306a36Sopenharmony_ci}; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_vol_wave_control = 258862306a36Sopenharmony_ci{ 258962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 259062306a36Sopenharmony_ci .name = "Wave Playback Volume", 259162306a36Sopenharmony_ci .info = snd_trident_vol_control_info, 259262306a36Sopenharmony_ci .get = snd_trident_vol_control_get, 259362306a36Sopenharmony_ci .put = snd_trident_vol_control_put, 259462306a36Sopenharmony_ci .private_value = 0, 259562306a36Sopenharmony_ci .tlv = { .p = db_scale_gvol }, 259662306a36Sopenharmony_ci}; 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 259962306a36Sopenharmony_ci snd_trident_pcm_vol_control 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci Description: PCM front volume control 260262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_cistatic int snd_trident_pcm_vol_control_info(struct snd_kcontrol *kcontrol, 260562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 260662306a36Sopenharmony_ci{ 260762306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 261062306a36Sopenharmony_ci uinfo->count = 1; 261162306a36Sopenharmony_ci uinfo->value.integer.min = 0; 261262306a36Sopenharmony_ci uinfo->value.integer.max = 255; 261362306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) 261462306a36Sopenharmony_ci uinfo->value.integer.max = 1023; 261562306a36Sopenharmony_ci return 0; 261662306a36Sopenharmony_ci} 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_cistatic int snd_trident_pcm_vol_control_get(struct snd_kcontrol *kcontrol, 261962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 262062306a36Sopenharmony_ci{ 262162306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 262262306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 262562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 1023 - mix->vol; 262662306a36Sopenharmony_ci } else { 262762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 255 - (mix->vol>>2); 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci return 0; 263062306a36Sopenharmony_ci} 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_cistatic int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol, 263362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 263462306a36Sopenharmony_ci{ 263562306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 263662306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 263762306a36Sopenharmony_ci unsigned int val; 263862306a36Sopenharmony_ci int change = 0; 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 264162306a36Sopenharmony_ci val = 1023 - (ucontrol->value.integer.value[0] & 1023); 264262306a36Sopenharmony_ci } else { 264362306a36Sopenharmony_ci val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2; 264462306a36Sopenharmony_ci } 264562306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 264662306a36Sopenharmony_ci change = val != mix->vol; 264762306a36Sopenharmony_ci mix->vol = val; 264862306a36Sopenharmony_ci if (mix->voice != NULL) 264962306a36Sopenharmony_ci snd_trident_write_vol_reg(trident, mix->voice, val); 265062306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 265162306a36Sopenharmony_ci return change; 265262306a36Sopenharmony_ci} 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_vol_control = 265562306a36Sopenharmony_ci{ 265662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 265762306a36Sopenharmony_ci .name = "PCM Front Playback Volume", 265862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, 265962306a36Sopenharmony_ci .count = 32, 266062306a36Sopenharmony_ci .info = snd_trident_pcm_vol_control_info, 266162306a36Sopenharmony_ci .get = snd_trident_pcm_vol_control_get, 266262306a36Sopenharmony_ci .put = snd_trident_pcm_vol_control_put, 266362306a36Sopenharmony_ci /* FIXME: no tlv yet */ 266462306a36Sopenharmony_ci}; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 266762306a36Sopenharmony_ci snd_trident_pcm_pan_control 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci Description: PCM front pan control 267062306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_cistatic int snd_trident_pcm_pan_control_info(struct snd_kcontrol *kcontrol, 267362306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 267462306a36Sopenharmony_ci{ 267562306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 267662306a36Sopenharmony_ci uinfo->count = 1; 267762306a36Sopenharmony_ci uinfo->value.integer.min = 0; 267862306a36Sopenharmony_ci uinfo->value.integer.max = 127; 267962306a36Sopenharmony_ci return 0; 268062306a36Sopenharmony_ci} 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_cistatic int snd_trident_pcm_pan_control_get(struct snd_kcontrol *kcontrol, 268362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 268462306a36Sopenharmony_ci{ 268562306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 268662306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mix->pan; 268962306a36Sopenharmony_ci if (ucontrol->value.integer.value[0] & 0x40) { 269062306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)); 269162306a36Sopenharmony_ci } else { 269262306a36Sopenharmony_ci ucontrol->value.integer.value[0] |= 0x40; 269362306a36Sopenharmony_ci } 269462306a36Sopenharmony_ci return 0; 269562306a36Sopenharmony_ci} 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_cistatic int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol, 269862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 269962306a36Sopenharmony_ci{ 270062306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 270162306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 270262306a36Sopenharmony_ci unsigned char val; 270362306a36Sopenharmony_ci int change = 0; 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci if (ucontrol->value.integer.value[0] & 0x40) 270662306a36Sopenharmony_ci val = ucontrol->value.integer.value[0] & 0x3f; 270762306a36Sopenharmony_ci else 270862306a36Sopenharmony_ci val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40; 270962306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 271062306a36Sopenharmony_ci change = val != mix->pan; 271162306a36Sopenharmony_ci mix->pan = val; 271262306a36Sopenharmony_ci if (mix->voice != NULL) 271362306a36Sopenharmony_ci snd_trident_write_pan_reg(trident, mix->voice, val); 271462306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 271562306a36Sopenharmony_ci return change; 271662306a36Sopenharmony_ci} 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_pan_control = 271962306a36Sopenharmony_ci{ 272062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 272162306a36Sopenharmony_ci .name = "PCM Pan Playback Control", 272262306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, 272362306a36Sopenharmony_ci .count = 32, 272462306a36Sopenharmony_ci .info = snd_trident_pcm_pan_control_info, 272562306a36Sopenharmony_ci .get = snd_trident_pcm_pan_control_get, 272662306a36Sopenharmony_ci .put = snd_trident_pcm_pan_control_put, 272762306a36Sopenharmony_ci}; 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 273062306a36Sopenharmony_ci snd_trident_pcm_rvol_control 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci Description: PCM reverb volume control 273362306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_cistatic int snd_trident_pcm_rvol_control_info(struct snd_kcontrol *kcontrol, 273662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 273762306a36Sopenharmony_ci{ 273862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 273962306a36Sopenharmony_ci uinfo->count = 1; 274062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 274162306a36Sopenharmony_ci uinfo->value.integer.max = 127; 274262306a36Sopenharmony_ci return 0; 274362306a36Sopenharmony_ci} 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_cistatic int snd_trident_pcm_rvol_control_get(struct snd_kcontrol *kcontrol, 274662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 274762306a36Sopenharmony_ci{ 274862306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 274962306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 127 - mix->rvol; 275262306a36Sopenharmony_ci return 0; 275362306a36Sopenharmony_ci} 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_cistatic int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol, 275662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 275762306a36Sopenharmony_ci{ 275862306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 275962306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 276062306a36Sopenharmony_ci unsigned short val; 276162306a36Sopenharmony_ci int change = 0; 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); 276462306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 276562306a36Sopenharmony_ci change = val != mix->rvol; 276662306a36Sopenharmony_ci mix->rvol = val; 276762306a36Sopenharmony_ci if (mix->voice != NULL) 276862306a36Sopenharmony_ci snd_trident_write_rvol_reg(trident, mix->voice, val); 276962306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 277062306a36Sopenharmony_ci return change; 277162306a36Sopenharmony_ci} 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1); 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_rvol_control = 277662306a36Sopenharmony_ci{ 277762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 277862306a36Sopenharmony_ci .name = "PCM Reverb Playback Volume", 277962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, 278062306a36Sopenharmony_ci .count = 32, 278162306a36Sopenharmony_ci .info = snd_trident_pcm_rvol_control_info, 278262306a36Sopenharmony_ci .get = snd_trident_pcm_rvol_control_get, 278362306a36Sopenharmony_ci .put = snd_trident_pcm_rvol_control_put, 278462306a36Sopenharmony_ci .tlv = { .p = db_scale_crvol }, 278562306a36Sopenharmony_ci}; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 278862306a36Sopenharmony_ci snd_trident_pcm_cvol_control 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci Description: PCM chorus volume control 279162306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_cistatic int snd_trident_pcm_cvol_control_info(struct snd_kcontrol *kcontrol, 279462306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 279562306a36Sopenharmony_ci{ 279662306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 279762306a36Sopenharmony_ci uinfo->count = 1; 279862306a36Sopenharmony_ci uinfo->value.integer.min = 0; 279962306a36Sopenharmony_ci uinfo->value.integer.max = 127; 280062306a36Sopenharmony_ci return 0; 280162306a36Sopenharmony_ci} 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_cistatic int snd_trident_pcm_cvol_control_get(struct snd_kcontrol *kcontrol, 280462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 280562306a36Sopenharmony_ci{ 280662306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 280762306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 127 - mix->cvol; 281062306a36Sopenharmony_ci return 0; 281162306a36Sopenharmony_ci} 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_cistatic int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol, 281462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 281562306a36Sopenharmony_ci{ 281662306a36Sopenharmony_ci struct snd_trident *trident = snd_kcontrol_chip(kcontrol); 281762306a36Sopenharmony_ci struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; 281862306a36Sopenharmony_ci unsigned short val; 281962306a36Sopenharmony_ci int change = 0; 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); 282262306a36Sopenharmony_ci spin_lock_irq(&trident->reg_lock); 282362306a36Sopenharmony_ci change = val != mix->cvol; 282462306a36Sopenharmony_ci mix->cvol = val; 282562306a36Sopenharmony_ci if (mix->voice != NULL) 282662306a36Sopenharmony_ci snd_trident_write_cvol_reg(trident, mix->voice, val); 282762306a36Sopenharmony_ci spin_unlock_irq(&trident->reg_lock); 282862306a36Sopenharmony_ci return change; 282962306a36Sopenharmony_ci} 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_cvol_control = 283262306a36Sopenharmony_ci{ 283362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 283462306a36Sopenharmony_ci .name = "PCM Chorus Playback Volume", 283562306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, 283662306a36Sopenharmony_ci .count = 32, 283762306a36Sopenharmony_ci .info = snd_trident_pcm_cvol_control_info, 283862306a36Sopenharmony_ci .get = snd_trident_pcm_cvol_control_get, 283962306a36Sopenharmony_ci .put = snd_trident_pcm_cvol_control_put, 284062306a36Sopenharmony_ci .tlv = { .p = db_scale_crvol }, 284162306a36Sopenharmony_ci}; 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_cistatic void snd_trident_notify_pcm_change1(struct snd_card *card, 284462306a36Sopenharmony_ci struct snd_kcontrol *kctl, 284562306a36Sopenharmony_ci int num, int activate) 284662306a36Sopenharmony_ci{ 284762306a36Sopenharmony_ci struct snd_ctl_elem_id id; 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci if (! kctl) 285062306a36Sopenharmony_ci return; 285162306a36Sopenharmony_ci if (activate) 285262306a36Sopenharmony_ci kctl->vd[num].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 285362306a36Sopenharmony_ci else 285462306a36Sopenharmony_ci kctl->vd[num].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 285562306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | 285662306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_INFO, 285762306a36Sopenharmony_ci snd_ctl_build_ioff(&id, kctl, num)); 285862306a36Sopenharmony_ci} 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_cistatic void snd_trident_notify_pcm_change(struct snd_trident *trident, 286162306a36Sopenharmony_ci struct snd_trident_pcm_mixer *tmix, 286262306a36Sopenharmony_ci int num, int activate) 286362306a36Sopenharmony_ci{ 286462306a36Sopenharmony_ci snd_trident_notify_pcm_change1(trident->card, trident->ctl_vol, num, activate); 286562306a36Sopenharmony_ci snd_trident_notify_pcm_change1(trident->card, trident->ctl_pan, num, activate); 286662306a36Sopenharmony_ci snd_trident_notify_pcm_change1(trident->card, trident->ctl_rvol, num, activate); 286762306a36Sopenharmony_ci snd_trident_notify_pcm_change1(trident->card, trident->ctl_cvol, num, activate); 286862306a36Sopenharmony_ci} 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_cistatic int snd_trident_pcm_mixer_build(struct snd_trident *trident, 287162306a36Sopenharmony_ci struct snd_trident_voice *voice, 287262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 287362306a36Sopenharmony_ci{ 287462306a36Sopenharmony_ci struct snd_trident_pcm_mixer *tmix; 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci if (snd_BUG_ON(!trident || !voice || !substream)) 287762306a36Sopenharmony_ci return -EINVAL; 287862306a36Sopenharmony_ci tmix = &trident->pcm_mixer[substream->number]; 287962306a36Sopenharmony_ci tmix->voice = voice; 288062306a36Sopenharmony_ci tmix->vol = T4D_DEFAULT_PCM_VOL; 288162306a36Sopenharmony_ci tmix->pan = T4D_DEFAULT_PCM_PAN; 288262306a36Sopenharmony_ci tmix->rvol = T4D_DEFAULT_PCM_RVOL; 288362306a36Sopenharmony_ci tmix->cvol = T4D_DEFAULT_PCM_CVOL; 288462306a36Sopenharmony_ci snd_trident_notify_pcm_change(trident, tmix, substream->number, 1); 288562306a36Sopenharmony_ci return 0; 288662306a36Sopenharmony_ci} 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_cistatic int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_trident_voice *voice, struct snd_pcm_substream *substream) 288962306a36Sopenharmony_ci{ 289062306a36Sopenharmony_ci struct snd_trident_pcm_mixer *tmix; 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci if (snd_BUG_ON(!trident || !substream)) 289362306a36Sopenharmony_ci return -EINVAL; 289462306a36Sopenharmony_ci tmix = &trident->pcm_mixer[substream->number]; 289562306a36Sopenharmony_ci tmix->voice = NULL; 289662306a36Sopenharmony_ci snd_trident_notify_pcm_change(trident, tmix, substream->number, 0); 289762306a36Sopenharmony_ci return 0; 289862306a36Sopenharmony_ci} 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 290162306a36Sopenharmony_ci snd_trident_mixer 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci Description: This routine registers the 4DWave device for mixer support. 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci Returns: None 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_cistatic int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device) 291262306a36Sopenharmony_ci{ 291362306a36Sopenharmony_ci struct snd_ac97_template _ac97; 291462306a36Sopenharmony_ci struct snd_card *card = trident->card; 291562306a36Sopenharmony_ci struct snd_kcontrol *kctl; 291662306a36Sopenharmony_ci struct snd_ctl_elem_value *uctl; 291762306a36Sopenharmony_ci int idx, err, retries = 2; 291862306a36Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 291962306a36Sopenharmony_ci .write = snd_trident_codec_write, 292062306a36Sopenharmony_ci .read = snd_trident_codec_read, 292162306a36Sopenharmony_ci }; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); 292462306a36Sopenharmony_ci if (!uctl) 292562306a36Sopenharmony_ci return -ENOMEM; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus); 292862306a36Sopenharmony_ci if (err < 0) 292962306a36Sopenharmony_ci goto __out; 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci memset(&_ac97, 0, sizeof(_ac97)); 293262306a36Sopenharmony_ci _ac97.private_data = trident; 293362306a36Sopenharmony_ci trident->ac97_detect = 1; 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci __again: 293662306a36Sopenharmony_ci err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97); 293762306a36Sopenharmony_ci if (err < 0) { 293862306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 293962306a36Sopenharmony_ci err = snd_trident_sis_reset(trident); 294062306a36Sopenharmony_ci if (err < 0) 294162306a36Sopenharmony_ci goto __out; 294262306a36Sopenharmony_ci if (retries-- > 0) 294362306a36Sopenharmony_ci goto __again; 294462306a36Sopenharmony_ci err = -EIO; 294562306a36Sopenharmony_ci } 294662306a36Sopenharmony_ci goto __out; 294762306a36Sopenharmony_ci } 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci /* secondary codec? */ 295062306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018 && 295162306a36Sopenharmony_ci (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) { 295262306a36Sopenharmony_ci _ac97.num = 1; 295362306a36Sopenharmony_ci err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec); 295462306a36Sopenharmony_ci if (err < 0) 295562306a36Sopenharmony_ci dev_err(trident->card->dev, 295662306a36Sopenharmony_ci "SI7018: the secondary codec - invalid access\n"); 295762306a36Sopenharmony_ci#if 0 // only for my testing purpose --jk 295862306a36Sopenharmony_ci { 295962306a36Sopenharmony_ci struct snd_ac97 *mc97; 296062306a36Sopenharmony_ci err = snd_ac97_modem(trident->card, &_ac97, &mc97); 296162306a36Sopenharmony_ci if (err < 0) 296262306a36Sopenharmony_ci dev_err(trident->card->dev, 296362306a36Sopenharmony_ci "snd_ac97_modem returned error %i\n", err); 296462306a36Sopenharmony_ci } 296562306a36Sopenharmony_ci#endif 296662306a36Sopenharmony_ci } 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci trident->ac97_detect = 0; 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci if (trident->device != TRIDENT_DEVICE_ID_SI7018) { 297162306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident); 297262306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 297362306a36Sopenharmony_ci if (err < 0) 297462306a36Sopenharmony_ci goto __out; 297562306a36Sopenharmony_ci kctl->put(kctl, uctl); 297662306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident); 297762306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 297862306a36Sopenharmony_ci if (err < 0) 297962306a36Sopenharmony_ci goto __out; 298062306a36Sopenharmony_ci kctl->put(kctl, uctl); 298162306a36Sopenharmony_ci outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); 298262306a36Sopenharmony_ci } else { 298362306a36Sopenharmony_ci outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); 298462306a36Sopenharmony_ci } 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci for (idx = 0; idx < 32; idx++) { 298762306a36Sopenharmony_ci struct snd_trident_pcm_mixer *tmix; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci tmix = &trident->pcm_mixer[idx]; 299062306a36Sopenharmony_ci tmix->voice = NULL; 299162306a36Sopenharmony_ci } 299262306a36Sopenharmony_ci trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident); 299362306a36Sopenharmony_ci if (!trident->ctl_vol) 299462306a36Sopenharmony_ci goto __nomem; 299562306a36Sopenharmony_ci err = snd_ctl_add(card, trident->ctl_vol); 299662306a36Sopenharmony_ci if (err) 299762306a36Sopenharmony_ci goto __out; 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident); 300062306a36Sopenharmony_ci if (!trident->ctl_pan) 300162306a36Sopenharmony_ci goto __nomem; 300262306a36Sopenharmony_ci err = snd_ctl_add(card, trident->ctl_pan); 300362306a36Sopenharmony_ci if (err) 300462306a36Sopenharmony_ci goto __out; 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident); 300762306a36Sopenharmony_ci if (!trident->ctl_rvol) 300862306a36Sopenharmony_ci goto __nomem; 300962306a36Sopenharmony_ci err = snd_ctl_add(card, trident->ctl_rvol); 301062306a36Sopenharmony_ci if (err) 301162306a36Sopenharmony_ci goto __out; 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident); 301462306a36Sopenharmony_ci if (!trident->ctl_cvol) 301562306a36Sopenharmony_ci goto __nomem; 301662306a36Sopenharmony_ci err = snd_ctl_add(card, trident->ctl_cvol); 301762306a36Sopenharmony_ci if (err) 301862306a36Sopenharmony_ci goto __out; 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_NX) { 302162306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident); 302262306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 302362306a36Sopenharmony_ci if (err < 0) 302462306a36Sopenharmony_ci goto __out; 302562306a36Sopenharmony_ci kctl->put(kctl, uctl); 302662306a36Sopenharmony_ci } 302762306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_trident_spdif_control, trident); 303062306a36Sopenharmony_ci if (kctl == NULL) { 303162306a36Sopenharmony_ci err = -ENOMEM; 303262306a36Sopenharmony_ci goto __out; 303362306a36Sopenharmony_ci } 303462306a36Sopenharmony_ci if (trident->ac97->ext_id & AC97_EI_SPDIF) 303562306a36Sopenharmony_ci kctl->id.index++; 303662306a36Sopenharmony_ci if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF)) 303762306a36Sopenharmony_ci kctl->id.index++; 303862306a36Sopenharmony_ci idx = kctl->id.index; 303962306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 304062306a36Sopenharmony_ci if (err < 0) 304162306a36Sopenharmony_ci goto __out; 304262306a36Sopenharmony_ci kctl->put(kctl, uctl); 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_trident_spdif_default, trident); 304562306a36Sopenharmony_ci if (kctl == NULL) { 304662306a36Sopenharmony_ci err = -ENOMEM; 304762306a36Sopenharmony_ci goto __out; 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci kctl->id.index = idx; 305062306a36Sopenharmony_ci kctl->id.device = pcm_spdif_device; 305162306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 305262306a36Sopenharmony_ci if (err < 0) 305362306a36Sopenharmony_ci goto __out; 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident); 305662306a36Sopenharmony_ci if (kctl == NULL) { 305762306a36Sopenharmony_ci err = -ENOMEM; 305862306a36Sopenharmony_ci goto __out; 305962306a36Sopenharmony_ci } 306062306a36Sopenharmony_ci kctl->id.index = idx; 306162306a36Sopenharmony_ci kctl->id.device = pcm_spdif_device; 306262306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 306362306a36Sopenharmony_ci if (err < 0) 306462306a36Sopenharmony_ci goto __out; 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident); 306762306a36Sopenharmony_ci if (kctl == NULL) { 306862306a36Sopenharmony_ci err = -ENOMEM; 306962306a36Sopenharmony_ci goto __out; 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci kctl->id.index = idx; 307262306a36Sopenharmony_ci kctl->id.device = pcm_spdif_device; 307362306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 307462306a36Sopenharmony_ci if (err < 0) 307562306a36Sopenharmony_ci goto __out; 307662306a36Sopenharmony_ci trident->spdif_pcm_ctl = kctl; 307762306a36Sopenharmony_ci } 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ci err = 0; 308062306a36Sopenharmony_ci goto __out; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci __nomem: 308362306a36Sopenharmony_ci err = -ENOMEM; 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci __out: 308662306a36Sopenharmony_ci kfree(uctl); 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci return err; 308962306a36Sopenharmony_ci} 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci/* 309262306a36Sopenharmony_ci * gameport interface 309362306a36Sopenharmony_ci */ 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT) 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_cistatic unsigned char snd_trident_gameport_read(struct gameport *gameport) 309862306a36Sopenharmony_ci{ 309962306a36Sopenharmony_ci struct snd_trident *chip = gameport_get_port_data(gameport); 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci if (snd_BUG_ON(!chip)) 310262306a36Sopenharmony_ci return 0; 310362306a36Sopenharmony_ci return inb(TRID_REG(chip, GAMEPORT_LEGACY)); 310462306a36Sopenharmony_ci} 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_cistatic void snd_trident_gameport_trigger(struct gameport *gameport) 310762306a36Sopenharmony_ci{ 310862306a36Sopenharmony_ci struct snd_trident *chip = gameport_get_port_data(gameport); 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci if (snd_BUG_ON(!chip)) 311162306a36Sopenharmony_ci return; 311262306a36Sopenharmony_ci outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY)); 311362306a36Sopenharmony_ci} 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_cistatic int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) 311662306a36Sopenharmony_ci{ 311762306a36Sopenharmony_ci struct snd_trident *chip = gameport_get_port_data(gameport); 311862306a36Sopenharmony_ci int i; 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci if (snd_BUG_ON(!chip)) 312162306a36Sopenharmony_ci return 0; 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci *buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf; 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 312662306a36Sopenharmony_ci axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2)); 312762306a36Sopenharmony_ci if (axes[i] == 0xffff) axes[i] = -1; 312862306a36Sopenharmony_ci } 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci return 0; 313162306a36Sopenharmony_ci} 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_cistatic int snd_trident_gameport_open(struct gameport *gameport, int mode) 313462306a36Sopenharmony_ci{ 313562306a36Sopenharmony_ci struct snd_trident *chip = gameport_get_port_data(gameport); 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci if (snd_BUG_ON(!chip)) 313862306a36Sopenharmony_ci return 0; 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci switch (mode) { 314162306a36Sopenharmony_ci case GAMEPORT_MODE_COOKED: 314262306a36Sopenharmony_ci outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR)); 314362306a36Sopenharmony_ci msleep(20); 314462306a36Sopenharmony_ci return 0; 314562306a36Sopenharmony_ci case GAMEPORT_MODE_RAW: 314662306a36Sopenharmony_ci outb(0, TRID_REG(chip, GAMEPORT_GCR)); 314762306a36Sopenharmony_ci return 0; 314862306a36Sopenharmony_ci default: 314962306a36Sopenharmony_ci return -1; 315062306a36Sopenharmony_ci } 315162306a36Sopenharmony_ci} 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ciint snd_trident_create_gameport(struct snd_trident *chip) 315462306a36Sopenharmony_ci{ 315562306a36Sopenharmony_ci struct gameport *gp; 315662306a36Sopenharmony_ci 315762306a36Sopenharmony_ci chip->gameport = gp = gameport_allocate_port(); 315862306a36Sopenharmony_ci if (!gp) { 315962306a36Sopenharmony_ci dev_err(chip->card->dev, 316062306a36Sopenharmony_ci "cannot allocate memory for gameport\n"); 316162306a36Sopenharmony_ci return -ENOMEM; 316262306a36Sopenharmony_ci } 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci gameport_set_name(gp, "Trident 4DWave"); 316562306a36Sopenharmony_ci gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); 316662306a36Sopenharmony_ci gameport_set_dev_parent(gp, &chip->pci->dev); 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci gameport_set_port_data(gp, chip); 316962306a36Sopenharmony_ci gp->fuzz = 64; 317062306a36Sopenharmony_ci gp->read = snd_trident_gameport_read; 317162306a36Sopenharmony_ci gp->trigger = snd_trident_gameport_trigger; 317262306a36Sopenharmony_ci gp->cooked_read = snd_trident_gameport_cooked_read; 317362306a36Sopenharmony_ci gp->open = snd_trident_gameport_open; 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_ci gameport_register_port(gp); 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci return 0; 317862306a36Sopenharmony_ci} 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_cistatic inline void snd_trident_free_gameport(struct snd_trident *chip) 318162306a36Sopenharmony_ci{ 318262306a36Sopenharmony_ci if (chip->gameport) { 318362306a36Sopenharmony_ci gameport_unregister_port(chip->gameport); 318462306a36Sopenharmony_ci chip->gameport = NULL; 318562306a36Sopenharmony_ci } 318662306a36Sopenharmony_ci} 318762306a36Sopenharmony_ci#else 318862306a36Sopenharmony_ciint snd_trident_create_gameport(struct snd_trident *chip) { return -ENOSYS; } 318962306a36Sopenharmony_cistatic inline void snd_trident_free_gameport(struct snd_trident *chip) { } 319062306a36Sopenharmony_ci#endif /* CONFIG_GAMEPORT */ 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_ci/* 319362306a36Sopenharmony_ci * delay for 1 tick 319462306a36Sopenharmony_ci */ 319562306a36Sopenharmony_cistatic inline void do_delay(struct snd_trident *chip) 319662306a36Sopenharmony_ci{ 319762306a36Sopenharmony_ci schedule_timeout_uninterruptible(1); 319862306a36Sopenharmony_ci} 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci/* 320162306a36Sopenharmony_ci * SiS reset routine 320262306a36Sopenharmony_ci */ 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_cistatic int snd_trident_sis_reset(struct snd_trident *trident) 320562306a36Sopenharmony_ci{ 320662306a36Sopenharmony_ci unsigned long end_time; 320762306a36Sopenharmony_ci unsigned int i; 320862306a36Sopenharmony_ci int r; 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci r = trident->in_suspend ? 0 : 2; /* count of retries */ 321162306a36Sopenharmony_ci __si7018_retry: 321262306a36Sopenharmony_ci pci_write_config_byte(trident->pci, 0x46, 0x04); /* SOFTWARE RESET */ 321362306a36Sopenharmony_ci udelay(100); 321462306a36Sopenharmony_ci pci_write_config_byte(trident->pci, 0x46, 0x00); 321562306a36Sopenharmony_ci udelay(100); 321662306a36Sopenharmony_ci /* disable AC97 GPIO interrupt */ 321762306a36Sopenharmony_ci outb(0x00, TRID_REG(trident, SI_AC97_GPIO)); 321862306a36Sopenharmony_ci /* initialize serial interface, force cold reset */ 321962306a36Sopenharmony_ci i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET; 322062306a36Sopenharmony_ci outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 322162306a36Sopenharmony_ci udelay(1000); 322262306a36Sopenharmony_ci /* remove cold reset */ 322362306a36Sopenharmony_ci i &= ~COLD_RESET; 322462306a36Sopenharmony_ci outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 322562306a36Sopenharmony_ci udelay(2000); 322662306a36Sopenharmony_ci /* wait, until the codec is ready */ 322762306a36Sopenharmony_ci end_time = (jiffies + (HZ * 3) / 4) + 1; 322862306a36Sopenharmony_ci do { 322962306a36Sopenharmony_ci if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) 323062306a36Sopenharmony_ci goto __si7018_ok; 323162306a36Sopenharmony_ci do_delay(trident); 323262306a36Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 323362306a36Sopenharmony_ci dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n", 323462306a36Sopenharmony_ci inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL))); 323562306a36Sopenharmony_ci if (r-- > 0) { 323662306a36Sopenharmony_ci end_time = jiffies + HZ; 323762306a36Sopenharmony_ci do { 323862306a36Sopenharmony_ci do_delay(trident); 323962306a36Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 324062306a36Sopenharmony_ci goto __si7018_retry; 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci __si7018_ok: 324362306a36Sopenharmony_ci /* wait for the second codec */ 324462306a36Sopenharmony_ci do { 324562306a36Sopenharmony_ci if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0) 324662306a36Sopenharmony_ci break; 324762306a36Sopenharmony_ci do_delay(trident); 324862306a36Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 324962306a36Sopenharmony_ci /* enable 64 channel mode */ 325062306a36Sopenharmony_ci outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR)); 325162306a36Sopenharmony_ci return 0; 325262306a36Sopenharmony_ci} 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci/* 325562306a36Sopenharmony_ci * /proc interface 325662306a36Sopenharmony_ci */ 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_cistatic void snd_trident_proc_read(struct snd_info_entry *entry, 325962306a36Sopenharmony_ci struct snd_info_buffer *buffer) 326062306a36Sopenharmony_ci{ 326162306a36Sopenharmony_ci struct snd_trident *trident = entry->private_data; 326262306a36Sopenharmony_ci char *s; 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci switch (trident->device) { 326562306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_SI7018: 326662306a36Sopenharmony_ci s = "SiS 7018 Audio"; 326762306a36Sopenharmony_ci break; 326862306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_DX: 326962306a36Sopenharmony_ci s = "Trident 4DWave PCI DX"; 327062306a36Sopenharmony_ci break; 327162306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_NX: 327262306a36Sopenharmony_ci s = "Trident 4DWave PCI NX"; 327362306a36Sopenharmony_ci break; 327462306a36Sopenharmony_ci default: 327562306a36Sopenharmony_ci s = "???"; 327662306a36Sopenharmony_ci } 327762306a36Sopenharmony_ci snd_iprintf(buffer, "%s\n\n", s); 327862306a36Sopenharmony_ci snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count); 327962306a36Sopenharmony_ci snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta); 328062306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) 328162306a36Sopenharmony_ci snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off"); 328262306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_NX) { 328362306a36Sopenharmony_ci snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off"); 328462306a36Sopenharmony_ci if (trident->tlb.entries) { 328562306a36Sopenharmony_ci snd_iprintf(buffer,"\nVirtual Memory\n"); 328662306a36Sopenharmony_ci snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size); 328762306a36Sopenharmony_ci snd_iprintf(buffer, "Memory Used : %d\n", trident->tlb.memhdr->used); 328862306a36Sopenharmony_ci snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci } 329162306a36Sopenharmony_ci} 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_cistatic void snd_trident_proc_init(struct snd_trident *trident) 329462306a36Sopenharmony_ci{ 329562306a36Sopenharmony_ci const char *s = "trident"; 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_SI7018) 329862306a36Sopenharmony_ci s = "sis7018"; 329962306a36Sopenharmony_ci snd_card_ro_proc_new(trident->card, s, trident, snd_trident_proc_read); 330062306a36Sopenharmony_ci} 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 330362306a36Sopenharmony_ci snd_trident_tlb_alloc 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci Description: Allocate and set up the TLB page table on 4D NX. 330662306a36Sopenharmony_ci Each entry has 4 bytes (physical PCI address). 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci Parameters: trident - pointer to target device class for 4DWave. 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci Returns: 0 or negative error code 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_cistatic int snd_trident_tlb_alloc(struct snd_trident *trident) 331562306a36Sopenharmony_ci{ 331662306a36Sopenharmony_ci int i; 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci /* TLB array must be aligned to 16kB !!! so we allocate 331962306a36Sopenharmony_ci 32kB region and correct offset when necessary */ 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci trident->tlb.buffer = 332262306a36Sopenharmony_ci snd_devm_alloc_pages(&trident->pci->dev, SNDRV_DMA_TYPE_DEV, 332362306a36Sopenharmony_ci 2 * SNDRV_TRIDENT_MAX_PAGES * 4); 332462306a36Sopenharmony_ci if (!trident->tlb.buffer) { 332562306a36Sopenharmony_ci dev_err(trident->card->dev, "unable to allocate TLB buffer\n"); 332662306a36Sopenharmony_ci return -ENOMEM; 332762306a36Sopenharmony_ci } 332862306a36Sopenharmony_ci trident->tlb.entries = (__le32 *)ALIGN((unsigned long)trident->tlb.buffer->area, SNDRV_TRIDENT_MAX_PAGES * 4); 332962306a36Sopenharmony_ci trident->tlb.entries_dmaaddr = ALIGN(trident->tlb.buffer->addr, SNDRV_TRIDENT_MAX_PAGES * 4); 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_ci /* allocate and setup silent page and initialise TLB entries */ 333262306a36Sopenharmony_ci trident->tlb.silent_page = 333362306a36Sopenharmony_ci snd_devm_alloc_pages(&trident->pci->dev, SNDRV_DMA_TYPE_DEV, 333462306a36Sopenharmony_ci SNDRV_TRIDENT_PAGE_SIZE); 333562306a36Sopenharmony_ci if (!trident->tlb.silent_page) { 333662306a36Sopenharmony_ci dev_err(trident->card->dev, "unable to allocate silent page\n"); 333762306a36Sopenharmony_ci return -ENOMEM; 333862306a36Sopenharmony_ci } 333962306a36Sopenharmony_ci memset(trident->tlb.silent_page->area, 0, SNDRV_TRIDENT_PAGE_SIZE); 334062306a36Sopenharmony_ci for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) 334162306a36Sopenharmony_ci trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page->addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci /* use emu memory block manager code to manage tlb page allocation */ 334462306a36Sopenharmony_ci trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); 334562306a36Sopenharmony_ci if (trident->tlb.memhdr == NULL) 334662306a36Sopenharmony_ci return -ENOMEM; 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci trident->tlb.memhdr->block_extra_size = sizeof(struct snd_trident_memblk_arg); 334962306a36Sopenharmony_ci return 0; 335062306a36Sopenharmony_ci} 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci/* 335362306a36Sopenharmony_ci * initialize 4D DX chip 335462306a36Sopenharmony_ci */ 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_cistatic void snd_trident_stop_all_voices(struct snd_trident *trident) 335762306a36Sopenharmony_ci{ 335862306a36Sopenharmony_ci outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); 335962306a36Sopenharmony_ci outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); 336062306a36Sopenharmony_ci outl(0, TRID_REG(trident, T4D_AINTEN_A)); 336162306a36Sopenharmony_ci outl(0, TRID_REG(trident, T4D_AINTEN_B)); 336262306a36Sopenharmony_ci} 336362306a36Sopenharmony_ci 336462306a36Sopenharmony_cistatic int snd_trident_4d_dx_init(struct snd_trident *trident) 336562306a36Sopenharmony_ci{ 336662306a36Sopenharmony_ci struct pci_dev *pci = trident->pci; 336762306a36Sopenharmony_ci unsigned long end_time; 336862306a36Sopenharmony_ci 336962306a36Sopenharmony_ci /* reset the legacy configuration and whole audio/wavetable block */ 337062306a36Sopenharmony_ci pci_write_config_dword(pci, 0x40, 0); /* DDMA */ 337162306a36Sopenharmony_ci pci_write_config_byte(pci, 0x44, 0); /* ports */ 337262306a36Sopenharmony_ci pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ 337362306a36Sopenharmony_ci pci_write_config_byte(pci, 0x46, 4); /* reset */ 337462306a36Sopenharmony_ci udelay(100); 337562306a36Sopenharmony_ci pci_write_config_byte(pci, 0x46, 0); /* release reset */ 337662306a36Sopenharmony_ci udelay(100); 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci /* warm reset of the AC'97 codec */ 337962306a36Sopenharmony_ci outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); 338062306a36Sopenharmony_ci udelay(100); 338162306a36Sopenharmony_ci outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); 338262306a36Sopenharmony_ci /* DAC on, disable SB IRQ and try to force ADC valid signal */ 338362306a36Sopenharmony_ci trident->ac97_ctrl = 0x0000004a; 338462306a36Sopenharmony_ci outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); 338562306a36Sopenharmony_ci /* wait, until the codec is ready */ 338662306a36Sopenharmony_ci end_time = (jiffies + (HZ * 3) / 4) + 1; 338762306a36Sopenharmony_ci do { 338862306a36Sopenharmony_ci if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) 338962306a36Sopenharmony_ci goto __dx_ok; 339062306a36Sopenharmony_ci do_delay(trident); 339162306a36Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 339262306a36Sopenharmony_ci dev_err(trident->card->dev, "AC'97 codec ready error\n"); 339362306a36Sopenharmony_ci return -EIO; 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci __dx_ok: 339662306a36Sopenharmony_ci snd_trident_stop_all_voices(trident); 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci return 0; 339962306a36Sopenharmony_ci} 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci/* 340262306a36Sopenharmony_ci * initialize 4D NX chip 340362306a36Sopenharmony_ci */ 340462306a36Sopenharmony_cistatic int snd_trident_4d_nx_init(struct snd_trident *trident) 340562306a36Sopenharmony_ci{ 340662306a36Sopenharmony_ci struct pci_dev *pci = trident->pci; 340762306a36Sopenharmony_ci unsigned long end_time; 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci /* reset the legacy configuration and whole audio/wavetable block */ 341062306a36Sopenharmony_ci pci_write_config_dword(pci, 0x40, 0); /* DDMA */ 341162306a36Sopenharmony_ci pci_write_config_byte(pci, 0x44, 0); /* ports */ 341262306a36Sopenharmony_ci pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci pci_write_config_byte(pci, 0x46, 1); /* reset */ 341562306a36Sopenharmony_ci udelay(100); 341662306a36Sopenharmony_ci pci_write_config_byte(pci, 0x46, 0); /* release reset */ 341762306a36Sopenharmony_ci udelay(100); 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci /* warm reset of the AC'97 codec */ 342062306a36Sopenharmony_ci outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); 342162306a36Sopenharmony_ci udelay(100); 342262306a36Sopenharmony_ci outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); 342362306a36Sopenharmony_ci /* wait, until the codec is ready */ 342462306a36Sopenharmony_ci end_time = (jiffies + (HZ * 3) / 4) + 1; 342562306a36Sopenharmony_ci do { 342662306a36Sopenharmony_ci if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) 342762306a36Sopenharmony_ci goto __nx_ok; 342862306a36Sopenharmony_ci do_delay(trident); 342962306a36Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 343062306a36Sopenharmony_ci dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n", 343162306a36Sopenharmony_ci inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); 343262306a36Sopenharmony_ci return -EIO; 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci __nx_ok: 343562306a36Sopenharmony_ci /* DAC on */ 343662306a36Sopenharmony_ci trident->ac97_ctrl = 0x00000002; 343762306a36Sopenharmony_ci outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); 343862306a36Sopenharmony_ci /* disable SB IRQ */ 343962306a36Sopenharmony_ci outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_ci snd_trident_stop_all_voices(trident); 344262306a36Sopenharmony_ci 344362306a36Sopenharmony_ci if (trident->tlb.entries != NULL) { 344462306a36Sopenharmony_ci unsigned int i; 344562306a36Sopenharmony_ci /* enable virtual addressing via TLB */ 344662306a36Sopenharmony_ci i = trident->tlb.entries_dmaaddr; 344762306a36Sopenharmony_ci i |= 0x00000001; 344862306a36Sopenharmony_ci outl(i, TRID_REG(trident, NX_TLBC)); 344962306a36Sopenharmony_ci } else { 345062306a36Sopenharmony_ci outl(0, TRID_REG(trident, NX_TLBC)); 345162306a36Sopenharmony_ci } 345262306a36Sopenharmony_ci /* initialize S/PDIF */ 345362306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); 345462306a36Sopenharmony_ci outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci return 0; 345762306a36Sopenharmony_ci} 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_ci/* 346062306a36Sopenharmony_ci * initialize sis7018 chip 346162306a36Sopenharmony_ci */ 346262306a36Sopenharmony_cistatic int snd_trident_sis_init(struct snd_trident *trident) 346362306a36Sopenharmony_ci{ 346462306a36Sopenharmony_ci int err; 346562306a36Sopenharmony_ci 346662306a36Sopenharmony_ci err = snd_trident_sis_reset(trident); 346762306a36Sopenharmony_ci if (err < 0) 346862306a36Sopenharmony_ci return err; 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_ci snd_trident_stop_all_voices(trident); 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci /* initialize S/PDIF */ 347362306a36Sopenharmony_ci outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci return 0; 347662306a36Sopenharmony_ci} 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 347962306a36Sopenharmony_ci snd_trident_create 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci Description: This routine will create the device specific class for 348262306a36Sopenharmony_ci the 4DWave card. It will also perform basic initialization. 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci Parameters: card - which card to create 348562306a36Sopenharmony_ci pci - interface to PCI bus resource info 348662306a36Sopenharmony_ci dma1ptr - playback dma buffer 348762306a36Sopenharmony_ci dma2ptr - capture dma buffer 348862306a36Sopenharmony_ci irqptr - interrupt resource info 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci Returns: 4DWave device class private data 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 349362306a36Sopenharmony_ci 349462306a36Sopenharmony_ciint snd_trident_create(struct snd_card *card, 349562306a36Sopenharmony_ci struct pci_dev *pci, 349662306a36Sopenharmony_ci int pcm_streams, 349762306a36Sopenharmony_ci int pcm_spdif_device, 349862306a36Sopenharmony_ci int max_wavetable_size) 349962306a36Sopenharmony_ci{ 350062306a36Sopenharmony_ci struct snd_trident *trident = card->private_data; 350162306a36Sopenharmony_ci int i, err; 350262306a36Sopenharmony_ci struct snd_trident_voice *voice; 350362306a36Sopenharmony_ci struct snd_trident_pcm_mixer *tmix; 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ci /* enable PCI device */ 350662306a36Sopenharmony_ci err = pcim_enable_device(pci); 350762306a36Sopenharmony_ci if (err < 0) 350862306a36Sopenharmony_ci return err; 350962306a36Sopenharmony_ci /* check, if we can restrict PCI DMA transfers to 30 bits */ 351062306a36Sopenharmony_ci if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(30))) { 351162306a36Sopenharmony_ci dev_err(card->dev, 351262306a36Sopenharmony_ci "architecture does not support 30bit PCI busmaster DMA\n"); 351362306a36Sopenharmony_ci return -ENXIO; 351462306a36Sopenharmony_ci } 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci trident->device = (pci->vendor << 16) | pci->device; 351762306a36Sopenharmony_ci trident->card = card; 351862306a36Sopenharmony_ci trident->pci = pci; 351962306a36Sopenharmony_ci spin_lock_init(&trident->reg_lock); 352062306a36Sopenharmony_ci spin_lock_init(&trident->event_lock); 352162306a36Sopenharmony_ci spin_lock_init(&trident->voice_alloc); 352262306a36Sopenharmony_ci if (pcm_streams < 1) 352362306a36Sopenharmony_ci pcm_streams = 1; 352462306a36Sopenharmony_ci if (pcm_streams > 32) 352562306a36Sopenharmony_ci pcm_streams = 32; 352662306a36Sopenharmony_ci trident->ChanPCM = pcm_streams; 352762306a36Sopenharmony_ci if (max_wavetable_size < 0 ) 352862306a36Sopenharmony_ci max_wavetable_size = 0; 352962306a36Sopenharmony_ci trident->synth.max_size = max_wavetable_size * 1024; 353062306a36Sopenharmony_ci trident->irq = -1; 353162306a36Sopenharmony_ci card->private_free = snd_trident_free; 353262306a36Sopenharmony_ci 353362306a36Sopenharmony_ci trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE); 353462306a36Sopenharmony_ci pci_set_master(pci); 353562306a36Sopenharmony_ci 353662306a36Sopenharmony_ci err = pci_request_regions(pci, "Trident Audio"); 353762306a36Sopenharmony_ci if (err < 0) 353862306a36Sopenharmony_ci return err; 353962306a36Sopenharmony_ci trident->port = pci_resource_start(pci, 0); 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_ci if (devm_request_irq(&pci->dev, pci->irq, snd_trident_interrupt, 354262306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, trident)) { 354362306a36Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 354462306a36Sopenharmony_ci return -EBUSY; 354562306a36Sopenharmony_ci } 354662306a36Sopenharmony_ci trident->irq = pci->irq; 354762306a36Sopenharmony_ci card->sync_irq = trident->irq; 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_ci /* allocate 16k-aligned TLB for NX cards */ 355062306a36Sopenharmony_ci trident->tlb.entries = NULL; 355162306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_NX) { 355262306a36Sopenharmony_ci err = snd_trident_tlb_alloc(trident); 355362306a36Sopenharmony_ci if (err < 0) 355462306a36Sopenharmony_ci return err; 355562306a36Sopenharmony_ci } 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ci trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; 355862306a36Sopenharmony_ci 355962306a36Sopenharmony_ci /* initialize chip */ 356062306a36Sopenharmony_ci switch (trident->device) { 356162306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_DX: 356262306a36Sopenharmony_ci err = snd_trident_4d_dx_init(trident); 356362306a36Sopenharmony_ci break; 356462306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_NX: 356562306a36Sopenharmony_ci err = snd_trident_4d_nx_init(trident); 356662306a36Sopenharmony_ci break; 356762306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_SI7018: 356862306a36Sopenharmony_ci err = snd_trident_sis_init(trident); 356962306a36Sopenharmony_ci break; 357062306a36Sopenharmony_ci default: 357162306a36Sopenharmony_ci snd_BUG(); 357262306a36Sopenharmony_ci break; 357362306a36Sopenharmony_ci } 357462306a36Sopenharmony_ci if (err < 0) 357562306a36Sopenharmony_ci return err; 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci err = snd_trident_mixer(trident, pcm_spdif_device); 357862306a36Sopenharmony_ci if (err < 0) 357962306a36Sopenharmony_ci return err; 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_ci /* initialise synth voices */ 358262306a36Sopenharmony_ci for (i = 0; i < 64; i++) { 358362306a36Sopenharmony_ci voice = &trident->synth.voices[i]; 358462306a36Sopenharmony_ci voice->number = i; 358562306a36Sopenharmony_ci voice->trident = trident; 358662306a36Sopenharmony_ci } 358762306a36Sopenharmony_ci /* initialize pcm mixer entries */ 358862306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 358962306a36Sopenharmony_ci tmix = &trident->pcm_mixer[i]; 359062306a36Sopenharmony_ci tmix->vol = T4D_DEFAULT_PCM_VOL; 359162306a36Sopenharmony_ci tmix->pan = T4D_DEFAULT_PCM_PAN; 359262306a36Sopenharmony_ci tmix->rvol = T4D_DEFAULT_PCM_RVOL; 359362306a36Sopenharmony_ci tmix->cvol = T4D_DEFAULT_PCM_CVOL; 359462306a36Sopenharmony_ci } 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci snd_trident_enable_eso(trident); 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci snd_trident_proc_init(trident); 359962306a36Sopenharmony_ci return 0; 360062306a36Sopenharmony_ci} 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 360362306a36Sopenharmony_ci snd_trident_free 360462306a36Sopenharmony_ci 360562306a36Sopenharmony_ci Description: This routine will free the device specific class for 360662306a36Sopenharmony_ci the 4DWave card. 360762306a36Sopenharmony_ci 360862306a36Sopenharmony_ci Parameters: card - card to release 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci Returns: None. 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 361362306a36Sopenharmony_ci 361462306a36Sopenharmony_cistatic void snd_trident_free(struct snd_card *card) 361562306a36Sopenharmony_ci{ 361662306a36Sopenharmony_ci struct snd_trident *trident = card->private_data; 361762306a36Sopenharmony_ci 361862306a36Sopenharmony_ci snd_trident_free_gameport(trident); 361962306a36Sopenharmony_ci snd_trident_disable_eso(trident); 362062306a36Sopenharmony_ci // Disable S/PDIF out 362162306a36Sopenharmony_ci if (trident->device == TRIDENT_DEVICE_ID_NX) 362262306a36Sopenharmony_ci outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); 362362306a36Sopenharmony_ci else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { 362462306a36Sopenharmony_ci outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); 362562306a36Sopenharmony_ci } 362662306a36Sopenharmony_ci if (trident->tlb.buffer) { 362762306a36Sopenharmony_ci outl(0, TRID_REG(trident, NX_TLBC)); 362862306a36Sopenharmony_ci snd_util_memhdr_free(trident->tlb.memhdr); 362962306a36Sopenharmony_ci } 363062306a36Sopenharmony_ci} 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_ci/*--------------------------------------------------------------------------- 363362306a36Sopenharmony_ci snd_trident_interrupt 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci Description: ISR for Trident 4DWave device 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci Parameters: trident - device specific private data for 4DWave card 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci Problems: It seems that Trident chips generates interrupts more than 364062306a36Sopenharmony_ci one time in special cases. The spurious interrupts are 364162306a36Sopenharmony_ci detected via sample timer (T4D_STIMER) and computing 364262306a36Sopenharmony_ci corresponding delta value. The limits are detected with 364362306a36Sopenharmony_ci the method try & fail so it is possible that it won't 364462306a36Sopenharmony_ci work on all computers. [jaroslav] 364562306a36Sopenharmony_ci 364662306a36Sopenharmony_ci Returns: None. 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci ---------------------------------------------------------------------------*/ 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_cistatic irqreturn_t snd_trident_interrupt(int irq, void *dev_id) 365162306a36Sopenharmony_ci{ 365262306a36Sopenharmony_ci struct snd_trident *trident = dev_id; 365362306a36Sopenharmony_ci unsigned int audio_int, chn_int, stimer, channel, mask, tmp; 365462306a36Sopenharmony_ci int delta; 365562306a36Sopenharmony_ci struct snd_trident_voice *voice; 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_ci audio_int = inl(TRID_REG(trident, T4D_MISCINT)); 365862306a36Sopenharmony_ci if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0) 365962306a36Sopenharmony_ci return IRQ_NONE; 366062306a36Sopenharmony_ci if (audio_int & ADDRESS_IRQ) { 366162306a36Sopenharmony_ci // get interrupt status for all channels 366262306a36Sopenharmony_ci spin_lock(&trident->reg_lock); 366362306a36Sopenharmony_ci stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; 366462306a36Sopenharmony_ci chn_int = inl(TRID_REG(trident, T4D_AINT_A)); 366562306a36Sopenharmony_ci if (chn_int == 0) 366662306a36Sopenharmony_ci goto __skip1; 366762306a36Sopenharmony_ci outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */ 366862306a36Sopenharmony_ci __skip1: 366962306a36Sopenharmony_ci chn_int = inl(TRID_REG(trident, T4D_AINT_B)); 367062306a36Sopenharmony_ci if (chn_int == 0) 367162306a36Sopenharmony_ci goto __skip2; 367262306a36Sopenharmony_ci for (channel = 63; channel >= 32; channel--) { 367362306a36Sopenharmony_ci mask = 1 << (channel&0x1f); 367462306a36Sopenharmony_ci if ((chn_int & mask) == 0) 367562306a36Sopenharmony_ci continue; 367662306a36Sopenharmony_ci voice = &trident->synth.voices[channel]; 367762306a36Sopenharmony_ci if (!voice->pcm || voice->substream == NULL) { 367862306a36Sopenharmony_ci outl(mask, TRID_REG(trident, T4D_STOP_B)); 367962306a36Sopenharmony_ci continue; 368062306a36Sopenharmony_ci } 368162306a36Sopenharmony_ci delta = (int)stimer - (int)voice->stimer; 368262306a36Sopenharmony_ci if (delta < 0) 368362306a36Sopenharmony_ci delta = -delta; 368462306a36Sopenharmony_ci if ((unsigned int)delta < voice->spurious_threshold) { 368562306a36Sopenharmony_ci /* do some statistics here */ 368662306a36Sopenharmony_ci trident->spurious_irq_count++; 368762306a36Sopenharmony_ci if (trident->spurious_irq_max_delta < (unsigned int)delta) 368862306a36Sopenharmony_ci trident->spurious_irq_max_delta = delta; 368962306a36Sopenharmony_ci continue; 369062306a36Sopenharmony_ci } 369162306a36Sopenharmony_ci voice->stimer = stimer; 369262306a36Sopenharmony_ci if (voice->isync) { 369362306a36Sopenharmony_ci if (!voice->isync3) { 369462306a36Sopenharmony_ci tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL)); 369562306a36Sopenharmony_ci if (trident->bDMAStart & 0x40) 369662306a36Sopenharmony_ci tmp >>= 1; 369762306a36Sopenharmony_ci if (tmp > 0) 369862306a36Sopenharmony_ci tmp = voice->isync_max - tmp; 369962306a36Sopenharmony_ci } else { 370062306a36Sopenharmony_ci tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; 370162306a36Sopenharmony_ci } 370262306a36Sopenharmony_ci if (tmp < voice->isync_mark) { 370362306a36Sopenharmony_ci if (tmp > 0x10) 370462306a36Sopenharmony_ci tmp = voice->isync_ESO - 7; 370562306a36Sopenharmony_ci else 370662306a36Sopenharmony_ci tmp = voice->isync_ESO + 2; 370762306a36Sopenharmony_ci /* update ESO for IRQ voice to preserve sync */ 370862306a36Sopenharmony_ci snd_trident_stop_voice(trident, voice->number); 370962306a36Sopenharmony_ci snd_trident_write_eso_reg(trident, voice, tmp); 371062306a36Sopenharmony_ci snd_trident_start_voice(trident, voice->number); 371162306a36Sopenharmony_ci } 371262306a36Sopenharmony_ci } else if (voice->isync2) { 371362306a36Sopenharmony_ci voice->isync2 = 0; 371462306a36Sopenharmony_ci /* write original ESO and update CSO for IRQ voice to preserve sync */ 371562306a36Sopenharmony_ci snd_trident_stop_voice(trident, voice->number); 371662306a36Sopenharmony_ci snd_trident_write_cso_reg(trident, voice, voice->isync_mark); 371762306a36Sopenharmony_ci snd_trident_write_eso_reg(trident, voice, voice->ESO); 371862306a36Sopenharmony_ci snd_trident_start_voice(trident, voice->number); 371962306a36Sopenharmony_ci } 372062306a36Sopenharmony_ci#if 0 372162306a36Sopenharmony_ci if (voice->extra) { 372262306a36Sopenharmony_ci /* update CSO for extra voice to preserve sync */ 372362306a36Sopenharmony_ci snd_trident_stop_voice(trident, voice->extra->number); 372462306a36Sopenharmony_ci snd_trident_write_cso_reg(trident, voice->extra, 0); 372562306a36Sopenharmony_ci snd_trident_start_voice(trident, voice->extra->number); 372662306a36Sopenharmony_ci } 372762306a36Sopenharmony_ci#endif 372862306a36Sopenharmony_ci spin_unlock(&trident->reg_lock); 372962306a36Sopenharmony_ci snd_pcm_period_elapsed(voice->substream); 373062306a36Sopenharmony_ci spin_lock(&trident->reg_lock); 373162306a36Sopenharmony_ci } 373262306a36Sopenharmony_ci outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */ 373362306a36Sopenharmony_ci __skip2: 373462306a36Sopenharmony_ci spin_unlock(&trident->reg_lock); 373562306a36Sopenharmony_ci } 373662306a36Sopenharmony_ci if (audio_int & MPU401_IRQ) { 373762306a36Sopenharmony_ci if (trident->rmidi) { 373862306a36Sopenharmony_ci snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data); 373962306a36Sopenharmony_ci } else { 374062306a36Sopenharmony_ci inb(TRID_REG(trident, T4D_MPUR0)); 374162306a36Sopenharmony_ci } 374262306a36Sopenharmony_ci } 374362306a36Sopenharmony_ci // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT)); 374462306a36Sopenharmony_ci return IRQ_HANDLED; 374562306a36Sopenharmony_ci} 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_cistruct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port) 374862306a36Sopenharmony_ci{ 374962306a36Sopenharmony_ci struct snd_trident_voice *pvoice; 375062306a36Sopenharmony_ci unsigned long flags; 375162306a36Sopenharmony_ci int idx; 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_ci spin_lock_irqsave(&trident->voice_alloc, flags); 375462306a36Sopenharmony_ci if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) { 375562306a36Sopenharmony_ci idx = snd_trident_allocate_pcm_channel(trident); 375662306a36Sopenharmony_ci if(idx < 0) { 375762306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->voice_alloc, flags); 375862306a36Sopenharmony_ci return NULL; 375962306a36Sopenharmony_ci } 376062306a36Sopenharmony_ci pvoice = &trident->synth.voices[idx]; 376162306a36Sopenharmony_ci pvoice->use = 1; 376262306a36Sopenharmony_ci pvoice->pcm = 1; 376362306a36Sopenharmony_ci pvoice->capture = 0; 376462306a36Sopenharmony_ci pvoice->spdif = 0; 376562306a36Sopenharmony_ci pvoice->memblk = NULL; 376662306a36Sopenharmony_ci pvoice->substream = NULL; 376762306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->voice_alloc, flags); 376862306a36Sopenharmony_ci return pvoice; 376962306a36Sopenharmony_ci } 377062306a36Sopenharmony_ci if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) { 377162306a36Sopenharmony_ci idx = snd_trident_allocate_synth_channel(trident); 377262306a36Sopenharmony_ci if(idx < 0) { 377362306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->voice_alloc, flags); 377462306a36Sopenharmony_ci return NULL; 377562306a36Sopenharmony_ci } 377662306a36Sopenharmony_ci pvoice = &trident->synth.voices[idx]; 377762306a36Sopenharmony_ci pvoice->use = 1; 377862306a36Sopenharmony_ci pvoice->synth = 1; 377962306a36Sopenharmony_ci pvoice->client = client; 378062306a36Sopenharmony_ci pvoice->port = port; 378162306a36Sopenharmony_ci pvoice->memblk = NULL; 378262306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->voice_alloc, flags); 378362306a36Sopenharmony_ci return pvoice; 378462306a36Sopenharmony_ci } 378562306a36Sopenharmony_ci if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) { 378662306a36Sopenharmony_ci } 378762306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->voice_alloc, flags); 378862306a36Sopenharmony_ci return NULL; 378962306a36Sopenharmony_ci} 379062306a36Sopenharmony_ci 379162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_trident_alloc_voice); 379262306a36Sopenharmony_ci 379362306a36Sopenharmony_civoid snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice) 379462306a36Sopenharmony_ci{ 379562306a36Sopenharmony_ci unsigned long flags; 379662306a36Sopenharmony_ci void (*private_free)(struct snd_trident_voice *); 379762306a36Sopenharmony_ci 379862306a36Sopenharmony_ci if (voice == NULL || !voice->use) 379962306a36Sopenharmony_ci return; 380062306a36Sopenharmony_ci snd_trident_clear_voices(trident, voice->number, voice->number); 380162306a36Sopenharmony_ci spin_lock_irqsave(&trident->voice_alloc, flags); 380262306a36Sopenharmony_ci private_free = voice->private_free; 380362306a36Sopenharmony_ci voice->private_free = NULL; 380462306a36Sopenharmony_ci voice->private_data = NULL; 380562306a36Sopenharmony_ci if (voice->pcm) 380662306a36Sopenharmony_ci snd_trident_free_pcm_channel(trident, voice->number); 380762306a36Sopenharmony_ci if (voice->synth) 380862306a36Sopenharmony_ci snd_trident_free_synth_channel(trident, voice->number); 380962306a36Sopenharmony_ci voice->use = voice->pcm = voice->synth = voice->midi = 0; 381062306a36Sopenharmony_ci voice->capture = voice->spdif = 0; 381162306a36Sopenharmony_ci voice->sample_ops = NULL; 381262306a36Sopenharmony_ci voice->substream = NULL; 381362306a36Sopenharmony_ci voice->extra = NULL; 381462306a36Sopenharmony_ci spin_unlock_irqrestore(&trident->voice_alloc, flags); 381562306a36Sopenharmony_ci if (private_free) 381662306a36Sopenharmony_ci private_free(voice); 381762306a36Sopenharmony_ci} 381862306a36Sopenharmony_ci 381962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_trident_free_voice); 382062306a36Sopenharmony_ci 382162306a36Sopenharmony_cistatic void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max) 382262306a36Sopenharmony_ci{ 382362306a36Sopenharmony_ci unsigned int i, val, mask[2] = { 0, 0 }; 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci if (snd_BUG_ON(v_min > 63 || v_max > 63)) 382662306a36Sopenharmony_ci return; 382762306a36Sopenharmony_ci for (i = v_min; i <= v_max; i++) 382862306a36Sopenharmony_ci mask[i >> 5] |= 1 << (i & 0x1f); 382962306a36Sopenharmony_ci if (mask[0]) { 383062306a36Sopenharmony_ci outl(mask[0], TRID_REG(trident, T4D_STOP_A)); 383162306a36Sopenharmony_ci val = inl(TRID_REG(trident, T4D_AINTEN_A)); 383262306a36Sopenharmony_ci outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A)); 383362306a36Sopenharmony_ci } 383462306a36Sopenharmony_ci if (mask[1]) { 383562306a36Sopenharmony_ci outl(mask[1], TRID_REG(trident, T4D_STOP_B)); 383662306a36Sopenharmony_ci val = inl(TRID_REG(trident, T4D_AINTEN_B)); 383762306a36Sopenharmony_ci outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B)); 383862306a36Sopenharmony_ci } 383962306a36Sopenharmony_ci} 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 384262306a36Sopenharmony_cistatic int snd_trident_suspend(struct device *dev) 384362306a36Sopenharmony_ci{ 384462306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 384562306a36Sopenharmony_ci struct snd_trident *trident = card->private_data; 384662306a36Sopenharmony_ci 384762306a36Sopenharmony_ci trident->in_suspend = 1; 384862306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 384962306a36Sopenharmony_ci snd_ac97_suspend(trident->ac97); 385062306a36Sopenharmony_ci snd_ac97_suspend(trident->ac97_sec); 385162306a36Sopenharmony_ci return 0; 385262306a36Sopenharmony_ci} 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_cistatic int snd_trident_resume(struct device *dev) 385562306a36Sopenharmony_ci{ 385662306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 385762306a36Sopenharmony_ci struct snd_trident *trident = card->private_data; 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci switch (trident->device) { 386062306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_DX: 386162306a36Sopenharmony_ci snd_trident_4d_dx_init(trident); 386262306a36Sopenharmony_ci break; 386362306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_NX: 386462306a36Sopenharmony_ci snd_trident_4d_nx_init(trident); 386562306a36Sopenharmony_ci break; 386662306a36Sopenharmony_ci case TRIDENT_DEVICE_ID_SI7018: 386762306a36Sopenharmony_ci snd_trident_sis_init(trident); 386862306a36Sopenharmony_ci break; 386962306a36Sopenharmony_ci } 387062306a36Sopenharmony_ci 387162306a36Sopenharmony_ci snd_ac97_resume(trident->ac97); 387262306a36Sopenharmony_ci snd_ac97_resume(trident->ac97_sec); 387362306a36Sopenharmony_ci 387462306a36Sopenharmony_ci /* restore some registers */ 387562306a36Sopenharmony_ci outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_ci snd_trident_enable_eso(trident); 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 388062306a36Sopenharmony_ci trident->in_suspend = 0; 388162306a36Sopenharmony_ci return 0; 388262306a36Sopenharmony_ci} 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ciSIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume); 388562306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 3886