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