xref: /openbmc/linux/sound/pci/emu10k1/emumixer.c (revision 9f4bd5dde81b5cb94e4f52f2f05825aa0422f1ff)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
31da177e4SLinus Torvalds  *                   Takashi Iwai <tiwai@suse.de>
41da177e4SLinus Torvalds  *                   Creative Labs, Inc.
51da177e4SLinus Torvalds  *  Routines for control of EMU10K1 chips / mixer routines
61da177e4SLinus Torvalds  *  Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
71da177e4SLinus Torvalds  *
8*9f4bd5ddSJames Courtier-Dutton  *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
9*9f4bd5ddSJames Courtier-Dutton  *  	Added EMU 1010 support.
10*9f4bd5ddSJames Courtier-Dutton  *
111da177e4SLinus Torvalds  *  BUGS:
121da177e4SLinus Torvalds  *    --
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  TODO:
151da177e4SLinus Torvalds  *    --
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
181da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
191da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
201da177e4SLinus Torvalds  *   (at your option) any later version.
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
231da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
241da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
251da177e4SLinus Torvalds  *   GNU General Public License for more details.
261da177e4SLinus Torvalds  *
271da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
281da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
291da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  */
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds #include <sound/driver.h>
341da177e4SLinus Torvalds #include <linux/time.h>
351da177e4SLinus Torvalds #include <linux/init.h>
361da177e4SLinus Torvalds #include <sound/core.h>
371da177e4SLinus Torvalds #include <sound/emu10k1.h>
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #define AC97_ID_STAC9758	0x83847658
401da177e4SLinus Torvalds 
41eb4698f3STakashi Iwai static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
441da177e4SLinus Torvalds 	uinfo->count = 1;
451da177e4SLinus Torvalds 	return 0;
461da177e4SLinus Torvalds }
471da177e4SLinus Torvalds 
48eb4698f3STakashi Iwai static int snd_emu10k1_spdif_get(struct snd_kcontrol *kcontrol,
49eb4698f3STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
501da177e4SLinus Torvalds {
51eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
521da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
531da177e4SLinus Torvalds 	unsigned long flags;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
561da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
571da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
581da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
591da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
601da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
611da177e4SLinus Torvalds 	return 0;
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
64eb4698f3STakashi Iwai static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
65eb4698f3STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
661da177e4SLinus Torvalds {
671da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = 0xff;
681da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = 0xff;
691da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = 0xff;
701da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = 0xff;
711da177e4SLinus Torvalds 	return 0;
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
74*9f4bd5ddSJames Courtier-Dutton static char *emu1010_src_texts[] = {
75*9f4bd5ddSJames Courtier-Dutton 	"Silence",
76*9f4bd5ddSJames Courtier-Dutton 	"Dock Mic A",
77*9f4bd5ddSJames Courtier-Dutton 	"Dock Mic B",
78*9f4bd5ddSJames Courtier-Dutton 	"Dock ADC1 Left",
79*9f4bd5ddSJames Courtier-Dutton 	"Dock ADC1 Right",
80*9f4bd5ddSJames Courtier-Dutton 	"Dock ADC2 Left",
81*9f4bd5ddSJames Courtier-Dutton 	"Dock ADC2 Right",
82*9f4bd5ddSJames Courtier-Dutton 	"Dock ADC3 Left",
83*9f4bd5ddSJames Courtier-Dutton 	"Dock ADC3 Right",
84*9f4bd5ddSJames Courtier-Dutton 	"0202 ADC Left",
85*9f4bd5ddSJames Courtier-Dutton 	"0202 ADC Right",
86*9f4bd5ddSJames Courtier-Dutton 	"0202 SPDIF Left",
87*9f4bd5ddSJames Courtier-Dutton 	"0202 SPDIF Right",
88*9f4bd5ddSJames Courtier-Dutton 	"ADAT 0",
89*9f4bd5ddSJames Courtier-Dutton 	"ADAT 1",
90*9f4bd5ddSJames Courtier-Dutton 	"ADAT 2",
91*9f4bd5ddSJames Courtier-Dutton 	"ADAT 3",
92*9f4bd5ddSJames Courtier-Dutton 	"ADAT 4",
93*9f4bd5ddSJames Courtier-Dutton 	"ADAT 5",
94*9f4bd5ddSJames Courtier-Dutton 	"ADAT 6",
95*9f4bd5ddSJames Courtier-Dutton 	"ADAT 7",
96*9f4bd5ddSJames Courtier-Dutton 	"DSP 0",
97*9f4bd5ddSJames Courtier-Dutton 	"DSP 1",
98*9f4bd5ddSJames Courtier-Dutton 	"DSP 2",
99*9f4bd5ddSJames Courtier-Dutton 	"DSP 3",
100*9f4bd5ddSJames Courtier-Dutton 	"DSP 4",
101*9f4bd5ddSJames Courtier-Dutton 	"DSP 5",
102*9f4bd5ddSJames Courtier-Dutton 	"DSP 6",
103*9f4bd5ddSJames Courtier-Dutton 	"DSP 7",
104*9f4bd5ddSJames Courtier-Dutton 	"DSP 8",
105*9f4bd5ddSJames Courtier-Dutton 	"DSP 9",
106*9f4bd5ddSJames Courtier-Dutton 	"DSP 10",
107*9f4bd5ddSJames Courtier-Dutton 	"DSP 11",
108*9f4bd5ddSJames Courtier-Dutton 	"DSP 12",
109*9f4bd5ddSJames Courtier-Dutton 	"DSP 13",
110*9f4bd5ddSJames Courtier-Dutton 	"DSP 14",
111*9f4bd5ddSJames Courtier-Dutton 	"DSP 15",
112*9f4bd5ddSJames Courtier-Dutton 	"DSP 16",
113*9f4bd5ddSJames Courtier-Dutton 	"DSP 17",
114*9f4bd5ddSJames Courtier-Dutton 	"DSP 18",
115*9f4bd5ddSJames Courtier-Dutton 	"DSP 19",
116*9f4bd5ddSJames Courtier-Dutton 	"DSP 20",
117*9f4bd5ddSJames Courtier-Dutton 	"DSP 21",
118*9f4bd5ddSJames Courtier-Dutton 	"DSP 22",
119*9f4bd5ddSJames Courtier-Dutton 	"DSP 23",
120*9f4bd5ddSJames Courtier-Dutton 	"DSP 24",
121*9f4bd5ddSJames Courtier-Dutton 	"DSP 25",
122*9f4bd5ddSJames Courtier-Dutton 	"DSP 26",
123*9f4bd5ddSJames Courtier-Dutton 	"DSP 27",
124*9f4bd5ddSJames Courtier-Dutton 	"DSP 28",
125*9f4bd5ddSJames Courtier-Dutton 	"DSP 29",
126*9f4bd5ddSJames Courtier-Dutton 	"DSP 30",
127*9f4bd5ddSJames Courtier-Dutton 	"DSP 31",
128*9f4bd5ddSJames Courtier-Dutton };
129*9f4bd5ddSJames Courtier-Dutton 
130*9f4bd5ddSJames Courtier-Dutton static unsigned int emu1010_src_regs[] = {
131*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_SILENCE,/* 0 */
132*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_MIC_A1, /* 1 */
133*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_MIC_B1, /* 2 */
134*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_ADC1_LEFT1, /* 3 */
135*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_ADC1_RIGHT1, /* 4 */
136*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_ADC2_LEFT1, /* 5 */
137*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_ADC2_RIGHT1, /* 6 */
138*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_ADC3_LEFT1, /* 7 */
139*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_DOCK_ADC3_RIGHT1, /* 8 */
140*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HAMOA_ADC_LEFT1, /* 9 */
141*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HAMOA_ADC_RIGHT1, /* 10 */
142*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_SPDIF_LEFT1, /* 11 */
143*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_SPDIF_RIGHT1, /* 12 */
144*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT, /* 13 */
145*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT+1, /* 14 */
146*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT+2, /* 15 */
147*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT+3, /* 16 */
148*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT+4, /* 17 */
149*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT+5, /* 18 */
150*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT+6, /* 19 */
151*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_HANA_ADAT+7, /* 20 */
152*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A, /* 21 */
153*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+1, /* 22 */
154*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+2, /* 23 */
155*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+3, /* 24 */
156*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+4, /* 25 */
157*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+5, /* 26 */
158*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+6, /* 27 */
159*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+7, /* 28 */
160*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+8, /* 29 */
161*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+9, /* 30 */
162*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+0xa, /* 31 */
163*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+0xb, /* 32 */
164*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+0xc, /* 33 */
165*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+0xd, /* 34 */
166*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+0xe, /* 35 */
167*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32A+0xf, /* 36 */
168*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B, /* 37 */
169*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+1, /* 38 */
170*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+2, /* 39 */
171*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+3, /* 40 */
172*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+4, /* 41 */
173*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+5, /* 42 */
174*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+6, /* 43 */
175*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+7, /* 44 */
176*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+8, /* 45 */
177*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+9, /* 46 */
178*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+0xa, /* 47 */
179*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+0xb, /* 48 */
180*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+0xc, /* 49 */
181*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+0xd, /* 50 */
182*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+0xe, /* 51 */
183*9f4bd5ddSJames Courtier-Dutton 	EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
184*9f4bd5ddSJames Courtier-Dutton };
185*9f4bd5ddSJames Courtier-Dutton 
186*9f4bd5ddSJames Courtier-Dutton static unsigned int emu1010_output_dst[] = {
187*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
188*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
189*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC2_LEFT1, /* 2 */
190*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC2_RIGHT1, /* 3 */
191*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC3_LEFT1, /* 4 */
192*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC3_RIGHT1, /* 5 */
193*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC4_LEFT1, /* 6 */
194*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_DAC4_RIGHT1, /* 7 */
195*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_PHONES_LEFT1, /* 8 */
196*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_PHONES_RIGHT1, /* 9 */
197*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_SPDIF_LEFT1, /* 10 */
198*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_DOCK_SPDIF_RIGHT1, /* 11 */
199*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_SPDIF_LEFT1, /* 12 */
200*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_SPDIF_RIGHT1, /* 13 */
201*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HAMOA_DAC_LEFT1, /* 14 */
202*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HAMOA_DAC_RIGHT1, /* 15 */
203*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT, /* 16 */
204*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT+1, /* 17 */
205*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT+2, /* 18 */
206*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT+3, /* 19 */
207*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT+4, /* 20 */
208*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT+5, /* 21 */
209*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT+6, /* 22 */
210*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_HANA_ADAT+7, /* 23 */
211*9f4bd5ddSJames Courtier-Dutton };
212*9f4bd5ddSJames Courtier-Dutton 
213*9f4bd5ddSJames Courtier-Dutton static unsigned int emu1010_input_dst[] = {
214*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_0,
215*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_1,
216*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_2,
217*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_3,
218*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_4,
219*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_5,
220*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_6,
221*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_7,
222*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_8,
223*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_9,
224*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_A,
225*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_B,
226*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_C,
227*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_D,
228*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_E,
229*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE2_EMU32_F,
230*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE_I2S0_LEFT,
231*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE_I2S0_RIGHT,
232*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE_I2S1_LEFT,
233*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE_I2S1_RIGHT,
234*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE_I2S2_LEFT,
235*9f4bd5ddSJames Courtier-Dutton 	EMU_DST_ALICE_I2S2_RIGHT,
236*9f4bd5ddSJames Courtier-Dutton };
237*9f4bd5ddSJames Courtier-Dutton 
238*9f4bd5ddSJames Courtier-Dutton static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
239*9f4bd5ddSJames Courtier-Dutton {
240*9f4bd5ddSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
241*9f4bd5ddSJames Courtier-Dutton 	uinfo->count = 1;
242*9f4bd5ddSJames Courtier-Dutton 	uinfo->value.enumerated.items = 53;
243*9f4bd5ddSJames Courtier-Dutton 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
244*9f4bd5ddSJames Courtier-Dutton 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
245*9f4bd5ddSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]);
246*9f4bd5ddSJames Courtier-Dutton 	return 0;
247*9f4bd5ddSJames Courtier-Dutton }
248*9f4bd5ddSJames Courtier-Dutton 
249*9f4bd5ddSJames Courtier-Dutton static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
250*9f4bd5ddSJames Courtier-Dutton                                  struct snd_ctl_elem_value *ucontrol)
251*9f4bd5ddSJames Courtier-Dutton {
252*9f4bd5ddSJames Courtier-Dutton 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
253*9f4bd5ddSJames Courtier-Dutton 	int channel;
254*9f4bd5ddSJames Courtier-Dutton 
255*9f4bd5ddSJames Courtier-Dutton 	channel = (kcontrol->private_value) & 0xff;
256*9f4bd5ddSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
257*9f4bd5ddSJames Courtier-Dutton 	return 0;
258*9f4bd5ddSJames Courtier-Dutton }
259*9f4bd5ddSJames Courtier-Dutton 
260*9f4bd5ddSJames Courtier-Dutton static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
261*9f4bd5ddSJames Courtier-Dutton                                  struct snd_ctl_elem_value *ucontrol)
262*9f4bd5ddSJames Courtier-Dutton {
263*9f4bd5ddSJames Courtier-Dutton 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
264*9f4bd5ddSJames Courtier-Dutton 	int change = 0;
265*9f4bd5ddSJames Courtier-Dutton 	unsigned int val;
266*9f4bd5ddSJames Courtier-Dutton 	int channel;
267*9f4bd5ddSJames Courtier-Dutton 
268*9f4bd5ddSJames Courtier-Dutton 	channel = (kcontrol->private_value) & 0xff;
269*9f4bd5ddSJames Courtier-Dutton 	if (emu->emu1010.output_source[channel] != ucontrol->value.enumerated.item[0]) {
270*9f4bd5ddSJames Courtier-Dutton 		val = emu->emu1010.output_source[channel] = ucontrol->value.enumerated.item[0];
271*9f4bd5ddSJames Courtier-Dutton 		change = 1;
272*9f4bd5ddSJames Courtier-Dutton 		snd_emu1010_fpga_link_dst_src_write(emu,
273*9f4bd5ddSJames Courtier-Dutton 			emu1010_output_dst[channel], emu1010_src_regs[val]);
274*9f4bd5ddSJames Courtier-Dutton 	}
275*9f4bd5ddSJames Courtier-Dutton 	return change;
276*9f4bd5ddSJames Courtier-Dutton }
277*9f4bd5ddSJames Courtier-Dutton 
278*9f4bd5ddSJames Courtier-Dutton static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
279*9f4bd5ddSJames Courtier-Dutton                                  struct snd_ctl_elem_value *ucontrol)
280*9f4bd5ddSJames Courtier-Dutton {
281*9f4bd5ddSJames Courtier-Dutton 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
282*9f4bd5ddSJames Courtier-Dutton 	int channel;
283*9f4bd5ddSJames Courtier-Dutton 
284*9f4bd5ddSJames Courtier-Dutton 	channel = (kcontrol->private_value) & 0xff;
285*9f4bd5ddSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel];
286*9f4bd5ddSJames Courtier-Dutton 	return 0;
287*9f4bd5ddSJames Courtier-Dutton }
288*9f4bd5ddSJames Courtier-Dutton 
289*9f4bd5ddSJames Courtier-Dutton static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
290*9f4bd5ddSJames Courtier-Dutton                                  struct snd_ctl_elem_value *ucontrol)
291*9f4bd5ddSJames Courtier-Dutton {
292*9f4bd5ddSJames Courtier-Dutton 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
293*9f4bd5ddSJames Courtier-Dutton 	int change = 0;
294*9f4bd5ddSJames Courtier-Dutton 	unsigned int val;
295*9f4bd5ddSJames Courtier-Dutton 	int channel;
296*9f4bd5ddSJames Courtier-Dutton 
297*9f4bd5ddSJames Courtier-Dutton 	channel = (kcontrol->private_value) & 0xff;
298*9f4bd5ddSJames Courtier-Dutton 	if (emu->emu1010.input_source[channel] != ucontrol->value.enumerated.item[0]) {
299*9f4bd5ddSJames Courtier-Dutton 		val = emu->emu1010.input_source[channel] = ucontrol->value.enumerated.item[0];
300*9f4bd5ddSJames Courtier-Dutton 		change = 1;
301*9f4bd5ddSJames Courtier-Dutton 		snd_emu1010_fpga_link_dst_src_write(emu,
302*9f4bd5ddSJames Courtier-Dutton 			emu1010_input_dst[channel], emu1010_src_regs[val]);
303*9f4bd5ddSJames Courtier-Dutton 	}
304*9f4bd5ddSJames Courtier-Dutton 	return change;
305*9f4bd5ddSJames Courtier-Dutton }
306*9f4bd5ddSJames Courtier-Dutton 
307*9f4bd5ddSJames Courtier-Dutton #define EMU1010_SOURCE_OUTPUT(xname,chid) \
308*9f4bd5ddSJames Courtier-Dutton {								\
309*9f4bd5ddSJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
310*9f4bd5ddSJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
311*9f4bd5ddSJames Courtier-Dutton 	.info =  snd_emu1010_input_output_source_info,		\
312*9f4bd5ddSJames Courtier-Dutton 	.get =   snd_emu1010_output_source_get,			\
313*9f4bd5ddSJames Courtier-Dutton 	.put =   snd_emu1010_output_source_put,			\
314*9f4bd5ddSJames Courtier-Dutton 	.private_value = chid					\
315*9f4bd5ddSJames Courtier-Dutton }
316*9f4bd5ddSJames Courtier-Dutton 
317*9f4bd5ddSJames Courtier-Dutton static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
318*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC1 Left", 0),
319*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC1 Right", 1),
320*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC2 Left", 2),
321*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC2 Right", 3),
322*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC3 Left", 4),
323*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC3 Right", 5),
324*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC4 Left", 6),
325*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock DAC4 Right", 7),
326*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock Phones Left", 8),
327*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock Phones Right", 9),
328*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock SPDIF Left", 0xa),
329*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback Dock SPDIF Right", 0xb),
330*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 SPDIF Left", 0xc),
331*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 SPDIF Right", 0xd),
332*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 0202 DAC Left", 0xe),
333*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 0202 DAC Right", 0xf),
334*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 0", 0x10),
335*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 1", 0x11),
336*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 2", 0x12),
337*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 3", 0x13),
338*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 4", 0x14),
339*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 5", 0x15),
340*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 6", 0x16),
341*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_OUTPUT("Playback 1010 ADAT 7", 0x17),
342*9f4bd5ddSJames Courtier-Dutton };
343*9f4bd5ddSJames Courtier-Dutton 
344*9f4bd5ddSJames Courtier-Dutton #define EMU1010_SOURCE_INPUT(xname,chid) \
345*9f4bd5ddSJames Courtier-Dutton {								\
346*9f4bd5ddSJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
347*9f4bd5ddSJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
348*9f4bd5ddSJames Courtier-Dutton 	.info =  snd_emu1010_input_output_source_info,		\
349*9f4bd5ddSJames Courtier-Dutton 	.get =   snd_emu1010_input_source_get,			\
350*9f4bd5ddSJames Courtier-Dutton 	.put =   snd_emu1010_input_source_put,			\
351*9f4bd5ddSJames Courtier-Dutton 	.private_value = chid					\
352*9f4bd5ddSJames Courtier-Dutton }
353*9f4bd5ddSJames Courtier-Dutton 
354*9f4bd5ddSJames Courtier-Dutton static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = {
355*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 0 CAPTURE ENUM", 0),
356*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 1 CAPTURE ENUM", 1),
357*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 2 CAPTURE ENUM", 2),
358*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 3 CAPTURE ENUM", 3),
359*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 4 CAPTURE ENUM", 4),
360*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 5 CAPTURE ENUM", 5),
361*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 6 CAPTURE ENUM", 6),
362*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 7 CAPTURE ENUM", 7),
363*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 8 CAPTURE ENUM", 8),
364*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 9 CAPTURE ENUM", 9),
365*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP A CAPTURE ENUM", 0xa),
366*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP B CAPTURE ENUM", 0xb),
367*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP C CAPTURE ENUM", 0xc),
368*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP D CAPTURE ENUM", 0xd),
369*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP E CAPTURE ENUM", 0xe),
370*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP F CAPTURE ENUM", 0xf),
371*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 10 CAPTURE ENUM", 0x10),
372*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 11 CAPTURE ENUM", 0x11),
373*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 12 CAPTURE ENUM", 0x12),
374*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 13 CAPTURE ENUM", 0x13),
375*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 14 CAPTURE ENUM", 0x14),
376*9f4bd5ddSJames Courtier-Dutton 	EMU1010_SOURCE_INPUT("DSP 15 CAPTURE ENUM", 0x15),
377*9f4bd5ddSJames Courtier-Dutton };
378*9f4bd5ddSJames Courtier-Dutton 
3790af68e5eSTakashi Iwai #if 0
380eb4698f3STakashi Iwai static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
3811da177e4SLinus Torvalds {
3821da177e4SLinus Torvalds 	static char *texts[] = {"44100", "48000", "96000"};
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
3851da177e4SLinus Torvalds 	uinfo->count = 1;
3861da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 3;
3871da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
3881da177e4SLinus Torvalds 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
3891da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
3901da177e4SLinus Torvalds 	return 0;
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds 
393eb4698f3STakashi Iwai static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
394eb4698f3STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3951da177e4SLinus Torvalds {
396eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
3971da177e4SLinus Torvalds 	unsigned int tmp;
3981da177e4SLinus Torvalds 	unsigned long flags;
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
4021da177e4SLinus Torvalds 	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
4031da177e4SLinus Torvalds 	switch (tmp & A_SPDIF_RATE_MASK) {
4041da177e4SLinus Torvalds 	case A_SPDIF_44100:
4051da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 0;
4061da177e4SLinus Torvalds 		break;
4071da177e4SLinus Torvalds 	case A_SPDIF_48000:
4081da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
4091da177e4SLinus Torvalds 		break;
4101da177e4SLinus Torvalds 	case A_SPDIF_96000:
4111da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 2;
4121da177e4SLinus Torvalds 		break;
4131da177e4SLinus Torvalds 	default:
4141da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
4151da177e4SLinus Torvalds 	}
4161da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
4171da177e4SLinus Torvalds 	return 0;
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
420eb4698f3STakashi Iwai static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
421eb4698f3STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
4221da177e4SLinus Torvalds {
423eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
4241da177e4SLinus Torvalds 	int change;
4251da177e4SLinus Torvalds 	unsigned int reg, val, tmp;
4261da177e4SLinus Torvalds 	unsigned long flags;
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	switch(ucontrol->value.enumerated.item[0]) {
4291da177e4SLinus Torvalds 	case 0:
4301da177e4SLinus Torvalds 		val = A_SPDIF_44100;
4311da177e4SLinus Torvalds 		break;
4321da177e4SLinus Torvalds 	case 1:
4331da177e4SLinus Torvalds 		val = A_SPDIF_48000;
4341da177e4SLinus Torvalds 		break;
4351da177e4SLinus Torvalds 	case 2:
4361da177e4SLinus Torvalds 		val = A_SPDIF_96000;
4371da177e4SLinus Torvalds 		break;
4381da177e4SLinus Torvalds 	default:
4391da177e4SLinus Torvalds 		val = A_SPDIF_48000;
4401da177e4SLinus Torvalds 		break;
4411da177e4SLinus Torvalds 	}
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
4451da177e4SLinus Torvalds 	reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
4461da177e4SLinus Torvalds 	tmp = reg & ~A_SPDIF_RATE_MASK;
4471da177e4SLinus Torvalds 	tmp |= val;
4481da177e4SLinus Torvalds 	if ((change = (tmp != reg)))
4491da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
4501da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
4511da177e4SLinus Torvalds 	return change;
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds 
454eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_audigy_spdif_output_rate =
4551da177e4SLinus Torvalds {
4561da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
4571da177e4SLinus Torvalds 	.iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
4581da177e4SLinus Torvalds 	.name =         "Audigy SPDIF Output Sample Rate",
4591da177e4SLinus Torvalds 	.count =	1,
4601da177e4SLinus Torvalds 	.info =         snd_audigy_spdif_output_rate_info,
4611da177e4SLinus Torvalds 	.get =          snd_audigy_spdif_output_rate_get,
4621da177e4SLinus Torvalds 	.put =          snd_audigy_spdif_output_rate_put
4631da177e4SLinus Torvalds };
4640af68e5eSTakashi Iwai #endif
4651da177e4SLinus Torvalds 
466eb4698f3STakashi Iwai static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
467eb4698f3STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
4681da177e4SLinus Torvalds {
469eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
4701da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
4711da177e4SLinus Torvalds 	int change;
4721da177e4SLinus Torvalds 	unsigned int val;
4731da177e4SLinus Torvalds 	unsigned long flags;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	val = (ucontrol->value.iec958.status[0] << 0) |
4761da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[1] << 8) |
4771da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[2] << 16) |
4781da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[3] << 24);
4791da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
4801da177e4SLinus Torvalds 	change = val != emu->spdif_bits[idx];
4811da177e4SLinus Torvalds 	if (change) {
4821da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
4831da177e4SLinus Torvalds 		emu->spdif_bits[idx] = val;
4841da177e4SLinus Torvalds 	}
4851da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
4861da177e4SLinus Torvalds 	return change;
4871da177e4SLinus Torvalds }
4881da177e4SLinus Torvalds 
489eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
4901da177e4SLinus Torvalds {
4911da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
4925549d549SClemens Ladisch 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
4931da177e4SLinus Torvalds 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
4941da177e4SLinus Torvalds 	.count =	4,
4951da177e4SLinus Torvalds 	.info =         snd_emu10k1_spdif_info,
4961da177e4SLinus Torvalds 	.get =          snd_emu10k1_spdif_get_mask
4971da177e4SLinus Torvalds };
4981da177e4SLinus Torvalds 
499eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_spdif_control =
5001da177e4SLinus Torvalds {
5015549d549SClemens Ladisch 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
5021da177e4SLinus Torvalds 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
5031da177e4SLinus Torvalds 	.count =	4,
5041da177e4SLinus Torvalds 	.info =         snd_emu10k1_spdif_info,
5051da177e4SLinus Torvalds 	.get =          snd_emu10k1_spdif_get,
5061da177e4SLinus Torvalds 	.put =          snd_emu10k1_spdif_put
5071da177e4SLinus Torvalds };
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 
510eb4698f3STakashi Iwai static void update_emu10k1_fxrt(struct snd_emu10k1 *emu, int voice, unsigned char *route)
5111da177e4SLinus Torvalds {
5121da177e4SLinus Torvalds 	if (emu->audigy) {
5131da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
5141da177e4SLinus Torvalds 				      snd_emu10k1_compose_audigy_fxrt1(route));
5151da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
5161da177e4SLinus Torvalds 				      snd_emu10k1_compose_audigy_fxrt2(route));
5171da177e4SLinus Torvalds 	} else {
5181da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(emu, FXRT, voice,
5191da177e4SLinus Torvalds 				      snd_emu10k1_compose_send_routing(route));
5201da177e4SLinus Torvalds 	}
5211da177e4SLinus Torvalds }
5221da177e4SLinus Torvalds 
523eb4698f3STakashi Iwai static void update_emu10k1_send_volume(struct snd_emu10k1 *emu, int voice, unsigned char *volume)
5241da177e4SLinus Torvalds {
5251da177e4SLinus Torvalds 	snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]);
5261da177e4SLinus Torvalds 	snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]);
5271da177e4SLinus Torvalds 	snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
5281da177e4SLinus Torvalds 	snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
5291da177e4SLinus Torvalds 	if (emu->audigy) {
5301da177e4SLinus Torvalds 		unsigned int val = ((unsigned int)volume[4] << 24) |
5311da177e4SLinus Torvalds 			((unsigned int)volume[5] << 16) |
5321da177e4SLinus Torvalds 			((unsigned int)volume[6] << 8) |
5331da177e4SLinus Torvalds 			(unsigned int)volume[7];
5341da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val);
5351da177e4SLinus Torvalds 	}
5361da177e4SLinus Torvalds }
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds /* PCM stream controls */
5391da177e4SLinus Torvalds 
540eb4698f3STakashi Iwai static int snd_emu10k1_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
5411da177e4SLinus Torvalds {
542eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
5431da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
5441da177e4SLinus Torvalds 	uinfo->count = emu->audigy ? 3*8 : 3*4;
5451da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
5461da177e4SLinus Torvalds 	uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
5471da177e4SLinus Torvalds 	return 0;
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds 
550eb4698f3STakashi Iwai static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
551eb4698f3STakashi Iwai                                         struct snd_ctl_elem_value *ucontrol)
5521da177e4SLinus Torvalds {
5531da177e4SLinus Torvalds 	unsigned long flags;
554eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
555eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
556eb4698f3STakashi Iwai 		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
5571da177e4SLinus Torvalds 	int voice, idx;
5581da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
5591da177e4SLinus Torvalds 	int mask = emu->audigy ? 0x3f : 0x0f;
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
5621da177e4SLinus Torvalds 	for (voice = 0; voice < 3; voice++)
5631da177e4SLinus Torvalds 		for (idx = 0; idx < num_efx; idx++)
5641da177e4SLinus Torvalds 			ucontrol->value.integer.value[(voice * num_efx) + idx] =
5651da177e4SLinus Torvalds 				mix->send_routing[voice][idx] & mask;
5661da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
5671da177e4SLinus Torvalds 	return 0;
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds 
570eb4698f3STakashi Iwai static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
571eb4698f3STakashi Iwai                                         struct snd_ctl_elem_value *ucontrol)
5721da177e4SLinus Torvalds {
5731da177e4SLinus Torvalds 	unsigned long flags;
574eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
575eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
576eb4698f3STakashi Iwai 		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
5771da177e4SLinus Torvalds 	int change = 0, voice, idx, val;
5781da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
5791da177e4SLinus Torvalds 	int mask = emu->audigy ? 0x3f : 0x0f;
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
5821da177e4SLinus Torvalds 	for (voice = 0; voice < 3; voice++)
5831da177e4SLinus Torvalds 		for (idx = 0; idx < num_efx; idx++) {
5841da177e4SLinus Torvalds 			val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
5851da177e4SLinus Torvalds 			if (mix->send_routing[voice][idx] != val) {
5861da177e4SLinus Torvalds 				mix->send_routing[voice][idx] = val;
5871da177e4SLinus Torvalds 				change = 1;
5881da177e4SLinus Torvalds 			}
5891da177e4SLinus Torvalds 		}
5901da177e4SLinus Torvalds 	if (change && mix->epcm) {
5911da177e4SLinus Torvalds 		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
5921da177e4SLinus Torvalds 			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
5931da177e4SLinus Torvalds 					    &mix->send_routing[1][0]);
5941da177e4SLinus Torvalds 			update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
5951da177e4SLinus Torvalds 					    &mix->send_routing[2][0]);
5961da177e4SLinus Torvalds 		} else if (mix->epcm->voices[0]) {
5971da177e4SLinus Torvalds 			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
5981da177e4SLinus Torvalds 					    &mix->send_routing[0][0]);
5991da177e4SLinus Torvalds 		}
6001da177e4SLinus Torvalds 	}
6011da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
6021da177e4SLinus Torvalds 	return change;
6031da177e4SLinus Torvalds }
6041da177e4SLinus Torvalds 
605eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_send_routing_control =
6061da177e4SLinus Torvalds {
6071da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
60867ed4161SClemens Ladisch 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
6091da177e4SLinus Torvalds 	.name =         "EMU10K1 PCM Send Routing",
6101da177e4SLinus Torvalds 	.count =	32,
6111da177e4SLinus Torvalds 	.info =         snd_emu10k1_send_routing_info,
6121da177e4SLinus Torvalds 	.get =          snd_emu10k1_send_routing_get,
6131da177e4SLinus Torvalds 	.put =          snd_emu10k1_send_routing_put
6141da177e4SLinus Torvalds };
6151da177e4SLinus Torvalds 
616eb4698f3STakashi Iwai static int snd_emu10k1_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
6171da177e4SLinus Torvalds {
618eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
6191da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
6201da177e4SLinus Torvalds 	uinfo->count = emu->audigy ? 3*8 : 3*4;
6211da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
6221da177e4SLinus Torvalds 	uinfo->value.integer.max = 255;
6231da177e4SLinus Torvalds 	return 0;
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds 
626eb4698f3STakashi Iwai static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
627eb4698f3STakashi Iwai                                        struct snd_ctl_elem_value *ucontrol)
6281da177e4SLinus Torvalds {
6291da177e4SLinus Torvalds 	unsigned long flags;
630eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
631eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
632eb4698f3STakashi Iwai 		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
6331da177e4SLinus Torvalds 	int idx;
6341da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
6371da177e4SLinus Torvalds 	for (idx = 0; idx < 3*num_efx; idx++)
6381da177e4SLinus Torvalds 		ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
6391da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
6401da177e4SLinus Torvalds 	return 0;
6411da177e4SLinus Torvalds }
6421da177e4SLinus Torvalds 
643eb4698f3STakashi Iwai static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
644eb4698f3STakashi Iwai                                        struct snd_ctl_elem_value *ucontrol)
6451da177e4SLinus Torvalds {
6461da177e4SLinus Torvalds 	unsigned long flags;
647eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
648eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
649eb4698f3STakashi Iwai 		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
6501da177e4SLinus Torvalds 	int change = 0, idx, val;
6511da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
6521da177e4SLinus Torvalds 
6531da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
6541da177e4SLinus Torvalds 	for (idx = 0; idx < 3*num_efx; idx++) {
6551da177e4SLinus Torvalds 		val = ucontrol->value.integer.value[idx] & 255;
6561da177e4SLinus Torvalds 		if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
6571da177e4SLinus Torvalds 			mix->send_volume[idx/num_efx][idx%num_efx] = val;
6581da177e4SLinus Torvalds 			change = 1;
6591da177e4SLinus Torvalds 		}
6601da177e4SLinus Torvalds 	}
6611da177e4SLinus Torvalds 	if (change && mix->epcm) {
6621da177e4SLinus Torvalds 		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
6631da177e4SLinus Torvalds 			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
6641da177e4SLinus Torvalds 						   &mix->send_volume[1][0]);
6651da177e4SLinus Torvalds 			update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
6661da177e4SLinus Torvalds 						   &mix->send_volume[2][0]);
6671da177e4SLinus Torvalds 		} else if (mix->epcm->voices[0]) {
6681da177e4SLinus Torvalds 			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
6691da177e4SLinus Torvalds 						   &mix->send_volume[0][0]);
6701da177e4SLinus Torvalds 		}
6711da177e4SLinus Torvalds 	}
6721da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
6731da177e4SLinus Torvalds 	return change;
6741da177e4SLinus Torvalds }
6751da177e4SLinus Torvalds 
676eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_send_volume_control =
6771da177e4SLinus Torvalds {
6781da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
67967ed4161SClemens Ladisch 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
6801da177e4SLinus Torvalds 	.name =         "EMU10K1 PCM Send Volume",
6811da177e4SLinus Torvalds 	.count =	32,
6821da177e4SLinus Torvalds 	.info =         snd_emu10k1_send_volume_info,
6831da177e4SLinus Torvalds 	.get =          snd_emu10k1_send_volume_get,
6841da177e4SLinus Torvalds 	.put =          snd_emu10k1_send_volume_put
6851da177e4SLinus Torvalds };
6861da177e4SLinus Torvalds 
687eb4698f3STakashi Iwai static int snd_emu10k1_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
6881da177e4SLinus Torvalds {
6891da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
6901da177e4SLinus Torvalds 	uinfo->count = 3;
6911da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
6921da177e4SLinus Torvalds 	uinfo->value.integer.max = 0xffff;
6931da177e4SLinus Torvalds 	return 0;
6941da177e4SLinus Torvalds }
6951da177e4SLinus Torvalds 
696eb4698f3STakashi Iwai static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
697eb4698f3STakashi Iwai                                 struct snd_ctl_elem_value *ucontrol)
6981da177e4SLinus Torvalds {
699eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
700eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
701eb4698f3STakashi Iwai 		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
7021da177e4SLinus Torvalds 	unsigned long flags;
7031da177e4SLinus Torvalds 	int idx;
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
7061da177e4SLinus Torvalds 	for (idx = 0; idx < 3; idx++)
7071da177e4SLinus Torvalds 		ucontrol->value.integer.value[idx] = mix->attn[idx];
7081da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
7091da177e4SLinus Torvalds 	return 0;
7101da177e4SLinus Torvalds }
7111da177e4SLinus Torvalds 
712eb4698f3STakashi Iwai static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
713eb4698f3STakashi Iwai 				struct snd_ctl_elem_value *ucontrol)
7141da177e4SLinus Torvalds {
7151da177e4SLinus Torvalds 	unsigned long flags;
716eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
717eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
718eb4698f3STakashi Iwai 		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
7191da177e4SLinus Torvalds 	int change = 0, idx, val;
7201da177e4SLinus Torvalds 
7211da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
7221da177e4SLinus Torvalds 	for (idx = 0; idx < 3; idx++) {
7231da177e4SLinus Torvalds 		val = ucontrol->value.integer.value[idx] & 0xffff;
7241da177e4SLinus Torvalds 		if (mix->attn[idx] != val) {
7251da177e4SLinus Torvalds 			mix->attn[idx] = val;
7261da177e4SLinus Torvalds 			change = 1;
7271da177e4SLinus Torvalds 		}
7281da177e4SLinus Torvalds 	}
7291da177e4SLinus Torvalds 	if (change && mix->epcm) {
7301da177e4SLinus Torvalds 		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
7311da177e4SLinus Torvalds 			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
7321da177e4SLinus Torvalds 			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
7331da177e4SLinus Torvalds 		} else if (mix->epcm->voices[0]) {
7341da177e4SLinus Torvalds 			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
7351da177e4SLinus Torvalds 		}
7361da177e4SLinus Torvalds 	}
7371da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
7381da177e4SLinus Torvalds 	return change;
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds 
741eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_attn_control =
7421da177e4SLinus Torvalds {
7431da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
74467ed4161SClemens Ladisch 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
7451da177e4SLinus Torvalds 	.name =         "EMU10K1 PCM Volume",
7461da177e4SLinus Torvalds 	.count =	32,
7471da177e4SLinus Torvalds 	.info =         snd_emu10k1_attn_info,
7481da177e4SLinus Torvalds 	.get =          snd_emu10k1_attn_get,
7491da177e4SLinus Torvalds 	.put =          snd_emu10k1_attn_put
7501da177e4SLinus Torvalds };
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds /* Mutichannel PCM stream controls */
7531da177e4SLinus Torvalds 
754eb4698f3STakashi Iwai static int snd_emu10k1_efx_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
7551da177e4SLinus Torvalds {
756eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
7571da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
7581da177e4SLinus Torvalds 	uinfo->count = emu->audigy ? 8 : 4;
7591da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
7601da177e4SLinus Torvalds 	uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
7611da177e4SLinus Torvalds 	return 0;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds 
764eb4698f3STakashi Iwai static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
765eb4698f3STakashi Iwai                                         struct snd_ctl_elem_value *ucontrol)
7661da177e4SLinus Torvalds {
7671da177e4SLinus Torvalds 	unsigned long flags;
768eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
769eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
770eb4698f3STakashi Iwai 		&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
7711da177e4SLinus Torvalds 	int idx;
7721da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
7731da177e4SLinus Torvalds 	int mask = emu->audigy ? 0x3f : 0x0f;
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
7761da177e4SLinus Torvalds 	for (idx = 0; idx < num_efx; idx++)
7771da177e4SLinus Torvalds 		ucontrol->value.integer.value[idx] =
7781da177e4SLinus Torvalds 			mix->send_routing[0][idx] & mask;
7791da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
7801da177e4SLinus Torvalds 	return 0;
7811da177e4SLinus Torvalds }
7821da177e4SLinus Torvalds 
783eb4698f3STakashi Iwai static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
784eb4698f3STakashi Iwai                                         struct snd_ctl_elem_value *ucontrol)
7851da177e4SLinus Torvalds {
7861da177e4SLinus Torvalds 	unsigned long flags;
787eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
7881da177e4SLinus Torvalds 	int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
789eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
7901da177e4SLinus Torvalds 	int change = 0, idx, val;
7911da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
7921da177e4SLinus Torvalds 	int mask = emu->audigy ? 0x3f : 0x0f;
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
7951da177e4SLinus Torvalds 	for (idx = 0; idx < num_efx; idx++) {
7961da177e4SLinus Torvalds 		val = ucontrol->value.integer.value[idx] & mask;
7971da177e4SLinus Torvalds 		if (mix->send_routing[0][idx] != val) {
7981da177e4SLinus Torvalds 			mix->send_routing[0][idx] = val;
7991da177e4SLinus Torvalds 			change = 1;
8001da177e4SLinus Torvalds 		}
8011da177e4SLinus Torvalds 	}
8021da177e4SLinus Torvalds 
8031da177e4SLinus Torvalds 	if (change && mix->epcm) {
8041da177e4SLinus Torvalds 		if (mix->epcm->voices[ch]) {
8051da177e4SLinus Torvalds 			update_emu10k1_fxrt(emu, mix->epcm->voices[ch]->number,
8061da177e4SLinus Torvalds 					&mix->send_routing[0][0]);
8071da177e4SLinus Torvalds 		}
8081da177e4SLinus Torvalds 	}
8091da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
8101da177e4SLinus Torvalds 	return change;
8111da177e4SLinus Torvalds }
8121da177e4SLinus Torvalds 
813eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
8141da177e4SLinus Torvalds {
8151da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
8161da177e4SLinus Torvalds 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
8171da177e4SLinus Torvalds 	.name =         "Multichannel PCM Send Routing",
8181da177e4SLinus Torvalds 	.count =	16,
8191da177e4SLinus Torvalds 	.info =         snd_emu10k1_efx_send_routing_info,
8201da177e4SLinus Torvalds 	.get =          snd_emu10k1_efx_send_routing_get,
8211da177e4SLinus Torvalds 	.put =          snd_emu10k1_efx_send_routing_put
8221da177e4SLinus Torvalds };
8231da177e4SLinus Torvalds 
824eb4698f3STakashi Iwai static int snd_emu10k1_efx_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
8251da177e4SLinus Torvalds {
826eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
8271da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
8281da177e4SLinus Torvalds 	uinfo->count = emu->audigy ? 8 : 4;
8291da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
8301da177e4SLinus Torvalds 	uinfo->value.integer.max = 255;
8311da177e4SLinus Torvalds 	return 0;
8321da177e4SLinus Torvalds }
8331da177e4SLinus Torvalds 
834eb4698f3STakashi Iwai static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol,
835eb4698f3STakashi Iwai                                        struct snd_ctl_elem_value *ucontrol)
8361da177e4SLinus Torvalds {
8371da177e4SLinus Torvalds 	unsigned long flags;
838eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
839eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
840eb4698f3STakashi Iwai 		&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
8411da177e4SLinus Torvalds 	int idx;
8421da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
8451da177e4SLinus Torvalds 	for (idx = 0; idx < num_efx; idx++)
8461da177e4SLinus Torvalds 		ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
8471da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
8481da177e4SLinus Torvalds 	return 0;
8491da177e4SLinus Torvalds }
8501da177e4SLinus Torvalds 
851eb4698f3STakashi Iwai static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
852eb4698f3STakashi Iwai                                        struct snd_ctl_elem_value *ucontrol)
8531da177e4SLinus Torvalds {
8541da177e4SLinus Torvalds 	unsigned long flags;
855eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
8561da177e4SLinus Torvalds 	int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
857eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
8581da177e4SLinus Torvalds 	int change = 0, idx, val;
8591da177e4SLinus Torvalds 	int num_efx = emu->audigy ? 8 : 4;
8601da177e4SLinus Torvalds 
8611da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
8621da177e4SLinus Torvalds 	for (idx = 0; idx < num_efx; idx++) {
8631da177e4SLinus Torvalds 		val = ucontrol->value.integer.value[idx] & 255;
8641da177e4SLinus Torvalds 		if (mix->send_volume[0][idx] != val) {
8651da177e4SLinus Torvalds 			mix->send_volume[0][idx] = val;
8661da177e4SLinus Torvalds 			change = 1;
8671da177e4SLinus Torvalds 		}
8681da177e4SLinus Torvalds 	}
8691da177e4SLinus Torvalds 	if (change && mix->epcm) {
8701da177e4SLinus Torvalds 		if (mix->epcm->voices[ch]) {
8711da177e4SLinus Torvalds 			update_emu10k1_send_volume(emu, mix->epcm->voices[ch]->number,
8721da177e4SLinus Torvalds 						   &mix->send_volume[0][0]);
8731da177e4SLinus Torvalds 		}
8741da177e4SLinus Torvalds 	}
8751da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
8761da177e4SLinus Torvalds 	return change;
8771da177e4SLinus Torvalds }
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 
880eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
8811da177e4SLinus Torvalds {
8821da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
8831da177e4SLinus Torvalds 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
8841da177e4SLinus Torvalds 	.name =         "Multichannel PCM Send Volume",
8851da177e4SLinus Torvalds 	.count =	16,
8861da177e4SLinus Torvalds 	.info =         snd_emu10k1_efx_send_volume_info,
8871da177e4SLinus Torvalds 	.get =          snd_emu10k1_efx_send_volume_get,
8881da177e4SLinus Torvalds 	.put =          snd_emu10k1_efx_send_volume_put
8891da177e4SLinus Torvalds };
8901da177e4SLinus Torvalds 
891eb4698f3STakashi Iwai static int snd_emu10k1_efx_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
8921da177e4SLinus Torvalds {
8931da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
8941da177e4SLinus Torvalds 	uinfo->count = 1;
8951da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
8961da177e4SLinus Torvalds 	uinfo->value.integer.max = 0xffff;
8971da177e4SLinus Torvalds 	return 0;
8981da177e4SLinus Torvalds }
8991da177e4SLinus Torvalds 
900eb4698f3STakashi Iwai static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
901eb4698f3STakashi Iwai                                 struct snd_ctl_elem_value *ucontrol)
9021da177e4SLinus Torvalds {
903eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
904eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix =
905eb4698f3STakashi Iwai 		&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
9061da177e4SLinus Torvalds 	unsigned long flags;
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
9091da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = mix->attn[0];
9101da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
9111da177e4SLinus Torvalds 	return 0;
9121da177e4SLinus Torvalds }
9131da177e4SLinus Torvalds 
914eb4698f3STakashi Iwai static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
915eb4698f3STakashi Iwai 				struct snd_ctl_elem_value *ucontrol)
9161da177e4SLinus Torvalds {
9171da177e4SLinus Torvalds 	unsigned long flags;
918eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
9191da177e4SLinus Torvalds 	int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
920eb4698f3STakashi Iwai 	struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
9211da177e4SLinus Torvalds 	int change = 0, val;
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
9241da177e4SLinus Torvalds 	val = ucontrol->value.integer.value[0] & 0xffff;
9251da177e4SLinus Torvalds 	if (mix->attn[0] != val) {
9261da177e4SLinus Torvalds 		mix->attn[0] = val;
9271da177e4SLinus Torvalds 		change = 1;
9281da177e4SLinus Torvalds 	}
9291da177e4SLinus Torvalds 	if (change && mix->epcm) {
9301da177e4SLinus Torvalds 		if (mix->epcm->voices[ch]) {
9311da177e4SLinus Torvalds 			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
9321da177e4SLinus Torvalds 		}
9331da177e4SLinus Torvalds 	}
9341da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
9351da177e4SLinus Torvalds 	return change;
9361da177e4SLinus Torvalds }
9371da177e4SLinus Torvalds 
938eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
9391da177e4SLinus Torvalds {
9401da177e4SLinus Torvalds 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
9411da177e4SLinus Torvalds 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
9421da177e4SLinus Torvalds 	.name =         "Multichannel PCM Volume",
9431da177e4SLinus Torvalds 	.count =	16,
9441da177e4SLinus Torvalds 	.info =         snd_emu10k1_efx_attn_info,
9451da177e4SLinus Torvalds 	.get =          snd_emu10k1_efx_attn_get,
9461da177e4SLinus Torvalds 	.put =          snd_emu10k1_efx_attn_put
9471da177e4SLinus Torvalds };
9481da177e4SLinus Torvalds 
949eb4698f3STakashi Iwai static int snd_emu10k1_shared_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
9501da177e4SLinus Torvalds {
9511da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
9521da177e4SLinus Torvalds 	uinfo->count = 1;
9531da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
9541da177e4SLinus Torvalds 	uinfo->value.integer.max = 1;
9551da177e4SLinus Torvalds 	return 0;
9561da177e4SLinus Torvalds }
9571da177e4SLinus Torvalds 
958eb4698f3STakashi Iwai static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
959eb4698f3STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
9601da177e4SLinus Torvalds {
961eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds 	if (emu->audigy)
9641da177e4SLinus Torvalds 		ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
9651da177e4SLinus Torvalds 	else
9661da177e4SLinus Torvalds 		ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
9671da177e4SLinus Torvalds 	return 0;
9681da177e4SLinus Torvalds }
9691da177e4SLinus Torvalds 
970eb4698f3STakashi Iwai static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
971eb4698f3STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
9721da177e4SLinus Torvalds {
9731da177e4SLinus Torvalds 	unsigned long flags;
974eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
9751da177e4SLinus Torvalds 	unsigned int reg, val;
9761da177e4SLinus Torvalds 	int change = 0;
9771da177e4SLinus Torvalds 
9781da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->reg_lock, flags);
9791da177e4SLinus Torvalds 	if (emu->audigy) {
9801da177e4SLinus Torvalds 		reg = inl(emu->port + A_IOCFG);
9811da177e4SLinus Torvalds 		val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
9821da177e4SLinus Torvalds 		change = (reg & A_IOCFG_GPOUT0) != val;
9831da177e4SLinus Torvalds 		if (change) {
9841da177e4SLinus Torvalds 			reg &= ~A_IOCFG_GPOUT0;
9851da177e4SLinus Torvalds 			reg |= val;
9861da177e4SLinus Torvalds 			outl(reg | val, emu->port + A_IOCFG);
9871da177e4SLinus Torvalds 		}
9881da177e4SLinus Torvalds 	}
9891da177e4SLinus Torvalds 	reg = inl(emu->port + HCFG);
9901da177e4SLinus Torvalds 	val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0;
9911da177e4SLinus Torvalds 	change |= (reg & HCFG_GPOUT0) != val;
9921da177e4SLinus Torvalds 	if (change) {
9931da177e4SLinus Torvalds 		reg &= ~HCFG_GPOUT0;
9941da177e4SLinus Torvalds 		reg |= val;
9951da177e4SLinus Torvalds 		outl(reg | val, emu->port + HCFG);
9961da177e4SLinus Torvalds 	}
9971da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->reg_lock, flags);
9981da177e4SLinus Torvalds 	return change;
9991da177e4SLinus Torvalds }
10001da177e4SLinus Torvalds 
1001eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_emu10k1_shared_spdif __devinitdata =
10021da177e4SLinus Torvalds {
10031da177e4SLinus Torvalds 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
10041da177e4SLinus Torvalds 	.name =		"SB Live Analog/Digital Output Jack",
10051da177e4SLinus Torvalds 	.info =		snd_emu10k1_shared_spdif_info,
10061da177e4SLinus Torvalds 	.get =		snd_emu10k1_shared_spdif_get,
10071da177e4SLinus Torvalds 	.put =		snd_emu10k1_shared_spdif_put
10081da177e4SLinus Torvalds };
10091da177e4SLinus Torvalds 
1010eb4698f3STakashi Iwai static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
10111da177e4SLinus Torvalds {
10121da177e4SLinus Torvalds 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
10131da177e4SLinus Torvalds 	.name =		"Audigy Analog/Digital Output Jack",
10141da177e4SLinus Torvalds 	.info =		snd_emu10k1_shared_spdif_info,
10151da177e4SLinus Torvalds 	.get =		snd_emu10k1_shared_spdif_get,
10161da177e4SLinus Torvalds 	.put =		snd_emu10k1_shared_spdif_put
10171da177e4SLinus Torvalds };
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds /*
10201da177e4SLinus Torvalds  */
1021eb4698f3STakashi Iwai static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
10221da177e4SLinus Torvalds {
1023eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = ac97->private_data;
10241da177e4SLinus Torvalds 	emu->ac97 = NULL;
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds 
10271da177e4SLinus Torvalds /*
10281da177e4SLinus Torvalds  */
1029eb4698f3STakashi Iwai static int remove_ctl(struct snd_card *card, const char *name)
10301da177e4SLinus Torvalds {
1031eb4698f3STakashi Iwai 	struct snd_ctl_elem_id id;
10321da177e4SLinus Torvalds 	memset(&id, 0, sizeof(id));
10331da177e4SLinus Torvalds 	strcpy(id.name, name);
10341da177e4SLinus Torvalds 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
10351da177e4SLinus Torvalds 	return snd_ctl_remove_id(card, &id);
10361da177e4SLinus Torvalds }
10371da177e4SLinus Torvalds 
1038eb4698f3STakashi Iwai static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
10391da177e4SLinus Torvalds {
1040eb4698f3STakashi Iwai 	struct snd_ctl_elem_id sid;
10411da177e4SLinus Torvalds 	memset(&sid, 0, sizeof(sid));
10421da177e4SLinus Torvalds 	strcpy(sid.name, name);
10431da177e4SLinus Torvalds 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
10441da177e4SLinus Torvalds 	return snd_ctl_find_id(card, &sid);
10451da177e4SLinus Torvalds }
10461da177e4SLinus Torvalds 
1047eb4698f3STakashi Iwai static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
10481da177e4SLinus Torvalds {
1049eb4698f3STakashi Iwai 	struct snd_kcontrol *kctl = ctl_find(card, src);
10501da177e4SLinus Torvalds 	if (kctl) {
10511da177e4SLinus Torvalds 		strcpy(kctl->id.name, dst);
10521da177e4SLinus Torvalds 		return 0;
10531da177e4SLinus Torvalds 	}
10541da177e4SLinus Torvalds 	return -ENOENT;
10551da177e4SLinus Torvalds }
10561da177e4SLinus Torvalds 
1057eb4698f3STakashi Iwai int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
105867ed4161SClemens Ladisch 				int pcm_device, int multi_device)
10591da177e4SLinus Torvalds {
10601da177e4SLinus Torvalds 	int err, pcm;
1061eb4698f3STakashi Iwai 	struct snd_kcontrol *kctl;
1062eb4698f3STakashi Iwai 	struct snd_card *card = emu->card;
10631da177e4SLinus Torvalds 	char **c;
10641da177e4SLinus Torvalds 	static char *emu10k1_remove_ctls[] = {
10651da177e4SLinus Torvalds 		/* no AC97 mono, surround, center/lfe */
10661da177e4SLinus Torvalds 		"Master Mono Playback Switch",
10671da177e4SLinus Torvalds 		"Master Mono Playback Volume",
10681da177e4SLinus Torvalds 		"PCM Out Path & Mute",
10691da177e4SLinus Torvalds 		"Mono Output Select",
10707eae36fbSTakashi Iwai 		"Front Playback Switch",
10717eae36fbSTakashi Iwai 		"Front Playback Volume",
10721da177e4SLinus Torvalds 		"Surround Playback Switch",
10731da177e4SLinus Torvalds 		"Surround Playback Volume",
10741da177e4SLinus Torvalds 		"Center Playback Switch",
10751da177e4SLinus Torvalds 		"Center Playback Volume",
10761da177e4SLinus Torvalds 		"LFE Playback Switch",
10771da177e4SLinus Torvalds 		"LFE Playback Volume",
10781da177e4SLinus Torvalds 		NULL
10791da177e4SLinus Torvalds 	};
10801da177e4SLinus Torvalds 	static char *emu10k1_rename_ctls[] = {
10811da177e4SLinus Torvalds 		"Surround Digital Playback Volume", "Surround Playback Volume",
10821da177e4SLinus Torvalds 		"Center Digital Playback Volume", "Center Playback Volume",
10831da177e4SLinus Torvalds 		"LFE Digital Playback Volume", "LFE Playback Volume",
10841da177e4SLinus Torvalds 		NULL
10851da177e4SLinus Torvalds 	};
10861da177e4SLinus Torvalds 	static char *audigy_remove_ctls[] = {
10871da177e4SLinus Torvalds 		/* Master/PCM controls on ac97 of Audigy has no effect */
108821fdddeaSJames Courtier-Dutton 		/* On the Audigy2 the AC97 playback is piped into
108921fdddeaSJames Courtier-Dutton 		 * the Philips ADC for 24bit capture */
10901da177e4SLinus Torvalds 		"PCM Playback Switch",
10911da177e4SLinus Torvalds 		"PCM Playback Volume",
10921da177e4SLinus Torvalds 		"Master Mono Playback Switch",
10931da177e4SLinus Torvalds 		"Master Mono Playback Volume",
10941da177e4SLinus Torvalds 		"Master Playback Switch",
10951da177e4SLinus Torvalds 		"Master Playback Volume",
10961da177e4SLinus Torvalds 		"PCM Out Path & Mute",
10971da177e4SLinus Torvalds 		"Mono Output Select",
10981da177e4SLinus Torvalds 		/* remove unused AC97 capture controls */
10991da177e4SLinus Torvalds 		"Capture Source",
11001da177e4SLinus Torvalds 		"Capture Switch",
11011da177e4SLinus Torvalds 		"Capture Volume",
11021da177e4SLinus Torvalds 		"Mic Select",
11031da177e4SLinus Torvalds 		"Video Playback Switch",
11041da177e4SLinus Torvalds 		"Video Playback Volume",
11051da177e4SLinus Torvalds 		"Mic Playback Switch",
11061da177e4SLinus Torvalds 		"Mic Playback Volume",
11071da177e4SLinus Torvalds 		NULL
11081da177e4SLinus Torvalds 	};
11091da177e4SLinus Torvalds 	static char *audigy_rename_ctls[] = {
11101da177e4SLinus Torvalds 		/* use conventional names */
11111da177e4SLinus Torvalds 		"Wave Playback Volume", "PCM Playback Volume",
11121da177e4SLinus Torvalds 		/* "Wave Capture Volume", "PCM Capture Volume", */
11131da177e4SLinus Torvalds 		"Wave Master Playback Volume", "Master Playback Volume",
11141da177e4SLinus Torvalds 		"AMic Playback Volume", "Mic Playback Volume",
11151da177e4SLinus Torvalds 		NULL
11161da177e4SLinus Torvalds 	};
111721fdddeaSJames Courtier-Dutton 	static char *audigy_remove_ctls_1361t_adc[] = {
111821fdddeaSJames Courtier-Dutton 		/* On the Audigy2 the AC97 playback is piped into
111921fdddeaSJames Courtier-Dutton 		 * the Philips ADC for 24bit capture */
112021fdddeaSJames Courtier-Dutton 		"PCM Playback Switch",
112121fdddeaSJames Courtier-Dutton 		"PCM Playback Volume",
112221fdddeaSJames Courtier-Dutton 		"Master Mono Playback Switch",
112321fdddeaSJames Courtier-Dutton 		"Master Mono Playback Volume",
112421fdddeaSJames Courtier-Dutton 		"Capture Source",
112521fdddeaSJames Courtier-Dutton 		"Capture Switch",
112621fdddeaSJames Courtier-Dutton 		"Capture Volume",
112721fdddeaSJames Courtier-Dutton 		"Mic Capture Volume",
112821fdddeaSJames Courtier-Dutton 		"Headphone Playback Switch",
112921fdddeaSJames Courtier-Dutton 		"Headphone Playback Volume",
113021fdddeaSJames Courtier-Dutton 		"3D Control - Center",
113121fdddeaSJames Courtier-Dutton 		"3D Control - Depth",
113221fdddeaSJames Courtier-Dutton 		"3D Control - Switch",
113321fdddeaSJames Courtier-Dutton 		"Line2 Playback Volume",
113421fdddeaSJames Courtier-Dutton 		"Line2 Capture Volume",
113521fdddeaSJames Courtier-Dutton 		NULL
113621fdddeaSJames Courtier-Dutton 	};
113721fdddeaSJames Courtier-Dutton 	static char *audigy_rename_ctls_1361t_adc[] = {
113821fdddeaSJames Courtier-Dutton 		"Master Playback Switch", "Master Capture Switch",
113921fdddeaSJames Courtier-Dutton 		"Master Playback Volume", "Master Capture Volume",
114021fdddeaSJames Courtier-Dutton 		"Wave Master Playback Volume", "Master Playback Volume",
114121fdddeaSJames Courtier-Dutton 		"PC Speaker Playback Switch", "PC Speaker Capture Switch",
114221fdddeaSJames Courtier-Dutton 		"PC Speaker Playback Volume", "PC Speaker Capture Volume",
114321fdddeaSJames Courtier-Dutton 		"Phone Playback Switch", "Phone Capture Switch",
114421fdddeaSJames Courtier-Dutton 		"Phone Playback Volume", "Phone Capture Volume",
114521fdddeaSJames Courtier-Dutton 		"Mic Playback Switch", "Mic Capture Switch",
114621fdddeaSJames Courtier-Dutton 		"Mic Playback Volume", "Mic Capture Volume",
114721fdddeaSJames Courtier-Dutton 		"Line Playback Switch", "Line Capture Switch",
114821fdddeaSJames Courtier-Dutton 		"Line Playback Volume", "Line Capture Volume",
114921fdddeaSJames Courtier-Dutton 		"CD Playback Switch", "CD Capture Switch",
115021fdddeaSJames Courtier-Dutton 		"CD Playback Volume", "CD Capture Volume",
115121fdddeaSJames Courtier-Dutton 		"Aux Playback Switch", "Aux Capture Switch",
115221fdddeaSJames Courtier-Dutton 		"Aux Playback Volume", "Aux Capture Volume",
115321fdddeaSJames Courtier-Dutton 		"Video Playback Switch", "Video Capture Switch",
115421fdddeaSJames Courtier-Dutton 		"Video Playback Volume", "Video Capture Volume",
115521fdddeaSJames Courtier-Dutton 
115621fdddeaSJames Courtier-Dutton 		NULL
115721fdddeaSJames Courtier-Dutton 	};
11581da177e4SLinus Torvalds 
11592b637da5SLee Revell 	if (emu->card_capabilities->ac97_chip) {
1160eb4698f3STakashi Iwai 		struct snd_ac97_bus *pbus;
1161eb4698f3STakashi Iwai 		struct snd_ac97_template ac97;
1162eb4698f3STakashi Iwai 		static struct snd_ac97_bus_ops ops = {
11631da177e4SLinus Torvalds 			.write = snd_emu10k1_ac97_write,
11641da177e4SLinus Torvalds 			.read = snd_emu10k1_ac97_read,
11651da177e4SLinus Torvalds 		};
11661da177e4SLinus Torvalds 
1167b1508693STakashi Iwai 		if ((err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus)) < 0)
11681da177e4SLinus Torvalds 			return err;
11691da177e4SLinus Torvalds 		pbus->no_vra = 1; /* we don't need VRA */
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 		memset(&ac97, 0, sizeof(ac97));
11721da177e4SLinus Torvalds 		ac97.private_data = emu;
11731da177e4SLinus Torvalds 		ac97.private_free = snd_emu10k1_mixer_free_ac97;
11741da177e4SLinus Torvalds 		ac97.scaps = AC97_SCAP_NO_SPDIF;
1175b1508693STakashi Iwai 		if ((err = snd_ac97_mixer(pbus, &ac97, &emu->ac97)) < 0) {
1176b1508693STakashi Iwai 			if (emu->card_capabilities->ac97_chip == 1)
11771da177e4SLinus Torvalds 				return err;
1178b1508693STakashi Iwai 			snd_printd(KERN_INFO "emu10k1: AC97 is optional on this board\n");
1179b1508693STakashi Iwai 			snd_printd(KERN_INFO"          Proceeding without ac97 mixers...\n");
1180b1508693STakashi Iwai 			snd_device_free(emu->card, pbus);
1181b1508693STakashi Iwai 			goto no_ac97; /* FIXME: get rid of ugly gotos.. */
1182b1508693STakashi Iwai 		}
11831da177e4SLinus Torvalds 		if (emu->audigy) {
11841da177e4SLinus Torvalds 			/* set master volume to 0 dB */
11854d7d7596STakashi Iwai 			snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
11861da177e4SLinus Torvalds 			/* set capture source to mic */
11874d7d7596STakashi Iwai 			snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
118821fdddeaSJames Courtier-Dutton 			if (emu->card_capabilities->adc_1361t)
118921fdddeaSJames Courtier-Dutton 				c = audigy_remove_ctls_1361t_adc;
119021fdddeaSJames Courtier-Dutton 			else
11911da177e4SLinus Torvalds 				c = audigy_remove_ctls;
11921da177e4SLinus Torvalds 		} else {
11931da177e4SLinus Torvalds 			/*
11941da177e4SLinus Torvalds 			 * Credits for cards based on STAC9758:
11951da177e4SLinus Torvalds 			 *   James Courtier-Dutton <James@superbug.demon.co.uk>
11961da177e4SLinus Torvalds 			 *   Voluspa <voluspa@comhem.se>
11971da177e4SLinus Torvalds 			 */
11981da177e4SLinus Torvalds 			if (emu->ac97->id == AC97_ID_STAC9758) {
11991da177e4SLinus Torvalds 				emu->rear_ac97 = 1;
12001da177e4SLinus Torvalds 				snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
12011da177e4SLinus Torvalds 			}
12021da177e4SLinus Torvalds 			/* remove unused AC97 controls */
12034d7d7596STakashi Iwai 			snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
12044d7d7596STakashi Iwai 			snd_ac97_write_cache(emu->ac97, AC97_CENTER_LFE_MASTER, 0x0202);
12051da177e4SLinus Torvalds 			c = emu10k1_remove_ctls;
12061da177e4SLinus Torvalds 		}
12071da177e4SLinus Torvalds 		for (; *c; c++)
12081da177e4SLinus Torvalds 			remove_ctl(card, *c);
12091da177e4SLinus Torvalds 	} else {
1210f12aa40cSTakashi Iwai 	no_ac97:
12112b637da5SLee Revell 		if (emu->card_capabilities->ecard)
12121da177e4SLinus Torvalds 			strcpy(emu->card->mixername, "EMU APS");
12131da177e4SLinus Torvalds 		else if (emu->audigy)
12141da177e4SLinus Torvalds 			strcpy(emu->card->mixername, "SB Audigy");
12151da177e4SLinus Torvalds 		else
12161da177e4SLinus Torvalds 			strcpy(emu->card->mixername, "Emu10k1");
12171da177e4SLinus Torvalds 	}
12181da177e4SLinus Torvalds 
12191da177e4SLinus Torvalds 	if (emu->audigy)
122021fdddeaSJames Courtier-Dutton 		if (emu->card_capabilities->adc_1361t)
122121fdddeaSJames Courtier-Dutton 			c = audigy_rename_ctls_1361t_adc;
122221fdddeaSJames Courtier-Dutton 		else
12231da177e4SLinus Torvalds 			c = audigy_rename_ctls;
12241da177e4SLinus Torvalds 	else
12251da177e4SLinus Torvalds 		c = emu10k1_rename_ctls;
12261da177e4SLinus Torvalds 	for (; *c; c += 2)
12271da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
122821fdddeaSJames Courtier-Dutton 
1229e3b9bc0eSJames Courtier-Dutton 	if (emu->card_capabilities->subsystem == 0x20071102) {  /* Audigy 4 Pro */
1230e3b9bc0eSJames Courtier-Dutton 		rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
1231e3b9bc0eSJames Courtier-Dutton 		rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
1232e3b9bc0eSJames Courtier-Dutton 		rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
1233e3b9bc0eSJames Courtier-Dutton 		rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
1234e3b9bc0eSJames Courtier-Dutton 		remove_ctl(card, "Headphone Playback Switch");
1235e3b9bc0eSJames Courtier-Dutton 		remove_ctl(card, "Headphone Playback Volume");
1236e3b9bc0eSJames Courtier-Dutton 		remove_ctl(card, "3D Control - Center");
1237e3b9bc0eSJames Courtier-Dutton 		remove_ctl(card, "3D Control - Depth");
1238e3b9bc0eSJames Courtier-Dutton 		remove_ctl(card, "3D Control - Switch");
1239e3b9bc0eSJames Courtier-Dutton 	}
12401da177e4SLinus Torvalds 	if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
12411da177e4SLinus Torvalds 		return -ENOMEM;
124267ed4161SClemens Ladisch 	kctl->id.device = pcm_device;
12431da177e4SLinus Torvalds 	if ((err = snd_ctl_add(card, kctl)))
12441da177e4SLinus Torvalds 		return err;
12451da177e4SLinus Torvalds 	if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
12461da177e4SLinus Torvalds 		return -ENOMEM;
124767ed4161SClemens Ladisch 	kctl->id.device = pcm_device;
12481da177e4SLinus Torvalds 	if ((err = snd_ctl_add(card, kctl)))
12491da177e4SLinus Torvalds 		return err;
12501da177e4SLinus Torvalds 	if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
12511da177e4SLinus Torvalds 		return -ENOMEM;
125267ed4161SClemens Ladisch 	kctl->id.device = pcm_device;
12531da177e4SLinus Torvalds 	if ((err = snd_ctl_add(card, kctl)))
12541da177e4SLinus Torvalds 		return err;
12551da177e4SLinus Torvalds 
12561da177e4SLinus Torvalds 	if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL)
12571da177e4SLinus Torvalds 		return -ENOMEM;
125867ed4161SClemens Ladisch 	kctl->id.device = multi_device;
12591da177e4SLinus Torvalds 	if ((err = snd_ctl_add(card, kctl)))
12601da177e4SLinus Torvalds 		return err;
12611da177e4SLinus Torvalds 
12621da177e4SLinus Torvalds 	if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL)
12631da177e4SLinus Torvalds 		return -ENOMEM;
126467ed4161SClemens Ladisch 	kctl->id.device = multi_device;
12651da177e4SLinus Torvalds 	if ((err = snd_ctl_add(card, kctl)))
12661da177e4SLinus Torvalds 		return err;
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds 	if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL)
12691da177e4SLinus Torvalds 		return -ENOMEM;
127067ed4161SClemens Ladisch 	kctl->id.device = multi_device;
12711da177e4SLinus Torvalds 	if ((err = snd_ctl_add(card, kctl)))
12721da177e4SLinus Torvalds 		return err;
12731da177e4SLinus Torvalds 
12741da177e4SLinus Torvalds 	/* initialize the routing and volume table for each pcm playback stream */
12751da177e4SLinus Torvalds 	for (pcm = 0; pcm < 32; pcm++) {
1276eb4698f3STakashi Iwai 		struct snd_emu10k1_pcm_mixer *mix;
12771da177e4SLinus Torvalds 		int v;
12781da177e4SLinus Torvalds 
12791da177e4SLinus Torvalds 		mix = &emu->pcm_mixer[pcm];
12801da177e4SLinus Torvalds 		mix->epcm = NULL;
12811da177e4SLinus Torvalds 
12821da177e4SLinus Torvalds 		for (v = 0; v < 4; v++)
12831da177e4SLinus Torvalds 			mix->send_routing[0][v] =
12841da177e4SLinus Torvalds 				mix->send_routing[1][v] =
12851da177e4SLinus Torvalds 				mix->send_routing[2][v] = v;
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
12881da177e4SLinus Torvalds 		mix->send_volume[0][0] = mix->send_volume[0][1] =
12891da177e4SLinus Torvalds 		mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
12901da177e4SLinus Torvalds 
12911da177e4SLinus Torvalds 		mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
12921da177e4SLinus Torvalds 	}
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 	/* initialize the routing and volume table for the multichannel playback stream */
12951da177e4SLinus Torvalds 	for (pcm = 0; pcm < NUM_EFX_PLAYBACK; pcm++) {
1296eb4698f3STakashi Iwai 		struct snd_emu10k1_pcm_mixer *mix;
12971da177e4SLinus Torvalds 		int v;
12981da177e4SLinus Torvalds 
12991da177e4SLinus Torvalds 		mix = &emu->efx_pcm_mixer[pcm];
13001da177e4SLinus Torvalds 		mix->epcm = NULL;
13011da177e4SLinus Torvalds 
13021da177e4SLinus Torvalds 		mix->send_routing[0][0] = pcm;
13031da177e4SLinus Torvalds 		mix->send_routing[0][1] = (pcm == 0) ? 1 : 0;
13041da177e4SLinus Torvalds 		for (v = 0; v < 2; v++)
13051da177e4SLinus Torvalds 			mix->send_routing[0][2+v] = 13+v;
13061da177e4SLinus Torvalds 		if (emu->audigy)
13071da177e4SLinus Torvalds 			for (v = 0; v < 4; v++)
13081da177e4SLinus Torvalds 				mix->send_routing[0][4+v] = 60+v;
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds 		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
13111da177e4SLinus Torvalds 		mix->send_volume[0][0]  = 255;
13121da177e4SLinus Torvalds 
13131da177e4SLinus Torvalds 		mix->attn[0] = 0xffff;
13141da177e4SLinus Torvalds 	}
13151da177e4SLinus Torvalds 
13162b637da5SLee Revell 	if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
13171da177e4SLinus Torvalds 		/* sb live! and audigy */
13181da177e4SLinus Torvalds 		if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
13191da177e4SLinus Torvalds 			return -ENOMEM;
13205549d549SClemens Ladisch 		if (!emu->audigy)
13215549d549SClemens Ladisch 			kctl->id.device = emu->pcm_efx->device;
13221da177e4SLinus Torvalds 		if ((err = snd_ctl_add(card, kctl)))
13231da177e4SLinus Torvalds 			return err;
13241da177e4SLinus Torvalds 		if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
13251da177e4SLinus Torvalds 			return -ENOMEM;
13265549d549SClemens Ladisch 		if (!emu->audigy)
13275549d549SClemens Ladisch 			kctl->id.device = emu->pcm_efx->device;
13281da177e4SLinus Torvalds 		if ((err = snd_ctl_add(card, kctl)))
13291da177e4SLinus Torvalds 			return err;
13301da177e4SLinus Torvalds 	}
13311da177e4SLinus Torvalds 
1332*9f4bd5ddSJames Courtier-Dutton 	if ( emu->card_capabilities->emu1010) {
133319b99fbaSJames Courtier-Dutton 		;  /* Disable the snd_audigy_spdif_shared_spdif */
133419b99fbaSJames Courtier-Dutton 	} else if (emu->audigy) {
13351da177e4SLinus Torvalds 		if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
13361da177e4SLinus Torvalds 			return -ENOMEM;
13371da177e4SLinus Torvalds 		if ((err = snd_ctl_add(card, kctl)))
13381da177e4SLinus Torvalds 			return err;
1339001f7589SJames Courtier-Dutton #if 0
13401da177e4SLinus Torvalds 		if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
13411da177e4SLinus Torvalds 			return -ENOMEM;
13421da177e4SLinus Torvalds 		if ((err = snd_ctl_add(card, kctl)))
13431da177e4SLinus Torvalds 			return err;
1344001f7589SJames Courtier-Dutton #endif
13452b637da5SLee Revell 	} else if (! emu->card_capabilities->ecard) {
13461da177e4SLinus Torvalds 		/* sb live! */
13471da177e4SLinus Torvalds 		if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
13481da177e4SLinus Torvalds 			return -ENOMEM;
13491da177e4SLinus Torvalds 		if ((err = snd_ctl_add(card, kctl)))
13501da177e4SLinus Torvalds 			return err;
13511da177e4SLinus Torvalds 	}
13522b637da5SLee Revell 	if (emu->card_capabilities->ca0151_chip) { /* P16V */
13531da177e4SLinus Torvalds 		if ((err = snd_p16v_mixer(emu)))
13541da177e4SLinus Torvalds 			return err;
13551da177e4SLinus Torvalds 	}
13561da177e4SLinus Torvalds 
1357*9f4bd5ddSJames Courtier-Dutton 	if ( emu->card_capabilities->emu1010) {
1358*9f4bd5ddSJames Courtier-Dutton 		int i;
1359*9f4bd5ddSJames Courtier-Dutton 
1360*9f4bd5ddSJames Courtier-Dutton 		for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
1361*9f4bd5ddSJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu));
1362*9f4bd5ddSJames Courtier-Dutton 			if (err < 0)
1363*9f4bd5ddSJames Courtier-Dutton 				return err;
1364*9f4bd5ddSJames Courtier-Dutton 		}
1365*9f4bd5ddSJames Courtier-Dutton 		for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
1366*9f4bd5ddSJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu));
1367*9f4bd5ddSJames Courtier-Dutton 			if (err < 0)
1368*9f4bd5ddSJames Courtier-Dutton 				return err;
1369*9f4bd5ddSJames Courtier-Dutton 		}
1370*9f4bd5ddSJames Courtier-Dutton 	}
1371*9f4bd5ddSJames Courtier-Dutton 
13721da177e4SLinus Torvalds 	return 0;
13731da177e4SLinus Torvalds }
1374