xref: /OK3568_Linux_fs/kernel/sound/pci/trident/trident_main.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Maintained by Jaroslav Kysela <perex@perex.cz>
4*4882a593Smuzhiyun  *  Originated by audio@tridentmicro.com
5*4882a593Smuzhiyun  *  Fri Feb 19 15:55:28 MST 1999
6*4882a593Smuzhiyun  *  Routines for control of Trident 4DWave (DX and NX) chip
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *  BUGS:
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  *  TODO:
11*4882a593Smuzhiyun  *    ---
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  *  SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <linux/delay.h>
17*4882a593Smuzhiyun #include <linux/init.h>
18*4882a593Smuzhiyun #include <linux/interrupt.h>
19*4882a593Smuzhiyun #include <linux/pci.h>
20*4882a593Smuzhiyun #include <linux/slab.h>
21*4882a593Smuzhiyun #include <linux/vmalloc.h>
22*4882a593Smuzhiyun #include <linux/gameport.h>
23*4882a593Smuzhiyun #include <linux/dma-mapping.h>
24*4882a593Smuzhiyun #include <linux/export.h>
25*4882a593Smuzhiyun #include <linux/io.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include <sound/core.h>
28*4882a593Smuzhiyun #include <sound/info.h>
29*4882a593Smuzhiyun #include <sound/control.h>
30*4882a593Smuzhiyun #include <sound/tlv.h>
31*4882a593Smuzhiyun #include "trident.h"
32*4882a593Smuzhiyun #include <sound/asoundef.h>
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun static int snd_trident_pcm_mixer_build(struct snd_trident *trident,
35*4882a593Smuzhiyun 				       struct snd_trident_voice * voice,
36*4882a593Smuzhiyun 				       struct snd_pcm_substream *substream);
37*4882a593Smuzhiyun static int snd_trident_pcm_mixer_free(struct snd_trident *trident,
38*4882a593Smuzhiyun 				      struct snd_trident_voice * voice,
39*4882a593Smuzhiyun 				      struct snd_pcm_substream *substream);
40*4882a593Smuzhiyun static irqreturn_t snd_trident_interrupt(int irq, void *dev_id);
41*4882a593Smuzhiyun static int snd_trident_sis_reset(struct snd_trident *trident);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun static void snd_trident_clear_voices(struct snd_trident * trident,
44*4882a593Smuzhiyun 				     unsigned short v_min, unsigned short v_max);
45*4882a593Smuzhiyun static int snd_trident_free(struct snd_trident *trident);
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun /*
48*4882a593Smuzhiyun  *  common I/O routines
49*4882a593Smuzhiyun  */
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun #if 0
53*4882a593Smuzhiyun static void snd_trident_print_voice_regs(struct snd_trident *trident, int voice)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun 	unsigned int val, tmp;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "Trident voice %i:\n", voice);
58*4882a593Smuzhiyun 	outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
59*4882a593Smuzhiyun 	val = inl(TRID_REG(trident, CH_LBA));
60*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "LBA: 0x%x\n", val);
61*4882a593Smuzhiyun 	val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
62*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "GVSel: %i\n", val >> 31);
63*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "Pan: 0x%x\n", (val >> 24) & 0x7f);
64*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "Vol: 0x%x\n", (val >> 16) & 0xff);
65*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "CTRL: 0x%x\n", (val >> 12) & 0x0f);
66*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "EC: 0x%x\n", val & 0x0fff);
67*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_NX) {
68*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
69*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "CSO: 0x%x\n", val >> 16);
70*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "Alpha: 0x%x\n", (val >> 4) & 0x0fff);
71*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "FMS: 0x%x\n", val & 0x0f);
72*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
73*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "ESO: 0x%x\n", val >> 16);
74*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "Delta: 0x%x\n", val & 0xffff);
75*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
76*4882a593Smuzhiyun 	} else {		// TRIDENT_DEVICE_ID_NX
77*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
78*4882a593Smuzhiyun 		tmp = (val >> 24) & 0xff;
79*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "CSO: 0x%x\n", val & 0x00ffffff);
80*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
81*4882a593Smuzhiyun 		tmp |= (val >> 16) & 0xff00;
82*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "Delta: 0x%x\n", tmp);
83*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "ESO: 0x%x\n", val & 0x00ffffff);
84*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
85*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "Alpha: 0x%x\n", val >> 20);
86*4882a593Smuzhiyun 		dev_dbg(trident->card->dev, "FMS: 0x%x\n", (val >> 16) & 0x0f);
87*4882a593Smuzhiyun 	}
88*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "FMC: 0x%x\n", (val >> 14) & 3);
89*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "RVol: 0x%x\n", (val >> 7) & 0x7f);
90*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "CVol: 0x%x\n", val & 0x7f);
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun #endif
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun /*---------------------------------------------------------------------------
95*4882a593Smuzhiyun    unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun    Description: This routine will do all of the reading from the external
98*4882a593Smuzhiyun                 CODEC (AC97).
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun    Parameters:  ac97 - ac97 codec structure
101*4882a593Smuzhiyun                 reg - CODEC register index, from AC97 Hal.
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun    returns:     16 bit value read from the AC97.
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_codec_read(struct snd_ac97 * ac97,unsigned short reg)106*4882a593Smuzhiyun static unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun 	unsigned int data = 0, treg;
109*4882a593Smuzhiyun 	unsigned short count = 0xffff;
110*4882a593Smuzhiyun 	unsigned long flags;
111*4882a593Smuzhiyun 	struct snd_trident *trident = ac97->private_data;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	spin_lock_irqsave(&trident->reg_lock, flags);
114*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_DX) {
115*4882a593Smuzhiyun 		data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
116*4882a593Smuzhiyun 		outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
117*4882a593Smuzhiyun 		do {
118*4882a593Smuzhiyun 			data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
119*4882a593Smuzhiyun 			if ((data & DX_AC97_BUSY_READ) == 0)
120*4882a593Smuzhiyun 				break;
121*4882a593Smuzhiyun 		} while (--count);
122*4882a593Smuzhiyun 	} else if (trident->device == TRIDENT_DEVICE_ID_NX) {
123*4882a593Smuzhiyun 		data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
124*4882a593Smuzhiyun 		treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
125*4882a593Smuzhiyun 		outl(data, TRID_REG(trident, treg));
126*4882a593Smuzhiyun 		do {
127*4882a593Smuzhiyun 			data = inl(TRID_REG(trident, treg));
128*4882a593Smuzhiyun 			if ((data & 0x00000C00) == 0)
129*4882a593Smuzhiyun 				break;
130*4882a593Smuzhiyun 		} while (--count);
131*4882a593Smuzhiyun 	} else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
132*4882a593Smuzhiyun 		data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
133*4882a593Smuzhiyun 		if (ac97->num == 1)
134*4882a593Smuzhiyun 			data |= SI_AC97_SECONDARY;
135*4882a593Smuzhiyun 		outl(data, TRID_REG(trident, SI_AC97_READ));
136*4882a593Smuzhiyun 		do {
137*4882a593Smuzhiyun 			data = inl(TRID_REG(trident, SI_AC97_READ));
138*4882a593Smuzhiyun 			if ((data & (SI_AC97_BUSY_READ)) == 0)
139*4882a593Smuzhiyun 				break;
140*4882a593Smuzhiyun 		} while (--count);
141*4882a593Smuzhiyun 	}
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	if (count == 0 && !trident->ac97_detect) {
144*4882a593Smuzhiyun 		dev_err(trident->card->dev,
145*4882a593Smuzhiyun 			"ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n",
146*4882a593Smuzhiyun 			   reg, data);
147*4882a593Smuzhiyun 		data = 0;
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	spin_unlock_irqrestore(&trident->reg_lock, flags);
151*4882a593Smuzhiyun 	return ((unsigned short) (data >> 16));
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun /*---------------------------------------------------------------------------
155*4882a593Smuzhiyun    void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
156*4882a593Smuzhiyun    unsigned short wdata)
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun    Description: This routine will do all of the writing to the external
159*4882a593Smuzhiyun                 CODEC (AC97).
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun    Parameters:	ac97 - ac97 codec structure
162*4882a593Smuzhiyun    	        reg - CODEC register index, from AC97 Hal.
163*4882a593Smuzhiyun                 data  - Lower 16 bits are the data to write to CODEC.
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun    returns:     TRUE if everything went ok, else FALSE.
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_codec_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short wdata)168*4882a593Smuzhiyun static void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
169*4882a593Smuzhiyun 				    unsigned short wdata)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	unsigned int address, data;
172*4882a593Smuzhiyun 	unsigned short count = 0xffff;
173*4882a593Smuzhiyun 	unsigned long flags;
174*4882a593Smuzhiyun 	struct snd_trident *trident = ac97->private_data;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	data = ((unsigned long) wdata) << 16;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	spin_lock_irqsave(&trident->reg_lock, flags);
179*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_DX) {
180*4882a593Smuzhiyun 		address = DX_ACR0_AC97_W;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 		/* read AC-97 write register status */
183*4882a593Smuzhiyun 		do {
184*4882a593Smuzhiyun 			if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
185*4882a593Smuzhiyun 				break;
186*4882a593Smuzhiyun 		} while (--count);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 		data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
189*4882a593Smuzhiyun 	} else if (trident->device == TRIDENT_DEVICE_ID_NX) {
190*4882a593Smuzhiyun 		address = NX_ACR1_AC97_W;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 		/* read AC-97 write register status */
193*4882a593Smuzhiyun 		do {
194*4882a593Smuzhiyun 			if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
195*4882a593Smuzhiyun 				break;
196*4882a593Smuzhiyun 		} while (--count);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 		data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
199*4882a593Smuzhiyun 	} else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
200*4882a593Smuzhiyun 		address = SI_AC97_WRITE;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 		/* read AC-97 write register status */
203*4882a593Smuzhiyun 		do {
204*4882a593Smuzhiyun 			if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
205*4882a593Smuzhiyun 				break;
206*4882a593Smuzhiyun 		} while (--count);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 		data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
209*4882a593Smuzhiyun 		if (ac97->num == 1)
210*4882a593Smuzhiyun 			data |= SI_AC97_SECONDARY;
211*4882a593Smuzhiyun 	} else {
212*4882a593Smuzhiyun 		address = 0;	/* keep GCC happy */
213*4882a593Smuzhiyun 		count = 0;	/* return */
214*4882a593Smuzhiyun 	}
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	if (count == 0) {
217*4882a593Smuzhiyun 		spin_unlock_irqrestore(&trident->reg_lock, flags);
218*4882a593Smuzhiyun 		return;
219*4882a593Smuzhiyun 	}
220*4882a593Smuzhiyun 	outl(data, TRID_REG(trident, address));
221*4882a593Smuzhiyun 	spin_unlock_irqrestore(&trident->reg_lock, flags);
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun /*---------------------------------------------------------------------------
225*4882a593Smuzhiyun    void snd_trident_enable_eso(struct snd_trident *trident)
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun    Description: This routine will enable end of loop interrupts.
228*4882a593Smuzhiyun                 End of loop interrupts will occur when a running
229*4882a593Smuzhiyun                 channel reaches ESO.
230*4882a593Smuzhiyun                 Also enables middle of loop interrupts.
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
235*4882a593Smuzhiyun 
snd_trident_enable_eso(struct snd_trident * trident)236*4882a593Smuzhiyun static void snd_trident_enable_eso(struct snd_trident * trident)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun 	unsigned int val;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
241*4882a593Smuzhiyun 	val |= ENDLP_IE;
242*4882a593Smuzhiyun 	val |= MIDLP_IE;
243*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
244*4882a593Smuzhiyun 		val |= BANK_B_EN;
245*4882a593Smuzhiyun 	outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun /*---------------------------------------------------------------------------
249*4882a593Smuzhiyun    void snd_trident_disable_eso(struct snd_trident *trident)
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun    Description: This routine will disable end of loop interrupts.
252*4882a593Smuzhiyun                 End of loop interrupts will occur when a running
253*4882a593Smuzhiyun                 channel reaches ESO.
254*4882a593Smuzhiyun                 Also disables middle of loop interrupts.
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun    Parameters:
257*4882a593Smuzhiyun                 trident - pointer to target device class for 4DWave.
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun    returns:     TRUE if everything went ok, else FALSE.
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
262*4882a593Smuzhiyun 
snd_trident_disable_eso(struct snd_trident * trident)263*4882a593Smuzhiyun static void snd_trident_disable_eso(struct snd_trident * trident)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun 	unsigned int tmp;
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
268*4882a593Smuzhiyun 	tmp &= ~ENDLP_IE;
269*4882a593Smuzhiyun 	tmp &= ~MIDLP_IE;
270*4882a593Smuzhiyun 	outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun /*---------------------------------------------------------------------------
274*4882a593Smuzhiyun    void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun     Description: Start a voice, any channel 0 thru 63.
277*4882a593Smuzhiyun                  This routine automatically handles the fact that there are
278*4882a593Smuzhiyun                  more than 32 channels available.
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun     Parameters : voice - Voice number 0 thru n.
281*4882a593Smuzhiyun                  trident - pointer to target device class for 4DWave.
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun     Return Value: None.
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
286*4882a593Smuzhiyun 
snd_trident_start_voice(struct snd_trident * trident,unsigned int voice)287*4882a593Smuzhiyun void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun 	unsigned int mask = 1 << (voice & 0x1f);
290*4882a593Smuzhiyun 	unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	outl(mask, TRID_REG(trident, reg));
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun EXPORT_SYMBOL(snd_trident_start_voice);
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun /*---------------------------------------------------------------------------
298*4882a593Smuzhiyun    void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun     Description: Stop a voice, any channel 0 thru 63.
301*4882a593Smuzhiyun                  This routine automatically handles the fact that there are
302*4882a593Smuzhiyun                  more than 32 channels available.
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun     Parameters : voice - Voice number 0 thru n.
305*4882a593Smuzhiyun                  trident - pointer to target device class for 4DWave.
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun     Return Value: None.
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
310*4882a593Smuzhiyun 
snd_trident_stop_voice(struct snd_trident * trident,unsigned int voice)311*4882a593Smuzhiyun void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun 	unsigned int mask = 1 << (voice & 0x1f);
314*4882a593Smuzhiyun 	unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	outl(mask, TRID_REG(trident, reg));
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun EXPORT_SYMBOL(snd_trident_stop_voice);
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun /*---------------------------------------------------------------------------
322*4882a593Smuzhiyun     int snd_trident_allocate_pcm_channel(struct snd_trident *trident)
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun     Description: Allocate hardware channel in Bank B (32-63).
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun     Parameters :  trident - pointer to target device class for 4DWave.
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun     Return Value: hardware channel - 32-63 or -1 when no channel is available
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
331*4882a593Smuzhiyun 
snd_trident_allocate_pcm_channel(struct snd_trident * trident)332*4882a593Smuzhiyun static int snd_trident_allocate_pcm_channel(struct snd_trident * trident)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	int idx;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	if (trident->ChanPCMcnt >= trident->ChanPCM)
337*4882a593Smuzhiyun 		return -1;
338*4882a593Smuzhiyun 	for (idx = 31; idx >= 0; idx--) {
339*4882a593Smuzhiyun 		if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
340*4882a593Smuzhiyun 			trident->ChanMap[T4D_BANK_B] |= 1 << idx;
341*4882a593Smuzhiyun 			trident->ChanPCMcnt++;
342*4882a593Smuzhiyun 			return idx + 32;
343*4882a593Smuzhiyun 		}
344*4882a593Smuzhiyun 	}
345*4882a593Smuzhiyun 	return -1;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun /*---------------------------------------------------------------------------
349*4882a593Smuzhiyun     void snd_trident_free_pcm_channel(int channel)
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun     Description: Free hardware channel in Bank B (32-63)
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun     Parameters :  trident - pointer to target device class for 4DWave.
354*4882a593Smuzhiyun 	          channel - hardware channel number 0-63
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun     Return Value: none
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
359*4882a593Smuzhiyun 
snd_trident_free_pcm_channel(struct snd_trident * trident,int channel)360*4882a593Smuzhiyun static void snd_trident_free_pcm_channel(struct snd_trident *trident, int channel)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun 	if (channel < 32 || channel > 63)
363*4882a593Smuzhiyun 		return;
364*4882a593Smuzhiyun 	channel &= 0x1f;
365*4882a593Smuzhiyun 	if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
366*4882a593Smuzhiyun 		trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
367*4882a593Smuzhiyun 		trident->ChanPCMcnt--;
368*4882a593Smuzhiyun 	}
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun /*---------------------------------------------------------------------------
372*4882a593Smuzhiyun     unsigned int snd_trident_allocate_synth_channel(void)
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun     Description: Allocate hardware channel in Bank A (0-31).
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun     Parameters :  trident - pointer to target device class for 4DWave.
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun     Return Value: hardware channel - 0-31 or -1 when no channel is available
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
381*4882a593Smuzhiyun 
snd_trident_allocate_synth_channel(struct snd_trident * trident)382*4882a593Smuzhiyun static int snd_trident_allocate_synth_channel(struct snd_trident * trident)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun 	int idx;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	for (idx = 31; idx >= 0; idx--) {
387*4882a593Smuzhiyun 		if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
388*4882a593Smuzhiyun 			trident->ChanMap[T4D_BANK_A] |= 1 << idx;
389*4882a593Smuzhiyun 			trident->synth.ChanSynthCount++;
390*4882a593Smuzhiyun 			return idx;
391*4882a593Smuzhiyun 		}
392*4882a593Smuzhiyun 	}
393*4882a593Smuzhiyun 	return -1;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun /*---------------------------------------------------------------------------
397*4882a593Smuzhiyun     void snd_trident_free_synth_channel( int channel )
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun     Description: Free hardware channel in Bank B (0-31).
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun     Parameters :  trident - pointer to target device class for 4DWave.
402*4882a593Smuzhiyun 	          channel - hardware channel number 0-63
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun     Return Value: none
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
407*4882a593Smuzhiyun 
snd_trident_free_synth_channel(struct snd_trident * trident,int channel)408*4882a593Smuzhiyun static void snd_trident_free_synth_channel(struct snd_trident *trident, int channel)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	if (channel < 0 || channel > 31)
411*4882a593Smuzhiyun 		return;
412*4882a593Smuzhiyun 	channel &= 0x1f;
413*4882a593Smuzhiyun 	if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) {
414*4882a593Smuzhiyun 		trident->ChanMap[T4D_BANK_A] &= ~(1 << channel);
415*4882a593Smuzhiyun 		trident->synth.ChanSynthCount--;
416*4882a593Smuzhiyun 	}
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun /*---------------------------------------------------------------------------
420*4882a593Smuzhiyun    snd_trident_write_voice_regs
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun    Description: This routine will complete and write the 5 hardware channel
423*4882a593Smuzhiyun                 registers to hardware.
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
426*4882a593Smuzhiyun                 voice - synthesizer voice structure
427*4882a593Smuzhiyun                 Each register field.
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
430*4882a593Smuzhiyun 
snd_trident_write_voice_regs(struct snd_trident * trident,struct snd_trident_voice * voice)431*4882a593Smuzhiyun void snd_trident_write_voice_regs(struct snd_trident * trident,
432*4882a593Smuzhiyun 				  struct snd_trident_voice * voice)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun 	unsigned int FmcRvolCvol;
435*4882a593Smuzhiyun 	unsigned int regs[5];
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	regs[1] = voice->LBA;
438*4882a593Smuzhiyun 	regs[4] = (voice->GVSel << 31) |
439*4882a593Smuzhiyun 		  ((voice->Pan & 0x0000007f) << 24) |
440*4882a593Smuzhiyun 		  ((voice->CTRL & 0x0000000f) << 12);
441*4882a593Smuzhiyun 	FmcRvolCvol = ((voice->FMC & 3) << 14) |
442*4882a593Smuzhiyun 	              ((voice->RVol & 0x7f) << 7) |
443*4882a593Smuzhiyun 	              (voice->CVol & 0x7f);
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	switch (trident->device) {
446*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_SI7018:
447*4882a593Smuzhiyun 		regs[4] |= voice->number > 31 ?
448*4882a593Smuzhiyun 				(voice->Vol & 0x000003ff) :
449*4882a593Smuzhiyun 				((voice->Vol & 0x00003fc) << (16-2)) |
450*4882a593Smuzhiyun 				(voice->EC & 0x00000fff);
451*4882a593Smuzhiyun 		regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
452*4882a593Smuzhiyun 			(voice->FMS & 0x0000000f);
453*4882a593Smuzhiyun 		regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
454*4882a593Smuzhiyun 		regs[3] = (voice->Attribute << 16) | FmcRvolCvol;
455*4882a593Smuzhiyun 		break;
456*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_DX:
457*4882a593Smuzhiyun 		regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
458*4882a593Smuzhiyun 			   (voice->EC & 0x00000fff);
459*4882a593Smuzhiyun 		regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
460*4882a593Smuzhiyun 			(voice->FMS & 0x0000000f);
461*4882a593Smuzhiyun 		regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
462*4882a593Smuzhiyun 		regs[3] = FmcRvolCvol;
463*4882a593Smuzhiyun 		break;
464*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_NX:
465*4882a593Smuzhiyun 		regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
466*4882a593Smuzhiyun 			   (voice->EC & 0x00000fff);
467*4882a593Smuzhiyun 		regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff);
468*4882a593Smuzhiyun 		regs[2] = ((voice->Delta << 16) & 0xff000000) |
469*4882a593Smuzhiyun 			(voice->ESO & 0x00ffffff);
470*4882a593Smuzhiyun 		regs[3] = (voice->Alpha << 20) |
471*4882a593Smuzhiyun 			((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol;
472*4882a593Smuzhiyun 		break;
473*4882a593Smuzhiyun 	default:
474*4882a593Smuzhiyun 		snd_BUG();
475*4882a593Smuzhiyun 		return;
476*4882a593Smuzhiyun 	}
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
479*4882a593Smuzhiyun 	outl(regs[0], TRID_REG(trident, CH_START + 0));
480*4882a593Smuzhiyun 	outl(regs[1], TRID_REG(trident, CH_START + 4));
481*4882a593Smuzhiyun 	outl(regs[2], TRID_REG(trident, CH_START + 8));
482*4882a593Smuzhiyun 	outl(regs[3], TRID_REG(trident, CH_START + 12));
483*4882a593Smuzhiyun 	outl(regs[4], TRID_REG(trident, CH_START + 16));
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun #if 0
486*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "written %i channel:\n", voice->number);
487*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "  regs[0] = 0x%x/0x%x\n",
488*4882a593Smuzhiyun 	       regs[0], inl(TRID_REG(trident, CH_START + 0)));
489*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "  regs[1] = 0x%x/0x%x\n",
490*4882a593Smuzhiyun 	       regs[1], inl(TRID_REG(trident, CH_START + 4)));
491*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "  regs[2] = 0x%x/0x%x\n",
492*4882a593Smuzhiyun 	       regs[2], inl(TRID_REG(trident, CH_START + 8)));
493*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "  regs[3] = 0x%x/0x%x\n",
494*4882a593Smuzhiyun 	       regs[3], inl(TRID_REG(trident, CH_START + 12)));
495*4882a593Smuzhiyun 	dev_dbg(trident->card->dev, "  regs[4] = 0x%x/0x%x\n",
496*4882a593Smuzhiyun 	       regs[4], inl(TRID_REG(trident, CH_START + 16)));
497*4882a593Smuzhiyun #endif
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun EXPORT_SYMBOL(snd_trident_write_voice_regs);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun /*---------------------------------------------------------------------------
503*4882a593Smuzhiyun    snd_trident_write_cso_reg
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun    Description: This routine will write the new CSO offset
506*4882a593Smuzhiyun                 register to hardware.
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
509*4882a593Smuzhiyun                 voice - synthesizer voice structure
510*4882a593Smuzhiyun                 CSO - new CSO value
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
513*4882a593Smuzhiyun 
snd_trident_write_cso_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int CSO)514*4882a593Smuzhiyun static void snd_trident_write_cso_reg(struct snd_trident * trident,
515*4882a593Smuzhiyun 				      struct snd_trident_voice * voice,
516*4882a593Smuzhiyun 				      unsigned int CSO)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun 	voice->CSO = CSO;
519*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
520*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_NX) {
521*4882a593Smuzhiyun 		outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
522*4882a593Smuzhiyun 	} else {
523*4882a593Smuzhiyun 		outl((voice->Delta << 24) |
524*4882a593Smuzhiyun 		     (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO));
525*4882a593Smuzhiyun 	}
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun /*---------------------------------------------------------------------------
529*4882a593Smuzhiyun    snd_trident_write_eso_reg
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun    Description: This routine will write the new ESO offset
532*4882a593Smuzhiyun                 register to hardware.
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
535*4882a593Smuzhiyun                 voice - synthesizer voice structure
536*4882a593Smuzhiyun                 ESO - new ESO value
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
539*4882a593Smuzhiyun 
snd_trident_write_eso_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int ESO)540*4882a593Smuzhiyun static void snd_trident_write_eso_reg(struct snd_trident * trident,
541*4882a593Smuzhiyun 				      struct snd_trident_voice * voice,
542*4882a593Smuzhiyun 				      unsigned int ESO)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun 	voice->ESO = ESO;
545*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
546*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_NX) {
547*4882a593Smuzhiyun 		outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2);
548*4882a593Smuzhiyun 	} else {
549*4882a593Smuzhiyun 		outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff),
550*4882a593Smuzhiyun 		     TRID_REG(trident, CH_NX_DELTA_ESO));
551*4882a593Smuzhiyun 	}
552*4882a593Smuzhiyun }
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun /*---------------------------------------------------------------------------
555*4882a593Smuzhiyun    snd_trident_write_vol_reg
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun    Description: This routine will write the new voice volume
558*4882a593Smuzhiyun                 register to hardware.
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
561*4882a593Smuzhiyun                 voice - synthesizer voice structure
562*4882a593Smuzhiyun                 Vol - new voice volume
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
565*4882a593Smuzhiyun 
snd_trident_write_vol_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int Vol)566*4882a593Smuzhiyun static void snd_trident_write_vol_reg(struct snd_trident * trident,
567*4882a593Smuzhiyun 				      struct snd_trident_voice * voice,
568*4882a593Smuzhiyun 				      unsigned int Vol)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun 	voice->Vol = Vol;
571*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
572*4882a593Smuzhiyun 	switch (trident->device) {
573*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_DX:
574*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_NX:
575*4882a593Smuzhiyun 		outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
576*4882a593Smuzhiyun 		break;
577*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_SI7018:
578*4882a593Smuzhiyun 		/* dev_dbg(trident->card->dev, "voice->Vol = 0x%x\n", voice->Vol); */
579*4882a593Smuzhiyun 		outw((voice->CTRL << 12) | voice->Vol,
580*4882a593Smuzhiyun 		     TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
581*4882a593Smuzhiyun 		break;
582*4882a593Smuzhiyun 	}
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun /*---------------------------------------------------------------------------
586*4882a593Smuzhiyun    snd_trident_write_pan_reg
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun    Description: This routine will write the new voice pan
589*4882a593Smuzhiyun                 register to hardware.
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
592*4882a593Smuzhiyun                 voice - synthesizer voice structure
593*4882a593Smuzhiyun                 Pan - new pan value
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
596*4882a593Smuzhiyun 
snd_trident_write_pan_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int Pan)597*4882a593Smuzhiyun static void snd_trident_write_pan_reg(struct snd_trident * trident,
598*4882a593Smuzhiyun 				      struct snd_trident_voice * voice,
599*4882a593Smuzhiyun 				      unsigned int Pan)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun 	voice->Pan = Pan;
602*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
603*4882a593Smuzhiyun 	outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f),
604*4882a593Smuzhiyun 	     TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3));
605*4882a593Smuzhiyun }
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun /*---------------------------------------------------------------------------
608*4882a593Smuzhiyun    snd_trident_write_rvol_reg
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun    Description: This routine will write the new reverb volume
611*4882a593Smuzhiyun                 register to hardware.
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
614*4882a593Smuzhiyun                 voice - synthesizer voice structure
615*4882a593Smuzhiyun                 RVol - new reverb volume
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
618*4882a593Smuzhiyun 
snd_trident_write_rvol_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int RVol)619*4882a593Smuzhiyun static void snd_trident_write_rvol_reg(struct snd_trident * trident,
620*4882a593Smuzhiyun 				       struct snd_trident_voice * voice,
621*4882a593Smuzhiyun 				       unsigned int RVol)
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun 	voice->RVol = RVol;
624*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
625*4882a593Smuzhiyun 	outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
626*4882a593Smuzhiyun 	     (voice->CVol & 0x007f),
627*4882a593Smuzhiyun 	     TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
628*4882a593Smuzhiyun 		      CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun /*---------------------------------------------------------------------------
632*4882a593Smuzhiyun    snd_trident_write_cvol_reg
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun    Description: This routine will write the new chorus volume
635*4882a593Smuzhiyun                 register to hardware.
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
638*4882a593Smuzhiyun                 voice - synthesizer voice structure
639*4882a593Smuzhiyun                 CVol - new chorus volume
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
642*4882a593Smuzhiyun 
snd_trident_write_cvol_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int CVol)643*4882a593Smuzhiyun static void snd_trident_write_cvol_reg(struct snd_trident * trident,
644*4882a593Smuzhiyun 				       struct snd_trident_voice * voice,
645*4882a593Smuzhiyun 				       unsigned int CVol)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun 	voice->CVol = CVol;
648*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
649*4882a593Smuzhiyun 	outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
650*4882a593Smuzhiyun 	     (voice->CVol & 0x007f),
651*4882a593Smuzhiyun 	     TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
652*4882a593Smuzhiyun 		      CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun /*---------------------------------------------------------------------------
656*4882a593Smuzhiyun    snd_trident_convert_rate
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun    Description: This routine converts rate in HZ to hardware delta value.
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
661*4882a593Smuzhiyun                 rate - Real or Virtual channel number.
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun    Returns:     Delta value.
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_convert_rate(unsigned int rate)666*4882a593Smuzhiyun static unsigned int snd_trident_convert_rate(unsigned int rate)
667*4882a593Smuzhiyun {
668*4882a593Smuzhiyun 	unsigned int delta;
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun 	// We special case 44100 and 8000 since rounding with the equation
671*4882a593Smuzhiyun 	// does not give us an accurate enough value. For 11025 and 22050
672*4882a593Smuzhiyun 	// the equation gives us the best answer. All other frequencies will
673*4882a593Smuzhiyun 	// also use the equation. JDW
674*4882a593Smuzhiyun 	if (rate == 44100)
675*4882a593Smuzhiyun 		delta = 0xeb3;
676*4882a593Smuzhiyun 	else if (rate == 8000)
677*4882a593Smuzhiyun 		delta = 0x2ab;
678*4882a593Smuzhiyun 	else if (rate == 48000)
679*4882a593Smuzhiyun 		delta = 0x1000;
680*4882a593Smuzhiyun 	else
681*4882a593Smuzhiyun 		delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
682*4882a593Smuzhiyun 	return delta;
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun /*---------------------------------------------------------------------------
686*4882a593Smuzhiyun    snd_trident_convert_adc_rate
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun    Description: This routine converts rate in HZ to hardware delta value.
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
691*4882a593Smuzhiyun                 rate - Real or Virtual channel number.
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun    Returns:     Delta value.
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_convert_adc_rate(unsigned int rate)696*4882a593Smuzhiyun static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
697*4882a593Smuzhiyun {
698*4882a593Smuzhiyun 	unsigned int delta;
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	// We special case 44100 and 8000 since rounding with the equation
701*4882a593Smuzhiyun 	// does not give us an accurate enough value. For 11025 and 22050
702*4882a593Smuzhiyun 	// the equation gives us the best answer. All other frequencies will
703*4882a593Smuzhiyun 	// also use the equation. JDW
704*4882a593Smuzhiyun 	if (rate == 44100)
705*4882a593Smuzhiyun 		delta = 0x116a;
706*4882a593Smuzhiyun 	else if (rate == 8000)
707*4882a593Smuzhiyun 		delta = 0x6000;
708*4882a593Smuzhiyun 	else if (rate == 48000)
709*4882a593Smuzhiyun 		delta = 0x1000;
710*4882a593Smuzhiyun 	else
711*4882a593Smuzhiyun 		delta = ((48000 << 12) / rate) & 0x0000ffff;
712*4882a593Smuzhiyun 	return delta;
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun /*---------------------------------------------------------------------------
716*4882a593Smuzhiyun    snd_trident_spurious_threshold
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun    Description: This routine converts rate in HZ to spurious threshold.
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
721*4882a593Smuzhiyun                 rate - Real or Virtual channel number.
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun    Returns:     Delta value.
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_spurious_threshold(unsigned int rate,unsigned int period_size)726*4882a593Smuzhiyun static unsigned int snd_trident_spurious_threshold(unsigned int rate,
727*4882a593Smuzhiyun 						   unsigned int period_size)
728*4882a593Smuzhiyun {
729*4882a593Smuzhiyun 	unsigned int res = (rate * period_size) / 48000;
730*4882a593Smuzhiyun 	if (res < 64)
731*4882a593Smuzhiyun 		res = res / 2;
732*4882a593Smuzhiyun 	else
733*4882a593Smuzhiyun 		res -= 32;
734*4882a593Smuzhiyun 	return res;
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun 
737*4882a593Smuzhiyun /*---------------------------------------------------------------------------
738*4882a593Smuzhiyun    snd_trident_control_mode
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun    Description: This routine returns a control mode for a PCM channel.
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
743*4882a593Smuzhiyun                 substream  - PCM substream
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun    Returns:     Control value.
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_control_mode(struct snd_pcm_substream * substream)748*4882a593Smuzhiyun static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream)
749*4882a593Smuzhiyun {
750*4882a593Smuzhiyun 	unsigned int CTRL;
751*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 	/* set ctrl mode
754*4882a593Smuzhiyun 	   CTRL default: 8-bit (unsigned) mono, loop mode enabled
755*4882a593Smuzhiyun 	 */
756*4882a593Smuzhiyun 	CTRL = 0x00000001;
757*4882a593Smuzhiyun 	if (snd_pcm_format_width(runtime->format) == 16)
758*4882a593Smuzhiyun 		CTRL |= 0x00000008;	// 16-bit data
759*4882a593Smuzhiyun 	if (snd_pcm_format_signed(runtime->format))
760*4882a593Smuzhiyun 		CTRL |= 0x00000002;	// signed data
761*4882a593Smuzhiyun 	if (runtime->channels > 1)
762*4882a593Smuzhiyun 		CTRL |= 0x00000004;	// stereo data
763*4882a593Smuzhiyun 	return CTRL;
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun /*
767*4882a593Smuzhiyun  *  PCM part
768*4882a593Smuzhiyun  */
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun /*---------------------------------------------------------------------------
771*4882a593Smuzhiyun    snd_trident_allocate_pcm_mem
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun    Description: Allocate PCM ring buffer for given substream
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
776*4882a593Smuzhiyun 		hw_params  - hardware parameters
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun    Returns:     Error status
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
781*4882a593Smuzhiyun 
snd_trident_allocate_pcm_mem(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)782*4882a593Smuzhiyun static int snd_trident_allocate_pcm_mem(struct snd_pcm_substream *substream,
783*4882a593Smuzhiyun 					struct snd_pcm_hw_params *hw_params)
784*4882a593Smuzhiyun {
785*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
786*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
787*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 	if (trident->tlb.entries) {
790*4882a593Smuzhiyun 		if (runtime->buffer_changed) {
791*4882a593Smuzhiyun 			if (voice->memblk)
792*4882a593Smuzhiyun 				snd_trident_free_pages(trident, voice->memblk);
793*4882a593Smuzhiyun 			voice->memblk = snd_trident_alloc_pages(trident, substream);
794*4882a593Smuzhiyun 			if (voice->memblk == NULL)
795*4882a593Smuzhiyun 				return -ENOMEM;
796*4882a593Smuzhiyun 		}
797*4882a593Smuzhiyun 	}
798*4882a593Smuzhiyun 	return 0;
799*4882a593Smuzhiyun }
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun /*---------------------------------------------------------------------------
802*4882a593Smuzhiyun    snd_trident_allocate_evoice
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun    Description: Allocate extra voice as interrupt generator
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
807*4882a593Smuzhiyun 		hw_params  - hardware parameters
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun    Returns:     Error status
810*4882a593Smuzhiyun 
811*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
812*4882a593Smuzhiyun 
snd_trident_allocate_evoice(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)813*4882a593Smuzhiyun static int snd_trident_allocate_evoice(struct snd_pcm_substream *substream,
814*4882a593Smuzhiyun 				       struct snd_pcm_hw_params *hw_params)
815*4882a593Smuzhiyun {
816*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
817*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
818*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
819*4882a593Smuzhiyun 	struct snd_trident_voice *evoice = voice->extra;
820*4882a593Smuzhiyun 
821*4882a593Smuzhiyun 	/* voice management */
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
824*4882a593Smuzhiyun 		if (evoice == NULL) {
825*4882a593Smuzhiyun 			evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
826*4882a593Smuzhiyun 			if (evoice == NULL)
827*4882a593Smuzhiyun 				return -ENOMEM;
828*4882a593Smuzhiyun 			voice->extra = evoice;
829*4882a593Smuzhiyun 			evoice->substream = substream;
830*4882a593Smuzhiyun 		}
831*4882a593Smuzhiyun 	} else {
832*4882a593Smuzhiyun 		if (evoice != NULL) {
833*4882a593Smuzhiyun 			snd_trident_free_voice(trident, evoice);
834*4882a593Smuzhiyun 			voice->extra = evoice = NULL;
835*4882a593Smuzhiyun 		}
836*4882a593Smuzhiyun 	}
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	return 0;
839*4882a593Smuzhiyun }
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun /*---------------------------------------------------------------------------
842*4882a593Smuzhiyun    snd_trident_hw_params
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun    Description: Set the hardware parameters for the playback device.
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
847*4882a593Smuzhiyun 		hw_params  - hardware parameters
848*4882a593Smuzhiyun 
849*4882a593Smuzhiyun    Returns:     Error status
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
852*4882a593Smuzhiyun 
snd_trident_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)853*4882a593Smuzhiyun static int snd_trident_hw_params(struct snd_pcm_substream *substream,
854*4882a593Smuzhiyun 				 struct snd_pcm_hw_params *hw_params)
855*4882a593Smuzhiyun {
856*4882a593Smuzhiyun 	int err;
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun 	err = snd_trident_allocate_pcm_mem(substream, hw_params);
859*4882a593Smuzhiyun 	if (err >= 0)
860*4882a593Smuzhiyun 		err = snd_trident_allocate_evoice(substream, hw_params);
861*4882a593Smuzhiyun 	return err;
862*4882a593Smuzhiyun }
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun /*---------------------------------------------------------------------------
865*4882a593Smuzhiyun    snd_trident_playback_hw_free
866*4882a593Smuzhiyun 
867*4882a593Smuzhiyun    Description: Release the hardware resources for the playback device.
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun    Returns:     Error status
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
874*4882a593Smuzhiyun 
snd_trident_hw_free(struct snd_pcm_substream * substream)875*4882a593Smuzhiyun static int snd_trident_hw_free(struct snd_pcm_substream *substream)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
878*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
879*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
880*4882a593Smuzhiyun 	struct snd_trident_voice *evoice = voice ? voice->extra : NULL;
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 	if (trident->tlb.entries) {
883*4882a593Smuzhiyun 		if (voice && voice->memblk) {
884*4882a593Smuzhiyun 			snd_trident_free_pages(trident, voice->memblk);
885*4882a593Smuzhiyun 			voice->memblk = NULL;
886*4882a593Smuzhiyun 		}
887*4882a593Smuzhiyun 	}
888*4882a593Smuzhiyun 	if (evoice != NULL) {
889*4882a593Smuzhiyun 		snd_trident_free_voice(trident, evoice);
890*4882a593Smuzhiyun 		voice->extra = NULL;
891*4882a593Smuzhiyun 	}
892*4882a593Smuzhiyun 	return 0;
893*4882a593Smuzhiyun }
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun /*---------------------------------------------------------------------------
896*4882a593Smuzhiyun    snd_trident_playback_prepare
897*4882a593Smuzhiyun 
898*4882a593Smuzhiyun    Description: Prepare playback device for playback.
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
901*4882a593Smuzhiyun 
902*4882a593Smuzhiyun    Returns:     Error status
903*4882a593Smuzhiyun 
904*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
905*4882a593Smuzhiyun 
snd_trident_playback_prepare(struct snd_pcm_substream * substream)906*4882a593Smuzhiyun static int snd_trident_playback_prepare(struct snd_pcm_substream *substream)
907*4882a593Smuzhiyun {
908*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
909*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
910*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
911*4882a593Smuzhiyun 	struct snd_trident_voice *evoice = voice->extra;
912*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 	/* set delta (rate) value */
917*4882a593Smuzhiyun 	voice->Delta = snd_trident_convert_rate(runtime->rate);
918*4882a593Smuzhiyun 	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun 	/* set Loop Begin Address */
921*4882a593Smuzhiyun 	if (voice->memblk)
922*4882a593Smuzhiyun 		voice->LBA = voice->memblk->offset;
923*4882a593Smuzhiyun 	else
924*4882a593Smuzhiyun 		voice->LBA = runtime->dma_addr;
925*4882a593Smuzhiyun 
926*4882a593Smuzhiyun 	voice->CSO = 0;
927*4882a593Smuzhiyun 	voice->ESO = runtime->buffer_size - 1;	/* in samples */
928*4882a593Smuzhiyun 	voice->CTRL = snd_trident_control_mode(substream);
929*4882a593Smuzhiyun 	voice->FMC = 3;
930*4882a593Smuzhiyun 	voice->GVSel = 1;
931*4882a593Smuzhiyun 	voice->EC = 0;
932*4882a593Smuzhiyun 	voice->Alpha = 0;
933*4882a593Smuzhiyun 	voice->FMS = 0;
934*4882a593Smuzhiyun 	voice->Vol = mix->vol;
935*4882a593Smuzhiyun 	voice->RVol = mix->rvol;
936*4882a593Smuzhiyun 	voice->CVol = mix->cvol;
937*4882a593Smuzhiyun 	voice->Pan = mix->pan;
938*4882a593Smuzhiyun 	voice->Attribute = 0;
939*4882a593Smuzhiyun #if 0
940*4882a593Smuzhiyun 	voice->Attribute = (1<<(30-16))|(2<<(26-16))|
941*4882a593Smuzhiyun 			   (0<<(24-16))|(0x1f<<(19-16));
942*4882a593Smuzhiyun #else
943*4882a593Smuzhiyun 	voice->Attribute = 0;
944*4882a593Smuzhiyun #endif
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun 	snd_trident_write_voice_regs(trident, voice);
947*4882a593Smuzhiyun 
948*4882a593Smuzhiyun 	if (evoice != NULL) {
949*4882a593Smuzhiyun 		evoice->Delta = voice->Delta;
950*4882a593Smuzhiyun 		evoice->spurious_threshold = voice->spurious_threshold;
951*4882a593Smuzhiyun 		evoice->LBA = voice->LBA;
952*4882a593Smuzhiyun 		evoice->CSO = 0;
953*4882a593Smuzhiyun 		evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
954*4882a593Smuzhiyun 		evoice->CTRL = voice->CTRL;
955*4882a593Smuzhiyun 		evoice->FMC = 3;
956*4882a593Smuzhiyun 		evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
957*4882a593Smuzhiyun 		evoice->EC = 0;
958*4882a593Smuzhiyun 		evoice->Alpha = 0;
959*4882a593Smuzhiyun 		evoice->FMS = 0;
960*4882a593Smuzhiyun 		evoice->Vol = 0x3ff;			/* mute */
961*4882a593Smuzhiyun 		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
962*4882a593Smuzhiyun 		evoice->Pan = 0x7f;			/* mute */
963*4882a593Smuzhiyun #if 0
964*4882a593Smuzhiyun 		evoice->Attribute = (1<<(30-16))|(2<<(26-16))|
965*4882a593Smuzhiyun 				    (0<<(24-16))|(0x1f<<(19-16));
966*4882a593Smuzhiyun #else
967*4882a593Smuzhiyun 		evoice->Attribute = 0;
968*4882a593Smuzhiyun #endif
969*4882a593Smuzhiyun 		snd_trident_write_voice_regs(trident, evoice);
970*4882a593Smuzhiyun 		evoice->isync2 = 1;
971*4882a593Smuzhiyun 		evoice->isync_mark = runtime->period_size;
972*4882a593Smuzhiyun 		evoice->ESO = (runtime->period_size * 2) - 1;
973*4882a593Smuzhiyun 	}
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
976*4882a593Smuzhiyun 
977*4882a593Smuzhiyun 	return 0;
978*4882a593Smuzhiyun }
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun /*---------------------------------------------------------------------------
981*4882a593Smuzhiyun    snd_trident_capture_hw_params
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun    Description: Set the hardware parameters for the capture device.
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
986*4882a593Smuzhiyun 		hw_params  - hardware parameters
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun    Returns:     Error status
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
991*4882a593Smuzhiyun 
snd_trident_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)992*4882a593Smuzhiyun static int snd_trident_capture_hw_params(struct snd_pcm_substream *substream,
993*4882a593Smuzhiyun 					 struct snd_pcm_hw_params *hw_params)
994*4882a593Smuzhiyun {
995*4882a593Smuzhiyun 	return snd_trident_allocate_pcm_mem(substream, hw_params);
996*4882a593Smuzhiyun }
997*4882a593Smuzhiyun 
998*4882a593Smuzhiyun /*---------------------------------------------------------------------------
999*4882a593Smuzhiyun    snd_trident_capture_prepare
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun    Description: Prepare capture device for playback.
1002*4882a593Smuzhiyun 
1003*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun    Returns:     Error status
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1008*4882a593Smuzhiyun 
snd_trident_capture_prepare(struct snd_pcm_substream * substream)1009*4882a593Smuzhiyun static int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
1010*4882a593Smuzhiyun {
1011*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1012*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1013*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1014*4882a593Smuzhiyun 	unsigned int val, ESO_bytes;
1015*4882a593Smuzhiyun 
1016*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	// Initialize the channel and set channel Mode
1019*4882a593Smuzhiyun 	outb(0, TRID_REG(trident, LEGACY_DMAR15));
1020*4882a593Smuzhiyun 
1021*4882a593Smuzhiyun 	// Set DMA channel operation mode register
1022*4882a593Smuzhiyun 	outb(0x54, TRID_REG(trident, LEGACY_DMAR11));
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun 	// Set channel buffer Address, DMAR0 expects contiguous PCI memory area
1025*4882a593Smuzhiyun 	voice->LBA = runtime->dma_addr;
1026*4882a593Smuzhiyun 	outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0));
1027*4882a593Smuzhiyun 	if (voice->memblk)
1028*4882a593Smuzhiyun 		voice->LBA = voice->memblk->offset;
1029*4882a593Smuzhiyun 
1030*4882a593Smuzhiyun 	// set ESO
1031*4882a593Smuzhiyun 	ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1;
1032*4882a593Smuzhiyun 	outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6));
1033*4882a593Smuzhiyun 	outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4));
1034*4882a593Smuzhiyun 	ESO_bytes++;
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	// Set channel sample rate, 4.12 format
1037*4882a593Smuzhiyun 	val = (((unsigned int) 48000L << 12) + (runtime->rate/2)) / runtime->rate;
1038*4882a593Smuzhiyun 	outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun 	// Set channel interrupt blk length
1041*4882a593Smuzhiyun 	if (snd_pcm_format_width(runtime->format) == 16) {
1042*4882a593Smuzhiyun 		val = (unsigned short) ((ESO_bytes >> 1) - 1);
1043*4882a593Smuzhiyun 	} else {
1044*4882a593Smuzhiyun 		val = (unsigned short) (ESO_bytes - 1);
1045*4882a593Smuzhiyun 	}
1046*4882a593Smuzhiyun 
1047*4882a593Smuzhiyun 	outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL));
1048*4882a593Smuzhiyun 
1049*4882a593Smuzhiyun 	// Right now, set format and start to run captureing,
1050*4882a593Smuzhiyun 	// continuous run loop enable.
1051*4882a593Smuzhiyun 	trident->bDMAStart = 0x19;	// 0001 1001b
1052*4882a593Smuzhiyun 
1053*4882a593Smuzhiyun 	if (snd_pcm_format_width(runtime->format) == 16)
1054*4882a593Smuzhiyun 		trident->bDMAStart |= 0x80;
1055*4882a593Smuzhiyun 	if (snd_pcm_format_signed(runtime->format))
1056*4882a593Smuzhiyun 		trident->bDMAStart |= 0x20;
1057*4882a593Smuzhiyun 	if (runtime->channels > 1)
1058*4882a593Smuzhiyun 		trident->bDMAStart |= 0x40;
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun 	// Prepare capture intr channel
1061*4882a593Smuzhiyun 
1062*4882a593Smuzhiyun 	voice->Delta = snd_trident_convert_rate(runtime->rate);
1063*4882a593Smuzhiyun 	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
1064*4882a593Smuzhiyun 	voice->isync = 1;
1065*4882a593Smuzhiyun 	voice->isync_mark = runtime->period_size;
1066*4882a593Smuzhiyun 	voice->isync_max = runtime->buffer_size;
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun 	// Set voice parameters
1069*4882a593Smuzhiyun 	voice->CSO = 0;
1070*4882a593Smuzhiyun 	voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
1071*4882a593Smuzhiyun 	voice->CTRL = snd_trident_control_mode(substream);
1072*4882a593Smuzhiyun 	voice->FMC = 3;
1073*4882a593Smuzhiyun 	voice->RVol = 0x7f;
1074*4882a593Smuzhiyun 	voice->CVol = 0x7f;
1075*4882a593Smuzhiyun 	voice->GVSel = 1;
1076*4882a593Smuzhiyun 	voice->Pan = 0x7f;		/* mute */
1077*4882a593Smuzhiyun 	voice->Vol = 0x3ff;		/* mute */
1078*4882a593Smuzhiyun 	voice->EC = 0;
1079*4882a593Smuzhiyun 	voice->Alpha = 0;
1080*4882a593Smuzhiyun 	voice->FMS = 0;
1081*4882a593Smuzhiyun 	voice->Attribute = 0;
1082*4882a593Smuzhiyun 
1083*4882a593Smuzhiyun 	snd_trident_write_voice_regs(trident, voice);
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
1086*4882a593Smuzhiyun 	return 0;
1087*4882a593Smuzhiyun }
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1090*4882a593Smuzhiyun    snd_trident_si7018_capture_hw_params
1091*4882a593Smuzhiyun 
1092*4882a593Smuzhiyun    Description: Set the hardware parameters for the capture device.
1093*4882a593Smuzhiyun 
1094*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1095*4882a593Smuzhiyun 		hw_params  - hardware parameters
1096*4882a593Smuzhiyun 
1097*4882a593Smuzhiyun    Returns:     Error status
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1100*4882a593Smuzhiyun 
snd_trident_si7018_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)1101*4882a593Smuzhiyun static int snd_trident_si7018_capture_hw_params(struct snd_pcm_substream *substream,
1102*4882a593Smuzhiyun 						struct snd_pcm_hw_params *hw_params)
1103*4882a593Smuzhiyun {
1104*4882a593Smuzhiyun 	return snd_trident_allocate_evoice(substream, hw_params);
1105*4882a593Smuzhiyun }
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1108*4882a593Smuzhiyun    snd_trident_si7018_capture_hw_free
1109*4882a593Smuzhiyun 
1110*4882a593Smuzhiyun    Description: Release the hardware resources for the capture device.
1111*4882a593Smuzhiyun 
1112*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun    Returns:     Error status
1115*4882a593Smuzhiyun 
1116*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1117*4882a593Smuzhiyun 
snd_trident_si7018_capture_hw_free(struct snd_pcm_substream * substream)1118*4882a593Smuzhiyun static int snd_trident_si7018_capture_hw_free(struct snd_pcm_substream *substream)
1119*4882a593Smuzhiyun {
1120*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1121*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1122*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1123*4882a593Smuzhiyun 	struct snd_trident_voice *evoice = voice ? voice->extra : NULL;
1124*4882a593Smuzhiyun 
1125*4882a593Smuzhiyun 	if (evoice != NULL) {
1126*4882a593Smuzhiyun 		snd_trident_free_voice(trident, evoice);
1127*4882a593Smuzhiyun 		voice->extra = NULL;
1128*4882a593Smuzhiyun 	}
1129*4882a593Smuzhiyun 	return 0;
1130*4882a593Smuzhiyun }
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1133*4882a593Smuzhiyun    snd_trident_si7018_capture_prepare
1134*4882a593Smuzhiyun 
1135*4882a593Smuzhiyun    Description: Prepare capture device for playback.
1136*4882a593Smuzhiyun 
1137*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1138*4882a593Smuzhiyun 
1139*4882a593Smuzhiyun    Returns:     Error status
1140*4882a593Smuzhiyun 
1141*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1142*4882a593Smuzhiyun 
snd_trident_si7018_capture_prepare(struct snd_pcm_substream * substream)1143*4882a593Smuzhiyun static int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substream)
1144*4882a593Smuzhiyun {
1145*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1146*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1147*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1148*4882a593Smuzhiyun 	struct snd_trident_voice *evoice = voice->extra;
1149*4882a593Smuzhiyun 
1150*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
1151*4882a593Smuzhiyun 
1152*4882a593Smuzhiyun 	voice->LBA = runtime->dma_addr;
1153*4882a593Smuzhiyun 	voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
1154*4882a593Smuzhiyun 	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
1155*4882a593Smuzhiyun 
1156*4882a593Smuzhiyun 	// Set voice parameters
1157*4882a593Smuzhiyun 	voice->CSO = 0;
1158*4882a593Smuzhiyun 	voice->ESO = runtime->buffer_size - 1;		/* in samples */
1159*4882a593Smuzhiyun 	voice->CTRL = snd_trident_control_mode(substream);
1160*4882a593Smuzhiyun 	voice->FMC = 0;
1161*4882a593Smuzhiyun 	voice->RVol = 0;
1162*4882a593Smuzhiyun 	voice->CVol = 0;
1163*4882a593Smuzhiyun 	voice->GVSel = 1;
1164*4882a593Smuzhiyun 	voice->Pan = T4D_DEFAULT_PCM_PAN;
1165*4882a593Smuzhiyun 	voice->Vol = 0;
1166*4882a593Smuzhiyun 	voice->EC = 0;
1167*4882a593Smuzhiyun 	voice->Alpha = 0;
1168*4882a593Smuzhiyun 	voice->FMS = 0;
1169*4882a593Smuzhiyun 
1170*4882a593Smuzhiyun 	voice->Attribute = (2 << (30-16)) |
1171*4882a593Smuzhiyun 			   (2 << (26-16)) |
1172*4882a593Smuzhiyun 			   (2 << (24-16)) |
1173*4882a593Smuzhiyun 			   (1 << (23-16));
1174*4882a593Smuzhiyun 
1175*4882a593Smuzhiyun 	snd_trident_write_voice_regs(trident, voice);
1176*4882a593Smuzhiyun 
1177*4882a593Smuzhiyun 	if (evoice != NULL) {
1178*4882a593Smuzhiyun 		evoice->Delta = snd_trident_convert_rate(runtime->rate);
1179*4882a593Smuzhiyun 		evoice->spurious_threshold = voice->spurious_threshold;
1180*4882a593Smuzhiyun 		evoice->LBA = voice->LBA;
1181*4882a593Smuzhiyun 		evoice->CSO = 0;
1182*4882a593Smuzhiyun 		evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */
1183*4882a593Smuzhiyun 		evoice->CTRL = voice->CTRL;
1184*4882a593Smuzhiyun 		evoice->FMC = 3;
1185*4882a593Smuzhiyun 		evoice->GVSel = 0;
1186*4882a593Smuzhiyun 		evoice->EC = 0;
1187*4882a593Smuzhiyun 		evoice->Alpha = 0;
1188*4882a593Smuzhiyun 		evoice->FMS = 0;
1189*4882a593Smuzhiyun 		evoice->Vol = 0x3ff;			/* mute */
1190*4882a593Smuzhiyun 		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
1191*4882a593Smuzhiyun 		evoice->Pan = 0x7f;			/* mute */
1192*4882a593Smuzhiyun 		evoice->Attribute = 0;
1193*4882a593Smuzhiyun 		snd_trident_write_voice_regs(trident, evoice);
1194*4882a593Smuzhiyun 		evoice->isync2 = 1;
1195*4882a593Smuzhiyun 		evoice->isync_mark = runtime->period_size;
1196*4882a593Smuzhiyun 		evoice->ESO = (runtime->period_size * 2) - 1;
1197*4882a593Smuzhiyun 	}
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
1200*4882a593Smuzhiyun 	return 0;
1201*4882a593Smuzhiyun }
1202*4882a593Smuzhiyun 
1203*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1204*4882a593Smuzhiyun    snd_trident_foldback_prepare
1205*4882a593Smuzhiyun 
1206*4882a593Smuzhiyun    Description: Prepare foldback capture device for playback.
1207*4882a593Smuzhiyun 
1208*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1209*4882a593Smuzhiyun 
1210*4882a593Smuzhiyun    Returns:     Error status
1211*4882a593Smuzhiyun 
1212*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1213*4882a593Smuzhiyun 
snd_trident_foldback_prepare(struct snd_pcm_substream * substream)1214*4882a593Smuzhiyun static int snd_trident_foldback_prepare(struct snd_pcm_substream *substream)
1215*4882a593Smuzhiyun {
1216*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1217*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1218*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1219*4882a593Smuzhiyun 	struct snd_trident_voice *evoice = voice->extra;
1220*4882a593Smuzhiyun 
1221*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
1222*4882a593Smuzhiyun 
1223*4882a593Smuzhiyun 	/* Set channel buffer Address */
1224*4882a593Smuzhiyun 	if (voice->memblk)
1225*4882a593Smuzhiyun 		voice->LBA = voice->memblk->offset;
1226*4882a593Smuzhiyun 	else
1227*4882a593Smuzhiyun 		voice->LBA = runtime->dma_addr;
1228*4882a593Smuzhiyun 
1229*4882a593Smuzhiyun 	/* set target ESO for channel */
1230*4882a593Smuzhiyun 	voice->ESO = runtime->buffer_size - 1;	/* in samples */
1231*4882a593Smuzhiyun 
1232*4882a593Smuzhiyun 	/* set sample rate */
1233*4882a593Smuzhiyun 	voice->Delta = 0x1000;
1234*4882a593Smuzhiyun 	voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
1235*4882a593Smuzhiyun 
1236*4882a593Smuzhiyun 	voice->CSO = 0;
1237*4882a593Smuzhiyun 	voice->CTRL = snd_trident_control_mode(substream);
1238*4882a593Smuzhiyun 	voice->FMC = 3;
1239*4882a593Smuzhiyun 	voice->RVol = 0x7f;
1240*4882a593Smuzhiyun 	voice->CVol = 0x7f;
1241*4882a593Smuzhiyun 	voice->GVSel = 1;
1242*4882a593Smuzhiyun 	voice->Pan = 0x7f;	/* mute */
1243*4882a593Smuzhiyun 	voice->Vol = 0x3ff;	/* mute */
1244*4882a593Smuzhiyun 	voice->EC = 0;
1245*4882a593Smuzhiyun 	voice->Alpha = 0;
1246*4882a593Smuzhiyun 	voice->FMS = 0;
1247*4882a593Smuzhiyun 	voice->Attribute = 0;
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun 	/* set up capture channel */
1250*4882a593Smuzhiyun 	outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan));
1251*4882a593Smuzhiyun 
1252*4882a593Smuzhiyun 	snd_trident_write_voice_regs(trident, voice);
1253*4882a593Smuzhiyun 
1254*4882a593Smuzhiyun 	if (evoice != NULL) {
1255*4882a593Smuzhiyun 		evoice->Delta = voice->Delta;
1256*4882a593Smuzhiyun 		evoice->spurious_threshold = voice->spurious_threshold;
1257*4882a593Smuzhiyun 		evoice->LBA = voice->LBA;
1258*4882a593Smuzhiyun 		evoice->CSO = 0;
1259*4882a593Smuzhiyun 		evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
1260*4882a593Smuzhiyun 		evoice->CTRL = voice->CTRL;
1261*4882a593Smuzhiyun 		evoice->FMC = 3;
1262*4882a593Smuzhiyun 		evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
1263*4882a593Smuzhiyun 		evoice->EC = 0;
1264*4882a593Smuzhiyun 		evoice->Alpha = 0;
1265*4882a593Smuzhiyun 		evoice->FMS = 0;
1266*4882a593Smuzhiyun 		evoice->Vol = 0x3ff;			/* mute */
1267*4882a593Smuzhiyun 		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
1268*4882a593Smuzhiyun 		evoice->Pan = 0x7f;			/* mute */
1269*4882a593Smuzhiyun 		evoice->Attribute = 0;
1270*4882a593Smuzhiyun 		snd_trident_write_voice_regs(trident, evoice);
1271*4882a593Smuzhiyun 		evoice->isync2 = 1;
1272*4882a593Smuzhiyun 		evoice->isync_mark = runtime->period_size;
1273*4882a593Smuzhiyun 		evoice->ESO = (runtime->period_size * 2) - 1;
1274*4882a593Smuzhiyun 	}
1275*4882a593Smuzhiyun 
1276*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
1277*4882a593Smuzhiyun 	return 0;
1278*4882a593Smuzhiyun }
1279*4882a593Smuzhiyun 
1280*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1281*4882a593Smuzhiyun    snd_trident_spdif_hw_params
1282*4882a593Smuzhiyun 
1283*4882a593Smuzhiyun    Description: Set the hardware parameters for the spdif device.
1284*4882a593Smuzhiyun 
1285*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1286*4882a593Smuzhiyun 		hw_params  - hardware parameters
1287*4882a593Smuzhiyun 
1288*4882a593Smuzhiyun    Returns:     Error status
1289*4882a593Smuzhiyun 
1290*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1291*4882a593Smuzhiyun 
snd_trident_spdif_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)1292*4882a593Smuzhiyun static int snd_trident_spdif_hw_params(struct snd_pcm_substream *substream,
1293*4882a593Smuzhiyun 				       struct snd_pcm_hw_params *hw_params)
1294*4882a593Smuzhiyun {
1295*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1296*4882a593Smuzhiyun 	unsigned int old_bits = 0, change = 0;
1297*4882a593Smuzhiyun 	int err;
1298*4882a593Smuzhiyun 
1299*4882a593Smuzhiyun 	err = snd_trident_allocate_pcm_mem(substream, hw_params);
1300*4882a593Smuzhiyun 	if (err < 0)
1301*4882a593Smuzhiyun 		return err;
1302*4882a593Smuzhiyun 
1303*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
1304*4882a593Smuzhiyun 		err = snd_trident_allocate_evoice(substream, hw_params);
1305*4882a593Smuzhiyun 		if (err < 0)
1306*4882a593Smuzhiyun 			return err;
1307*4882a593Smuzhiyun 	}
1308*4882a593Smuzhiyun 
1309*4882a593Smuzhiyun 	/* prepare SPDIF channel */
1310*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
1311*4882a593Smuzhiyun 	old_bits = trident->spdif_pcm_bits;
1312*4882a593Smuzhiyun 	if (old_bits & IEC958_AES0_PROFESSIONAL)
1313*4882a593Smuzhiyun 		trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
1314*4882a593Smuzhiyun 	else
1315*4882a593Smuzhiyun 		trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
1316*4882a593Smuzhiyun 	if (params_rate(hw_params) >= 48000) {
1317*4882a593Smuzhiyun 		trident->spdif_pcm_ctrl = 0x3c;	// 48000 Hz
1318*4882a593Smuzhiyun 		trident->spdif_pcm_bits |=
1319*4882a593Smuzhiyun 			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
1320*4882a593Smuzhiyun 				IEC958_AES0_PRO_FS_48000 :
1321*4882a593Smuzhiyun 				(IEC958_AES3_CON_FS_48000 << 24);
1322*4882a593Smuzhiyun 	}
1323*4882a593Smuzhiyun 	else if (params_rate(hw_params) >= 44100) {
1324*4882a593Smuzhiyun 		trident->spdif_pcm_ctrl = 0x3e;	// 44100 Hz
1325*4882a593Smuzhiyun 		trident->spdif_pcm_bits |=
1326*4882a593Smuzhiyun 			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
1327*4882a593Smuzhiyun 				IEC958_AES0_PRO_FS_44100 :
1328*4882a593Smuzhiyun 				(IEC958_AES3_CON_FS_44100 << 24);
1329*4882a593Smuzhiyun 	}
1330*4882a593Smuzhiyun 	else {
1331*4882a593Smuzhiyun 		trident->spdif_pcm_ctrl = 0x3d;	// 32000 Hz
1332*4882a593Smuzhiyun 		trident->spdif_pcm_bits |=
1333*4882a593Smuzhiyun 			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
1334*4882a593Smuzhiyun 				IEC958_AES0_PRO_FS_32000 :
1335*4882a593Smuzhiyun 				(IEC958_AES3_CON_FS_32000 << 24);
1336*4882a593Smuzhiyun 	}
1337*4882a593Smuzhiyun 	change = old_bits != trident->spdif_pcm_bits;
1338*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
1339*4882a593Smuzhiyun 
1340*4882a593Smuzhiyun 	if (change)
1341*4882a593Smuzhiyun 		snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);
1342*4882a593Smuzhiyun 
1343*4882a593Smuzhiyun 	return 0;
1344*4882a593Smuzhiyun }
1345*4882a593Smuzhiyun 
1346*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1347*4882a593Smuzhiyun    snd_trident_spdif_prepare
1348*4882a593Smuzhiyun 
1349*4882a593Smuzhiyun    Description: Prepare SPDIF device for playback.
1350*4882a593Smuzhiyun 
1351*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1352*4882a593Smuzhiyun 
1353*4882a593Smuzhiyun    Returns:     Error status
1354*4882a593Smuzhiyun 
1355*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1356*4882a593Smuzhiyun 
snd_trident_spdif_prepare(struct snd_pcm_substream * substream)1357*4882a593Smuzhiyun static int snd_trident_spdif_prepare(struct snd_pcm_substream *substream)
1358*4882a593Smuzhiyun {
1359*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1360*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1361*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1362*4882a593Smuzhiyun 	struct snd_trident_voice *evoice = voice->extra;
1363*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
1364*4882a593Smuzhiyun 	unsigned int RESO, LBAO;
1365*4882a593Smuzhiyun 	unsigned int temp;
1366*4882a593Smuzhiyun 
1367*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
1368*4882a593Smuzhiyun 
1369*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
1370*4882a593Smuzhiyun 
1371*4882a593Smuzhiyun 		/* set delta (rate) value */
1372*4882a593Smuzhiyun 		voice->Delta = snd_trident_convert_rate(runtime->rate);
1373*4882a593Smuzhiyun 		voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
1374*4882a593Smuzhiyun 
1375*4882a593Smuzhiyun 		/* set Loop Back Address */
1376*4882a593Smuzhiyun 		LBAO = runtime->dma_addr;
1377*4882a593Smuzhiyun 		if (voice->memblk)
1378*4882a593Smuzhiyun 			voice->LBA = voice->memblk->offset;
1379*4882a593Smuzhiyun 		else
1380*4882a593Smuzhiyun 			voice->LBA = LBAO;
1381*4882a593Smuzhiyun 
1382*4882a593Smuzhiyun 		voice->isync = 1;
1383*4882a593Smuzhiyun 		voice->isync3 = 1;
1384*4882a593Smuzhiyun 		voice->isync_mark = runtime->period_size;
1385*4882a593Smuzhiyun 		voice->isync_max = runtime->buffer_size;
1386*4882a593Smuzhiyun 
1387*4882a593Smuzhiyun 		/* set target ESO for channel */
1388*4882a593Smuzhiyun 		RESO = runtime->buffer_size - 1;
1389*4882a593Smuzhiyun 		voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
1390*4882a593Smuzhiyun 
1391*4882a593Smuzhiyun 		/* set ctrl mode */
1392*4882a593Smuzhiyun 		voice->CTRL = snd_trident_control_mode(substream);
1393*4882a593Smuzhiyun 
1394*4882a593Smuzhiyun 		voice->FMC = 3;
1395*4882a593Smuzhiyun 		voice->RVol = 0x7f;
1396*4882a593Smuzhiyun 		voice->CVol = 0x7f;
1397*4882a593Smuzhiyun 		voice->GVSel = 1;
1398*4882a593Smuzhiyun 		voice->Pan = 0x7f;
1399*4882a593Smuzhiyun 		voice->Vol = 0x3ff;
1400*4882a593Smuzhiyun 		voice->EC = 0;
1401*4882a593Smuzhiyun 		voice->CSO = 0;
1402*4882a593Smuzhiyun 		voice->Alpha = 0;
1403*4882a593Smuzhiyun 		voice->FMS = 0;
1404*4882a593Smuzhiyun 		voice->Attribute = 0;
1405*4882a593Smuzhiyun 
1406*4882a593Smuzhiyun 		/* prepare surrogate IRQ channel */
1407*4882a593Smuzhiyun 		snd_trident_write_voice_regs(trident, voice);
1408*4882a593Smuzhiyun 
1409*4882a593Smuzhiyun 		outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO));
1410*4882a593Smuzhiyun 		outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2));
1411*4882a593Smuzhiyun 		outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA));
1412*4882a593Smuzhiyun 		outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO));
1413*4882a593Smuzhiyun 		outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2));
1414*4882a593Smuzhiyun 
1415*4882a593Smuzhiyun 		/* set SPDIF setting */
1416*4882a593Smuzhiyun 		outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
1417*4882a593Smuzhiyun 		outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
1418*4882a593Smuzhiyun 
1419*4882a593Smuzhiyun 	} else {	/* SiS */
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun 		/* set delta (rate) value */
1422*4882a593Smuzhiyun 		voice->Delta = 0x800;
1423*4882a593Smuzhiyun 		voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
1424*4882a593Smuzhiyun 
1425*4882a593Smuzhiyun 		/* set Loop Begin Address */
1426*4882a593Smuzhiyun 		if (voice->memblk)
1427*4882a593Smuzhiyun 			voice->LBA = voice->memblk->offset;
1428*4882a593Smuzhiyun 		else
1429*4882a593Smuzhiyun 			voice->LBA = runtime->dma_addr;
1430*4882a593Smuzhiyun 
1431*4882a593Smuzhiyun 		voice->CSO = 0;
1432*4882a593Smuzhiyun 		voice->ESO = runtime->buffer_size - 1;	/* in samples */
1433*4882a593Smuzhiyun 		voice->CTRL = snd_trident_control_mode(substream);
1434*4882a593Smuzhiyun 		voice->FMC = 3;
1435*4882a593Smuzhiyun 		voice->GVSel = 1;
1436*4882a593Smuzhiyun 		voice->EC = 0;
1437*4882a593Smuzhiyun 		voice->Alpha = 0;
1438*4882a593Smuzhiyun 		voice->FMS = 0;
1439*4882a593Smuzhiyun 		voice->Vol = mix->vol;
1440*4882a593Smuzhiyun 		voice->RVol = mix->rvol;
1441*4882a593Smuzhiyun 		voice->CVol = mix->cvol;
1442*4882a593Smuzhiyun 		voice->Pan = mix->pan;
1443*4882a593Smuzhiyun 		voice->Attribute = (1<<(30-16))|(7<<(26-16))|
1444*4882a593Smuzhiyun 				   (0<<(24-16))|(0<<(19-16));
1445*4882a593Smuzhiyun 
1446*4882a593Smuzhiyun 		snd_trident_write_voice_regs(trident, voice);
1447*4882a593Smuzhiyun 
1448*4882a593Smuzhiyun 		if (evoice != NULL) {
1449*4882a593Smuzhiyun 			evoice->Delta = voice->Delta;
1450*4882a593Smuzhiyun 			evoice->spurious_threshold = voice->spurious_threshold;
1451*4882a593Smuzhiyun 			evoice->LBA = voice->LBA;
1452*4882a593Smuzhiyun 			evoice->CSO = 0;
1453*4882a593Smuzhiyun 			evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
1454*4882a593Smuzhiyun 			evoice->CTRL = voice->CTRL;
1455*4882a593Smuzhiyun 			evoice->FMC = 3;
1456*4882a593Smuzhiyun 			evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
1457*4882a593Smuzhiyun 			evoice->EC = 0;
1458*4882a593Smuzhiyun 			evoice->Alpha = 0;
1459*4882a593Smuzhiyun 			evoice->FMS = 0;
1460*4882a593Smuzhiyun 			evoice->Vol = 0x3ff;			/* mute */
1461*4882a593Smuzhiyun 			evoice->RVol = evoice->CVol = 0x7f;	/* mute */
1462*4882a593Smuzhiyun 			evoice->Pan = 0x7f;			/* mute */
1463*4882a593Smuzhiyun 			evoice->Attribute = 0;
1464*4882a593Smuzhiyun 			snd_trident_write_voice_regs(trident, evoice);
1465*4882a593Smuzhiyun 			evoice->isync2 = 1;
1466*4882a593Smuzhiyun 			evoice->isync_mark = runtime->period_size;
1467*4882a593Smuzhiyun 			evoice->ESO = (runtime->period_size * 2) - 1;
1468*4882a593Smuzhiyun 		}
1469*4882a593Smuzhiyun 
1470*4882a593Smuzhiyun 		outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
1471*4882a593Smuzhiyun 		temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
1472*4882a593Smuzhiyun 		temp &= ~(1<<19);
1473*4882a593Smuzhiyun 		outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR));
1474*4882a593Smuzhiyun 		temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
1475*4882a593Smuzhiyun 		temp |= SPDIF_EN;
1476*4882a593Smuzhiyun 		outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
1477*4882a593Smuzhiyun 	}
1478*4882a593Smuzhiyun 
1479*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
1480*4882a593Smuzhiyun 
1481*4882a593Smuzhiyun 	return 0;
1482*4882a593Smuzhiyun }
1483*4882a593Smuzhiyun 
1484*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1485*4882a593Smuzhiyun    snd_trident_trigger
1486*4882a593Smuzhiyun 
1487*4882a593Smuzhiyun    Description: Start/stop devices
1488*4882a593Smuzhiyun 
1489*4882a593Smuzhiyun    Parameters:  substream  - PCM substream class
1490*4882a593Smuzhiyun    		cmd	- trigger command (STOP, GO)
1491*4882a593Smuzhiyun 
1492*4882a593Smuzhiyun    Returns:     Error status
1493*4882a593Smuzhiyun 
1494*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1495*4882a593Smuzhiyun 
snd_trident_trigger(struct snd_pcm_substream * substream,int cmd)1496*4882a593Smuzhiyun static int snd_trident_trigger(struct snd_pcm_substream *substream,
1497*4882a593Smuzhiyun 			       int cmd)
1498*4882a593Smuzhiyun 
1499*4882a593Smuzhiyun {
1500*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1501*4882a593Smuzhiyun 	struct snd_pcm_substream *s;
1502*4882a593Smuzhiyun 	unsigned int what, whati, capture_flag, spdif_flag;
1503*4882a593Smuzhiyun 	struct snd_trident_voice *voice, *evoice;
1504*4882a593Smuzhiyun 	unsigned int val, go;
1505*4882a593Smuzhiyun 
1506*4882a593Smuzhiyun 	switch (cmd) {
1507*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
1508*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1509*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
1510*4882a593Smuzhiyun 		go = 1;
1511*4882a593Smuzhiyun 		break;
1512*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
1513*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1514*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
1515*4882a593Smuzhiyun 		go = 0;
1516*4882a593Smuzhiyun 		break;
1517*4882a593Smuzhiyun 	default:
1518*4882a593Smuzhiyun 		return -EINVAL;
1519*4882a593Smuzhiyun 	}
1520*4882a593Smuzhiyun 	what = whati = capture_flag = spdif_flag = 0;
1521*4882a593Smuzhiyun 	spin_lock(&trident->reg_lock);
1522*4882a593Smuzhiyun 	val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
1523*4882a593Smuzhiyun 	snd_pcm_group_for_each_entry(s, substream) {
1524*4882a593Smuzhiyun 		if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) {
1525*4882a593Smuzhiyun 			voice = s->runtime->private_data;
1526*4882a593Smuzhiyun 			evoice = voice->extra;
1527*4882a593Smuzhiyun 			what |= 1 << (voice->number & 0x1f);
1528*4882a593Smuzhiyun 			if (evoice == NULL) {
1529*4882a593Smuzhiyun 				whati |= 1 << (voice->number & 0x1f);
1530*4882a593Smuzhiyun 			} else {
1531*4882a593Smuzhiyun 				what |= 1 << (evoice->number & 0x1f);
1532*4882a593Smuzhiyun 				whati |= 1 << (evoice->number & 0x1f);
1533*4882a593Smuzhiyun 				if (go)
1534*4882a593Smuzhiyun 					evoice->stimer = val;
1535*4882a593Smuzhiyun 			}
1536*4882a593Smuzhiyun 			if (go) {
1537*4882a593Smuzhiyun 				voice->running = 1;
1538*4882a593Smuzhiyun 				voice->stimer = val;
1539*4882a593Smuzhiyun 			} else {
1540*4882a593Smuzhiyun 				voice->running = 0;
1541*4882a593Smuzhiyun 			}
1542*4882a593Smuzhiyun 			snd_pcm_trigger_done(s, substream);
1543*4882a593Smuzhiyun 			if (voice->capture)
1544*4882a593Smuzhiyun 				capture_flag = 1;
1545*4882a593Smuzhiyun 			if (voice->spdif)
1546*4882a593Smuzhiyun 				spdif_flag = 1;
1547*4882a593Smuzhiyun 		}
1548*4882a593Smuzhiyun 	}
1549*4882a593Smuzhiyun 	if (spdif_flag) {
1550*4882a593Smuzhiyun 		if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
1551*4882a593Smuzhiyun 			outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
1552*4882a593Smuzhiyun 			val = trident->spdif_pcm_ctrl;
1553*4882a593Smuzhiyun 			if (!go)
1554*4882a593Smuzhiyun 				val &= ~(0x28);
1555*4882a593Smuzhiyun 			outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
1556*4882a593Smuzhiyun 		} else {
1557*4882a593Smuzhiyun 			outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
1558*4882a593Smuzhiyun 			val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
1559*4882a593Smuzhiyun 			outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
1560*4882a593Smuzhiyun 		}
1561*4882a593Smuzhiyun 	}
1562*4882a593Smuzhiyun 	if (!go)
1563*4882a593Smuzhiyun 		outl(what, TRID_REG(trident, T4D_STOP_B));
1564*4882a593Smuzhiyun 	val = inl(TRID_REG(trident, T4D_AINTEN_B));
1565*4882a593Smuzhiyun 	if (go) {
1566*4882a593Smuzhiyun 		val |= whati;
1567*4882a593Smuzhiyun 	} else {
1568*4882a593Smuzhiyun 		val &= ~whati;
1569*4882a593Smuzhiyun 	}
1570*4882a593Smuzhiyun 	outl(val, TRID_REG(trident, T4D_AINTEN_B));
1571*4882a593Smuzhiyun 	if (go) {
1572*4882a593Smuzhiyun 		outl(what, TRID_REG(trident, T4D_START_B));
1573*4882a593Smuzhiyun 
1574*4882a593Smuzhiyun 		if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
1575*4882a593Smuzhiyun 			outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
1576*4882a593Smuzhiyun 	} else {
1577*4882a593Smuzhiyun 		if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
1578*4882a593Smuzhiyun 			outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
1579*4882a593Smuzhiyun 	}
1580*4882a593Smuzhiyun 	spin_unlock(&trident->reg_lock);
1581*4882a593Smuzhiyun 	return 0;
1582*4882a593Smuzhiyun }
1583*4882a593Smuzhiyun 
1584*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1585*4882a593Smuzhiyun    snd_trident_playback_pointer
1586*4882a593Smuzhiyun 
1587*4882a593Smuzhiyun    Description: This routine return the playback position
1588*4882a593Smuzhiyun 
1589*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1590*4882a593Smuzhiyun 
1591*4882a593Smuzhiyun    Returns:     position of buffer
1592*4882a593Smuzhiyun 
1593*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1594*4882a593Smuzhiyun 
snd_trident_playback_pointer(struct snd_pcm_substream * substream)1595*4882a593Smuzhiyun static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *substream)
1596*4882a593Smuzhiyun {
1597*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1598*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1599*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1600*4882a593Smuzhiyun 	unsigned int cso;
1601*4882a593Smuzhiyun 
1602*4882a593Smuzhiyun 	if (!voice->running)
1603*4882a593Smuzhiyun 		return 0;
1604*4882a593Smuzhiyun 
1605*4882a593Smuzhiyun 	spin_lock(&trident->reg_lock);
1606*4882a593Smuzhiyun 
1607*4882a593Smuzhiyun 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
1608*4882a593Smuzhiyun 
1609*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_NX) {
1610*4882a593Smuzhiyun 		cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
1611*4882a593Smuzhiyun 	} else {		// ID_4DWAVE_NX
1612*4882a593Smuzhiyun 		cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
1613*4882a593Smuzhiyun 	}
1614*4882a593Smuzhiyun 
1615*4882a593Smuzhiyun 	spin_unlock(&trident->reg_lock);
1616*4882a593Smuzhiyun 
1617*4882a593Smuzhiyun 	if (cso >= runtime->buffer_size)
1618*4882a593Smuzhiyun 		cso = 0;
1619*4882a593Smuzhiyun 
1620*4882a593Smuzhiyun 	return cso;
1621*4882a593Smuzhiyun }
1622*4882a593Smuzhiyun 
1623*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1624*4882a593Smuzhiyun    snd_trident_capture_pointer
1625*4882a593Smuzhiyun 
1626*4882a593Smuzhiyun    Description: This routine return the capture position
1627*4882a593Smuzhiyun 
1628*4882a593Smuzhiyun    Parameters:   pcm1    - PCM device class
1629*4882a593Smuzhiyun 
1630*4882a593Smuzhiyun    Returns:     position of buffer
1631*4882a593Smuzhiyun 
1632*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1633*4882a593Smuzhiyun 
snd_trident_capture_pointer(struct snd_pcm_substream * substream)1634*4882a593Smuzhiyun static snd_pcm_uframes_t snd_trident_capture_pointer(struct snd_pcm_substream *substream)
1635*4882a593Smuzhiyun {
1636*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1637*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1638*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1639*4882a593Smuzhiyun 	unsigned int result;
1640*4882a593Smuzhiyun 
1641*4882a593Smuzhiyun 	if (!voice->running)
1642*4882a593Smuzhiyun 		return 0;
1643*4882a593Smuzhiyun 
1644*4882a593Smuzhiyun 	result = inw(TRID_REG(trident, T4D_SBBL_SBCL));
1645*4882a593Smuzhiyun 	if (runtime->channels > 1)
1646*4882a593Smuzhiyun 		result >>= 1;
1647*4882a593Smuzhiyun 	if (result > 0)
1648*4882a593Smuzhiyun 		result = runtime->buffer_size - result;
1649*4882a593Smuzhiyun 
1650*4882a593Smuzhiyun 	return result;
1651*4882a593Smuzhiyun }
1652*4882a593Smuzhiyun 
1653*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1654*4882a593Smuzhiyun    snd_trident_spdif_pointer
1655*4882a593Smuzhiyun 
1656*4882a593Smuzhiyun    Description: This routine return the SPDIF playback position
1657*4882a593Smuzhiyun 
1658*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1659*4882a593Smuzhiyun 
1660*4882a593Smuzhiyun    Returns:     position of buffer
1661*4882a593Smuzhiyun 
1662*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1663*4882a593Smuzhiyun 
snd_trident_spdif_pointer(struct snd_pcm_substream * substream)1664*4882a593Smuzhiyun static snd_pcm_uframes_t snd_trident_spdif_pointer(struct snd_pcm_substream *substream)
1665*4882a593Smuzhiyun {
1666*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1667*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1668*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1669*4882a593Smuzhiyun 	unsigned int result;
1670*4882a593Smuzhiyun 
1671*4882a593Smuzhiyun 	if (!voice->running)
1672*4882a593Smuzhiyun 		return 0;
1673*4882a593Smuzhiyun 
1674*4882a593Smuzhiyun 	result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
1675*4882a593Smuzhiyun 
1676*4882a593Smuzhiyun 	return result;
1677*4882a593Smuzhiyun }
1678*4882a593Smuzhiyun 
1679*4882a593Smuzhiyun /*
1680*4882a593Smuzhiyun  *  Playback support device description
1681*4882a593Smuzhiyun  */
1682*4882a593Smuzhiyun 
1683*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_trident_playback =
1684*4882a593Smuzhiyun {
1685*4882a593Smuzhiyun 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1686*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
1687*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
1688*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
1689*4882a593Smuzhiyun 	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
1690*4882a593Smuzhiyun 				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
1691*4882a593Smuzhiyun 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
1692*4882a593Smuzhiyun 	.rate_min =		4000,
1693*4882a593Smuzhiyun 	.rate_max =		48000,
1694*4882a593Smuzhiyun 	.channels_min =		1,
1695*4882a593Smuzhiyun 	.channels_max =		2,
1696*4882a593Smuzhiyun 	.buffer_bytes_max =	(256*1024),
1697*4882a593Smuzhiyun 	.period_bytes_min =	64,
1698*4882a593Smuzhiyun 	.period_bytes_max =	(256*1024),
1699*4882a593Smuzhiyun 	.periods_min =		1,
1700*4882a593Smuzhiyun 	.periods_max =		1024,
1701*4882a593Smuzhiyun 	.fifo_size =		0,
1702*4882a593Smuzhiyun };
1703*4882a593Smuzhiyun 
1704*4882a593Smuzhiyun /*
1705*4882a593Smuzhiyun  *  Capture support device description
1706*4882a593Smuzhiyun  */
1707*4882a593Smuzhiyun 
1708*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_trident_capture =
1709*4882a593Smuzhiyun {
1710*4882a593Smuzhiyun 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1711*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
1712*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
1713*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
1714*4882a593Smuzhiyun 	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
1715*4882a593Smuzhiyun 				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
1716*4882a593Smuzhiyun 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
1717*4882a593Smuzhiyun 	.rate_min =		4000,
1718*4882a593Smuzhiyun 	.rate_max =		48000,
1719*4882a593Smuzhiyun 	.channels_min =		1,
1720*4882a593Smuzhiyun 	.channels_max =		2,
1721*4882a593Smuzhiyun 	.buffer_bytes_max =	(128*1024),
1722*4882a593Smuzhiyun 	.period_bytes_min =	64,
1723*4882a593Smuzhiyun 	.period_bytes_max =	(128*1024),
1724*4882a593Smuzhiyun 	.periods_min =		1,
1725*4882a593Smuzhiyun 	.periods_max =		1024,
1726*4882a593Smuzhiyun 	.fifo_size =		0,
1727*4882a593Smuzhiyun };
1728*4882a593Smuzhiyun 
1729*4882a593Smuzhiyun /*
1730*4882a593Smuzhiyun  *  Foldback capture support device description
1731*4882a593Smuzhiyun  */
1732*4882a593Smuzhiyun 
1733*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_trident_foldback =
1734*4882a593Smuzhiyun {
1735*4882a593Smuzhiyun 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1736*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
1737*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
1738*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
1739*4882a593Smuzhiyun 	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
1740*4882a593Smuzhiyun 	.rates =		SNDRV_PCM_RATE_48000,
1741*4882a593Smuzhiyun 	.rate_min =		48000,
1742*4882a593Smuzhiyun 	.rate_max =		48000,
1743*4882a593Smuzhiyun 	.channels_min =		2,
1744*4882a593Smuzhiyun 	.channels_max =		2,
1745*4882a593Smuzhiyun 	.buffer_bytes_max =	(128*1024),
1746*4882a593Smuzhiyun 	.period_bytes_min =	64,
1747*4882a593Smuzhiyun 	.period_bytes_max =	(128*1024),
1748*4882a593Smuzhiyun 	.periods_min =		1,
1749*4882a593Smuzhiyun 	.periods_max =		1024,
1750*4882a593Smuzhiyun 	.fifo_size =		0,
1751*4882a593Smuzhiyun };
1752*4882a593Smuzhiyun 
1753*4882a593Smuzhiyun /*
1754*4882a593Smuzhiyun  *  SPDIF playback support device description
1755*4882a593Smuzhiyun  */
1756*4882a593Smuzhiyun 
1757*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_trident_spdif =
1758*4882a593Smuzhiyun {
1759*4882a593Smuzhiyun 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1760*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
1761*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
1762*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
1763*4882a593Smuzhiyun 	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
1764*4882a593Smuzhiyun 	.rates =		(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
1765*4882a593Smuzhiyun 				 SNDRV_PCM_RATE_48000),
1766*4882a593Smuzhiyun 	.rate_min =		32000,
1767*4882a593Smuzhiyun 	.rate_max =		48000,
1768*4882a593Smuzhiyun 	.channels_min =		2,
1769*4882a593Smuzhiyun 	.channels_max =		2,
1770*4882a593Smuzhiyun 	.buffer_bytes_max =	(128*1024),
1771*4882a593Smuzhiyun 	.period_bytes_min =	64,
1772*4882a593Smuzhiyun 	.period_bytes_max =	(128*1024),
1773*4882a593Smuzhiyun 	.periods_min =		1,
1774*4882a593Smuzhiyun 	.periods_max =		1024,
1775*4882a593Smuzhiyun 	.fifo_size =		0,
1776*4882a593Smuzhiyun };
1777*4882a593Smuzhiyun 
1778*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_trident_spdif_7018 =
1779*4882a593Smuzhiyun {
1780*4882a593Smuzhiyun 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
1781*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
1782*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
1783*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
1784*4882a593Smuzhiyun 	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
1785*4882a593Smuzhiyun 	.rates =		SNDRV_PCM_RATE_48000,
1786*4882a593Smuzhiyun 	.rate_min =		48000,
1787*4882a593Smuzhiyun 	.rate_max =		48000,
1788*4882a593Smuzhiyun 	.channels_min =		2,
1789*4882a593Smuzhiyun 	.channels_max =		2,
1790*4882a593Smuzhiyun 	.buffer_bytes_max =	(128*1024),
1791*4882a593Smuzhiyun 	.period_bytes_min =	64,
1792*4882a593Smuzhiyun 	.period_bytes_max =	(128*1024),
1793*4882a593Smuzhiyun 	.periods_min =		1,
1794*4882a593Smuzhiyun 	.periods_max =		1024,
1795*4882a593Smuzhiyun 	.fifo_size =		0,
1796*4882a593Smuzhiyun };
1797*4882a593Smuzhiyun 
snd_trident_pcm_free_substream(struct snd_pcm_runtime * runtime)1798*4882a593Smuzhiyun static void snd_trident_pcm_free_substream(struct snd_pcm_runtime *runtime)
1799*4882a593Smuzhiyun {
1800*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1801*4882a593Smuzhiyun 	struct snd_trident *trident;
1802*4882a593Smuzhiyun 
1803*4882a593Smuzhiyun 	if (voice) {
1804*4882a593Smuzhiyun 		trident = voice->trident;
1805*4882a593Smuzhiyun 		snd_trident_free_voice(trident, voice);
1806*4882a593Smuzhiyun 	}
1807*4882a593Smuzhiyun }
1808*4882a593Smuzhiyun 
snd_trident_playback_open(struct snd_pcm_substream * substream)1809*4882a593Smuzhiyun static int snd_trident_playback_open(struct snd_pcm_substream *substream)
1810*4882a593Smuzhiyun {
1811*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1812*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1813*4882a593Smuzhiyun 	struct snd_trident_voice *voice;
1814*4882a593Smuzhiyun 
1815*4882a593Smuzhiyun 	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
1816*4882a593Smuzhiyun 	if (voice == NULL)
1817*4882a593Smuzhiyun 		return -EAGAIN;
1818*4882a593Smuzhiyun 	snd_trident_pcm_mixer_build(trident, voice, substream);
1819*4882a593Smuzhiyun 	voice->substream = substream;
1820*4882a593Smuzhiyun 	runtime->private_data = voice;
1821*4882a593Smuzhiyun 	runtime->private_free = snd_trident_pcm_free_substream;
1822*4882a593Smuzhiyun 	runtime->hw = snd_trident_playback;
1823*4882a593Smuzhiyun 	snd_pcm_set_sync(substream);
1824*4882a593Smuzhiyun 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
1825*4882a593Smuzhiyun 	return 0;
1826*4882a593Smuzhiyun }
1827*4882a593Smuzhiyun 
1828*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1829*4882a593Smuzhiyun    snd_trident_playback_close
1830*4882a593Smuzhiyun 
1831*4882a593Smuzhiyun    Description: This routine will close the 4DWave playback device. For now
1832*4882a593Smuzhiyun                 we will simply free the dma transfer buffer.
1833*4882a593Smuzhiyun 
1834*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1835*4882a593Smuzhiyun 
1836*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_playback_close(struct snd_pcm_substream * substream)1837*4882a593Smuzhiyun static int snd_trident_playback_close(struct snd_pcm_substream *substream)
1838*4882a593Smuzhiyun {
1839*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1840*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1841*4882a593Smuzhiyun 	struct snd_trident_voice *voice = runtime->private_data;
1842*4882a593Smuzhiyun 
1843*4882a593Smuzhiyun 	snd_trident_pcm_mixer_free(trident, voice, substream);
1844*4882a593Smuzhiyun 	return 0;
1845*4882a593Smuzhiyun }
1846*4882a593Smuzhiyun 
1847*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1848*4882a593Smuzhiyun    snd_trident_spdif_open
1849*4882a593Smuzhiyun 
1850*4882a593Smuzhiyun    Description: This routine will open the 4DWave SPDIF device.
1851*4882a593Smuzhiyun 
1852*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1853*4882a593Smuzhiyun 
1854*4882a593Smuzhiyun    Returns:     status  - success or failure flag
1855*4882a593Smuzhiyun 
1856*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1857*4882a593Smuzhiyun 
snd_trident_spdif_open(struct snd_pcm_substream * substream)1858*4882a593Smuzhiyun static int snd_trident_spdif_open(struct snd_pcm_substream *substream)
1859*4882a593Smuzhiyun {
1860*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1861*4882a593Smuzhiyun 	struct snd_trident_voice *voice;
1862*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1863*4882a593Smuzhiyun 
1864*4882a593Smuzhiyun 	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
1865*4882a593Smuzhiyun 	if (voice == NULL)
1866*4882a593Smuzhiyun 		return -EAGAIN;
1867*4882a593Smuzhiyun 	voice->spdif = 1;
1868*4882a593Smuzhiyun 	voice->substream = substream;
1869*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
1870*4882a593Smuzhiyun 	trident->spdif_pcm_bits = trident->spdif_bits;
1871*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
1872*4882a593Smuzhiyun 
1873*4882a593Smuzhiyun 	runtime->private_data = voice;
1874*4882a593Smuzhiyun 	runtime->private_free = snd_trident_pcm_free_substream;
1875*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
1876*4882a593Smuzhiyun 		runtime->hw = snd_trident_spdif;
1877*4882a593Smuzhiyun 	} else {
1878*4882a593Smuzhiyun 		runtime->hw = snd_trident_spdif_7018;
1879*4882a593Smuzhiyun 	}
1880*4882a593Smuzhiyun 
1881*4882a593Smuzhiyun 	trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
1882*4882a593Smuzhiyun 	snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
1883*4882a593Smuzhiyun 		       SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
1884*4882a593Smuzhiyun 
1885*4882a593Smuzhiyun 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
1886*4882a593Smuzhiyun 	return 0;
1887*4882a593Smuzhiyun }
1888*4882a593Smuzhiyun 
1889*4882a593Smuzhiyun 
1890*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1891*4882a593Smuzhiyun    snd_trident_spdif_close
1892*4882a593Smuzhiyun 
1893*4882a593Smuzhiyun    Description: This routine will close the 4DWave SPDIF device.
1894*4882a593Smuzhiyun 
1895*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1896*4882a593Smuzhiyun 
1897*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1898*4882a593Smuzhiyun 
snd_trident_spdif_close(struct snd_pcm_substream * substream)1899*4882a593Smuzhiyun static int snd_trident_spdif_close(struct snd_pcm_substream *substream)
1900*4882a593Smuzhiyun {
1901*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1902*4882a593Smuzhiyun 	unsigned int temp;
1903*4882a593Smuzhiyun 
1904*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
1905*4882a593Smuzhiyun 	// restore default SPDIF setting
1906*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
1907*4882a593Smuzhiyun 		outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
1908*4882a593Smuzhiyun 		outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
1909*4882a593Smuzhiyun 	} else {
1910*4882a593Smuzhiyun 		outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
1911*4882a593Smuzhiyun 		temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
1912*4882a593Smuzhiyun 		if (trident->spdif_ctrl) {
1913*4882a593Smuzhiyun 			temp |= SPDIF_EN;
1914*4882a593Smuzhiyun 		} else {
1915*4882a593Smuzhiyun 			temp &= ~SPDIF_EN;
1916*4882a593Smuzhiyun 		}
1917*4882a593Smuzhiyun 		outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
1918*4882a593Smuzhiyun 	}
1919*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
1920*4882a593Smuzhiyun 	trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
1921*4882a593Smuzhiyun 	snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
1922*4882a593Smuzhiyun 		       SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
1923*4882a593Smuzhiyun 	return 0;
1924*4882a593Smuzhiyun }
1925*4882a593Smuzhiyun 
1926*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1927*4882a593Smuzhiyun    snd_trident_capture_open
1928*4882a593Smuzhiyun 
1929*4882a593Smuzhiyun    Description: This routine will open the 4DWave capture device.
1930*4882a593Smuzhiyun 
1931*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1932*4882a593Smuzhiyun 
1933*4882a593Smuzhiyun    Returns:     status  - success or failure flag
1934*4882a593Smuzhiyun 
1935*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1936*4882a593Smuzhiyun 
snd_trident_capture_open(struct snd_pcm_substream * substream)1937*4882a593Smuzhiyun static int snd_trident_capture_open(struct snd_pcm_substream *substream)
1938*4882a593Smuzhiyun {
1939*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1940*4882a593Smuzhiyun 	struct snd_trident_voice *voice;
1941*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1942*4882a593Smuzhiyun 
1943*4882a593Smuzhiyun 	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
1944*4882a593Smuzhiyun 	if (voice == NULL)
1945*4882a593Smuzhiyun 		return -EAGAIN;
1946*4882a593Smuzhiyun 	voice->capture = 1;
1947*4882a593Smuzhiyun 	voice->substream = substream;
1948*4882a593Smuzhiyun 	runtime->private_data = voice;
1949*4882a593Smuzhiyun 	runtime->private_free = snd_trident_pcm_free_substream;
1950*4882a593Smuzhiyun 	runtime->hw = snd_trident_capture;
1951*4882a593Smuzhiyun 	snd_pcm_set_sync(substream);
1952*4882a593Smuzhiyun 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
1953*4882a593Smuzhiyun 	return 0;
1954*4882a593Smuzhiyun }
1955*4882a593Smuzhiyun 
1956*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1957*4882a593Smuzhiyun    snd_trident_capture_close
1958*4882a593Smuzhiyun 
1959*4882a593Smuzhiyun    Description: This routine will close the 4DWave capture device. For now
1960*4882a593Smuzhiyun                 we will simply free the dma transfer buffer.
1961*4882a593Smuzhiyun 
1962*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1963*4882a593Smuzhiyun 
1964*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_capture_close(struct snd_pcm_substream * substream)1965*4882a593Smuzhiyun static int snd_trident_capture_close(struct snd_pcm_substream *substream)
1966*4882a593Smuzhiyun {
1967*4882a593Smuzhiyun 	return 0;
1968*4882a593Smuzhiyun }
1969*4882a593Smuzhiyun 
1970*4882a593Smuzhiyun /*---------------------------------------------------------------------------
1971*4882a593Smuzhiyun    snd_trident_foldback_open
1972*4882a593Smuzhiyun 
1973*4882a593Smuzhiyun    Description: This routine will open the 4DWave foldback capture device.
1974*4882a593Smuzhiyun 
1975*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
1976*4882a593Smuzhiyun 
1977*4882a593Smuzhiyun    Returns:     status  - success or failure flag
1978*4882a593Smuzhiyun 
1979*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
1980*4882a593Smuzhiyun 
snd_trident_foldback_open(struct snd_pcm_substream * substream)1981*4882a593Smuzhiyun static int snd_trident_foldback_open(struct snd_pcm_substream *substream)
1982*4882a593Smuzhiyun {
1983*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
1984*4882a593Smuzhiyun 	struct snd_trident_voice *voice;
1985*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
1986*4882a593Smuzhiyun 
1987*4882a593Smuzhiyun 	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
1988*4882a593Smuzhiyun 	if (voice == NULL)
1989*4882a593Smuzhiyun 		return -EAGAIN;
1990*4882a593Smuzhiyun 	voice->foldback_chan = substream->number;
1991*4882a593Smuzhiyun 	voice->substream = substream;
1992*4882a593Smuzhiyun 	runtime->private_data = voice;
1993*4882a593Smuzhiyun 	runtime->private_free = snd_trident_pcm_free_substream;
1994*4882a593Smuzhiyun 	runtime->hw = snd_trident_foldback;
1995*4882a593Smuzhiyun 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
1996*4882a593Smuzhiyun 	return 0;
1997*4882a593Smuzhiyun }
1998*4882a593Smuzhiyun 
1999*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2000*4882a593Smuzhiyun    snd_trident_foldback_close
2001*4882a593Smuzhiyun 
2002*4882a593Smuzhiyun    Description: This routine will close the 4DWave foldback capture device.
2003*4882a593Smuzhiyun 		For now we will simply free the dma transfer buffer.
2004*4882a593Smuzhiyun 
2005*4882a593Smuzhiyun    Parameters:	substream  - PCM substream class
2006*4882a593Smuzhiyun 
2007*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
snd_trident_foldback_close(struct snd_pcm_substream * substream)2008*4882a593Smuzhiyun static int snd_trident_foldback_close(struct snd_pcm_substream *substream)
2009*4882a593Smuzhiyun {
2010*4882a593Smuzhiyun 	struct snd_trident *trident = snd_pcm_substream_chip(substream);
2011*4882a593Smuzhiyun 	struct snd_trident_voice *voice;
2012*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
2013*4882a593Smuzhiyun 	voice = runtime->private_data;
2014*4882a593Smuzhiyun 
2015*4882a593Smuzhiyun 	/* stop capture channel */
2016*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2017*4882a593Smuzhiyun 	outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
2018*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2019*4882a593Smuzhiyun 	return 0;
2020*4882a593Smuzhiyun }
2021*4882a593Smuzhiyun 
2022*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2023*4882a593Smuzhiyun    PCM operations
2024*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2025*4882a593Smuzhiyun 
2026*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_playback_ops = {
2027*4882a593Smuzhiyun 	.open =		snd_trident_playback_open,
2028*4882a593Smuzhiyun 	.close =	snd_trident_playback_close,
2029*4882a593Smuzhiyun 	.hw_params =	snd_trident_hw_params,
2030*4882a593Smuzhiyun 	.hw_free =	snd_trident_hw_free,
2031*4882a593Smuzhiyun 	.prepare =	snd_trident_playback_prepare,
2032*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2033*4882a593Smuzhiyun 	.pointer =	snd_trident_playback_pointer,
2034*4882a593Smuzhiyun };
2035*4882a593Smuzhiyun 
2036*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_nx_playback_ops = {
2037*4882a593Smuzhiyun 	.open =		snd_trident_playback_open,
2038*4882a593Smuzhiyun 	.close =	snd_trident_playback_close,
2039*4882a593Smuzhiyun 	.hw_params =	snd_trident_hw_params,
2040*4882a593Smuzhiyun 	.hw_free =	snd_trident_hw_free,
2041*4882a593Smuzhiyun 	.prepare =	snd_trident_playback_prepare,
2042*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2043*4882a593Smuzhiyun 	.pointer =	snd_trident_playback_pointer,
2044*4882a593Smuzhiyun };
2045*4882a593Smuzhiyun 
2046*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_capture_ops = {
2047*4882a593Smuzhiyun 	.open =		snd_trident_capture_open,
2048*4882a593Smuzhiyun 	.close =	snd_trident_capture_close,
2049*4882a593Smuzhiyun 	.hw_params =	snd_trident_capture_hw_params,
2050*4882a593Smuzhiyun 	.hw_free =	snd_trident_hw_free,
2051*4882a593Smuzhiyun 	.prepare =	snd_trident_capture_prepare,
2052*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2053*4882a593Smuzhiyun 	.pointer =	snd_trident_capture_pointer,
2054*4882a593Smuzhiyun };
2055*4882a593Smuzhiyun 
2056*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_si7018_capture_ops = {
2057*4882a593Smuzhiyun 	.open =		snd_trident_capture_open,
2058*4882a593Smuzhiyun 	.close =	snd_trident_capture_close,
2059*4882a593Smuzhiyun 	.hw_params =	snd_trident_si7018_capture_hw_params,
2060*4882a593Smuzhiyun 	.hw_free =	snd_trident_si7018_capture_hw_free,
2061*4882a593Smuzhiyun 	.prepare =	snd_trident_si7018_capture_prepare,
2062*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2063*4882a593Smuzhiyun 	.pointer =	snd_trident_playback_pointer,
2064*4882a593Smuzhiyun };
2065*4882a593Smuzhiyun 
2066*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_foldback_ops = {
2067*4882a593Smuzhiyun 	.open =		snd_trident_foldback_open,
2068*4882a593Smuzhiyun 	.close =	snd_trident_foldback_close,
2069*4882a593Smuzhiyun 	.hw_params =	snd_trident_hw_params,
2070*4882a593Smuzhiyun 	.hw_free =	snd_trident_hw_free,
2071*4882a593Smuzhiyun 	.prepare =	snd_trident_foldback_prepare,
2072*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2073*4882a593Smuzhiyun 	.pointer =	snd_trident_playback_pointer,
2074*4882a593Smuzhiyun };
2075*4882a593Smuzhiyun 
2076*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_nx_foldback_ops = {
2077*4882a593Smuzhiyun 	.open =		snd_trident_foldback_open,
2078*4882a593Smuzhiyun 	.close =	snd_trident_foldback_close,
2079*4882a593Smuzhiyun 	.hw_params =	snd_trident_hw_params,
2080*4882a593Smuzhiyun 	.hw_free =	snd_trident_hw_free,
2081*4882a593Smuzhiyun 	.prepare =	snd_trident_foldback_prepare,
2082*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2083*4882a593Smuzhiyun 	.pointer =	snd_trident_playback_pointer,
2084*4882a593Smuzhiyun };
2085*4882a593Smuzhiyun 
2086*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_spdif_ops = {
2087*4882a593Smuzhiyun 	.open =		snd_trident_spdif_open,
2088*4882a593Smuzhiyun 	.close =	snd_trident_spdif_close,
2089*4882a593Smuzhiyun 	.hw_params =	snd_trident_spdif_hw_params,
2090*4882a593Smuzhiyun 	.hw_free =	snd_trident_hw_free,
2091*4882a593Smuzhiyun 	.prepare =	snd_trident_spdif_prepare,
2092*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2093*4882a593Smuzhiyun 	.pointer =	snd_trident_spdif_pointer,
2094*4882a593Smuzhiyun };
2095*4882a593Smuzhiyun 
2096*4882a593Smuzhiyun static const struct snd_pcm_ops snd_trident_spdif_7018_ops = {
2097*4882a593Smuzhiyun 	.open =		snd_trident_spdif_open,
2098*4882a593Smuzhiyun 	.close =	snd_trident_spdif_close,
2099*4882a593Smuzhiyun 	.hw_params =	snd_trident_spdif_hw_params,
2100*4882a593Smuzhiyun 	.hw_free =	snd_trident_hw_free,
2101*4882a593Smuzhiyun 	.prepare =	snd_trident_spdif_prepare,
2102*4882a593Smuzhiyun 	.trigger =	snd_trident_trigger,
2103*4882a593Smuzhiyun 	.pointer =	snd_trident_playback_pointer,
2104*4882a593Smuzhiyun };
2105*4882a593Smuzhiyun 
2106*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2107*4882a593Smuzhiyun    snd_trident_pcm
2108*4882a593Smuzhiyun 
2109*4882a593Smuzhiyun    Description: This routine registers the 4DWave device for PCM support.
2110*4882a593Smuzhiyun 
2111*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
2112*4882a593Smuzhiyun 
2113*4882a593Smuzhiyun    Returns:     None
2114*4882a593Smuzhiyun 
2115*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2116*4882a593Smuzhiyun 
snd_trident_pcm(struct snd_trident * trident,int device)2117*4882a593Smuzhiyun int snd_trident_pcm(struct snd_trident *trident, int device)
2118*4882a593Smuzhiyun {
2119*4882a593Smuzhiyun 	struct snd_pcm *pcm;
2120*4882a593Smuzhiyun 	int err;
2121*4882a593Smuzhiyun 
2122*4882a593Smuzhiyun 	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0)
2123*4882a593Smuzhiyun 		return err;
2124*4882a593Smuzhiyun 
2125*4882a593Smuzhiyun 	pcm->private_data = trident;
2126*4882a593Smuzhiyun 
2127*4882a593Smuzhiyun 	if (trident->tlb.entries) {
2128*4882a593Smuzhiyun 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops);
2129*4882a593Smuzhiyun 	} else {
2130*4882a593Smuzhiyun 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops);
2131*4882a593Smuzhiyun 	}
2132*4882a593Smuzhiyun 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
2133*4882a593Smuzhiyun 			trident->device != TRIDENT_DEVICE_ID_SI7018 ?
2134*4882a593Smuzhiyun 			&snd_trident_capture_ops :
2135*4882a593Smuzhiyun 			&snd_trident_si7018_capture_ops);
2136*4882a593Smuzhiyun 
2137*4882a593Smuzhiyun 	pcm->info_flags = 0;
2138*4882a593Smuzhiyun 	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
2139*4882a593Smuzhiyun 	strcpy(pcm->name, "Trident 4DWave");
2140*4882a593Smuzhiyun 	trident->pcm = pcm;
2141*4882a593Smuzhiyun 
2142*4882a593Smuzhiyun 	if (trident->tlb.entries) {
2143*4882a593Smuzhiyun 		struct snd_pcm_substream *substream;
2144*4882a593Smuzhiyun 		for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
2145*4882a593Smuzhiyun 			snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_SG,
2146*4882a593Smuzhiyun 						   &trident->pci->dev,
2147*4882a593Smuzhiyun 						   64*1024, 128*1024);
2148*4882a593Smuzhiyun 		snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
2149*4882a593Smuzhiyun 					   SNDRV_DMA_TYPE_DEV,
2150*4882a593Smuzhiyun 					   &trident->pci->dev,
2151*4882a593Smuzhiyun 					   64*1024, 128*1024);
2152*4882a593Smuzhiyun 	} else {
2153*4882a593Smuzhiyun 		snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
2154*4882a593Smuzhiyun 					       &trident->pci->dev,
2155*4882a593Smuzhiyun 					       64*1024, 128*1024);
2156*4882a593Smuzhiyun 	}
2157*4882a593Smuzhiyun 
2158*4882a593Smuzhiyun 	return 0;
2159*4882a593Smuzhiyun }
2160*4882a593Smuzhiyun 
2161*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2162*4882a593Smuzhiyun    snd_trident_foldback_pcm
2163*4882a593Smuzhiyun 
2164*4882a593Smuzhiyun    Description: This routine registers the 4DWave device for foldback PCM support.
2165*4882a593Smuzhiyun 
2166*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
2167*4882a593Smuzhiyun 
2168*4882a593Smuzhiyun    Returns:     None
2169*4882a593Smuzhiyun 
2170*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2171*4882a593Smuzhiyun 
snd_trident_foldback_pcm(struct snd_trident * trident,int device)2172*4882a593Smuzhiyun int snd_trident_foldback_pcm(struct snd_trident *trident, int device)
2173*4882a593Smuzhiyun {
2174*4882a593Smuzhiyun 	struct snd_pcm *foldback;
2175*4882a593Smuzhiyun 	int err;
2176*4882a593Smuzhiyun 	int num_chan = 3;
2177*4882a593Smuzhiyun 	struct snd_pcm_substream *substream;
2178*4882a593Smuzhiyun 
2179*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_NX)
2180*4882a593Smuzhiyun 		num_chan = 4;
2181*4882a593Smuzhiyun 	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0)
2182*4882a593Smuzhiyun 		return err;
2183*4882a593Smuzhiyun 
2184*4882a593Smuzhiyun 	foldback->private_data = trident;
2185*4882a593Smuzhiyun 	if (trident->tlb.entries)
2186*4882a593Smuzhiyun 		snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops);
2187*4882a593Smuzhiyun 	else
2188*4882a593Smuzhiyun 		snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops);
2189*4882a593Smuzhiyun 	foldback->info_flags = 0;
2190*4882a593Smuzhiyun 	strcpy(foldback->name, "Trident 4DWave");
2191*4882a593Smuzhiyun 	substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
2192*4882a593Smuzhiyun 	strcpy(substream->name, "Front Mixer");
2193*4882a593Smuzhiyun 	substream = substream->next;
2194*4882a593Smuzhiyun 	strcpy(substream->name, "Reverb Mixer");
2195*4882a593Smuzhiyun 	substream = substream->next;
2196*4882a593Smuzhiyun 	strcpy(substream->name, "Chorus Mixer");
2197*4882a593Smuzhiyun 	if (num_chan == 4) {
2198*4882a593Smuzhiyun 		substream = substream->next;
2199*4882a593Smuzhiyun 		strcpy(substream->name, "Second AC'97 ADC");
2200*4882a593Smuzhiyun 	}
2201*4882a593Smuzhiyun 	trident->foldback = foldback;
2202*4882a593Smuzhiyun 
2203*4882a593Smuzhiyun 	if (trident->tlb.entries)
2204*4882a593Smuzhiyun 		snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV_SG,
2205*4882a593Smuzhiyun 					       &trident->pci->dev,
2206*4882a593Smuzhiyun 					       0, 128*1024);
2207*4882a593Smuzhiyun 	else
2208*4882a593Smuzhiyun 		snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV,
2209*4882a593Smuzhiyun 					       &trident->pci->dev,
2210*4882a593Smuzhiyun 					       64*1024, 128*1024);
2211*4882a593Smuzhiyun 
2212*4882a593Smuzhiyun 	return 0;
2213*4882a593Smuzhiyun }
2214*4882a593Smuzhiyun 
2215*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2216*4882a593Smuzhiyun    snd_trident_spdif
2217*4882a593Smuzhiyun 
2218*4882a593Smuzhiyun    Description: This routine registers the 4DWave-NX device for SPDIF support.
2219*4882a593Smuzhiyun 
2220*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave-NX.
2221*4882a593Smuzhiyun 
2222*4882a593Smuzhiyun    Returns:     None
2223*4882a593Smuzhiyun 
2224*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2225*4882a593Smuzhiyun 
snd_trident_spdif_pcm(struct snd_trident * trident,int device)2226*4882a593Smuzhiyun int snd_trident_spdif_pcm(struct snd_trident *trident, int device)
2227*4882a593Smuzhiyun {
2228*4882a593Smuzhiyun 	struct snd_pcm *spdif;
2229*4882a593Smuzhiyun 	int err;
2230*4882a593Smuzhiyun 
2231*4882a593Smuzhiyun 	if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0)
2232*4882a593Smuzhiyun 		return err;
2233*4882a593Smuzhiyun 
2234*4882a593Smuzhiyun 	spdif->private_data = trident;
2235*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
2236*4882a593Smuzhiyun 		snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
2237*4882a593Smuzhiyun 	} else {
2238*4882a593Smuzhiyun 		snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops);
2239*4882a593Smuzhiyun 	}
2240*4882a593Smuzhiyun 	spdif->info_flags = 0;
2241*4882a593Smuzhiyun 	strcpy(spdif->name, "Trident 4DWave IEC958");
2242*4882a593Smuzhiyun 	trident->spdif = spdif;
2243*4882a593Smuzhiyun 
2244*4882a593Smuzhiyun 	snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV,
2245*4882a593Smuzhiyun 				       &trident->pci->dev, 64*1024, 128*1024);
2246*4882a593Smuzhiyun 
2247*4882a593Smuzhiyun 	return 0;
2248*4882a593Smuzhiyun }
2249*4882a593Smuzhiyun 
2250*4882a593Smuzhiyun /*
2251*4882a593Smuzhiyun  *  Mixer part
2252*4882a593Smuzhiyun  */
2253*4882a593Smuzhiyun 
2254*4882a593Smuzhiyun 
2255*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2256*4882a593Smuzhiyun     snd_trident_spdif_control
2257*4882a593Smuzhiyun 
2258*4882a593Smuzhiyun     Description: enable/disable S/PDIF out from ac97 mixer
2259*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2260*4882a593Smuzhiyun 
2261*4882a593Smuzhiyun #define snd_trident_spdif_control_info	snd_ctl_boolean_mono_info
2262*4882a593Smuzhiyun 
snd_trident_spdif_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2263*4882a593Smuzhiyun static int snd_trident_spdif_control_get(struct snd_kcontrol *kcontrol,
2264*4882a593Smuzhiyun 					 struct snd_ctl_elem_value *ucontrol)
2265*4882a593Smuzhiyun {
2266*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2267*4882a593Smuzhiyun 	unsigned char val;
2268*4882a593Smuzhiyun 
2269*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2270*4882a593Smuzhiyun 	val = trident->spdif_ctrl;
2271*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = val == kcontrol->private_value;
2272*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2273*4882a593Smuzhiyun 	return 0;
2274*4882a593Smuzhiyun }
2275*4882a593Smuzhiyun 
snd_trident_spdif_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2276*4882a593Smuzhiyun static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
2277*4882a593Smuzhiyun 					 struct snd_ctl_elem_value *ucontrol)
2278*4882a593Smuzhiyun {
2279*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2280*4882a593Smuzhiyun 	unsigned char val;
2281*4882a593Smuzhiyun 	int change;
2282*4882a593Smuzhiyun 
2283*4882a593Smuzhiyun 	val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00;
2284*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2285*4882a593Smuzhiyun 	/* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */
2286*4882a593Smuzhiyun 	change = trident->spdif_ctrl != val;
2287*4882a593Smuzhiyun 	trident->spdif_ctrl = val;
2288*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
2289*4882a593Smuzhiyun 		if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) {
2290*4882a593Smuzhiyun 			outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
2291*4882a593Smuzhiyun 			outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
2292*4882a593Smuzhiyun 		}
2293*4882a593Smuzhiyun 	} else {
2294*4882a593Smuzhiyun 		if (trident->spdif == NULL) {
2295*4882a593Smuzhiyun 			unsigned int temp;
2296*4882a593Smuzhiyun 			outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
2297*4882a593Smuzhiyun 			temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN;
2298*4882a593Smuzhiyun 			if (val)
2299*4882a593Smuzhiyun 				temp |= SPDIF_EN;
2300*4882a593Smuzhiyun 			outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
2301*4882a593Smuzhiyun 		}
2302*4882a593Smuzhiyun 	}
2303*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2304*4882a593Smuzhiyun 	return change;
2305*4882a593Smuzhiyun }
2306*4882a593Smuzhiyun 
2307*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_spdif_control =
2308*4882a593Smuzhiyun {
2309*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2310*4882a593Smuzhiyun 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
2311*4882a593Smuzhiyun 	.info =		snd_trident_spdif_control_info,
2312*4882a593Smuzhiyun 	.get =		snd_trident_spdif_control_get,
2313*4882a593Smuzhiyun 	.put =		snd_trident_spdif_control_put,
2314*4882a593Smuzhiyun 	.private_value = 0x28,
2315*4882a593Smuzhiyun };
2316*4882a593Smuzhiyun 
2317*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2318*4882a593Smuzhiyun     snd_trident_spdif_default
2319*4882a593Smuzhiyun 
2320*4882a593Smuzhiyun     Description: put/get the S/PDIF default settings
2321*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2322*4882a593Smuzhiyun 
snd_trident_spdif_default_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2323*4882a593Smuzhiyun static int snd_trident_spdif_default_info(struct snd_kcontrol *kcontrol,
2324*4882a593Smuzhiyun 					  struct snd_ctl_elem_info *uinfo)
2325*4882a593Smuzhiyun {
2326*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
2327*4882a593Smuzhiyun 	uinfo->count = 1;
2328*4882a593Smuzhiyun 	return 0;
2329*4882a593Smuzhiyun }
2330*4882a593Smuzhiyun 
snd_trident_spdif_default_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2331*4882a593Smuzhiyun static int snd_trident_spdif_default_get(struct snd_kcontrol *kcontrol,
2332*4882a593Smuzhiyun 					 struct snd_ctl_elem_value *ucontrol)
2333*4882a593Smuzhiyun {
2334*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2335*4882a593Smuzhiyun 
2336*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2337*4882a593Smuzhiyun 	ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff;
2338*4882a593Smuzhiyun 	ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff;
2339*4882a593Smuzhiyun 	ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff;
2340*4882a593Smuzhiyun 	ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff;
2341*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2342*4882a593Smuzhiyun 	return 0;
2343*4882a593Smuzhiyun }
2344*4882a593Smuzhiyun 
snd_trident_spdif_default_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2345*4882a593Smuzhiyun static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
2346*4882a593Smuzhiyun 					 struct snd_ctl_elem_value *ucontrol)
2347*4882a593Smuzhiyun {
2348*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2349*4882a593Smuzhiyun 	unsigned int val;
2350*4882a593Smuzhiyun 	int change;
2351*4882a593Smuzhiyun 
2352*4882a593Smuzhiyun 	val = (ucontrol->value.iec958.status[0] << 0) |
2353*4882a593Smuzhiyun 	      (ucontrol->value.iec958.status[1] << 8) |
2354*4882a593Smuzhiyun 	      (ucontrol->value.iec958.status[2] << 16) |
2355*4882a593Smuzhiyun 	      (ucontrol->value.iec958.status[3] << 24);
2356*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2357*4882a593Smuzhiyun 	change = trident->spdif_bits != val;
2358*4882a593Smuzhiyun 	trident->spdif_bits = val;
2359*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
2360*4882a593Smuzhiyun 		if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0)
2361*4882a593Smuzhiyun 			outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
2362*4882a593Smuzhiyun 	} else {
2363*4882a593Smuzhiyun 		if (trident->spdif == NULL)
2364*4882a593Smuzhiyun 			outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
2365*4882a593Smuzhiyun 	}
2366*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2367*4882a593Smuzhiyun 	return change;
2368*4882a593Smuzhiyun }
2369*4882a593Smuzhiyun 
2370*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_spdif_default =
2371*4882a593Smuzhiyun {
2372*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
2373*4882a593Smuzhiyun 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
2374*4882a593Smuzhiyun 	.info =		snd_trident_spdif_default_info,
2375*4882a593Smuzhiyun 	.get =		snd_trident_spdif_default_get,
2376*4882a593Smuzhiyun 	.put =		snd_trident_spdif_default_put
2377*4882a593Smuzhiyun };
2378*4882a593Smuzhiyun 
2379*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2380*4882a593Smuzhiyun     snd_trident_spdif_mask
2381*4882a593Smuzhiyun 
2382*4882a593Smuzhiyun     Description: put/get the S/PDIF mask
2383*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2384*4882a593Smuzhiyun 
snd_trident_spdif_mask_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2385*4882a593Smuzhiyun static int snd_trident_spdif_mask_info(struct snd_kcontrol *kcontrol,
2386*4882a593Smuzhiyun 				       struct snd_ctl_elem_info *uinfo)
2387*4882a593Smuzhiyun {
2388*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
2389*4882a593Smuzhiyun 	uinfo->count = 1;
2390*4882a593Smuzhiyun 	return 0;
2391*4882a593Smuzhiyun }
2392*4882a593Smuzhiyun 
snd_trident_spdif_mask_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2393*4882a593Smuzhiyun static int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol,
2394*4882a593Smuzhiyun 				      struct snd_ctl_elem_value *ucontrol)
2395*4882a593Smuzhiyun {
2396*4882a593Smuzhiyun 	ucontrol->value.iec958.status[0] = 0xff;
2397*4882a593Smuzhiyun 	ucontrol->value.iec958.status[1] = 0xff;
2398*4882a593Smuzhiyun 	ucontrol->value.iec958.status[2] = 0xff;
2399*4882a593Smuzhiyun 	ucontrol->value.iec958.status[3] = 0xff;
2400*4882a593Smuzhiyun 	return 0;
2401*4882a593Smuzhiyun }
2402*4882a593Smuzhiyun 
2403*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_spdif_mask =
2404*4882a593Smuzhiyun {
2405*4882a593Smuzhiyun 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
2406*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
2407*4882a593Smuzhiyun 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
2408*4882a593Smuzhiyun 	.info =		snd_trident_spdif_mask_info,
2409*4882a593Smuzhiyun 	.get =		snd_trident_spdif_mask_get,
2410*4882a593Smuzhiyun };
2411*4882a593Smuzhiyun 
2412*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2413*4882a593Smuzhiyun     snd_trident_spdif_stream
2414*4882a593Smuzhiyun 
2415*4882a593Smuzhiyun     Description: put/get the S/PDIF stream settings
2416*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2417*4882a593Smuzhiyun 
snd_trident_spdif_stream_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2418*4882a593Smuzhiyun static int snd_trident_spdif_stream_info(struct snd_kcontrol *kcontrol,
2419*4882a593Smuzhiyun 					 struct snd_ctl_elem_info *uinfo)
2420*4882a593Smuzhiyun {
2421*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
2422*4882a593Smuzhiyun 	uinfo->count = 1;
2423*4882a593Smuzhiyun 	return 0;
2424*4882a593Smuzhiyun }
2425*4882a593Smuzhiyun 
snd_trident_spdif_stream_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2426*4882a593Smuzhiyun static int snd_trident_spdif_stream_get(struct snd_kcontrol *kcontrol,
2427*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
2428*4882a593Smuzhiyun {
2429*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2430*4882a593Smuzhiyun 
2431*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2432*4882a593Smuzhiyun 	ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff;
2433*4882a593Smuzhiyun 	ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff;
2434*4882a593Smuzhiyun 	ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff;
2435*4882a593Smuzhiyun 	ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff;
2436*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2437*4882a593Smuzhiyun 	return 0;
2438*4882a593Smuzhiyun }
2439*4882a593Smuzhiyun 
snd_trident_spdif_stream_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2440*4882a593Smuzhiyun static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
2441*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
2442*4882a593Smuzhiyun {
2443*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2444*4882a593Smuzhiyun 	unsigned int val;
2445*4882a593Smuzhiyun 	int change;
2446*4882a593Smuzhiyun 
2447*4882a593Smuzhiyun 	val = (ucontrol->value.iec958.status[0] << 0) |
2448*4882a593Smuzhiyun 	      (ucontrol->value.iec958.status[1] << 8) |
2449*4882a593Smuzhiyun 	      (ucontrol->value.iec958.status[2] << 16) |
2450*4882a593Smuzhiyun 	      (ucontrol->value.iec958.status[3] << 24);
2451*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2452*4882a593Smuzhiyun 	change = trident->spdif_pcm_bits != val;
2453*4882a593Smuzhiyun 	trident->spdif_pcm_bits = val;
2454*4882a593Smuzhiyun 	if (trident->spdif != NULL) {
2455*4882a593Smuzhiyun 		if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
2456*4882a593Smuzhiyun 			outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
2457*4882a593Smuzhiyun 		} else {
2458*4882a593Smuzhiyun 			outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
2459*4882a593Smuzhiyun 		}
2460*4882a593Smuzhiyun 	}
2461*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2462*4882a593Smuzhiyun 	return change;
2463*4882a593Smuzhiyun }
2464*4882a593Smuzhiyun 
2465*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_spdif_stream =
2466*4882a593Smuzhiyun {
2467*4882a593Smuzhiyun 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
2468*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
2469*4882a593Smuzhiyun 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
2470*4882a593Smuzhiyun 	.info =		snd_trident_spdif_stream_info,
2471*4882a593Smuzhiyun 	.get =		snd_trident_spdif_stream_get,
2472*4882a593Smuzhiyun 	.put =		snd_trident_spdif_stream_put
2473*4882a593Smuzhiyun };
2474*4882a593Smuzhiyun 
2475*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2476*4882a593Smuzhiyun     snd_trident_ac97_control
2477*4882a593Smuzhiyun 
2478*4882a593Smuzhiyun     Description: enable/disable rear path for ac97
2479*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2480*4882a593Smuzhiyun 
2481*4882a593Smuzhiyun #define snd_trident_ac97_control_info	snd_ctl_boolean_mono_info
2482*4882a593Smuzhiyun 
snd_trident_ac97_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2483*4882a593Smuzhiyun static int snd_trident_ac97_control_get(struct snd_kcontrol *kcontrol,
2484*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
2485*4882a593Smuzhiyun {
2486*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2487*4882a593Smuzhiyun 	unsigned char val;
2488*4882a593Smuzhiyun 
2489*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2490*4882a593Smuzhiyun 	val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
2491*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0;
2492*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2493*4882a593Smuzhiyun 	return 0;
2494*4882a593Smuzhiyun }
2495*4882a593Smuzhiyun 
snd_trident_ac97_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2496*4882a593Smuzhiyun static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
2497*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
2498*4882a593Smuzhiyun {
2499*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2500*4882a593Smuzhiyun 	unsigned char val;
2501*4882a593Smuzhiyun 	int change = 0;
2502*4882a593Smuzhiyun 
2503*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2504*4882a593Smuzhiyun 	val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
2505*4882a593Smuzhiyun 	val &= ~(1 << kcontrol->private_value);
2506*4882a593Smuzhiyun 	if (ucontrol->value.integer.value[0])
2507*4882a593Smuzhiyun 		val |= 1 << kcontrol->private_value;
2508*4882a593Smuzhiyun 	change = val != trident->ac97_ctrl;
2509*4882a593Smuzhiyun 	trident->ac97_ctrl = val;
2510*4882a593Smuzhiyun 	outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
2511*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2512*4882a593Smuzhiyun 	return change;
2513*4882a593Smuzhiyun }
2514*4882a593Smuzhiyun 
2515*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_ac97_rear_control =
2516*4882a593Smuzhiyun {
2517*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2518*4882a593Smuzhiyun 	.name =         "Rear Path",
2519*4882a593Smuzhiyun 	.info =		snd_trident_ac97_control_info,
2520*4882a593Smuzhiyun 	.get =		snd_trident_ac97_control_get,
2521*4882a593Smuzhiyun 	.put =		snd_trident_ac97_control_put,
2522*4882a593Smuzhiyun 	.private_value = 4,
2523*4882a593Smuzhiyun };
2524*4882a593Smuzhiyun 
2525*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2526*4882a593Smuzhiyun     snd_trident_vol_control
2527*4882a593Smuzhiyun 
2528*4882a593Smuzhiyun     Description: wave & music volume control
2529*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2530*4882a593Smuzhiyun 
snd_trident_vol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2531*4882a593Smuzhiyun static int snd_trident_vol_control_info(struct snd_kcontrol *kcontrol,
2532*4882a593Smuzhiyun 					struct snd_ctl_elem_info *uinfo)
2533*4882a593Smuzhiyun {
2534*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2535*4882a593Smuzhiyun 	uinfo->count = 2;
2536*4882a593Smuzhiyun 	uinfo->value.integer.min = 0;
2537*4882a593Smuzhiyun 	uinfo->value.integer.max = 255;
2538*4882a593Smuzhiyun 	return 0;
2539*4882a593Smuzhiyun }
2540*4882a593Smuzhiyun 
snd_trident_vol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2541*4882a593Smuzhiyun static int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol,
2542*4882a593Smuzhiyun 				       struct snd_ctl_elem_value *ucontrol)
2543*4882a593Smuzhiyun {
2544*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2545*4882a593Smuzhiyun 	unsigned int val;
2546*4882a593Smuzhiyun 
2547*4882a593Smuzhiyun 	val = trident->musicvol_wavevol;
2548*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff);
2549*4882a593Smuzhiyun 	ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff);
2550*4882a593Smuzhiyun 	return 0;
2551*4882a593Smuzhiyun }
2552*4882a593Smuzhiyun 
2553*4882a593Smuzhiyun static const DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0);
2554*4882a593Smuzhiyun 
snd_trident_vol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2555*4882a593Smuzhiyun static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
2556*4882a593Smuzhiyun 				       struct snd_ctl_elem_value *ucontrol)
2557*4882a593Smuzhiyun {
2558*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2559*4882a593Smuzhiyun 	unsigned int val;
2560*4882a593Smuzhiyun 	int change = 0;
2561*4882a593Smuzhiyun 
2562*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2563*4882a593Smuzhiyun 	val = trident->musicvol_wavevol;
2564*4882a593Smuzhiyun 	val &= ~(0xffff << kcontrol->private_value);
2565*4882a593Smuzhiyun 	val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) |
2566*4882a593Smuzhiyun 	        ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value;
2567*4882a593Smuzhiyun 	change = val != trident->musicvol_wavevol;
2568*4882a593Smuzhiyun 	outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
2569*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2570*4882a593Smuzhiyun 	return change;
2571*4882a593Smuzhiyun }
2572*4882a593Smuzhiyun 
2573*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_vol_music_control =
2574*4882a593Smuzhiyun {
2575*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2576*4882a593Smuzhiyun 	.name =         "Music Playback Volume",
2577*4882a593Smuzhiyun 	.info =		snd_trident_vol_control_info,
2578*4882a593Smuzhiyun 	.get =		snd_trident_vol_control_get,
2579*4882a593Smuzhiyun 	.put =		snd_trident_vol_control_put,
2580*4882a593Smuzhiyun 	.private_value = 16,
2581*4882a593Smuzhiyun 	.tlv = { .p = db_scale_gvol },
2582*4882a593Smuzhiyun };
2583*4882a593Smuzhiyun 
2584*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_vol_wave_control =
2585*4882a593Smuzhiyun {
2586*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2587*4882a593Smuzhiyun 	.name =         "Wave Playback Volume",
2588*4882a593Smuzhiyun 	.info =		snd_trident_vol_control_info,
2589*4882a593Smuzhiyun 	.get =		snd_trident_vol_control_get,
2590*4882a593Smuzhiyun 	.put =		snd_trident_vol_control_put,
2591*4882a593Smuzhiyun 	.private_value = 0,
2592*4882a593Smuzhiyun 	.tlv = { .p = db_scale_gvol },
2593*4882a593Smuzhiyun };
2594*4882a593Smuzhiyun 
2595*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2596*4882a593Smuzhiyun     snd_trident_pcm_vol_control
2597*4882a593Smuzhiyun 
2598*4882a593Smuzhiyun     Description: PCM front volume control
2599*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2600*4882a593Smuzhiyun 
snd_trident_pcm_vol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2601*4882a593Smuzhiyun static int snd_trident_pcm_vol_control_info(struct snd_kcontrol *kcontrol,
2602*4882a593Smuzhiyun 					    struct snd_ctl_elem_info *uinfo)
2603*4882a593Smuzhiyun {
2604*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2605*4882a593Smuzhiyun 
2606*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2607*4882a593Smuzhiyun 	uinfo->count = 1;
2608*4882a593Smuzhiyun 	uinfo->value.integer.min = 0;
2609*4882a593Smuzhiyun 	uinfo->value.integer.max = 255;
2610*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
2611*4882a593Smuzhiyun 		uinfo->value.integer.max = 1023;
2612*4882a593Smuzhiyun 	return 0;
2613*4882a593Smuzhiyun }
2614*4882a593Smuzhiyun 
snd_trident_pcm_vol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2615*4882a593Smuzhiyun static int snd_trident_pcm_vol_control_get(struct snd_kcontrol *kcontrol,
2616*4882a593Smuzhiyun 					   struct snd_ctl_elem_value *ucontrol)
2617*4882a593Smuzhiyun {
2618*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2619*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2620*4882a593Smuzhiyun 
2621*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
2622*4882a593Smuzhiyun 		ucontrol->value.integer.value[0] = 1023 - mix->vol;
2623*4882a593Smuzhiyun 	} else {
2624*4882a593Smuzhiyun 		ucontrol->value.integer.value[0] = 255 - (mix->vol>>2);
2625*4882a593Smuzhiyun 	}
2626*4882a593Smuzhiyun 	return 0;
2627*4882a593Smuzhiyun }
2628*4882a593Smuzhiyun 
snd_trident_pcm_vol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2629*4882a593Smuzhiyun static int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol,
2630*4882a593Smuzhiyun 					   struct snd_ctl_elem_value *ucontrol)
2631*4882a593Smuzhiyun {
2632*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2633*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2634*4882a593Smuzhiyun 	unsigned int val;
2635*4882a593Smuzhiyun 	int change = 0;
2636*4882a593Smuzhiyun 
2637*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
2638*4882a593Smuzhiyun 		val = 1023 - (ucontrol->value.integer.value[0] & 1023);
2639*4882a593Smuzhiyun 	} else {
2640*4882a593Smuzhiyun 		val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2;
2641*4882a593Smuzhiyun 	}
2642*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2643*4882a593Smuzhiyun 	change = val != mix->vol;
2644*4882a593Smuzhiyun 	mix->vol = val;
2645*4882a593Smuzhiyun 	if (mix->voice != NULL)
2646*4882a593Smuzhiyun 		snd_trident_write_vol_reg(trident, mix->voice, val);
2647*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2648*4882a593Smuzhiyun 	return change;
2649*4882a593Smuzhiyun }
2650*4882a593Smuzhiyun 
2651*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_pcm_vol_control =
2652*4882a593Smuzhiyun {
2653*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2654*4882a593Smuzhiyun 	.name =         "PCM Front Playback Volume",
2655*4882a593Smuzhiyun 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
2656*4882a593Smuzhiyun 	.count =	32,
2657*4882a593Smuzhiyun 	.info =		snd_trident_pcm_vol_control_info,
2658*4882a593Smuzhiyun 	.get =		snd_trident_pcm_vol_control_get,
2659*4882a593Smuzhiyun 	.put =		snd_trident_pcm_vol_control_put,
2660*4882a593Smuzhiyun 	/* FIXME: no tlv yet */
2661*4882a593Smuzhiyun };
2662*4882a593Smuzhiyun 
2663*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2664*4882a593Smuzhiyun     snd_trident_pcm_pan_control
2665*4882a593Smuzhiyun 
2666*4882a593Smuzhiyun     Description: PCM front pan control
2667*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2668*4882a593Smuzhiyun 
snd_trident_pcm_pan_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2669*4882a593Smuzhiyun static int snd_trident_pcm_pan_control_info(struct snd_kcontrol *kcontrol,
2670*4882a593Smuzhiyun 					    struct snd_ctl_elem_info *uinfo)
2671*4882a593Smuzhiyun {
2672*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2673*4882a593Smuzhiyun 	uinfo->count = 1;
2674*4882a593Smuzhiyun 	uinfo->value.integer.min = 0;
2675*4882a593Smuzhiyun 	uinfo->value.integer.max = 127;
2676*4882a593Smuzhiyun 	return 0;
2677*4882a593Smuzhiyun }
2678*4882a593Smuzhiyun 
snd_trident_pcm_pan_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2679*4882a593Smuzhiyun static int snd_trident_pcm_pan_control_get(struct snd_kcontrol *kcontrol,
2680*4882a593Smuzhiyun 					   struct snd_ctl_elem_value *ucontrol)
2681*4882a593Smuzhiyun {
2682*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2683*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2684*4882a593Smuzhiyun 
2685*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = mix->pan;
2686*4882a593Smuzhiyun 	if (ucontrol->value.integer.value[0] & 0x40) {
2687*4882a593Smuzhiyun 		ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f));
2688*4882a593Smuzhiyun 	} else {
2689*4882a593Smuzhiyun 		ucontrol->value.integer.value[0] |= 0x40;
2690*4882a593Smuzhiyun 	}
2691*4882a593Smuzhiyun 	return 0;
2692*4882a593Smuzhiyun }
2693*4882a593Smuzhiyun 
snd_trident_pcm_pan_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2694*4882a593Smuzhiyun static int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol,
2695*4882a593Smuzhiyun 					   struct snd_ctl_elem_value *ucontrol)
2696*4882a593Smuzhiyun {
2697*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2698*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2699*4882a593Smuzhiyun 	unsigned char val;
2700*4882a593Smuzhiyun 	int change = 0;
2701*4882a593Smuzhiyun 
2702*4882a593Smuzhiyun 	if (ucontrol->value.integer.value[0] & 0x40)
2703*4882a593Smuzhiyun 		val = ucontrol->value.integer.value[0] & 0x3f;
2704*4882a593Smuzhiyun 	else
2705*4882a593Smuzhiyun 		val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40;
2706*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2707*4882a593Smuzhiyun 	change = val != mix->pan;
2708*4882a593Smuzhiyun 	mix->pan = val;
2709*4882a593Smuzhiyun 	if (mix->voice != NULL)
2710*4882a593Smuzhiyun 		snd_trident_write_pan_reg(trident, mix->voice, val);
2711*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2712*4882a593Smuzhiyun 	return change;
2713*4882a593Smuzhiyun }
2714*4882a593Smuzhiyun 
2715*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_pcm_pan_control =
2716*4882a593Smuzhiyun {
2717*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2718*4882a593Smuzhiyun 	.name =         "PCM Pan Playback Control",
2719*4882a593Smuzhiyun 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
2720*4882a593Smuzhiyun 	.count =	32,
2721*4882a593Smuzhiyun 	.info =		snd_trident_pcm_pan_control_info,
2722*4882a593Smuzhiyun 	.get =		snd_trident_pcm_pan_control_get,
2723*4882a593Smuzhiyun 	.put =		snd_trident_pcm_pan_control_put,
2724*4882a593Smuzhiyun };
2725*4882a593Smuzhiyun 
2726*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2727*4882a593Smuzhiyun     snd_trident_pcm_rvol_control
2728*4882a593Smuzhiyun 
2729*4882a593Smuzhiyun     Description: PCM reverb volume control
2730*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2731*4882a593Smuzhiyun 
snd_trident_pcm_rvol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2732*4882a593Smuzhiyun static int snd_trident_pcm_rvol_control_info(struct snd_kcontrol *kcontrol,
2733*4882a593Smuzhiyun 					     struct snd_ctl_elem_info *uinfo)
2734*4882a593Smuzhiyun {
2735*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2736*4882a593Smuzhiyun 	uinfo->count = 1;
2737*4882a593Smuzhiyun 	uinfo->value.integer.min = 0;
2738*4882a593Smuzhiyun 	uinfo->value.integer.max = 127;
2739*4882a593Smuzhiyun 	return 0;
2740*4882a593Smuzhiyun }
2741*4882a593Smuzhiyun 
snd_trident_pcm_rvol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2742*4882a593Smuzhiyun static int snd_trident_pcm_rvol_control_get(struct snd_kcontrol *kcontrol,
2743*4882a593Smuzhiyun 					    struct snd_ctl_elem_value *ucontrol)
2744*4882a593Smuzhiyun {
2745*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2746*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2747*4882a593Smuzhiyun 
2748*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = 127 - mix->rvol;
2749*4882a593Smuzhiyun 	return 0;
2750*4882a593Smuzhiyun }
2751*4882a593Smuzhiyun 
snd_trident_pcm_rvol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2752*4882a593Smuzhiyun static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
2753*4882a593Smuzhiyun 					    struct snd_ctl_elem_value *ucontrol)
2754*4882a593Smuzhiyun {
2755*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2756*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2757*4882a593Smuzhiyun 	unsigned short val;
2758*4882a593Smuzhiyun 	int change = 0;
2759*4882a593Smuzhiyun 
2760*4882a593Smuzhiyun 	val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
2761*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2762*4882a593Smuzhiyun 	change = val != mix->rvol;
2763*4882a593Smuzhiyun 	mix->rvol = val;
2764*4882a593Smuzhiyun 	if (mix->voice != NULL)
2765*4882a593Smuzhiyun 		snd_trident_write_rvol_reg(trident, mix->voice, val);
2766*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2767*4882a593Smuzhiyun 	return change;
2768*4882a593Smuzhiyun }
2769*4882a593Smuzhiyun 
2770*4882a593Smuzhiyun static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
2771*4882a593Smuzhiyun 
2772*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_pcm_rvol_control =
2773*4882a593Smuzhiyun {
2774*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2775*4882a593Smuzhiyun 	.name =         "PCM Reverb Playback Volume",
2776*4882a593Smuzhiyun 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
2777*4882a593Smuzhiyun 	.count = 	32,
2778*4882a593Smuzhiyun 	.info =		snd_trident_pcm_rvol_control_info,
2779*4882a593Smuzhiyun 	.get =		snd_trident_pcm_rvol_control_get,
2780*4882a593Smuzhiyun 	.put =		snd_trident_pcm_rvol_control_put,
2781*4882a593Smuzhiyun 	.tlv = { .p = db_scale_crvol },
2782*4882a593Smuzhiyun };
2783*4882a593Smuzhiyun 
2784*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2785*4882a593Smuzhiyun     snd_trident_pcm_cvol_control
2786*4882a593Smuzhiyun 
2787*4882a593Smuzhiyun     Description: PCM chorus volume control
2788*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2789*4882a593Smuzhiyun 
snd_trident_pcm_cvol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2790*4882a593Smuzhiyun static int snd_trident_pcm_cvol_control_info(struct snd_kcontrol *kcontrol,
2791*4882a593Smuzhiyun 					     struct snd_ctl_elem_info *uinfo)
2792*4882a593Smuzhiyun {
2793*4882a593Smuzhiyun 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2794*4882a593Smuzhiyun 	uinfo->count = 1;
2795*4882a593Smuzhiyun 	uinfo->value.integer.min = 0;
2796*4882a593Smuzhiyun 	uinfo->value.integer.max = 127;
2797*4882a593Smuzhiyun 	return 0;
2798*4882a593Smuzhiyun }
2799*4882a593Smuzhiyun 
snd_trident_pcm_cvol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2800*4882a593Smuzhiyun static int snd_trident_pcm_cvol_control_get(struct snd_kcontrol *kcontrol,
2801*4882a593Smuzhiyun 					    struct snd_ctl_elem_value *ucontrol)
2802*4882a593Smuzhiyun {
2803*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2804*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2805*4882a593Smuzhiyun 
2806*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = 127 - mix->cvol;
2807*4882a593Smuzhiyun 	return 0;
2808*4882a593Smuzhiyun }
2809*4882a593Smuzhiyun 
snd_trident_pcm_cvol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2810*4882a593Smuzhiyun static int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol,
2811*4882a593Smuzhiyun 					    struct snd_ctl_elem_value *ucontrol)
2812*4882a593Smuzhiyun {
2813*4882a593Smuzhiyun 	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2814*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
2815*4882a593Smuzhiyun 	unsigned short val;
2816*4882a593Smuzhiyun 	int change = 0;
2817*4882a593Smuzhiyun 
2818*4882a593Smuzhiyun 	val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
2819*4882a593Smuzhiyun 	spin_lock_irq(&trident->reg_lock);
2820*4882a593Smuzhiyun 	change = val != mix->cvol;
2821*4882a593Smuzhiyun 	mix->cvol = val;
2822*4882a593Smuzhiyun 	if (mix->voice != NULL)
2823*4882a593Smuzhiyun 		snd_trident_write_cvol_reg(trident, mix->voice, val);
2824*4882a593Smuzhiyun 	spin_unlock_irq(&trident->reg_lock);
2825*4882a593Smuzhiyun 	return change;
2826*4882a593Smuzhiyun }
2827*4882a593Smuzhiyun 
2828*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_trident_pcm_cvol_control =
2829*4882a593Smuzhiyun {
2830*4882a593Smuzhiyun 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2831*4882a593Smuzhiyun 	.name =         "PCM Chorus Playback Volume",
2832*4882a593Smuzhiyun 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
2833*4882a593Smuzhiyun 	.count =	32,
2834*4882a593Smuzhiyun 	.info =		snd_trident_pcm_cvol_control_info,
2835*4882a593Smuzhiyun 	.get =		snd_trident_pcm_cvol_control_get,
2836*4882a593Smuzhiyun 	.put =		snd_trident_pcm_cvol_control_put,
2837*4882a593Smuzhiyun 	.tlv = { .p = db_scale_crvol },
2838*4882a593Smuzhiyun };
2839*4882a593Smuzhiyun 
snd_trident_notify_pcm_change1(struct snd_card * card,struct snd_kcontrol * kctl,int num,int activate)2840*4882a593Smuzhiyun static void snd_trident_notify_pcm_change1(struct snd_card *card,
2841*4882a593Smuzhiyun 					   struct snd_kcontrol *kctl,
2842*4882a593Smuzhiyun 					   int num, int activate)
2843*4882a593Smuzhiyun {
2844*4882a593Smuzhiyun 	struct snd_ctl_elem_id id;
2845*4882a593Smuzhiyun 
2846*4882a593Smuzhiyun 	if (! kctl)
2847*4882a593Smuzhiyun 		return;
2848*4882a593Smuzhiyun 	if (activate)
2849*4882a593Smuzhiyun 		kctl->vd[num].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
2850*4882a593Smuzhiyun 	else
2851*4882a593Smuzhiyun 		kctl->vd[num].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
2852*4882a593Smuzhiyun 	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
2853*4882a593Smuzhiyun 		       SNDRV_CTL_EVENT_MASK_INFO,
2854*4882a593Smuzhiyun 		       snd_ctl_build_ioff(&id, kctl, num));
2855*4882a593Smuzhiyun }
2856*4882a593Smuzhiyun 
snd_trident_notify_pcm_change(struct snd_trident * trident,struct snd_trident_pcm_mixer * tmix,int num,int activate)2857*4882a593Smuzhiyun static void snd_trident_notify_pcm_change(struct snd_trident *trident,
2858*4882a593Smuzhiyun 					  struct snd_trident_pcm_mixer *tmix,
2859*4882a593Smuzhiyun 					  int num, int activate)
2860*4882a593Smuzhiyun {
2861*4882a593Smuzhiyun 	snd_trident_notify_pcm_change1(trident->card, trident->ctl_vol, num, activate);
2862*4882a593Smuzhiyun 	snd_trident_notify_pcm_change1(trident->card, trident->ctl_pan, num, activate);
2863*4882a593Smuzhiyun 	snd_trident_notify_pcm_change1(trident->card, trident->ctl_rvol, num, activate);
2864*4882a593Smuzhiyun 	snd_trident_notify_pcm_change1(trident->card, trident->ctl_cvol, num, activate);
2865*4882a593Smuzhiyun }
2866*4882a593Smuzhiyun 
snd_trident_pcm_mixer_build(struct snd_trident * trident,struct snd_trident_voice * voice,struct snd_pcm_substream * substream)2867*4882a593Smuzhiyun static int snd_trident_pcm_mixer_build(struct snd_trident *trident,
2868*4882a593Smuzhiyun 				       struct snd_trident_voice *voice,
2869*4882a593Smuzhiyun 				       struct snd_pcm_substream *substream)
2870*4882a593Smuzhiyun {
2871*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *tmix;
2872*4882a593Smuzhiyun 
2873*4882a593Smuzhiyun 	if (snd_BUG_ON(!trident || !voice || !substream))
2874*4882a593Smuzhiyun 		return -EINVAL;
2875*4882a593Smuzhiyun 	tmix = &trident->pcm_mixer[substream->number];
2876*4882a593Smuzhiyun 	tmix->voice = voice;
2877*4882a593Smuzhiyun 	tmix->vol = T4D_DEFAULT_PCM_VOL;
2878*4882a593Smuzhiyun 	tmix->pan = T4D_DEFAULT_PCM_PAN;
2879*4882a593Smuzhiyun 	tmix->rvol = T4D_DEFAULT_PCM_RVOL;
2880*4882a593Smuzhiyun 	tmix->cvol = T4D_DEFAULT_PCM_CVOL;
2881*4882a593Smuzhiyun 	snd_trident_notify_pcm_change(trident, tmix, substream->number, 1);
2882*4882a593Smuzhiyun 	return 0;
2883*4882a593Smuzhiyun }
2884*4882a593Smuzhiyun 
snd_trident_pcm_mixer_free(struct snd_trident * trident,struct snd_trident_voice * voice,struct snd_pcm_substream * substream)2885*4882a593Smuzhiyun static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_trident_voice *voice, struct snd_pcm_substream *substream)
2886*4882a593Smuzhiyun {
2887*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *tmix;
2888*4882a593Smuzhiyun 
2889*4882a593Smuzhiyun 	if (snd_BUG_ON(!trident || !substream))
2890*4882a593Smuzhiyun 		return -EINVAL;
2891*4882a593Smuzhiyun 	tmix = &trident->pcm_mixer[substream->number];
2892*4882a593Smuzhiyun 	tmix->voice = NULL;
2893*4882a593Smuzhiyun 	snd_trident_notify_pcm_change(trident, tmix, substream->number, 0);
2894*4882a593Smuzhiyun 	return 0;
2895*4882a593Smuzhiyun }
2896*4882a593Smuzhiyun 
2897*4882a593Smuzhiyun /*---------------------------------------------------------------------------
2898*4882a593Smuzhiyun    snd_trident_mixer
2899*4882a593Smuzhiyun 
2900*4882a593Smuzhiyun    Description: This routine registers the 4DWave device for mixer support.
2901*4882a593Smuzhiyun 
2902*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
2903*4882a593Smuzhiyun 
2904*4882a593Smuzhiyun    Returns:     None
2905*4882a593Smuzhiyun 
2906*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
2907*4882a593Smuzhiyun 
snd_trident_mixer(struct snd_trident * trident,int pcm_spdif_device)2908*4882a593Smuzhiyun static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
2909*4882a593Smuzhiyun {
2910*4882a593Smuzhiyun 	struct snd_ac97_template _ac97;
2911*4882a593Smuzhiyun 	struct snd_card *card = trident->card;
2912*4882a593Smuzhiyun 	struct snd_kcontrol *kctl;
2913*4882a593Smuzhiyun 	struct snd_ctl_elem_value *uctl;
2914*4882a593Smuzhiyun 	int idx, err, retries = 2;
2915*4882a593Smuzhiyun 	static const struct snd_ac97_bus_ops ops = {
2916*4882a593Smuzhiyun 		.write = snd_trident_codec_write,
2917*4882a593Smuzhiyun 		.read = snd_trident_codec_read,
2918*4882a593Smuzhiyun 	};
2919*4882a593Smuzhiyun 
2920*4882a593Smuzhiyun 	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
2921*4882a593Smuzhiyun 	if (!uctl)
2922*4882a593Smuzhiyun 		return -ENOMEM;
2923*4882a593Smuzhiyun 
2924*4882a593Smuzhiyun 	if ((err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus)) < 0)
2925*4882a593Smuzhiyun 		goto __out;
2926*4882a593Smuzhiyun 
2927*4882a593Smuzhiyun 	memset(&_ac97, 0, sizeof(_ac97));
2928*4882a593Smuzhiyun 	_ac97.private_data = trident;
2929*4882a593Smuzhiyun 	trident->ac97_detect = 1;
2930*4882a593Smuzhiyun 
2931*4882a593Smuzhiyun       __again:
2932*4882a593Smuzhiyun 	if ((err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97)) < 0) {
2933*4882a593Smuzhiyun 		if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
2934*4882a593Smuzhiyun 			if ((err = snd_trident_sis_reset(trident)) < 0)
2935*4882a593Smuzhiyun 				goto __out;
2936*4882a593Smuzhiyun 			if (retries-- > 0)
2937*4882a593Smuzhiyun 				goto __again;
2938*4882a593Smuzhiyun 			err = -EIO;
2939*4882a593Smuzhiyun 		}
2940*4882a593Smuzhiyun 		goto __out;
2941*4882a593Smuzhiyun 	}
2942*4882a593Smuzhiyun 
2943*4882a593Smuzhiyun 	/* secondary codec? */
2944*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018 &&
2945*4882a593Smuzhiyun 	    (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) {
2946*4882a593Smuzhiyun 		_ac97.num = 1;
2947*4882a593Smuzhiyun 		err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec);
2948*4882a593Smuzhiyun 		if (err < 0)
2949*4882a593Smuzhiyun 			dev_err(trident->card->dev,
2950*4882a593Smuzhiyun 				"SI7018: the secondary codec - invalid access\n");
2951*4882a593Smuzhiyun #if 0	// only for my testing purpose --jk
2952*4882a593Smuzhiyun 		{
2953*4882a593Smuzhiyun 			struct snd_ac97 *mc97;
2954*4882a593Smuzhiyun 			err = snd_ac97_modem(trident->card, &_ac97, &mc97);
2955*4882a593Smuzhiyun 			if (err < 0)
2956*4882a593Smuzhiyun 				dev_err(trident->card->dev,
2957*4882a593Smuzhiyun 					"snd_ac97_modem returned error %i\n", err);
2958*4882a593Smuzhiyun 		}
2959*4882a593Smuzhiyun #endif
2960*4882a593Smuzhiyun 	}
2961*4882a593Smuzhiyun 
2962*4882a593Smuzhiyun 	trident->ac97_detect = 0;
2963*4882a593Smuzhiyun 
2964*4882a593Smuzhiyun 	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
2965*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0)
2966*4882a593Smuzhiyun 			goto __out;
2967*4882a593Smuzhiyun 		kctl->put(kctl, uctl);
2968*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0)
2969*4882a593Smuzhiyun 			goto __out;
2970*4882a593Smuzhiyun 		kctl->put(kctl, uctl);
2971*4882a593Smuzhiyun 		outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
2972*4882a593Smuzhiyun 	} else {
2973*4882a593Smuzhiyun 		outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
2974*4882a593Smuzhiyun 	}
2975*4882a593Smuzhiyun 
2976*4882a593Smuzhiyun 	for (idx = 0; idx < 32; idx++) {
2977*4882a593Smuzhiyun 		struct snd_trident_pcm_mixer *tmix;
2978*4882a593Smuzhiyun 
2979*4882a593Smuzhiyun 		tmix = &trident->pcm_mixer[idx];
2980*4882a593Smuzhiyun 		tmix->voice = NULL;
2981*4882a593Smuzhiyun 	}
2982*4882a593Smuzhiyun 	if ((trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL)
2983*4882a593Smuzhiyun 		goto __nomem;
2984*4882a593Smuzhiyun 	if ((err = snd_ctl_add(card, trident->ctl_vol)))
2985*4882a593Smuzhiyun 		goto __out;
2986*4882a593Smuzhiyun 
2987*4882a593Smuzhiyun 	if ((trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL)
2988*4882a593Smuzhiyun 		goto __nomem;
2989*4882a593Smuzhiyun 	if ((err = snd_ctl_add(card, trident->ctl_pan)))
2990*4882a593Smuzhiyun 		goto __out;
2991*4882a593Smuzhiyun 
2992*4882a593Smuzhiyun 	if ((trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL)
2993*4882a593Smuzhiyun 		goto __nomem;
2994*4882a593Smuzhiyun 	if ((err = snd_ctl_add(card, trident->ctl_rvol)))
2995*4882a593Smuzhiyun 		goto __out;
2996*4882a593Smuzhiyun 
2997*4882a593Smuzhiyun 	if ((trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL)
2998*4882a593Smuzhiyun 		goto __nomem;
2999*4882a593Smuzhiyun 	if ((err = snd_ctl_add(card, trident->ctl_cvol)))
3000*4882a593Smuzhiyun 		goto __out;
3001*4882a593Smuzhiyun 
3002*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_NX) {
3003*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0)
3004*4882a593Smuzhiyun 			goto __out;
3005*4882a593Smuzhiyun 		kctl->put(kctl, uctl);
3006*4882a593Smuzhiyun 	}
3007*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
3008*4882a593Smuzhiyun 
3009*4882a593Smuzhiyun 		kctl = snd_ctl_new1(&snd_trident_spdif_control, trident);
3010*4882a593Smuzhiyun 		if (kctl == NULL) {
3011*4882a593Smuzhiyun 			err = -ENOMEM;
3012*4882a593Smuzhiyun 			goto __out;
3013*4882a593Smuzhiyun 		}
3014*4882a593Smuzhiyun 		if (trident->ac97->ext_id & AC97_EI_SPDIF)
3015*4882a593Smuzhiyun 			kctl->id.index++;
3016*4882a593Smuzhiyun 		if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF))
3017*4882a593Smuzhiyun 			kctl->id.index++;
3018*4882a593Smuzhiyun 		idx = kctl->id.index;
3019*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, kctl)) < 0)
3020*4882a593Smuzhiyun 			goto __out;
3021*4882a593Smuzhiyun 		kctl->put(kctl, uctl);
3022*4882a593Smuzhiyun 
3023*4882a593Smuzhiyun 		kctl = snd_ctl_new1(&snd_trident_spdif_default, trident);
3024*4882a593Smuzhiyun 		if (kctl == NULL) {
3025*4882a593Smuzhiyun 			err = -ENOMEM;
3026*4882a593Smuzhiyun 			goto __out;
3027*4882a593Smuzhiyun 		}
3028*4882a593Smuzhiyun 		kctl->id.index = idx;
3029*4882a593Smuzhiyun 		kctl->id.device = pcm_spdif_device;
3030*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, kctl)) < 0)
3031*4882a593Smuzhiyun 			goto __out;
3032*4882a593Smuzhiyun 
3033*4882a593Smuzhiyun 		kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident);
3034*4882a593Smuzhiyun 		if (kctl == NULL) {
3035*4882a593Smuzhiyun 			err = -ENOMEM;
3036*4882a593Smuzhiyun 			goto __out;
3037*4882a593Smuzhiyun 		}
3038*4882a593Smuzhiyun 		kctl->id.index = idx;
3039*4882a593Smuzhiyun 		kctl->id.device = pcm_spdif_device;
3040*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, kctl)) < 0)
3041*4882a593Smuzhiyun 			goto __out;
3042*4882a593Smuzhiyun 
3043*4882a593Smuzhiyun 		kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident);
3044*4882a593Smuzhiyun 		if (kctl == NULL) {
3045*4882a593Smuzhiyun 			err = -ENOMEM;
3046*4882a593Smuzhiyun 			goto __out;
3047*4882a593Smuzhiyun 		}
3048*4882a593Smuzhiyun 		kctl->id.index = idx;
3049*4882a593Smuzhiyun 		kctl->id.device = pcm_spdif_device;
3050*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, kctl)) < 0)
3051*4882a593Smuzhiyun 			goto __out;
3052*4882a593Smuzhiyun 		trident->spdif_pcm_ctl = kctl;
3053*4882a593Smuzhiyun 	}
3054*4882a593Smuzhiyun 
3055*4882a593Smuzhiyun 	err = 0;
3056*4882a593Smuzhiyun 	goto __out;
3057*4882a593Smuzhiyun 
3058*4882a593Smuzhiyun  __nomem:
3059*4882a593Smuzhiyun 	err = -ENOMEM;
3060*4882a593Smuzhiyun 
3061*4882a593Smuzhiyun  __out:
3062*4882a593Smuzhiyun 	kfree(uctl);
3063*4882a593Smuzhiyun 
3064*4882a593Smuzhiyun 	return err;
3065*4882a593Smuzhiyun }
3066*4882a593Smuzhiyun 
3067*4882a593Smuzhiyun /*
3068*4882a593Smuzhiyun  * gameport interface
3069*4882a593Smuzhiyun  */
3070*4882a593Smuzhiyun 
3071*4882a593Smuzhiyun #if IS_REACHABLE(CONFIG_GAMEPORT)
3072*4882a593Smuzhiyun 
snd_trident_gameport_read(struct gameport * gameport)3073*4882a593Smuzhiyun static unsigned char snd_trident_gameport_read(struct gameport *gameport)
3074*4882a593Smuzhiyun {
3075*4882a593Smuzhiyun 	struct snd_trident *chip = gameport_get_port_data(gameport);
3076*4882a593Smuzhiyun 
3077*4882a593Smuzhiyun 	if (snd_BUG_ON(!chip))
3078*4882a593Smuzhiyun 		return 0;
3079*4882a593Smuzhiyun 	return inb(TRID_REG(chip, GAMEPORT_LEGACY));
3080*4882a593Smuzhiyun }
3081*4882a593Smuzhiyun 
snd_trident_gameport_trigger(struct gameport * gameport)3082*4882a593Smuzhiyun static void snd_trident_gameport_trigger(struct gameport *gameport)
3083*4882a593Smuzhiyun {
3084*4882a593Smuzhiyun 	struct snd_trident *chip = gameport_get_port_data(gameport);
3085*4882a593Smuzhiyun 
3086*4882a593Smuzhiyun 	if (snd_BUG_ON(!chip))
3087*4882a593Smuzhiyun 		return;
3088*4882a593Smuzhiyun 	outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));
3089*4882a593Smuzhiyun }
3090*4882a593Smuzhiyun 
snd_trident_gameport_cooked_read(struct gameport * gameport,int * axes,int * buttons)3091*4882a593Smuzhiyun static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
3092*4882a593Smuzhiyun {
3093*4882a593Smuzhiyun 	struct snd_trident *chip = gameport_get_port_data(gameport);
3094*4882a593Smuzhiyun 	int i;
3095*4882a593Smuzhiyun 
3096*4882a593Smuzhiyun 	if (snd_BUG_ON(!chip))
3097*4882a593Smuzhiyun 		return 0;
3098*4882a593Smuzhiyun 
3099*4882a593Smuzhiyun 	*buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf;
3100*4882a593Smuzhiyun 
3101*4882a593Smuzhiyun 	for (i = 0; i < 4; i++) {
3102*4882a593Smuzhiyun 		axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2));
3103*4882a593Smuzhiyun 		if (axes[i] == 0xffff) axes[i] = -1;
3104*4882a593Smuzhiyun 	}
3105*4882a593Smuzhiyun 
3106*4882a593Smuzhiyun         return 0;
3107*4882a593Smuzhiyun }
3108*4882a593Smuzhiyun 
snd_trident_gameport_open(struct gameport * gameport,int mode)3109*4882a593Smuzhiyun static int snd_trident_gameport_open(struct gameport *gameport, int mode)
3110*4882a593Smuzhiyun {
3111*4882a593Smuzhiyun 	struct snd_trident *chip = gameport_get_port_data(gameport);
3112*4882a593Smuzhiyun 
3113*4882a593Smuzhiyun 	if (snd_BUG_ON(!chip))
3114*4882a593Smuzhiyun 		return 0;
3115*4882a593Smuzhiyun 
3116*4882a593Smuzhiyun 	switch (mode) {
3117*4882a593Smuzhiyun 		case GAMEPORT_MODE_COOKED:
3118*4882a593Smuzhiyun 			outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR));
3119*4882a593Smuzhiyun 			msleep(20);
3120*4882a593Smuzhiyun 			return 0;
3121*4882a593Smuzhiyun 		case GAMEPORT_MODE_RAW:
3122*4882a593Smuzhiyun 			outb(0, TRID_REG(chip, GAMEPORT_GCR));
3123*4882a593Smuzhiyun 			return 0;
3124*4882a593Smuzhiyun 		default:
3125*4882a593Smuzhiyun 			return -1;
3126*4882a593Smuzhiyun 	}
3127*4882a593Smuzhiyun }
3128*4882a593Smuzhiyun 
snd_trident_create_gameport(struct snd_trident * chip)3129*4882a593Smuzhiyun int snd_trident_create_gameport(struct snd_trident *chip)
3130*4882a593Smuzhiyun {
3131*4882a593Smuzhiyun 	struct gameport *gp;
3132*4882a593Smuzhiyun 
3133*4882a593Smuzhiyun 	chip->gameport = gp = gameport_allocate_port();
3134*4882a593Smuzhiyun 	if (!gp) {
3135*4882a593Smuzhiyun 		dev_err(chip->card->dev,
3136*4882a593Smuzhiyun 			"cannot allocate memory for gameport\n");
3137*4882a593Smuzhiyun 		return -ENOMEM;
3138*4882a593Smuzhiyun 	}
3139*4882a593Smuzhiyun 
3140*4882a593Smuzhiyun 	gameport_set_name(gp, "Trident 4DWave");
3141*4882a593Smuzhiyun 	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
3142*4882a593Smuzhiyun 	gameport_set_dev_parent(gp, &chip->pci->dev);
3143*4882a593Smuzhiyun 
3144*4882a593Smuzhiyun 	gameport_set_port_data(gp, chip);
3145*4882a593Smuzhiyun 	gp->fuzz = 64;
3146*4882a593Smuzhiyun 	gp->read = snd_trident_gameport_read;
3147*4882a593Smuzhiyun 	gp->trigger = snd_trident_gameport_trigger;
3148*4882a593Smuzhiyun 	gp->cooked_read = snd_trident_gameport_cooked_read;
3149*4882a593Smuzhiyun 	gp->open = snd_trident_gameport_open;
3150*4882a593Smuzhiyun 
3151*4882a593Smuzhiyun 	gameport_register_port(gp);
3152*4882a593Smuzhiyun 
3153*4882a593Smuzhiyun 	return 0;
3154*4882a593Smuzhiyun }
3155*4882a593Smuzhiyun 
snd_trident_free_gameport(struct snd_trident * chip)3156*4882a593Smuzhiyun static inline void snd_trident_free_gameport(struct snd_trident *chip)
3157*4882a593Smuzhiyun {
3158*4882a593Smuzhiyun 	if (chip->gameport) {
3159*4882a593Smuzhiyun 		gameport_unregister_port(chip->gameport);
3160*4882a593Smuzhiyun 		chip->gameport = NULL;
3161*4882a593Smuzhiyun 	}
3162*4882a593Smuzhiyun }
3163*4882a593Smuzhiyun #else
snd_trident_create_gameport(struct snd_trident * chip)3164*4882a593Smuzhiyun int snd_trident_create_gameport(struct snd_trident *chip) { return -ENOSYS; }
snd_trident_free_gameport(struct snd_trident * chip)3165*4882a593Smuzhiyun static inline void snd_trident_free_gameport(struct snd_trident *chip) { }
3166*4882a593Smuzhiyun #endif /* CONFIG_GAMEPORT */
3167*4882a593Smuzhiyun 
3168*4882a593Smuzhiyun /*
3169*4882a593Smuzhiyun  * delay for 1 tick
3170*4882a593Smuzhiyun  */
do_delay(struct snd_trident * chip)3171*4882a593Smuzhiyun static inline void do_delay(struct snd_trident *chip)
3172*4882a593Smuzhiyun {
3173*4882a593Smuzhiyun 	schedule_timeout_uninterruptible(1);
3174*4882a593Smuzhiyun }
3175*4882a593Smuzhiyun 
3176*4882a593Smuzhiyun /*
3177*4882a593Smuzhiyun  *  SiS reset routine
3178*4882a593Smuzhiyun  */
3179*4882a593Smuzhiyun 
snd_trident_sis_reset(struct snd_trident * trident)3180*4882a593Smuzhiyun static int snd_trident_sis_reset(struct snd_trident *trident)
3181*4882a593Smuzhiyun {
3182*4882a593Smuzhiyun 	unsigned long end_time;
3183*4882a593Smuzhiyun 	unsigned int i;
3184*4882a593Smuzhiyun 	int r;
3185*4882a593Smuzhiyun 
3186*4882a593Smuzhiyun 	r = trident->in_suspend ? 0 : 2;	/* count of retries */
3187*4882a593Smuzhiyun       __si7018_retry:
3188*4882a593Smuzhiyun 	pci_write_config_byte(trident->pci, 0x46, 0x04);	/* SOFTWARE RESET */
3189*4882a593Smuzhiyun 	udelay(100);
3190*4882a593Smuzhiyun 	pci_write_config_byte(trident->pci, 0x46, 0x00);
3191*4882a593Smuzhiyun 	udelay(100);
3192*4882a593Smuzhiyun 	/* disable AC97 GPIO interrupt */
3193*4882a593Smuzhiyun 	outb(0x00, TRID_REG(trident, SI_AC97_GPIO));
3194*4882a593Smuzhiyun 	/* initialize serial interface, force cold reset */
3195*4882a593Smuzhiyun 	i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET;
3196*4882a593Smuzhiyun 	outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
3197*4882a593Smuzhiyun 	udelay(1000);
3198*4882a593Smuzhiyun 	/* remove cold reset */
3199*4882a593Smuzhiyun 	i &= ~COLD_RESET;
3200*4882a593Smuzhiyun 	outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
3201*4882a593Smuzhiyun 	udelay(2000);
3202*4882a593Smuzhiyun 	/* wait, until the codec is ready */
3203*4882a593Smuzhiyun 	end_time = (jiffies + (HZ * 3) / 4) + 1;
3204*4882a593Smuzhiyun 	do {
3205*4882a593Smuzhiyun 		if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0)
3206*4882a593Smuzhiyun 			goto __si7018_ok;
3207*4882a593Smuzhiyun 		do_delay(trident);
3208*4882a593Smuzhiyun 	} while (time_after_eq(end_time, jiffies));
3209*4882a593Smuzhiyun 	dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
3210*4882a593Smuzhiyun 		inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
3211*4882a593Smuzhiyun 	if (r-- > 0) {
3212*4882a593Smuzhiyun 		end_time = jiffies + HZ;
3213*4882a593Smuzhiyun 		do {
3214*4882a593Smuzhiyun 			do_delay(trident);
3215*4882a593Smuzhiyun 		} while (time_after_eq(end_time, jiffies));
3216*4882a593Smuzhiyun 		goto __si7018_retry;
3217*4882a593Smuzhiyun 	}
3218*4882a593Smuzhiyun       __si7018_ok:
3219*4882a593Smuzhiyun 	/* wait for the second codec */
3220*4882a593Smuzhiyun 	do {
3221*4882a593Smuzhiyun 		if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0)
3222*4882a593Smuzhiyun 			break;
3223*4882a593Smuzhiyun 		do_delay(trident);
3224*4882a593Smuzhiyun 	} while (time_after_eq(end_time, jiffies));
3225*4882a593Smuzhiyun 	/* enable 64 channel mode */
3226*4882a593Smuzhiyun 	outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR));
3227*4882a593Smuzhiyun 	return 0;
3228*4882a593Smuzhiyun }
3229*4882a593Smuzhiyun 
3230*4882a593Smuzhiyun /*
3231*4882a593Smuzhiyun  *  /proc interface
3232*4882a593Smuzhiyun  */
3233*4882a593Smuzhiyun 
snd_trident_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)3234*4882a593Smuzhiyun static void snd_trident_proc_read(struct snd_info_entry *entry,
3235*4882a593Smuzhiyun 				  struct snd_info_buffer *buffer)
3236*4882a593Smuzhiyun {
3237*4882a593Smuzhiyun 	struct snd_trident *trident = entry->private_data;
3238*4882a593Smuzhiyun 	char *s;
3239*4882a593Smuzhiyun 
3240*4882a593Smuzhiyun 	switch (trident->device) {
3241*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_SI7018:
3242*4882a593Smuzhiyun 		s = "SiS 7018 Audio";
3243*4882a593Smuzhiyun 		break;
3244*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_DX:
3245*4882a593Smuzhiyun 		s = "Trident 4DWave PCI DX";
3246*4882a593Smuzhiyun 		break;
3247*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_NX:
3248*4882a593Smuzhiyun 		s = "Trident 4DWave PCI NX";
3249*4882a593Smuzhiyun 		break;
3250*4882a593Smuzhiyun 	default:
3251*4882a593Smuzhiyun 		s = "???";
3252*4882a593Smuzhiyun 	}
3253*4882a593Smuzhiyun 	snd_iprintf(buffer, "%s\n\n", s);
3254*4882a593Smuzhiyun 	snd_iprintf(buffer, "Spurious IRQs    : %d\n", trident->spurious_irq_count);
3255*4882a593Smuzhiyun 	snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta);
3256*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018)
3257*4882a593Smuzhiyun 		snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off");
3258*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_NX) {
3259*4882a593Smuzhiyun 		snd_iprintf(buffer, "Rear Speakers    : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off");
3260*4882a593Smuzhiyun 		if (trident->tlb.entries) {
3261*4882a593Smuzhiyun 			snd_iprintf(buffer,"\nVirtual Memory\n");
3262*4882a593Smuzhiyun 			snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size);
3263*4882a593Smuzhiyun 			snd_iprintf(buffer, "Memory Used    : %d\n", trident->tlb.memhdr->used);
3264*4882a593Smuzhiyun 			snd_iprintf(buffer, "Memory Free    : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
3265*4882a593Smuzhiyun 		}
3266*4882a593Smuzhiyun 	}
3267*4882a593Smuzhiyun }
3268*4882a593Smuzhiyun 
snd_trident_proc_init(struct snd_trident * trident)3269*4882a593Smuzhiyun static void snd_trident_proc_init(struct snd_trident *trident)
3270*4882a593Smuzhiyun {
3271*4882a593Smuzhiyun 	const char *s = "trident";
3272*4882a593Smuzhiyun 
3273*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
3274*4882a593Smuzhiyun 		s = "sis7018";
3275*4882a593Smuzhiyun 	snd_card_ro_proc_new(trident->card, s, trident, snd_trident_proc_read);
3276*4882a593Smuzhiyun }
3277*4882a593Smuzhiyun 
snd_trident_dev_free(struct snd_device * device)3278*4882a593Smuzhiyun static int snd_trident_dev_free(struct snd_device *device)
3279*4882a593Smuzhiyun {
3280*4882a593Smuzhiyun 	struct snd_trident *trident = device->device_data;
3281*4882a593Smuzhiyun 	return snd_trident_free(trident);
3282*4882a593Smuzhiyun }
3283*4882a593Smuzhiyun 
3284*4882a593Smuzhiyun /*---------------------------------------------------------------------------
3285*4882a593Smuzhiyun    snd_trident_tlb_alloc
3286*4882a593Smuzhiyun 
3287*4882a593Smuzhiyun    Description: Allocate and set up the TLB page table on 4D NX.
3288*4882a593Smuzhiyun 		Each entry has 4 bytes (physical PCI address).
3289*4882a593Smuzhiyun 
3290*4882a593Smuzhiyun    Parameters:  trident - pointer to target device class for 4DWave.
3291*4882a593Smuzhiyun 
3292*4882a593Smuzhiyun    Returns:     0 or negative error code
3293*4882a593Smuzhiyun 
3294*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
3295*4882a593Smuzhiyun 
snd_trident_tlb_alloc(struct snd_trident * trident)3296*4882a593Smuzhiyun static int snd_trident_tlb_alloc(struct snd_trident *trident)
3297*4882a593Smuzhiyun {
3298*4882a593Smuzhiyun 	int i;
3299*4882a593Smuzhiyun 
3300*4882a593Smuzhiyun 	/* TLB array must be aligned to 16kB !!! so we allocate
3301*4882a593Smuzhiyun 	   32kB region and correct offset when necessary */
3302*4882a593Smuzhiyun 
3303*4882a593Smuzhiyun 	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &trident->pci->dev,
3304*4882a593Smuzhiyun 				2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) {
3305*4882a593Smuzhiyun 		dev_err(trident->card->dev, "unable to allocate TLB buffer\n");
3306*4882a593Smuzhiyun 		return -ENOMEM;
3307*4882a593Smuzhiyun 	}
3308*4882a593Smuzhiyun 	trident->tlb.entries = (__le32 *)ALIGN((unsigned long)trident->tlb.buffer.area, SNDRV_TRIDENT_MAX_PAGES * 4);
3309*4882a593Smuzhiyun 	trident->tlb.entries_dmaaddr = ALIGN(trident->tlb.buffer.addr, SNDRV_TRIDENT_MAX_PAGES * 4);
3310*4882a593Smuzhiyun 	/* allocate shadow TLB page table (virtual addresses) */
3311*4882a593Smuzhiyun 	trident->tlb.shadow_entries =
3312*4882a593Smuzhiyun 		vmalloc(array_size(SNDRV_TRIDENT_MAX_PAGES,
3313*4882a593Smuzhiyun 				   sizeof(unsigned long)));
3314*4882a593Smuzhiyun 	if (!trident->tlb.shadow_entries)
3315*4882a593Smuzhiyun 		return -ENOMEM;
3316*4882a593Smuzhiyun 
3317*4882a593Smuzhiyun 	/* allocate and setup silent page and initialise TLB entries */
3318*4882a593Smuzhiyun 	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &trident->pci->dev,
3319*4882a593Smuzhiyun 				SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) {
3320*4882a593Smuzhiyun 		dev_err(trident->card->dev, "unable to allocate silent page\n");
3321*4882a593Smuzhiyun 		return -ENOMEM;
3322*4882a593Smuzhiyun 	}
3323*4882a593Smuzhiyun 	memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE);
3324*4882a593Smuzhiyun 	for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) {
3325*4882a593Smuzhiyun 		trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1));
3326*4882a593Smuzhiyun 		trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area;
3327*4882a593Smuzhiyun 	}
3328*4882a593Smuzhiyun 
3329*4882a593Smuzhiyun 	/* use emu memory block manager code to manage tlb page allocation */
3330*4882a593Smuzhiyun 	trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES);
3331*4882a593Smuzhiyun 	if (trident->tlb.memhdr == NULL)
3332*4882a593Smuzhiyun 		return -ENOMEM;
3333*4882a593Smuzhiyun 
3334*4882a593Smuzhiyun 	trident->tlb.memhdr->block_extra_size = sizeof(struct snd_trident_memblk_arg);
3335*4882a593Smuzhiyun 	return 0;
3336*4882a593Smuzhiyun }
3337*4882a593Smuzhiyun 
3338*4882a593Smuzhiyun /*
3339*4882a593Smuzhiyun  * initialize 4D DX chip
3340*4882a593Smuzhiyun  */
3341*4882a593Smuzhiyun 
snd_trident_stop_all_voices(struct snd_trident * trident)3342*4882a593Smuzhiyun static void snd_trident_stop_all_voices(struct snd_trident *trident)
3343*4882a593Smuzhiyun {
3344*4882a593Smuzhiyun 	outl(0xffffffff, TRID_REG(trident, T4D_STOP_A));
3345*4882a593Smuzhiyun 	outl(0xffffffff, TRID_REG(trident, T4D_STOP_B));
3346*4882a593Smuzhiyun 	outl(0, TRID_REG(trident, T4D_AINTEN_A));
3347*4882a593Smuzhiyun 	outl(0, TRID_REG(trident, T4D_AINTEN_B));
3348*4882a593Smuzhiyun }
3349*4882a593Smuzhiyun 
snd_trident_4d_dx_init(struct snd_trident * trident)3350*4882a593Smuzhiyun static int snd_trident_4d_dx_init(struct snd_trident *trident)
3351*4882a593Smuzhiyun {
3352*4882a593Smuzhiyun 	struct pci_dev *pci = trident->pci;
3353*4882a593Smuzhiyun 	unsigned long end_time;
3354*4882a593Smuzhiyun 
3355*4882a593Smuzhiyun 	/* reset the legacy configuration and whole audio/wavetable block */
3356*4882a593Smuzhiyun 	pci_write_config_dword(pci, 0x40, 0);	/* DDMA */
3357*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x44, 0);	/* ports */
3358*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x45, 0);	/* Legacy DMA */
3359*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x46, 4); /* reset */
3360*4882a593Smuzhiyun 	udelay(100);
3361*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x46, 0); /* release reset */
3362*4882a593Smuzhiyun 	udelay(100);
3363*4882a593Smuzhiyun 
3364*4882a593Smuzhiyun 	/* warm reset of the AC'97 codec */
3365*4882a593Smuzhiyun 	outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
3366*4882a593Smuzhiyun 	udelay(100);
3367*4882a593Smuzhiyun 	outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
3368*4882a593Smuzhiyun 	/* DAC on, disable SB IRQ and try to force ADC valid signal */
3369*4882a593Smuzhiyun 	trident->ac97_ctrl = 0x0000004a;
3370*4882a593Smuzhiyun 	outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
3371*4882a593Smuzhiyun 	/* wait, until the codec is ready */
3372*4882a593Smuzhiyun 	end_time = (jiffies + (HZ * 3) / 4) + 1;
3373*4882a593Smuzhiyun 	do {
3374*4882a593Smuzhiyun 		if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0)
3375*4882a593Smuzhiyun 			goto __dx_ok;
3376*4882a593Smuzhiyun 		do_delay(trident);
3377*4882a593Smuzhiyun 	} while (time_after_eq(end_time, jiffies));
3378*4882a593Smuzhiyun 	dev_err(trident->card->dev, "AC'97 codec ready error\n");
3379*4882a593Smuzhiyun 	return -EIO;
3380*4882a593Smuzhiyun 
3381*4882a593Smuzhiyun  __dx_ok:
3382*4882a593Smuzhiyun 	snd_trident_stop_all_voices(trident);
3383*4882a593Smuzhiyun 
3384*4882a593Smuzhiyun 	return 0;
3385*4882a593Smuzhiyun }
3386*4882a593Smuzhiyun 
3387*4882a593Smuzhiyun /*
3388*4882a593Smuzhiyun  * initialize 4D NX chip
3389*4882a593Smuzhiyun  */
snd_trident_4d_nx_init(struct snd_trident * trident)3390*4882a593Smuzhiyun static int snd_trident_4d_nx_init(struct snd_trident *trident)
3391*4882a593Smuzhiyun {
3392*4882a593Smuzhiyun 	struct pci_dev *pci = trident->pci;
3393*4882a593Smuzhiyun 	unsigned long end_time;
3394*4882a593Smuzhiyun 
3395*4882a593Smuzhiyun 	/* reset the legacy configuration and whole audio/wavetable block */
3396*4882a593Smuzhiyun 	pci_write_config_dword(pci, 0x40, 0);	/* DDMA */
3397*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x44, 0);	/* ports */
3398*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x45, 0);	/* Legacy DMA */
3399*4882a593Smuzhiyun 
3400*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x46, 1); /* reset */
3401*4882a593Smuzhiyun 	udelay(100);
3402*4882a593Smuzhiyun 	pci_write_config_byte(pci, 0x46, 0); /* release reset */
3403*4882a593Smuzhiyun 	udelay(100);
3404*4882a593Smuzhiyun 
3405*4882a593Smuzhiyun 	/* warm reset of the AC'97 codec */
3406*4882a593Smuzhiyun 	outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
3407*4882a593Smuzhiyun 	udelay(100);
3408*4882a593Smuzhiyun 	outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
3409*4882a593Smuzhiyun 	/* wait, until the codec is ready */
3410*4882a593Smuzhiyun 	end_time = (jiffies + (HZ * 3) / 4) + 1;
3411*4882a593Smuzhiyun 	do {
3412*4882a593Smuzhiyun 		if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0)
3413*4882a593Smuzhiyun 			goto __nx_ok;
3414*4882a593Smuzhiyun 		do_delay(trident);
3415*4882a593Smuzhiyun 	} while (time_after_eq(end_time, jiffies));
3416*4882a593Smuzhiyun 	dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
3417*4882a593Smuzhiyun 		inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
3418*4882a593Smuzhiyun 	return -EIO;
3419*4882a593Smuzhiyun 
3420*4882a593Smuzhiyun  __nx_ok:
3421*4882a593Smuzhiyun 	/* DAC on */
3422*4882a593Smuzhiyun 	trident->ac97_ctrl = 0x00000002;
3423*4882a593Smuzhiyun 	outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
3424*4882a593Smuzhiyun 	/* disable SB IRQ */
3425*4882a593Smuzhiyun 	outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT));
3426*4882a593Smuzhiyun 
3427*4882a593Smuzhiyun 	snd_trident_stop_all_voices(trident);
3428*4882a593Smuzhiyun 
3429*4882a593Smuzhiyun 	if (trident->tlb.entries != NULL) {
3430*4882a593Smuzhiyun 		unsigned int i;
3431*4882a593Smuzhiyun 		/* enable virtual addressing via TLB */
3432*4882a593Smuzhiyun 		i = trident->tlb.entries_dmaaddr;
3433*4882a593Smuzhiyun 		i |= 0x00000001;
3434*4882a593Smuzhiyun 		outl(i, TRID_REG(trident, NX_TLBC));
3435*4882a593Smuzhiyun 	} else {
3436*4882a593Smuzhiyun 		outl(0, TRID_REG(trident, NX_TLBC));
3437*4882a593Smuzhiyun 	}
3438*4882a593Smuzhiyun 	/* initialize S/PDIF */
3439*4882a593Smuzhiyun 	outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
3440*4882a593Smuzhiyun 	outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
3441*4882a593Smuzhiyun 
3442*4882a593Smuzhiyun 	return 0;
3443*4882a593Smuzhiyun }
3444*4882a593Smuzhiyun 
3445*4882a593Smuzhiyun /*
3446*4882a593Smuzhiyun  * initialize sis7018 chip
3447*4882a593Smuzhiyun  */
snd_trident_sis_init(struct snd_trident * trident)3448*4882a593Smuzhiyun static int snd_trident_sis_init(struct snd_trident *trident)
3449*4882a593Smuzhiyun {
3450*4882a593Smuzhiyun 	int err;
3451*4882a593Smuzhiyun 
3452*4882a593Smuzhiyun 	if ((err = snd_trident_sis_reset(trident)) < 0)
3453*4882a593Smuzhiyun 		return err;
3454*4882a593Smuzhiyun 
3455*4882a593Smuzhiyun 	snd_trident_stop_all_voices(trident);
3456*4882a593Smuzhiyun 
3457*4882a593Smuzhiyun 	/* initialize S/PDIF */
3458*4882a593Smuzhiyun 	outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
3459*4882a593Smuzhiyun 
3460*4882a593Smuzhiyun 	return 0;
3461*4882a593Smuzhiyun }
3462*4882a593Smuzhiyun 
3463*4882a593Smuzhiyun /*---------------------------------------------------------------------------
3464*4882a593Smuzhiyun    snd_trident_create
3465*4882a593Smuzhiyun 
3466*4882a593Smuzhiyun    Description: This routine will create the device specific class for
3467*4882a593Smuzhiyun                 the 4DWave card. It will also perform basic initialization.
3468*4882a593Smuzhiyun 
3469*4882a593Smuzhiyun    Parameters:  card  - which card to create
3470*4882a593Smuzhiyun                 pci   - interface to PCI bus resource info
3471*4882a593Smuzhiyun                 dma1ptr - playback dma buffer
3472*4882a593Smuzhiyun                 dma2ptr - capture dma buffer
3473*4882a593Smuzhiyun                 irqptr  -  interrupt resource info
3474*4882a593Smuzhiyun 
3475*4882a593Smuzhiyun    Returns:     4DWave device class private data
3476*4882a593Smuzhiyun 
3477*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
3478*4882a593Smuzhiyun 
snd_trident_create(struct snd_card * card,struct pci_dev * pci,int pcm_streams,int pcm_spdif_device,int max_wavetable_size,struct snd_trident ** rtrident)3479*4882a593Smuzhiyun int snd_trident_create(struct snd_card *card,
3480*4882a593Smuzhiyun 		       struct pci_dev *pci,
3481*4882a593Smuzhiyun 		       int pcm_streams,
3482*4882a593Smuzhiyun 		       int pcm_spdif_device,
3483*4882a593Smuzhiyun 		       int max_wavetable_size,
3484*4882a593Smuzhiyun 		       struct snd_trident ** rtrident)
3485*4882a593Smuzhiyun {
3486*4882a593Smuzhiyun 	struct snd_trident *trident;
3487*4882a593Smuzhiyun 	int i, err;
3488*4882a593Smuzhiyun 	struct snd_trident_voice *voice;
3489*4882a593Smuzhiyun 	struct snd_trident_pcm_mixer *tmix;
3490*4882a593Smuzhiyun 	static const struct snd_device_ops ops = {
3491*4882a593Smuzhiyun 		.dev_free =	snd_trident_dev_free,
3492*4882a593Smuzhiyun 	};
3493*4882a593Smuzhiyun 
3494*4882a593Smuzhiyun 	*rtrident = NULL;
3495*4882a593Smuzhiyun 
3496*4882a593Smuzhiyun 	/* enable PCI device */
3497*4882a593Smuzhiyun 	if ((err = pci_enable_device(pci)) < 0)
3498*4882a593Smuzhiyun 		return err;
3499*4882a593Smuzhiyun 	/* check, if we can restrict PCI DMA transfers to 30 bits */
3500*4882a593Smuzhiyun 	if (dma_set_mask(&pci->dev, DMA_BIT_MASK(30)) < 0 ||
3501*4882a593Smuzhiyun 	    dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(30)) < 0) {
3502*4882a593Smuzhiyun 		dev_err(card->dev,
3503*4882a593Smuzhiyun 			"architecture does not support 30bit PCI busmaster DMA\n");
3504*4882a593Smuzhiyun 		pci_disable_device(pci);
3505*4882a593Smuzhiyun 		return -ENXIO;
3506*4882a593Smuzhiyun 	}
3507*4882a593Smuzhiyun 
3508*4882a593Smuzhiyun 	trident = kzalloc(sizeof(*trident), GFP_KERNEL);
3509*4882a593Smuzhiyun 	if (trident == NULL) {
3510*4882a593Smuzhiyun 		pci_disable_device(pci);
3511*4882a593Smuzhiyun 		return -ENOMEM;
3512*4882a593Smuzhiyun 	}
3513*4882a593Smuzhiyun 	trident->device = (pci->vendor << 16) | pci->device;
3514*4882a593Smuzhiyun 	trident->card = card;
3515*4882a593Smuzhiyun 	trident->pci = pci;
3516*4882a593Smuzhiyun 	spin_lock_init(&trident->reg_lock);
3517*4882a593Smuzhiyun 	spin_lock_init(&trident->event_lock);
3518*4882a593Smuzhiyun 	spin_lock_init(&trident->voice_alloc);
3519*4882a593Smuzhiyun 	if (pcm_streams < 1)
3520*4882a593Smuzhiyun 		pcm_streams = 1;
3521*4882a593Smuzhiyun 	if (pcm_streams > 32)
3522*4882a593Smuzhiyun 		pcm_streams = 32;
3523*4882a593Smuzhiyun 	trident->ChanPCM = pcm_streams;
3524*4882a593Smuzhiyun 	if (max_wavetable_size < 0 )
3525*4882a593Smuzhiyun 		max_wavetable_size = 0;
3526*4882a593Smuzhiyun 	trident->synth.max_size = max_wavetable_size * 1024;
3527*4882a593Smuzhiyun 	trident->irq = -1;
3528*4882a593Smuzhiyun 
3529*4882a593Smuzhiyun 	trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE);
3530*4882a593Smuzhiyun 	pci_set_master(pci);
3531*4882a593Smuzhiyun 
3532*4882a593Smuzhiyun 	if ((err = pci_request_regions(pci, "Trident Audio")) < 0) {
3533*4882a593Smuzhiyun 		kfree(trident);
3534*4882a593Smuzhiyun 		pci_disable_device(pci);
3535*4882a593Smuzhiyun 		return err;
3536*4882a593Smuzhiyun 	}
3537*4882a593Smuzhiyun 	trident->port = pci_resource_start(pci, 0);
3538*4882a593Smuzhiyun 
3539*4882a593Smuzhiyun 	if (request_irq(pci->irq, snd_trident_interrupt, IRQF_SHARED,
3540*4882a593Smuzhiyun 			KBUILD_MODNAME, trident)) {
3541*4882a593Smuzhiyun 		dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
3542*4882a593Smuzhiyun 		snd_trident_free(trident);
3543*4882a593Smuzhiyun 		return -EBUSY;
3544*4882a593Smuzhiyun 	}
3545*4882a593Smuzhiyun 	trident->irq = pci->irq;
3546*4882a593Smuzhiyun 	card->sync_irq = trident->irq;
3547*4882a593Smuzhiyun 
3548*4882a593Smuzhiyun 	/* allocate 16k-aligned TLB for NX cards */
3549*4882a593Smuzhiyun 	trident->tlb.entries = NULL;
3550*4882a593Smuzhiyun 	trident->tlb.buffer.area = NULL;
3551*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_NX) {
3552*4882a593Smuzhiyun 		if ((err = snd_trident_tlb_alloc(trident)) < 0) {
3553*4882a593Smuzhiyun 			snd_trident_free(trident);
3554*4882a593Smuzhiyun 			return err;
3555*4882a593Smuzhiyun 		}
3556*4882a593Smuzhiyun 	}
3557*4882a593Smuzhiyun 
3558*4882a593Smuzhiyun 	trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
3559*4882a593Smuzhiyun 
3560*4882a593Smuzhiyun 	/* initialize chip */
3561*4882a593Smuzhiyun 	switch (trident->device) {
3562*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_DX:
3563*4882a593Smuzhiyun 		err = snd_trident_4d_dx_init(trident);
3564*4882a593Smuzhiyun 		break;
3565*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_NX:
3566*4882a593Smuzhiyun 		err = snd_trident_4d_nx_init(trident);
3567*4882a593Smuzhiyun 		break;
3568*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_SI7018:
3569*4882a593Smuzhiyun 		err = snd_trident_sis_init(trident);
3570*4882a593Smuzhiyun 		break;
3571*4882a593Smuzhiyun 	default:
3572*4882a593Smuzhiyun 		snd_BUG();
3573*4882a593Smuzhiyun 		break;
3574*4882a593Smuzhiyun 	}
3575*4882a593Smuzhiyun 	if (err < 0) {
3576*4882a593Smuzhiyun 		snd_trident_free(trident);
3577*4882a593Smuzhiyun 		return err;
3578*4882a593Smuzhiyun 	}
3579*4882a593Smuzhiyun 
3580*4882a593Smuzhiyun 	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) {
3581*4882a593Smuzhiyun 		snd_trident_free(trident);
3582*4882a593Smuzhiyun 		return err;
3583*4882a593Smuzhiyun 	}
3584*4882a593Smuzhiyun 
3585*4882a593Smuzhiyun 	if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0)
3586*4882a593Smuzhiyun 		return err;
3587*4882a593Smuzhiyun 
3588*4882a593Smuzhiyun 	/* initialise synth voices */
3589*4882a593Smuzhiyun 	for (i = 0; i < 64; i++) {
3590*4882a593Smuzhiyun 		voice = &trident->synth.voices[i];
3591*4882a593Smuzhiyun 		voice->number = i;
3592*4882a593Smuzhiyun 		voice->trident = trident;
3593*4882a593Smuzhiyun 	}
3594*4882a593Smuzhiyun 	/* initialize pcm mixer entries */
3595*4882a593Smuzhiyun 	for (i = 0; i < 32; i++) {
3596*4882a593Smuzhiyun 		tmix = &trident->pcm_mixer[i];
3597*4882a593Smuzhiyun 		tmix->vol = T4D_DEFAULT_PCM_VOL;
3598*4882a593Smuzhiyun 		tmix->pan = T4D_DEFAULT_PCM_PAN;
3599*4882a593Smuzhiyun 		tmix->rvol = T4D_DEFAULT_PCM_RVOL;
3600*4882a593Smuzhiyun 		tmix->cvol = T4D_DEFAULT_PCM_CVOL;
3601*4882a593Smuzhiyun 	}
3602*4882a593Smuzhiyun 
3603*4882a593Smuzhiyun 	snd_trident_enable_eso(trident);
3604*4882a593Smuzhiyun 
3605*4882a593Smuzhiyun 	snd_trident_proc_init(trident);
3606*4882a593Smuzhiyun 	*rtrident = trident;
3607*4882a593Smuzhiyun 	return 0;
3608*4882a593Smuzhiyun }
3609*4882a593Smuzhiyun 
3610*4882a593Smuzhiyun /*---------------------------------------------------------------------------
3611*4882a593Smuzhiyun    snd_trident_free
3612*4882a593Smuzhiyun 
3613*4882a593Smuzhiyun    Description: This routine will free the device specific class for
3614*4882a593Smuzhiyun                 the 4DWave card.
3615*4882a593Smuzhiyun 
3616*4882a593Smuzhiyun    Parameters:  trident  - device specific private data for 4DWave card
3617*4882a593Smuzhiyun 
3618*4882a593Smuzhiyun    Returns:     None.
3619*4882a593Smuzhiyun 
3620*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
3621*4882a593Smuzhiyun 
snd_trident_free(struct snd_trident * trident)3622*4882a593Smuzhiyun static int snd_trident_free(struct snd_trident *trident)
3623*4882a593Smuzhiyun {
3624*4882a593Smuzhiyun 	snd_trident_free_gameport(trident);
3625*4882a593Smuzhiyun 	snd_trident_disable_eso(trident);
3626*4882a593Smuzhiyun 	// Disable S/PDIF out
3627*4882a593Smuzhiyun 	if (trident->device == TRIDENT_DEVICE_ID_NX)
3628*4882a593Smuzhiyun 		outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
3629*4882a593Smuzhiyun 	else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
3630*4882a593Smuzhiyun 		outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
3631*4882a593Smuzhiyun 	}
3632*4882a593Smuzhiyun 	if (trident->irq >= 0)
3633*4882a593Smuzhiyun 		free_irq(trident->irq, trident);
3634*4882a593Smuzhiyun 	if (trident->tlb.buffer.area) {
3635*4882a593Smuzhiyun 		outl(0, TRID_REG(trident, NX_TLBC));
3636*4882a593Smuzhiyun 		snd_util_memhdr_free(trident->tlb.memhdr);
3637*4882a593Smuzhiyun 		if (trident->tlb.silent_page.area)
3638*4882a593Smuzhiyun 			snd_dma_free_pages(&trident->tlb.silent_page);
3639*4882a593Smuzhiyun 		vfree(trident->tlb.shadow_entries);
3640*4882a593Smuzhiyun 		snd_dma_free_pages(&trident->tlb.buffer);
3641*4882a593Smuzhiyun 	}
3642*4882a593Smuzhiyun 	pci_release_regions(trident->pci);
3643*4882a593Smuzhiyun 	pci_disable_device(trident->pci);
3644*4882a593Smuzhiyun 	kfree(trident);
3645*4882a593Smuzhiyun 	return 0;
3646*4882a593Smuzhiyun }
3647*4882a593Smuzhiyun 
3648*4882a593Smuzhiyun /*---------------------------------------------------------------------------
3649*4882a593Smuzhiyun    snd_trident_interrupt
3650*4882a593Smuzhiyun 
3651*4882a593Smuzhiyun    Description: ISR for Trident 4DWave device
3652*4882a593Smuzhiyun 
3653*4882a593Smuzhiyun    Parameters:  trident  - device specific private data for 4DWave card
3654*4882a593Smuzhiyun 
3655*4882a593Smuzhiyun    Problems:    It seems that Trident chips generates interrupts more than
3656*4882a593Smuzhiyun                 one time in special cases. The spurious interrupts are
3657*4882a593Smuzhiyun                 detected via sample timer (T4D_STIMER) and computing
3658*4882a593Smuzhiyun                 corresponding delta value. The limits are detected with
3659*4882a593Smuzhiyun                 the method try & fail so it is possible that it won't
3660*4882a593Smuzhiyun                 work on all computers. [jaroslav]
3661*4882a593Smuzhiyun 
3662*4882a593Smuzhiyun    Returns:     None.
3663*4882a593Smuzhiyun 
3664*4882a593Smuzhiyun   ---------------------------------------------------------------------------*/
3665*4882a593Smuzhiyun 
snd_trident_interrupt(int irq,void * dev_id)3666*4882a593Smuzhiyun static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
3667*4882a593Smuzhiyun {
3668*4882a593Smuzhiyun 	struct snd_trident *trident = dev_id;
3669*4882a593Smuzhiyun 	unsigned int audio_int, chn_int, stimer, channel, mask, tmp;
3670*4882a593Smuzhiyun 	int delta;
3671*4882a593Smuzhiyun 	struct snd_trident_voice *voice;
3672*4882a593Smuzhiyun 
3673*4882a593Smuzhiyun 	audio_int = inl(TRID_REG(trident, T4D_MISCINT));
3674*4882a593Smuzhiyun 	if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0)
3675*4882a593Smuzhiyun 		return IRQ_NONE;
3676*4882a593Smuzhiyun 	if (audio_int & ADDRESS_IRQ) {
3677*4882a593Smuzhiyun 		// get interrupt status for all channels
3678*4882a593Smuzhiyun 		spin_lock(&trident->reg_lock);
3679*4882a593Smuzhiyun 		stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
3680*4882a593Smuzhiyun 		chn_int = inl(TRID_REG(trident, T4D_AINT_A));
3681*4882a593Smuzhiyun 		if (chn_int == 0)
3682*4882a593Smuzhiyun 			goto __skip1;
3683*4882a593Smuzhiyun 		outl(chn_int, TRID_REG(trident, T4D_AINT_A));	/* ack */
3684*4882a593Smuzhiyun 	      __skip1:
3685*4882a593Smuzhiyun 		chn_int = inl(TRID_REG(trident, T4D_AINT_B));
3686*4882a593Smuzhiyun 		if (chn_int == 0)
3687*4882a593Smuzhiyun 			goto __skip2;
3688*4882a593Smuzhiyun 		for (channel = 63; channel >= 32; channel--) {
3689*4882a593Smuzhiyun 			mask = 1 << (channel&0x1f);
3690*4882a593Smuzhiyun 			if ((chn_int & mask) == 0)
3691*4882a593Smuzhiyun 				continue;
3692*4882a593Smuzhiyun 			voice = &trident->synth.voices[channel];
3693*4882a593Smuzhiyun 			if (!voice->pcm || voice->substream == NULL) {
3694*4882a593Smuzhiyun 				outl(mask, TRID_REG(trident, T4D_STOP_B));
3695*4882a593Smuzhiyun 				continue;
3696*4882a593Smuzhiyun 			}
3697*4882a593Smuzhiyun 			delta = (int)stimer - (int)voice->stimer;
3698*4882a593Smuzhiyun 			if (delta < 0)
3699*4882a593Smuzhiyun 				delta = -delta;
3700*4882a593Smuzhiyun 			if ((unsigned int)delta < voice->spurious_threshold) {
3701*4882a593Smuzhiyun 				/* do some statistics here */
3702*4882a593Smuzhiyun 				trident->spurious_irq_count++;
3703*4882a593Smuzhiyun 				if (trident->spurious_irq_max_delta < (unsigned int)delta)
3704*4882a593Smuzhiyun 					trident->spurious_irq_max_delta = delta;
3705*4882a593Smuzhiyun 				continue;
3706*4882a593Smuzhiyun 			}
3707*4882a593Smuzhiyun 			voice->stimer = stimer;
3708*4882a593Smuzhiyun 			if (voice->isync) {
3709*4882a593Smuzhiyun 				if (!voice->isync3) {
3710*4882a593Smuzhiyun 					tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL));
3711*4882a593Smuzhiyun 					if (trident->bDMAStart & 0x40)
3712*4882a593Smuzhiyun 						tmp >>= 1;
3713*4882a593Smuzhiyun 					if (tmp > 0)
3714*4882a593Smuzhiyun 						tmp = voice->isync_max - tmp;
3715*4882a593Smuzhiyun 				} else {
3716*4882a593Smuzhiyun 					tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
3717*4882a593Smuzhiyun 				}
3718*4882a593Smuzhiyun 				if (tmp < voice->isync_mark) {
3719*4882a593Smuzhiyun 					if (tmp > 0x10)
3720*4882a593Smuzhiyun 						tmp = voice->isync_ESO - 7;
3721*4882a593Smuzhiyun 					else
3722*4882a593Smuzhiyun 						tmp = voice->isync_ESO + 2;
3723*4882a593Smuzhiyun 					/* update ESO for IRQ voice to preserve sync */
3724*4882a593Smuzhiyun 					snd_trident_stop_voice(trident, voice->number);
3725*4882a593Smuzhiyun 					snd_trident_write_eso_reg(trident, voice, tmp);
3726*4882a593Smuzhiyun 					snd_trident_start_voice(trident, voice->number);
3727*4882a593Smuzhiyun 				}
3728*4882a593Smuzhiyun 			} else if (voice->isync2) {
3729*4882a593Smuzhiyun 				voice->isync2 = 0;
3730*4882a593Smuzhiyun 				/* write original ESO and update CSO for IRQ voice to preserve sync */
3731*4882a593Smuzhiyun 				snd_trident_stop_voice(trident, voice->number);
3732*4882a593Smuzhiyun 				snd_trident_write_cso_reg(trident, voice, voice->isync_mark);
3733*4882a593Smuzhiyun 				snd_trident_write_eso_reg(trident, voice, voice->ESO);
3734*4882a593Smuzhiyun 				snd_trident_start_voice(trident, voice->number);
3735*4882a593Smuzhiyun 			}
3736*4882a593Smuzhiyun #if 0
3737*4882a593Smuzhiyun 			if (voice->extra) {
3738*4882a593Smuzhiyun 				/* update CSO for extra voice to preserve sync */
3739*4882a593Smuzhiyun 				snd_trident_stop_voice(trident, voice->extra->number);
3740*4882a593Smuzhiyun 				snd_trident_write_cso_reg(trident, voice->extra, 0);
3741*4882a593Smuzhiyun 				snd_trident_start_voice(trident, voice->extra->number);
3742*4882a593Smuzhiyun 			}
3743*4882a593Smuzhiyun #endif
3744*4882a593Smuzhiyun 			spin_unlock(&trident->reg_lock);
3745*4882a593Smuzhiyun 			snd_pcm_period_elapsed(voice->substream);
3746*4882a593Smuzhiyun 			spin_lock(&trident->reg_lock);
3747*4882a593Smuzhiyun 		}
3748*4882a593Smuzhiyun 		outl(chn_int, TRID_REG(trident, T4D_AINT_B));	/* ack */
3749*4882a593Smuzhiyun 	      __skip2:
3750*4882a593Smuzhiyun 		spin_unlock(&trident->reg_lock);
3751*4882a593Smuzhiyun 	}
3752*4882a593Smuzhiyun 	if (audio_int & MPU401_IRQ) {
3753*4882a593Smuzhiyun 		if (trident->rmidi) {
3754*4882a593Smuzhiyun 			snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data);
3755*4882a593Smuzhiyun 		} else {
3756*4882a593Smuzhiyun 			inb(TRID_REG(trident, T4D_MPUR0));
3757*4882a593Smuzhiyun 		}
3758*4882a593Smuzhiyun 	}
3759*4882a593Smuzhiyun 	// outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT));
3760*4882a593Smuzhiyun 	return IRQ_HANDLED;
3761*4882a593Smuzhiyun }
3762*4882a593Smuzhiyun 
snd_trident_alloc_voice(struct snd_trident * trident,int type,int client,int port)3763*4882a593Smuzhiyun struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
3764*4882a593Smuzhiyun {
3765*4882a593Smuzhiyun 	struct snd_trident_voice *pvoice;
3766*4882a593Smuzhiyun 	unsigned long flags;
3767*4882a593Smuzhiyun 	int idx;
3768*4882a593Smuzhiyun 
3769*4882a593Smuzhiyun 	spin_lock_irqsave(&trident->voice_alloc, flags);
3770*4882a593Smuzhiyun 	if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) {
3771*4882a593Smuzhiyun 		idx = snd_trident_allocate_pcm_channel(trident);
3772*4882a593Smuzhiyun 		if(idx < 0) {
3773*4882a593Smuzhiyun 			spin_unlock_irqrestore(&trident->voice_alloc, flags);
3774*4882a593Smuzhiyun 			return NULL;
3775*4882a593Smuzhiyun 		}
3776*4882a593Smuzhiyun 		pvoice = &trident->synth.voices[idx];
3777*4882a593Smuzhiyun 		pvoice->use = 1;
3778*4882a593Smuzhiyun 		pvoice->pcm = 1;
3779*4882a593Smuzhiyun 		pvoice->capture = 0;
3780*4882a593Smuzhiyun 		pvoice->spdif = 0;
3781*4882a593Smuzhiyun 		pvoice->memblk = NULL;
3782*4882a593Smuzhiyun 		pvoice->substream = NULL;
3783*4882a593Smuzhiyun 		spin_unlock_irqrestore(&trident->voice_alloc, flags);
3784*4882a593Smuzhiyun 		return pvoice;
3785*4882a593Smuzhiyun 	}
3786*4882a593Smuzhiyun 	if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) {
3787*4882a593Smuzhiyun 		idx = snd_trident_allocate_synth_channel(trident);
3788*4882a593Smuzhiyun 		if(idx < 0) {
3789*4882a593Smuzhiyun 			spin_unlock_irqrestore(&trident->voice_alloc, flags);
3790*4882a593Smuzhiyun 			return NULL;
3791*4882a593Smuzhiyun 		}
3792*4882a593Smuzhiyun 		pvoice = &trident->synth.voices[idx];
3793*4882a593Smuzhiyun 		pvoice->use = 1;
3794*4882a593Smuzhiyun 		pvoice->synth = 1;
3795*4882a593Smuzhiyun 		pvoice->client = client;
3796*4882a593Smuzhiyun 		pvoice->port = port;
3797*4882a593Smuzhiyun 		pvoice->memblk = NULL;
3798*4882a593Smuzhiyun 		spin_unlock_irqrestore(&trident->voice_alloc, flags);
3799*4882a593Smuzhiyun 		return pvoice;
3800*4882a593Smuzhiyun 	}
3801*4882a593Smuzhiyun 	if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) {
3802*4882a593Smuzhiyun 	}
3803*4882a593Smuzhiyun 	spin_unlock_irqrestore(&trident->voice_alloc, flags);
3804*4882a593Smuzhiyun 	return NULL;
3805*4882a593Smuzhiyun }
3806*4882a593Smuzhiyun 
3807*4882a593Smuzhiyun EXPORT_SYMBOL(snd_trident_alloc_voice);
3808*4882a593Smuzhiyun 
snd_trident_free_voice(struct snd_trident * trident,struct snd_trident_voice * voice)3809*4882a593Smuzhiyun void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice)
3810*4882a593Smuzhiyun {
3811*4882a593Smuzhiyun 	unsigned long flags;
3812*4882a593Smuzhiyun 	void (*private_free)(struct snd_trident_voice *);
3813*4882a593Smuzhiyun 
3814*4882a593Smuzhiyun 	if (voice == NULL || !voice->use)
3815*4882a593Smuzhiyun 		return;
3816*4882a593Smuzhiyun 	snd_trident_clear_voices(trident, voice->number, voice->number);
3817*4882a593Smuzhiyun 	spin_lock_irqsave(&trident->voice_alloc, flags);
3818*4882a593Smuzhiyun 	private_free = voice->private_free;
3819*4882a593Smuzhiyun 	voice->private_free = NULL;
3820*4882a593Smuzhiyun 	voice->private_data = NULL;
3821*4882a593Smuzhiyun 	if (voice->pcm)
3822*4882a593Smuzhiyun 		snd_trident_free_pcm_channel(trident, voice->number);
3823*4882a593Smuzhiyun 	if (voice->synth)
3824*4882a593Smuzhiyun 		snd_trident_free_synth_channel(trident, voice->number);
3825*4882a593Smuzhiyun 	voice->use = voice->pcm = voice->synth = voice->midi = 0;
3826*4882a593Smuzhiyun 	voice->capture = voice->spdif = 0;
3827*4882a593Smuzhiyun 	voice->sample_ops = NULL;
3828*4882a593Smuzhiyun 	voice->substream = NULL;
3829*4882a593Smuzhiyun 	voice->extra = NULL;
3830*4882a593Smuzhiyun 	spin_unlock_irqrestore(&trident->voice_alloc, flags);
3831*4882a593Smuzhiyun 	if (private_free)
3832*4882a593Smuzhiyun 		private_free(voice);
3833*4882a593Smuzhiyun }
3834*4882a593Smuzhiyun 
3835*4882a593Smuzhiyun EXPORT_SYMBOL(snd_trident_free_voice);
3836*4882a593Smuzhiyun 
snd_trident_clear_voices(struct snd_trident * trident,unsigned short v_min,unsigned short v_max)3837*4882a593Smuzhiyun static void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max)
3838*4882a593Smuzhiyun {
3839*4882a593Smuzhiyun 	unsigned int i, val, mask[2] = { 0, 0 };
3840*4882a593Smuzhiyun 
3841*4882a593Smuzhiyun 	if (snd_BUG_ON(v_min > 63 || v_max > 63))
3842*4882a593Smuzhiyun 		return;
3843*4882a593Smuzhiyun 	for (i = v_min; i <= v_max; i++)
3844*4882a593Smuzhiyun 		mask[i >> 5] |= 1 << (i & 0x1f);
3845*4882a593Smuzhiyun 	if (mask[0]) {
3846*4882a593Smuzhiyun 		outl(mask[0], TRID_REG(trident, T4D_STOP_A));
3847*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, T4D_AINTEN_A));
3848*4882a593Smuzhiyun 		outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A));
3849*4882a593Smuzhiyun 	}
3850*4882a593Smuzhiyun 	if (mask[1]) {
3851*4882a593Smuzhiyun 		outl(mask[1], TRID_REG(trident, T4D_STOP_B));
3852*4882a593Smuzhiyun 		val = inl(TRID_REG(trident, T4D_AINTEN_B));
3853*4882a593Smuzhiyun 		outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B));
3854*4882a593Smuzhiyun 	}
3855*4882a593Smuzhiyun }
3856*4882a593Smuzhiyun 
3857*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
snd_trident_suspend(struct device * dev)3858*4882a593Smuzhiyun static int snd_trident_suspend(struct device *dev)
3859*4882a593Smuzhiyun {
3860*4882a593Smuzhiyun 	struct snd_card *card = dev_get_drvdata(dev);
3861*4882a593Smuzhiyun 	struct snd_trident *trident = card->private_data;
3862*4882a593Smuzhiyun 
3863*4882a593Smuzhiyun 	trident->in_suspend = 1;
3864*4882a593Smuzhiyun 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
3865*4882a593Smuzhiyun 	snd_ac97_suspend(trident->ac97);
3866*4882a593Smuzhiyun 	snd_ac97_suspend(trident->ac97_sec);
3867*4882a593Smuzhiyun 	return 0;
3868*4882a593Smuzhiyun }
3869*4882a593Smuzhiyun 
snd_trident_resume(struct device * dev)3870*4882a593Smuzhiyun static int snd_trident_resume(struct device *dev)
3871*4882a593Smuzhiyun {
3872*4882a593Smuzhiyun 	struct snd_card *card = dev_get_drvdata(dev);
3873*4882a593Smuzhiyun 	struct snd_trident *trident = card->private_data;
3874*4882a593Smuzhiyun 
3875*4882a593Smuzhiyun 	switch (trident->device) {
3876*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_DX:
3877*4882a593Smuzhiyun 		snd_trident_4d_dx_init(trident);
3878*4882a593Smuzhiyun 		break;
3879*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_NX:
3880*4882a593Smuzhiyun 		snd_trident_4d_nx_init(trident);
3881*4882a593Smuzhiyun 		break;
3882*4882a593Smuzhiyun 	case TRIDENT_DEVICE_ID_SI7018:
3883*4882a593Smuzhiyun 		snd_trident_sis_init(trident);
3884*4882a593Smuzhiyun 		break;
3885*4882a593Smuzhiyun 	}
3886*4882a593Smuzhiyun 
3887*4882a593Smuzhiyun 	snd_ac97_resume(trident->ac97);
3888*4882a593Smuzhiyun 	snd_ac97_resume(trident->ac97_sec);
3889*4882a593Smuzhiyun 
3890*4882a593Smuzhiyun 	/* restore some registers */
3891*4882a593Smuzhiyun 	outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
3892*4882a593Smuzhiyun 
3893*4882a593Smuzhiyun 	snd_trident_enable_eso(trident);
3894*4882a593Smuzhiyun 
3895*4882a593Smuzhiyun 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
3896*4882a593Smuzhiyun 	trident->in_suspend = 0;
3897*4882a593Smuzhiyun 	return 0;
3898*4882a593Smuzhiyun }
3899*4882a593Smuzhiyun 
3900*4882a593Smuzhiyun SIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume);
3901*4882a593Smuzhiyun #endif /* CONFIG_PM_SLEEP */
3902